2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.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"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gint compose_parse_manual_headers (Compose *compose,
225 HeaderEntry *entries);
226 static gchar *compose_parse_references (const gchar *ref,
229 static gchar *compose_quote_fmt (Compose *compose,
235 gboolean need_unescape,
236 const gchar *err_msg);
238 static void compose_reply_set_entry (Compose *compose,
244 followup_and_reply_to);
245 static void compose_reedit_set_entry (Compose *compose,
248 static void compose_insert_sig (Compose *compose,
250 static ComposeInsertResult compose_insert_file (Compose *compose,
253 static gboolean compose_attach_append (Compose *compose,
256 const gchar *content_type,
257 const gchar *charset);
258 static void compose_attach_parts (Compose *compose,
261 static gboolean compose_beautify_paragraph (Compose *compose,
262 GtkTextIter *par_iter,
264 static void compose_wrap_all (Compose *compose);
265 static void compose_wrap_all_full (Compose *compose,
268 static void compose_set_title (Compose *compose);
269 static void compose_select_account (Compose *compose,
270 PrefsAccount *account,
273 static PrefsAccount *compose_current_mail_account(void);
274 /* static gint compose_send (Compose *compose); */
275 static gboolean compose_check_for_valid_recipient
277 static gboolean compose_check_entries (Compose *compose,
278 gboolean check_everything);
279 static gint compose_write_to_file (Compose *compose,
282 gboolean attach_parts);
283 static gint compose_write_body_to_file (Compose *compose,
285 static gint compose_remove_reedit_target (Compose *compose,
287 static void compose_remove_draft (Compose *compose);
288 static gint compose_queue_sub (Compose *compose,
292 gboolean check_subject,
293 gboolean remove_reedit_target);
294 static int compose_add_attachments (Compose *compose,
296 static gchar *compose_get_header (Compose *compose);
297 static gchar *compose_get_manual_headers_info (Compose *compose);
299 static void compose_convert_header (Compose *compose,
304 gboolean addr_field);
306 static void compose_attach_info_free (AttachInfo *ainfo);
307 static void compose_attach_remove_selected (GtkAction *action,
310 static void compose_template_apply (Compose *compose,
313 static void compose_attach_property (GtkAction *action,
315 static void compose_attach_property_create (gboolean *cancelled);
316 static void attach_property_ok (GtkWidget *widget,
317 gboolean *cancelled);
318 static void attach_property_cancel (GtkWidget *widget,
319 gboolean *cancelled);
320 static gint attach_property_delete_event (GtkWidget *widget,
322 gboolean *cancelled);
323 static gboolean attach_property_key_pressed (GtkWidget *widget,
325 gboolean *cancelled);
327 static void compose_exec_ext_editor (Compose *compose);
329 static gint compose_exec_ext_editor_real (const gchar *file);
330 static gboolean compose_ext_editor_kill (Compose *compose);
331 static gboolean compose_input_cb (GIOChannel *source,
332 GIOCondition condition,
334 static void compose_set_ext_editor_sensitive (Compose *compose,
336 #endif /* G_OS_UNIX */
338 static void compose_undo_state_changed (UndoMain *undostruct,
343 static void compose_create_header_entry (Compose *compose);
344 static void compose_add_header_entry (Compose *compose, const gchar *header,
345 gchar *text, ComposePrefType pref_type);
346 static void compose_remove_header_entries(Compose *compose);
348 static void compose_update_priority_menu_item(Compose * compose);
350 static void compose_spell_menu_changed (void *data);
351 static void compose_dict_changed (void *data);
353 static void compose_add_field_list ( Compose *compose,
354 GList *listAddress );
356 /* callback functions */
358 static void compose_notebook_size_alloc (GtkNotebook *notebook,
359 GtkAllocation *allocation,
361 static gboolean compose_edit_size_alloc (GtkEditable *widget,
362 GtkAllocation *allocation,
363 GtkSHRuler *shruler);
364 static void account_activated (GtkComboBox *optmenu,
366 static void attach_selected (GtkTreeView *tree_view,
367 GtkTreePath *tree_path,
368 GtkTreeViewColumn *column,
370 static gboolean attach_button_pressed (GtkWidget *widget,
371 GdkEventButton *event,
373 static gboolean attach_key_pressed (GtkWidget *widget,
376 static void compose_send_cb (GtkAction *action, gpointer data);
377 static void compose_send_later_cb (GtkAction *action, gpointer data);
379 static void compose_save_cb (GtkAction *action,
382 static void compose_attach_cb (GtkAction *action,
384 static void compose_insert_file_cb (GtkAction *action,
386 static void compose_insert_sig_cb (GtkAction *action,
389 static void compose_close_cb (GtkAction *action,
391 static void compose_print_cb (GtkAction *action,
394 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
396 static void compose_address_cb (GtkAction *action,
398 static void about_show_cb (GtkAction *action,
400 static void compose_template_activate_cb(GtkWidget *widget,
403 static void compose_ext_editor_cb (GtkAction *action,
406 static gint compose_delete_cb (GtkWidget *widget,
410 static void compose_undo_cb (GtkAction *action,
412 static void compose_redo_cb (GtkAction *action,
414 static void compose_cut_cb (GtkAction *action,
416 static void compose_copy_cb (GtkAction *action,
418 static void compose_paste_cb (GtkAction *action,
420 static void compose_paste_as_quote_cb (GtkAction *action,
422 static void compose_paste_no_wrap_cb (GtkAction *action,
424 static void compose_paste_wrap_cb (GtkAction *action,
426 static void compose_allsel_cb (GtkAction *action,
429 static void compose_advanced_action_cb (GtkAction *action,
432 static void compose_grab_focus_cb (GtkWidget *widget,
435 static void compose_changed_cb (GtkTextBuffer *textbuf,
438 static void compose_wrap_cb (GtkAction *action,
440 static void compose_wrap_all_cb (GtkAction *action,
442 static void compose_find_cb (GtkAction *action,
444 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
446 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
449 static void compose_toggle_ruler_cb (GtkToggleAction *action,
451 static void compose_toggle_sign_cb (GtkToggleAction *action,
453 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
455 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
456 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
457 static void activate_privacy_system (Compose *compose,
458 PrefsAccount *account,
460 static void compose_use_signing(Compose *compose, gboolean use_signing);
461 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
462 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
464 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
466 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
467 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
468 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
470 static void compose_attach_drag_received_cb (GtkWidget *widget,
471 GdkDragContext *drag_context,
474 GtkSelectionData *data,
478 static void compose_insert_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_header_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
495 static gboolean compose_drag_drop (GtkWidget *widget,
496 GdkDragContext *drag_context,
498 guint time, gpointer user_data);
500 static void text_inserted (GtkTextBuffer *buffer,
505 static Compose *compose_generic_reply(MsgInfo *msginfo,
506 ComposeQuoteMode quote_mode,
510 gboolean followup_and_reply_to,
513 static void compose_headerentry_changed_cb (GtkWidget *entry,
514 ComposeHeaderEntry *headerentry);
515 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
517 ComposeHeaderEntry *headerentry);
518 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
519 ComposeHeaderEntry *headerentry);
521 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
523 static void compose_allow_user_actions (Compose *compose, gboolean allow);
525 static void compose_nothing_cb (GtkAction *action, gpointer data)
531 static void compose_check_all (GtkAction *action, gpointer data);
532 static void compose_highlight_all (GtkAction *action, gpointer data);
533 static void compose_check_backwards (GtkAction *action, gpointer data);
534 static void compose_check_forwards_go (GtkAction *action, gpointer data);
537 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
539 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
542 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
543 FolderItem *folder_item);
545 static void compose_attach_update_label(Compose *compose);
546 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
547 gboolean respect_default_to);
549 static GtkActionEntry compose_popup_entries[] =
551 {"Compose", NULL, "Compose" },
552 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
553 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
554 {"Compose/---", NULL, "---", NULL, NULL, NULL },
555 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
558 static GtkActionEntry compose_entries[] =
560 {"Menu", NULL, "Menu" },
562 {"Message", NULL, N_("_Message") },
563 {"Edit", NULL, N_("_Edit") },
565 {"Spelling", NULL, N_("_Spelling") },
567 {"Options", NULL, N_("_Options") },
568 {"Tools", NULL, N_("_Tools") },
569 {"Help", NULL, N_("_Help") },
571 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
572 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
573 {"Message/---", NULL, "---" },
575 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
576 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
577 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
578 /* {"Message/---", NULL, "---" }, */
579 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
580 /* {"Message/---", NULL, "---" }, */
581 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
582 /* {"Message/---", NULL, "---" }, */
583 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
586 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
587 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
588 {"Edit/---", NULL, "---" },
590 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
591 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
592 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
594 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
595 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
596 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
597 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
599 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
601 {"Edit/Advanced", NULL, N_("A_dvanced") },
602 {"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*/
603 {"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*/
604 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
605 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
606 {"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*/
607 {"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*/
608 {"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*/
609 {"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*/
610 {"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*/
611 {"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*/
612 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
613 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
614 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
615 {"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*/
617 /* {"Edit/---", NULL, "---" }, */
618 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
620 /* {"Edit/---", NULL, "---" }, */
621 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
622 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
623 /* {"Edit/---", NULL, "---" }, */
624 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
627 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
628 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
629 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
630 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
632 {"Spelling/---", NULL, "---" },
633 {"Spelling/Options", NULL, N_("_Options") },
638 {"Options/ReplyMode", NULL, N_("Reply _mode") },
639 {"Options/---", NULL, "---" },
640 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
641 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 /* {"Options/---", NULL, "---" }, */
645 {"Options/Priority", NULL, N_("_Priority") },
647 {"Options/Encoding", NULL, N_("Character _encoding") },
648 {"Options/Encoding/---", NULL, "---" },
649 #define ENC_ACTION(cs_char,c_char,string) \
650 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
652 {"Options/Encoding/Western", NULL, N_("Western European") },
653 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
654 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
655 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
656 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
657 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
658 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
659 {"Options/Encoding/Korean", NULL, N_("Korean") },
660 {"Options/Encoding/Thai", NULL, N_("Thai") },
663 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
665 {"Tools/Template", NULL, N_("_Template") },
666 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
667 {"Tools/Actions", NULL, N_("Actio_ns") },
668 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
671 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
674 static GtkToggleActionEntry compose_toggle_entries[] =
676 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
677 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
678 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
679 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
680 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
681 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
682 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
685 static GtkRadioActionEntry compose_radio_rm_entries[] =
687 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
688 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
689 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
690 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
693 static GtkRadioActionEntry compose_radio_prio_entries[] =
695 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
696 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
697 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
698 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
699 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
702 static GtkRadioActionEntry compose_radio_enc_entries[] =
704 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
738 static GtkTargetEntry compose_mime_types[] =
740 {"text/uri-list", 0, 0},
741 {"UTF8_STRING", 0, 0},
745 static gboolean compose_put_existing_to_front(MsgInfo *info)
747 GList *compose_list = compose_get_compose_list();
751 for (elem = compose_list; elem != NULL && elem->data != NULL;
753 Compose *c = (Compose*)elem->data;
755 if (!c->targetinfo || !c->targetinfo->msgid ||
759 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
760 gtkut_window_popup(c->window);
768 static GdkColor quote_color1 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor quote_color2 =
771 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor quote_color3 =
773 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor quote_bgcolor1 =
776 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
777 static GdkColor quote_bgcolor2 =
778 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
779 static GdkColor quote_bgcolor3 =
780 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor signature_color = {
789 static GdkColor uri_color = {
796 static void compose_create_tags(GtkTextView *text, Compose *compose)
798 GtkTextBuffer *buffer;
799 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
800 #if !GTK_CHECK_VERSION(2, 24, 0)
807 buffer = gtk_text_view_get_buffer(text);
809 if (prefs_common.enable_color) {
810 /* grab the quote colors, converting from an int to a GdkColor */
811 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
813 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
815 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
817 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
819 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
821 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
823 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
825 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
828 signature_color = quote_color1 = quote_color2 = quote_color3 =
829 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
832 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
833 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
834 "foreground-gdk", "e_color1,
835 "paragraph-background-gdk", "e_bgcolor1,
837 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
838 "foreground-gdk", "e_color2,
839 "paragraph-background-gdk", "e_bgcolor2,
841 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
842 "foreground-gdk", "e_color3,
843 "paragraph-background-gdk", "e_bgcolor3,
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
849 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
850 "foreground-gdk", "e_color2,
852 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
853 "foreground-gdk", "e_color3,
857 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
858 "foreground-gdk", &signature_color,
861 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
862 "foreground-gdk", &uri_color,
864 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
865 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
867 color[0] = quote_color1;
868 color[1] = quote_color2;
869 color[2] = quote_color3;
870 color[3] = quote_bgcolor1;
871 color[4] = quote_bgcolor2;
872 color[5] = quote_bgcolor3;
873 color[6] = signature_color;
874 color[7] = uri_color;
875 #if !GTK_CHECK_VERSION(2, 24, 0)
876 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
877 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
879 for (i = 0; i < 8; i++) {
880 if (success[i] == FALSE) {
883 g_warning("Compose: color allocation failed.\n");
884 style = gtk_widget_get_style(GTK_WIDGET(text));
885 quote_color1 = quote_color2 = quote_color3 =
886 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
887 signature_color = uri_color = black;
893 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
894 GPtrArray *attach_files)
896 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
899 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
901 return compose_generic_new(account, mailto, item, NULL, NULL);
904 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
906 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
909 #define SCROLL_TO_CURSOR(compose) { \
910 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
911 gtk_text_view_get_buffer( \
912 GTK_TEXT_VIEW(compose->text))); \
913 gtk_text_view_scroll_mark_onscreen( \
914 GTK_TEXT_VIEW(compose->text), \
918 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
921 if (folderidentifier) {
922 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
923 prefs_common.compose_save_to_history = add_history(
924 prefs_common.compose_save_to_history, folderidentifier);
925 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
926 prefs_common.compose_save_to_history);
929 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
930 if (folderidentifier)
931 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
933 gtk_entry_set_text(GTK_ENTRY(entry), "");
936 static gchar *compose_get_save_to(Compose *compose)
939 gchar *result = NULL;
940 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
941 result = gtk_editable_get_chars(entry, 0, -1);
944 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
945 prefs_common.compose_save_to_history = add_history(
946 prefs_common.compose_save_to_history, result);
947 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
948 prefs_common.compose_save_to_history);
953 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
954 GPtrArray *attach_files, GList *listAddress )
957 GtkTextView *textview;
958 GtkTextBuffer *textbuf;
960 const gchar *subject_format = NULL;
961 const gchar *body_format = NULL;
962 gchar *mailto_from = NULL;
963 PrefsAccount *mailto_account = NULL;
964 MsgInfo* dummyinfo = NULL;
965 gint cursor_pos = -1;
966 MailField mfield = NO_FIELD_PRESENT;
970 /* check if mailto defines a from */
971 if (mailto && *mailto != '\0') {
972 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
973 /* mailto defines a from, check if we can get account prefs from it,
974 if not, the account prefs will be guessed using other ways, but we'll keep
977 mailto_account = account_find_from_address(mailto_from, TRUE);
979 account = mailto_account;
982 /* if no account prefs set from mailto, set if from folder prefs (if any) */
983 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
984 account = account_find_from_id(item->prefs->default_account);
986 /* if no account prefs set, fallback to the current one */
987 if (!account) account = cur_account;
988 cm_return_val_if_fail(account != NULL, NULL);
990 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
992 /* override from name if mailto asked for it */
994 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
997 /* override from name according to folder properties */
998 if (item && item->prefs &&
999 item->prefs->compose_with_format &&
1000 item->prefs->compose_override_from_format &&
1001 *item->prefs->compose_override_from_format != '\0') {
1006 dummyinfo = compose_msginfo_new_from_compose(compose);
1008 /* decode \-escape sequences in the internal representation of the quote format */
1009 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1010 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1013 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1014 compose->gtkaspell);
1016 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1018 quote_fmt_scan_string(tmp);
1021 buf = quote_fmt_get_buffer();
1023 alertpanel_error(_("New message From format error."));
1025 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1026 quote_fmt_reset_vartable();
1031 compose->replyinfo = NULL;
1032 compose->fwdinfo = NULL;
1034 textview = GTK_TEXT_VIEW(compose->text);
1035 textbuf = gtk_text_view_get_buffer(textview);
1036 compose_create_tags(textview, compose);
1038 undo_block(compose->undostruct);
1040 compose_set_dictionaries_from_folder_prefs(compose, item);
1043 if (account->auto_sig)
1044 compose_insert_sig(compose, FALSE);
1045 gtk_text_buffer_get_start_iter(textbuf, &iter);
1046 gtk_text_buffer_place_cursor(textbuf, &iter);
1048 if (account->protocol != A_NNTP) {
1049 if (mailto && *mailto != '\0') {
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1053 compose_set_folder_prefs(compose, item, TRUE);
1055 if (item && item->ret_rcpt) {
1056 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1059 if (mailto && *mailto != '\0') {
1060 if (!strchr(mailto, '@'))
1061 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1063 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1064 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1065 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1066 mfield = TO_FIELD_PRESENT;
1069 * CLAWS: just don't allow return receipt request, even if the user
1070 * may want to send an email. simple but foolproof.
1072 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1074 compose_add_field_list( compose, listAddress );
1076 if (item && item->prefs && item->prefs->compose_with_format) {
1077 subject_format = item->prefs->compose_subject_format;
1078 body_format = item->prefs->compose_body_format;
1079 } else if (account->compose_with_format) {
1080 subject_format = account->compose_subject_format;
1081 body_format = account->compose_body_format;
1082 } else if (prefs_common.compose_with_format) {
1083 subject_format = prefs_common.compose_subject_format;
1084 body_format = prefs_common.compose_body_format;
1087 if (subject_format || body_format) {
1090 && *subject_format != '\0' )
1092 gchar *subject = NULL;
1097 dummyinfo = compose_msginfo_new_from_compose(compose);
1099 /* decode \-escape sequences in the internal representation of the quote format */
1100 tmp = g_malloc(strlen(subject_format)+1);
1101 pref_get_unescaped_pref(tmp, subject_format);
1103 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1105 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1106 compose->gtkaspell);
1108 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1110 quote_fmt_scan_string(tmp);
1113 buf = quote_fmt_get_buffer();
1115 alertpanel_error(_("New message subject format error."));
1117 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1118 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1119 quote_fmt_reset_vartable();
1123 mfield = SUBJECT_FIELD_PRESENT;
1127 && *body_format != '\0' )
1130 GtkTextBuffer *buffer;
1131 GtkTextIter start, end;
1135 dummyinfo = compose_msginfo_new_from_compose(compose);
1137 text = GTK_TEXT_VIEW(compose->text);
1138 buffer = gtk_text_view_get_buffer(text);
1139 gtk_text_buffer_get_start_iter(buffer, &start);
1140 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1141 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1143 compose_quote_fmt(compose, dummyinfo,
1145 NULL, tmp, FALSE, TRUE,
1146 _("The body of the \"New message\" template has an error at line %d."));
1147 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1148 quote_fmt_reset_vartable();
1152 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1153 gtkaspell_highlight_all(compose->gtkaspell);
1155 mfield = BODY_FIELD_PRESENT;
1159 procmsg_msginfo_free( dummyinfo );
1165 for (i = 0; i < attach_files->len; i++) {
1166 file = g_ptr_array_index(attach_files, i);
1167 compose_attach_append(compose, file, file, NULL, NULL);
1171 compose_show_first_last_header(compose, TRUE);
1173 /* Set save folder */
1174 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1175 gchar *folderidentifier;
1177 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1178 folderidentifier = folder_item_get_identifier(item);
1179 compose_set_save_to(compose, folderidentifier);
1180 g_free(folderidentifier);
1183 /* Place cursor according to provided input (mfield) */
1185 case NO_FIELD_PRESENT:
1186 if (compose->header_last)
1187 gtk_widget_grab_focus(compose->header_last->entry);
1189 case TO_FIELD_PRESENT:
1190 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1192 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1195 gtk_widget_grab_focus(compose->subject_entry);
1197 case SUBJECT_FIELD_PRESENT:
1198 textview = GTK_TEXT_VIEW(compose->text);
1201 textbuf = gtk_text_view_get_buffer(textview);
1204 mark = gtk_text_buffer_get_insert(textbuf);
1205 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1206 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1208 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1209 * only defers where it comes to the variable body
1210 * is not null. If no body is present compose->text
1211 * will be null in which case you cannot place the
1212 * cursor inside the component so. An empty component
1213 * is therefore created before placing the cursor
1215 case BODY_FIELD_PRESENT:
1216 cursor_pos = quote_fmt_get_cursor_pos();
1217 if (cursor_pos == -1)
1218 gtk_widget_grab_focus(compose->header_last->entry);
1220 gtk_widget_grab_focus(compose->text);
1224 undo_unblock(compose->undostruct);
1226 if (prefs_common.auto_exteditor)
1227 compose_exec_ext_editor(compose);
1229 compose->draft_timeout_tag = -1;
1230 SCROLL_TO_CURSOR(compose);
1232 compose->modified = FALSE;
1233 compose_set_title(compose);
1235 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1240 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1241 gboolean override_pref, const gchar *system)
1243 const gchar *privacy = NULL;
1245 cm_return_if_fail(compose != NULL);
1246 cm_return_if_fail(account != NULL);
1248 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1253 else if (account->default_privacy_system
1254 && strlen(account->default_privacy_system)) {
1255 privacy = account->default_privacy_system;
1257 GSList *privacy_avail = privacy_get_system_ids();
1258 if (privacy_avail && g_slist_length(privacy_avail)) {
1259 privacy = (gchar *)(privacy_avail->data);
1262 if (privacy != NULL) {
1264 g_free(compose->privacy_system);
1265 compose->privacy_system = NULL;
1267 if (compose->privacy_system == NULL)
1268 compose->privacy_system = g_strdup(privacy);
1269 else if (*(compose->privacy_system) == '\0') {
1270 g_free(compose->privacy_system);
1271 compose->privacy_system = g_strdup(privacy);
1273 compose_update_privacy_system_menu_item(compose, FALSE);
1274 compose_use_encryption(compose, TRUE);
1278 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1280 const gchar *privacy = NULL;
1284 else if (account->default_privacy_system
1285 && strlen(account->default_privacy_system)) {
1286 privacy = account->default_privacy_system;
1288 GSList *privacy_avail = privacy_get_system_ids();
1289 if (privacy_avail && g_slist_length(privacy_avail)) {
1290 privacy = (gchar *)(privacy_avail->data);
1294 if (privacy != NULL) {
1296 g_free(compose->privacy_system);
1297 compose->privacy_system = NULL;
1299 if (compose->privacy_system == NULL)
1300 compose->privacy_system = g_strdup(privacy);
1301 compose_update_privacy_system_menu_item(compose, FALSE);
1302 compose_use_signing(compose, TRUE);
1306 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1310 Compose *compose = NULL;
1312 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1314 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1315 cm_return_val_if_fail(msginfo != NULL, NULL);
1317 list_len = g_slist_length(msginfo_list);
1321 case COMPOSE_REPLY_TO_ADDRESS:
1322 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1323 FALSE, prefs_common.default_reply_list, FALSE, body);
1325 case COMPOSE_REPLY_WITH_QUOTE:
1326 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1327 FALSE, prefs_common.default_reply_list, FALSE, body);
1329 case COMPOSE_REPLY_WITHOUT_QUOTE:
1330 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1331 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1333 case COMPOSE_REPLY_TO_SENDER:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1335 FALSE, FALSE, TRUE, body);
1337 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1338 compose = compose_followup_and_reply_to(msginfo,
1339 COMPOSE_QUOTE_CHECK,
1340 FALSE, FALSE, body);
1342 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1343 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1344 FALSE, FALSE, TRUE, body);
1346 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1347 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1348 FALSE, FALSE, TRUE, NULL);
1350 case COMPOSE_REPLY_TO_ALL:
1351 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1352 TRUE, FALSE, FALSE, body);
1354 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1355 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1356 TRUE, FALSE, FALSE, body);
1358 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1359 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1360 TRUE, FALSE, FALSE, NULL);
1362 case COMPOSE_REPLY_TO_LIST:
1363 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1364 FALSE, TRUE, FALSE, body);
1366 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1367 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1368 FALSE, TRUE, FALSE, body);
1370 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1371 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1372 FALSE, TRUE, FALSE, NULL);
1374 case COMPOSE_FORWARD:
1375 if (prefs_common.forward_as_attachment) {
1376 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1379 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1383 case COMPOSE_FORWARD_INLINE:
1384 /* check if we reply to more than one Message */
1385 if (list_len == 1) {
1386 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1389 /* more messages FALL THROUGH */
1390 case COMPOSE_FORWARD_AS_ATTACH:
1391 compose = compose_forward_multiple(NULL, msginfo_list);
1393 case COMPOSE_REDIRECT:
1394 compose = compose_redirect(NULL, msginfo, FALSE);
1397 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1400 if (compose == NULL) {
1401 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1405 compose->rmode = mode;
1406 switch (compose->rmode) {
1408 case COMPOSE_REPLY_WITH_QUOTE:
1409 case COMPOSE_REPLY_WITHOUT_QUOTE:
1410 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1411 debug_print("reply mode Normal\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1413 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1415 case COMPOSE_REPLY_TO_SENDER:
1416 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1417 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1418 debug_print("reply mode Sender\n");
1419 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1421 case COMPOSE_REPLY_TO_ALL:
1422 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1423 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1424 debug_print("reply mode All\n");
1425 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1427 case COMPOSE_REPLY_TO_LIST:
1428 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1429 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1430 debug_print("reply mode List\n");
1431 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1433 case COMPOSE_REPLY_TO_ADDRESS:
1434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1442 static Compose *compose_reply(MsgInfo *msginfo,
1443 ComposeQuoteMode quote_mode,
1449 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1450 to_sender, FALSE, body);
1453 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1454 ComposeQuoteMode quote_mode,
1459 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1460 to_sender, TRUE, body);
1463 static void compose_extract_original_charset(Compose *compose)
1465 MsgInfo *info = NULL;
1466 if (compose->replyinfo) {
1467 info = compose->replyinfo;
1468 } else if (compose->fwdinfo) {
1469 info = compose->fwdinfo;
1470 } else if (compose->targetinfo) {
1471 info = compose->targetinfo;
1474 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1475 MimeInfo *partinfo = mimeinfo;
1476 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1477 partinfo = procmime_mimeinfo_next(partinfo);
1479 compose->orig_charset =
1480 g_strdup(procmime_mimeinfo_get_parameter(
1481 partinfo, "charset"));
1483 procmime_mimeinfo_free_all(mimeinfo);
1487 #define SIGNAL_BLOCK(buffer) { \
1488 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1489 G_CALLBACK(compose_changed_cb), \
1491 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1492 G_CALLBACK(text_inserted), \
1496 #define SIGNAL_UNBLOCK(buffer) { \
1497 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(compose_changed_cb), \
1500 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1501 G_CALLBACK(text_inserted), \
1505 static Compose *compose_generic_reply(MsgInfo *msginfo,
1506 ComposeQuoteMode quote_mode,
1507 gboolean to_all, gboolean to_ml,
1509 gboolean followup_and_reply_to,
1513 PrefsAccount *account = NULL;
1514 GtkTextView *textview;
1515 GtkTextBuffer *textbuf;
1516 gboolean quote = FALSE;
1517 const gchar *qmark = NULL;
1518 const gchar *body_fmt = NULL;
1519 gchar *s_system = NULL;
1521 cm_return_val_if_fail(msginfo != NULL, NULL);
1522 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1524 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1526 cm_return_val_if_fail(account != NULL, NULL);
1528 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1530 compose->updating = TRUE;
1532 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1533 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1535 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1536 if (!compose->replyinfo)
1537 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1539 compose_extract_original_charset(compose);
1541 if (msginfo->folder && msginfo->folder->ret_rcpt)
1542 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1544 /* Set save folder */
1545 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1546 gchar *folderidentifier;
1548 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1549 folderidentifier = folder_item_get_identifier(msginfo->folder);
1550 compose_set_save_to(compose, folderidentifier);
1551 g_free(folderidentifier);
1554 if (compose_parse_header(compose, msginfo) < 0) {
1555 compose->updating = FALSE;
1556 compose_destroy(compose);
1560 /* override from name according to folder properties */
1561 if (msginfo->folder && msginfo->folder->prefs &&
1562 msginfo->folder->prefs->reply_with_format &&
1563 msginfo->folder->prefs->reply_override_from_format &&
1564 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1569 /* decode \-escape sequences in the internal representation of the quote format */
1570 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1571 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1574 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1575 compose->gtkaspell);
1577 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1579 quote_fmt_scan_string(tmp);
1582 buf = quote_fmt_get_buffer();
1584 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1586 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1587 quote_fmt_reset_vartable();
1592 textview = (GTK_TEXT_VIEW(compose->text));
1593 textbuf = gtk_text_view_get_buffer(textview);
1594 compose_create_tags(textview, compose);
1596 undo_block(compose->undostruct);
1598 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1599 gtkaspell_block_check(compose->gtkaspell);
1602 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1603 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1604 /* use the reply format of folder (if enabled), or the account's one
1605 (if enabled) or fallback to the global reply format, which is always
1606 enabled (even if empty), and use the relevant quotemark */
1608 if (msginfo->folder && msginfo->folder->prefs &&
1609 msginfo->folder->prefs->reply_with_format) {
1610 qmark = msginfo->folder->prefs->reply_quotemark;
1611 body_fmt = msginfo->folder->prefs->reply_body_format;
1613 } else if (account->reply_with_format) {
1614 qmark = account->reply_quotemark;
1615 body_fmt = account->reply_body_format;
1618 qmark = prefs_common.quotemark;
1619 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1620 body_fmt = gettext(prefs_common.quotefmt);
1627 /* empty quotemark is not allowed */
1628 if (qmark == NULL || *qmark == '\0')
1630 compose_quote_fmt(compose, compose->replyinfo,
1631 body_fmt, qmark, body, FALSE, TRUE,
1632 _("The body of the \"Reply\" template has an error at line %d."));
1633 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1634 quote_fmt_reset_vartable();
1637 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1638 compose_force_encryption(compose, account, FALSE, s_system);
1641 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1642 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1643 compose_force_signing(compose, account, s_system);
1647 SIGNAL_BLOCK(textbuf);
1649 if (account->auto_sig)
1650 compose_insert_sig(compose, FALSE);
1652 compose_wrap_all(compose);
1655 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1656 gtkaspell_highlight_all(compose->gtkaspell);
1657 gtkaspell_unblock_check(compose->gtkaspell);
1659 SIGNAL_UNBLOCK(textbuf);
1661 gtk_widget_grab_focus(compose->text);
1663 undo_unblock(compose->undostruct);
1665 if (prefs_common.auto_exteditor)
1666 compose_exec_ext_editor(compose);
1668 compose->modified = FALSE;
1669 compose_set_title(compose);
1671 compose->updating = FALSE;
1672 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1673 SCROLL_TO_CURSOR(compose);
1675 if (compose->deferred_destroy) {
1676 compose_destroy(compose);
1684 #define INSERT_FW_HEADER(var, hdr) \
1685 if (msginfo->var && *msginfo->var) { \
1686 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1687 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1688 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1691 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1692 gboolean as_attach, const gchar *body,
1693 gboolean no_extedit,
1697 GtkTextView *textview;
1698 GtkTextBuffer *textbuf;
1699 gint cursor_pos = -1;
1702 cm_return_val_if_fail(msginfo != NULL, NULL);
1703 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1706 !(account = compose_guess_forward_account_from_msginfo
1708 account = cur_account;
1710 if (!prefs_common.forward_as_attachment)
1711 mode = COMPOSE_FORWARD_INLINE;
1713 mode = COMPOSE_FORWARD;
1714 compose = compose_create(account, msginfo->folder, mode, batch);
1716 compose->updating = TRUE;
1717 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1718 if (!compose->fwdinfo)
1719 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1721 compose_extract_original_charset(compose);
1723 if (msginfo->subject && *msginfo->subject) {
1724 gchar *buf, *buf2, *p;
1726 buf = p = g_strdup(msginfo->subject);
1727 p += subject_get_prefix_length(p);
1728 memmove(buf, p, strlen(p) + 1);
1730 buf2 = g_strdup_printf("Fw: %s", buf);
1731 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1737 /* override from name according to folder properties */
1738 if (msginfo->folder && msginfo->folder->prefs &&
1739 msginfo->folder->prefs->forward_with_format &&
1740 msginfo->folder->prefs->forward_override_from_format &&
1741 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1745 MsgInfo *full_msginfo = NULL;
1748 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1750 full_msginfo = procmsg_msginfo_copy(msginfo);
1752 /* decode \-escape sequences in the internal representation of the quote format */
1753 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1754 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1757 gtkaspell_block_check(compose->gtkaspell);
1758 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1759 compose->gtkaspell);
1761 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1763 quote_fmt_scan_string(tmp);
1766 buf = quote_fmt_get_buffer();
1768 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1770 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1771 quote_fmt_reset_vartable();
1774 procmsg_msginfo_free(full_msginfo);
1777 textview = GTK_TEXT_VIEW(compose->text);
1778 textbuf = gtk_text_view_get_buffer(textview);
1779 compose_create_tags(textview, compose);
1781 undo_block(compose->undostruct);
1785 msgfile = procmsg_get_message_file(msginfo);
1786 if (!is_file_exist(msgfile))
1787 g_warning("%s: file not exist\n", msgfile);
1789 compose_attach_append(compose, msgfile, msgfile,
1790 "message/rfc822", NULL);
1794 const gchar *qmark = NULL;
1795 const gchar *body_fmt = NULL;
1796 MsgInfo *full_msginfo;
1798 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1799 body_fmt = gettext(prefs_common.fw_quotefmt);
1803 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1805 full_msginfo = procmsg_msginfo_copy(msginfo);
1807 /* use the forward format of folder (if enabled), or the account's one
1808 (if enabled) or fallback to the global forward format, which is always
1809 enabled (even if empty), and use the relevant quotemark */
1810 if (msginfo->folder && msginfo->folder->prefs &&
1811 msginfo->folder->prefs->forward_with_format) {
1812 qmark = msginfo->folder->prefs->forward_quotemark;
1813 body_fmt = msginfo->folder->prefs->forward_body_format;
1815 } else if (account->forward_with_format) {
1816 qmark = account->forward_quotemark;
1817 body_fmt = account->forward_body_format;
1820 qmark = prefs_common.fw_quotemark;
1821 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1822 body_fmt = gettext(prefs_common.fw_quotefmt);
1827 /* empty quotemark is not allowed */
1828 if (qmark == NULL || *qmark == '\0')
1831 compose_quote_fmt(compose, full_msginfo,
1832 body_fmt, qmark, body, FALSE, TRUE,
1833 _("The body of the \"Forward\" template has an error at line %d."));
1834 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1835 quote_fmt_reset_vartable();
1836 compose_attach_parts(compose, msginfo);
1838 procmsg_msginfo_free(full_msginfo);
1841 SIGNAL_BLOCK(textbuf);
1843 if (account->auto_sig)
1844 compose_insert_sig(compose, FALSE);
1846 compose_wrap_all(compose);
1849 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1850 gtkaspell_highlight_all(compose->gtkaspell);
1851 gtkaspell_unblock_check(compose->gtkaspell);
1853 SIGNAL_UNBLOCK(textbuf);
1855 cursor_pos = quote_fmt_get_cursor_pos();
1856 if (cursor_pos == -1)
1857 gtk_widget_grab_focus(compose->header_last->entry);
1859 gtk_widget_grab_focus(compose->text);
1861 if (!no_extedit && prefs_common.auto_exteditor)
1862 compose_exec_ext_editor(compose);
1865 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1866 gchar *folderidentifier;
1868 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1869 folderidentifier = folder_item_get_identifier(msginfo->folder);
1870 compose_set_save_to(compose, folderidentifier);
1871 g_free(folderidentifier);
1874 undo_unblock(compose->undostruct);
1876 compose->modified = FALSE;
1877 compose_set_title(compose);
1879 compose->updating = FALSE;
1880 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1881 SCROLL_TO_CURSOR(compose);
1883 if (compose->deferred_destroy) {
1884 compose_destroy(compose);
1888 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1893 #undef INSERT_FW_HEADER
1895 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1898 GtkTextView *textview;
1899 GtkTextBuffer *textbuf;
1903 gboolean single_mail = TRUE;
1905 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1907 if (g_slist_length(msginfo_list) > 1)
1908 single_mail = FALSE;
1910 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1911 if (((MsgInfo *)msginfo->data)->folder == NULL)
1914 /* guess account from first selected message */
1916 !(account = compose_guess_forward_account_from_msginfo
1917 (msginfo_list->data)))
1918 account = cur_account;
1920 cm_return_val_if_fail(account != NULL, NULL);
1922 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1923 if (msginfo->data) {
1924 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1925 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1929 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1930 g_warning("no msginfo_list");
1934 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1936 compose->updating = TRUE;
1938 /* override from name according to folder properties */
1939 if (msginfo_list->data) {
1940 MsgInfo *msginfo = msginfo_list->data;
1942 if (msginfo->folder && msginfo->folder->prefs &&
1943 msginfo->folder->prefs->forward_with_format &&
1944 msginfo->folder->prefs->forward_override_from_format &&
1945 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1950 /* decode \-escape sequences in the internal representation of the quote format */
1951 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1952 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1955 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1956 compose->gtkaspell);
1958 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1960 quote_fmt_scan_string(tmp);
1963 buf = quote_fmt_get_buffer();
1965 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1967 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1968 quote_fmt_reset_vartable();
1974 textview = GTK_TEXT_VIEW(compose->text);
1975 textbuf = gtk_text_view_get_buffer(textview);
1976 compose_create_tags(textview, compose);
1978 undo_block(compose->undostruct);
1979 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1980 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1982 if (!is_file_exist(msgfile))
1983 g_warning("%s: file not exist\n", msgfile);
1985 compose_attach_append(compose, msgfile, msgfile,
1986 "message/rfc822", NULL);
1991 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1992 if (info->subject && *info->subject) {
1993 gchar *buf, *buf2, *p;
1995 buf = p = g_strdup(info->subject);
1996 p += subject_get_prefix_length(p);
1997 memmove(buf, p, strlen(p) + 1);
1999 buf2 = g_strdup_printf("Fw: %s", buf);
2000 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2006 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2007 _("Fw: multiple emails"));
2010 SIGNAL_BLOCK(textbuf);
2012 if (account->auto_sig)
2013 compose_insert_sig(compose, FALSE);
2015 compose_wrap_all(compose);
2017 SIGNAL_UNBLOCK(textbuf);
2019 gtk_text_buffer_get_start_iter(textbuf, &iter);
2020 gtk_text_buffer_place_cursor(textbuf, &iter);
2022 gtk_widget_grab_focus(compose->header_last->entry);
2023 undo_unblock(compose->undostruct);
2024 compose->modified = FALSE;
2025 compose_set_title(compose);
2027 compose->updating = FALSE;
2028 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2029 SCROLL_TO_CURSOR(compose);
2031 if (compose->deferred_destroy) {
2032 compose_destroy(compose);
2036 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2041 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2043 GtkTextIter start = *iter;
2044 GtkTextIter end_iter;
2045 int start_pos = gtk_text_iter_get_offset(&start);
2047 if (!compose->account->sig_sep)
2050 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2051 start_pos+strlen(compose->account->sig_sep));
2053 /* check sig separator */
2054 str = gtk_text_iter_get_text(&start, &end_iter);
2055 if (!strcmp(str, compose->account->sig_sep)) {
2057 /* check end of line (\n) */
2058 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2059 start_pos+strlen(compose->account->sig_sep));
2060 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2061 start_pos+strlen(compose->account->sig_sep)+1);
2062 tmp = gtk_text_iter_get_text(&start, &end_iter);
2063 if (!strcmp(tmp,"\n")) {
2075 static void compose_colorize_signature(Compose *compose)
2077 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2079 GtkTextIter end_iter;
2080 gtk_text_buffer_get_start_iter(buffer, &iter);
2081 while (gtk_text_iter_forward_line(&iter))
2082 if (compose_is_sig_separator(compose, buffer, &iter)) {
2083 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2084 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2088 #define BLOCK_WRAP() { \
2089 prev_autowrap = compose->autowrap; \
2090 buffer = gtk_text_view_get_buffer( \
2091 GTK_TEXT_VIEW(compose->text)); \
2092 compose->autowrap = FALSE; \
2094 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2095 G_CALLBACK(compose_changed_cb), \
2097 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2098 G_CALLBACK(text_inserted), \
2101 #define UNBLOCK_WRAP() { \
2102 compose->autowrap = prev_autowrap; \
2103 if (compose->autowrap) { \
2104 gint old = compose->draft_timeout_tag; \
2105 compose->draft_timeout_tag = -2; \
2106 compose_wrap_all(compose); \
2107 compose->draft_timeout_tag = old; \
2110 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2111 G_CALLBACK(compose_changed_cb), \
2113 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2114 G_CALLBACK(text_inserted), \
2118 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2120 Compose *compose = NULL;
2121 PrefsAccount *account = NULL;
2122 GtkTextView *textview;
2123 GtkTextBuffer *textbuf;
2127 gchar buf[BUFFSIZE];
2128 gboolean use_signing = FALSE;
2129 gboolean use_encryption = FALSE;
2130 gchar *privacy_system = NULL;
2131 int priority = PRIORITY_NORMAL;
2132 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2133 gboolean autowrap = prefs_common.autowrap;
2134 gboolean autoindent = prefs_common.auto_indent;
2135 HeaderEntry *manual_headers = NULL;
2137 cm_return_val_if_fail(msginfo != NULL, NULL);
2138 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2140 if (compose_put_existing_to_front(msginfo)) {
2144 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2145 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2146 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2147 gchar queueheader_buf[BUFFSIZE];
2150 /* Select Account from queue headers */
2151 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2152 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2153 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2154 account = account_find_from_id(id);
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2158 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2159 account = account_find_from_id(id);
2161 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2162 sizeof(queueheader_buf), "NAID:")) {
2163 id = atoi(&queueheader_buf[strlen("NAID:")]);
2164 account = account_find_from_id(id);
2166 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2167 sizeof(queueheader_buf), "MAID:")) {
2168 id = atoi(&queueheader_buf[strlen("MAID:")]);
2169 account = account_find_from_id(id);
2171 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "S:")) {
2173 account = account_find_from_address(queueheader_buf, FALSE);
2175 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2177 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2178 use_signing = param;
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2184 use_signing = param;
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2189 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2190 use_encryption = param;
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2194 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2195 use_encryption = param;
2197 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2198 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2199 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2202 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2203 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2204 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2209 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2211 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2212 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2213 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2215 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2216 sizeof(queueheader_buf), "X-Priority: ")) {
2217 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2220 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2221 sizeof(queueheader_buf), "RMID:")) {
2222 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2223 if (tokens[0] && tokens[1] && tokens[2]) {
2224 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2225 if (orig_item != NULL) {
2226 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2231 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2232 sizeof(queueheader_buf), "FMID:")) {
2233 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2234 if (tokens[0] && tokens[1] && tokens[2]) {
2235 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2236 if (orig_item != NULL) {
2237 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2242 /* Get manual headers */
2243 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2244 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2245 if (*listmh != '\0') {
2246 debug_print("Got manual headers: %s\n", listmh);
2247 manual_headers = procheader_entries_from_str(listmh);
2252 account = msginfo->folder->folder->account;
2255 if (!account && prefs_common.reedit_account_autosel) {
2256 gchar from[BUFFSIZE];
2257 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2258 extract_address(from);
2259 account = account_find_from_address(from, FALSE);
2263 account = cur_account;
2265 cm_return_val_if_fail(account != NULL, NULL);
2267 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2269 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2270 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2271 compose->autowrap = autowrap;
2272 compose->replyinfo = replyinfo;
2273 compose->fwdinfo = fwdinfo;
2275 compose->updating = TRUE;
2276 compose->priority = priority;
2278 if (privacy_system != NULL) {
2279 compose->privacy_system = privacy_system;
2280 compose_use_signing(compose, use_signing);
2281 compose_use_encryption(compose, use_encryption);
2282 compose_update_privacy_system_menu_item(compose, FALSE);
2284 activate_privacy_system(compose, account, FALSE);
2287 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2289 compose_extract_original_charset(compose);
2291 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2292 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2293 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2294 gchar queueheader_buf[BUFFSIZE];
2296 /* Set message save folder */
2297 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2298 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2299 compose_set_save_to(compose, &queueheader_buf[4]);
2301 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2302 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2304 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2309 if (compose_parse_header(compose, msginfo) < 0) {
2310 compose->updating = FALSE;
2311 compose_destroy(compose);
2314 compose_reedit_set_entry(compose, msginfo);
2316 textview = GTK_TEXT_VIEW(compose->text);
2317 textbuf = gtk_text_view_get_buffer(textview);
2318 compose_create_tags(textview, compose);
2320 mark = gtk_text_buffer_get_insert(textbuf);
2321 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2323 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2324 G_CALLBACK(compose_changed_cb),
2327 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2328 fp = procmime_get_first_encrypted_text_content(msginfo);
2330 compose_force_encryption(compose, account, TRUE, NULL);
2333 fp = procmime_get_first_text_content(msginfo);
2336 g_warning("Can't get text part\n");
2340 gboolean prev_autowrap = compose->autowrap;
2341 GtkTextBuffer *buffer = textbuf;
2343 while (fgets(buf, sizeof(buf), fp) != NULL) {
2345 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2351 compose_attach_parts(compose, msginfo);
2353 compose_colorize_signature(compose);
2355 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2356 G_CALLBACK(compose_changed_cb),
2359 if (manual_headers != NULL) {
2360 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2361 procheader_entries_free(manual_headers);
2362 compose->updating = FALSE;
2363 compose_destroy(compose);
2366 procheader_entries_free(manual_headers);
2369 gtk_widget_grab_focus(compose->text);
2371 if (prefs_common.auto_exteditor) {
2372 compose_exec_ext_editor(compose);
2374 compose->modified = FALSE;
2375 compose_set_title(compose);
2377 compose->updating = FALSE;
2378 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2379 SCROLL_TO_CURSOR(compose);
2381 if (compose->deferred_destroy) {
2382 compose_destroy(compose);
2386 compose->sig_str = account_get_signature_str(compose->account);
2388 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2393 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2400 cm_return_val_if_fail(msginfo != NULL, NULL);
2403 account = account_get_reply_account(msginfo,
2404 prefs_common.reply_account_autosel);
2405 cm_return_val_if_fail(account != NULL, NULL);
2407 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2409 compose->updating = TRUE;
2411 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2412 compose->replyinfo = NULL;
2413 compose->fwdinfo = NULL;
2415 compose_show_first_last_header(compose, TRUE);
2417 gtk_widget_grab_focus(compose->header_last->entry);
2419 filename = procmsg_get_message_file(msginfo);
2421 if (filename == NULL) {
2422 compose->updating = FALSE;
2423 compose_destroy(compose);
2428 compose->redirect_filename = filename;
2430 /* Set save folder */
2431 item = msginfo->folder;
2432 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2433 gchar *folderidentifier;
2435 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2436 folderidentifier = folder_item_get_identifier(item);
2437 compose_set_save_to(compose, folderidentifier);
2438 g_free(folderidentifier);
2441 compose_attach_parts(compose, msginfo);
2443 if (msginfo->subject)
2444 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2446 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2448 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2449 _("The body of the \"Redirect\" template has an error at line %d."));
2450 quote_fmt_reset_vartable();
2451 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2453 compose_colorize_signature(compose);
2456 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2457 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2458 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2460 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2461 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2469 if (compose->toolbar->draft_btn)
2470 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2471 if (compose->toolbar->insert_btn)
2472 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2473 if (compose->toolbar->attach_btn)
2474 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2475 if (compose->toolbar->sig_btn)
2476 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2477 if (compose->toolbar->exteditor_btn)
2478 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2479 if (compose->toolbar->linewrap_current_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2481 if (compose->toolbar->linewrap_all_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2484 compose->modified = FALSE;
2485 compose_set_title(compose);
2486 compose->updating = FALSE;
2487 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2488 SCROLL_TO_CURSOR(compose);
2490 if (compose->deferred_destroy) {
2491 compose_destroy(compose);
2495 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2500 GList *compose_get_compose_list(void)
2502 return compose_list;
2505 void compose_entry_append(Compose *compose, const gchar *address,
2506 ComposeEntryType type, ComposePrefType pref_type)
2508 const gchar *header;
2510 gboolean in_quote = FALSE;
2511 if (!address || *address == '\0') return;
2518 header = N_("Bcc:");
2520 case COMPOSE_REPLYTO:
2521 header = N_("Reply-To:");
2523 case COMPOSE_NEWSGROUPS:
2524 header = N_("Newsgroups:");
2526 case COMPOSE_FOLLOWUPTO:
2527 header = N_( "Followup-To:");
2529 case COMPOSE_INREPLYTO:
2530 header = N_( "In-Reply-To:");
2537 header = prefs_common_translated_header_name(header);
2539 cur = begin = (gchar *)address;
2541 /* we separate the line by commas, but not if we're inside a quoted
2543 while (*cur != '\0') {
2545 in_quote = !in_quote;
2546 if (*cur == ',' && !in_quote) {
2547 gchar *tmp = g_strdup(begin);
2549 tmp[cur-begin]='\0';
2552 while (*tmp == ' ' || *tmp == '\t')
2554 compose_add_header_entry(compose, header, tmp, pref_type);
2561 gchar *tmp = g_strdup(begin);
2563 tmp[cur-begin]='\0';
2566 while (*tmp == ' ' || *tmp == '\t')
2568 compose_add_header_entry(compose, header, tmp, pref_type);
2573 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2575 #if !GTK_CHECK_VERSION(3, 0, 0)
2576 static GdkColor yellow;
2577 static GdkColor black;
2578 static gboolean yellow_initialised = FALSE;
2580 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2581 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2586 #if !GTK_CHECK_VERSION(3, 0, 0)
2587 if (!yellow_initialised) {
2588 gdk_color_parse("#f5f6be", &yellow);
2589 gdk_color_parse("#000000", &black);
2590 yellow_initialised = gdk_colormap_alloc_color(
2591 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2592 yellow_initialised &= gdk_colormap_alloc_color(
2593 gdk_colormap_get_system(), &black, FALSE, TRUE);
2597 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2598 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2599 if (gtk_entry_get_text(entry) &&
2600 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2601 #if !GTK_CHECK_VERSION(3, 0, 0)
2602 if (yellow_initialised) {
2604 gtk_widget_modify_base(
2605 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2606 GTK_STATE_NORMAL, &yellow);
2607 gtk_widget_modify_text(
2608 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2609 GTK_STATE_NORMAL, &black);
2610 #if !GTK_CHECK_VERSION(3, 0, 0)
2617 void compose_toolbar_cb(gint action, gpointer data)
2619 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2620 Compose *compose = (Compose*)toolbar_item->parent;
2622 cm_return_if_fail(compose != NULL);
2626 compose_send_cb(NULL, compose);
2629 compose_send_later_cb(NULL, compose);
2632 compose_draft(compose, COMPOSE_QUIT_EDITING);
2635 compose_insert_file_cb(NULL, compose);
2638 compose_attach_cb(NULL, compose);
2641 compose_insert_sig(compose, FALSE);
2644 compose_ext_editor_cb(NULL, compose);
2646 case A_LINEWRAP_CURRENT:
2647 compose_beautify_paragraph(compose, NULL, TRUE);
2649 case A_LINEWRAP_ALL:
2650 compose_wrap_all_full(compose, TRUE);
2653 compose_address_cb(NULL, compose);
2656 case A_CHECK_SPELLING:
2657 compose_check_all(NULL, compose);
2665 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2670 gchar *subject = NULL;
2674 gchar **attach = NULL;
2675 gchar *inreplyto = NULL;
2676 MailField mfield = NO_FIELD_PRESENT;
2678 /* get mailto parts but skip from */
2679 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2682 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2683 mfield = TO_FIELD_PRESENT;
2686 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2688 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2690 if (!g_utf8_validate (subject, -1, NULL)) {
2691 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2692 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2695 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2697 mfield = SUBJECT_FIELD_PRESENT;
2700 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2701 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2704 gboolean prev_autowrap = compose->autowrap;
2706 compose->autowrap = FALSE;
2708 mark = gtk_text_buffer_get_insert(buffer);
2709 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2711 if (!g_utf8_validate (body, -1, NULL)) {
2712 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2713 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2716 gtk_text_buffer_insert(buffer, &iter, body, -1);
2718 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2720 compose->autowrap = prev_autowrap;
2721 if (compose->autowrap)
2722 compose_wrap_all(compose);
2723 mfield = BODY_FIELD_PRESENT;
2727 gint i = 0, att = 0;
2728 gchar *warn_files = NULL;
2729 while (attach[i] != NULL) {
2730 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2731 if (utf8_filename) {
2732 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2733 gchar *tmp = g_strdup_printf("%s%s\n",
2734 warn_files?warn_files:"",
2740 g_free(utf8_filename);
2742 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2747 alertpanel_notice(ngettext(
2748 "The following file has been attached: \n%s",
2749 "The following files have been attached: \n%s", att), warn_files);
2754 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2767 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2769 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2770 {"Cc:", NULL, TRUE},
2771 {"References:", NULL, FALSE},
2772 {"Bcc:", NULL, TRUE},
2773 {"Newsgroups:", NULL, TRUE},
2774 {"Followup-To:", NULL, TRUE},
2775 {"List-Post:", NULL, FALSE},
2776 {"X-Priority:", NULL, FALSE},
2777 {NULL, NULL, FALSE}};
2793 cm_return_val_if_fail(msginfo != NULL, -1);
2795 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2796 procheader_get_header_fields(fp, hentry);
2799 if (hentry[H_REPLY_TO].body != NULL) {
2800 if (hentry[H_REPLY_TO].body[0] != '\0') {
2802 conv_unmime_header(hentry[H_REPLY_TO].body,
2805 g_free(hentry[H_REPLY_TO].body);
2806 hentry[H_REPLY_TO].body = NULL;
2808 if (hentry[H_CC].body != NULL) {
2809 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2810 g_free(hentry[H_CC].body);
2811 hentry[H_CC].body = NULL;
2813 if (hentry[H_REFERENCES].body != NULL) {
2814 if (compose->mode == COMPOSE_REEDIT)
2815 compose->references = hentry[H_REFERENCES].body;
2817 compose->references = compose_parse_references
2818 (hentry[H_REFERENCES].body, msginfo->msgid);
2819 g_free(hentry[H_REFERENCES].body);
2821 hentry[H_REFERENCES].body = NULL;
2823 if (hentry[H_BCC].body != NULL) {
2824 if (compose->mode == COMPOSE_REEDIT)
2826 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2827 g_free(hentry[H_BCC].body);
2828 hentry[H_BCC].body = NULL;
2830 if (hentry[H_NEWSGROUPS].body != NULL) {
2831 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2832 hentry[H_NEWSGROUPS].body = NULL;
2834 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2835 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2836 compose->followup_to =
2837 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2840 g_free(hentry[H_FOLLOWUP_TO].body);
2841 hentry[H_FOLLOWUP_TO].body = NULL;
2843 if (hentry[H_LIST_POST].body != NULL) {
2844 gchar *to = NULL, *start = NULL;
2846 extract_address(hentry[H_LIST_POST].body);
2847 if (hentry[H_LIST_POST].body[0] != '\0') {
2848 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2850 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2851 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2854 g_free(compose->ml_post);
2855 compose->ml_post = to;
2858 g_free(hentry[H_LIST_POST].body);
2859 hentry[H_LIST_POST].body = NULL;
2862 /* CLAWS - X-Priority */
2863 if (compose->mode == COMPOSE_REEDIT)
2864 if (hentry[H_X_PRIORITY].body != NULL) {
2867 priority = atoi(hentry[H_X_PRIORITY].body);
2868 g_free(hentry[H_X_PRIORITY].body);
2870 hentry[H_X_PRIORITY].body = NULL;
2872 if (priority < PRIORITY_HIGHEST ||
2873 priority > PRIORITY_LOWEST)
2874 priority = PRIORITY_NORMAL;
2876 compose->priority = priority;
2879 if (compose->mode == COMPOSE_REEDIT) {
2880 if (msginfo->inreplyto && *msginfo->inreplyto)
2881 compose->inreplyto = g_strdup(msginfo->inreplyto);
2885 if (msginfo->msgid && *msginfo->msgid)
2886 compose->inreplyto = g_strdup(msginfo->msgid);
2888 if (!compose->references) {
2889 if (msginfo->msgid && *msginfo->msgid) {
2890 if (msginfo->inreplyto && *msginfo->inreplyto)
2891 compose->references =
2892 g_strdup_printf("<%s>\n\t<%s>",
2896 compose->references =
2897 g_strconcat("<", msginfo->msgid, ">",
2899 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2900 compose->references =
2901 g_strconcat("<", msginfo->inreplyto, ">",
2909 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2914 cm_return_val_if_fail(msginfo != NULL, -1);
2916 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2917 procheader_get_header_fields(fp, entries);
2921 while (he != NULL && he->name != NULL) {
2923 GtkListStore *model = NULL;
2925 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2926 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2927 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2928 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2929 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2936 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2938 GSList *ref_id_list, *cur;
2942 ref_id_list = references_list_append(NULL, ref);
2943 if (!ref_id_list) return NULL;
2944 if (msgid && *msgid)
2945 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2950 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2951 /* "<" + Message-ID + ">" + CR+LF+TAB */
2952 len += strlen((gchar *)cur->data) + 5;
2954 if (len > MAX_REFERENCES_LEN) {
2955 /* remove second message-ID */
2956 if (ref_id_list && ref_id_list->next &&
2957 ref_id_list->next->next) {
2958 g_free(ref_id_list->next->data);
2959 ref_id_list = g_slist_remove
2960 (ref_id_list, ref_id_list->next->data);
2962 slist_free_strings(ref_id_list);
2963 g_slist_free(ref_id_list);
2970 new_ref = g_string_new("");
2971 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2972 if (new_ref->len > 0)
2973 g_string_append(new_ref, "\n\t");
2974 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2977 slist_free_strings(ref_id_list);
2978 g_slist_free(ref_id_list);
2980 new_ref_str = new_ref->str;
2981 g_string_free(new_ref, FALSE);
2986 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2987 const gchar *fmt, const gchar *qmark,
2988 const gchar *body, gboolean rewrap,
2989 gboolean need_unescape,
2990 const gchar *err_msg)
2992 MsgInfo* dummyinfo = NULL;
2993 gchar *quote_str = NULL;
2995 gboolean prev_autowrap;
2996 const gchar *trimmed_body = body;
2997 gint cursor_pos = -1;
2998 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2999 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3004 SIGNAL_BLOCK(buffer);
3007 dummyinfo = compose_msginfo_new_from_compose(compose);
3008 msginfo = dummyinfo;
3011 if (qmark != NULL) {
3013 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3014 compose->gtkaspell);
3016 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3018 quote_fmt_scan_string(qmark);
3021 buf = quote_fmt_get_buffer();
3023 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3025 Xstrdup_a(quote_str, buf, goto error)
3028 if (fmt && *fmt != '\0') {
3031 while (*trimmed_body == '\n')
3035 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3036 compose->gtkaspell);
3038 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3040 if (need_unescape) {
3043 /* decode \-escape sequences in the internal representation of the quote format */
3044 tmp = g_malloc(strlen(fmt)+1);
3045 pref_get_unescaped_pref(tmp, fmt);
3046 quote_fmt_scan_string(tmp);
3050 quote_fmt_scan_string(fmt);
3054 buf = quote_fmt_get_buffer();
3056 gint line = quote_fmt_get_line();
3057 alertpanel_error(err_msg, line);
3063 prev_autowrap = compose->autowrap;
3064 compose->autowrap = FALSE;
3066 mark = gtk_text_buffer_get_insert(buffer);
3067 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3068 if (g_utf8_validate(buf, -1, NULL)) {
3069 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3071 gchar *tmpout = NULL;
3072 tmpout = conv_codeset_strdup
3073 (buf, conv_get_locale_charset_str_no_utf8(),
3075 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3077 tmpout = g_malloc(strlen(buf)*2+1);
3078 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3080 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3084 cursor_pos = quote_fmt_get_cursor_pos();
3085 if (cursor_pos == -1)
3086 cursor_pos = gtk_text_iter_get_offset(&iter);
3087 compose->set_cursor_pos = cursor_pos;
3089 gtk_text_buffer_get_start_iter(buffer, &iter);
3090 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3091 gtk_text_buffer_place_cursor(buffer, &iter);
3093 compose->autowrap = prev_autowrap;
3094 if (compose->autowrap && rewrap)
3095 compose_wrap_all(compose);
3102 SIGNAL_UNBLOCK(buffer);
3104 procmsg_msginfo_free( dummyinfo );
3109 /* if ml_post is of type addr@host and from is of type
3110 * addr-anything@host, return TRUE
3112 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3114 gchar *left_ml = NULL;
3115 gchar *right_ml = NULL;
3116 gchar *left_from = NULL;
3117 gchar *right_from = NULL;
3118 gboolean result = FALSE;
3120 if (!ml_post || !from)
3123 left_ml = g_strdup(ml_post);
3124 if (strstr(left_ml, "@")) {
3125 right_ml = strstr(left_ml, "@")+1;
3126 *(strstr(left_ml, "@")) = '\0';
3129 left_from = g_strdup(from);
3130 if (strstr(left_from, "@")) {
3131 right_from = strstr(left_from, "@")+1;
3132 *(strstr(left_from, "@")) = '\0';
3135 if (left_ml && left_from && right_ml && right_from
3136 && !strncmp(left_from, left_ml, strlen(left_ml))
3137 && !strcmp(right_from, right_ml)) {
3146 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3147 gboolean respect_default_to)
3151 if (!folder || !folder->prefs)
3154 if (respect_default_to && folder->prefs->enable_default_to) {
3155 compose_entry_append(compose, folder->prefs->default_to,
3156 COMPOSE_TO, PREF_FOLDER);
3157 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3159 if (folder->prefs->enable_default_cc)
3160 compose_entry_append(compose, folder->prefs->default_cc,
3161 COMPOSE_CC, PREF_FOLDER);
3162 if (folder->prefs->enable_default_bcc)
3163 compose_entry_append(compose, folder->prefs->default_bcc,
3164 COMPOSE_BCC, PREF_FOLDER);
3165 if (folder->prefs->enable_default_replyto)
3166 compose_entry_append(compose, folder->prefs->default_replyto,
3167 COMPOSE_REPLYTO, PREF_FOLDER);
3170 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3175 if (!compose || !msginfo)
3178 if (msginfo->subject && *msginfo->subject) {
3179 buf = p = g_strdup(msginfo->subject);
3180 p += subject_get_prefix_length(p);
3181 memmove(buf, p, strlen(p) + 1);
3183 buf2 = g_strdup_printf("Re: %s", buf);
3184 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3189 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3192 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3193 gboolean to_all, gboolean to_ml,
3195 gboolean followup_and_reply_to)
3197 GSList *cc_list = NULL;
3200 gchar *replyto = NULL;
3201 gchar *ac_email = NULL;
3203 gboolean reply_to_ml = FALSE;
3204 gboolean default_reply_to = FALSE;
3206 cm_return_if_fail(compose->account != NULL);
3207 cm_return_if_fail(msginfo != NULL);
3209 reply_to_ml = to_ml && compose->ml_post;
3211 default_reply_to = msginfo->folder &&
3212 msginfo->folder->prefs->enable_default_reply_to;
3214 if (compose->account->protocol != A_NNTP) {
3215 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3217 if (reply_to_ml && !default_reply_to) {
3219 gboolean is_subscr = is_subscription(compose->ml_post,
3222 /* normal answer to ml post with a reply-to */
3223 compose_entry_append(compose,
3225 COMPOSE_TO, PREF_ML);
3226 if (compose->replyto)
3227 compose_entry_append(compose,
3229 COMPOSE_CC, PREF_ML);
3231 /* answer to subscription confirmation */
3232 if (compose->replyto)
3233 compose_entry_append(compose,
3235 COMPOSE_TO, PREF_ML);
3236 else if (msginfo->from)
3237 compose_entry_append(compose,
3239 COMPOSE_TO, PREF_ML);
3242 else if (!(to_all || to_sender) && default_reply_to) {
3243 compose_entry_append(compose,
3244 msginfo->folder->prefs->default_reply_to,
3245 COMPOSE_TO, PREF_FOLDER);
3246 compose_entry_mark_default_to(compose,
3247 msginfo->folder->prefs->default_reply_to);
3252 Xstrdup_a(tmp1, msginfo->from, return);
3253 extract_address(tmp1);
3254 if (to_all || to_sender ||
3255 !account_find_from_address(tmp1, FALSE))
3256 compose_entry_append(compose,
3257 (compose->replyto && !to_sender)
3258 ? compose->replyto :
3259 msginfo->from ? msginfo->from : "",
3260 COMPOSE_TO, PREF_NONE);
3261 else if (!to_all && !to_sender) {
3262 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3263 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3264 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3265 if (compose->replyto) {
3266 compose_entry_append(compose,
3268 COMPOSE_TO, PREF_NONE);
3270 compose_entry_append(compose,
3271 msginfo->from ? msginfo->from : "",
3272 COMPOSE_TO, PREF_NONE);
3275 /* replying to own mail, use original recp */
3276 compose_entry_append(compose,
3277 msginfo->to ? msginfo->to : "",
3278 COMPOSE_TO, PREF_NONE);
3279 compose_entry_append(compose,
3280 msginfo->cc ? msginfo->cc : "",
3281 COMPOSE_CC, PREF_NONE);
3286 if (to_sender || (compose->followup_to &&
3287 !strncmp(compose->followup_to, "poster", 6)))
3288 compose_entry_append
3290 (compose->replyto ? compose->replyto :
3291 msginfo->from ? msginfo->from : ""),
3292 COMPOSE_TO, PREF_NONE);
3294 else if (followup_and_reply_to || to_all) {
3295 compose_entry_append
3297 (compose->replyto ? compose->replyto :
3298 msginfo->from ? msginfo->from : ""),
3299 COMPOSE_TO, PREF_NONE);
3301 compose_entry_append
3303 compose->followup_to ? compose->followup_to :
3304 compose->newsgroups ? compose->newsgroups : "",
3305 COMPOSE_NEWSGROUPS, PREF_NONE);
3308 compose_entry_append
3310 compose->followup_to ? compose->followup_to :
3311 compose->newsgroups ? compose->newsgroups : "",
3312 COMPOSE_NEWSGROUPS, PREF_NONE);
3314 compose_reply_set_subject(compose, msginfo);
3316 if (to_ml && compose->ml_post) return;
3317 if (!to_all || compose->account->protocol == A_NNTP) return;
3319 if (compose->replyto) {
3320 Xstrdup_a(replyto, compose->replyto, return);
3321 extract_address(replyto);
3323 if (msginfo->from) {
3324 Xstrdup_a(from, msginfo->from, return);
3325 extract_address(from);
3328 if (replyto && from)
3329 cc_list = address_list_append_with_comments(cc_list, from);
3330 if (to_all && msginfo->folder &&
3331 msginfo->folder->prefs->enable_default_reply_to)
3332 cc_list = address_list_append_with_comments(cc_list,
3333 msginfo->folder->prefs->default_reply_to);
3334 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3335 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3337 ac_email = g_utf8_strdown(compose->account->address, -1);
3340 for (cur = cc_list; cur != NULL; cur = cur->next) {
3341 gchar *addr = g_utf8_strdown(cur->data, -1);
3342 extract_address(addr);
3344 if (strcmp(ac_email, addr))
3345 compose_entry_append(compose, (gchar *)cur->data,
3346 COMPOSE_CC, PREF_NONE);
3348 debug_print("Cc address same as compose account's, ignoring\n");
3353 slist_free_strings(cc_list);
3354 g_slist_free(cc_list);
3360 #define SET_ENTRY(entry, str) \
3363 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3366 #define SET_ADDRESS(type, str) \
3369 compose_entry_append(compose, str, type, PREF_NONE); \
3372 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3374 cm_return_if_fail(msginfo != NULL);
3376 SET_ENTRY(subject_entry, msginfo->subject);
3377 SET_ENTRY(from_name, msginfo->from);
3378 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3379 SET_ADDRESS(COMPOSE_CC, compose->cc);
3380 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3381 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3382 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3383 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3385 compose_update_priority_menu_item(compose);
3386 compose_update_privacy_system_menu_item(compose, FALSE);
3387 compose_show_first_last_header(compose, TRUE);
3393 static void compose_insert_sig(Compose *compose, gboolean replace)
3395 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3396 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3398 GtkTextIter iter, iter_end;
3399 gint cur_pos, ins_pos;
3400 gboolean prev_autowrap;
3401 gboolean found = FALSE;
3402 gboolean exists = FALSE;
3404 cm_return_if_fail(compose->account != NULL);
3408 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3409 G_CALLBACK(compose_changed_cb),
3412 mark = gtk_text_buffer_get_insert(buffer);
3413 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3414 cur_pos = gtk_text_iter_get_offset (&iter);
3417 gtk_text_buffer_get_end_iter(buffer, &iter);
3419 exists = (compose->sig_str != NULL);
3422 GtkTextIter first_iter, start_iter, end_iter;
3424 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3426 if (!exists || compose->sig_str[0] == '\0')
3429 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3430 compose->signature_tag);
3433 /* include previous \n\n */
3434 gtk_text_iter_backward_chars(&first_iter, 1);
3435 start_iter = first_iter;
3436 end_iter = first_iter;
3438 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3439 compose->signature_tag);
3440 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3441 compose->signature_tag);
3443 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3449 g_free(compose->sig_str);
3450 compose->sig_str = account_get_signature_str(compose->account);
3452 cur_pos = gtk_text_iter_get_offset(&iter);
3454 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3455 g_free(compose->sig_str);
3456 compose->sig_str = NULL;
3458 if (compose->sig_inserted == FALSE)
3459 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3460 compose->sig_inserted = TRUE;
3462 cur_pos = gtk_text_iter_get_offset(&iter);
3463 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3465 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3466 gtk_text_iter_forward_chars(&iter, 1);
3467 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3468 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3470 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3471 cur_pos = gtk_text_buffer_get_char_count (buffer);
3474 /* put the cursor where it should be
3475 * either where the quote_fmt says, either where it was */
3476 if (compose->set_cursor_pos < 0)
3477 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3479 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3480 compose->set_cursor_pos);
3482 compose->set_cursor_pos = -1;
3483 gtk_text_buffer_place_cursor(buffer, &iter);
3484 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3485 G_CALLBACK(compose_changed_cb),
3491 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3494 GtkTextBuffer *buffer;
3497 const gchar *cur_encoding;
3498 gchar buf[BUFFSIZE];
3501 gboolean prev_autowrap;
3502 gboolean badtxt = FALSE;
3503 struct stat file_stat;
3506 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3508 /* get the size of the file we are about to insert */
3509 ret = g_stat(file, &file_stat);
3511 gchar *shortfile = g_path_get_basename(file);
3512 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3514 return COMPOSE_INSERT_NO_FILE;
3515 } else if (prefs_common.warn_large_insert == TRUE) {
3517 /* ask user for confirmation if the file is large */
3518 if (prefs_common.warn_large_insert_size < 0 ||
3519 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3523 msg = g_strdup_printf(_("You are about to insert a file of %s "
3524 "in the message body. Are you sure you want to do that?"),
3525 to_human_readable(file_stat.st_size));
3526 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3527 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3530 /* do we ask for confirmation next time? */
3531 if (aval & G_ALERTDISABLE) {
3532 /* no confirmation next time, disable feature in preferences */
3533 aval &= ~G_ALERTDISABLE;
3534 prefs_common.warn_large_insert = FALSE;
3537 /* abort file insertion if user canceled action */
3538 if (aval != G_ALERTALTERNATE) {
3539 return COMPOSE_INSERT_NO_FILE;
3545 if ((fp = g_fopen(file, "rb")) == NULL) {
3546 FILE_OP_ERROR(file, "fopen");
3547 return COMPOSE_INSERT_READ_ERROR;
3550 prev_autowrap = compose->autowrap;
3551 compose->autowrap = FALSE;
3553 text = GTK_TEXT_VIEW(compose->text);
3554 buffer = gtk_text_view_get_buffer(text);
3555 mark = gtk_text_buffer_get_insert(buffer);
3556 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3558 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3559 G_CALLBACK(text_inserted),
3562 cur_encoding = conv_get_locale_charset_str_no_utf8();
3564 while (fgets(buf, sizeof(buf), fp) != NULL) {
3567 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3568 str = g_strdup(buf);
3570 str = conv_codeset_strdup
3571 (buf, cur_encoding, CS_INTERNAL);
3574 /* strip <CR> if DOS/Windows file,
3575 replace <CR> with <LF> if Macintosh file. */
3578 if (len > 0 && str[len - 1] != '\n') {
3580 if (str[len] == '\r') str[len] = '\n';
3583 gtk_text_buffer_insert(buffer, &iter, str, -1);
3587 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3588 G_CALLBACK(text_inserted),
3590 compose->autowrap = prev_autowrap;
3591 if (compose->autowrap)
3592 compose_wrap_all(compose);
3597 return COMPOSE_INSERT_INVALID_CHARACTER;
3599 return COMPOSE_INSERT_SUCCESS;
3602 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3603 const gchar *filename,
3604 const gchar *content_type,
3605 const gchar *charset)
3613 GtkListStore *store;
3615 gboolean has_binary = FALSE;
3617 if (!is_file_exist(file)) {
3618 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3619 gboolean result = FALSE;
3620 if (file_from_uri && is_file_exist(file_from_uri)) {
3621 result = compose_attach_append(
3622 compose, file_from_uri,
3623 filename, content_type,
3626 g_free(file_from_uri);
3629 alertpanel_error("File %s doesn't exist\n", filename);
3632 if ((size = get_file_size(file)) < 0) {
3633 alertpanel_error("Can't get file size of %s\n", filename);
3637 alertpanel_error(_("File %s is empty."), filename);
3640 if ((fp = g_fopen(file, "rb")) == NULL) {
3641 alertpanel_error(_("Can't read %s."), filename);
3646 ainfo = g_new0(AttachInfo, 1);
3647 auto_ainfo = g_auto_pointer_new_with_free
3648 (ainfo, (GFreeFunc) compose_attach_info_free);
3649 ainfo->file = g_strdup(file);
3652 ainfo->content_type = g_strdup(content_type);
3653 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3655 MsgFlags flags = {0, 0};
3657 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3658 ainfo->encoding = ENC_7BIT;
3660 ainfo->encoding = ENC_8BIT;
3662 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3663 if (msginfo && msginfo->subject)
3664 name = g_strdup(msginfo->subject);
3666 name = g_path_get_basename(filename ? filename : file);
3668 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3670 procmsg_msginfo_free(msginfo);
3672 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3673 ainfo->charset = g_strdup(charset);
3674 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3676 ainfo->encoding = ENC_BASE64;
3678 name = g_path_get_basename(filename ? filename : file);
3679 ainfo->name = g_strdup(name);
3683 ainfo->content_type = procmime_get_mime_type(file);
3684 if (!ainfo->content_type) {
3685 ainfo->content_type =
3686 g_strdup("application/octet-stream");
3687 ainfo->encoding = ENC_BASE64;
3688 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3690 procmime_get_encoding_for_text_file(file, &has_binary);
3692 ainfo->encoding = ENC_BASE64;
3693 name = g_path_get_basename(filename ? filename : file);
3694 ainfo->name = g_strdup(name);
3698 if (ainfo->name != NULL
3699 && !strcmp(ainfo->name, ".")) {
3700 g_free(ainfo->name);
3704 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3705 g_free(ainfo->content_type);
3706 ainfo->content_type = g_strdup("application/octet-stream");
3707 g_free(ainfo->charset);
3708 ainfo->charset = NULL;
3711 ainfo->size = (goffset)size;
3712 size_text = to_human_readable((goffset)size);
3714 store = GTK_LIST_STORE(gtk_tree_view_get_model
3715 (GTK_TREE_VIEW(compose->attach_clist)));
3717 gtk_list_store_append(store, &iter);
3718 gtk_list_store_set(store, &iter,
3719 COL_MIMETYPE, ainfo->content_type,
3720 COL_SIZE, size_text,
3721 COL_NAME, ainfo->name,
3722 COL_CHARSET, ainfo->charset,
3724 COL_AUTODATA, auto_ainfo,
3727 g_auto_pointer_free(auto_ainfo);
3728 compose_attach_update_label(compose);
3732 static void compose_use_signing(Compose *compose, gboolean use_signing)
3734 compose->use_signing = use_signing;
3735 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3738 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3740 compose->use_encryption = use_encryption;
3741 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3744 #define NEXT_PART_NOT_CHILD(info) \
3746 node = info->node; \
3747 while (node->children) \
3748 node = g_node_last_child(node); \
3749 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3752 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3756 MimeInfo *firsttext = NULL;
3757 MimeInfo *encrypted = NULL;
3760 const gchar *partname = NULL;
3762 mimeinfo = procmime_scan_message(msginfo);
3763 if (!mimeinfo) return;
3765 if (mimeinfo->node->children == NULL) {
3766 procmime_mimeinfo_free_all(mimeinfo);
3770 /* find first content part */
3771 child = (MimeInfo *) mimeinfo->node->children->data;
3772 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3773 child = (MimeInfo *)child->node->children->data;
3776 if (child->type == MIMETYPE_TEXT) {
3778 debug_print("First text part found\n");
3779 } else if (compose->mode == COMPOSE_REEDIT &&
3780 child->type == MIMETYPE_APPLICATION &&
3781 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3782 encrypted = (MimeInfo *)child->node->parent->data;
3785 child = (MimeInfo *) mimeinfo->node->children->data;
3786 while (child != NULL) {
3789 if (child == encrypted) {
3790 /* skip this part of tree */
3791 NEXT_PART_NOT_CHILD(child);
3795 if (child->type == MIMETYPE_MULTIPART) {
3796 /* get the actual content */
3797 child = procmime_mimeinfo_next(child);
3801 if (child == firsttext) {
3802 child = procmime_mimeinfo_next(child);
3806 outfile = procmime_get_tmp_file_name(child);
3807 if ((err = procmime_get_part(outfile, child)) < 0)
3808 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3810 gchar *content_type;
3812 content_type = procmime_get_content_type_str(child->type, child->subtype);
3814 /* if we meet a pgp signature, we don't attach it, but
3815 * we force signing. */
3816 if ((strcmp(content_type, "application/pgp-signature") &&
3817 strcmp(content_type, "application/pkcs7-signature") &&
3818 strcmp(content_type, "application/x-pkcs7-signature"))
3819 || compose->mode == COMPOSE_REDIRECT) {
3820 partname = procmime_mimeinfo_get_parameter(child, "filename");
3821 if (partname == NULL)
3822 partname = procmime_mimeinfo_get_parameter(child, "name");
3823 if (partname == NULL)
3825 compose_attach_append(compose, outfile,
3826 partname, content_type,
3827 procmime_mimeinfo_get_parameter(child, "charset"));
3829 compose_force_signing(compose, compose->account, NULL);
3831 g_free(content_type);
3834 NEXT_PART_NOT_CHILD(child);
3836 procmime_mimeinfo_free_all(mimeinfo);
3839 #undef NEXT_PART_NOT_CHILD
3844 WAIT_FOR_INDENT_CHAR,
3845 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3848 /* return indent length, we allow:
3849 indent characters followed by indent characters or spaces/tabs,
3850 alphabets and numbers immediately followed by indent characters,
3851 and the repeating sequences of the above
3852 If quote ends with multiple spaces, only the first one is included. */
3853 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3854 const GtkTextIter *start, gint *len)
3856 GtkTextIter iter = *start;
3860 IndentState state = WAIT_FOR_INDENT_CHAR;
3863 gint alnum_count = 0;
3864 gint space_count = 0;
3867 if (prefs_common.quote_chars == NULL) {
3871 while (!gtk_text_iter_ends_line(&iter)) {
3872 wc = gtk_text_iter_get_char(&iter);
3873 if (g_unichar_iswide(wc))
3875 clen = g_unichar_to_utf8(wc, ch);
3879 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3880 is_space = g_unichar_isspace(wc);
3882 if (state == WAIT_FOR_INDENT_CHAR) {
3883 if (!is_indent && !g_unichar_isalnum(wc))
3886 quote_len += alnum_count + space_count + 1;
3887 alnum_count = space_count = 0;
3888 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3891 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3892 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3896 else if (is_indent) {
3897 quote_len += alnum_count + space_count + 1;
3898 alnum_count = space_count = 0;
3901 state = WAIT_FOR_INDENT_CHAR;
3905 gtk_text_iter_forward_char(&iter);
3908 if (quote_len > 0 && space_count > 0)
3914 if (quote_len > 0) {
3916 gtk_text_iter_forward_chars(&iter, quote_len);
3917 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3923 /* return >0 if the line is itemized */
3924 static int compose_itemized_length(GtkTextBuffer *buffer,
3925 const GtkTextIter *start)
3927 GtkTextIter iter = *start;
3932 if (gtk_text_iter_ends_line(&iter))
3937 wc = gtk_text_iter_get_char(&iter);
3938 if (!g_unichar_isspace(wc))
3940 gtk_text_iter_forward_char(&iter);
3941 if (gtk_text_iter_ends_line(&iter))
3945 clen = g_unichar_to_utf8(wc, ch);
3949 if (!strchr("*-+", ch[0]))
3952 gtk_text_iter_forward_char(&iter);
3953 if (gtk_text_iter_ends_line(&iter))
3955 wc = gtk_text_iter_get_char(&iter);
3956 if (g_unichar_isspace(wc)) {
3962 /* return the string at the start of the itemization */
3963 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3964 const GtkTextIter *start)
3966 GtkTextIter iter = *start;
3969 GString *item_chars = g_string_new("");
3972 if (gtk_text_iter_ends_line(&iter))
3977 wc = gtk_text_iter_get_char(&iter);
3978 if (!g_unichar_isspace(wc))
3980 gtk_text_iter_forward_char(&iter);
3981 if (gtk_text_iter_ends_line(&iter))
3983 g_string_append_unichar(item_chars, wc);
3986 str = item_chars->str;
3987 g_string_free(item_chars, FALSE);
3991 /* return the number of spaces at a line's start */
3992 static int compose_left_offset_length(GtkTextBuffer *buffer,
3993 const GtkTextIter *start)
3995 GtkTextIter iter = *start;
3998 if (gtk_text_iter_ends_line(&iter))
4002 wc = gtk_text_iter_get_char(&iter);
4003 if (!g_unichar_isspace(wc))
4006 gtk_text_iter_forward_char(&iter);
4007 if (gtk_text_iter_ends_line(&iter))
4011 gtk_text_iter_forward_char(&iter);
4012 if (gtk_text_iter_ends_line(&iter))
4017 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4018 const GtkTextIter *start,
4019 GtkTextIter *break_pos,
4023 GtkTextIter iter = *start, line_end = *start;
4024 PangoLogAttr *attrs;
4031 gboolean can_break = FALSE;
4032 gboolean do_break = FALSE;
4033 gboolean was_white = FALSE;
4034 gboolean prev_dont_break = FALSE;
4036 gtk_text_iter_forward_to_line_end(&line_end);
4037 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4038 len = g_utf8_strlen(str, -1);
4042 g_warning("compose_get_line_break_pos: len = 0!\n");
4046 /* g_print("breaking line: %d: %s (len = %d)\n",
4047 gtk_text_iter_get_line(&iter), str, len); */
4049 attrs = g_new(PangoLogAttr, len + 1);
4051 pango_default_break(str, -1, NULL, attrs, len + 1);
4055 /* skip quote and leading spaces */
4056 for (i = 0; *p != '\0' && i < len; i++) {
4059 wc = g_utf8_get_char(p);
4060 if (i >= quote_len && !g_unichar_isspace(wc))
4062 if (g_unichar_iswide(wc))
4064 else if (*p == '\t')
4068 p = g_utf8_next_char(p);
4071 for (; *p != '\0' && i < len; i++) {
4072 PangoLogAttr *attr = attrs + i;
4076 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4079 was_white = attr->is_white;
4081 /* don't wrap URI */
4082 if ((uri_len = get_uri_len(p)) > 0) {
4084 if (pos > 0 && col > max_col) {
4094 wc = g_utf8_get_char(p);
4095 if (g_unichar_iswide(wc)) {
4097 if (prev_dont_break && can_break && attr->is_line_break)
4099 } else if (*p == '\t')
4103 if (pos > 0 && col > max_col) {
4108 if (*p == '-' || *p == '/')
4109 prev_dont_break = TRUE;
4111 prev_dont_break = FALSE;
4113 p = g_utf8_next_char(p);
4117 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4122 *break_pos = *start;
4123 gtk_text_iter_set_line_offset(break_pos, pos);
4128 static gboolean compose_join_next_line(Compose *compose,
4129 GtkTextBuffer *buffer,
4131 const gchar *quote_str)
4133 GtkTextIter iter_ = *iter, cur, prev, next, end;
4134 PangoLogAttr attrs[3];
4136 gchar *next_quote_str;
4139 gboolean keep_cursor = FALSE;
4141 if (!gtk_text_iter_forward_line(&iter_) ||
4142 gtk_text_iter_ends_line(&iter_)) {
4145 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4147 if ((quote_str || next_quote_str) &&
4148 strcmp2(quote_str, next_quote_str) != 0) {
4149 g_free(next_quote_str);
4152 g_free(next_quote_str);
4155 if (quote_len > 0) {
4156 gtk_text_iter_forward_chars(&end, quote_len);
4157 if (gtk_text_iter_ends_line(&end)) {
4162 /* don't join itemized lines */
4163 if (compose_itemized_length(buffer, &end) > 0) {
4167 /* don't join signature separator */
4168 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4171 /* delete quote str */
4173 gtk_text_buffer_delete(buffer, &iter_, &end);
4175 /* don't join line breaks put by the user */
4177 gtk_text_iter_backward_char(&cur);
4178 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4179 gtk_text_iter_forward_char(&cur);
4183 gtk_text_iter_forward_char(&cur);
4184 /* delete linebreak and extra spaces */
4185 while (gtk_text_iter_backward_char(&cur)) {
4186 wc1 = gtk_text_iter_get_char(&cur);
4187 if (!g_unichar_isspace(wc1))
4192 while (!gtk_text_iter_ends_line(&cur)) {
4193 wc1 = gtk_text_iter_get_char(&cur);
4194 if (!g_unichar_isspace(wc1))
4196 gtk_text_iter_forward_char(&cur);
4199 if (!gtk_text_iter_equal(&prev, &next)) {
4202 mark = gtk_text_buffer_get_insert(buffer);
4203 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4204 if (gtk_text_iter_equal(&prev, &cur))
4206 gtk_text_buffer_delete(buffer, &prev, &next);
4210 /* insert space if required */
4211 gtk_text_iter_backward_char(&prev);
4212 wc1 = gtk_text_iter_get_char(&prev);
4213 wc2 = gtk_text_iter_get_char(&next);
4214 gtk_text_iter_forward_char(&next);
4215 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4216 pango_default_break(str, -1, NULL, attrs, 3);
4217 if (!attrs[1].is_line_break ||
4218 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4219 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4221 gtk_text_iter_backward_char(&iter_);
4222 gtk_text_buffer_place_cursor(buffer, &iter_);
4231 #define ADD_TXT_POS(bp_, ep_, pti_) \
4232 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4233 last = last->next; \
4234 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4235 last->next = NULL; \
4237 g_warning("alloc error scanning URIs\n"); \
4240 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4242 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4243 GtkTextBuffer *buffer;
4244 GtkTextIter iter, break_pos, end_of_line;
4245 gchar *quote_str = NULL;
4247 gboolean wrap_quote = prefs_common.linewrap_quote;
4248 gboolean prev_autowrap = compose->autowrap;
4249 gint startq_offset = -1, noq_offset = -1;
4250 gint uri_start = -1, uri_stop = -1;
4251 gint nouri_start = -1, nouri_stop = -1;
4252 gint num_blocks = 0;
4253 gint quotelevel = -1;
4254 gboolean modified = force;
4255 gboolean removed = FALSE;
4256 gboolean modified_before_remove = FALSE;
4258 gboolean start = TRUE;
4259 gint itemized_len = 0, rem_item_len = 0;
4260 gchar *itemized_chars = NULL;
4261 gboolean item_continuation = FALSE;
4266 if (compose->draft_timeout_tag == -2) {
4270 compose->autowrap = FALSE;
4272 buffer = gtk_text_view_get_buffer(text);
4273 undo_wrapping(compose->undostruct, TRUE);
4278 mark = gtk_text_buffer_get_insert(buffer);
4279 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4283 if (compose->draft_timeout_tag == -2) {
4284 if (gtk_text_iter_ends_line(&iter)) {
4285 while (gtk_text_iter_ends_line(&iter) &&
4286 gtk_text_iter_forward_line(&iter))
4289 while (gtk_text_iter_backward_line(&iter)) {
4290 if (gtk_text_iter_ends_line(&iter)) {
4291 gtk_text_iter_forward_line(&iter);
4297 /* move to line start */
4298 gtk_text_iter_set_line_offset(&iter, 0);
4301 itemized_len = compose_itemized_length(buffer, &iter);
4303 if (!itemized_len) {
4304 itemized_len = compose_left_offset_length(buffer, &iter);
4305 item_continuation = TRUE;
4309 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4311 /* go until paragraph end (empty line) */
4312 while (start || !gtk_text_iter_ends_line(&iter)) {
4313 gchar *scanpos = NULL;
4314 /* parse table - in order of priority */
4316 const gchar *needle; /* token */
4318 /* token search function */
4319 gchar *(*search) (const gchar *haystack,
4320 const gchar *needle);
4321 /* part parsing function */
4322 gboolean (*parse) (const gchar *start,
4323 const gchar *scanpos,
4327 /* part to URI function */
4328 gchar *(*build_uri) (const gchar *bp,
4332 static struct table parser[] = {
4333 {"http://", strcasestr, get_uri_part, make_uri_string},
4334 {"https://", strcasestr, get_uri_part, make_uri_string},
4335 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4336 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4337 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4338 {"www.", strcasestr, get_uri_part, make_http_string},
4339 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4340 {"@", strcasestr, get_email_part, make_email_string}
4342 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4343 gint last_index = PARSE_ELEMS;
4345 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4349 if (!prev_autowrap && num_blocks == 0) {
4351 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4352 G_CALLBACK(text_inserted),
4355 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4358 uri_start = uri_stop = -1;
4360 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4363 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4364 if (startq_offset == -1)
4365 startq_offset = gtk_text_iter_get_offset(&iter);
4366 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4367 if (quotelevel > 2) {
4368 /* recycle colors */
4369 if (prefs_common.recycle_quote_colors)
4378 if (startq_offset == -1)
4379 noq_offset = gtk_text_iter_get_offset(&iter);
4383 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4386 if (gtk_text_iter_ends_line(&iter)) {
4388 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4389 prefs_common.linewrap_len,
4391 GtkTextIter prev, next, cur;
4392 if (prev_autowrap != FALSE || force) {
4393 compose->automatic_break = TRUE;
4395 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4396 compose->automatic_break = FALSE;
4397 if (itemized_len && compose->autoindent) {
4398 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4399 if (!item_continuation)
4400 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4402 } else if (quote_str && wrap_quote) {
4403 compose->automatic_break = TRUE;
4405 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4406 compose->automatic_break = FALSE;
4407 if (itemized_len && compose->autoindent) {
4408 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4409 if (!item_continuation)
4410 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4414 /* remove trailing spaces */
4416 rem_item_len = itemized_len;
4417 while (compose->autoindent && rem_item_len-- > 0)
4418 gtk_text_iter_backward_char(&cur);
4419 gtk_text_iter_backward_char(&cur);
4422 while (!gtk_text_iter_starts_line(&cur)) {
4425 gtk_text_iter_backward_char(&cur);
4426 wc = gtk_text_iter_get_char(&cur);
4427 if (!g_unichar_isspace(wc))
4431 if (!gtk_text_iter_equal(&prev, &next)) {
4432 gtk_text_buffer_delete(buffer, &prev, &next);
4434 gtk_text_iter_forward_char(&break_pos);
4438 gtk_text_buffer_insert(buffer, &break_pos,
4442 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4444 /* move iter to current line start */
4445 gtk_text_iter_set_line_offset(&iter, 0);
4452 /* move iter to next line start */
4458 if (!prev_autowrap && num_blocks > 0) {
4460 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4461 G_CALLBACK(text_inserted),
4465 while (!gtk_text_iter_ends_line(&end_of_line)) {
4466 gtk_text_iter_forward_char(&end_of_line);
4468 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4470 nouri_start = gtk_text_iter_get_offset(&iter);
4471 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4473 walk_pos = gtk_text_iter_get_offset(&iter);
4474 /* FIXME: this looks phony. scanning for anything in the parse table */
4475 for (n = 0; n < PARSE_ELEMS; n++) {
4478 tmp = parser[n].search(walk, parser[n].needle);
4480 if (scanpos == NULL || tmp < scanpos) {
4489 /* check if URI can be parsed */
4490 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4491 (const gchar **)&ep, FALSE)
4492 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4496 strlen(parser[last_index].needle);
4499 uri_start = walk_pos + (bp - o_walk);
4500 uri_stop = walk_pos + (ep - o_walk);
4504 gtk_text_iter_forward_line(&iter);
4507 if (startq_offset != -1) {
4508 GtkTextIter startquote, endquote;
4509 gtk_text_buffer_get_iter_at_offset(
4510 buffer, &startquote, startq_offset);
4513 switch (quotelevel) {
4515 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4516 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4517 gtk_text_buffer_apply_tag_by_name(
4518 buffer, "quote0", &startquote, &endquote);
4519 gtk_text_buffer_remove_tag_by_name(
4520 buffer, "quote1", &startquote, &endquote);
4521 gtk_text_buffer_remove_tag_by_name(
4522 buffer, "quote2", &startquote, &endquote);
4527 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4528 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4529 gtk_text_buffer_apply_tag_by_name(
4530 buffer, "quote1", &startquote, &endquote);
4531 gtk_text_buffer_remove_tag_by_name(
4532 buffer, "quote0", &startquote, &endquote);
4533 gtk_text_buffer_remove_tag_by_name(
4534 buffer, "quote2", &startquote, &endquote);
4539 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4540 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4541 gtk_text_buffer_apply_tag_by_name(
4542 buffer, "quote2", &startquote, &endquote);
4543 gtk_text_buffer_remove_tag_by_name(
4544 buffer, "quote0", &startquote, &endquote);
4545 gtk_text_buffer_remove_tag_by_name(
4546 buffer, "quote1", &startquote, &endquote);
4552 } else if (noq_offset != -1) {
4553 GtkTextIter startnoquote, endnoquote;
4554 gtk_text_buffer_get_iter_at_offset(
4555 buffer, &startnoquote, noq_offset);
4558 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4559 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4560 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4561 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4562 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4563 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4564 gtk_text_buffer_remove_tag_by_name(
4565 buffer, "quote0", &startnoquote, &endnoquote);
4566 gtk_text_buffer_remove_tag_by_name(
4567 buffer, "quote1", &startnoquote, &endnoquote);
4568 gtk_text_buffer_remove_tag_by_name(
4569 buffer, "quote2", &startnoquote, &endnoquote);
4575 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4576 GtkTextIter nouri_start_iter, nouri_end_iter;
4577 gtk_text_buffer_get_iter_at_offset(
4578 buffer, &nouri_start_iter, nouri_start);
4579 gtk_text_buffer_get_iter_at_offset(
4580 buffer, &nouri_end_iter, nouri_stop);
4581 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4582 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4583 gtk_text_buffer_remove_tag_by_name(
4584 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4585 modified_before_remove = modified;
4590 if (uri_start >= 0 && uri_stop > 0) {
4591 GtkTextIter uri_start_iter, uri_end_iter, back;
4592 gtk_text_buffer_get_iter_at_offset(
4593 buffer, &uri_start_iter, uri_start);
4594 gtk_text_buffer_get_iter_at_offset(
4595 buffer, &uri_end_iter, uri_stop);
4596 back = uri_end_iter;
4597 gtk_text_iter_backward_char(&back);
4598 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4599 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4600 gtk_text_buffer_apply_tag_by_name(
4601 buffer, "link", &uri_start_iter, &uri_end_iter);
4603 if (removed && !modified_before_remove) {
4609 // debug_print("not modified, out after %d lines\n", lines);
4613 // debug_print("modified, out after %d lines\n", lines);
4615 g_free(itemized_chars);
4618 undo_wrapping(compose->undostruct, FALSE);
4619 compose->autowrap = prev_autowrap;
4624 void compose_action_cb(void *data)
4626 Compose *compose = (Compose *)data;
4627 compose_wrap_all(compose);
4630 static void compose_wrap_all(Compose *compose)
4632 compose_wrap_all_full(compose, FALSE);
4635 static void compose_wrap_all_full(Compose *compose, gboolean force)
4637 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4638 GtkTextBuffer *buffer;
4640 gboolean modified = TRUE;
4642 buffer = gtk_text_view_get_buffer(text);
4644 gtk_text_buffer_get_start_iter(buffer, &iter);
4645 while (!gtk_text_iter_is_end(&iter) && modified)
4646 modified = compose_beautify_paragraph(compose, &iter, force);
4650 static void compose_set_title(Compose *compose)
4656 edited = compose->modified ? _(" [Edited]") : "";
4658 subject = gtk_editable_get_chars(
4659 GTK_EDITABLE(compose->subject_entry), 0, -1);
4661 #ifndef GENERIC_UMPC
4662 if (subject && strlen(subject))
4663 str = g_strdup_printf(_("%s - Compose message%s"),
4666 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4668 str = g_strdup(_("Compose message"));
4671 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4677 * compose_current_mail_account:
4679 * Find a current mail account (the currently selected account, or the
4680 * default account, if a news account is currently selected). If a
4681 * mail account cannot be found, display an error message.
4683 * Return value: Mail account, or NULL if not found.
4685 static PrefsAccount *
4686 compose_current_mail_account(void)
4690 if (cur_account && cur_account->protocol != A_NNTP)
4693 ac = account_get_default();
4694 if (!ac || ac->protocol == A_NNTP) {
4695 alertpanel_error(_("Account for sending mail is not specified.\n"
4696 "Please select a mail account before sending."));
4703 #define QUOTE_IF_REQUIRED(out, str) \
4705 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4709 len = strlen(str) + 3; \
4710 if ((__tmp = alloca(len)) == NULL) { \
4711 g_warning("can't allocate memory\n"); \
4712 g_string_free(header, TRUE); \
4715 g_snprintf(__tmp, len, "\"%s\"", str); \
4720 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4721 g_warning("can't allocate memory\n"); \
4722 g_string_free(header, TRUE); \
4725 strcpy(__tmp, str); \
4731 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4733 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4737 len = strlen(str) + 3; \
4738 if ((__tmp = alloca(len)) == NULL) { \
4739 g_warning("can't allocate memory\n"); \
4742 g_snprintf(__tmp, len, "\"%s\"", str); \
4747 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4748 g_warning("can't allocate memory\n"); \
4751 strcpy(__tmp, str); \
4757 static void compose_select_account(Compose *compose, PrefsAccount *account,
4760 gchar *from = NULL, *header;
4761 ComposeHeaderEntry *header_entry;
4763 cm_return_if_fail(account != NULL);
4765 compose->account = account;
4766 if (account->name && *account->name) {
4768 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4769 from = g_strdup_printf("%s <%s>",
4770 buf, account->address);
4771 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4773 from = g_strdup_printf("<%s>",
4775 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4780 compose_set_title(compose);
4782 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4783 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4785 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4786 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4787 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4789 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4791 activate_privacy_system(compose, account, FALSE);
4793 if (!init && compose->mode != COMPOSE_REDIRECT) {
4794 undo_block(compose->undostruct);
4795 compose_insert_sig(compose, TRUE);
4796 undo_unblock(compose->undostruct);
4799 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4800 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4802 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4803 if (account->protocol == A_NNTP) {
4804 if (!strcmp(header, _("To:")))
4805 combobox_select_by_text(
4806 GTK_COMBO_BOX(header_entry->combo),
4809 if (!strcmp(header, _("Newsgroups:")))
4810 combobox_select_by_text(
4811 GTK_COMBO_BOX(header_entry->combo),
4819 /* use account's dict info if set */
4820 if (compose->gtkaspell) {
4821 if (account->enable_default_dictionary)
4822 gtkaspell_change_dict(compose->gtkaspell,
4823 account->default_dictionary, FALSE);
4824 if (account->enable_default_alt_dictionary)
4825 gtkaspell_change_alt_dict(compose->gtkaspell,
4826 account->default_alt_dictionary);
4827 if (account->enable_default_dictionary
4828 || account->enable_default_alt_dictionary)
4829 compose_spell_menu_changed(compose);
4834 gboolean compose_check_for_valid_recipient(Compose *compose) {
4835 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4836 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4837 gboolean recipient_found = FALSE;
4841 /* free to and newsgroup list */
4842 slist_free_strings(compose->to_list);
4843 g_slist_free(compose->to_list);
4844 compose->to_list = NULL;
4846 slist_free_strings(compose->newsgroup_list);
4847 g_slist_free(compose->newsgroup_list);
4848 compose->newsgroup_list = NULL;
4850 /* search header entries for to and newsgroup entries */
4851 for (list = compose->header_list; list; list = list->next) {
4854 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4855 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4858 if (entry[0] != '\0') {
4859 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4860 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4861 compose->to_list = address_list_append(compose->to_list, entry);
4862 recipient_found = TRUE;
4865 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4866 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4867 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4868 recipient_found = TRUE;
4875 return recipient_found;
4878 static gboolean compose_check_for_set_recipients(Compose *compose)
4880 if (compose->account->set_autocc && compose->account->auto_cc) {
4881 gboolean found_other = FALSE;
4883 /* search header entries for to and newsgroup entries */
4884 for (list = compose->header_list; list; list = list->next) {
4887 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4888 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4891 if (strcmp(entry, compose->account->auto_cc)
4892 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4902 if (compose->batch) {
4903 gtk_widget_show_all(compose->window);
4905 aval = alertpanel(_("Send"),
4906 _("The only recipient is the default CC address. Send anyway?"),
4907 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4908 if (aval != G_ALERTALTERNATE)
4912 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4913 gboolean found_other = FALSE;
4915 /* search header entries for to and newsgroup entries */
4916 for (list = compose->header_list; list; list = list->next) {
4919 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4920 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4923 if (strcmp(entry, compose->account->auto_bcc)
4924 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4934 if (compose->batch) {
4935 gtk_widget_show_all(compose->window);
4937 aval = alertpanel(_("Send"),
4938 _("The only recipient is the default BCC address. Send anyway?"),
4939 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4940 if (aval != G_ALERTALTERNATE)
4947 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4951 if (compose_check_for_valid_recipient(compose) == FALSE) {
4952 if (compose->batch) {
4953 gtk_widget_show_all(compose->window);
4955 alertpanel_error(_("Recipient is not specified."));
4959 if (compose_check_for_set_recipients(compose) == FALSE) {
4963 if (!compose->batch) {
4964 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4965 if (*str == '\0' && check_everything == TRUE &&
4966 compose->mode != COMPOSE_REDIRECT) {
4968 gchar *button_label;
4971 if (compose->sending)
4972 button_label = _("+_Send");
4974 button_label = _("+_Queue");
4975 message = g_strdup_printf(_("Subject is empty. %s"),
4976 compose->sending?_("Send it anyway?"):
4977 _("Queue it anyway?"));
4979 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4980 GTK_STOCK_CANCEL, button_label, NULL);
4982 if (aval != G_ALERTALTERNATE)
4987 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4993 gint compose_send(Compose *compose)
4996 FolderItem *folder = NULL;
4998 gchar *msgpath = NULL;
4999 gboolean discard_window = FALSE;
5000 gchar *errstr = NULL;
5001 gchar *tmsgid = NULL;
5002 MainWindow *mainwin = mainwindow_get_mainwindow();
5003 gboolean queued_removed = FALSE;
5005 if (prefs_common.send_dialog_invisible
5006 || compose->batch == TRUE)
5007 discard_window = TRUE;
5009 compose_allow_user_actions (compose, FALSE);
5010 compose->sending = TRUE;
5012 if (compose_check_entries(compose, TRUE) == FALSE) {
5013 if (compose->batch) {
5014 gtk_widget_show_all(compose->window);
5020 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5023 if (compose->batch) {
5024 gtk_widget_show_all(compose->window);
5027 alertpanel_error(_("Could not queue message for sending:\n\n"
5028 "Charset conversion failed."));
5029 } else if (val == -5) {
5030 alertpanel_error(_("Could not queue message for sending:\n\n"
5031 "Couldn't get recipient encryption key."));
5032 } else if (val == -6) {
5034 } else if (val == -3) {
5035 if (privacy_peek_error())
5036 alertpanel_error(_("Could not queue message for sending:\n\n"
5037 "Signature failed: %s"), privacy_get_error());
5038 } else if (val == -2 && errno != 0) {
5039 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5041 alertpanel_error(_("Could not queue message for sending."));
5046 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5047 if (discard_window) {
5048 compose->sending = FALSE;
5049 compose_close(compose);
5050 /* No more compose access in the normal codepath
5051 * after this point! */
5056 alertpanel_error(_("The message was queued but could not be "
5057 "sent.\nUse \"Send queued messages\" from "
5058 "the main window to retry."));
5059 if (!discard_window) {
5066 if (msgpath == NULL) {
5067 msgpath = folder_item_fetch_msg(folder, msgnum);
5068 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5071 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5072 claws_unlink(msgpath);
5075 if (!discard_window) {
5077 if (!queued_removed)
5078 folder_item_remove_msg(folder, msgnum);
5079 folder_item_scan(folder);
5081 /* make sure we delete that */
5082 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5084 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5085 folder_item_remove_msg(folder, tmp->msgnum);
5086 procmsg_msginfo_free(tmp);
5093 if (!queued_removed)
5094 folder_item_remove_msg(folder, msgnum);
5095 folder_item_scan(folder);
5097 /* make sure we delete that */
5098 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5100 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5101 folder_item_remove_msg(folder, tmp->msgnum);
5102 procmsg_msginfo_free(tmp);
5105 if (!discard_window) {
5106 compose->sending = FALSE;
5107 compose_allow_user_actions (compose, TRUE);
5108 compose_close(compose);
5112 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5113 "the main window to retry."), errstr);
5116 alertpanel_error_log(_("The message was queued but could not be "
5117 "sent.\nUse \"Send queued messages\" from "
5118 "the main window to retry."));
5120 if (!discard_window) {
5129 toolbar_main_set_sensitive(mainwin);
5130 main_window_set_menu_sensitive(mainwin);
5136 compose_allow_user_actions (compose, TRUE);
5137 compose->sending = FALSE;
5138 compose->modified = TRUE;
5139 toolbar_main_set_sensitive(mainwin);
5140 main_window_set_menu_sensitive(mainwin);
5145 static gboolean compose_use_attach(Compose *compose)
5147 GtkTreeModel *model = gtk_tree_view_get_model
5148 (GTK_TREE_VIEW(compose->attach_clist));
5149 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5152 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5155 gchar buf[BUFFSIZE];
5157 gboolean first_to_address;
5158 gboolean first_cc_address;
5160 ComposeHeaderEntry *headerentry;
5161 const gchar *headerentryname;
5162 const gchar *cc_hdr;
5163 const gchar *to_hdr;
5164 gboolean err = FALSE;
5166 debug_print("Writing redirect header\n");
5168 cc_hdr = prefs_common_translated_header_name("Cc:");
5169 to_hdr = prefs_common_translated_header_name("To:");
5171 first_to_address = TRUE;
5172 for (list = compose->header_list; list; list = list->next) {
5173 headerentry = ((ComposeHeaderEntry *)list->data);
5174 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5176 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5177 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5178 Xstrdup_a(str, entstr, return -1);
5180 if (str[0] != '\0') {
5181 compose_convert_header
5182 (compose, buf, sizeof(buf), str,
5183 strlen("Resent-To") + 2, TRUE);
5185 if (first_to_address) {
5186 err |= (fprintf(fp, "Resent-To: ") < 0);
5187 first_to_address = FALSE;
5189 err |= (fprintf(fp, ",") < 0);
5191 err |= (fprintf(fp, "%s", buf) < 0);
5195 if (!first_to_address) {
5196 err |= (fprintf(fp, "\n") < 0);
5199 first_cc_address = TRUE;
5200 for (list = compose->header_list; list; list = list->next) {
5201 headerentry = ((ComposeHeaderEntry *)list->data);
5202 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5204 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5205 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5206 Xstrdup_a(str, strg, return -1);
5208 if (str[0] != '\0') {
5209 compose_convert_header
5210 (compose, buf, sizeof(buf), str,
5211 strlen("Resent-Cc") + 2, TRUE);
5213 if (first_cc_address) {
5214 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5215 first_cc_address = FALSE;
5217 err |= (fprintf(fp, ",") < 0);
5219 err |= (fprintf(fp, "%s", buf) < 0);
5223 if (!first_cc_address) {
5224 err |= (fprintf(fp, "\n") < 0);
5227 return (err ? -1:0);
5230 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5232 gchar buf[BUFFSIZE];
5234 const gchar *entstr;
5235 /* struct utsname utsbuf; */
5236 gboolean err = FALSE;
5238 cm_return_val_if_fail(fp != NULL, -1);
5239 cm_return_val_if_fail(compose->account != NULL, -1);
5240 cm_return_val_if_fail(compose->account->address != NULL, -1);
5243 get_rfc822_date(buf, sizeof(buf));
5244 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5247 if (compose->account->name && *compose->account->name) {
5248 compose_convert_header
5249 (compose, buf, sizeof(buf), compose->account->name,
5250 strlen("From: "), TRUE);
5251 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5252 buf, compose->account->address) < 0);
5254 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5257 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5258 if (*entstr != '\0') {
5259 Xstrdup_a(str, entstr, return -1);
5262 compose_convert_header(compose, buf, sizeof(buf), str,
5263 strlen("Subject: "), FALSE);
5264 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5268 /* Resent-Message-ID */
5269 if (compose->account->set_domain && compose->account->domain) {
5270 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5271 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5272 g_snprintf(buf, sizeof(buf), "%s",
5273 strchr(compose->account->address, '@') ?
5274 strchr(compose->account->address, '@')+1 :
5275 compose->account->address);
5277 g_snprintf(buf, sizeof(buf), "%s", "");
5280 if (compose->account->gen_msgid) {
5282 if (compose->account->msgid_with_addr) {
5283 addr = compose->account->address;
5285 generate_msgid(buf, sizeof(buf), addr);
5286 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5287 compose->msgid = g_strdup(buf);
5289 compose->msgid = NULL;
5292 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5295 /* separator between header and body */
5296 err |= (fputs("\n", fp) == EOF);
5298 return (err ? -1:0);
5301 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5305 gchar buf[BUFFSIZE];
5307 gboolean skip = FALSE;
5308 gboolean err = FALSE;
5309 gchar *not_included[]={
5310 "Return-Path:", "Delivered-To:", "Received:",
5311 "Subject:", "X-UIDL:", "AF:",
5312 "NF:", "PS:", "SRH:",
5313 "SFN:", "DSR:", "MID:",
5314 "CFG:", "PT:", "S:",
5315 "RQ:", "SSV:", "NSV:",
5316 "SSH:", "R:", "MAID:",
5317 "NAID:", "RMID:", "FMID:",
5318 "SCF:", "RRCPT:", "NG:",
5319 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5320 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5321 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5322 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5323 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5326 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5327 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5331 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5333 for (i = 0; not_included[i] != NULL; i++) {
5334 if (g_ascii_strncasecmp(buf, not_included[i],
5335 strlen(not_included[i])) == 0) {
5342 if (fputs(buf, fdest) == -1)
5345 if (!prefs_common.redirect_keep_from) {
5346 if (g_ascii_strncasecmp(buf, "From:",
5347 strlen("From:")) == 0) {
5348 err |= (fputs(" (by way of ", fdest) == EOF);
5349 if (compose->account->name
5350 && *compose->account->name) {
5351 compose_convert_header
5352 (compose, buf, sizeof(buf),
5353 compose->account->name,
5356 err |= (fprintf(fdest, "%s <%s>",
5358 compose->account->address) < 0);
5360 err |= (fprintf(fdest, "%s",
5361 compose->account->address) < 0);
5362 err |= (fputs(")", fdest) == EOF);
5366 if (fputs("\n", fdest) == -1)
5373 if (compose_redirect_write_headers(compose, fdest))
5376 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5377 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5390 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5392 GtkTextBuffer *buffer;
5393 GtkTextIter start, end;
5396 const gchar *out_codeset;
5397 EncodingType encoding = ENC_UNKNOWN;
5398 MimeInfo *mimemsg, *mimetext;
5400 const gchar *src_codeset = CS_INTERNAL;
5401 gchar *from_addr = NULL;
5402 gchar *from_name = NULL;
5404 if (action == COMPOSE_WRITE_FOR_SEND)
5405 attach_parts = TRUE;
5407 /* create message MimeInfo */
5408 mimemsg = procmime_mimeinfo_new();
5409 mimemsg->type = MIMETYPE_MESSAGE;
5410 mimemsg->subtype = g_strdup("rfc822");
5411 mimemsg->content = MIMECONTENT_MEM;
5412 mimemsg->tmp = TRUE; /* must free content later */
5413 mimemsg->data.mem = compose_get_header(compose);
5415 /* Create text part MimeInfo */
5416 /* get all composed text */
5417 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5418 gtk_text_buffer_get_start_iter(buffer, &start);
5419 gtk_text_buffer_get_end_iter(buffer, &end);
5420 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5422 out_codeset = conv_get_charset_str(compose->out_encoding);
5424 if (!out_codeset && is_ascii_str(chars)) {
5425 out_codeset = CS_US_ASCII;
5426 } else if (prefs_common.outgoing_fallback_to_ascii &&
5427 is_ascii_str(chars)) {
5428 out_codeset = CS_US_ASCII;
5429 encoding = ENC_7BIT;
5433 gchar *test_conv_global_out = NULL;
5434 gchar *test_conv_reply = NULL;
5436 /* automatic mode. be automatic. */
5437 codeconv_set_strict(TRUE);
5439 out_codeset = conv_get_outgoing_charset_str();
5441 debug_print("trying to convert to %s\n", out_codeset);
5442 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5445 if (!test_conv_global_out && compose->orig_charset
5446 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5447 out_codeset = compose->orig_charset;
5448 debug_print("failure; trying to convert to %s\n", out_codeset);
5449 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5452 if (!test_conv_global_out && !test_conv_reply) {
5454 out_codeset = CS_INTERNAL;
5455 debug_print("failure; finally using %s\n", out_codeset);
5457 g_free(test_conv_global_out);
5458 g_free(test_conv_reply);
5459 codeconv_set_strict(FALSE);
5462 if (encoding == ENC_UNKNOWN) {
5463 if (prefs_common.encoding_method == CTE_BASE64)
5464 encoding = ENC_BASE64;
5465 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5466 encoding = ENC_QUOTED_PRINTABLE;
5467 else if (prefs_common.encoding_method == CTE_8BIT)
5468 encoding = ENC_8BIT;
5470 encoding = procmime_get_encoding_for_charset(out_codeset);
5473 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5474 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5476 if (action == COMPOSE_WRITE_FOR_SEND) {
5477 codeconv_set_strict(TRUE);
5478 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5479 codeconv_set_strict(FALSE);
5485 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5486 "to the specified %s charset.\n"
5487 "Send it as %s?"), out_codeset, src_codeset);
5488 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5489 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5492 if (aval != G_ALERTALTERNATE) {
5497 out_codeset = src_codeset;
5503 out_codeset = src_codeset;
5508 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5509 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5510 strstr(buf, "\nFrom ") != NULL) {
5511 encoding = ENC_QUOTED_PRINTABLE;
5515 mimetext = procmime_mimeinfo_new();
5516 mimetext->content = MIMECONTENT_MEM;
5517 mimetext->tmp = TRUE; /* must free content later */
5518 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5519 * and free the data, which we need later. */
5520 mimetext->data.mem = g_strdup(buf);
5521 mimetext->type = MIMETYPE_TEXT;
5522 mimetext->subtype = g_strdup("plain");
5523 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5524 g_strdup(out_codeset));
5526 /* protect trailing spaces when signing message */
5527 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5528 privacy_system_can_sign(compose->privacy_system)) {
5529 encoding = ENC_QUOTED_PRINTABLE;
5532 debug_print("main text: %zd bytes encoded as %s in %d\n",
5533 strlen(buf), out_codeset, encoding);
5535 /* check for line length limit */
5536 if (action == COMPOSE_WRITE_FOR_SEND &&
5537 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5538 check_line_length(buf, 1000, &line) < 0) {
5542 msg = g_strdup_printf
5543 (_("Line %d exceeds the line length limit (998 bytes).\n"
5544 "The contents of the message might be broken on the way to the delivery.\n"
5546 "Send it anyway?"), line + 1);
5547 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5549 if (aval != G_ALERTALTERNATE) {
5555 if (encoding != ENC_UNKNOWN)
5556 procmime_encode_content(mimetext, encoding);
5558 /* append attachment parts */
5559 if (compose_use_attach(compose) && attach_parts) {
5560 MimeInfo *mimempart;
5561 gchar *boundary = NULL;
5562 mimempart = procmime_mimeinfo_new();
5563 mimempart->content = MIMECONTENT_EMPTY;
5564 mimempart->type = MIMETYPE_MULTIPART;
5565 mimempart->subtype = g_strdup("mixed");
5569 boundary = generate_mime_boundary(NULL);
5570 } while (strstr(buf, boundary) != NULL);
5572 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5575 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5577 g_node_append(mimempart->node, mimetext->node);
5578 g_node_append(mimemsg->node, mimempart->node);
5580 if (compose_add_attachments(compose, mimempart) < 0)
5583 g_node_append(mimemsg->node, mimetext->node);
5587 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5588 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5589 /* extract name and address */
5590 if (strstr(spec, " <") && strstr(spec, ">")) {
5591 from_addr = g_strdup(strrchr(spec, '<')+1);
5592 *(strrchr(from_addr, '>')) = '\0';
5593 from_name = g_strdup(spec);
5594 *(strrchr(from_name, '<')) = '\0';
5601 /* sign message if sending */
5602 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5603 privacy_system_can_sign(compose->privacy_system))
5604 if (!privacy_sign(compose->privacy_system, mimemsg,
5605 compose->account, from_addr)) {
5612 procmime_write_mimeinfo(mimemsg, fp);
5614 procmime_mimeinfo_free_all(mimemsg);
5619 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5621 GtkTextBuffer *buffer;
5622 GtkTextIter start, end;
5627 if ((fp = g_fopen(file, "wb")) == NULL) {
5628 FILE_OP_ERROR(file, "fopen");
5632 /* chmod for security */
5633 if (change_file_mode_rw(fp, file) < 0) {
5634 FILE_OP_ERROR(file, "chmod");
5635 g_warning("can't change file mode\n");
5638 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5639 gtk_text_buffer_get_start_iter(buffer, &start);
5640 gtk_text_buffer_get_end_iter(buffer, &end);
5641 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5643 chars = conv_codeset_strdup
5644 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5647 if (!chars) return -1;
5650 len = strlen(chars);
5651 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5652 FILE_OP_ERROR(file, "fwrite");
5661 if (fclose(fp) == EOF) {
5662 FILE_OP_ERROR(file, "fclose");
5669 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5672 MsgInfo *msginfo = compose->targetinfo;
5674 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5675 if (!msginfo) return -1;
5677 if (!force && MSG_IS_LOCKED(msginfo->flags))
5680 item = msginfo->folder;
5681 cm_return_val_if_fail(item != NULL, -1);
5683 if (procmsg_msg_exist(msginfo) &&
5684 (folder_has_parent_of_type(item, F_QUEUE) ||
5685 folder_has_parent_of_type(item, F_DRAFT)
5686 || msginfo == compose->autosaved_draft)) {
5687 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5688 g_warning("can't remove the old message\n");
5691 debug_print("removed reedit target %d\n", msginfo->msgnum);
5698 static void compose_remove_draft(Compose *compose)
5701 MsgInfo *msginfo = compose->targetinfo;
5702 drafts = account_get_special_folder(compose->account, F_DRAFT);
5704 if (procmsg_msg_exist(msginfo)) {
5705 folder_item_remove_msg(drafts, msginfo->msgnum);
5710 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5711 gboolean remove_reedit_target)
5713 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5716 static gboolean compose_warn_encryption(Compose *compose)
5718 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5719 AlertValue val = G_ALERTALTERNATE;
5721 if (warning == NULL)
5724 val = alertpanel_full(_("Encryption warning"), warning,
5725 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5726 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5727 if (val & G_ALERTDISABLE) {
5728 val &= ~G_ALERTDISABLE;
5729 if (val == G_ALERTALTERNATE)
5730 privacy_inhibit_encrypt_warning(compose->privacy_system,
5734 if (val == G_ALERTALTERNATE) {
5741 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5742 gchar **msgpath, gboolean check_subject,
5743 gboolean remove_reedit_target)
5750 static gboolean lock = FALSE;
5751 PrefsAccount *mailac = NULL, *newsac = NULL;
5752 gboolean err = FALSE;
5754 debug_print("queueing message...\n");
5755 cm_return_val_if_fail(compose->account != NULL, -1);
5759 if (compose_check_entries(compose, check_subject) == FALSE) {
5761 if (compose->batch) {
5762 gtk_widget_show_all(compose->window);
5767 if (!compose->to_list && !compose->newsgroup_list) {
5768 g_warning("can't get recipient list.");
5773 if (compose->to_list) {
5774 if (compose->account->protocol != A_NNTP)
5775 mailac = compose->account;
5776 else if (cur_account && cur_account->protocol != A_NNTP)
5777 mailac = cur_account;
5778 else if (!(mailac = compose_current_mail_account())) {
5780 alertpanel_error(_("No account for sending mails available!"));
5785 if (compose->newsgroup_list) {
5786 if (compose->account->protocol == A_NNTP)
5787 newsac = compose->account;
5790 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5795 /* write queue header */
5796 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5797 G_DIR_SEPARATOR, compose, (guint) rand());
5798 debug_print("queuing to %s\n", tmp);
5799 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5800 FILE_OP_ERROR(tmp, "fopen");
5806 if (change_file_mode_rw(fp, tmp) < 0) {
5807 FILE_OP_ERROR(tmp, "chmod");
5808 g_warning("can't change file mode\n");
5811 /* queueing variables */
5812 err |= (fprintf(fp, "AF:\n") < 0);
5813 err |= (fprintf(fp, "NF:0\n") < 0);
5814 err |= (fprintf(fp, "PS:10\n") < 0);
5815 err |= (fprintf(fp, "SRH:1\n") < 0);
5816 err |= (fprintf(fp, "SFN:\n") < 0);
5817 err |= (fprintf(fp, "DSR:\n") < 0);
5819 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5821 err |= (fprintf(fp, "MID:\n") < 0);
5822 err |= (fprintf(fp, "CFG:\n") < 0);
5823 err |= (fprintf(fp, "PT:0\n") < 0);
5824 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5825 err |= (fprintf(fp, "RQ:\n") < 0);
5827 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5829 err |= (fprintf(fp, "SSV:\n") < 0);
5831 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5833 err |= (fprintf(fp, "NSV:\n") < 0);
5834 err |= (fprintf(fp, "SSH:\n") < 0);
5835 /* write recepient list */
5836 if (compose->to_list) {
5837 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5838 for (cur = compose->to_list->next; cur != NULL;
5840 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5841 err |= (fprintf(fp, "\n") < 0);
5843 /* write newsgroup list */
5844 if (compose->newsgroup_list) {
5845 err |= (fprintf(fp, "NG:") < 0);
5846 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5847 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5848 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5849 err |= (fprintf(fp, "\n") < 0);
5851 /* Sylpheed account IDs */
5853 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5855 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5858 if (compose->privacy_system != NULL) {
5859 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5860 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5861 if (compose->use_encryption) {
5863 if (!compose_warn_encryption(compose)) {
5870 if (mailac && mailac->encrypt_to_self) {
5871 GSList *tmp_list = g_slist_copy(compose->to_list);
5872 tmp_list = g_slist_append(tmp_list, compose->account->address);
5873 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5874 g_slist_free(tmp_list);
5876 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5878 if (encdata != NULL) {
5879 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5880 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5881 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5883 } /* else we finally dont want to encrypt */
5885 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5886 /* and if encdata was null, it means there's been a problem in
5898 /* Save copy folder */
5899 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5900 gchar *savefolderid;
5902 savefolderid = compose_get_save_to(compose);
5903 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5904 g_free(savefolderid);
5906 /* Save copy folder */
5907 if (compose->return_receipt) {
5908 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5910 /* Message-ID of message replying to */
5911 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5914 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5915 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5918 /* Message-ID of message forwarding to */
5919 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5922 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5923 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5927 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5928 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5930 /* end of headers */
5931 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5933 if (compose->redirect_filename != NULL) {
5934 if (compose_redirect_write_to_file(compose, fp) < 0) {
5943 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5948 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5952 g_warning("failed to write queue message\n");
5959 if (fclose(fp) == EOF) {
5960 FILE_OP_ERROR(tmp, "fclose");
5967 if (item && *item) {
5970 queue = account_get_special_folder(compose->account, F_QUEUE);
5973 g_warning("can't find queue folder\n");
5979 folder_item_scan(queue);
5980 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5981 g_warning("can't queue the message\n");
5988 if (msgpath == NULL) {
5994 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5995 compose_remove_reedit_target(compose, FALSE);
5998 if ((msgnum != NULL) && (item != NULL)) {
6006 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6009 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6011 struct stat statbuf;
6012 gchar *type, *subtype;
6013 GtkTreeModel *model;
6016 model = gtk_tree_view_get_model(tree_view);
6018 if (!gtk_tree_model_get_iter_first(model, &iter))
6021 gtk_tree_model_get(model, &iter,
6025 if (!is_file_exist(ainfo->file)) {
6026 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6027 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6028 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6030 if (val == G_ALERTDEFAULT) {
6035 mimepart = procmime_mimeinfo_new();
6036 mimepart->content = MIMECONTENT_FILE;
6037 mimepart->data.filename = g_strdup(ainfo->file);
6038 mimepart->tmp = FALSE; /* or we destroy our attachment */
6039 mimepart->offset = 0;
6041 g_stat(ainfo->file, &statbuf);
6042 mimepart->length = statbuf.st_size;
6044 type = g_strdup(ainfo->content_type);
6046 if (!strchr(type, '/')) {
6048 type = g_strdup("application/octet-stream");
6051 subtype = strchr(type, '/') + 1;
6052 *(subtype - 1) = '\0';
6053 mimepart->type = procmime_get_media_type(type);
6054 mimepart->subtype = g_strdup(subtype);
6057 if (mimepart->type == MIMETYPE_MESSAGE &&
6058 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6059 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6060 } else if (mimepart->type == MIMETYPE_TEXT) {
6061 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6062 /* Text parts with no name come from multipart/alternative
6063 * forwards. Make sure the recipient won't look at the
6064 * original HTML part by mistake. */
6065 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6066 ainfo->name = g_strdup_printf(_("Original %s part"),
6070 g_hash_table_insert(mimepart->typeparameters,
6071 g_strdup("charset"), g_strdup(ainfo->charset));
6073 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6074 if (mimepart->type == MIMETYPE_APPLICATION &&
6075 !strcmp2(mimepart->subtype, "octet-stream"))
6076 g_hash_table_insert(mimepart->typeparameters,
6077 g_strdup("name"), g_strdup(ainfo->name));
6078 g_hash_table_insert(mimepart->dispositionparameters,
6079 g_strdup("filename"), g_strdup(ainfo->name));
6080 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6083 if (mimepart->type == MIMETYPE_MESSAGE
6084 || mimepart->type == MIMETYPE_MULTIPART)
6085 ainfo->encoding = ENC_BINARY;
6086 else if (compose->use_signing) {
6087 if (ainfo->encoding == ENC_7BIT)
6088 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6089 else if (ainfo->encoding == ENC_8BIT)
6090 ainfo->encoding = ENC_BASE64;
6095 procmime_encode_content(mimepart, ainfo->encoding);
6097 g_node_append(parent->node, mimepart->node);
6098 } while (gtk_tree_model_iter_next(model, &iter));
6103 #define IS_IN_CUSTOM_HEADER(header) \
6104 (compose->account->add_customhdr && \
6105 custom_header_find(compose->account->customhdr_list, header) != NULL)
6107 static void compose_add_headerfield_from_headerlist(Compose *compose,
6109 const gchar *fieldname,
6110 const gchar *seperator)
6112 gchar *str, *fieldname_w_colon;
6113 gboolean add_field = FALSE;
6115 ComposeHeaderEntry *headerentry;
6116 const gchar *headerentryname;
6117 const gchar *trans_fieldname;
6120 if (IS_IN_CUSTOM_HEADER(fieldname))
6123 debug_print("Adding %s-fields\n", fieldname);
6125 fieldstr = g_string_sized_new(64);
6127 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6128 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6130 for (list = compose->header_list; list; list = list->next) {
6131 headerentry = ((ComposeHeaderEntry *)list->data);
6132 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6134 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6135 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6137 if (str[0] != '\0') {
6139 g_string_append(fieldstr, seperator);
6140 g_string_append(fieldstr, str);
6149 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6150 compose_convert_header
6151 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6152 strlen(fieldname) + 2, TRUE);
6153 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6157 g_free(fieldname_w_colon);
6158 g_string_free(fieldstr, TRUE);
6163 static gchar *compose_get_manual_headers_info(Compose *compose)
6165 GString *sh_header = g_string_new(" ");
6167 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6169 for (list = compose->header_list; list; list = list->next) {
6170 ComposeHeaderEntry *headerentry;
6173 gchar *headername_wcolon;
6174 const gchar *headername_trans;
6176 gboolean standard_header = FALSE;
6178 headerentry = ((ComposeHeaderEntry *)list->data);
6180 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6182 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6187 if (!strstr(tmp, ":")) {
6188 headername_wcolon = g_strconcat(tmp, ":", NULL);
6189 headername = g_strdup(tmp);
6191 headername_wcolon = g_strdup(tmp);
6192 headername = g_strdup(strtok(tmp, ":"));
6196 string = std_headers;
6197 while (*string != NULL) {
6198 headername_trans = prefs_common_translated_header_name(*string);
6199 if (!strcmp(headername_trans, headername_wcolon))
6200 standard_header = TRUE;
6203 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6204 g_string_append_printf(sh_header, "%s ", headername);
6206 g_free(headername_wcolon);
6208 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6209 return g_string_free(sh_header, FALSE);
6212 static gchar *compose_get_header(Compose *compose)
6214 gchar buf[BUFFSIZE];
6215 const gchar *entry_str;
6219 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6221 gchar *from_name = NULL, *from_address = NULL;
6224 cm_return_val_if_fail(compose->account != NULL, NULL);
6225 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6227 header = g_string_sized_new(64);
6230 get_rfc822_date(buf, sizeof(buf));
6231 g_string_append_printf(header, "Date: %s\n", buf);
6235 if (compose->account->name && *compose->account->name) {
6237 QUOTE_IF_REQUIRED(buf, compose->account->name);
6238 tmp = g_strdup_printf("%s <%s>",
6239 buf, compose->account->address);
6241 tmp = g_strdup_printf("%s",
6242 compose->account->address);
6244 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6245 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6247 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6248 from_address = g_strdup(compose->account->address);
6250 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6251 /* extract name and address */
6252 if (strstr(spec, " <") && strstr(spec, ">")) {
6253 from_address = g_strdup(strrchr(spec, '<')+1);
6254 *(strrchr(from_address, '>')) = '\0';
6255 from_name = g_strdup(spec);
6256 *(strrchr(from_name, '<')) = '\0';
6259 from_address = g_strdup(spec);
6266 if (from_name && *from_name) {
6267 compose_convert_header
6268 (compose, buf, sizeof(buf), from_name,
6269 strlen("From: "), TRUE);
6270 QUOTE_IF_REQUIRED(name, buf);
6272 g_string_append_printf(header, "From: %s <%s>\n",
6273 name, from_address);
6275 g_string_append_printf(header, "From: %s\n", from_address);
6278 g_free(from_address);
6281 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6284 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6287 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6291 * If this account is a NNTP account remove Bcc header from
6292 * message body since it otherwise will be publicly shown
6294 if (compose->account->protocol != A_NNTP)
6295 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6298 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6300 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6303 compose_convert_header(compose, buf, sizeof(buf), str,
6304 strlen("Subject: "), FALSE);
6305 g_string_append_printf(header, "Subject: %s\n", buf);
6311 if (compose->account->set_domain && compose->account->domain) {
6312 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6313 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6314 g_snprintf(buf, sizeof(buf), "%s",
6315 strchr(compose->account->address, '@') ?
6316 strchr(compose->account->address, '@')+1 :
6317 compose->account->address);
6319 g_snprintf(buf, sizeof(buf), "%s", "");
6322 if (compose->account->gen_msgid) {
6324 if (compose->account->msgid_with_addr) {
6325 addr = compose->account->address;
6327 generate_msgid(buf, sizeof(buf), addr);
6328 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6329 compose->msgid = g_strdup(buf);
6331 compose->msgid = NULL;
6334 if (compose->remove_references == FALSE) {
6336 if (compose->inreplyto && compose->to_list)
6337 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6340 if (compose->references)
6341 g_string_append_printf(header, "References: %s\n", compose->references);
6345 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6348 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6351 if (compose->account->organization &&
6352 strlen(compose->account->organization) &&
6353 !IS_IN_CUSTOM_HEADER("Organization")) {
6354 compose_convert_header(compose, buf, sizeof(buf),
6355 compose->account->organization,
6356 strlen("Organization: "), FALSE);
6357 g_string_append_printf(header, "Organization: %s\n", buf);
6360 /* Program version and system info */
6361 if (compose->account->gen_xmailer &&
6362 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6363 !compose->newsgroup_list) {
6364 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6366 gtk_major_version, gtk_minor_version, gtk_micro_version,
6369 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6370 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6372 gtk_major_version, gtk_minor_version, gtk_micro_version,
6376 /* custom headers */
6377 if (compose->account->add_customhdr) {
6380 for (cur = compose->account->customhdr_list; cur != NULL;
6382 CustomHeader *chdr = (CustomHeader *)cur->data;
6384 if (custom_header_is_allowed(chdr->name)
6385 && chdr->value != NULL
6386 && *(chdr->value) != '\0') {
6387 compose_convert_header
6388 (compose, buf, sizeof(buf),
6390 strlen(chdr->name) + 2, FALSE);
6391 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6396 /* Automatic Faces and X-Faces */
6397 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6398 g_string_append_printf(header, "X-Face: %s\n", buf);
6400 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6401 g_string_append_printf(header, "X-Face: %s\n", buf);
6403 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6404 g_string_append_printf(header, "Face: %s\n", buf);
6406 else if (get_default_face (buf, sizeof(buf)) == 0) {
6407 g_string_append_printf(header, "Face: %s\n", buf);
6411 switch (compose->priority) {
6412 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6413 "X-Priority: 1 (Highest)\n");
6415 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6416 "X-Priority: 2 (High)\n");
6418 case PRIORITY_NORMAL: break;
6419 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6420 "X-Priority: 4 (Low)\n");
6422 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6423 "X-Priority: 5 (Lowest)\n");
6425 default: debug_print("compose: priority unknown : %d\n",
6429 /* Request Return Receipt */
6430 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6431 if (compose->return_receipt) {
6432 if (compose->account->name
6433 && *compose->account->name) {
6434 compose_convert_header(compose, buf, sizeof(buf),
6435 compose->account->name,
6436 strlen("Disposition-Notification-To: "),
6438 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6440 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6444 /* get special headers */
6445 for (list = compose->header_list; list; list = list->next) {
6446 ComposeHeaderEntry *headerentry;
6449 gchar *headername_wcolon;
6450 const gchar *headername_trans;
6453 gboolean standard_header = FALSE;
6455 headerentry = ((ComposeHeaderEntry *)list->data);
6457 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6459 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6464 if (!strstr(tmp, ":")) {
6465 headername_wcolon = g_strconcat(tmp, ":", NULL);
6466 headername = g_strdup(tmp);
6468 headername_wcolon = g_strdup(tmp);
6469 headername = g_strdup(strtok(tmp, ":"));
6473 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6474 Xstrdup_a(headervalue, entry_str, return NULL);
6475 subst_char(headervalue, '\r', ' ');
6476 subst_char(headervalue, '\n', ' ');
6477 string = std_headers;
6478 while (*string != NULL) {
6479 headername_trans = prefs_common_translated_header_name(*string);
6480 if (!strcmp(headername_trans, headername_wcolon))
6481 standard_header = TRUE;
6484 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6485 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6488 g_free(headername_wcolon);
6492 g_string_free(header, FALSE);
6497 #undef IS_IN_CUSTOM_HEADER
6499 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6500 gint header_len, gboolean addr_field)
6502 gchar *tmpstr = NULL;
6503 const gchar *out_codeset = NULL;
6505 cm_return_if_fail(src != NULL);
6506 cm_return_if_fail(dest != NULL);
6508 if (len < 1) return;
6510 tmpstr = g_strdup(src);
6512 subst_char(tmpstr, '\n', ' ');
6513 subst_char(tmpstr, '\r', ' ');
6516 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6517 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6518 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6523 codeconv_set_strict(TRUE);
6524 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6525 conv_get_charset_str(compose->out_encoding));
6526 codeconv_set_strict(FALSE);
6528 if (!dest || *dest == '\0') {
6529 gchar *test_conv_global_out = NULL;
6530 gchar *test_conv_reply = NULL;
6532 /* automatic mode. be automatic. */
6533 codeconv_set_strict(TRUE);
6535 out_codeset = conv_get_outgoing_charset_str();
6537 debug_print("trying to convert to %s\n", out_codeset);
6538 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6541 if (!test_conv_global_out && compose->orig_charset
6542 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6543 out_codeset = compose->orig_charset;
6544 debug_print("failure; trying to convert to %s\n", out_codeset);
6545 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6548 if (!test_conv_global_out && !test_conv_reply) {
6550 out_codeset = CS_INTERNAL;
6551 debug_print("finally using %s\n", out_codeset);
6553 g_free(test_conv_global_out);
6554 g_free(test_conv_reply);
6555 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6557 codeconv_set_strict(FALSE);
6562 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6566 cm_return_if_fail(user_data != NULL);
6568 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6569 g_strstrip(address);
6570 if (*address != '\0') {
6571 gchar *name = procheader_get_fromname(address);
6572 extract_address(address);
6573 addressbook_add_contact(name, address, NULL, NULL);
6578 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6580 GtkWidget *menuitem;
6583 cm_return_if_fail(menu != NULL);
6584 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6586 menuitem = gtk_separator_menu_item_new();
6587 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6588 gtk_widget_show(menuitem);
6590 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6591 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6593 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6594 g_strstrip(address);
6595 if (*address == '\0') {
6596 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6599 g_signal_connect(G_OBJECT(menuitem), "activate",
6600 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6601 gtk_widget_show(menuitem);
6604 static void compose_create_header_entry(Compose *compose)
6606 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6613 const gchar *header = NULL;
6614 ComposeHeaderEntry *headerentry;
6615 gboolean standard_header = FALSE;
6616 GtkListStore *model;
6618 #if !(GTK_CHECK_VERSION(2,12,0))
6619 GtkTooltips *tips = compose->tooltips;
6622 headerentry = g_new0(ComposeHeaderEntry, 1);
6625 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6626 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6627 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6629 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6631 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6633 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6634 COMPOSE_NEWSGROUPS);
6635 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6637 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6638 COMPOSE_FOLLOWUPTO);
6640 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6641 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6642 G_CALLBACK(compose_grab_focus_cb), compose);
6643 gtk_widget_show(combo);
6644 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6645 compose->header_nextrow, compose->header_nextrow+1,
6646 GTK_SHRINK, GTK_FILL, 0, 0);
6647 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6648 const gchar *last_header_entry = gtk_entry_get_text(
6649 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6651 while (*string != NULL) {
6652 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6653 standard_header = TRUE;
6656 if (standard_header)
6657 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6659 if (!compose->header_last || !standard_header) {
6660 switch(compose->account->protocol) {
6662 header = prefs_common_translated_header_name("Newsgroups:");
6665 header = prefs_common_translated_header_name("To:");
6670 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6672 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6673 G_CALLBACK(compose_grab_focus_cb), compose);
6675 /* Entry field with cleanup button */
6676 button = gtk_button_new();
6677 gtk_button_set_image(GTK_BUTTON(button),
6678 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6679 gtk_widget_show(button);
6680 CLAWS_SET_TIP(button,
6681 _("Delete entry contents"));
6682 entry = gtk_entry_new();
6683 gtk_widget_show(entry);
6684 CLAWS_SET_TIP(entry,
6685 _("Use <tab> to autocomplete from addressbook"));
6686 hbox = gtk_hbox_new (FALSE, 0);
6687 gtk_widget_show(hbox);
6688 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6689 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6690 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6691 compose->header_nextrow, compose->header_nextrow+1,
6692 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6694 g_signal_connect(G_OBJECT(entry), "key-press-event",
6695 G_CALLBACK(compose_headerentry_key_press_event_cb),
6697 g_signal_connect(G_OBJECT(entry), "changed",
6698 G_CALLBACK(compose_headerentry_changed_cb),
6700 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6701 G_CALLBACK(compose_grab_focus_cb), compose);
6703 g_signal_connect(G_OBJECT(button), "clicked",
6704 G_CALLBACK(compose_headerentry_button_clicked_cb),
6708 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6709 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6710 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6711 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6712 G_CALLBACK(compose_header_drag_received_cb),
6714 g_signal_connect(G_OBJECT(entry), "drag-drop",
6715 G_CALLBACK(compose_drag_drop),
6717 g_signal_connect(G_OBJECT(entry), "populate-popup",
6718 G_CALLBACK(compose_entry_popup_extend),
6721 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6723 headerentry->compose = compose;
6724 headerentry->combo = combo;
6725 headerentry->entry = entry;
6726 headerentry->button = button;
6727 headerentry->hbox = hbox;
6728 headerentry->headernum = compose->header_nextrow;
6729 headerentry->type = PREF_NONE;
6731 compose->header_nextrow++;
6732 compose->header_last = headerentry;
6733 compose->header_list =
6734 g_slist_append(compose->header_list,
6738 static void compose_add_header_entry(Compose *compose, const gchar *header,
6739 gchar *text, ComposePrefType pref_type)
6741 ComposeHeaderEntry *last_header = compose->header_last;
6742 gchar *tmp = g_strdup(text), *email;
6743 gboolean replyto_hdr;
6745 replyto_hdr = (!strcasecmp(header,
6746 prefs_common_translated_header_name("Reply-To:")) ||
6748 prefs_common_translated_header_name("Followup-To:")) ||
6750 prefs_common_translated_header_name("In-Reply-To:")));
6752 extract_address(tmp);
6753 email = g_utf8_strdown(tmp, -1);
6755 if (replyto_hdr == FALSE &&
6756 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6758 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6759 header, text, (gint) pref_type);
6765 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6766 gtk_entry_set_text(GTK_ENTRY(
6767 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6769 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6770 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6771 last_header->type = pref_type;
6773 if (replyto_hdr == FALSE)
6774 g_hash_table_insert(compose->email_hashtable, email,
6775 GUINT_TO_POINTER(1));
6782 static void compose_destroy_headerentry(Compose *compose,
6783 ComposeHeaderEntry *headerentry)
6785 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6788 extract_address(text);
6789 email = g_utf8_strdown(text, -1);
6790 g_hash_table_remove(compose->email_hashtable, email);
6794 gtk_widget_destroy(headerentry->combo);
6795 gtk_widget_destroy(headerentry->entry);
6796 gtk_widget_destroy(headerentry->button);
6797 gtk_widget_destroy(headerentry->hbox);
6798 g_free(headerentry);
6801 static void compose_remove_header_entries(Compose *compose)
6804 for (list = compose->header_list; list; list = list->next)
6805 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6807 compose->header_last = NULL;
6808 g_slist_free(compose->header_list);
6809 compose->header_list = NULL;
6810 compose->header_nextrow = 1;
6811 compose_create_header_entry(compose);
6814 static GtkWidget *compose_create_header(Compose *compose)
6816 GtkWidget *from_optmenu_hbox;
6817 GtkWidget *header_scrolledwin_main;
6818 GtkWidget *header_table_main;
6819 GtkWidget *header_scrolledwin;
6820 GtkWidget *header_table;
6822 /* parent with account selection and from header */
6823 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6824 gtk_widget_show(header_scrolledwin_main);
6825 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6827 header_table_main = gtk_table_new(2, 2, FALSE);
6828 gtk_widget_show(header_table_main);
6829 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6830 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6831 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6833 from_optmenu_hbox = compose_account_option_menu_create(compose);
6834 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6835 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6837 /* child with header labels and entries */
6838 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6839 gtk_widget_show(header_scrolledwin);
6840 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6842 header_table = gtk_table_new(2, 2, FALSE);
6843 gtk_widget_show(header_table);
6844 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6845 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6846 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6848 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6849 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6851 compose->header_table = header_table;
6852 compose->header_list = NULL;
6853 compose->header_nextrow = 0;
6855 compose_create_header_entry(compose);
6857 compose->table = NULL;
6859 return header_scrolledwin_main;
6862 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6864 Compose *compose = (Compose *)data;
6865 GdkEventButton event;
6868 event.time = gtk_get_current_event_time();
6870 return attach_button_pressed(compose->attach_clist, &event, compose);
6873 static GtkWidget *compose_create_attach(Compose *compose)
6875 GtkWidget *attach_scrwin;
6876 GtkWidget *attach_clist;
6878 GtkListStore *store;
6879 GtkCellRenderer *renderer;
6880 GtkTreeViewColumn *column;
6881 GtkTreeSelection *selection;
6883 /* attachment list */
6884 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6885 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6886 GTK_POLICY_AUTOMATIC,
6887 GTK_POLICY_AUTOMATIC);
6888 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6890 store = gtk_list_store_new(N_ATTACH_COLS,
6896 G_TYPE_AUTO_POINTER,
6898 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6899 (GTK_TREE_MODEL(store)));
6900 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6901 g_object_unref(store);
6903 renderer = gtk_cell_renderer_text_new();
6904 column = gtk_tree_view_column_new_with_attributes
6905 (_("Mime type"), renderer, "text",
6906 COL_MIMETYPE, NULL);
6907 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6909 renderer = gtk_cell_renderer_text_new();
6910 column = gtk_tree_view_column_new_with_attributes
6911 (_("Size"), renderer, "text",
6913 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6915 renderer = gtk_cell_renderer_text_new();
6916 column = gtk_tree_view_column_new_with_attributes
6917 (_("Name"), renderer, "text",
6919 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6921 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6922 prefs_common.use_stripes_everywhere);
6923 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6924 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6926 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6927 G_CALLBACK(attach_selected), compose);
6928 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6929 G_CALLBACK(attach_button_pressed), compose);
6931 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6932 G_CALLBACK(popup_attach_button_pressed), compose);
6934 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6935 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6936 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6937 G_CALLBACK(popup_attach_button_pressed), compose);
6939 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6940 G_CALLBACK(attach_key_pressed), compose);
6943 gtk_drag_dest_set(attach_clist,
6944 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6945 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6946 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6947 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6948 G_CALLBACK(compose_attach_drag_received_cb),
6950 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6951 G_CALLBACK(compose_drag_drop),
6954 compose->attach_scrwin = attach_scrwin;
6955 compose->attach_clist = attach_clist;
6957 return attach_scrwin;
6960 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6961 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6963 static GtkWidget *compose_create_others(Compose *compose)
6966 GtkWidget *savemsg_checkbtn;
6967 GtkWidget *savemsg_combo;
6968 GtkWidget *savemsg_select;
6971 gchar *folderidentifier;
6973 /* Table for settings */
6974 table = gtk_table_new(3, 1, FALSE);
6975 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6976 gtk_widget_show(table);
6977 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6980 /* Save Message to folder */
6981 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6982 gtk_widget_show(savemsg_checkbtn);
6983 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6984 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6985 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6987 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6988 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6990 savemsg_combo = gtk_combo_box_entry_new_text();
6991 compose->savemsg_checkbtn = savemsg_checkbtn;
6992 compose->savemsg_combo = savemsg_combo;
6993 gtk_widget_show(savemsg_combo);
6995 if (prefs_common.compose_save_to_history)
6996 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6997 prefs_common.compose_save_to_history);
6999 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7000 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7001 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7002 G_CALLBACK(compose_grab_focus_cb), compose);
7003 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7004 folderidentifier = folder_item_get_identifier(account_get_special_folder
7005 (compose->account, F_OUTBOX));
7006 compose_set_save_to(compose, folderidentifier);
7007 g_free(folderidentifier);
7010 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7011 gtk_widget_show(savemsg_select);
7012 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7013 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7014 G_CALLBACK(compose_savemsg_select_cb),
7022 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7024 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7025 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7028 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7033 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7036 path = folder_item_get_identifier(dest);
7038 compose_set_save_to(compose, path);
7042 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7043 GdkAtom clip, GtkTextIter *insert_place);
7046 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7050 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7052 if (event->button == 3) {
7054 GtkTextIter sel_start, sel_end;
7055 gboolean stuff_selected;
7057 /* move the cursor to allow GtkAspell to check the word
7058 * under the mouse */
7059 if (event->x && event->y) {
7060 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7061 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7063 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7066 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7067 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7070 stuff_selected = gtk_text_buffer_get_selection_bounds(
7072 &sel_start, &sel_end);
7074 gtk_text_buffer_place_cursor (buffer, &iter);
7075 /* reselect stuff */
7077 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7078 gtk_text_buffer_select_range(buffer,
7079 &sel_start, &sel_end);
7081 return FALSE; /* pass the event so that the right-click goes through */
7084 if (event->button == 2) {
7089 /* get the middle-click position to paste at the correct place */
7090 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7091 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7093 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7096 entry_paste_clipboard(compose, text,
7097 prefs_common.linewrap_pastes,
7098 GDK_SELECTION_PRIMARY, &iter);
7106 static void compose_spell_menu_changed(void *data)
7108 Compose *compose = (Compose *)data;
7110 GtkWidget *menuitem;
7111 GtkWidget *parent_item;
7112 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7115 if (compose->gtkaspell == NULL)
7118 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7119 "/Menu/Spelling/Options");
7121 /* setting the submenu removes /Spelling/Options from the factory
7122 * so we need to save it */
7124 if (parent_item == NULL) {
7125 parent_item = compose->aspell_options_menu;
7126 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7128 compose->aspell_options_menu = parent_item;
7130 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7132 spell_menu = g_slist_reverse(spell_menu);
7133 for (items = spell_menu;
7134 items; items = items->next) {
7135 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7136 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7137 gtk_widget_show(GTK_WIDGET(menuitem));
7139 g_slist_free(spell_menu);
7141 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7142 gtk_widget_show(parent_item);
7145 static void compose_dict_changed(void *data)
7147 Compose *compose = (Compose *) data;
7149 if(compose->gtkaspell &&
7150 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7153 gtkaspell_highlight_all(compose->gtkaspell);
7154 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7158 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7160 Compose *compose = (Compose *)data;
7161 GdkEventButton event;
7164 event.time = gtk_get_current_event_time();
7168 return text_clicked(compose->text, &event, compose);
7171 static gboolean compose_force_window_origin = TRUE;
7172 static Compose *compose_create(PrefsAccount *account,
7181 GtkWidget *handlebox;
7183 GtkWidget *notebook;
7185 GtkWidget *attach_hbox;
7186 GtkWidget *attach_lab1;
7187 GtkWidget *attach_lab2;
7192 GtkWidget *subject_hbox;
7193 GtkWidget *subject_frame;
7194 GtkWidget *subject_entry;
7198 GtkWidget *edit_vbox;
7199 GtkWidget *ruler_hbox;
7201 GtkWidget *scrolledwin;
7203 GtkTextBuffer *buffer;
7204 GtkClipboard *clipboard;
7206 UndoMain *undostruct;
7208 gchar *titles[N_ATTACH_COLS];
7209 GtkWidget *popupmenu;
7210 GtkWidget *tmpl_menu;
7211 GtkActionGroup *action_group = NULL;
7214 GtkAspell * gtkaspell = NULL;
7217 static GdkGeometry geometry;
7219 cm_return_val_if_fail(account != NULL, NULL);
7221 debug_print("Creating compose window...\n");
7222 compose = g_new0(Compose, 1);
7224 titles[COL_MIMETYPE] = _("MIME type");
7225 titles[COL_SIZE] = _("Size");
7226 titles[COL_NAME] = _("Name");
7227 titles[COL_CHARSET] = _("Charset");
7229 compose->batch = batch;
7230 compose->account = account;
7231 compose->folder = folder;
7233 compose->mutex = g_mutex_new();
7234 compose->set_cursor_pos = -1;
7236 #if !(GTK_CHECK_VERSION(2,12,0))
7237 compose->tooltips = tips;
7240 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7242 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7243 gtk_widget_set_size_request(window, prefs_common.compose_width,
7244 prefs_common.compose_height);
7246 if (!geometry.max_width) {
7247 geometry.max_width = gdk_screen_width();
7248 geometry.max_height = gdk_screen_height();
7251 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7252 &geometry, GDK_HINT_MAX_SIZE);
7253 if (!geometry.min_width) {
7254 geometry.min_width = 600;
7255 geometry.min_height = 440;
7257 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7258 &geometry, GDK_HINT_MIN_SIZE);
7260 #ifndef GENERIC_UMPC
7261 if (compose_force_window_origin)
7262 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7263 prefs_common.compose_y);
7265 g_signal_connect(G_OBJECT(window), "delete_event",
7266 G_CALLBACK(compose_delete_cb), compose);
7267 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7268 gtk_widget_realize(window);
7270 gtkut_widget_set_composer_icon(window);
7272 vbox = gtk_vbox_new(FALSE, 0);
7273 gtk_container_add(GTK_CONTAINER(window), vbox);
7275 compose->ui_manager = gtk_ui_manager_new();
7276 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7277 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7278 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7279 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7280 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7281 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7282 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7283 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7284 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7285 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7304 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7308 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7309 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7344 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7362 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7364 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7372 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7373 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7374 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7385 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7393 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7407 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)
7408 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)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7414 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)
7415 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)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7420 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)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7424 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)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7427 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7429 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7430 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)
7431 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7435 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7436 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)
7437 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)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7439 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7441 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7443 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7444 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7445 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7449 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7450 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)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7453 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7454 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7458 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7459 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7460 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7461 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7462 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7463 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7466 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7468 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7469 gtk_widget_show_all(menubar);
7471 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7473 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7475 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7478 if (prefs_common.toolbar_detachable) {
7479 handlebox = gtk_handle_box_new();
7481 handlebox = gtk_hbox_new(FALSE, 0);
7483 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7485 gtk_widget_realize(handlebox);
7487 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7490 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7494 vbox2 = gtk_vbox_new(FALSE, 2);
7495 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7496 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7499 notebook = gtk_notebook_new();
7500 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7501 gtk_widget_show(notebook);
7503 /* header labels and entries */
7504 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7505 compose_create_header(compose),
7506 gtk_label_new_with_mnemonic(_("Hea_der")));
7507 /* attachment list */
7508 attach_hbox = gtk_hbox_new(FALSE, 0);
7509 gtk_widget_show(attach_hbox);
7511 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7512 gtk_widget_show(attach_lab1);
7513 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7515 attach_lab2 = gtk_label_new("");
7516 gtk_widget_show(attach_lab2);
7517 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7519 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7520 compose_create_attach(compose),
7523 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7524 compose_create_others(compose),
7525 gtk_label_new_with_mnemonic(_("Othe_rs")));
7528 subject_hbox = gtk_hbox_new(FALSE, 0);
7529 gtk_widget_show(subject_hbox);
7531 subject_frame = gtk_frame_new(NULL);
7532 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7533 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7534 gtk_widget_show(subject_frame);
7536 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7537 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7538 gtk_widget_show(subject);
7540 label = gtk_label_new(_("Subject:"));
7541 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7542 gtk_widget_show(label);
7545 subject_entry = claws_spell_entry_new();
7547 subject_entry = gtk_entry_new();
7549 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7550 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7551 G_CALLBACK(compose_grab_focus_cb), compose);
7552 gtk_widget_show(subject_entry);
7553 compose->subject_entry = subject_entry;
7554 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7556 edit_vbox = gtk_vbox_new(FALSE, 0);
7558 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7561 ruler_hbox = gtk_hbox_new(FALSE, 0);
7562 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7564 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7565 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7566 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7570 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7571 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7572 GTK_POLICY_AUTOMATIC,
7573 GTK_POLICY_AUTOMATIC);
7574 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7576 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7578 text = gtk_text_view_new();
7579 if (prefs_common.show_compose_margin) {
7580 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7581 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7583 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7584 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7585 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7586 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7587 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7589 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7590 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7591 G_CALLBACK(compose_notebook_size_alloc), compose);
7592 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7593 G_CALLBACK(compose_edit_size_alloc),
7595 g_signal_connect(G_OBJECT(buffer), "changed",
7596 G_CALLBACK(compose_changed_cb), compose);
7597 g_signal_connect(G_OBJECT(text), "grab_focus",
7598 G_CALLBACK(compose_grab_focus_cb), compose);
7599 g_signal_connect(G_OBJECT(buffer), "insert_text",
7600 G_CALLBACK(text_inserted), compose);
7601 g_signal_connect(G_OBJECT(text), "button_press_event",
7602 G_CALLBACK(text_clicked), compose);
7604 g_signal_connect(G_OBJECT(text), "popup-menu",
7605 G_CALLBACK(compose_popup_menu), compose);
7607 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7608 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7609 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7610 G_CALLBACK(compose_popup_menu), compose);
7612 g_signal_connect(G_OBJECT(subject_entry), "changed",
7613 G_CALLBACK(compose_changed_cb), compose);
7616 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7617 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7618 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7619 g_signal_connect(G_OBJECT(text), "drag_data_received",
7620 G_CALLBACK(compose_insert_drag_received_cb),
7622 g_signal_connect(G_OBJECT(text), "drag-drop",
7623 G_CALLBACK(compose_drag_drop),
7625 gtk_widget_show_all(vbox);
7627 /* pane between attach clist and text */
7628 paned = gtk_vpaned_new();
7629 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7631 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7632 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7634 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7636 gtk_paned_add1(GTK_PANED(paned), notebook);
7637 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7638 gtk_widget_show_all(paned);
7641 if (prefs_common.textfont) {
7642 PangoFontDescription *font_desc;
7644 font_desc = pango_font_description_from_string
7645 (prefs_common.textfont);
7647 gtk_widget_modify_font(text, font_desc);
7648 pango_font_description_free(font_desc);
7652 gtk_action_group_add_actions(action_group, compose_popup_entries,
7653 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7654 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7655 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7656 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7657 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7658 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7659 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7661 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7663 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7664 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7665 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7667 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7669 undostruct = undo_init(text);
7670 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7673 address_completion_start(window);
7675 compose->window = window;
7676 compose->vbox = vbox;
7677 compose->menubar = menubar;
7678 compose->handlebox = handlebox;
7680 compose->vbox2 = vbox2;
7682 compose->paned = paned;
7684 compose->attach_label = attach_lab2;
7686 compose->notebook = notebook;
7687 compose->edit_vbox = edit_vbox;
7688 compose->ruler_hbox = ruler_hbox;
7689 compose->ruler = ruler;
7690 compose->scrolledwin = scrolledwin;
7691 compose->text = text;
7693 compose->focused_editable = NULL;
7695 compose->popupmenu = popupmenu;
7697 compose->tmpl_menu = tmpl_menu;
7699 compose->mode = mode;
7700 compose->rmode = mode;
7702 compose->targetinfo = NULL;
7703 compose->replyinfo = NULL;
7704 compose->fwdinfo = NULL;
7706 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7707 g_str_equal, (GDestroyNotify) g_free, NULL);
7709 compose->replyto = NULL;
7711 compose->bcc = NULL;
7712 compose->followup_to = NULL;
7714 compose->ml_post = NULL;
7716 compose->inreplyto = NULL;
7717 compose->references = NULL;
7718 compose->msgid = NULL;
7719 compose->boundary = NULL;
7721 compose->autowrap = prefs_common.autowrap;
7722 compose->autoindent = prefs_common.auto_indent;
7723 compose->use_signing = FALSE;
7724 compose->use_encryption = FALSE;
7725 compose->privacy_system = NULL;
7727 compose->modified = FALSE;
7729 compose->return_receipt = FALSE;
7731 compose->to_list = NULL;
7732 compose->newsgroup_list = NULL;
7734 compose->undostruct = undostruct;
7736 compose->sig_str = NULL;
7738 compose->exteditor_file = NULL;
7739 compose->exteditor_pid = -1;
7740 compose->exteditor_tag = -1;
7741 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7744 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7745 if (mode != COMPOSE_REDIRECT) {
7746 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7747 strcmp(prefs_common.dictionary, "")) {
7748 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7749 prefs_common.alt_dictionary,
7750 conv_get_locale_charset_str(),
7751 prefs_common.misspelled_col,
7752 prefs_common.check_while_typing,
7753 prefs_common.recheck_when_changing_dict,
7754 prefs_common.use_alternate,
7755 prefs_common.use_both_dicts,
7756 GTK_TEXT_VIEW(text),
7757 GTK_WINDOW(compose->window),
7758 compose_dict_changed,
7759 compose_spell_menu_changed,
7762 alertpanel_error(_("Spell checker could not "
7764 gtkaspell_checkers_strerror());
7765 gtkaspell_checkers_reset_error();
7767 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7771 compose->gtkaspell = gtkaspell;
7772 compose_spell_menu_changed(compose);
7773 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7776 compose_select_account(compose, account, TRUE);
7778 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7779 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7781 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7782 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7784 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7785 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7787 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7788 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7790 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7791 if (account->protocol != A_NNTP)
7792 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7793 prefs_common_translated_header_name("To:"));
7795 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7796 prefs_common_translated_header_name("Newsgroups:"));
7798 addressbook_set_target_compose(compose);
7800 if (mode != COMPOSE_REDIRECT)
7801 compose_set_template_menu(compose);
7803 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7806 compose_list = g_list_append(compose_list, compose);
7808 if (!prefs_common.show_ruler)
7809 gtk_widget_hide(ruler_hbox);
7811 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7814 compose->priority = PRIORITY_NORMAL;
7815 compose_update_priority_menu_item(compose);
7817 compose_set_out_encoding(compose);
7820 compose_update_actions_menu(compose);
7822 /* Privacy Systems menu */
7823 compose_update_privacy_systems_menu(compose);
7825 activate_privacy_system(compose, account, TRUE);
7826 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7828 gtk_widget_realize(window);
7830 gtk_widget_show(window);
7832 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7833 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7840 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7845 GtkWidget *optmenubox;
7848 GtkWidget *from_name = NULL;
7849 #if !(GTK_CHECK_VERSION(2,12,0))
7850 GtkTooltips *tips = compose->tooltips;
7853 gint num = 0, def_menu = 0;
7855 accounts = account_get_list();
7856 cm_return_val_if_fail(accounts != NULL, NULL);
7858 optmenubox = gtk_event_box_new();
7859 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7860 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7862 hbox = gtk_hbox_new(FALSE, 6);
7863 from_name = gtk_entry_new();
7865 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7866 G_CALLBACK(compose_grab_focus_cb), compose);
7868 for (; accounts != NULL; accounts = accounts->next, num++) {
7869 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7870 gchar *name, *from = NULL;
7872 if (ac == compose->account) def_menu = num;
7874 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7877 if (ac == compose->account) {
7878 if (ac->name && *ac->name) {
7880 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7881 from = g_strdup_printf("%s <%s>",
7883 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7885 from = g_strdup_printf("%s",
7887 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7890 COMBOBOX_ADD(menu, name, ac->account_id);
7895 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7897 g_signal_connect(G_OBJECT(optmenu), "changed",
7898 G_CALLBACK(account_activated),
7900 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7901 G_CALLBACK(compose_entry_popup_extend),
7904 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7905 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7907 CLAWS_SET_TIP(optmenubox,
7908 _("Account to use for this email"));
7909 CLAWS_SET_TIP(from_name,
7910 _("Sender address to be used"));
7912 compose->account_combo = optmenu;
7913 compose->from_name = from_name;
7918 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7920 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7921 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7922 Compose *compose = (Compose *) data;
7924 compose->priority = value;
7928 static void compose_reply_change_mode(Compose *compose,
7931 gboolean was_modified = compose->modified;
7933 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7935 cm_return_if_fail(compose->replyinfo != NULL);
7937 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7939 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7941 if (action == COMPOSE_REPLY_TO_ALL)
7943 if (action == COMPOSE_REPLY_TO_SENDER)
7945 if (action == COMPOSE_REPLY_TO_LIST)
7948 compose_remove_header_entries(compose);
7949 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7950 if (compose->account->set_autocc && compose->account->auto_cc)
7951 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7953 if (compose->account->set_autobcc && compose->account->auto_bcc)
7954 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7956 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7957 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7958 compose_show_first_last_header(compose, TRUE);
7959 compose->modified = was_modified;
7960 compose_set_title(compose);
7963 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7965 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7966 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7967 Compose *compose = (Compose *) data;
7970 compose_reply_change_mode(compose, value);
7973 static void compose_update_priority_menu_item(Compose * compose)
7975 GtkWidget *menuitem = NULL;
7976 switch (compose->priority) {
7977 case PRIORITY_HIGHEST:
7978 menuitem = gtk_ui_manager_get_widget
7979 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7982 menuitem = gtk_ui_manager_get_widget
7983 (compose->ui_manager, "/Menu/Options/Priority/High");
7985 case PRIORITY_NORMAL:
7986 menuitem = gtk_ui_manager_get_widget
7987 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7990 menuitem = gtk_ui_manager_get_widget
7991 (compose->ui_manager, "/Menu/Options/Priority/Low");
7993 case PRIORITY_LOWEST:
7994 menuitem = gtk_ui_manager_get_widget
7995 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7998 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8001 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8003 Compose *compose = (Compose *) data;
8005 gboolean can_sign = FALSE, can_encrypt = FALSE;
8007 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8009 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8012 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8013 g_free(compose->privacy_system);
8014 compose->privacy_system = NULL;
8015 if (systemid != NULL) {
8016 compose->privacy_system = g_strdup(systemid);
8018 can_sign = privacy_system_can_sign(systemid);
8019 can_encrypt = privacy_system_can_encrypt(systemid);
8022 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8024 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8025 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8028 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8030 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8031 GtkWidget *menuitem = NULL;
8032 GList *children, *amenu;
8033 gboolean can_sign = FALSE, can_encrypt = FALSE;
8034 gboolean found = FALSE;
8036 if (compose->privacy_system != NULL) {
8038 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8039 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8040 cm_return_if_fail(menuitem != NULL);
8042 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8045 while (amenu != NULL) {
8046 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8047 if (systemid != NULL) {
8048 if (strcmp(systemid, compose->privacy_system) == 0 &&
8049 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8050 menuitem = GTK_WIDGET(amenu->data);
8052 can_sign = privacy_system_can_sign(systemid);
8053 can_encrypt = privacy_system_can_encrypt(systemid);
8057 } else if (strlen(compose->privacy_system) == 0 &&
8058 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8059 menuitem = GTK_WIDGET(amenu->data);
8062 can_encrypt = FALSE;
8067 amenu = amenu->next;
8069 g_list_free(children);
8070 if (menuitem != NULL)
8071 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8073 if (warn && !found && strlen(compose->privacy_system)) {
8074 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8075 "will not be able to sign or encrypt this message."),
8076 compose->privacy_system);
8080 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8081 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8084 static void compose_set_out_encoding(Compose *compose)
8086 CharSet out_encoding;
8087 const gchar *branch = NULL;
8088 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8090 switch(out_encoding) {
8091 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8092 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8093 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8094 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8095 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8096 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8097 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8098 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8099 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8100 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8101 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8102 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8103 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8104 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8105 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8106 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8107 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8108 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8109 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8110 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8111 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8112 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8113 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8114 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8115 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8116 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8117 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8118 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8119 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8120 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8121 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8122 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8123 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8125 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8128 static void compose_set_template_menu(Compose *compose)
8130 GSList *tmpl_list, *cur;
8134 tmpl_list = template_get_config();
8136 menu = gtk_menu_new();
8138 gtk_menu_set_accel_group (GTK_MENU (menu),
8139 gtk_ui_manager_get_accel_group(compose->ui_manager));
8140 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8141 Template *tmpl = (Template *)cur->data;
8142 gchar *accel_path = NULL;
8143 item = gtk_menu_item_new_with_label(tmpl->name);
8144 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8145 g_signal_connect(G_OBJECT(item), "activate",
8146 G_CALLBACK(compose_template_activate_cb),
8148 g_object_set_data(G_OBJECT(item), "template", tmpl);
8149 gtk_widget_show(item);
8150 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8151 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8155 gtk_widget_show(menu);
8156 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8159 void compose_update_actions_menu(Compose *compose)
8161 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8164 static void compose_update_privacy_systems_menu(Compose *compose)
8166 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8167 GSList *systems, *cur;
8169 GtkWidget *system_none;
8171 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8172 GtkWidget *privacy_menu = gtk_menu_new();
8174 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8175 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8177 g_signal_connect(G_OBJECT(system_none), "activate",
8178 G_CALLBACK(compose_set_privacy_system_cb), compose);
8180 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8181 gtk_widget_show(system_none);
8183 systems = privacy_get_system_ids();
8184 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8185 gchar *systemid = cur->data;
8187 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8188 widget = gtk_radio_menu_item_new_with_label(group,
8189 privacy_system_get_name(systemid));
8190 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8191 g_strdup(systemid), g_free);
8192 g_signal_connect(G_OBJECT(widget), "activate",
8193 G_CALLBACK(compose_set_privacy_system_cb), compose);
8195 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8196 gtk_widget_show(widget);
8199 g_slist_free(systems);
8200 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8201 gtk_widget_show_all(privacy_menu);
8202 gtk_widget_show_all(privacy_menuitem);
8205 void compose_reflect_prefs_all(void)
8210 for (cur = compose_list; cur != NULL; cur = cur->next) {
8211 compose = (Compose *)cur->data;
8212 compose_set_template_menu(compose);
8216 void compose_reflect_prefs_pixmap_theme(void)
8221 for (cur = compose_list; cur != NULL; cur = cur->next) {
8222 compose = (Compose *)cur->data;
8223 toolbar_update(TOOLBAR_COMPOSE, compose);
8227 static const gchar *compose_quote_char_from_context(Compose *compose)
8229 const gchar *qmark = NULL;
8231 cm_return_val_if_fail(compose != NULL, NULL);
8233 switch (compose->mode) {
8234 /* use forward-specific quote char */
8235 case COMPOSE_FORWARD:
8236 case COMPOSE_FORWARD_AS_ATTACH:
8237 case COMPOSE_FORWARD_INLINE:
8238 if (compose->folder && compose->folder->prefs &&
8239 compose->folder->prefs->forward_with_format)
8240 qmark = compose->folder->prefs->forward_quotemark;
8241 else if (compose->account->forward_with_format)
8242 qmark = compose->account->forward_quotemark;
8244 qmark = prefs_common.fw_quotemark;
8247 /* use reply-specific quote char in all other modes */
8249 if (compose->folder && compose->folder->prefs &&
8250 compose->folder->prefs->reply_with_format)
8251 qmark = compose->folder->prefs->reply_quotemark;
8252 else if (compose->account->reply_with_format)
8253 qmark = compose->account->reply_quotemark;
8255 qmark = prefs_common.quotemark;
8259 if (qmark == NULL || *qmark == '\0')
8265 static void compose_template_apply(Compose *compose, Template *tmpl,
8269 GtkTextBuffer *buffer;
8273 gchar *parsed_str = NULL;
8274 gint cursor_pos = 0;
8275 const gchar *err_msg = _("The body of the template has an error at line %d.");
8278 /* process the body */
8280 text = GTK_TEXT_VIEW(compose->text);
8281 buffer = gtk_text_view_get_buffer(text);
8284 qmark = compose_quote_char_from_context(compose);
8286 if (compose->replyinfo != NULL) {
8289 gtk_text_buffer_set_text(buffer, "", -1);
8290 mark = gtk_text_buffer_get_insert(buffer);
8291 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8293 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8294 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8296 } else if (compose->fwdinfo != NULL) {
8299 gtk_text_buffer_set_text(buffer, "", -1);
8300 mark = gtk_text_buffer_get_insert(buffer);
8301 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8303 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8304 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8307 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8309 GtkTextIter start, end;
8312 gtk_text_buffer_get_start_iter(buffer, &start);
8313 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8314 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8316 /* clear the buffer now */
8318 gtk_text_buffer_set_text(buffer, "", -1);
8320 parsed_str = compose_quote_fmt(compose, dummyinfo,
8321 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8322 procmsg_msginfo_free( dummyinfo );
8328 gtk_text_buffer_set_text(buffer, "", -1);
8329 mark = gtk_text_buffer_get_insert(buffer);
8330 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8333 if (replace && parsed_str && compose->account->auto_sig)
8334 compose_insert_sig(compose, FALSE);
8336 if (replace && parsed_str) {
8337 gtk_text_buffer_get_start_iter(buffer, &iter);
8338 gtk_text_buffer_place_cursor(buffer, &iter);
8342 cursor_pos = quote_fmt_get_cursor_pos();
8343 compose->set_cursor_pos = cursor_pos;
8344 if (cursor_pos == -1)
8346 gtk_text_buffer_get_start_iter(buffer, &iter);
8347 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8348 gtk_text_buffer_place_cursor(buffer, &iter);
8351 /* process the other fields */
8353 compose_template_apply_fields(compose, tmpl);
8354 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8355 quote_fmt_reset_vartable();
8356 compose_changed_cb(NULL, compose);
8359 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8360 gtkaspell_highlight_all(compose->gtkaspell);
8364 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8366 MsgInfo* dummyinfo = NULL;
8367 MsgInfo *msginfo = NULL;
8370 if (compose->replyinfo != NULL)
8371 msginfo = compose->replyinfo;
8372 else if (compose->fwdinfo != NULL)
8373 msginfo = compose->fwdinfo;
8375 dummyinfo = compose_msginfo_new_from_compose(compose);
8376 msginfo = dummyinfo;
8379 if (tmpl->from && *tmpl->from != '\0') {
8381 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8382 compose->gtkaspell);
8384 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8386 quote_fmt_scan_string(tmpl->from);
8389 buf = quote_fmt_get_buffer();
8391 alertpanel_error(_("Template From format error."));
8393 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8397 if (tmpl->to && *tmpl->to != '\0') {
8399 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8400 compose->gtkaspell);
8402 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8404 quote_fmt_scan_string(tmpl->to);
8407 buf = quote_fmt_get_buffer();
8409 alertpanel_error(_("Template To format error."));
8411 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8415 if (tmpl->cc && *tmpl->cc != '\0') {
8417 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8418 compose->gtkaspell);
8420 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8422 quote_fmt_scan_string(tmpl->cc);
8425 buf = quote_fmt_get_buffer();
8427 alertpanel_error(_("Template Cc format error."));
8429 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8433 if (tmpl->bcc && *tmpl->bcc != '\0') {
8435 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8436 compose->gtkaspell);
8438 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8440 quote_fmt_scan_string(tmpl->bcc);
8443 buf = quote_fmt_get_buffer();
8445 alertpanel_error(_("Template Bcc format error."));
8447 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8451 /* process the subject */
8452 if (tmpl->subject && *tmpl->subject != '\0') {
8454 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8455 compose->gtkaspell);
8457 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8459 quote_fmt_scan_string(tmpl->subject);
8462 buf = quote_fmt_get_buffer();
8464 alertpanel_error(_("Template subject format error."));
8466 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8470 procmsg_msginfo_free( dummyinfo );
8473 static void compose_destroy(Compose *compose)
8475 GtkAllocation allocation;
8476 GtkTextBuffer *buffer;
8477 GtkClipboard *clipboard;
8479 compose_list = g_list_remove(compose_list, compose);
8481 if (compose->updating) {
8482 debug_print("danger, not destroying anything now\n");
8483 compose->deferred_destroy = TRUE;
8486 /* NOTE: address_completion_end() does nothing with the window
8487 * however this may change. */
8488 address_completion_end(compose->window);
8490 slist_free_strings(compose->to_list);
8491 g_slist_free(compose->to_list);
8492 slist_free_strings(compose->newsgroup_list);
8493 g_slist_free(compose->newsgroup_list);
8494 slist_free_strings(compose->header_list);
8495 g_slist_free(compose->header_list);
8497 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8499 g_hash_table_destroy(compose->email_hashtable);
8501 procmsg_msginfo_free(compose->targetinfo);
8502 procmsg_msginfo_free(compose->replyinfo);
8503 procmsg_msginfo_free(compose->fwdinfo);
8505 g_free(compose->replyto);
8506 g_free(compose->cc);
8507 g_free(compose->bcc);
8508 g_free(compose->newsgroups);
8509 g_free(compose->followup_to);
8511 g_free(compose->ml_post);
8513 g_free(compose->inreplyto);
8514 g_free(compose->references);
8515 g_free(compose->msgid);
8516 g_free(compose->boundary);
8518 g_free(compose->redirect_filename);
8519 if (compose->undostruct)
8520 undo_destroy(compose->undostruct);
8522 g_free(compose->sig_str);
8524 g_free(compose->exteditor_file);
8526 g_free(compose->orig_charset);
8528 g_free(compose->privacy_system);
8530 if (addressbook_get_target_compose() == compose)
8531 addressbook_set_target_compose(NULL);
8534 if (compose->gtkaspell) {
8535 gtkaspell_delete(compose->gtkaspell);
8536 compose->gtkaspell = NULL;
8540 if (!compose->batch) {
8541 gtk_widget_get_allocation(compose->window, &allocation);
8542 prefs_common.compose_width = allocation.width;
8543 prefs_common.compose_height = allocation.height;
8546 if (!gtk_widget_get_parent(compose->paned))
8547 gtk_widget_destroy(compose->paned);
8548 gtk_widget_destroy(compose->popupmenu);
8550 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8551 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8552 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8554 gtk_widget_destroy(compose->window);
8555 toolbar_destroy(compose->toolbar);
8556 g_free(compose->toolbar);
8557 g_mutex_free(compose->mutex);
8561 static void compose_attach_info_free(AttachInfo *ainfo)
8563 g_free(ainfo->file);
8564 g_free(ainfo->content_type);
8565 g_free(ainfo->name);
8566 g_free(ainfo->charset);
8570 static void compose_attach_update_label(Compose *compose)
8575 GtkTreeModel *model;
8580 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8581 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8582 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8586 while(gtk_tree_model_iter_next(model, &iter))
8589 text = g_strdup_printf("(%d)", i);
8590 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8594 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8596 Compose *compose = (Compose *)data;
8597 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8598 GtkTreeSelection *selection;
8600 GtkTreeModel *model;
8602 selection = gtk_tree_view_get_selection(tree_view);
8603 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8608 for (cur = sel; cur != NULL; cur = cur->next) {
8609 GtkTreePath *path = cur->data;
8610 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8613 gtk_tree_path_free(path);
8616 for (cur = sel; cur != NULL; cur = cur->next) {
8617 GtkTreeRowReference *ref = cur->data;
8618 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8621 if (gtk_tree_model_get_iter(model, &iter, path))
8622 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8624 gtk_tree_path_free(path);
8625 gtk_tree_row_reference_free(ref);
8629 compose_attach_update_label(compose);
8632 static struct _AttachProperty
8635 GtkWidget *mimetype_entry;
8636 GtkWidget *encoding_optmenu;
8637 GtkWidget *path_entry;
8638 GtkWidget *filename_entry;
8640 GtkWidget *cancel_btn;
8643 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8645 gtk_tree_path_free((GtkTreePath *)ptr);
8648 static void compose_attach_property(GtkAction *action, gpointer data)
8650 Compose *compose = (Compose *)data;
8651 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8653 GtkComboBox *optmenu;
8654 GtkTreeSelection *selection;
8656 GtkTreeModel *model;
8659 static gboolean cancelled;
8661 /* only if one selected */
8662 selection = gtk_tree_view_get_selection(tree_view);
8663 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8666 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8670 path = (GtkTreePath *) sel->data;
8671 gtk_tree_model_get_iter(model, &iter, path);
8672 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8675 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8681 if (!attach_prop.window)
8682 compose_attach_property_create(&cancelled);
8683 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8684 gtk_widget_grab_focus(attach_prop.ok_btn);
8685 gtk_widget_show(attach_prop.window);
8686 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8688 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8689 if (ainfo->encoding == ENC_UNKNOWN)
8690 combobox_select_by_data(optmenu, ENC_BASE64);
8692 combobox_select_by_data(optmenu, ainfo->encoding);
8694 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8695 ainfo->content_type ? ainfo->content_type : "");
8696 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8697 ainfo->file ? ainfo->file : "");
8698 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8699 ainfo->name ? ainfo->name : "");
8702 const gchar *entry_text;
8704 gchar *cnttype = NULL;
8711 gtk_widget_hide(attach_prop.window);
8712 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8717 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8718 if (*entry_text != '\0') {
8721 text = g_strstrip(g_strdup(entry_text));
8722 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8723 cnttype = g_strdup(text);
8726 alertpanel_error(_("Invalid MIME type."));
8732 ainfo->encoding = combobox_get_active_data(optmenu);
8734 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8735 if (*entry_text != '\0') {
8736 if (is_file_exist(entry_text) &&
8737 (size = get_file_size(entry_text)) > 0)
8738 file = g_strdup(entry_text);
8741 (_("File doesn't exist or is empty."));
8747 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8748 if (*entry_text != '\0') {
8749 g_free(ainfo->name);
8750 ainfo->name = g_strdup(entry_text);
8754 g_free(ainfo->content_type);
8755 ainfo->content_type = cnttype;
8758 g_free(ainfo->file);
8762 ainfo->size = (goffset)size;
8764 /* update tree store */
8765 text = to_human_readable(ainfo->size);
8766 gtk_tree_model_get_iter(model, &iter, path);
8767 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8768 COL_MIMETYPE, ainfo->content_type,
8770 COL_NAME, ainfo->name,
8771 COL_CHARSET, ainfo->charset,
8777 gtk_tree_path_free(path);
8780 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8782 label = gtk_label_new(str); \
8783 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8784 GTK_FILL, 0, 0, 0); \
8785 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8787 entry = gtk_entry_new(); \
8788 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8789 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8792 static void compose_attach_property_create(gboolean *cancelled)
8798 GtkWidget *mimetype_entry;
8801 GtkListStore *optmenu_menu;
8802 GtkWidget *path_entry;
8803 GtkWidget *filename_entry;
8806 GtkWidget *cancel_btn;
8807 GList *mime_type_list, *strlist;
8810 debug_print("Creating attach_property window...\n");
8812 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8813 gtk_widget_set_size_request(window, 480, -1);
8814 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8815 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8816 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8817 g_signal_connect(G_OBJECT(window), "delete_event",
8818 G_CALLBACK(attach_property_delete_event),
8820 g_signal_connect(G_OBJECT(window), "key_press_event",
8821 G_CALLBACK(attach_property_key_pressed),
8824 vbox = gtk_vbox_new(FALSE, 8);
8825 gtk_container_add(GTK_CONTAINER(window), vbox);
8827 table = gtk_table_new(4, 2, FALSE);
8828 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8829 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8830 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8832 label = gtk_label_new(_("MIME type"));
8833 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8835 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8836 mimetype_entry = gtk_combo_box_entry_new_text();
8837 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8838 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8840 /* stuff with list */
8841 mime_type_list = procmime_get_mime_type_list();
8843 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8844 MimeType *type = (MimeType *) mime_type_list->data;
8847 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8849 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8852 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8853 (GCompareFunc)strcmp2);
8856 for (mime_type_list = strlist; mime_type_list != NULL;
8857 mime_type_list = mime_type_list->next) {
8858 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8859 g_free(mime_type_list->data);
8861 g_list_free(strlist);
8862 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8863 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8865 label = gtk_label_new(_("Encoding"));
8866 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8868 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8870 hbox = gtk_hbox_new(FALSE, 0);
8871 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8872 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8874 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8875 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8877 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8878 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8879 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8880 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8881 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8883 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8885 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8886 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8888 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8889 &ok_btn, GTK_STOCK_OK,
8891 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8892 gtk_widget_grab_default(ok_btn);
8894 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8895 G_CALLBACK(attach_property_ok),
8897 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8898 G_CALLBACK(attach_property_cancel),
8901 gtk_widget_show_all(vbox);
8903 attach_prop.window = window;
8904 attach_prop.mimetype_entry = mimetype_entry;
8905 attach_prop.encoding_optmenu = optmenu;
8906 attach_prop.path_entry = path_entry;
8907 attach_prop.filename_entry = filename_entry;
8908 attach_prop.ok_btn = ok_btn;
8909 attach_prop.cancel_btn = cancel_btn;
8912 #undef SET_LABEL_AND_ENTRY
8914 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8920 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8926 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8927 gboolean *cancelled)
8935 static gboolean attach_property_key_pressed(GtkWidget *widget,
8937 gboolean *cancelled)
8939 if (event && event->keyval == GDK_KEY_Escape) {
8943 if (event && event->keyval == GDK_KEY_Return) {
8951 static void compose_exec_ext_editor(Compose *compose)
8958 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8959 G_DIR_SEPARATOR, compose);
8961 if (pipe(pipe_fds) < 0) {
8967 if ((pid = fork()) < 0) {
8974 /* close the write side of the pipe */
8977 compose->exteditor_file = g_strdup(tmp);
8978 compose->exteditor_pid = pid;
8980 compose_set_ext_editor_sensitive(compose, FALSE);
8983 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8985 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8987 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8991 } else { /* process-monitoring process */
8997 /* close the read side of the pipe */
9000 if (compose_write_body_to_file(compose, tmp) < 0) {
9001 fd_write_all(pipe_fds[1], "2\n", 2);
9005 pid_ed = compose_exec_ext_editor_real(tmp);
9007 fd_write_all(pipe_fds[1], "1\n", 2);
9011 /* wait until editor is terminated */
9012 waitpid(pid_ed, NULL, 0);
9014 fd_write_all(pipe_fds[1], "0\n", 2);
9021 #endif /* G_OS_UNIX */
9025 static gint compose_exec_ext_editor_real(const gchar *file)
9032 cm_return_val_if_fail(file != NULL, -1);
9034 if ((pid = fork()) < 0) {
9039 if (pid != 0) return pid;
9041 /* grandchild process */
9043 if (setpgid(0, getppid()))
9046 if (prefs_common_get_ext_editor_cmd() &&
9047 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9048 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9049 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9051 if (prefs_common_get_ext_editor_cmd())
9052 g_warning("External editor command-line is invalid: '%s'\n",
9053 prefs_common_get_ext_editor_cmd());
9054 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9057 cmdline = strsplit_with_quote(buf, " ", 1024);
9058 execvp(cmdline[0], cmdline);
9061 g_strfreev(cmdline);
9066 static gboolean compose_ext_editor_kill(Compose *compose)
9068 pid_t pgid = compose->exteditor_pid * -1;
9071 ret = kill(pgid, 0);
9073 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9077 msg = g_strdup_printf
9078 (_("The external editor is still working.\n"
9079 "Force terminating the process?\n"
9080 "process group id: %d"), -pgid);
9081 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9082 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9086 if (val == G_ALERTALTERNATE) {
9087 g_source_remove(compose->exteditor_tag);
9088 g_io_channel_shutdown(compose->exteditor_ch,
9090 g_io_channel_unref(compose->exteditor_ch);
9092 if (kill(pgid, SIGTERM) < 0) perror("kill");
9093 waitpid(compose->exteditor_pid, NULL, 0);
9095 g_warning("Terminated process group id: %d", -pgid);
9096 g_warning("Temporary file: %s",
9097 compose->exteditor_file);
9099 compose_set_ext_editor_sensitive(compose, TRUE);
9101 g_free(compose->exteditor_file);
9102 compose->exteditor_file = NULL;
9103 compose->exteditor_pid = -1;
9104 compose->exteditor_ch = NULL;
9105 compose->exteditor_tag = -1;
9113 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9117 Compose *compose = (Compose *)data;
9120 debug_print("Compose: input from monitoring process\n");
9122 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9124 g_io_channel_shutdown(source, FALSE, NULL);
9125 g_io_channel_unref(source);
9127 waitpid(compose->exteditor_pid, NULL, 0);
9129 if (buf[0] == '0') { /* success */
9130 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9131 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9133 gtk_text_buffer_set_text(buffer, "", -1);
9134 compose_insert_file(compose, compose->exteditor_file);
9135 compose_changed_cb(NULL, compose);
9136 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9138 if (claws_unlink(compose->exteditor_file) < 0)
9139 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9140 } else if (buf[0] == '1') { /* failed */
9141 g_warning("Couldn't exec external editor\n");
9142 if (claws_unlink(compose->exteditor_file) < 0)
9143 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9144 } else if (buf[0] == '2') {
9145 g_warning("Couldn't write to file\n");
9146 } else if (buf[0] == '3') {
9147 g_warning("Pipe read failed\n");
9150 compose_set_ext_editor_sensitive(compose, TRUE);
9152 g_free(compose->exteditor_file);
9153 compose->exteditor_file = NULL;
9154 compose->exteditor_pid = -1;
9155 compose->exteditor_ch = NULL;
9156 compose->exteditor_tag = -1;
9161 static void compose_set_ext_editor_sensitive(Compose *compose,
9164 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9165 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9166 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9167 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9168 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9169 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9170 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9172 gtk_widget_set_sensitive(compose->text, sensitive);
9173 if (compose->toolbar->send_btn)
9174 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9175 if (compose->toolbar->sendl_btn)
9176 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9177 if (compose->toolbar->draft_btn)
9178 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9179 if (compose->toolbar->insert_btn)
9180 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9181 if (compose->toolbar->sig_btn)
9182 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9183 if (compose->toolbar->exteditor_btn)
9184 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9185 if (compose->toolbar->linewrap_current_btn)
9186 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9187 if (compose->toolbar->linewrap_all_btn)
9188 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9190 #endif /* G_OS_UNIX */
9193 * compose_undo_state_changed:
9195 * Change the sensivity of the menuentries undo and redo
9197 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9198 gint redo_state, gpointer data)
9200 Compose *compose = (Compose *)data;
9202 switch (undo_state) {
9203 case UNDO_STATE_TRUE:
9204 if (!undostruct->undo_state) {
9205 undostruct->undo_state = TRUE;
9206 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9209 case UNDO_STATE_FALSE:
9210 if (undostruct->undo_state) {
9211 undostruct->undo_state = FALSE;
9212 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9215 case UNDO_STATE_UNCHANGED:
9217 case UNDO_STATE_REFRESH:
9218 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9221 g_warning("Undo state not recognized");
9225 switch (redo_state) {
9226 case UNDO_STATE_TRUE:
9227 if (!undostruct->redo_state) {
9228 undostruct->redo_state = TRUE;
9229 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9232 case UNDO_STATE_FALSE:
9233 if (undostruct->redo_state) {
9234 undostruct->redo_state = FALSE;
9235 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9238 case UNDO_STATE_UNCHANGED:
9240 case UNDO_STATE_REFRESH:
9241 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9244 g_warning("Redo state not recognized");
9249 /* callback functions */
9251 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9252 GtkAllocation *allocation,
9255 prefs_common.compose_notebook_height = allocation->height;
9258 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9259 * includes "non-client" (windows-izm) in calculation, so this calculation
9260 * may not be accurate.
9262 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9263 GtkAllocation *allocation,
9264 GtkSHRuler *shruler)
9266 if (prefs_common.show_ruler) {
9267 gint char_width = 0, char_height = 0;
9268 gint line_width_in_chars;
9270 gtkut_get_font_size(GTK_WIDGET(widget),
9271 &char_width, &char_height);
9272 line_width_in_chars =
9273 (allocation->width - allocation->x) / char_width;
9275 /* got the maximum */
9276 gtk_shruler_set_range(GTK_SHRULER(shruler),
9277 0.0, line_width_in_chars, 0);
9286 ComposePrefType type;
9287 gboolean entry_marked;
9290 static void account_activated(GtkComboBox *optmenu, gpointer data)
9292 Compose *compose = (Compose *)data;
9295 gchar *folderidentifier;
9296 gint account_id = 0;
9299 GSList *list, *saved_list = NULL;
9300 HeaderEntryState *state;
9301 GtkRcStyle *style = NULL;
9302 static GdkColor yellow;
9303 static gboolean color_set = FALSE;
9305 /* Get ID of active account in the combo box */
9306 menu = gtk_combo_box_get_model(optmenu);
9307 gtk_combo_box_get_active_iter(optmenu, &iter);
9308 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9310 ac = account_find_from_id(account_id);
9311 cm_return_if_fail(ac != NULL);
9313 if (ac != compose->account) {
9314 compose_select_account(compose, ac, FALSE);
9316 for (list = compose->header_list; list; list = list->next) {
9317 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9319 if (hentry->type == PREF_ACCOUNT || !list->next) {
9320 compose_destroy_headerentry(compose, hentry);
9324 state = g_malloc0(sizeof(HeaderEntryState));
9325 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9326 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9327 state->entry = gtk_editable_get_chars(
9328 GTK_EDITABLE(hentry->entry), 0, -1);
9329 state->type = hentry->type;
9332 gdk_color_parse("#f5f6be", &yellow);
9333 color_set = gdk_colormap_alloc_color(
9334 gdk_colormap_get_system(),
9335 &yellow, FALSE, TRUE);
9338 style = gtk_widget_get_modifier_style(hentry->entry);
9339 state->entry_marked = gdk_color_equal(&yellow,
9340 &style->base[GTK_STATE_NORMAL]);
9342 saved_list = g_slist_append(saved_list, state);
9343 compose_destroy_headerentry(compose, hentry);
9346 compose->header_last = NULL;
9347 g_slist_free(compose->header_list);
9348 compose->header_list = NULL;
9349 compose->header_nextrow = 1;
9350 compose_create_header_entry(compose);
9352 if (ac->set_autocc && ac->auto_cc)
9353 compose_entry_append(compose, ac->auto_cc,
9354 COMPOSE_CC, PREF_ACCOUNT);
9356 if (ac->set_autobcc && ac->auto_bcc)
9357 compose_entry_append(compose, ac->auto_bcc,
9358 COMPOSE_BCC, PREF_ACCOUNT);
9360 if (ac->set_autoreplyto && ac->auto_replyto)
9361 compose_entry_append(compose, ac->auto_replyto,
9362 COMPOSE_REPLYTO, PREF_ACCOUNT);
9364 for (list = saved_list; list; list = list->next) {
9365 state = (HeaderEntryState *) list->data;
9367 compose_add_header_entry(compose, state->header,
9368 state->entry, state->type);
9369 if (state->entry_marked)
9370 compose_entry_mark_default_to(compose, state->entry);
9372 g_free(state->header);
9373 g_free(state->entry);
9376 g_slist_free(saved_list);
9378 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9379 (ac->protocol == A_NNTP) ?
9380 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9383 /* Set message save folder */
9384 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9385 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9387 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9388 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9390 compose_set_save_to(compose, NULL);
9391 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9392 folderidentifier = folder_item_get_identifier(account_get_special_folder
9393 (compose->account, F_OUTBOX));
9394 compose_set_save_to(compose, folderidentifier);
9395 g_free(folderidentifier);
9399 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9400 GtkTreeViewColumn *column, Compose *compose)
9402 compose_attach_property(NULL, compose);
9405 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9408 Compose *compose = (Compose *)data;
9409 GtkTreeSelection *attach_selection;
9410 gint attach_nr_selected;
9412 if (!event) return FALSE;
9414 if (event->button == 3) {
9415 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9416 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9418 if (attach_nr_selected > 0)
9420 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9421 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9423 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9424 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9427 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9428 NULL, NULL, event->button, event->time);
9435 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9438 Compose *compose = (Compose *)data;
9440 if (!event) return FALSE;
9442 switch (event->keyval) {
9443 case GDK_KEY_Delete:
9444 compose_attach_remove_selected(NULL, compose);
9450 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9452 toolbar_comp_set_sensitive(compose, allow);
9453 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9454 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9456 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9458 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9459 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9460 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9462 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9466 static void compose_send_cb(GtkAction *action, gpointer data)
9468 Compose *compose = (Compose *)data;
9470 if (prefs_common.work_offline &&
9471 !inc_offline_should_override(TRUE,
9472 _("Claws Mail needs network access in order "
9473 "to send this email.")))
9476 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9477 g_source_remove(compose->draft_timeout_tag);
9478 compose->draft_timeout_tag = -1;
9481 compose_send(compose);
9484 static void compose_send_later_cb(GtkAction *action, gpointer data)
9486 Compose *compose = (Compose *)data;
9490 compose_allow_user_actions(compose, FALSE);
9491 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9492 compose_allow_user_actions(compose, TRUE);
9496 compose_close(compose);
9497 } else if (val == -1) {
9498 alertpanel_error(_("Could not queue message."));
9499 } else if (val == -2) {
9500 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9501 } else if (val == -3) {
9502 if (privacy_peek_error())
9503 alertpanel_error(_("Could not queue message for sending:\n\n"
9504 "Signature failed: %s"), privacy_get_error());
9505 } else if (val == -4) {
9506 alertpanel_error(_("Could not queue message for sending:\n\n"
9507 "Charset conversion failed."));
9508 } else if (val == -5) {
9509 alertpanel_error(_("Could not queue message for sending:\n\n"
9510 "Couldn't get recipient encryption key."));
9511 } else if (val == -6) {
9514 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9517 #define DRAFTED_AT_EXIT "drafted_at_exit"
9518 static void compose_register_draft(MsgInfo *info)
9520 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9521 DRAFTED_AT_EXIT, NULL);
9522 FILE *fp = g_fopen(filepath, "ab");
9525 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9533 gboolean compose_draft (gpointer data, guint action)
9535 Compose *compose = (Compose *)data;
9540 MsgFlags flag = {0, 0};
9541 static gboolean lock = FALSE;
9542 MsgInfo *newmsginfo;
9544 gboolean target_locked = FALSE;
9545 gboolean err = FALSE;
9547 if (lock) return FALSE;
9549 if (compose->sending)
9552 draft = account_get_special_folder(compose->account, F_DRAFT);
9553 cm_return_val_if_fail(draft != NULL, FALSE);
9555 if (!g_mutex_trylock(compose->mutex)) {
9556 /* we don't want to lock the mutex once it's available,
9557 * because as the only other part of compose.c locking
9558 * it is compose_close - which means once unlocked,
9559 * the compose struct will be freed */
9560 debug_print("couldn't lock mutex, probably sending\n");
9566 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9567 G_DIR_SEPARATOR, compose);
9568 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9569 FILE_OP_ERROR(tmp, "fopen");
9573 /* chmod for security */
9574 if (change_file_mode_rw(fp, tmp) < 0) {
9575 FILE_OP_ERROR(tmp, "chmod");
9576 g_warning("can't change file mode\n");
9579 /* Save draft infos */
9580 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9581 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9583 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9584 gchar *savefolderid;
9586 savefolderid = compose_get_save_to(compose);
9587 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9588 g_free(savefolderid);
9590 if (compose->return_receipt) {
9591 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9593 if (compose->privacy_system) {
9594 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9595 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9596 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9599 /* Message-ID of message replying to */
9600 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9603 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9604 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9607 /* Message-ID of message forwarding to */
9608 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9611 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9612 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9616 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9617 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9619 sheaders = compose_get_manual_headers_info(compose);
9620 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9623 /* end of headers */
9624 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9631 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9635 if (fclose(fp) == EOF) {
9639 if (compose->targetinfo) {
9640 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9641 flag.perm_flags = target_locked?MSG_LOCKED:0;
9643 flag.tmp_flags = MSG_DRAFT;
9645 folder_item_scan(draft);
9646 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9647 MsgInfo *tmpinfo = NULL;
9648 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9649 if (compose->msgid) {
9650 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9653 msgnum = tmpinfo->msgnum;
9654 procmsg_msginfo_free(tmpinfo);
9655 debug_print("got draft msgnum %d from scanning\n", msgnum);
9657 debug_print("didn't get draft msgnum after scanning\n");
9660 debug_print("got draft msgnum %d from adding\n", msgnum);
9666 if (action != COMPOSE_AUTO_SAVE) {
9667 if (action != COMPOSE_DRAFT_FOR_EXIT)
9668 alertpanel_error(_("Could not save draft."));
9671 gtkut_window_popup(compose->window);
9672 val = alertpanel_full(_("Could not save draft"),
9673 _("Could not save draft.\n"
9674 "Do you want to cancel exit or discard this email?"),
9675 _("_Cancel exit"), _("_Discard email"), NULL,
9676 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9677 if (val == G_ALERTALTERNATE) {
9679 g_mutex_unlock(compose->mutex); /* must be done before closing */
9680 compose_close(compose);
9684 g_mutex_unlock(compose->mutex); /* must be done before closing */
9693 if (compose->mode == COMPOSE_REEDIT) {
9694 compose_remove_reedit_target(compose, TRUE);
9697 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9700 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9702 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9704 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9705 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9706 procmsg_msginfo_set_flags(newmsginfo, 0,
9707 MSG_HAS_ATTACHMENT);
9709 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9710 compose_register_draft(newmsginfo);
9712 procmsg_msginfo_free(newmsginfo);
9715 folder_item_scan(draft);
9717 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9719 g_mutex_unlock(compose->mutex); /* must be done before closing */
9720 compose_close(compose);
9726 path = folder_item_fetch_msg(draft, msgnum);
9728 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9731 if (g_stat(path, &s) < 0) {
9732 FILE_OP_ERROR(path, "stat");
9738 procmsg_msginfo_free(compose->targetinfo);
9739 compose->targetinfo = procmsg_msginfo_new();
9740 compose->targetinfo->msgnum = msgnum;
9741 compose->targetinfo->size = (goffset)s.st_size;
9742 compose->targetinfo->mtime = s.st_mtime;
9743 compose->targetinfo->folder = draft;
9745 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9746 compose->mode = COMPOSE_REEDIT;
9748 if (action == COMPOSE_AUTO_SAVE) {
9749 compose->autosaved_draft = compose->targetinfo;
9751 compose->modified = FALSE;
9752 compose_set_title(compose);
9756 g_mutex_unlock(compose->mutex);
9760 void compose_clear_exit_drafts(void)
9762 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9763 DRAFTED_AT_EXIT, NULL);
9764 if (is_file_exist(filepath))
9765 claws_unlink(filepath);
9770 void compose_reopen_exit_drafts(void)
9772 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9773 DRAFTED_AT_EXIT, NULL);
9774 FILE *fp = g_fopen(filepath, "rb");
9778 while (fgets(buf, sizeof(buf), fp)) {
9779 gchar **parts = g_strsplit(buf, "\t", 2);
9780 const gchar *folder = parts[0];
9781 int msgnum = parts[1] ? atoi(parts[1]):-1;
9783 if (folder && *folder && msgnum > -1) {
9784 FolderItem *item = folder_find_item_from_identifier(folder);
9785 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9787 compose_reedit(info, FALSE);
9794 compose_clear_exit_drafts();
9797 static void compose_save_cb(GtkAction *action, gpointer data)
9799 Compose *compose = (Compose *)data;
9800 compose_draft(compose, COMPOSE_KEEP_EDITING);
9801 compose->rmode = COMPOSE_REEDIT;
9804 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9806 if (compose && file_list) {
9809 for ( tmp = file_list; tmp; tmp = tmp->next) {
9810 gchar *file = (gchar *) tmp->data;
9811 gchar *utf8_filename = conv_filename_to_utf8(file);
9812 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9813 compose_changed_cb(NULL, compose);
9818 g_free(utf8_filename);
9823 static void compose_attach_cb(GtkAction *action, gpointer data)
9825 Compose *compose = (Compose *)data;
9828 if (compose->redirect_filename != NULL)
9831 file_list = filesel_select_multiple_files_open(_("Select file"));
9834 compose_attach_from_list(compose, file_list, TRUE);
9835 g_list_free(file_list);
9839 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9841 Compose *compose = (Compose *)data;
9843 gint files_inserted = 0;
9845 file_list = filesel_select_multiple_files_open(_("Select file"));
9850 for ( tmp = file_list; tmp; tmp = tmp->next) {
9851 gchar *file = (gchar *) tmp->data;
9852 gchar *filedup = g_strdup(file);
9853 gchar *shortfile = g_path_get_basename(filedup);
9854 ComposeInsertResult res;
9855 /* insert the file if the file is short or if the user confirmed that
9856 he/she wants to insert the large file */
9857 res = compose_insert_file(compose, file);
9858 if (res == COMPOSE_INSERT_READ_ERROR) {
9859 alertpanel_error(_("File '%s' could not be read."), shortfile);
9860 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9861 alertpanel_error(_("File '%s' contained invalid characters\n"
9862 "for the current encoding, insertion may be incorrect."),
9864 } else if (res == COMPOSE_INSERT_SUCCESS)
9871 g_list_free(file_list);
9875 if (files_inserted > 0 && compose->gtkaspell &&
9876 compose->gtkaspell->check_while_typing)
9877 gtkaspell_highlight_all(compose->gtkaspell);
9881 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9883 Compose *compose = (Compose *)data;
9885 compose_insert_sig(compose, FALSE);
9888 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9892 Compose *compose = (Compose *)data;
9894 gtkut_widget_get_uposition(widget, &x, &y);
9895 if (!compose->batch) {
9896 prefs_common.compose_x = x;
9897 prefs_common.compose_y = y;
9899 if (compose->sending || compose->updating)
9901 compose_close_cb(NULL, compose);
9905 void compose_close_toolbar(Compose *compose)
9907 compose_close_cb(NULL, compose);
9910 static void compose_close_cb(GtkAction *action, gpointer data)
9912 Compose *compose = (Compose *)data;
9916 if (compose->exteditor_tag != -1) {
9917 if (!compose_ext_editor_kill(compose))
9922 if (compose->modified) {
9923 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9924 if (!g_mutex_trylock(compose->mutex)) {
9925 /* we don't want to lock the mutex once it's available,
9926 * because as the only other part of compose.c locking
9927 * it is compose_close - which means once unlocked,
9928 * the compose struct will be freed */
9929 debug_print("couldn't lock mutex, probably sending\n");
9933 val = alertpanel(_("Discard message"),
9934 _("This message has been modified. Discard it?"),
9935 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9937 val = alertpanel(_("Save changes"),
9938 _("This message has been modified. Save the latest changes?"),
9939 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9941 g_mutex_unlock(compose->mutex);
9943 case G_ALERTDEFAULT:
9944 if (prefs_common.autosave && !reedit)
9945 compose_remove_draft(compose);
9947 case G_ALERTALTERNATE:
9948 compose_draft(data, COMPOSE_QUIT_EDITING);
9955 compose_close(compose);
9958 static void compose_print_cb(GtkAction *action, gpointer data)
9960 Compose *compose = (Compose *) data;
9962 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9963 if (compose->targetinfo)
9964 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9967 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9969 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9970 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9971 Compose *compose = (Compose *) data;
9974 compose->out_encoding = (CharSet)value;
9977 static void compose_address_cb(GtkAction *action, gpointer data)
9979 Compose *compose = (Compose *)data;
9981 addressbook_open(compose);
9984 static void about_show_cb(GtkAction *action, gpointer data)
9989 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9991 Compose *compose = (Compose *)data;
9996 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9997 cm_return_if_fail(tmpl != NULL);
9999 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10001 val = alertpanel(_("Apply template"), msg,
10002 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10005 if (val == G_ALERTDEFAULT)
10006 compose_template_apply(compose, tmpl, TRUE);
10007 else if (val == G_ALERTALTERNATE)
10008 compose_template_apply(compose, tmpl, FALSE);
10011 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10013 Compose *compose = (Compose *)data;
10015 compose_exec_ext_editor(compose);
10018 static void compose_undo_cb(GtkAction *action, gpointer data)
10020 Compose *compose = (Compose *)data;
10021 gboolean prev_autowrap = compose->autowrap;
10023 compose->autowrap = FALSE;
10024 undo_undo(compose->undostruct);
10025 compose->autowrap = prev_autowrap;
10028 static void compose_redo_cb(GtkAction *action, gpointer data)
10030 Compose *compose = (Compose *)data;
10031 gboolean prev_autowrap = compose->autowrap;
10033 compose->autowrap = FALSE;
10034 undo_redo(compose->undostruct);
10035 compose->autowrap = prev_autowrap;
10038 static void entry_cut_clipboard(GtkWidget *entry)
10040 if (GTK_IS_EDITABLE(entry))
10041 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10042 else if (GTK_IS_TEXT_VIEW(entry))
10043 gtk_text_buffer_cut_clipboard(
10044 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10045 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10049 static void entry_copy_clipboard(GtkWidget *entry)
10051 if (GTK_IS_EDITABLE(entry))
10052 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10053 else if (GTK_IS_TEXT_VIEW(entry))
10054 gtk_text_buffer_copy_clipboard(
10055 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10056 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10059 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10060 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10062 if (GTK_IS_TEXT_VIEW(entry)) {
10063 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10064 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10065 GtkTextIter start_iter, end_iter;
10067 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10069 if (contents == NULL)
10072 /* we shouldn't delete the selection when middle-click-pasting, or we
10073 * can't mid-click-paste our own selection */
10074 if (clip != GDK_SELECTION_PRIMARY) {
10075 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10076 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10079 if (insert_place == NULL) {
10080 /* if insert_place isn't specified, insert at the cursor.
10081 * used for Ctrl-V pasting */
10082 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10083 start = gtk_text_iter_get_offset(&start_iter);
10084 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10086 /* if insert_place is specified, paste here.
10087 * used for mid-click-pasting */
10088 start = gtk_text_iter_get_offset(insert_place);
10089 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10090 if (prefs_common.primary_paste_unselects)
10091 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10095 /* paste unwrapped: mark the paste so it's not wrapped later */
10096 end = start + strlen(contents);
10097 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10098 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10099 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10100 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10101 /* rewrap paragraph now (after a mid-click-paste) */
10102 mark_start = gtk_text_buffer_get_insert(buffer);
10103 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10104 gtk_text_iter_backward_char(&start_iter);
10105 compose_beautify_paragraph(compose, &start_iter, TRUE);
10107 } else if (GTK_IS_EDITABLE(entry))
10108 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10110 compose->modified = TRUE;
10113 static void entry_allsel(GtkWidget *entry)
10115 if (GTK_IS_EDITABLE(entry))
10116 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10117 else if (GTK_IS_TEXT_VIEW(entry)) {
10118 GtkTextIter startiter, enditer;
10119 GtkTextBuffer *textbuf;
10121 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10122 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10123 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10125 gtk_text_buffer_move_mark_by_name(textbuf,
10126 "selection_bound", &startiter);
10127 gtk_text_buffer_move_mark_by_name(textbuf,
10128 "insert", &enditer);
10132 static void compose_cut_cb(GtkAction *action, gpointer data)
10134 Compose *compose = (Compose *)data;
10135 if (compose->focused_editable
10136 #ifndef GENERIC_UMPC
10137 && gtk_widget_has_focus(compose->focused_editable)
10140 entry_cut_clipboard(compose->focused_editable);
10143 static void compose_copy_cb(GtkAction *action, gpointer data)
10145 Compose *compose = (Compose *)data;
10146 if (compose->focused_editable
10147 #ifndef GENERIC_UMPC
10148 && gtk_widget_has_focus(compose->focused_editable)
10151 entry_copy_clipboard(compose->focused_editable);
10154 static void compose_paste_cb(GtkAction *action, gpointer data)
10156 Compose *compose = (Compose *)data;
10157 gint prev_autowrap;
10158 GtkTextBuffer *buffer;
10160 if (compose->focused_editable &&
10161 #ifndef GENERIC_UMPC
10162 gtk_widget_has_focus(compose->focused_editable)
10165 entry_paste_clipboard(compose, compose->focused_editable,
10166 prefs_common.linewrap_pastes,
10167 GDK_SELECTION_CLIPBOARD, NULL);
10172 #ifndef GENERIC_UMPC
10173 gtk_widget_has_focus(compose->text) &&
10175 compose->gtkaspell &&
10176 compose->gtkaspell->check_while_typing)
10177 gtkaspell_highlight_all(compose->gtkaspell);
10181 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10183 Compose *compose = (Compose *)data;
10184 gint wrap_quote = prefs_common.linewrap_quote;
10185 if (compose->focused_editable
10186 #ifndef GENERIC_UMPC
10187 && gtk_widget_has_focus(compose->focused_editable)
10190 /* let text_insert() (called directly or at a later time
10191 * after the gtk_editable_paste_clipboard) know that
10192 * text is to be inserted as a quotation. implemented
10193 * by using a simple refcount... */
10194 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10195 G_OBJECT(compose->focused_editable),
10196 "paste_as_quotation"));
10197 g_object_set_data(G_OBJECT(compose->focused_editable),
10198 "paste_as_quotation",
10199 GINT_TO_POINTER(paste_as_quotation + 1));
10200 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10201 entry_paste_clipboard(compose, compose->focused_editable,
10202 prefs_common.linewrap_pastes,
10203 GDK_SELECTION_CLIPBOARD, NULL);
10204 prefs_common.linewrap_quote = wrap_quote;
10208 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10210 Compose *compose = (Compose *)data;
10211 gint prev_autowrap;
10212 GtkTextBuffer *buffer;
10214 if (compose->focused_editable
10215 #ifndef GENERIC_UMPC
10216 && gtk_widget_has_focus(compose->focused_editable)
10219 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10220 GDK_SELECTION_CLIPBOARD, NULL);
10225 #ifndef GENERIC_UMPC
10226 gtk_widget_has_focus(compose->text) &&
10228 compose->gtkaspell &&
10229 compose->gtkaspell->check_while_typing)
10230 gtkaspell_highlight_all(compose->gtkaspell);
10234 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10236 Compose *compose = (Compose *)data;
10237 gint prev_autowrap;
10238 GtkTextBuffer *buffer;
10240 if (compose->focused_editable
10241 #ifndef GENERIC_UMPC
10242 && gtk_widget_has_focus(compose->focused_editable)
10245 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10246 GDK_SELECTION_CLIPBOARD, NULL);
10251 #ifndef GENERIC_UMPC
10252 gtk_widget_has_focus(compose->text) &&
10254 compose->gtkaspell &&
10255 compose->gtkaspell->check_while_typing)
10256 gtkaspell_highlight_all(compose->gtkaspell);
10260 static void compose_allsel_cb(GtkAction *action, gpointer data)
10262 Compose *compose = (Compose *)data;
10263 if (compose->focused_editable
10264 #ifndef GENERIC_UMPC
10265 && gtk_widget_has_focus(compose->focused_editable)
10268 entry_allsel(compose->focused_editable);
10271 static void textview_move_beginning_of_line (GtkTextView *text)
10273 GtkTextBuffer *buffer;
10277 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10279 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10280 mark = gtk_text_buffer_get_insert(buffer);
10281 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10282 gtk_text_iter_set_line_offset(&ins, 0);
10283 gtk_text_buffer_place_cursor(buffer, &ins);
10286 static void textview_move_forward_character (GtkTextView *text)
10288 GtkTextBuffer *buffer;
10292 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10294 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10295 mark = gtk_text_buffer_get_insert(buffer);
10296 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10297 if (gtk_text_iter_forward_cursor_position(&ins))
10298 gtk_text_buffer_place_cursor(buffer, &ins);
10301 static void textview_move_backward_character (GtkTextView *text)
10303 GtkTextBuffer *buffer;
10307 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10309 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10310 mark = gtk_text_buffer_get_insert(buffer);
10311 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10312 if (gtk_text_iter_backward_cursor_position(&ins))
10313 gtk_text_buffer_place_cursor(buffer, &ins);
10316 static void textview_move_forward_word (GtkTextView *text)
10318 GtkTextBuffer *buffer;
10323 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10325 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10326 mark = gtk_text_buffer_get_insert(buffer);
10327 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10328 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10329 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10330 gtk_text_iter_backward_word_start(&ins);
10331 gtk_text_buffer_place_cursor(buffer, &ins);
10335 static void textview_move_backward_word (GtkTextView *text)
10337 GtkTextBuffer *buffer;
10342 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10344 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10345 mark = gtk_text_buffer_get_insert(buffer);
10346 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10347 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10348 if (gtk_text_iter_backward_word_starts(&ins, 1))
10349 gtk_text_buffer_place_cursor(buffer, &ins);
10352 static void textview_move_end_of_line (GtkTextView *text)
10354 GtkTextBuffer *buffer;
10358 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10360 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10361 mark = gtk_text_buffer_get_insert(buffer);
10362 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10363 if (gtk_text_iter_forward_to_line_end(&ins))
10364 gtk_text_buffer_place_cursor(buffer, &ins);
10367 static void textview_move_next_line (GtkTextView *text)
10369 GtkTextBuffer *buffer;
10374 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10376 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10377 mark = gtk_text_buffer_get_insert(buffer);
10378 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10379 offset = gtk_text_iter_get_line_offset(&ins);
10380 if (gtk_text_iter_forward_line(&ins)) {
10381 gtk_text_iter_set_line_offset(&ins, offset);
10382 gtk_text_buffer_place_cursor(buffer, &ins);
10386 static void textview_move_previous_line (GtkTextView *text)
10388 GtkTextBuffer *buffer;
10393 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10395 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10396 mark = gtk_text_buffer_get_insert(buffer);
10397 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10398 offset = gtk_text_iter_get_line_offset(&ins);
10399 if (gtk_text_iter_backward_line(&ins)) {
10400 gtk_text_iter_set_line_offset(&ins, offset);
10401 gtk_text_buffer_place_cursor(buffer, &ins);
10405 static void textview_delete_forward_character (GtkTextView *text)
10407 GtkTextBuffer *buffer;
10409 GtkTextIter ins, end_iter;
10411 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10413 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10414 mark = gtk_text_buffer_get_insert(buffer);
10415 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10417 if (gtk_text_iter_forward_char(&end_iter)) {
10418 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10422 static void textview_delete_backward_character (GtkTextView *text)
10424 GtkTextBuffer *buffer;
10426 GtkTextIter ins, end_iter;
10428 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10430 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10431 mark = gtk_text_buffer_get_insert(buffer);
10432 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10434 if (gtk_text_iter_backward_char(&end_iter)) {
10435 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10439 static void textview_delete_forward_word (GtkTextView *text)
10441 GtkTextBuffer *buffer;
10443 GtkTextIter ins, end_iter;
10445 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10447 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10448 mark = gtk_text_buffer_get_insert(buffer);
10449 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10451 if (gtk_text_iter_forward_word_end(&end_iter)) {
10452 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10456 static void textview_delete_backward_word (GtkTextView *text)
10458 GtkTextBuffer *buffer;
10460 GtkTextIter ins, end_iter;
10462 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10464 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10465 mark = gtk_text_buffer_get_insert(buffer);
10466 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10468 if (gtk_text_iter_backward_word_start(&end_iter)) {
10469 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10473 static void textview_delete_line (GtkTextView *text)
10475 GtkTextBuffer *buffer;
10477 GtkTextIter ins, start_iter, end_iter;
10479 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10481 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10482 mark = gtk_text_buffer_get_insert(buffer);
10483 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10486 gtk_text_iter_set_line_offset(&start_iter, 0);
10489 if (gtk_text_iter_ends_line(&end_iter)){
10490 if (!gtk_text_iter_forward_char(&end_iter))
10491 gtk_text_iter_backward_char(&start_iter);
10494 gtk_text_iter_forward_to_line_end(&end_iter);
10495 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10498 static void textview_delete_to_line_end (GtkTextView *text)
10500 GtkTextBuffer *buffer;
10502 GtkTextIter ins, end_iter;
10504 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10506 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10507 mark = gtk_text_buffer_get_insert(buffer);
10508 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10510 if (gtk_text_iter_ends_line(&end_iter))
10511 gtk_text_iter_forward_char(&end_iter);
10513 gtk_text_iter_forward_to_line_end(&end_iter);
10514 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10517 #define DO_ACTION(name, act) { \
10518 if(!strcmp(name, a_name)) { \
10522 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10524 const gchar *a_name = gtk_action_get_name(action);
10525 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10526 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10527 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10528 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10529 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10530 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10531 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10532 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10533 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10534 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10535 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10536 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10537 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10538 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10542 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10544 Compose *compose = (Compose *)data;
10545 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10546 ComposeCallAdvancedAction action = -1;
10548 action = compose_call_advanced_action_from_path(gaction);
10551 void (*do_action) (GtkTextView *text);
10552 } action_table[] = {
10553 {textview_move_beginning_of_line},
10554 {textview_move_forward_character},
10555 {textview_move_backward_character},
10556 {textview_move_forward_word},
10557 {textview_move_backward_word},
10558 {textview_move_end_of_line},
10559 {textview_move_next_line},
10560 {textview_move_previous_line},
10561 {textview_delete_forward_character},
10562 {textview_delete_backward_character},
10563 {textview_delete_forward_word},
10564 {textview_delete_backward_word},
10565 {textview_delete_line},
10566 {textview_delete_to_line_end}
10569 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10571 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10572 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10573 if (action_table[action].do_action)
10574 action_table[action].do_action(text);
10576 g_warning("Not implemented yet.");
10580 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10582 GtkAllocation allocation;
10586 if (GTK_IS_EDITABLE(widget)) {
10587 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10588 gtk_editable_set_position(GTK_EDITABLE(widget),
10591 if ((parent = gtk_widget_get_parent(widget))
10592 && (parent = gtk_widget_get_parent(parent))
10593 && (parent = gtk_widget_get_parent(parent))) {
10594 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10595 gtk_widget_get_allocation(widget, &allocation);
10596 gint y = allocation.y;
10597 gint height = allocation.height;
10598 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10599 (GTK_SCROLLED_WINDOW(parent));
10601 gfloat value = gtk_adjustment_get_value(shown);
10602 gfloat upper = gtk_adjustment_get_upper(shown);
10603 gfloat page_size = gtk_adjustment_get_page_size(shown);
10604 if (y < (int)value) {
10605 gtk_adjustment_set_value(shown, y - 1);
10607 if ((y + height) > ((int)value + (int)page_size)) {
10608 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10609 gtk_adjustment_set_value(shown,
10610 y + height - (int)page_size - 1);
10612 gtk_adjustment_set_value(shown,
10613 (int)upper - (int)page_size - 1);
10620 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10621 compose->focused_editable = widget;
10623 #ifdef GENERIC_UMPC
10624 if (GTK_IS_TEXT_VIEW(widget)
10625 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10626 g_object_ref(compose->notebook);
10627 g_object_ref(compose->edit_vbox);
10628 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10629 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10630 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10631 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10632 g_object_unref(compose->notebook);
10633 g_object_unref(compose->edit_vbox);
10634 g_signal_handlers_block_by_func(G_OBJECT(widget),
10635 G_CALLBACK(compose_grab_focus_cb),
10637 gtk_widget_grab_focus(widget);
10638 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10639 G_CALLBACK(compose_grab_focus_cb),
10641 } else if (!GTK_IS_TEXT_VIEW(widget)
10642 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10643 g_object_ref(compose->notebook);
10644 g_object_ref(compose->edit_vbox);
10645 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10646 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10647 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10648 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10649 g_object_unref(compose->notebook);
10650 g_object_unref(compose->edit_vbox);
10651 g_signal_handlers_block_by_func(G_OBJECT(widget),
10652 G_CALLBACK(compose_grab_focus_cb),
10654 gtk_widget_grab_focus(widget);
10655 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10656 G_CALLBACK(compose_grab_focus_cb),
10662 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10664 compose->modified = TRUE;
10665 // compose_beautify_paragraph(compose, NULL, TRUE);
10666 #ifndef GENERIC_UMPC
10667 compose_set_title(compose);
10671 static void compose_wrap_cb(GtkAction *action, gpointer data)
10673 Compose *compose = (Compose *)data;
10674 compose_beautify_paragraph(compose, NULL, TRUE);
10677 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10679 Compose *compose = (Compose *)data;
10680 compose_wrap_all_full(compose, TRUE);
10683 static void compose_find_cb(GtkAction *action, gpointer data)
10685 Compose *compose = (Compose *)data;
10687 message_search_compose(compose);
10690 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10693 Compose *compose = (Compose *)data;
10694 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10695 if (compose->autowrap)
10696 compose_wrap_all_full(compose, TRUE);
10697 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10700 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10703 Compose *compose = (Compose *)data;
10704 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10707 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10709 Compose *compose = (Compose *)data;
10711 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10714 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10716 Compose *compose = (Compose *)data;
10718 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10721 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10723 g_free(compose->privacy_system);
10725 compose->privacy_system = g_strdup(account->default_privacy_system);
10726 compose_update_privacy_system_menu_item(compose, warn);
10729 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10731 Compose *compose = (Compose *)data;
10733 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10734 gtk_widget_show(compose->ruler_hbox);
10735 prefs_common.show_ruler = TRUE;
10737 gtk_widget_hide(compose->ruler_hbox);
10738 gtk_widget_queue_resize(compose->edit_vbox);
10739 prefs_common.show_ruler = FALSE;
10743 static void compose_attach_drag_received_cb (GtkWidget *widget,
10744 GdkDragContext *context,
10747 GtkSelectionData *data,
10750 gpointer user_data)
10752 Compose *compose = (Compose *)user_data;
10756 type = gtk_selection_data_get_data_type(data);
10757 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10759 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10761 ) && gtk_drag_get_source_widget(context) !=
10762 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10763 list = uri_list_extract_filenames(
10764 (const gchar *)gtk_selection_data_get_data(data));
10765 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10766 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10767 compose_attach_append
10768 (compose, (const gchar *)tmp->data,
10769 utf8_filename, NULL, NULL);
10770 g_free(utf8_filename);
10772 if (list) compose_changed_cb(NULL, compose);
10773 list_free_strings(list);
10775 } else if (gtk_drag_get_source_widget(context)
10776 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10777 /* comes from our summaryview */
10778 SummaryView * summaryview = NULL;
10779 GSList * list = NULL, *cur = NULL;
10781 if (mainwindow_get_mainwindow())
10782 summaryview = mainwindow_get_mainwindow()->summaryview;
10785 list = summary_get_selected_msg_list(summaryview);
10787 for (cur = list; cur; cur = cur->next) {
10788 MsgInfo *msginfo = (MsgInfo *)cur->data;
10789 gchar *file = NULL;
10791 file = procmsg_get_message_file_full(msginfo,
10794 compose_attach_append(compose, (const gchar *)file,
10795 (const gchar *)file, "message/rfc822", NULL);
10799 g_slist_free(list);
10803 static gboolean compose_drag_drop(GtkWidget *widget,
10804 GdkDragContext *drag_context,
10806 guint time, gpointer user_data)
10808 /* not handling this signal makes compose_insert_drag_received_cb
10813 static void compose_insert_drag_received_cb (GtkWidget *widget,
10814 GdkDragContext *drag_context,
10817 GtkSelectionData *data,
10820 gpointer user_data)
10822 Compose *compose = (Compose *)user_data;
10826 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10828 type = gtk_selection_data_get_data_type(data);
10830 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10832 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10834 AlertValue val = G_ALERTDEFAULT;
10835 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10837 list = uri_list_extract_filenames(ddata);
10838 if (list == NULL && strstr(ddata, "://")) {
10839 /* Assume a list of no files, and data has ://, is a remote link */
10840 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10841 gchar *tmpfile = get_tmp_file();
10842 str_write_to_file(tmpdata, tmpfile);
10844 compose_insert_file(compose, tmpfile);
10845 claws_unlink(tmpfile);
10847 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10848 compose_beautify_paragraph(compose, NULL, TRUE);
10851 switch (prefs_common.compose_dnd_mode) {
10852 case COMPOSE_DND_ASK:
10853 val = alertpanel_full(_("Insert or attach?"),
10854 _("Do you want to insert the contents of the file(s) "
10855 "into the message body, or attach it to the email?"),
10856 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10857 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10859 case COMPOSE_DND_INSERT:
10860 val = G_ALERTALTERNATE;
10862 case COMPOSE_DND_ATTACH:
10863 val = G_ALERTOTHER;
10866 /* unexpected case */
10867 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10870 if (val & G_ALERTDISABLE) {
10871 val &= ~G_ALERTDISABLE;
10872 /* remember what action to perform by default, only if we don't click Cancel */
10873 if (val == G_ALERTALTERNATE)
10874 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10875 else if (val == G_ALERTOTHER)
10876 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10879 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10880 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10881 list_free_strings(list);
10884 } else if (val == G_ALERTOTHER) {
10885 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10886 list_free_strings(list);
10891 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10892 compose_insert_file(compose, (const gchar *)tmp->data);
10894 list_free_strings(list);
10896 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10901 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10904 static void compose_header_drag_received_cb (GtkWidget *widget,
10905 GdkDragContext *drag_context,
10908 GtkSelectionData *data,
10911 gpointer user_data)
10913 GtkEditable *entry = (GtkEditable *)user_data;
10914 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10916 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10919 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10920 gchar *decoded=g_new(gchar, strlen(email));
10923 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10924 gtk_editable_delete_text(entry, 0, -1);
10925 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10926 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10930 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10933 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10935 Compose *compose = (Compose *)data;
10937 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10938 compose->return_receipt = TRUE;
10940 compose->return_receipt = FALSE;
10943 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10945 Compose *compose = (Compose *)data;
10947 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10948 compose->remove_references = TRUE;
10950 compose->remove_references = FALSE;
10953 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10954 ComposeHeaderEntry *headerentry)
10956 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10960 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10961 GdkEventKey *event,
10962 ComposeHeaderEntry *headerentry)
10964 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10965 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10966 !(event->state & GDK_MODIFIER_MASK) &&
10967 (event->keyval == GDK_KEY_BackSpace) &&
10968 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10969 gtk_container_remove
10970 (GTK_CONTAINER(headerentry->compose->header_table),
10971 headerentry->combo);
10972 gtk_container_remove
10973 (GTK_CONTAINER(headerentry->compose->header_table),
10974 headerentry->entry);
10975 headerentry->compose->header_list =
10976 g_slist_remove(headerentry->compose->header_list,
10978 g_free(headerentry);
10979 } else if (event->keyval == GDK_KEY_Tab) {
10980 if (headerentry->compose->header_last == headerentry) {
10981 /* Override default next focus, and give it to subject_entry
10982 * instead of notebook tabs
10984 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10985 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10992 static gboolean scroll_postpone(gpointer data)
10994 Compose *compose = (Compose *)data;
10996 cm_return_val_if_fail(!compose->batch, FALSE);
10998 GTK_EVENTS_FLUSH();
10999 compose_show_first_last_header(compose, FALSE);
11003 static void compose_headerentry_changed_cb(GtkWidget *entry,
11004 ComposeHeaderEntry *headerentry)
11006 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11007 compose_create_header_entry(headerentry->compose);
11008 g_signal_handlers_disconnect_matched
11009 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11010 0, 0, NULL, NULL, headerentry);
11012 if (!headerentry->compose->batch)
11013 g_timeout_add(0, scroll_postpone, headerentry->compose);
11017 static gboolean compose_defer_auto_save_draft(Compose *compose)
11019 compose->draft_timeout_tag = -1;
11020 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11024 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11026 GtkAdjustment *vadj;
11028 cm_return_if_fail(compose);
11029 cm_return_if_fail(!compose->batch);
11030 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11031 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11032 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11033 gtk_widget_get_parent(compose->header_table)));
11034 gtk_adjustment_set_value(vadj, (show_first ?
11035 gtk_adjustment_get_lower(vadj) :
11036 (gtk_adjustment_get_upper(vadj) -
11037 gtk_adjustment_get_page_size(vadj))));
11038 gtk_adjustment_changed(vadj);
11041 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11042 const gchar *text, gint len, Compose *compose)
11044 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11045 (G_OBJECT(compose->text), "paste_as_quotation"));
11048 cm_return_if_fail(text != NULL);
11050 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11051 G_CALLBACK(text_inserted),
11053 if (paste_as_quotation) {
11055 const gchar *qmark;
11057 GtkTextIter start_iter;
11060 len = strlen(text);
11062 new_text = g_strndup(text, len);
11064 qmark = compose_quote_char_from_context(compose);
11066 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11067 gtk_text_buffer_place_cursor(buffer, iter);
11069 pos = gtk_text_iter_get_offset(iter);
11071 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11072 _("Quote format error at line %d."));
11073 quote_fmt_reset_vartable();
11075 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11076 GINT_TO_POINTER(paste_as_quotation - 1));
11078 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11079 gtk_text_buffer_place_cursor(buffer, iter);
11080 gtk_text_buffer_delete_mark(buffer, mark);
11082 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11083 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11084 compose_beautify_paragraph(compose, &start_iter, FALSE);
11085 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11086 gtk_text_buffer_delete_mark(buffer, mark);
11088 if (strcmp(text, "\n") || compose->automatic_break
11089 || gtk_text_iter_starts_line(iter)) {
11090 GtkTextIter before_ins;
11091 gtk_text_buffer_insert(buffer, iter, text, len);
11092 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11093 before_ins = *iter;
11094 gtk_text_iter_backward_chars(&before_ins, len);
11095 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11098 /* check if the preceding is just whitespace or quote */
11099 GtkTextIter start_line;
11100 gchar *tmp = NULL, *quote = NULL;
11101 gint quote_len = 0, is_normal = 0;
11102 start_line = *iter;
11103 gtk_text_iter_set_line_offset(&start_line, 0);
11104 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11107 if (*tmp == '\0') {
11110 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11118 gtk_text_buffer_insert(buffer, iter, text, len);
11120 gtk_text_buffer_insert_with_tags_by_name(buffer,
11121 iter, text, len, "no_join", NULL);
11126 if (!paste_as_quotation) {
11127 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11128 compose_beautify_paragraph(compose, iter, FALSE);
11129 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11130 gtk_text_buffer_delete_mark(buffer, mark);
11133 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11134 G_CALLBACK(text_inserted),
11136 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11138 if (prefs_common.autosave &&
11139 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11140 compose->draft_timeout_tag != -2 /* disabled while loading */)
11141 compose->draft_timeout_tag = g_timeout_add
11142 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11146 static void compose_check_all(GtkAction *action, gpointer data)
11148 Compose *compose = (Compose *)data;
11149 if (!compose->gtkaspell)
11152 if (gtk_widget_has_focus(compose->subject_entry))
11153 claws_spell_entry_check_all(
11154 CLAWS_SPELL_ENTRY(compose->subject_entry));
11156 gtkaspell_check_all(compose->gtkaspell);
11159 static void compose_highlight_all(GtkAction *action, gpointer data)
11161 Compose *compose = (Compose *)data;
11162 if (compose->gtkaspell) {
11163 claws_spell_entry_recheck_all(
11164 CLAWS_SPELL_ENTRY(compose->subject_entry));
11165 gtkaspell_highlight_all(compose->gtkaspell);
11169 static void compose_check_backwards(GtkAction *action, gpointer data)
11171 Compose *compose = (Compose *)data;
11172 if (!compose->gtkaspell) {
11173 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11177 if (gtk_widget_has_focus(compose->subject_entry))
11178 claws_spell_entry_check_backwards(
11179 CLAWS_SPELL_ENTRY(compose->subject_entry));
11181 gtkaspell_check_backwards(compose->gtkaspell);
11184 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11186 Compose *compose = (Compose *)data;
11187 if (!compose->gtkaspell) {
11188 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11192 if (gtk_widget_has_focus(compose->subject_entry))
11193 claws_spell_entry_check_forwards_go(
11194 CLAWS_SPELL_ENTRY(compose->subject_entry));
11196 gtkaspell_check_forwards_go(compose->gtkaspell);
11201 *\brief Guess originating forward account from MsgInfo and several
11202 * "common preference" settings. Return NULL if no guess.
11204 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11206 PrefsAccount *account = NULL;
11208 cm_return_val_if_fail(msginfo, NULL);
11209 cm_return_val_if_fail(msginfo->folder, NULL);
11210 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11212 if (msginfo->folder->prefs->enable_default_account)
11213 account = account_find_from_id(msginfo->folder->prefs->default_account);
11216 account = msginfo->folder->folder->account;
11218 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11220 Xstrdup_a(to, msginfo->to, return NULL);
11221 extract_address(to);
11222 account = account_find_from_address(to, FALSE);
11225 if (!account && prefs_common.forward_account_autosel) {
11226 gchar cc[BUFFSIZE];
11227 if (!procheader_get_header_from_msginfo
11228 (msginfo, cc,sizeof cc , "Cc:")) {
11229 gchar *buf = cc + strlen("Cc:");
11230 extract_address(buf);
11231 account = account_find_from_address(buf, FALSE);
11235 if (!account && prefs_common.forward_account_autosel) {
11236 gchar deliveredto[BUFFSIZE];
11237 if (!procheader_get_header_from_msginfo
11238 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11239 gchar *buf = deliveredto + strlen("Delivered-To:");
11240 extract_address(buf);
11241 account = account_find_from_address(buf, FALSE);
11248 gboolean compose_close(Compose *compose)
11252 if (!g_mutex_trylock(compose->mutex)) {
11253 /* we have to wait for the (possibly deferred by auto-save)
11254 * drafting to be done, before destroying the compose under
11256 debug_print("waiting for drafting to finish...\n");
11257 compose_allow_user_actions(compose, FALSE);
11258 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11261 cm_return_val_if_fail(compose, FALSE);
11262 gtkut_widget_get_uposition(compose->window, &x, &y);
11263 if (!compose->batch) {
11264 prefs_common.compose_x = x;
11265 prefs_common.compose_y = y;
11267 g_mutex_unlock(compose->mutex);
11268 compose_destroy(compose);
11273 * Add entry field for each address in list.
11274 * \param compose E-Mail composition object.
11275 * \param listAddress List of (formatted) E-Mail addresses.
11277 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11280 node = listAddress;
11282 addr = ( gchar * ) node->data;
11283 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11284 node = g_list_next( node );
11288 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11289 guint action, gboolean opening_multiple)
11291 gchar *body = NULL;
11292 GSList *new_msglist = NULL;
11293 MsgInfo *tmp_msginfo = NULL;
11294 gboolean originally_enc = FALSE;
11295 gboolean originally_sig = FALSE;
11296 Compose *compose = NULL;
11297 gchar *s_system = NULL;
11299 cm_return_if_fail(msgview != NULL);
11301 cm_return_if_fail(msginfo_list != NULL);
11303 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11304 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11305 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11307 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11308 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11309 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11310 orig_msginfo, mimeinfo);
11311 if (tmp_msginfo != NULL) {
11312 new_msglist = g_slist_append(NULL, tmp_msginfo);
11314 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11315 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11316 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11318 tmp_msginfo->folder = orig_msginfo->folder;
11319 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11320 if (orig_msginfo->tags) {
11321 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11322 tmp_msginfo->folder->tags_dirty = TRUE;
11328 if (!opening_multiple)
11329 body = messageview_get_selection(msgview);
11332 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11333 procmsg_msginfo_free(tmp_msginfo);
11334 g_slist_free(new_msglist);
11336 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11338 if (compose && originally_enc) {
11339 compose_force_encryption(compose, compose->account, FALSE, s_system);
11342 if (compose && originally_sig && compose->account->default_sign_reply) {
11343 compose_force_signing(compose, compose->account, s_system);
11347 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11350 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11353 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11354 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11355 GSList *cur = msginfo_list;
11356 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11357 "messages. Opening the windows "
11358 "could take some time. Do you "
11359 "want to continue?"),
11360 g_slist_length(msginfo_list));
11361 if (g_slist_length(msginfo_list) > 9
11362 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11363 != G_ALERTALTERNATE) {
11368 /* We'll open multiple compose windows */
11369 /* let the WM place the next windows */
11370 compose_force_window_origin = FALSE;
11371 for (; cur; cur = cur->next) {
11373 tmplist.data = cur->data;
11374 tmplist.next = NULL;
11375 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11377 compose_force_window_origin = TRUE;
11379 /* forwarding multiple mails as attachments is done via a
11380 * single compose window */
11381 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11385 void compose_check_for_email_account(Compose *compose)
11387 PrefsAccount *ac = NULL, *curr = NULL;
11393 if (compose->account && compose->account->protocol == A_NNTP) {
11394 ac = account_get_cur_account();
11395 if (ac->protocol == A_NNTP) {
11396 list = account_get_list();
11398 for( ; list != NULL ; list = g_list_next(list)) {
11399 curr = (PrefsAccount *) list->data;
11400 if (curr->protocol != A_NNTP) {
11406 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11411 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11412 const gchar *address)
11414 GSList *msginfo_list = NULL;
11415 gchar *body = messageview_get_selection(msgview);
11418 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11420 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11421 compose_check_for_email_account(compose);
11422 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11423 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11424 compose_reply_set_subject(compose, msginfo);
11427 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11430 void compose_set_position(Compose *compose, gint pos)
11432 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11434 gtkut_text_view_set_position(text, pos);
11437 gboolean compose_search_string(Compose *compose,
11438 const gchar *str, gboolean case_sens)
11440 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11442 return gtkut_text_view_search_string(text, str, case_sens);
11445 gboolean compose_search_string_backward(Compose *compose,
11446 const gchar *str, gboolean case_sens)
11448 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11450 return gtkut_text_view_search_string_backward(text, str, case_sens);
11453 /* allocate a msginfo structure and populate its data from a compose data structure */
11454 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11456 MsgInfo *newmsginfo;
11458 gchar buf[BUFFSIZE];
11460 cm_return_val_if_fail( compose != NULL, NULL );
11462 newmsginfo = procmsg_msginfo_new();
11465 get_rfc822_date(buf, sizeof(buf));
11466 newmsginfo->date = g_strdup(buf);
11469 if (compose->from_name) {
11470 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11471 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11475 if (compose->subject_entry)
11476 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11478 /* to, cc, reply-to, newsgroups */
11479 for (list = compose->header_list; list; list = list->next) {
11480 gchar *header = gtk_editable_get_chars(
11482 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11483 gchar *entry = gtk_editable_get_chars(
11484 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11486 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11487 if ( newmsginfo->to == NULL ) {
11488 newmsginfo->to = g_strdup(entry);
11489 } else if (entry && *entry) {
11490 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11491 g_free(newmsginfo->to);
11492 newmsginfo->to = tmp;
11495 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11496 if ( newmsginfo->cc == NULL ) {
11497 newmsginfo->cc = g_strdup(entry);
11498 } else if (entry && *entry) {
11499 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11500 g_free(newmsginfo->cc);
11501 newmsginfo->cc = tmp;
11504 if ( strcasecmp(header,
11505 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11506 if ( newmsginfo->newsgroups == NULL ) {
11507 newmsginfo->newsgroups = g_strdup(entry);
11508 } else if (entry && *entry) {
11509 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11510 g_free(newmsginfo->newsgroups);
11511 newmsginfo->newsgroups = tmp;
11519 /* other data is unset */
11525 /* update compose's dictionaries from folder dict settings */
11526 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11527 FolderItem *folder_item)
11529 cm_return_if_fail(compose != NULL);
11531 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11532 FolderItemPrefs *prefs = folder_item->prefs;
11534 if (prefs->enable_default_dictionary)
11535 gtkaspell_change_dict(compose->gtkaspell,
11536 prefs->default_dictionary, FALSE);
11537 if (folder_item->prefs->enable_default_alt_dictionary)
11538 gtkaspell_change_alt_dict(compose->gtkaspell,
11539 prefs->default_alt_dictionary);
11540 if (prefs->enable_default_dictionary
11541 || prefs->enable_default_alt_dictionary)
11542 compose_spell_menu_changed(compose);