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"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
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 gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
353 #if !GTK_CHECK_VERSION(2,24,0)
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void compose_toggle_ruler_cb (GtkToggleAction *action,
360 static void account_activated (GtkComboBox *optmenu,
362 static void attach_selected (GtkTreeView *tree_view,
363 GtkTreePath *tree_path,
364 GtkTreeViewColumn *column,
366 static gboolean attach_button_pressed (GtkWidget *widget,
367 GdkEventButton *event,
369 static gboolean attach_key_pressed (GtkWidget *widget,
372 static void compose_send_cb (GtkAction *action, gpointer data);
373 static void compose_send_later_cb (GtkAction *action, gpointer data);
375 static void compose_save_cb (GtkAction *action,
378 static void compose_attach_cb (GtkAction *action,
380 static void compose_insert_file_cb (GtkAction *action,
382 static void compose_insert_sig_cb (GtkAction *action,
385 static void compose_close_cb (GtkAction *action,
388 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
390 static void compose_address_cb (GtkAction *action,
392 static void about_show_cb (GtkAction *action,
394 static void compose_template_activate_cb(GtkWidget *widget,
397 static void compose_ext_editor_cb (GtkAction *action,
400 static gint compose_delete_cb (GtkWidget *widget,
404 static void compose_undo_cb (GtkAction *action,
406 static void compose_redo_cb (GtkAction *action,
408 static void compose_cut_cb (GtkAction *action,
410 static void compose_copy_cb (GtkAction *action,
412 static void compose_paste_cb (GtkAction *action,
414 static void compose_paste_as_quote_cb (GtkAction *action,
416 static void compose_paste_no_wrap_cb (GtkAction *action,
418 static void compose_paste_wrap_cb (GtkAction *action,
420 static void compose_allsel_cb (GtkAction *action,
423 static void compose_advanced_action_cb (GtkAction *action,
426 static void compose_grab_focus_cb (GtkWidget *widget,
429 static void compose_changed_cb (GtkTextBuffer *textbuf,
432 static void compose_wrap_cb (GtkAction *action,
434 static void compose_wrap_all_cb (GtkAction *action,
436 static void compose_find_cb (GtkAction *action,
438 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
440 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
443 static void compose_toggle_sign_cb (GtkToggleAction *action,
445 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
447 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
448 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
449 static void activate_privacy_system (Compose *compose,
450 PrefsAccount *account,
452 static void compose_use_signing(Compose *compose, gboolean use_signing);
453 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
454 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
456 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
458 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
459 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
460 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
462 static void compose_attach_drag_received_cb (GtkWidget *widget,
463 GdkDragContext *drag_context,
466 GtkSelectionData *data,
470 static void compose_insert_drag_received_cb (GtkWidget *widget,
471 GdkDragContext *drag_context,
474 GtkSelectionData *data,
478 static void compose_header_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
487 static gboolean compose_drag_drop (GtkWidget *widget,
488 GdkDragContext *drag_context,
490 guint time, gpointer user_data);
492 static void text_inserted (GtkTextBuffer *buffer,
497 static Compose *compose_generic_reply(MsgInfo *msginfo,
498 ComposeQuoteMode quote_mode,
502 gboolean followup_and_reply_to,
505 static void compose_headerentry_changed_cb (GtkWidget *entry,
506 ComposeHeaderEntry *headerentry);
507 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
509 ComposeHeaderEntry *headerentry);
510 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
511 ComposeHeaderEntry *headerentry);
513 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
515 static void compose_allow_user_actions (Compose *compose, gboolean allow);
517 static void compose_nothing_cb (GtkAction *action, gpointer data)
523 static void compose_check_all (GtkAction *action, gpointer data);
524 static void compose_highlight_all (GtkAction *action, gpointer data);
525 static void compose_check_backwards (GtkAction *action, gpointer data);
526 static void compose_check_forwards_go (GtkAction *action, gpointer data);
529 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
531 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
534 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
535 FolderItem *folder_item);
537 static void compose_attach_update_label(Compose *compose);
538 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
539 gboolean respect_default_to);
541 static GtkActionEntry compose_popup_entries[] =
543 {"Compose", NULL, "Compose" },
544 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
545 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
546 {"Compose/---", NULL, "---", NULL, NULL, NULL },
547 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
550 static GtkActionEntry compose_entries[] =
552 {"Menu", NULL, "Menu" },
554 {"Message", NULL, N_("_Message") },
555 {"Edit", NULL, N_("_Edit") },
557 {"Spelling", NULL, N_("_Spelling") },
559 {"Options", NULL, N_("_Options") },
560 {"Tools", NULL, N_("_Tools") },
561 {"Help", NULL, N_("_Help") },
563 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
564 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
565 {"Message/---", NULL, "---" },
567 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
568 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
569 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
570 /* {"Message/---", NULL, "---" }, */
571 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
572 /* {"Message/---", NULL, "---" }, */
573 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
576 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
577 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
578 {"Edit/---", NULL, "---" },
580 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
581 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
582 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
584 {"Edit/SpecialPaste", NULL, N_("Special paste") },
585 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
586 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
587 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
589 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
591 {"Edit/Advanced", NULL, N_("A_dvanced") },
592 {"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*/
593 {"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*/
594 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
595 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
596 {"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*/
597 {"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*/
598 {"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*/
599 {"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*/
600 {"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*/
601 {"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*/
602 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
603 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
604 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
605 {"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*/
607 /* {"Edit/---", NULL, "---" }, */
608 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
610 /* {"Edit/---", NULL, "---" }, */
611 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
612 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
613 /* {"Edit/---", NULL, "---" }, */
614 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
617 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
618 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
619 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
620 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
622 {"Spelling/---", NULL, "---" },
623 {"Spelling/Options", NULL, N_("_Options") },
628 {"Options/ReplyMode", NULL, N_("Reply _mode") },
629 {"Options/---", NULL, "---" },
630 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
631 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
633 /* {"Options/---", NULL, "---" }, */
635 {"Options/Priority", NULL, N_("_Priority") },
637 {"Options/Encoding", NULL, N_("Character _encoding") },
638 {"Options/Encoding/---", NULL, "---" },
639 #define ENC_ACTION(cs_char,c_char,string) \
640 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
642 {"Options/Encoding/Western", NULL, N_("Western European") },
643 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
644 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
645 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
646 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
647 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
648 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
649 {"Options/Encoding/Korean", NULL, N_("Korean") },
650 {"Options/Encoding/Thai", NULL, N_("Thai") },
653 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
655 {"Tools/Template", NULL, N_("_Template") },
656 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Tools/Actions", NULL, N_("Actio_ns") },
658 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
661 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
664 static GtkToggleActionEntry compose_toggle_entries[] =
666 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
667 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
668 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
669 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
670 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
671 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
672 #if !GTK_CHECK_VERSION(2,24,0)
673 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
677 static GtkRadioActionEntry compose_radio_rm_entries[] =
679 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
680 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
681 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
682 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
685 static GtkRadioActionEntry compose_radio_prio_entries[] =
687 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
689 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
690 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
691 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
694 static GtkRadioActionEntry compose_radio_enc_entries[] =
696 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
730 static GtkTargetEntry compose_mime_types[] =
732 {"text/uri-list", 0, 0},
733 {"UTF8_STRING", 0, 0},
737 static gboolean compose_put_existing_to_front(MsgInfo *info)
739 GList *compose_list = compose_get_compose_list();
743 for (elem = compose_list; elem != NULL && elem->data != NULL;
745 Compose *c = (Compose*)elem->data;
747 if (!c->targetinfo || !c->targetinfo->msgid ||
751 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
752 gtkut_window_popup(c->window);
760 static GdkColor quote_color1 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762 static GdkColor quote_color2 =
763 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_color3 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor1 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
769 static GdkColor quote_bgcolor2 =
770 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
771 static GdkColor quote_bgcolor3 =
772 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
774 static GdkColor signature_color = {
781 static GdkColor uri_color = {
788 static void compose_create_tags(GtkTextView *text, Compose *compose)
790 GtkTextBuffer *buffer;
791 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
797 buffer = gtk_text_view_get_buffer(text);
799 if (prefs_common.enable_color) {
800 /* grab the quote colors, converting from an int to a GdkColor */
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
807 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
809 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
811 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
813 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
815 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
818 signature_color = quote_color1 = quote_color2 = quote_color3 =
819 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
822 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
823 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
824 "foreground-gdk", "e_color1,
825 "paragraph-background-gdk", "e_bgcolor1,
827 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
828 "foreground-gdk", "e_color2,
829 "paragraph-background-gdk", "e_bgcolor2,
831 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
832 "foreground-gdk", "e_color3,
833 "paragraph-background-gdk", "e_bgcolor3,
836 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
837 "foreground-gdk", "e_color1,
839 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
840 "foreground-gdk", "e_color2,
842 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
843 "foreground-gdk", "e_color3,
847 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
848 "foreground-gdk", &signature_color,
851 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
852 "foreground-gdk", &uri_color,
854 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
855 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
857 color[0] = quote_color1;
858 color[1] = quote_color2;
859 color[2] = quote_color3;
860 color[3] = quote_bgcolor1;
861 color[4] = quote_bgcolor2;
862 color[5] = quote_bgcolor3;
863 color[6] = signature_color;
864 color[7] = uri_color;
865 cmap = gdk_drawable_get_colormap(compose->window->window);
866 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
868 for (i = 0; i < 8; i++) {
869 if (success[i] == FALSE) {
872 g_warning("Compose: color allocation failed.\n");
873 style = gtk_widget_get_style(GTK_WIDGET(text));
874 quote_color1 = quote_color2 = quote_color3 =
875 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
876 signature_color = uri_color = black;
881 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
882 GPtrArray *attach_files)
884 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
887 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
889 return compose_generic_new(account, mailto, item, NULL, NULL);
892 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
894 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
897 #define SCROLL_TO_CURSOR(compose) { \
898 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
899 gtk_text_view_get_buffer( \
900 GTK_TEXT_VIEW(compose->text))); \
901 gtk_text_view_scroll_mark_onscreen( \
902 GTK_TEXT_VIEW(compose->text), \
906 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
909 if (folderidentifier) {
910 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
911 prefs_common.compose_save_to_history = add_history(
912 prefs_common.compose_save_to_history, folderidentifier);
913 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
914 prefs_common.compose_save_to_history);
917 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
918 if (folderidentifier)
919 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
921 gtk_entry_set_text(GTK_ENTRY(entry), "");
924 static gchar *compose_get_save_to(Compose *compose)
927 gchar *result = NULL;
928 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
929 result = gtk_editable_get_chars(entry, 0, -1);
932 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
933 prefs_common.compose_save_to_history = add_history(
934 prefs_common.compose_save_to_history, result);
935 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
936 prefs_common.compose_save_to_history);
941 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
942 GPtrArray *attach_files, GList *listAddress )
945 GtkTextView *textview;
946 GtkTextBuffer *textbuf;
948 const gchar *subject_format = NULL;
949 const gchar *body_format = NULL;
950 gchar *mailto_from = NULL;
951 PrefsAccount *mailto_account = NULL;
952 MsgInfo* dummyinfo = NULL;
953 MailField mfield = NO_FIELD_PRESENT;
957 /* check if mailto defines a from */
958 if (mailto && *mailto != '\0') {
959 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
960 /* mailto defines a from, check if we can get account prefs from it,
961 if not, the account prefs will be guessed using other ways, but we'll keep
964 mailto_account = account_find_from_address(mailto_from, TRUE);
966 account = mailto_account;
969 /* if no account prefs set from mailto, set if from folder prefs (if any) */
970 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
971 account = account_find_from_id(item->prefs->default_account);
973 /* if no account prefs set, fallback to the current one */
974 if (!account) account = cur_account;
975 cm_return_val_if_fail(account != NULL, NULL);
977 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
979 /* override from name if mailto asked for it */
981 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
984 /* override from name according to folder properties */
985 if (item && item->prefs &&
986 item->prefs->compose_with_format &&
987 item->prefs->compose_override_from_format &&
988 *item->prefs->compose_override_from_format != '\0') {
993 dummyinfo = compose_msginfo_new_from_compose(compose);
995 /* decode \-escape sequences in the internal representation of the quote format */
996 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
997 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1000 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1001 compose->gtkaspell);
1003 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1005 quote_fmt_scan_string(tmp);
1008 buf = quote_fmt_get_buffer();
1010 alertpanel_error(_("New message From format error."));
1012 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1013 quote_fmt_reset_vartable();
1018 compose->replyinfo = NULL;
1019 compose->fwdinfo = NULL;
1021 textview = GTK_TEXT_VIEW(compose->text);
1022 textbuf = gtk_text_view_get_buffer(textview);
1023 compose_create_tags(textview, compose);
1025 undo_block(compose->undostruct);
1027 compose_set_dictionaries_from_folder_prefs(compose, item);
1030 if (account->auto_sig)
1031 compose_insert_sig(compose, FALSE);
1032 gtk_text_buffer_get_start_iter(textbuf, &iter);
1033 gtk_text_buffer_place_cursor(textbuf, &iter);
1035 if (account->protocol != A_NNTP) {
1036 if (mailto && *mailto != '\0') {
1037 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1040 compose_set_folder_prefs(compose, item, TRUE);
1042 if (item && item->ret_rcpt) {
1043 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1046 if (mailto && *mailto != '\0') {
1047 if (!strchr(mailto, '@'))
1048 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1051 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1052 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1053 mfield = TO_FIELD_PRESENT;
1056 * CLAWS: just don't allow return receipt request, even if the user
1057 * may want to send an email. simple but foolproof.
1059 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1061 compose_add_field_list( compose, listAddress );
1063 if (item && item->prefs && item->prefs->compose_with_format) {
1064 subject_format = item->prefs->compose_subject_format;
1065 body_format = item->prefs->compose_body_format;
1066 } else if (account->compose_with_format) {
1067 subject_format = account->compose_subject_format;
1068 body_format = account->compose_body_format;
1069 } else if (prefs_common.compose_with_format) {
1070 subject_format = prefs_common.compose_subject_format;
1071 body_format = prefs_common.compose_body_format;
1074 if (subject_format || body_format) {
1077 && *subject_format != '\0' )
1079 gchar *subject = NULL;
1084 dummyinfo = compose_msginfo_new_from_compose(compose);
1086 /* decode \-escape sequences in the internal representation of the quote format */
1087 tmp = g_malloc(strlen(subject_format)+1);
1088 pref_get_unescaped_pref(tmp, subject_format);
1090 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1092 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1093 compose->gtkaspell);
1095 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1097 quote_fmt_scan_string(tmp);
1100 buf = quote_fmt_get_buffer();
1102 alertpanel_error(_("New message subject format error."));
1104 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1105 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1106 quote_fmt_reset_vartable();
1110 mfield = SUBJECT_FIELD_PRESENT;
1114 && *body_format != '\0' )
1117 GtkTextBuffer *buffer;
1118 GtkTextIter start, end;
1122 dummyinfo = compose_msginfo_new_from_compose(compose);
1124 text = GTK_TEXT_VIEW(compose->text);
1125 buffer = gtk_text_view_get_buffer(text);
1126 gtk_text_buffer_get_start_iter(buffer, &start);
1127 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1128 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1130 compose_quote_fmt(compose, dummyinfo,
1132 NULL, tmp, FALSE, TRUE,
1133 _("The body of the \"New message\" template has an error at line %d."));
1134 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1135 quote_fmt_reset_vartable();
1139 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1140 gtkaspell_highlight_all(compose->gtkaspell);
1142 mfield = BODY_FIELD_PRESENT;
1146 procmsg_msginfo_free( dummyinfo );
1152 for (i = 0; i < attach_files->len; i++) {
1153 file = g_ptr_array_index(attach_files, i);
1154 compose_attach_append(compose, file, file, NULL, NULL);
1158 compose_show_first_last_header(compose, TRUE);
1160 /* Set save folder */
1161 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1162 gchar *folderidentifier;
1164 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1165 folderidentifier = folder_item_get_identifier(item);
1166 compose_set_save_to(compose, folderidentifier);
1167 g_free(folderidentifier);
1170 /* Place cursor according to provided input (mfield) */
1172 case NO_FIELD_PRESENT:
1173 if (compose->header_last)
1174 gtk_widget_grab_focus(compose->header_last->entry);
1176 case TO_FIELD_PRESENT:
1177 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1182 gtk_widget_grab_focus(compose->subject_entry);
1184 case SUBJECT_FIELD_PRESENT:
1185 textview = GTK_TEXT_VIEW(compose->text);
1188 textbuf = gtk_text_view_get_buffer(textview);
1191 mark = gtk_text_buffer_get_insert(textbuf);
1192 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1193 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1195 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1196 * only defers where it comes to the variable body
1197 * is not null. If no body is present compose->text
1198 * will be null in which case you cannot place the
1199 * cursor inside the component so. An empty component
1200 * is therefore created before placing the cursor
1202 case BODY_FIELD_PRESENT:
1203 gtk_widget_grab_focus(compose->text);
1207 undo_unblock(compose->undostruct);
1209 if (prefs_common.auto_exteditor)
1210 compose_exec_ext_editor(compose);
1212 compose->draft_timeout_tag = -1;
1213 SCROLL_TO_CURSOR(compose);
1215 compose->modified = FALSE;
1216 compose_set_title(compose);
1218 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1223 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1224 gboolean override_pref, const gchar *system)
1226 const gchar *privacy = NULL;
1228 cm_return_if_fail(compose != NULL);
1229 cm_return_if_fail(account != NULL);
1231 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1236 else if (account->default_privacy_system
1237 && strlen(account->default_privacy_system)) {
1238 privacy = account->default_privacy_system;
1240 GSList *privacy_avail = privacy_get_system_ids();
1241 if (privacy_avail && g_slist_length(privacy_avail)) {
1242 privacy = (gchar *)(privacy_avail->data);
1245 if (privacy != NULL) {
1247 g_free(compose->privacy_system);
1248 compose->privacy_system = NULL;
1250 if (compose->privacy_system == NULL)
1251 compose->privacy_system = g_strdup(privacy);
1252 else if (*(compose->privacy_system) == '\0') {
1253 g_free(compose->privacy_system);
1254 compose->privacy_system = g_strdup(privacy);
1256 compose_update_privacy_system_menu_item(compose, FALSE);
1257 compose_use_encryption(compose, TRUE);
1261 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1263 const gchar *privacy = NULL;
1267 else if (account->default_privacy_system
1268 && strlen(account->default_privacy_system)) {
1269 privacy = account->default_privacy_system;
1271 GSList *privacy_avail = privacy_get_system_ids();
1272 if (privacy_avail && g_slist_length(privacy_avail)) {
1273 privacy = (gchar *)(privacy_avail->data);
1277 if (privacy != NULL) {
1279 g_free(compose->privacy_system);
1280 compose->privacy_system = NULL;
1282 if (compose->privacy_system == NULL)
1283 compose->privacy_system = g_strdup(privacy);
1284 compose_update_privacy_system_menu_item(compose, FALSE);
1285 compose_use_signing(compose, TRUE);
1289 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1293 Compose *compose = NULL;
1295 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1297 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1298 cm_return_val_if_fail(msginfo != NULL, NULL);
1300 list_len = g_slist_length(msginfo_list);
1304 case COMPOSE_REPLY_TO_ADDRESS:
1305 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1306 FALSE, prefs_common.default_reply_list, FALSE, body);
1308 case COMPOSE_REPLY_WITH_QUOTE:
1309 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1310 FALSE, prefs_common.default_reply_list, FALSE, body);
1312 case COMPOSE_REPLY_WITHOUT_QUOTE:
1313 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1314 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1316 case COMPOSE_REPLY_TO_SENDER:
1317 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1318 FALSE, FALSE, TRUE, body);
1320 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1321 compose = compose_followup_and_reply_to(msginfo,
1322 COMPOSE_QUOTE_CHECK,
1323 FALSE, FALSE, body);
1325 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1326 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1327 FALSE, FALSE, TRUE, body);
1329 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1330 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1331 FALSE, FALSE, TRUE, NULL);
1333 case COMPOSE_REPLY_TO_ALL:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1335 TRUE, FALSE, FALSE, body);
1337 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1338 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1339 TRUE, FALSE, FALSE, body);
1341 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1342 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1343 TRUE, FALSE, FALSE, NULL);
1345 case COMPOSE_REPLY_TO_LIST:
1346 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1347 FALSE, TRUE, FALSE, body);
1349 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1350 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1351 FALSE, TRUE, FALSE, body);
1353 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1354 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1355 FALSE, TRUE, FALSE, NULL);
1357 case COMPOSE_FORWARD:
1358 if (prefs_common.forward_as_attachment) {
1359 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1362 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1366 case COMPOSE_FORWARD_INLINE:
1367 /* check if we reply to more than one Message */
1368 if (list_len == 1) {
1369 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1372 /* more messages FALL THROUGH */
1373 case COMPOSE_FORWARD_AS_ATTACH:
1374 compose = compose_forward_multiple(NULL, msginfo_list);
1376 case COMPOSE_REDIRECT:
1377 compose = compose_redirect(NULL, msginfo, FALSE);
1380 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1383 if (compose == NULL) {
1384 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1388 compose->rmode = mode;
1389 switch (compose->rmode) {
1391 case COMPOSE_REPLY_WITH_QUOTE:
1392 case COMPOSE_REPLY_WITHOUT_QUOTE:
1393 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1394 debug_print("reply mode Normal\n");
1395 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1396 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1398 case COMPOSE_REPLY_TO_SENDER:
1399 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1400 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1401 debug_print("reply mode Sender\n");
1402 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1404 case COMPOSE_REPLY_TO_ALL:
1405 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1406 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1407 debug_print("reply mode All\n");
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1410 case COMPOSE_REPLY_TO_LIST:
1411 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1412 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1413 debug_print("reply mode List\n");
1414 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1416 case COMPOSE_REPLY_TO_ADDRESS:
1417 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1425 static Compose *compose_reply(MsgInfo *msginfo,
1426 ComposeQuoteMode quote_mode,
1432 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1433 to_sender, FALSE, body);
1436 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1437 ComposeQuoteMode quote_mode,
1442 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1443 to_sender, TRUE, body);
1446 static void compose_extract_original_charset(Compose *compose)
1448 MsgInfo *info = NULL;
1449 if (compose->replyinfo) {
1450 info = compose->replyinfo;
1451 } else if (compose->fwdinfo) {
1452 info = compose->fwdinfo;
1453 } else if (compose->targetinfo) {
1454 info = compose->targetinfo;
1457 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1458 MimeInfo *partinfo = mimeinfo;
1459 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1460 partinfo = procmime_mimeinfo_next(partinfo);
1462 compose->orig_charset =
1463 g_strdup(procmime_mimeinfo_get_parameter(
1464 partinfo, "charset"));
1466 procmime_mimeinfo_free_all(mimeinfo);
1470 #define SIGNAL_BLOCK(buffer) { \
1471 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1472 G_CALLBACK(compose_changed_cb), \
1474 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1475 G_CALLBACK(text_inserted), \
1479 #define SIGNAL_UNBLOCK(buffer) { \
1480 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(compose_changed_cb), \
1483 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1484 G_CALLBACK(text_inserted), \
1488 static Compose *compose_generic_reply(MsgInfo *msginfo,
1489 ComposeQuoteMode quote_mode,
1490 gboolean to_all, gboolean to_ml,
1492 gboolean followup_and_reply_to,
1496 PrefsAccount *account = NULL;
1497 GtkTextView *textview;
1498 GtkTextBuffer *textbuf;
1499 gboolean quote = FALSE;
1500 const gchar *qmark = NULL;
1501 const gchar *body_fmt = NULL;
1502 gchar *s_system = NULL;
1504 cm_return_val_if_fail(msginfo != NULL, NULL);
1505 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1507 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1509 cm_return_val_if_fail(account != NULL, NULL);
1511 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1513 compose->updating = TRUE;
1515 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1516 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1518 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1519 if (!compose->replyinfo)
1520 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1522 compose_extract_original_charset(compose);
1524 if (msginfo->folder && msginfo->folder->ret_rcpt)
1525 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1527 /* Set save folder */
1528 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1529 gchar *folderidentifier;
1531 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1532 folderidentifier = folder_item_get_identifier(msginfo->folder);
1533 compose_set_save_to(compose, folderidentifier);
1534 g_free(folderidentifier);
1537 if (compose_parse_header(compose, msginfo) < 0) {
1538 compose->updating = FALSE;
1539 compose_destroy(compose);
1543 /* override from name according to folder properties */
1544 if (msginfo->folder && msginfo->folder->prefs &&
1545 msginfo->folder->prefs->reply_with_format &&
1546 msginfo->folder->prefs->reply_override_from_format &&
1547 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1552 /* decode \-escape sequences in the internal representation of the quote format */
1553 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1554 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1557 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1558 compose->gtkaspell);
1560 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1562 quote_fmt_scan_string(tmp);
1565 buf = quote_fmt_get_buffer();
1567 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1569 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1570 quote_fmt_reset_vartable();
1575 textview = (GTK_TEXT_VIEW(compose->text));
1576 textbuf = gtk_text_view_get_buffer(textview);
1577 compose_create_tags(textview, compose);
1579 undo_block(compose->undostruct);
1581 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1584 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1585 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1586 /* use the reply format of folder (if enabled), or the account's one
1587 (if enabled) or fallback to the global reply format, which is always
1588 enabled (even if empty), and use the relevant quotemark */
1590 if (msginfo->folder && msginfo->folder->prefs &&
1591 msginfo->folder->prefs->reply_with_format) {
1592 qmark = msginfo->folder->prefs->reply_quotemark;
1593 body_fmt = msginfo->folder->prefs->reply_body_format;
1595 } else if (account->reply_with_format) {
1596 qmark = account->reply_quotemark;
1597 body_fmt = account->reply_body_format;
1600 qmark = prefs_common.quotemark;
1601 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1602 body_fmt = gettext(prefs_common.quotefmt);
1609 /* empty quotemark is not allowed */
1610 if (qmark == NULL || *qmark == '\0')
1612 compose_quote_fmt(compose, compose->replyinfo,
1613 body_fmt, qmark, body, FALSE, TRUE,
1614 _("The body of the \"Reply\" template has an error at line %d."));
1615 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1616 quote_fmt_reset_vartable();
1618 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1619 gtkaspell_highlight_all(compose->gtkaspell);
1623 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1624 compose_force_encryption(compose, account, FALSE, s_system);
1627 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1628 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1629 compose_force_signing(compose, account, s_system);
1633 SIGNAL_BLOCK(textbuf);
1635 if (account->auto_sig)
1636 compose_insert_sig(compose, FALSE);
1638 compose_wrap_all(compose);
1640 SIGNAL_UNBLOCK(textbuf);
1642 gtk_widget_grab_focus(compose->text);
1644 undo_unblock(compose->undostruct);
1646 if (prefs_common.auto_exteditor)
1647 compose_exec_ext_editor(compose);
1649 compose->modified = FALSE;
1650 compose_set_title(compose);
1652 compose->updating = FALSE;
1653 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1654 SCROLL_TO_CURSOR(compose);
1656 if (compose->deferred_destroy) {
1657 compose_destroy(compose);
1665 #define INSERT_FW_HEADER(var, hdr) \
1666 if (msginfo->var && *msginfo->var) { \
1667 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1668 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1669 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1672 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1673 gboolean as_attach, const gchar *body,
1674 gboolean no_extedit,
1678 GtkTextView *textview;
1679 GtkTextBuffer *textbuf;
1683 cm_return_val_if_fail(msginfo != NULL, NULL);
1684 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1687 !(account = compose_guess_forward_account_from_msginfo
1689 account = cur_account;
1691 if (!prefs_common.forward_as_attachment)
1692 mode = COMPOSE_FORWARD_INLINE;
1694 mode = COMPOSE_FORWARD;
1695 compose = compose_create(account, msginfo->folder, mode, batch);
1697 compose->updating = TRUE;
1698 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1699 if (!compose->fwdinfo)
1700 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1702 compose_extract_original_charset(compose);
1704 if (msginfo->subject && *msginfo->subject) {
1705 gchar *buf, *buf2, *p;
1707 buf = p = g_strdup(msginfo->subject);
1708 p += subject_get_prefix_length(p);
1709 memmove(buf, p, strlen(p) + 1);
1711 buf2 = g_strdup_printf("Fw: %s", buf);
1712 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1718 /* override from name according to folder properties */
1719 if (msginfo->folder && msginfo->folder->prefs &&
1720 msginfo->folder->prefs->forward_with_format &&
1721 msginfo->folder->prefs->forward_override_from_format &&
1722 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1726 MsgInfo *full_msginfo = NULL;
1729 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1731 full_msginfo = procmsg_msginfo_copy(msginfo);
1733 /* decode \-escape sequences in the internal representation of the quote format */
1734 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1735 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1738 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1739 compose->gtkaspell);
1741 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1743 quote_fmt_scan_string(tmp);
1746 buf = quote_fmt_get_buffer();
1748 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1750 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1751 quote_fmt_reset_vartable();
1754 procmsg_msginfo_free(full_msginfo);
1757 textview = GTK_TEXT_VIEW(compose->text);
1758 textbuf = gtk_text_view_get_buffer(textview);
1759 compose_create_tags(textview, compose);
1761 undo_block(compose->undostruct);
1765 msgfile = procmsg_get_message_file(msginfo);
1766 if (!is_file_exist(msgfile))
1767 g_warning("%s: file not exist\n", msgfile);
1769 compose_attach_append(compose, msgfile, msgfile,
1770 "message/rfc822", NULL);
1774 const gchar *qmark = NULL;
1775 const gchar *body_fmt = NULL;
1776 MsgInfo *full_msginfo;
1778 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1779 body_fmt = gettext(prefs_common.fw_quotefmt);
1783 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1785 full_msginfo = procmsg_msginfo_copy(msginfo);
1787 /* use the forward format of folder (if enabled), or the account's one
1788 (if enabled) or fallback to the global forward format, which is always
1789 enabled (even if empty), and use the relevant quotemark */
1790 if (msginfo->folder && msginfo->folder->prefs &&
1791 msginfo->folder->prefs->forward_with_format) {
1792 qmark = msginfo->folder->prefs->forward_quotemark;
1793 body_fmt = msginfo->folder->prefs->forward_body_format;
1795 } else if (account->forward_with_format) {
1796 qmark = account->forward_quotemark;
1797 body_fmt = account->forward_body_format;
1800 qmark = prefs_common.fw_quotemark;
1801 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1802 body_fmt = gettext(prefs_common.fw_quotefmt);
1807 /* empty quotemark is not allowed */
1808 if (qmark == NULL || *qmark == '\0')
1811 compose_quote_fmt(compose, full_msginfo,
1812 body_fmt, qmark, body, FALSE, TRUE,
1813 _("The body of the \"Forward\" template has an error at line %d."));
1814 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1815 quote_fmt_reset_vartable();
1816 compose_attach_parts(compose, msginfo);
1818 procmsg_msginfo_free(full_msginfo);
1820 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1821 gtkaspell_highlight_all(compose->gtkaspell);
1825 SIGNAL_BLOCK(textbuf);
1827 if (account->auto_sig)
1828 compose_insert_sig(compose, FALSE);
1830 compose_wrap_all(compose);
1832 SIGNAL_UNBLOCK(textbuf);
1834 gtk_text_buffer_get_start_iter(textbuf, &iter);
1835 gtk_text_buffer_place_cursor(textbuf, &iter);
1837 gtk_widget_grab_focus(compose->header_last->entry);
1839 if (!no_extedit && prefs_common.auto_exteditor)
1840 compose_exec_ext_editor(compose);
1843 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1844 gchar *folderidentifier;
1846 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1847 folderidentifier = folder_item_get_identifier(msginfo->folder);
1848 compose_set_save_to(compose, folderidentifier);
1849 g_free(folderidentifier);
1852 undo_unblock(compose->undostruct);
1854 compose->modified = FALSE;
1855 compose_set_title(compose);
1857 compose->updating = FALSE;
1858 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1859 SCROLL_TO_CURSOR(compose);
1861 if (compose->deferred_destroy) {
1862 compose_destroy(compose);
1866 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1871 #undef INSERT_FW_HEADER
1873 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1876 GtkTextView *textview;
1877 GtkTextBuffer *textbuf;
1881 gboolean single_mail = TRUE;
1883 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1885 if (g_slist_length(msginfo_list) > 1)
1886 single_mail = FALSE;
1888 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1889 if (((MsgInfo *)msginfo->data)->folder == NULL)
1892 /* guess account from first selected message */
1894 !(account = compose_guess_forward_account_from_msginfo
1895 (msginfo_list->data)))
1896 account = cur_account;
1898 cm_return_val_if_fail(account != NULL, NULL);
1900 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1901 if (msginfo->data) {
1902 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1903 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1907 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1908 g_warning("no msginfo_list");
1912 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1914 compose->updating = TRUE;
1916 /* override from name according to folder properties */
1917 if (msginfo_list->data) {
1918 MsgInfo *msginfo = msginfo_list->data;
1920 if (msginfo->folder && msginfo->folder->prefs &&
1921 msginfo->folder->prefs->forward_with_format &&
1922 msginfo->folder->prefs->forward_override_from_format &&
1923 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1928 /* decode \-escape sequences in the internal representation of the quote format */
1929 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1930 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1933 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1934 compose->gtkaspell);
1936 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1938 quote_fmt_scan_string(tmp);
1941 buf = quote_fmt_get_buffer();
1943 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1945 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1946 quote_fmt_reset_vartable();
1952 textview = GTK_TEXT_VIEW(compose->text);
1953 textbuf = gtk_text_view_get_buffer(textview);
1954 compose_create_tags(textview, compose);
1956 undo_block(compose->undostruct);
1957 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1958 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1960 if (!is_file_exist(msgfile))
1961 g_warning("%s: file not exist\n", msgfile);
1963 compose_attach_append(compose, msgfile, msgfile,
1964 "message/rfc822", NULL);
1969 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1970 if (info->subject && *info->subject) {
1971 gchar *buf, *buf2, *p;
1973 buf = p = g_strdup(info->subject);
1974 p += subject_get_prefix_length(p);
1975 memmove(buf, p, strlen(p) + 1);
1977 buf2 = g_strdup_printf("Fw: %s", buf);
1978 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1984 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1985 _("Fw: multiple emails"));
1988 SIGNAL_BLOCK(textbuf);
1990 if (account->auto_sig)
1991 compose_insert_sig(compose, FALSE);
1993 compose_wrap_all(compose);
1995 SIGNAL_UNBLOCK(textbuf);
1997 gtk_text_buffer_get_start_iter(textbuf, &iter);
1998 gtk_text_buffer_place_cursor(textbuf, &iter);
2000 gtk_widget_grab_focus(compose->header_last->entry);
2001 undo_unblock(compose->undostruct);
2002 compose->modified = FALSE;
2003 compose_set_title(compose);
2005 compose->updating = FALSE;
2006 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2007 SCROLL_TO_CURSOR(compose);
2009 if (compose->deferred_destroy) {
2010 compose_destroy(compose);
2014 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2019 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2021 GtkTextIter start = *iter;
2022 GtkTextIter end_iter;
2023 int start_pos = gtk_text_iter_get_offset(&start);
2025 if (!compose->account->sig_sep)
2028 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2029 start_pos+strlen(compose->account->sig_sep));
2031 /* check sig separator */
2032 str = gtk_text_iter_get_text(&start, &end_iter);
2033 if (!strcmp(str, compose->account->sig_sep)) {
2035 /* check end of line (\n) */
2036 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2037 start_pos+strlen(compose->account->sig_sep));
2038 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2039 start_pos+strlen(compose->account->sig_sep)+1);
2040 tmp = gtk_text_iter_get_text(&start, &end_iter);
2041 if (!strcmp(tmp,"\n")) {
2053 static void compose_colorize_signature(Compose *compose)
2055 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2057 GtkTextIter end_iter;
2058 gtk_text_buffer_get_start_iter(buffer, &iter);
2059 while (gtk_text_iter_forward_line(&iter))
2060 if (compose_is_sig_separator(compose, buffer, &iter)) {
2061 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2062 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2066 #define BLOCK_WRAP() { \
2067 prev_autowrap = compose->autowrap; \
2068 buffer = gtk_text_view_get_buffer( \
2069 GTK_TEXT_VIEW(compose->text)); \
2070 compose->autowrap = FALSE; \
2072 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2073 G_CALLBACK(compose_changed_cb), \
2075 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2076 G_CALLBACK(text_inserted), \
2079 #define UNBLOCK_WRAP() { \
2080 compose->autowrap = prev_autowrap; \
2081 if (compose->autowrap) { \
2082 gint old = compose->draft_timeout_tag; \
2083 compose->draft_timeout_tag = -2; \
2084 compose_wrap_all(compose); \
2085 compose->draft_timeout_tag = old; \
2088 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2089 G_CALLBACK(compose_changed_cb), \
2091 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2092 G_CALLBACK(text_inserted), \
2096 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2098 Compose *compose = NULL;
2099 PrefsAccount *account = NULL;
2100 GtkTextView *textview;
2101 GtkTextBuffer *textbuf;
2105 gchar buf[BUFFSIZE];
2106 gboolean use_signing = FALSE;
2107 gboolean use_encryption = FALSE;
2108 gchar *privacy_system = NULL;
2109 int priority = PRIORITY_NORMAL;
2110 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2111 gboolean autowrap = prefs_common.autowrap;
2112 gboolean autoindent = prefs_common.auto_indent;
2114 cm_return_val_if_fail(msginfo != NULL, NULL);
2115 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2117 if (compose_put_existing_to_front(msginfo)) {
2121 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2122 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2123 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2124 gchar queueheader_buf[BUFFSIZE];
2127 /* Select Account from queue headers */
2128 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2129 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2130 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2131 account = account_find_from_id(id);
2133 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2134 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2135 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2136 account = account_find_from_id(id);
2138 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2139 sizeof(queueheader_buf), "NAID:")) {
2140 id = atoi(&queueheader_buf[strlen("NAID:")]);
2141 account = account_find_from_id(id);
2143 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2144 sizeof(queueheader_buf), "MAID:")) {
2145 id = atoi(&queueheader_buf[strlen("MAID:")]);
2146 account = account_find_from_id(id);
2148 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2149 sizeof(queueheader_buf), "S:")) {
2150 account = account_find_from_address(queueheader_buf, FALSE);
2152 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2153 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2154 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2155 use_signing = param;
2158 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2159 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2160 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2161 use_signing = param;
2164 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2165 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2166 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2167 use_encryption = param;
2169 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2170 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2171 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2172 use_encryption = param;
2174 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2175 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2176 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2181 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2186 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2190 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Priority: ")) {
2194 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2197 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2198 sizeof(queueheader_buf), "RMID:")) {
2199 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2200 if (tokens[0] && tokens[1] && tokens[2]) {
2201 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2202 if (orig_item != NULL) {
2203 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "FMID:")) {
2210 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2211 if (tokens[0] && tokens[1] && tokens[2]) {
2212 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2213 if (orig_item != NULL) {
2214 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2220 account = msginfo->folder->folder->account;
2223 if (!account && prefs_common.reedit_account_autosel) {
2224 gchar from[BUFFSIZE];
2225 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2226 extract_address(from);
2227 account = account_find_from_address(from, FALSE);
2231 account = cur_account;
2233 cm_return_val_if_fail(account != NULL, NULL);
2235 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2237 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2238 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2239 compose->autowrap = autowrap;
2240 compose->replyinfo = replyinfo;
2241 compose->fwdinfo = fwdinfo;
2243 compose->updating = TRUE;
2244 compose->priority = priority;
2246 if (privacy_system != NULL) {
2247 compose->privacy_system = privacy_system;
2248 compose_use_signing(compose, use_signing);
2249 compose_use_encryption(compose, use_encryption);
2250 compose_update_privacy_system_menu_item(compose, FALSE);
2252 activate_privacy_system(compose, account, FALSE);
2255 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2257 compose_extract_original_charset(compose);
2259 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2260 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2261 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2262 gchar queueheader_buf[BUFFSIZE];
2264 /* Set message save folder */
2265 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2266 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2267 compose_set_save_to(compose, &queueheader_buf[4]);
2269 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2270 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2272 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2277 if (compose_parse_header(compose, msginfo) < 0) {
2278 compose->updating = FALSE;
2279 compose_destroy(compose);
2282 compose_reedit_set_entry(compose, msginfo);
2284 textview = GTK_TEXT_VIEW(compose->text);
2285 textbuf = gtk_text_view_get_buffer(textview);
2286 compose_create_tags(textview, compose);
2288 mark = gtk_text_buffer_get_insert(textbuf);
2289 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2291 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2292 G_CALLBACK(compose_changed_cb),
2295 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2296 fp = procmime_get_first_encrypted_text_content(msginfo);
2298 compose_force_encryption(compose, account, TRUE, NULL);
2301 fp = procmime_get_first_text_content(msginfo);
2304 g_warning("Can't get text part\n");
2308 gboolean prev_autowrap = compose->autowrap;
2309 GtkTextBuffer *buffer = textbuf;
2311 while (fgets(buf, sizeof(buf), fp) != NULL) {
2313 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2319 compose_attach_parts(compose, msginfo);
2321 compose_colorize_signature(compose);
2323 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2324 G_CALLBACK(compose_changed_cb),
2327 gtk_widget_grab_focus(compose->text);
2329 if (prefs_common.auto_exteditor) {
2330 compose_exec_ext_editor(compose);
2332 compose->modified = FALSE;
2333 compose_set_title(compose);
2335 compose->updating = FALSE;
2336 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2337 SCROLL_TO_CURSOR(compose);
2339 if (compose->deferred_destroy) {
2340 compose_destroy(compose);
2344 compose->sig_str = account_get_signature_str(compose->account);
2346 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2351 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2358 cm_return_val_if_fail(msginfo != NULL, NULL);
2361 account = account_get_reply_account(msginfo,
2362 prefs_common.reply_account_autosel);
2363 cm_return_val_if_fail(account != NULL, NULL);
2365 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2367 compose->updating = TRUE;
2369 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2370 compose->replyinfo = NULL;
2371 compose->fwdinfo = NULL;
2373 compose_show_first_last_header(compose, TRUE);
2375 gtk_widget_grab_focus(compose->header_last->entry);
2377 filename = procmsg_get_message_file(msginfo);
2379 if (filename == NULL) {
2380 compose->updating = FALSE;
2381 compose_destroy(compose);
2386 compose->redirect_filename = filename;
2388 /* Set save folder */
2389 item = msginfo->folder;
2390 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2391 gchar *folderidentifier;
2393 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2394 folderidentifier = folder_item_get_identifier(item);
2395 compose_set_save_to(compose, folderidentifier);
2396 g_free(folderidentifier);
2399 compose_attach_parts(compose, msginfo);
2401 if (msginfo->subject)
2402 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2404 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2406 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2407 _("The body of the \"Redirect\" template has an error at line %d."));
2408 quote_fmt_reset_vartable();
2409 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2411 compose_colorize_signature(compose);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2424 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2427 if (compose->toolbar->draft_btn)
2428 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2429 if (compose->toolbar->insert_btn)
2430 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2431 if (compose->toolbar->attach_btn)
2432 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2433 if (compose->toolbar->sig_btn)
2434 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2435 if (compose->toolbar->exteditor_btn)
2436 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2437 if (compose->toolbar->linewrap_current_btn)
2438 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2439 if (compose->toolbar->linewrap_all_btn)
2440 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2442 compose->modified = FALSE;
2443 compose_set_title(compose);
2444 compose->updating = FALSE;
2445 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2446 SCROLL_TO_CURSOR(compose);
2448 if (compose->deferred_destroy) {
2449 compose_destroy(compose);
2453 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2458 GList *compose_get_compose_list(void)
2460 return compose_list;
2463 void compose_entry_append(Compose *compose, const gchar *address,
2464 ComposeEntryType type, ComposePrefType pref_type)
2466 const gchar *header;
2468 gboolean in_quote = FALSE;
2469 if (!address || *address == '\0') return;
2476 header = N_("Bcc:");
2478 case COMPOSE_REPLYTO:
2479 header = N_("Reply-To:");
2481 case COMPOSE_NEWSGROUPS:
2482 header = N_("Newsgroups:");
2484 case COMPOSE_FOLLOWUPTO:
2485 header = N_( "Followup-To:");
2487 case COMPOSE_INREPLYTO:
2488 header = N_( "In-Reply-To:");
2495 header = prefs_common_translated_header_name(header);
2497 cur = begin = (gchar *)address;
2499 /* we separate the line by commas, but not if we're inside a quoted
2501 while (*cur != '\0') {
2503 in_quote = !in_quote;
2504 if (*cur == ',' && !in_quote) {
2505 gchar *tmp = g_strdup(begin);
2507 tmp[cur-begin]='\0';
2510 while (*tmp == ' ' || *tmp == '\t')
2512 compose_add_header_entry(compose, header, tmp, pref_type);
2519 gchar *tmp = g_strdup(begin);
2521 tmp[cur-begin]='\0';
2524 while (*tmp == ' ' || *tmp == '\t')
2526 compose_add_header_entry(compose, header, tmp, pref_type);
2531 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2533 static GdkColor yellow;
2534 static GdkColor black;
2535 static gboolean yellow_initialised = FALSE;
2539 if (!yellow_initialised) {
2540 gdk_color_parse("#f5f6be", &yellow);
2541 gdk_color_parse("#000000", &black);
2542 yellow_initialised = gdk_colormap_alloc_color(
2543 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2544 yellow_initialised &= gdk_colormap_alloc_color(
2545 gdk_colormap_get_system(), &black, FALSE, TRUE);
2548 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2549 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2550 if (gtk_entry_get_text(entry) &&
2551 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2552 if (yellow_initialised) {
2553 gtk_widget_modify_base(
2554 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2555 GTK_STATE_NORMAL, &yellow);
2556 gtk_widget_modify_text(
2557 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2558 GTK_STATE_NORMAL, &black);
2564 void compose_toolbar_cb(gint action, gpointer data)
2566 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2567 Compose *compose = (Compose*)toolbar_item->parent;
2569 cm_return_if_fail(compose != NULL);
2573 compose_send_cb(NULL, compose);
2576 compose_send_later_cb(NULL, compose);
2579 compose_draft(compose, COMPOSE_QUIT_EDITING);
2582 compose_insert_file_cb(NULL, compose);
2585 compose_attach_cb(NULL, compose);
2588 compose_insert_sig(compose, FALSE);
2591 compose_ext_editor_cb(NULL, compose);
2593 case A_LINEWRAP_CURRENT:
2594 compose_beautify_paragraph(compose, NULL, TRUE);
2596 case A_LINEWRAP_ALL:
2597 compose_wrap_all_full(compose, TRUE);
2600 compose_address_cb(NULL, compose);
2603 case A_CHECK_SPELLING:
2604 compose_check_all(NULL, compose);
2612 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2617 gchar *subject = NULL;
2621 gchar **attach = NULL;
2622 gchar *inreplyto = NULL;
2623 MailField mfield = NO_FIELD_PRESENT;
2625 /* get mailto parts but skip from */
2626 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2629 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2630 mfield = TO_FIELD_PRESENT;
2633 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2635 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2637 if (!g_utf8_validate (subject, -1, NULL)) {
2638 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2639 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2642 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2644 mfield = SUBJECT_FIELD_PRESENT;
2647 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2648 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2651 gboolean prev_autowrap = compose->autowrap;
2653 compose->autowrap = FALSE;
2655 mark = gtk_text_buffer_get_insert(buffer);
2656 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2658 if (!g_utf8_validate (body, -1, NULL)) {
2659 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2660 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2663 gtk_text_buffer_insert(buffer, &iter, body, -1);
2665 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2667 compose->autowrap = prev_autowrap;
2668 if (compose->autowrap)
2669 compose_wrap_all(compose);
2670 mfield = BODY_FIELD_PRESENT;
2674 gint i = 0, att = 0;
2675 gchar *warn_files = NULL;
2676 while (attach[i] != NULL) {
2677 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2678 if (utf8_filename) {
2679 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2680 gchar *tmp = g_strdup_printf("%s%s\n",
2681 warn_files?warn_files:"",
2687 g_free(utf8_filename);
2689 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2694 alertpanel_notice(ngettext(
2695 "The following file has been attached: \n%s",
2696 "The following files have been attached: \n%s", att), warn_files);
2701 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2714 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2716 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2717 {"Cc:", NULL, TRUE},
2718 {"References:", NULL, FALSE},
2719 {"Bcc:", NULL, TRUE},
2720 {"Newsgroups:", NULL, TRUE},
2721 {"Followup-To:", NULL, TRUE},
2722 {"List-Post:", NULL, FALSE},
2723 {"X-Priority:", NULL, FALSE},
2724 {NULL, NULL, FALSE}};
2740 cm_return_val_if_fail(msginfo != NULL, -1);
2742 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2743 procheader_get_header_fields(fp, hentry);
2746 if (hentry[H_REPLY_TO].body != NULL) {
2747 if (hentry[H_REPLY_TO].body[0] != '\0') {
2749 conv_unmime_header(hentry[H_REPLY_TO].body,
2752 g_free(hentry[H_REPLY_TO].body);
2753 hentry[H_REPLY_TO].body = NULL;
2755 if (hentry[H_CC].body != NULL) {
2756 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2757 g_free(hentry[H_CC].body);
2758 hentry[H_CC].body = NULL;
2760 if (hentry[H_REFERENCES].body != NULL) {
2761 if (compose->mode == COMPOSE_REEDIT)
2762 compose->references = hentry[H_REFERENCES].body;
2764 compose->references = compose_parse_references
2765 (hentry[H_REFERENCES].body, msginfo->msgid);
2766 g_free(hentry[H_REFERENCES].body);
2768 hentry[H_REFERENCES].body = NULL;
2770 if (hentry[H_BCC].body != NULL) {
2771 if (compose->mode == COMPOSE_REEDIT)
2773 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2774 g_free(hentry[H_BCC].body);
2775 hentry[H_BCC].body = NULL;
2777 if (hentry[H_NEWSGROUPS].body != NULL) {
2778 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2779 hentry[H_NEWSGROUPS].body = NULL;
2781 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2782 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2783 compose->followup_to =
2784 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2787 g_free(hentry[H_FOLLOWUP_TO].body);
2788 hentry[H_FOLLOWUP_TO].body = NULL;
2790 if (hentry[H_LIST_POST].body != NULL) {
2791 gchar *to = NULL, *start = NULL;
2793 extract_address(hentry[H_LIST_POST].body);
2794 if (hentry[H_LIST_POST].body[0] != '\0') {
2795 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2797 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2798 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2801 g_free(compose->ml_post);
2802 compose->ml_post = to;
2805 g_free(hentry[H_LIST_POST].body);
2806 hentry[H_LIST_POST].body = NULL;
2809 /* CLAWS - X-Priority */
2810 if (compose->mode == COMPOSE_REEDIT)
2811 if (hentry[H_X_PRIORITY].body != NULL) {
2814 priority = atoi(hentry[H_X_PRIORITY].body);
2815 g_free(hentry[H_X_PRIORITY].body);
2817 hentry[H_X_PRIORITY].body = NULL;
2819 if (priority < PRIORITY_HIGHEST ||
2820 priority > PRIORITY_LOWEST)
2821 priority = PRIORITY_NORMAL;
2823 compose->priority = priority;
2826 if (compose->mode == COMPOSE_REEDIT) {
2827 if (msginfo->inreplyto && *msginfo->inreplyto)
2828 compose->inreplyto = g_strdup(msginfo->inreplyto);
2832 if (msginfo->msgid && *msginfo->msgid)
2833 compose->inreplyto = g_strdup(msginfo->msgid);
2835 if (!compose->references) {
2836 if (msginfo->msgid && *msginfo->msgid) {
2837 if (msginfo->inreplyto && *msginfo->inreplyto)
2838 compose->references =
2839 g_strdup_printf("<%s>\n\t<%s>",
2843 compose->references =
2844 g_strconcat("<", msginfo->msgid, ">",
2846 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2847 compose->references =
2848 g_strconcat("<", msginfo->inreplyto, ">",
2856 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2858 GSList *ref_id_list, *cur;
2862 ref_id_list = references_list_append(NULL, ref);
2863 if (!ref_id_list) return NULL;
2864 if (msgid && *msgid)
2865 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2870 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2871 /* "<" + Message-ID + ">" + CR+LF+TAB */
2872 len += strlen((gchar *)cur->data) + 5;
2874 if (len > MAX_REFERENCES_LEN) {
2875 /* remove second message-ID */
2876 if (ref_id_list && ref_id_list->next &&
2877 ref_id_list->next->next) {
2878 g_free(ref_id_list->next->data);
2879 ref_id_list = g_slist_remove
2880 (ref_id_list, ref_id_list->next->data);
2882 slist_free_strings(ref_id_list);
2883 g_slist_free(ref_id_list);
2890 new_ref = g_string_new("");
2891 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2892 if (new_ref->len > 0)
2893 g_string_append(new_ref, "\n\t");
2894 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2897 slist_free_strings(ref_id_list);
2898 g_slist_free(ref_id_list);
2900 new_ref_str = new_ref->str;
2901 g_string_free(new_ref, FALSE);
2906 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2907 const gchar *fmt, const gchar *qmark,
2908 const gchar *body, gboolean rewrap,
2909 gboolean need_unescape,
2910 const gchar *err_msg)
2912 MsgInfo* dummyinfo = NULL;
2913 gchar *quote_str = NULL;
2915 gboolean prev_autowrap;
2916 const gchar *trimmed_body = body;
2917 gint cursor_pos = -1;
2918 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2919 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2924 SIGNAL_BLOCK(buffer);
2927 dummyinfo = compose_msginfo_new_from_compose(compose);
2928 msginfo = dummyinfo;
2931 if (qmark != NULL) {
2933 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2934 compose->gtkaspell);
2936 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2938 quote_fmt_scan_string(qmark);
2941 buf = quote_fmt_get_buffer();
2943 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2945 Xstrdup_a(quote_str, buf, goto error)
2948 if (fmt && *fmt != '\0') {
2951 while (*trimmed_body == '\n')
2955 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2956 compose->gtkaspell);
2958 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2960 if (need_unescape) {
2963 /* decode \-escape sequences in the internal representation of the quote format */
2964 tmp = g_malloc(strlen(fmt)+1);
2965 pref_get_unescaped_pref(tmp, fmt);
2966 quote_fmt_scan_string(tmp);
2970 quote_fmt_scan_string(fmt);
2974 buf = quote_fmt_get_buffer();
2976 gint line = quote_fmt_get_line();
2977 alertpanel_error(err_msg, line);
2983 prev_autowrap = compose->autowrap;
2984 compose->autowrap = FALSE;
2986 mark = gtk_text_buffer_get_insert(buffer);
2987 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2988 if (g_utf8_validate(buf, -1, NULL)) {
2989 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2991 gchar *tmpout = NULL;
2992 tmpout = conv_codeset_strdup
2993 (buf, conv_get_locale_charset_str_no_utf8(),
2995 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2997 tmpout = g_malloc(strlen(buf)*2+1);
2998 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3000 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3004 cursor_pos = quote_fmt_get_cursor_pos();
3005 if (cursor_pos == -1)
3006 cursor_pos = gtk_text_iter_get_offset(&iter);
3007 compose->set_cursor_pos = cursor_pos;
3009 gtk_text_buffer_get_start_iter(buffer, &iter);
3010 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3011 gtk_text_buffer_place_cursor(buffer, &iter);
3013 compose->autowrap = prev_autowrap;
3014 if (compose->autowrap && rewrap)
3015 compose_wrap_all(compose);
3022 SIGNAL_UNBLOCK(buffer);
3024 procmsg_msginfo_free( dummyinfo );
3029 /* if ml_post is of type addr@host and from is of type
3030 * addr-anything@host, return TRUE
3032 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3034 gchar *left_ml = NULL;
3035 gchar *right_ml = NULL;
3036 gchar *left_from = NULL;
3037 gchar *right_from = NULL;
3038 gboolean result = FALSE;
3040 if (!ml_post || !from)
3043 left_ml = g_strdup(ml_post);
3044 if (strstr(left_ml, "@")) {
3045 right_ml = strstr(left_ml, "@")+1;
3046 *(strstr(left_ml, "@")) = '\0';
3049 left_from = g_strdup(from);
3050 if (strstr(left_from, "@")) {
3051 right_from = strstr(left_from, "@")+1;
3052 *(strstr(left_from, "@")) = '\0';
3055 if (left_ml && left_from && right_ml && right_from
3056 && !strncmp(left_from, left_ml, strlen(left_ml))
3057 && !strcmp(right_from, right_ml)) {
3066 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3067 gboolean respect_default_to)
3071 if (!folder || !folder->prefs)
3074 if (respect_default_to && folder->prefs->enable_default_to) {
3075 compose_entry_append(compose, folder->prefs->default_to,
3076 COMPOSE_TO, PREF_FOLDER);
3077 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3079 if (folder->prefs->enable_default_cc)
3080 compose_entry_append(compose, folder->prefs->default_cc,
3081 COMPOSE_CC, PREF_FOLDER);
3082 if (folder->prefs->enable_default_bcc)
3083 compose_entry_append(compose, folder->prefs->default_bcc,
3084 COMPOSE_BCC, PREF_FOLDER);
3085 if (folder->prefs->enable_default_replyto)
3086 compose_entry_append(compose, folder->prefs->default_replyto,
3087 COMPOSE_REPLYTO, PREF_FOLDER);
3090 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3095 if (!compose || !msginfo)
3098 if (msginfo->subject && *msginfo->subject) {
3099 buf = p = g_strdup(msginfo->subject);
3100 p += subject_get_prefix_length(p);
3101 memmove(buf, p, strlen(p) + 1);
3103 buf2 = g_strdup_printf("Re: %s", buf);
3104 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3109 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3112 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3113 gboolean to_all, gboolean to_ml,
3115 gboolean followup_and_reply_to)
3117 GSList *cc_list = NULL;
3120 gchar *replyto = NULL;
3121 gchar *ac_email = NULL;
3123 gboolean reply_to_ml = FALSE;
3124 gboolean default_reply_to = FALSE;
3126 cm_return_if_fail(compose->account != NULL);
3127 cm_return_if_fail(msginfo != NULL);
3129 reply_to_ml = to_ml && compose->ml_post;
3131 default_reply_to = msginfo->folder &&
3132 msginfo->folder->prefs->enable_default_reply_to;
3134 if (compose->account->protocol != A_NNTP) {
3135 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3137 if (reply_to_ml && !default_reply_to) {
3139 gboolean is_subscr = is_subscription(compose->ml_post,
3142 /* normal answer to ml post with a reply-to */
3143 compose_entry_append(compose,
3145 COMPOSE_TO, PREF_ML);
3146 if (compose->replyto)
3147 compose_entry_append(compose,
3149 COMPOSE_CC, PREF_ML);
3151 /* answer to subscription confirmation */
3152 if (compose->replyto)
3153 compose_entry_append(compose,
3155 COMPOSE_TO, PREF_ML);
3156 else if (msginfo->from)
3157 compose_entry_append(compose,
3159 COMPOSE_TO, PREF_ML);
3162 else if (!(to_all || to_sender) && default_reply_to) {
3163 compose_entry_append(compose,
3164 msginfo->folder->prefs->default_reply_to,
3165 COMPOSE_TO, PREF_FOLDER);
3166 compose_entry_mark_default_to(compose,
3167 msginfo->folder->prefs->default_reply_to);
3172 Xstrdup_a(tmp1, msginfo->from, return);
3173 extract_address(tmp1);
3174 if (to_all || to_sender ||
3175 !account_find_from_address(tmp1, FALSE))
3176 compose_entry_append(compose,
3177 (compose->replyto && !to_sender)
3178 ? compose->replyto :
3179 msginfo->from ? msginfo->from : "",
3180 COMPOSE_TO, PREF_NONE);
3181 else if (!to_all && !to_sender) {
3182 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3183 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3184 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3185 if (compose->replyto) {
3186 compose_entry_append(compose,
3188 COMPOSE_TO, PREF_NONE);
3190 compose_entry_append(compose,
3191 msginfo->from ? msginfo->from : "",
3192 COMPOSE_TO, PREF_NONE);
3195 /* replying to own mail, use original recp */
3196 compose_entry_append(compose,
3197 msginfo->to ? msginfo->to : "",
3198 COMPOSE_TO, PREF_NONE);
3199 compose_entry_append(compose,
3200 msginfo->cc ? msginfo->cc : "",
3201 COMPOSE_CC, PREF_NONE);
3206 if (to_sender || (compose->followup_to &&
3207 !strncmp(compose->followup_to, "poster", 6)))
3208 compose_entry_append
3210 (compose->replyto ? compose->replyto :
3211 msginfo->from ? msginfo->from : ""),
3212 COMPOSE_TO, PREF_NONE);
3214 else if (followup_and_reply_to || to_all) {
3215 compose_entry_append
3217 (compose->replyto ? compose->replyto :
3218 msginfo->from ? msginfo->from : ""),
3219 COMPOSE_TO, PREF_NONE);
3221 compose_entry_append
3223 compose->followup_to ? compose->followup_to :
3224 compose->newsgroups ? compose->newsgroups : "",
3225 COMPOSE_NEWSGROUPS, PREF_NONE);
3228 compose_entry_append
3230 compose->followup_to ? compose->followup_to :
3231 compose->newsgroups ? compose->newsgroups : "",
3232 COMPOSE_NEWSGROUPS, PREF_NONE);
3234 compose_reply_set_subject(compose, msginfo);
3236 if (to_ml && compose->ml_post) return;
3237 if (!to_all || compose->account->protocol == A_NNTP) return;
3239 if (compose->replyto) {
3240 Xstrdup_a(replyto, compose->replyto, return);
3241 extract_address(replyto);
3243 if (msginfo->from) {
3244 Xstrdup_a(from, msginfo->from, return);
3245 extract_address(from);
3248 if (replyto && from)
3249 cc_list = address_list_append_with_comments(cc_list, from);
3250 if (to_all && msginfo->folder &&
3251 msginfo->folder->prefs->enable_default_reply_to)
3252 cc_list = address_list_append_with_comments(cc_list,
3253 msginfo->folder->prefs->default_reply_to);
3254 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3255 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3257 ac_email = g_utf8_strdown(compose->account->address, -1);
3260 for (cur = cc_list; cur != NULL; cur = cur->next) {
3261 gchar *addr = g_utf8_strdown(cur->data, -1);
3262 extract_address(addr);
3264 if (strcmp(ac_email, addr))
3265 compose_entry_append(compose, (gchar *)cur->data,
3266 COMPOSE_CC, PREF_NONE);
3268 debug_print("Cc address same as compose account's, ignoring\n");
3273 slist_free_strings(cc_list);
3274 g_slist_free(cc_list);
3280 #define SET_ENTRY(entry, str) \
3283 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3286 #define SET_ADDRESS(type, str) \
3289 compose_entry_append(compose, str, type, PREF_NONE); \
3292 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3294 cm_return_if_fail(msginfo != NULL);
3296 SET_ENTRY(subject_entry, msginfo->subject);
3297 SET_ENTRY(from_name, msginfo->from);
3298 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3299 SET_ADDRESS(COMPOSE_CC, compose->cc);
3300 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3301 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3302 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3303 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3305 compose_update_priority_menu_item(compose);
3306 compose_update_privacy_system_menu_item(compose, FALSE);
3307 compose_show_first_last_header(compose, TRUE);
3313 static void compose_insert_sig(Compose *compose, gboolean replace)
3315 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3316 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3318 GtkTextIter iter, iter_end;
3319 gint cur_pos, ins_pos;
3320 gboolean prev_autowrap;
3321 gboolean found = FALSE;
3322 gboolean exists = FALSE;
3324 cm_return_if_fail(compose->account != NULL);
3328 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3329 G_CALLBACK(compose_changed_cb),
3332 mark = gtk_text_buffer_get_insert(buffer);
3333 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3334 cur_pos = gtk_text_iter_get_offset (&iter);
3337 gtk_text_buffer_get_end_iter(buffer, &iter);
3339 exists = (compose->sig_str != NULL);
3342 GtkTextIter first_iter, start_iter, end_iter;
3344 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3346 if (!exists || compose->sig_str[0] == '\0')
3349 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3350 compose->signature_tag);
3353 /* include previous \n\n */
3354 gtk_text_iter_backward_chars(&first_iter, 1);
3355 start_iter = first_iter;
3356 end_iter = first_iter;
3358 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3359 compose->signature_tag);
3360 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3361 compose->signature_tag);
3363 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3369 g_free(compose->sig_str);
3370 compose->sig_str = account_get_signature_str(compose->account);
3372 cur_pos = gtk_text_iter_get_offset(&iter);
3374 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3375 g_free(compose->sig_str);
3376 compose->sig_str = NULL;
3378 if (compose->sig_inserted == FALSE)
3379 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3380 compose->sig_inserted = TRUE;
3382 cur_pos = gtk_text_iter_get_offset(&iter);
3383 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3385 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3386 gtk_text_iter_forward_chars(&iter, 1);
3387 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3388 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3390 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3391 cur_pos = gtk_text_buffer_get_char_count (buffer);
3394 /* put the cursor where it should be
3395 * either where the quote_fmt says, either where it was */
3396 if (compose->set_cursor_pos < 0)
3397 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3399 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3400 compose->set_cursor_pos);
3402 compose->set_cursor_pos = -1;
3403 gtk_text_buffer_place_cursor(buffer, &iter);
3404 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3405 G_CALLBACK(compose_changed_cb),
3411 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3414 GtkTextBuffer *buffer;
3417 const gchar *cur_encoding;
3418 gchar buf[BUFFSIZE];
3421 gboolean prev_autowrap;
3422 gboolean badtxt = FALSE;
3423 struct stat file_stat;
3426 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3428 /* get the size of the file we are about to insert */
3429 ret = g_stat(file, &file_stat);
3431 gchar *shortfile = g_path_get_basename(file);
3432 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3434 return COMPOSE_INSERT_NO_FILE;
3435 } else if (prefs_common.warn_large_insert == TRUE) {
3437 /* ask user for confirmation if the file is large */
3438 if (prefs_common.warn_large_insert_size < 0 ||
3439 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3443 msg = g_strdup_printf(_("You are about to insert a file of %s "
3444 "in the message body. Are you sure you want to do that?"),
3445 to_human_readable(file_stat.st_size));
3446 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3447 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3450 /* do we ask for confirmation next time? */
3451 if (aval & G_ALERTDISABLE) {
3452 /* no confirmation next time, disable feature in preferences */
3453 aval &= ~G_ALERTDISABLE;
3454 prefs_common.warn_large_insert = FALSE;
3457 /* abort file insertion if user canceled action */
3458 if (aval != G_ALERTALTERNATE) {
3459 return COMPOSE_INSERT_NO_FILE;
3465 if ((fp = g_fopen(file, "rb")) == NULL) {
3466 FILE_OP_ERROR(file, "fopen");
3467 return COMPOSE_INSERT_READ_ERROR;
3470 prev_autowrap = compose->autowrap;
3471 compose->autowrap = FALSE;
3473 text = GTK_TEXT_VIEW(compose->text);
3474 buffer = gtk_text_view_get_buffer(text);
3475 mark = gtk_text_buffer_get_insert(buffer);
3476 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3478 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3479 G_CALLBACK(text_inserted),
3482 cur_encoding = conv_get_locale_charset_str_no_utf8();
3484 while (fgets(buf, sizeof(buf), fp) != NULL) {
3487 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3488 str = g_strdup(buf);
3490 str = conv_codeset_strdup
3491 (buf, cur_encoding, CS_INTERNAL);
3494 /* strip <CR> if DOS/Windows file,
3495 replace <CR> with <LF> if Macintosh file. */
3498 if (len > 0 && str[len - 1] != '\n') {
3500 if (str[len] == '\r') str[len] = '\n';
3503 gtk_text_buffer_insert(buffer, &iter, str, -1);
3507 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3508 G_CALLBACK(text_inserted),
3510 compose->autowrap = prev_autowrap;
3511 if (compose->autowrap)
3512 compose_wrap_all(compose);
3517 return COMPOSE_INSERT_INVALID_CHARACTER;
3519 return COMPOSE_INSERT_SUCCESS;
3522 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3523 const gchar *filename,
3524 const gchar *content_type,
3525 const gchar *charset)
3533 GtkListStore *store;
3535 gboolean has_binary = FALSE;
3537 if (!is_file_exist(file)) {
3538 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3539 gboolean result = FALSE;
3540 if (file_from_uri && is_file_exist(file_from_uri)) {
3541 result = compose_attach_append(
3542 compose, file_from_uri,
3543 filename, content_type,
3546 g_free(file_from_uri);
3549 alertpanel_error("File %s doesn't exist\n", filename);
3552 if ((size = get_file_size(file)) < 0) {
3553 alertpanel_error("Can't get file size of %s\n", filename);
3557 alertpanel_error(_("File %s is empty."), filename);
3560 if ((fp = g_fopen(file, "rb")) == NULL) {
3561 alertpanel_error(_("Can't read %s."), filename);
3566 ainfo = g_new0(AttachInfo, 1);
3567 auto_ainfo = g_auto_pointer_new_with_free
3568 (ainfo, (GFreeFunc) compose_attach_info_free);
3569 ainfo->file = g_strdup(file);
3572 ainfo->content_type = g_strdup(content_type);
3573 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3575 MsgFlags flags = {0, 0};
3577 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3578 ainfo->encoding = ENC_7BIT;
3580 ainfo->encoding = ENC_8BIT;
3582 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3583 if (msginfo && msginfo->subject)
3584 name = g_strdup(msginfo->subject);
3586 name = g_path_get_basename(filename ? filename : file);
3588 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3590 procmsg_msginfo_free(msginfo);
3592 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3593 ainfo->charset = g_strdup(charset);
3594 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3596 ainfo->encoding = ENC_BASE64;
3598 name = g_path_get_basename(filename ? filename : file);
3599 ainfo->name = g_strdup(name);
3603 ainfo->content_type = procmime_get_mime_type(file);
3604 if (!ainfo->content_type) {
3605 ainfo->content_type =
3606 g_strdup("application/octet-stream");
3607 ainfo->encoding = ENC_BASE64;
3608 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3610 procmime_get_encoding_for_text_file(file, &has_binary);
3612 ainfo->encoding = ENC_BASE64;
3613 name = g_path_get_basename(filename ? filename : file);
3614 ainfo->name = g_strdup(name);
3618 if (ainfo->name != NULL
3619 && !strcmp(ainfo->name, ".")) {
3620 g_free(ainfo->name);
3624 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3625 g_free(ainfo->content_type);
3626 ainfo->content_type = g_strdup("application/octet-stream");
3627 g_free(ainfo->charset);
3628 ainfo->charset = NULL;
3631 ainfo->size = (goffset)size;
3632 size_text = to_human_readable((goffset)size);
3634 store = GTK_LIST_STORE(gtk_tree_view_get_model
3635 (GTK_TREE_VIEW(compose->attach_clist)));
3637 gtk_list_store_append(store, &iter);
3638 gtk_list_store_set(store, &iter,
3639 COL_MIMETYPE, ainfo->content_type,
3640 COL_SIZE, size_text,
3641 COL_NAME, ainfo->name,
3642 COL_CHARSET, ainfo->charset,
3644 COL_AUTODATA, auto_ainfo,
3647 g_auto_pointer_free(auto_ainfo);
3648 compose_attach_update_label(compose);
3652 static void compose_use_signing(Compose *compose, gboolean use_signing)
3654 compose->use_signing = use_signing;
3655 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3658 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3660 compose->use_encryption = use_encryption;
3661 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3664 #define NEXT_PART_NOT_CHILD(info) \
3666 node = info->node; \
3667 while (node->children) \
3668 node = g_node_last_child(node); \
3669 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3672 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3676 MimeInfo *firsttext = NULL;
3677 MimeInfo *encrypted = NULL;
3680 const gchar *partname = NULL;
3682 mimeinfo = procmime_scan_message(msginfo);
3683 if (!mimeinfo) return;
3685 if (mimeinfo->node->children == NULL) {
3686 procmime_mimeinfo_free_all(mimeinfo);
3690 /* find first content part */
3691 child = (MimeInfo *) mimeinfo->node->children->data;
3692 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3693 child = (MimeInfo *)child->node->children->data;
3696 if (child->type == MIMETYPE_TEXT) {
3698 debug_print("First text part found\n");
3699 } else if (compose->mode == COMPOSE_REEDIT &&
3700 child->type == MIMETYPE_APPLICATION &&
3701 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3702 encrypted = (MimeInfo *)child->node->parent->data;
3705 child = (MimeInfo *) mimeinfo->node->children->data;
3706 while (child != NULL) {
3709 if (child == encrypted) {
3710 /* skip this part of tree */
3711 NEXT_PART_NOT_CHILD(child);
3715 if (child->type == MIMETYPE_MULTIPART) {
3716 /* get the actual content */
3717 child = procmime_mimeinfo_next(child);
3721 if (child == firsttext) {
3722 child = procmime_mimeinfo_next(child);
3726 outfile = procmime_get_tmp_file_name(child);
3727 if ((err = procmime_get_part(outfile, child)) < 0)
3728 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3730 gchar *content_type;
3732 content_type = procmime_get_content_type_str(child->type, child->subtype);
3734 /* if we meet a pgp signature, we don't attach it, but
3735 * we force signing. */
3736 if ((strcmp(content_type, "application/pgp-signature") &&
3737 strcmp(content_type, "application/pkcs7-signature") &&
3738 strcmp(content_type, "application/x-pkcs7-signature"))
3739 || compose->mode == COMPOSE_REDIRECT) {
3740 partname = procmime_mimeinfo_get_parameter(child, "filename");
3741 if (partname == NULL)
3742 partname = procmime_mimeinfo_get_parameter(child, "name");
3743 if (partname == NULL)
3745 compose_attach_append(compose, outfile,
3746 partname, content_type,
3747 procmime_mimeinfo_get_parameter(child, "charset"));
3749 compose_force_signing(compose, compose->account, NULL);
3751 g_free(content_type);
3754 NEXT_PART_NOT_CHILD(child);
3756 procmime_mimeinfo_free_all(mimeinfo);
3759 #undef NEXT_PART_NOT_CHILD
3764 WAIT_FOR_INDENT_CHAR,
3765 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3768 /* return indent length, we allow:
3769 indent characters followed by indent characters or spaces/tabs,
3770 alphabets and numbers immediately followed by indent characters,
3771 and the repeating sequences of the above
3772 If quote ends with multiple spaces, only the first one is included. */
3773 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3774 const GtkTextIter *start, gint *len)
3776 GtkTextIter iter = *start;
3780 IndentState state = WAIT_FOR_INDENT_CHAR;
3783 gint alnum_count = 0;
3784 gint space_count = 0;
3787 if (prefs_common.quote_chars == NULL) {
3791 while (!gtk_text_iter_ends_line(&iter)) {
3792 wc = gtk_text_iter_get_char(&iter);
3793 if (g_unichar_iswide(wc))
3795 clen = g_unichar_to_utf8(wc, ch);
3799 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3800 is_space = g_unichar_isspace(wc);
3802 if (state == WAIT_FOR_INDENT_CHAR) {
3803 if (!is_indent && !g_unichar_isalnum(wc))
3806 quote_len += alnum_count + space_count + 1;
3807 alnum_count = space_count = 0;
3808 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3811 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3812 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3816 else if (is_indent) {
3817 quote_len += alnum_count + space_count + 1;
3818 alnum_count = space_count = 0;
3821 state = WAIT_FOR_INDENT_CHAR;
3825 gtk_text_iter_forward_char(&iter);
3828 if (quote_len > 0 && space_count > 0)
3834 if (quote_len > 0) {
3836 gtk_text_iter_forward_chars(&iter, quote_len);
3837 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3843 /* return >0 if the line is itemized */
3844 static int compose_itemized_length(GtkTextBuffer *buffer,
3845 const GtkTextIter *start)
3847 GtkTextIter iter = *start;
3852 if (gtk_text_iter_ends_line(&iter))
3857 wc = gtk_text_iter_get_char(&iter);
3858 if (!g_unichar_isspace(wc))
3860 gtk_text_iter_forward_char(&iter);
3861 if (gtk_text_iter_ends_line(&iter))
3865 clen = g_unichar_to_utf8(wc, ch);
3869 if (!strchr("*-+", ch[0]))
3872 gtk_text_iter_forward_char(&iter);
3873 if (gtk_text_iter_ends_line(&iter))
3875 wc = gtk_text_iter_get_char(&iter);
3876 if (g_unichar_isspace(wc)) {
3882 /* return the string at the start of the itemization */
3883 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3884 const GtkTextIter *start)
3886 GtkTextIter iter = *start;
3889 GString *item_chars = g_string_new("");
3892 if (gtk_text_iter_ends_line(&iter))
3897 wc = gtk_text_iter_get_char(&iter);
3898 if (!g_unichar_isspace(wc))
3900 gtk_text_iter_forward_char(&iter);
3901 if (gtk_text_iter_ends_line(&iter))
3903 g_string_append_unichar(item_chars, wc);
3906 str = item_chars->str;
3907 g_string_free(item_chars, FALSE);
3911 /* return the number of spaces at a line's start */
3912 static int compose_left_offset_length(GtkTextBuffer *buffer,
3913 const GtkTextIter *start)
3915 GtkTextIter iter = *start;
3918 if (gtk_text_iter_ends_line(&iter))
3922 wc = gtk_text_iter_get_char(&iter);
3923 if (!g_unichar_isspace(wc))
3926 gtk_text_iter_forward_char(&iter);
3927 if (gtk_text_iter_ends_line(&iter))
3931 gtk_text_iter_forward_char(&iter);
3932 if (gtk_text_iter_ends_line(&iter))
3937 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3938 const GtkTextIter *start,
3939 GtkTextIter *break_pos,
3943 GtkTextIter iter = *start, line_end = *start;
3944 PangoLogAttr *attrs;
3951 gboolean can_break = FALSE;
3952 gboolean do_break = FALSE;
3953 gboolean was_white = FALSE;
3954 gboolean prev_dont_break = FALSE;
3956 gtk_text_iter_forward_to_line_end(&line_end);
3957 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3958 len = g_utf8_strlen(str, -1);
3962 g_warning("compose_get_line_break_pos: len = 0!\n");
3966 /* g_print("breaking line: %d: %s (len = %d)\n",
3967 gtk_text_iter_get_line(&iter), str, len); */
3969 attrs = g_new(PangoLogAttr, len + 1);
3971 pango_default_break(str, -1, NULL, attrs, len + 1);
3975 /* skip quote and leading spaces */
3976 for (i = 0; *p != '\0' && i < len; i++) {
3979 wc = g_utf8_get_char(p);
3980 if (i >= quote_len && !g_unichar_isspace(wc))
3982 if (g_unichar_iswide(wc))
3984 else if (*p == '\t')
3988 p = g_utf8_next_char(p);
3991 for (; *p != '\0' && i < len; i++) {
3992 PangoLogAttr *attr = attrs + i;
3996 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3999 was_white = attr->is_white;
4001 /* don't wrap URI */
4002 if ((uri_len = get_uri_len(p)) > 0) {
4004 if (pos > 0 && col > max_col) {
4014 wc = g_utf8_get_char(p);
4015 if (g_unichar_iswide(wc)) {
4017 if (prev_dont_break && can_break && attr->is_line_break)
4019 } else if (*p == '\t')
4023 if (pos > 0 && col > max_col) {
4028 if (*p == '-' || *p == '/')
4029 prev_dont_break = TRUE;
4031 prev_dont_break = FALSE;
4033 p = g_utf8_next_char(p);
4037 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4042 *break_pos = *start;
4043 gtk_text_iter_set_line_offset(break_pos, pos);
4048 static gboolean compose_join_next_line(Compose *compose,
4049 GtkTextBuffer *buffer,
4051 const gchar *quote_str)
4053 GtkTextIter iter_ = *iter, cur, prev, next, end;
4054 PangoLogAttr attrs[3];
4056 gchar *next_quote_str;
4059 gboolean keep_cursor = FALSE;
4061 if (!gtk_text_iter_forward_line(&iter_) ||
4062 gtk_text_iter_ends_line(&iter_)) {
4065 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4067 if ((quote_str || next_quote_str) &&
4068 strcmp2(quote_str, next_quote_str) != 0) {
4069 g_free(next_quote_str);
4072 g_free(next_quote_str);
4075 if (quote_len > 0) {
4076 gtk_text_iter_forward_chars(&end, quote_len);
4077 if (gtk_text_iter_ends_line(&end)) {
4082 /* don't join itemized lines */
4083 if (compose_itemized_length(buffer, &end) > 0) {
4087 /* don't join signature separator */
4088 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4091 /* delete quote str */
4093 gtk_text_buffer_delete(buffer, &iter_, &end);
4095 /* don't join line breaks put by the user */
4097 gtk_text_iter_backward_char(&cur);
4098 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4099 gtk_text_iter_forward_char(&cur);
4103 gtk_text_iter_forward_char(&cur);
4104 /* delete linebreak and extra spaces */
4105 while (gtk_text_iter_backward_char(&cur)) {
4106 wc1 = gtk_text_iter_get_char(&cur);
4107 if (!g_unichar_isspace(wc1))
4112 while (!gtk_text_iter_ends_line(&cur)) {
4113 wc1 = gtk_text_iter_get_char(&cur);
4114 if (!g_unichar_isspace(wc1))
4116 gtk_text_iter_forward_char(&cur);
4119 if (!gtk_text_iter_equal(&prev, &next)) {
4122 mark = gtk_text_buffer_get_insert(buffer);
4123 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4124 if (gtk_text_iter_equal(&prev, &cur))
4126 gtk_text_buffer_delete(buffer, &prev, &next);
4130 /* insert space if required */
4131 gtk_text_iter_backward_char(&prev);
4132 wc1 = gtk_text_iter_get_char(&prev);
4133 wc2 = gtk_text_iter_get_char(&next);
4134 gtk_text_iter_forward_char(&next);
4135 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4136 pango_default_break(str, -1, NULL, attrs, 3);
4137 if (!attrs[1].is_line_break ||
4138 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4139 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4141 gtk_text_iter_backward_char(&iter_);
4142 gtk_text_buffer_place_cursor(buffer, &iter_);
4151 #define ADD_TXT_POS(bp_, ep_, pti_) \
4152 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4153 last = last->next; \
4154 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4155 last->next = NULL; \
4157 g_warning("alloc error scanning URIs\n"); \
4160 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4162 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4163 GtkTextBuffer *buffer;
4164 GtkTextIter iter, break_pos, end_of_line;
4165 gchar *quote_str = NULL;
4167 gboolean wrap_quote = prefs_common.linewrap_quote;
4168 gboolean prev_autowrap = compose->autowrap;
4169 gint startq_offset = -1, noq_offset = -1;
4170 gint uri_start = -1, uri_stop = -1;
4171 gint nouri_start = -1, nouri_stop = -1;
4172 gint num_blocks = 0;
4173 gint quotelevel = -1;
4174 gboolean modified = force;
4175 gboolean removed = FALSE;
4176 gboolean modified_before_remove = FALSE;
4178 gboolean start = TRUE;
4179 gint itemized_len = 0, rem_item_len = 0;
4180 gchar *itemized_chars = NULL;
4181 gboolean item_continuation = FALSE;
4186 if (compose->draft_timeout_tag == -2) {
4190 compose->autowrap = FALSE;
4192 buffer = gtk_text_view_get_buffer(text);
4193 undo_wrapping(compose->undostruct, TRUE);
4198 mark = gtk_text_buffer_get_insert(buffer);
4199 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4203 if (compose->draft_timeout_tag == -2) {
4204 if (gtk_text_iter_ends_line(&iter)) {
4205 while (gtk_text_iter_ends_line(&iter) &&
4206 gtk_text_iter_forward_line(&iter))
4209 while (gtk_text_iter_backward_line(&iter)) {
4210 if (gtk_text_iter_ends_line(&iter)) {
4211 gtk_text_iter_forward_line(&iter);
4217 /* move to line start */
4218 gtk_text_iter_set_line_offset(&iter, 0);
4221 itemized_len = compose_itemized_length(buffer, &iter);
4223 if (!itemized_len) {
4224 itemized_len = compose_left_offset_length(buffer, &iter);
4225 item_continuation = TRUE;
4229 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4231 /* go until paragraph end (empty line) */
4232 while (start || !gtk_text_iter_ends_line(&iter)) {
4233 gchar *scanpos = NULL;
4234 /* parse table - in order of priority */
4236 const gchar *needle; /* token */
4238 /* token search function */
4239 gchar *(*search) (const gchar *haystack,
4240 const gchar *needle);
4241 /* part parsing function */
4242 gboolean (*parse) (const gchar *start,
4243 const gchar *scanpos,
4247 /* part to URI function */
4248 gchar *(*build_uri) (const gchar *bp,
4252 static struct table parser[] = {
4253 {"http://", strcasestr, get_uri_part, make_uri_string},
4254 {"https://", strcasestr, get_uri_part, make_uri_string},
4255 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4256 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4257 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4258 {"www.", strcasestr, get_uri_part, make_http_string},
4259 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4260 {"@", strcasestr, get_email_part, make_email_string}
4262 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4263 gint last_index = PARSE_ELEMS;
4265 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4269 if (!prev_autowrap && num_blocks == 0) {
4271 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4272 G_CALLBACK(text_inserted),
4275 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4278 uri_start = uri_stop = -1;
4280 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4283 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4284 if (startq_offset == -1)
4285 startq_offset = gtk_text_iter_get_offset(&iter);
4286 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4287 if (quotelevel > 2) {
4288 /* recycle colors */
4289 if (prefs_common.recycle_quote_colors)
4298 if (startq_offset == -1)
4299 noq_offset = gtk_text_iter_get_offset(&iter);
4303 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4306 if (gtk_text_iter_ends_line(&iter)) {
4308 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4309 prefs_common.linewrap_len,
4311 GtkTextIter prev, next, cur;
4312 if (prev_autowrap != FALSE || force) {
4313 compose->automatic_break = TRUE;
4315 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4316 compose->automatic_break = FALSE;
4317 if (itemized_len && compose->autoindent) {
4318 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4319 if (!item_continuation)
4320 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4322 } else if (quote_str && wrap_quote) {
4323 compose->automatic_break = TRUE;
4325 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4326 compose->automatic_break = FALSE;
4327 if (itemized_len && compose->autoindent) {
4328 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4329 if (!item_continuation)
4330 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4334 /* remove trailing spaces */
4336 rem_item_len = itemized_len;
4337 while (compose->autoindent && rem_item_len-- > 0)
4338 gtk_text_iter_backward_char(&cur);
4339 gtk_text_iter_backward_char(&cur);
4342 while (!gtk_text_iter_starts_line(&cur)) {
4345 gtk_text_iter_backward_char(&cur);
4346 wc = gtk_text_iter_get_char(&cur);
4347 if (!g_unichar_isspace(wc))
4351 if (!gtk_text_iter_equal(&prev, &next)) {
4352 gtk_text_buffer_delete(buffer, &prev, &next);
4354 gtk_text_iter_forward_char(&break_pos);
4358 gtk_text_buffer_insert(buffer, &break_pos,
4362 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4364 /* move iter to current line start */
4365 gtk_text_iter_set_line_offset(&iter, 0);
4372 /* move iter to next line start */
4378 if (!prev_autowrap && num_blocks > 0) {
4380 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4381 G_CALLBACK(text_inserted),
4385 while (!gtk_text_iter_ends_line(&end_of_line)) {
4386 gtk_text_iter_forward_char(&end_of_line);
4388 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4390 nouri_start = gtk_text_iter_get_offset(&iter);
4391 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4393 walk_pos = gtk_text_iter_get_offset(&iter);
4394 /* FIXME: this looks phony. scanning for anything in the parse table */
4395 for (n = 0; n < PARSE_ELEMS; n++) {
4398 tmp = parser[n].search(walk, parser[n].needle);
4400 if (scanpos == NULL || tmp < scanpos) {
4409 /* check if URI can be parsed */
4410 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4411 (const gchar **)&ep, FALSE)
4412 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4416 strlen(parser[last_index].needle);
4419 uri_start = walk_pos + (bp - o_walk);
4420 uri_stop = walk_pos + (ep - o_walk);
4424 gtk_text_iter_forward_line(&iter);
4427 if (startq_offset != -1) {
4428 GtkTextIter startquote, endquote;
4429 gtk_text_buffer_get_iter_at_offset(
4430 buffer, &startquote, startq_offset);
4433 switch (quotelevel) {
4435 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4436 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4437 gtk_text_buffer_apply_tag_by_name(
4438 buffer, "quote0", &startquote, &endquote);
4439 gtk_text_buffer_remove_tag_by_name(
4440 buffer, "quote1", &startquote, &endquote);
4441 gtk_text_buffer_remove_tag_by_name(
4442 buffer, "quote2", &startquote, &endquote);
4447 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4448 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4449 gtk_text_buffer_apply_tag_by_name(
4450 buffer, "quote1", &startquote, &endquote);
4451 gtk_text_buffer_remove_tag_by_name(
4452 buffer, "quote0", &startquote, &endquote);
4453 gtk_text_buffer_remove_tag_by_name(
4454 buffer, "quote2", &startquote, &endquote);
4459 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4460 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4461 gtk_text_buffer_apply_tag_by_name(
4462 buffer, "quote2", &startquote, &endquote);
4463 gtk_text_buffer_remove_tag_by_name(
4464 buffer, "quote0", &startquote, &endquote);
4465 gtk_text_buffer_remove_tag_by_name(
4466 buffer, "quote1", &startquote, &endquote);
4472 } else if (noq_offset != -1) {
4473 GtkTextIter startnoquote, endnoquote;
4474 gtk_text_buffer_get_iter_at_offset(
4475 buffer, &startnoquote, noq_offset);
4478 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4479 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4480 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4481 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4482 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4483 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4484 gtk_text_buffer_remove_tag_by_name(
4485 buffer, "quote0", &startnoquote, &endnoquote);
4486 gtk_text_buffer_remove_tag_by_name(
4487 buffer, "quote1", &startnoquote, &endnoquote);
4488 gtk_text_buffer_remove_tag_by_name(
4489 buffer, "quote2", &startnoquote, &endnoquote);
4495 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4496 GtkTextIter nouri_start_iter, nouri_end_iter;
4497 gtk_text_buffer_get_iter_at_offset(
4498 buffer, &nouri_start_iter, nouri_start);
4499 gtk_text_buffer_get_iter_at_offset(
4500 buffer, &nouri_end_iter, nouri_stop);
4501 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4502 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4503 gtk_text_buffer_remove_tag_by_name(
4504 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4505 modified_before_remove = modified;
4510 if (uri_start >= 0 && uri_stop > 0) {
4511 GtkTextIter uri_start_iter, uri_end_iter, back;
4512 gtk_text_buffer_get_iter_at_offset(
4513 buffer, &uri_start_iter, uri_start);
4514 gtk_text_buffer_get_iter_at_offset(
4515 buffer, &uri_end_iter, uri_stop);
4516 back = uri_end_iter;
4517 gtk_text_iter_backward_char(&back);
4518 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4519 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4520 gtk_text_buffer_apply_tag_by_name(
4521 buffer, "link", &uri_start_iter, &uri_end_iter);
4523 if (removed && !modified_before_remove) {
4529 // debug_print("not modified, out after %d lines\n", lines);
4533 // debug_print("modified, out after %d lines\n", lines);
4535 g_free(itemized_chars);
4538 undo_wrapping(compose->undostruct, FALSE);
4539 compose->autowrap = prev_autowrap;
4544 void compose_action_cb(void *data)
4546 Compose *compose = (Compose *)data;
4547 compose_wrap_all(compose);
4550 static void compose_wrap_all(Compose *compose)
4552 compose_wrap_all_full(compose, FALSE);
4555 static void compose_wrap_all_full(Compose *compose, gboolean force)
4557 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4558 GtkTextBuffer *buffer;
4560 gboolean modified = TRUE;
4562 buffer = gtk_text_view_get_buffer(text);
4564 gtk_text_buffer_get_start_iter(buffer, &iter);
4565 while (!gtk_text_iter_is_end(&iter) && modified)
4566 modified = compose_beautify_paragraph(compose, &iter, force);
4570 static void compose_set_title(Compose *compose)
4576 edited = compose->modified ? _(" [Edited]") : "";
4578 subject = gtk_editable_get_chars(
4579 GTK_EDITABLE(compose->subject_entry), 0, -1);
4581 #ifndef GENERIC_UMPC
4582 if (subject && strlen(subject))
4583 str = g_strdup_printf(_("%s - Compose message%s"),
4586 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4588 str = g_strdup(_("Compose message"));
4591 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4597 * compose_current_mail_account:
4599 * Find a current mail account (the currently selected account, or the
4600 * default account, if a news account is currently selected). If a
4601 * mail account cannot be found, display an error message.
4603 * Return value: Mail account, or NULL if not found.
4605 static PrefsAccount *
4606 compose_current_mail_account(void)
4610 if (cur_account && cur_account->protocol != A_NNTP)
4613 ac = account_get_default();
4614 if (!ac || ac->protocol == A_NNTP) {
4615 alertpanel_error(_("Account for sending mail is not specified.\n"
4616 "Please select a mail account before sending."));
4623 #define QUOTE_IF_REQUIRED(out, str) \
4625 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4629 len = strlen(str) + 3; \
4630 if ((__tmp = alloca(len)) == NULL) { \
4631 g_warning("can't allocate memory\n"); \
4632 g_string_free(header, TRUE); \
4635 g_snprintf(__tmp, len, "\"%s\"", str); \
4640 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4641 g_warning("can't allocate memory\n"); \
4642 g_string_free(header, TRUE); \
4645 strcpy(__tmp, str); \
4651 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4653 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4657 len = strlen(str) + 3; \
4658 if ((__tmp = alloca(len)) == NULL) { \
4659 g_warning("can't allocate memory\n"); \
4662 g_snprintf(__tmp, len, "\"%s\"", str); \
4667 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4668 g_warning("can't allocate memory\n"); \
4671 strcpy(__tmp, str); \
4677 static void compose_select_account(Compose *compose, PrefsAccount *account,
4680 gchar *from = NULL, *header;
4681 ComposeHeaderEntry *header_entry;
4683 cm_return_if_fail(account != NULL);
4685 compose->account = account;
4686 if (account->name && *account->name) {
4688 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4689 from = g_strdup_printf("%s <%s>",
4690 buf, account->address);
4691 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4693 from = g_strdup_printf("<%s>",
4695 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4700 compose_set_title(compose);
4702 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4703 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4705 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4706 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4707 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4709 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4711 activate_privacy_system(compose, account, FALSE);
4713 if (!init && compose->mode != COMPOSE_REDIRECT) {
4714 undo_block(compose->undostruct);
4715 compose_insert_sig(compose, TRUE);
4716 undo_unblock(compose->undostruct);
4719 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4720 header = gtk_combo_box_text_get_active_text(
4721 GTK_COMBO_BOX_TEXT(header_entry->combo));
4723 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4724 if (account->protocol == A_NNTP) {
4725 if (!strcmp(header, _("To:")))
4726 combobox_select_by_text(
4727 GTK_COMBO_BOX(header_entry->combo),
4730 if (!strcmp(header, _("Newsgroups:")))
4731 combobox_select_by_text(
4732 GTK_COMBO_BOX(header_entry->combo),
4740 /* use account's dict info if set */
4741 if (compose->gtkaspell) {
4742 if (account->enable_default_dictionary)
4743 gtkaspell_change_dict(compose->gtkaspell,
4744 account->default_dictionary, FALSE);
4745 if (account->enable_default_alt_dictionary)
4746 gtkaspell_change_alt_dict(compose->gtkaspell,
4747 account->default_alt_dictionary);
4748 if (account->enable_default_dictionary
4749 || account->enable_default_alt_dictionary)
4750 compose_spell_menu_changed(compose);
4755 gboolean compose_check_for_valid_recipient(Compose *compose) {
4756 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4757 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4758 gboolean recipient_found = FALSE;
4762 /* free to and newsgroup list */
4763 slist_free_strings(compose->to_list);
4764 g_slist_free(compose->to_list);
4765 compose->to_list = NULL;
4767 slist_free_strings(compose->newsgroup_list);
4768 g_slist_free(compose->newsgroup_list);
4769 compose->newsgroup_list = NULL;
4771 /* search header entries for to and newsgroup entries */
4772 for (list = compose->header_list; list; list = list->next) {
4775 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4776 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4779 if (entry[0] != '\0') {
4780 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4781 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4782 compose->to_list = address_list_append(compose->to_list, entry);
4783 recipient_found = TRUE;
4786 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4787 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4788 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4789 recipient_found = TRUE;
4796 return recipient_found;
4799 static gboolean compose_check_for_set_recipients(Compose *compose)
4801 if (compose->account->set_autocc && compose->account->auto_cc) {
4802 gboolean found_other = FALSE;
4804 /* search header entries for to and newsgroup entries */
4805 for (list = compose->header_list; list; list = list->next) {
4808 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4809 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4812 if (strcmp(entry, compose->account->auto_cc)
4813 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4823 if (compose->batch) {
4824 gtk_widget_show_all(compose->window);
4826 aval = alertpanel(_("Send"),
4827 _("The only recipient is the default CC address. Send anyway?"),
4828 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4829 if (aval != G_ALERTALTERNATE)
4833 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4834 gboolean found_other = FALSE;
4836 /* search header entries for to and newsgroup entries */
4837 for (list = compose->header_list; list; list = list->next) {
4840 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4841 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4844 if (strcmp(entry, compose->account->auto_bcc)
4845 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4855 if (compose->batch) {
4856 gtk_widget_show_all(compose->window);
4858 aval = alertpanel(_("Send"),
4859 _("The only recipient is the default BCC address. Send anyway?"),
4860 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4861 if (aval != G_ALERTALTERNATE)
4868 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4872 if (compose_check_for_valid_recipient(compose) == FALSE) {
4873 if (compose->batch) {
4874 gtk_widget_show_all(compose->window);
4876 alertpanel_error(_("Recipient is not specified."));
4880 if (compose_check_for_set_recipients(compose) == FALSE) {
4884 if (!compose->batch) {
4885 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4886 if (*str == '\0' && check_everything == TRUE &&
4887 compose->mode != COMPOSE_REDIRECT) {
4889 gchar *button_label;
4892 if (compose->sending)
4893 button_label = _("+_Send");
4895 button_label = _("+_Queue");
4896 message = g_strdup_printf(_("Subject is empty. %s"),
4897 compose->sending?_("Send it anyway?"):
4898 _("Queue it anyway?"));
4900 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4901 GTK_STOCK_CANCEL, button_label, NULL);
4903 if (aval != G_ALERTALTERNATE)
4908 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4914 gint compose_send(Compose *compose)
4917 FolderItem *folder = NULL;
4919 gchar *msgpath = NULL;
4920 gboolean discard_window = FALSE;
4921 gchar *errstr = NULL;
4922 gchar *tmsgid = NULL;
4923 MainWindow *mainwin = mainwindow_get_mainwindow();
4924 gboolean queued_removed = FALSE;
4926 if (prefs_common.send_dialog_invisible
4927 || compose->batch == TRUE)
4928 discard_window = TRUE;
4930 compose_allow_user_actions (compose, FALSE);
4931 compose->sending = TRUE;
4933 if (compose_check_entries(compose, TRUE) == FALSE) {
4934 if (compose->batch) {
4935 gtk_widget_show_all(compose->window);
4941 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4944 if (compose->batch) {
4945 gtk_widget_show_all(compose->window);
4948 alertpanel_error(_("Could not queue message for sending:\n\n"
4949 "Charset conversion failed."));
4950 } else if (val == -5) {
4951 alertpanel_error(_("Could not queue message for sending:\n\n"
4952 "Couldn't get recipient encryption key."));
4953 } else if (val == -6) {
4955 } else if (val == -3) {
4956 if (privacy_peek_error())
4957 alertpanel_error(_("Could not queue message for sending:\n\n"
4958 "Signature failed: %s"), privacy_get_error());
4959 } else if (val == -2 && errno != 0) {
4960 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4962 alertpanel_error(_("Could not queue message for sending."));
4967 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4968 if (discard_window) {
4969 compose->sending = FALSE;
4970 compose_close(compose);
4971 /* No more compose access in the normal codepath
4972 * after this point! */
4977 alertpanel_error(_("The message was queued but could not be "
4978 "sent.\nUse \"Send queued messages\" from "
4979 "the main window to retry."));
4980 if (!discard_window) {
4987 if (msgpath == NULL) {
4988 msgpath = folder_item_fetch_msg(folder, msgnum);
4989 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4992 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4993 claws_unlink(msgpath);
4996 if (!discard_window) {
4998 if (!queued_removed)
4999 folder_item_remove_msg(folder, msgnum);
5000 folder_item_scan(folder);
5002 /* make sure we delete that */
5003 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5005 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5006 folder_item_remove_msg(folder, tmp->msgnum);
5007 procmsg_msginfo_free(tmp);
5014 if (!queued_removed)
5015 folder_item_remove_msg(folder, msgnum);
5016 folder_item_scan(folder);
5018 /* make sure we delete that */
5019 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5021 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5022 folder_item_remove_msg(folder, tmp->msgnum);
5023 procmsg_msginfo_free(tmp);
5026 if (!discard_window) {
5027 compose->sending = FALSE;
5028 compose_allow_user_actions (compose, TRUE);
5029 compose_close(compose);
5033 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5034 "the main window to retry."), errstr);
5037 alertpanel_error_log(_("The message was queued but could not be "
5038 "sent.\nUse \"Send queued messages\" from "
5039 "the main window to retry."));
5041 if (!discard_window) {
5050 toolbar_main_set_sensitive(mainwin);
5051 main_window_set_menu_sensitive(mainwin);
5057 compose_allow_user_actions (compose, TRUE);
5058 compose->sending = FALSE;
5059 compose->modified = TRUE;
5060 toolbar_main_set_sensitive(mainwin);
5061 main_window_set_menu_sensitive(mainwin);
5066 static gboolean compose_use_attach(Compose *compose)
5068 GtkTreeModel *model = gtk_tree_view_get_model
5069 (GTK_TREE_VIEW(compose->attach_clist));
5070 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5073 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5076 gchar buf[BUFFSIZE];
5078 gboolean first_to_address;
5079 gboolean first_cc_address;
5081 ComposeHeaderEntry *headerentry;
5082 const gchar *headerentryname;
5083 const gchar *cc_hdr;
5084 const gchar *to_hdr;
5085 gboolean err = FALSE;
5087 debug_print("Writing redirect header\n");
5089 cc_hdr = prefs_common_translated_header_name("Cc:");
5090 to_hdr = prefs_common_translated_header_name("To:");
5092 first_to_address = TRUE;
5093 for (list = compose->header_list; list; list = list->next) {
5094 headerentry = ((ComposeHeaderEntry *)list->data);
5095 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5097 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5098 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5099 Xstrdup_a(str, entstr, return -1);
5101 if (str[0] != '\0') {
5102 compose_convert_header
5103 (compose, buf, sizeof(buf), str,
5104 strlen("Resent-To") + 2, TRUE);
5106 if (first_to_address) {
5107 err |= (fprintf(fp, "Resent-To: ") < 0);
5108 first_to_address = FALSE;
5110 err |= (fprintf(fp, ",") < 0);
5112 err |= (fprintf(fp, "%s", buf) < 0);
5116 if (!first_to_address) {
5117 err |= (fprintf(fp, "\n") < 0);
5120 first_cc_address = TRUE;
5121 for (list = compose->header_list; list; list = list->next) {
5122 headerentry = ((ComposeHeaderEntry *)list->data);
5123 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5125 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5126 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5127 Xstrdup_a(str, strg, return -1);
5129 if (str[0] != '\0') {
5130 compose_convert_header
5131 (compose, buf, sizeof(buf), str,
5132 strlen("Resent-Cc") + 2, TRUE);
5134 if (first_cc_address) {
5135 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5136 first_cc_address = FALSE;
5138 err |= (fprintf(fp, ",") < 0);
5140 err |= (fprintf(fp, "%s", buf) < 0);
5144 if (!first_cc_address) {
5145 err |= (fprintf(fp, "\n") < 0);
5148 return (err ? -1:0);
5151 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5153 gchar buf[BUFFSIZE];
5155 const gchar *entstr;
5156 /* struct utsname utsbuf; */
5157 gboolean err = FALSE;
5159 cm_return_val_if_fail(fp != NULL, -1);
5160 cm_return_val_if_fail(compose->account != NULL, -1);
5161 cm_return_val_if_fail(compose->account->address != NULL, -1);
5164 get_rfc822_date(buf, sizeof(buf));
5165 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5168 if (compose->account->name && *compose->account->name) {
5169 compose_convert_header
5170 (compose, buf, sizeof(buf), compose->account->name,
5171 strlen("From: "), TRUE);
5172 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5173 buf, compose->account->address) < 0);
5175 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5178 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5179 if (*entstr != '\0') {
5180 Xstrdup_a(str, entstr, return -1);
5183 compose_convert_header(compose, buf, sizeof(buf), str,
5184 strlen("Subject: "), FALSE);
5185 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5189 /* Resent-Message-ID */
5190 if (compose->account->set_domain && compose->account->domain) {
5191 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5192 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5193 g_snprintf(buf, sizeof(buf), "%s",
5194 strchr(compose->account->address, '@') ?
5195 strchr(compose->account->address, '@')+1 :
5196 compose->account->address);
5198 g_snprintf(buf, sizeof(buf), "%s", "");
5201 if (compose->account->gen_msgid) {
5203 if (compose->account->msgid_with_addr) {
5204 addr = compose->account->address;
5206 generate_msgid(buf, sizeof(buf), addr);
5207 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5208 compose->msgid = g_strdup(buf);
5210 compose->msgid = NULL;
5213 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5216 /* separator between header and body */
5217 err |= (fputs("\n", fp) == EOF);
5219 return (err ? -1:0);
5222 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5226 gchar buf[BUFFSIZE];
5228 gboolean skip = FALSE;
5229 gboolean err = FALSE;
5230 gchar *not_included[]={
5231 "Return-Path:", "Delivered-To:", "Received:",
5232 "Subject:", "X-UIDL:", "AF:",
5233 "NF:", "PS:", "SRH:",
5234 "SFN:", "DSR:", "MID:",
5235 "CFG:", "PT:", "S:",
5236 "RQ:", "SSV:", "NSV:",
5237 "SSH:", "R:", "MAID:",
5238 "NAID:", "RMID:", "FMID:",
5239 "SCF:", "RRCPT:", "NG:",
5240 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5241 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5242 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5243 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5244 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5247 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5248 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5252 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5254 for (i = 0; not_included[i] != NULL; i++) {
5255 if (g_ascii_strncasecmp(buf, not_included[i],
5256 strlen(not_included[i])) == 0) {
5263 if (fputs(buf, fdest) == -1)
5266 if (!prefs_common.redirect_keep_from) {
5267 if (g_ascii_strncasecmp(buf, "From:",
5268 strlen("From:")) == 0) {
5269 err |= (fputs(" (by way of ", fdest) == EOF);
5270 if (compose->account->name
5271 && *compose->account->name) {
5272 compose_convert_header
5273 (compose, buf, sizeof(buf),
5274 compose->account->name,
5277 err |= (fprintf(fdest, "%s <%s>",
5279 compose->account->address) < 0);
5281 err |= (fprintf(fdest, "%s",
5282 compose->account->address) < 0);
5283 err |= (fputs(")", fdest) == EOF);
5287 if (fputs("\n", fdest) == -1)
5294 if (compose_redirect_write_headers(compose, fdest))
5297 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5298 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5311 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5313 GtkTextBuffer *buffer;
5314 GtkTextIter start, end;
5317 const gchar *out_codeset;
5318 EncodingType encoding = ENC_UNKNOWN;
5319 MimeInfo *mimemsg, *mimetext;
5321 const gchar *src_codeset = CS_INTERNAL;
5322 gchar *from_addr = NULL;
5323 gchar *from_name = NULL;
5325 if (action == COMPOSE_WRITE_FOR_SEND)
5326 attach_parts = TRUE;
5328 /* create message MimeInfo */
5329 mimemsg = procmime_mimeinfo_new();
5330 mimemsg->type = MIMETYPE_MESSAGE;
5331 mimemsg->subtype = g_strdup("rfc822");
5332 mimemsg->content = MIMECONTENT_MEM;
5333 mimemsg->tmp = TRUE; /* must free content later */
5334 mimemsg->data.mem = compose_get_header(compose);
5336 /* Create text part MimeInfo */
5337 /* get all composed text */
5338 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5339 gtk_text_buffer_get_start_iter(buffer, &start);
5340 gtk_text_buffer_get_end_iter(buffer, &end);
5341 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5343 out_codeset = conv_get_charset_str(compose->out_encoding);
5345 if (!out_codeset && is_ascii_str(chars)) {
5346 out_codeset = CS_US_ASCII;
5347 } else if (prefs_common.outgoing_fallback_to_ascii &&
5348 is_ascii_str(chars)) {
5349 out_codeset = CS_US_ASCII;
5350 encoding = ENC_7BIT;
5354 gchar *test_conv_global_out = NULL;
5355 gchar *test_conv_reply = NULL;
5357 /* automatic mode. be automatic. */
5358 codeconv_set_strict(TRUE);
5360 out_codeset = conv_get_outgoing_charset_str();
5362 debug_print("trying to convert to %s\n", out_codeset);
5363 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5366 if (!test_conv_global_out && compose->orig_charset
5367 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5368 out_codeset = compose->orig_charset;
5369 debug_print("failure; trying to convert to %s\n", out_codeset);
5370 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5373 if (!test_conv_global_out && !test_conv_reply) {
5375 out_codeset = CS_INTERNAL;
5376 debug_print("failure; finally using %s\n", out_codeset);
5378 g_free(test_conv_global_out);
5379 g_free(test_conv_reply);
5380 codeconv_set_strict(FALSE);
5383 if (encoding == ENC_UNKNOWN) {
5384 if (prefs_common.encoding_method == CTE_BASE64)
5385 encoding = ENC_BASE64;
5386 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5387 encoding = ENC_QUOTED_PRINTABLE;
5388 else if (prefs_common.encoding_method == CTE_8BIT)
5389 encoding = ENC_8BIT;
5391 encoding = procmime_get_encoding_for_charset(out_codeset);
5394 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5395 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5397 if (action == COMPOSE_WRITE_FOR_SEND) {
5398 codeconv_set_strict(TRUE);
5399 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5400 codeconv_set_strict(FALSE);
5406 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5407 "to the specified %s charset.\n"
5408 "Send it as %s?"), out_codeset, src_codeset);
5409 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5410 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5413 if (aval != G_ALERTALTERNATE) {
5418 out_codeset = src_codeset;
5424 out_codeset = src_codeset;
5429 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5430 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5431 strstr(buf, "\nFrom ") != NULL) {
5432 encoding = ENC_QUOTED_PRINTABLE;
5436 mimetext = procmime_mimeinfo_new();
5437 mimetext->content = MIMECONTENT_MEM;
5438 mimetext->tmp = TRUE; /* must free content later */
5439 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5440 * and free the data, which we need later. */
5441 mimetext->data.mem = g_strdup(buf);
5442 mimetext->type = MIMETYPE_TEXT;
5443 mimetext->subtype = g_strdup("plain");
5444 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5445 g_strdup(out_codeset));
5447 /* protect trailing spaces when signing message */
5448 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5449 privacy_system_can_sign(compose->privacy_system)) {
5450 encoding = ENC_QUOTED_PRINTABLE;
5453 debug_print("main text: %zd bytes encoded as %s in %d\n",
5454 strlen(buf), out_codeset, encoding);
5456 /* check for line length limit */
5457 if (action == COMPOSE_WRITE_FOR_SEND &&
5458 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5459 check_line_length(buf, 1000, &line) < 0) {
5463 msg = g_strdup_printf
5464 (_("Line %d exceeds the line length limit (998 bytes).\n"
5465 "The contents of the message might be broken on the way to the delivery.\n"
5467 "Send it anyway?"), line + 1);
5468 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5470 if (aval != G_ALERTALTERNATE) {
5476 if (encoding != ENC_UNKNOWN)
5477 procmime_encode_content(mimetext, encoding);
5479 /* append attachment parts */
5480 if (compose_use_attach(compose) && attach_parts) {
5481 MimeInfo *mimempart;
5482 gchar *boundary = NULL;
5483 mimempart = procmime_mimeinfo_new();
5484 mimempart->content = MIMECONTENT_EMPTY;
5485 mimempart->type = MIMETYPE_MULTIPART;
5486 mimempart->subtype = g_strdup("mixed");
5490 boundary = generate_mime_boundary(NULL);
5491 } while (strstr(buf, boundary) != NULL);
5493 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5496 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5498 g_node_append(mimempart->node, mimetext->node);
5499 g_node_append(mimemsg->node, mimempart->node);
5501 if (compose_add_attachments(compose, mimempart) < 0)
5504 g_node_append(mimemsg->node, mimetext->node);
5508 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5509 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5510 /* extract name and address */
5511 if (strstr(spec, " <") && strstr(spec, ">")) {
5512 from_addr = g_strdup(strrchr(spec, '<')+1);
5513 *(strrchr(from_addr, '>')) = '\0';
5514 from_name = g_strdup(spec);
5515 *(strrchr(from_name, '<')) = '\0';
5522 /* sign message if sending */
5523 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5524 privacy_system_can_sign(compose->privacy_system))
5525 if (!privacy_sign(compose->privacy_system, mimemsg,
5526 compose->account, from_addr)) {
5533 procmime_write_mimeinfo(mimemsg, fp);
5535 procmime_mimeinfo_free_all(mimemsg);
5540 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5542 GtkTextBuffer *buffer;
5543 GtkTextIter start, end;
5548 if ((fp = g_fopen(file, "wb")) == NULL) {
5549 FILE_OP_ERROR(file, "fopen");
5553 /* chmod for security */
5554 if (change_file_mode_rw(fp, file) < 0) {
5555 FILE_OP_ERROR(file, "chmod");
5556 g_warning("can't change file mode\n");
5559 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5560 gtk_text_buffer_get_start_iter(buffer, &start);
5561 gtk_text_buffer_get_end_iter(buffer, &end);
5562 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5564 chars = conv_codeset_strdup
5565 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5568 if (!chars) return -1;
5571 len = strlen(chars);
5572 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5573 FILE_OP_ERROR(file, "fwrite");
5582 if (fclose(fp) == EOF) {
5583 FILE_OP_ERROR(file, "fclose");
5590 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5593 MsgInfo *msginfo = compose->targetinfo;
5595 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5596 if (!msginfo) return -1;
5598 if (!force && MSG_IS_LOCKED(msginfo->flags))
5601 item = msginfo->folder;
5602 cm_return_val_if_fail(item != NULL, -1);
5604 if (procmsg_msg_exist(msginfo) &&
5605 (folder_has_parent_of_type(item, F_QUEUE) ||
5606 folder_has_parent_of_type(item, F_DRAFT)
5607 || msginfo == compose->autosaved_draft)) {
5608 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5609 g_warning("can't remove the old message\n");
5612 debug_print("removed reedit target %d\n", msginfo->msgnum);
5619 static void compose_remove_draft(Compose *compose)
5622 MsgInfo *msginfo = compose->targetinfo;
5623 drafts = account_get_special_folder(compose->account, F_DRAFT);
5625 if (procmsg_msg_exist(msginfo)) {
5626 folder_item_remove_msg(drafts, msginfo->msgnum);
5631 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5632 gboolean remove_reedit_target)
5634 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5637 static gboolean compose_warn_encryption(Compose *compose)
5639 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5640 AlertValue val = G_ALERTALTERNATE;
5642 if (warning == NULL)
5645 val = alertpanel_full(_("Encryption warning"), warning,
5646 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5647 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5648 if (val & G_ALERTDISABLE) {
5649 val &= ~G_ALERTDISABLE;
5650 if (val == G_ALERTALTERNATE)
5651 privacy_inhibit_encrypt_warning(compose->privacy_system,
5655 if (val == G_ALERTALTERNATE) {
5662 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5663 gchar **msgpath, gboolean check_subject,
5664 gboolean remove_reedit_target)
5671 static gboolean lock = FALSE;
5672 PrefsAccount *mailac = NULL, *newsac = NULL;
5673 gboolean err = FALSE;
5675 debug_print("queueing message...\n");
5676 cm_return_val_if_fail(compose->account != NULL, -1);
5680 if (compose_check_entries(compose, check_subject) == FALSE) {
5682 if (compose->batch) {
5683 gtk_widget_show_all(compose->window);
5688 if (!compose->to_list && !compose->newsgroup_list) {
5689 g_warning("can't get recipient list.");
5694 if (compose->to_list) {
5695 if (compose->account->protocol != A_NNTP)
5696 mailac = compose->account;
5697 else if (cur_account && cur_account->protocol != A_NNTP)
5698 mailac = cur_account;
5699 else if (!(mailac = compose_current_mail_account())) {
5701 alertpanel_error(_("No account for sending mails available!"));
5706 if (compose->newsgroup_list) {
5707 if (compose->account->protocol == A_NNTP)
5708 newsac = compose->account;
5711 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5716 /* write queue header */
5717 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5718 G_DIR_SEPARATOR, compose, (guint) rand());
5719 debug_print("queuing to %s\n", tmp);
5720 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5721 FILE_OP_ERROR(tmp, "fopen");
5727 if (change_file_mode_rw(fp, tmp) < 0) {
5728 FILE_OP_ERROR(tmp, "chmod");
5729 g_warning("can't change file mode\n");
5732 /* queueing variables */
5733 err |= (fprintf(fp, "AF:\n") < 0);
5734 err |= (fprintf(fp, "NF:0\n") < 0);
5735 err |= (fprintf(fp, "PS:10\n") < 0);
5736 err |= (fprintf(fp, "SRH:1\n") < 0);
5737 err |= (fprintf(fp, "SFN:\n") < 0);
5738 err |= (fprintf(fp, "DSR:\n") < 0);
5740 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5742 err |= (fprintf(fp, "MID:\n") < 0);
5743 err |= (fprintf(fp, "CFG:\n") < 0);
5744 err |= (fprintf(fp, "PT:0\n") < 0);
5745 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5746 err |= (fprintf(fp, "RQ:\n") < 0);
5748 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5750 err |= (fprintf(fp, "SSV:\n") < 0);
5752 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5754 err |= (fprintf(fp, "NSV:\n") < 0);
5755 err |= (fprintf(fp, "SSH:\n") < 0);
5756 /* write recepient list */
5757 if (compose->to_list) {
5758 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5759 for (cur = compose->to_list->next; cur != NULL;
5761 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5762 err |= (fprintf(fp, "\n") < 0);
5764 /* write newsgroup list */
5765 if (compose->newsgroup_list) {
5766 err |= (fprintf(fp, "NG:") < 0);
5767 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5768 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5769 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5770 err |= (fprintf(fp, "\n") < 0);
5772 /* Sylpheed account IDs */
5774 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5776 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5779 if (compose->privacy_system != NULL) {
5780 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5781 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5782 if (compose->use_encryption) {
5784 if (!compose_warn_encryption(compose)) {
5791 if (mailac && mailac->encrypt_to_self) {
5792 GSList *tmp_list = g_slist_copy(compose->to_list);
5793 tmp_list = g_slist_append(tmp_list, compose->account->address);
5794 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5795 g_slist_free(tmp_list);
5797 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5799 if (encdata != NULL) {
5800 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5801 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5802 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5804 } /* else we finally dont want to encrypt */
5806 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5807 /* and if encdata was null, it means there's been a problem in
5819 /* Save copy folder */
5820 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5821 gchar *savefolderid;
5823 savefolderid = compose_get_save_to(compose);
5824 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5825 g_free(savefolderid);
5827 /* Save copy folder */
5828 if (compose->return_receipt) {
5829 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5831 /* Message-ID of message replying to */
5832 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5835 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5836 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5839 /* Message-ID of message forwarding to */
5840 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5843 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5844 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5848 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5849 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5851 /* end of headers */
5852 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5854 if (compose->redirect_filename != NULL) {
5855 if (compose_redirect_write_to_file(compose, fp) < 0) {
5864 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5869 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5873 g_warning("failed to write queue message\n");
5880 if (fclose(fp) == EOF) {
5881 FILE_OP_ERROR(tmp, "fclose");
5888 if (item && *item) {
5891 queue = account_get_special_folder(compose->account, F_QUEUE);
5894 g_warning("can't find queue folder\n");
5900 folder_item_scan(queue);
5901 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5902 g_warning("can't queue the message\n");
5909 if (msgpath == NULL) {
5915 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5916 compose_remove_reedit_target(compose, FALSE);
5919 if ((msgnum != NULL) && (item != NULL)) {
5927 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5930 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5932 struct stat statbuf;
5933 gchar *type, *subtype;
5934 GtkTreeModel *model;
5937 model = gtk_tree_view_get_model(tree_view);
5939 if (!gtk_tree_model_get_iter_first(model, &iter))
5942 gtk_tree_model_get(model, &iter,
5946 if (!is_file_exist(ainfo->file)) {
5947 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5948 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5949 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5951 if (val == G_ALERTDEFAULT) {
5956 mimepart = procmime_mimeinfo_new();
5957 mimepart->content = MIMECONTENT_FILE;
5958 mimepart->data.filename = g_strdup(ainfo->file);
5959 mimepart->tmp = FALSE; /* or we destroy our attachment */
5960 mimepart->offset = 0;
5962 g_stat(ainfo->file, &statbuf);
5963 mimepart->length = statbuf.st_size;
5965 type = g_strdup(ainfo->content_type);
5967 if (!strchr(type, '/')) {
5969 type = g_strdup("application/octet-stream");
5972 subtype = strchr(type, '/') + 1;
5973 *(subtype - 1) = '\0';
5974 mimepart->type = procmime_get_media_type(type);
5975 mimepart->subtype = g_strdup(subtype);
5978 if (mimepart->type == MIMETYPE_MESSAGE &&
5979 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5980 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5981 } else if (mimepart->type == MIMETYPE_TEXT) {
5982 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
5983 /* Text parts with no name come from multipart/alternative
5984 * forwards. Make sure the recipient won't look at the
5985 * original HTML part by mistake. */
5986 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5987 ainfo->name = g_strdup_printf(_("Original %s part"),
5991 g_hash_table_insert(mimepart->typeparameters,
5992 g_strdup("charset"), g_strdup(ainfo->charset));
5994 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
5995 if (mimepart->type == MIMETYPE_APPLICATION &&
5996 !strcmp2(mimepart->subtype, "octet-stream"))
5997 g_hash_table_insert(mimepart->typeparameters,
5998 g_strdup("name"), g_strdup(ainfo->name));
5999 g_hash_table_insert(mimepart->dispositionparameters,
6000 g_strdup("filename"), g_strdup(ainfo->name));
6001 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6004 if (mimepart->type == MIMETYPE_MESSAGE
6005 || mimepart->type == MIMETYPE_MULTIPART)
6006 ainfo->encoding = ENC_BINARY;
6007 else if (compose->use_signing) {
6008 if (ainfo->encoding == ENC_7BIT)
6009 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6010 else if (ainfo->encoding == ENC_8BIT)
6011 ainfo->encoding = ENC_BASE64;
6016 procmime_encode_content(mimepart, ainfo->encoding);
6018 g_node_append(parent->node, mimepart->node);
6019 } while (gtk_tree_model_iter_next(model, &iter));
6024 #define IS_IN_CUSTOM_HEADER(header) \
6025 (compose->account->add_customhdr && \
6026 custom_header_find(compose->account->customhdr_list, header) != NULL)
6028 static void compose_add_headerfield_from_headerlist(Compose *compose,
6030 const gchar *fieldname,
6031 const gchar *seperator)
6033 gchar *str, *fieldname_w_colon;
6034 gboolean add_field = FALSE;
6036 ComposeHeaderEntry *headerentry;
6037 const gchar *headerentryname;
6038 const gchar *trans_fieldname;
6041 if (IS_IN_CUSTOM_HEADER(fieldname))
6044 debug_print("Adding %s-fields\n", fieldname);
6046 fieldstr = g_string_sized_new(64);
6048 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6049 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6051 for (list = compose->header_list; list; list = list->next) {
6052 headerentry = ((ComposeHeaderEntry *)list->data);
6053 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6055 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6056 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6058 if (str[0] != '\0') {
6060 g_string_append(fieldstr, seperator);
6061 g_string_append(fieldstr, str);
6070 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6071 compose_convert_header
6072 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6073 strlen(fieldname) + 2, TRUE);
6074 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6078 g_free(fieldname_w_colon);
6079 g_string_free(fieldstr, TRUE);
6084 static gchar *compose_get_header(Compose *compose)
6086 gchar buf[BUFFSIZE];
6087 const gchar *entry_str;
6091 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6093 gchar *from_name = NULL, *from_address = NULL;
6096 cm_return_val_if_fail(compose->account != NULL, NULL);
6097 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6099 header = g_string_sized_new(64);
6102 get_rfc822_date(buf, sizeof(buf));
6103 g_string_append_printf(header, "Date: %s\n", buf);
6107 if (compose->account->name && *compose->account->name) {
6109 QUOTE_IF_REQUIRED(buf, compose->account->name);
6110 tmp = g_strdup_printf("%s <%s>",
6111 buf, compose->account->address);
6113 tmp = g_strdup_printf("%s",
6114 compose->account->address);
6116 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6117 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6119 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6120 from_address = g_strdup(compose->account->address);
6122 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6123 /* extract name and address */
6124 if (strstr(spec, " <") && strstr(spec, ">")) {
6125 from_address = g_strdup(strrchr(spec, '<')+1);
6126 *(strrchr(from_address, '>')) = '\0';
6127 from_name = g_strdup(spec);
6128 *(strrchr(from_name, '<')) = '\0';
6131 from_address = g_strdup(spec);
6138 if (from_name && *from_name) {
6139 compose_convert_header
6140 (compose, buf, sizeof(buf), from_name,
6141 strlen("From: "), TRUE);
6142 QUOTE_IF_REQUIRED(name, buf);
6144 g_string_append_printf(header, "From: %s <%s>\n",
6145 name, from_address);
6147 g_string_append_printf(header, "From: %s\n", from_address);
6150 g_free(from_address);
6153 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6156 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6159 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6163 * If this account is a NNTP account remove Bcc header from
6164 * message body since it otherwise will be publicly shown
6166 if (compose->account->protocol != A_NNTP)
6167 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6170 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6172 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6175 compose_convert_header(compose, buf, sizeof(buf), str,
6176 strlen("Subject: "), FALSE);
6177 g_string_append_printf(header, "Subject: %s\n", buf);
6183 if (compose->account->set_domain && compose->account->domain) {
6184 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6185 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6186 g_snprintf(buf, sizeof(buf), "%s",
6187 strchr(compose->account->address, '@') ?
6188 strchr(compose->account->address, '@')+1 :
6189 compose->account->address);
6191 g_snprintf(buf, sizeof(buf), "%s", "");
6194 if (compose->account->gen_msgid) {
6196 if (compose->account->msgid_with_addr) {
6197 addr = compose->account->address;
6199 generate_msgid(buf, sizeof(buf), addr);
6200 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6201 compose->msgid = g_strdup(buf);
6203 compose->msgid = NULL;
6206 if (compose->remove_references == FALSE) {
6208 if (compose->inreplyto && compose->to_list)
6209 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6212 if (compose->references)
6213 g_string_append_printf(header, "References: %s\n", compose->references);
6217 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6220 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6223 if (compose->account->organization &&
6224 strlen(compose->account->organization) &&
6225 !IS_IN_CUSTOM_HEADER("Organization")) {
6226 compose_convert_header(compose, buf, sizeof(buf),
6227 compose->account->organization,
6228 strlen("Organization: "), FALSE);
6229 g_string_append_printf(header, "Organization: %s\n", buf);
6232 /* Program version and system info */
6233 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6234 !compose->newsgroup_list) {
6235 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6237 gtk_major_version, gtk_minor_version, gtk_micro_version,
6240 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6241 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6243 gtk_major_version, gtk_minor_version, gtk_micro_version,
6247 /* custom headers */
6248 if (compose->account->add_customhdr) {
6251 for (cur = compose->account->customhdr_list; cur != NULL;
6253 CustomHeader *chdr = (CustomHeader *)cur->data;
6255 if (custom_header_is_allowed(chdr->name)) {
6256 compose_convert_header
6257 (compose, buf, sizeof(buf),
6258 chdr->value ? chdr->value : "",
6259 strlen(chdr->name) + 2, FALSE);
6260 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6265 /* Automatic Faces and X-Faces */
6266 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6267 g_string_append_printf(header, "X-Face: %s\n", buf);
6269 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6270 g_string_append_printf(header, "X-Face: %s\n", buf);
6272 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6273 g_string_append_printf(header, "Face: %s\n", buf);
6275 else if (get_default_face (buf, sizeof(buf)) == 0) {
6276 g_string_append_printf(header, "Face: %s\n", buf);
6280 switch (compose->priority) {
6281 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6282 "X-Priority: 1 (Highest)\n");
6284 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6285 "X-Priority: 2 (High)\n");
6287 case PRIORITY_NORMAL: break;
6288 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6289 "X-Priority: 4 (Low)\n");
6291 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6292 "X-Priority: 5 (Lowest)\n");
6294 default: debug_print("compose: priority unknown : %d\n",
6298 /* Request Return Receipt */
6299 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6300 if (compose->return_receipt) {
6301 if (compose->account->name
6302 && *compose->account->name) {
6303 compose_convert_header(compose, buf, sizeof(buf),
6304 compose->account->name,
6305 strlen("Disposition-Notification-To: "),
6307 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6309 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6313 /* get special headers */
6314 for (list = compose->header_list; list; list = list->next) {
6315 ComposeHeaderEntry *headerentry;
6318 gchar *headername_wcolon;
6319 const gchar *headername_trans;
6322 gboolean standard_header = FALSE;
6324 headerentry = ((ComposeHeaderEntry *)list->data);
6326 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6328 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6333 if (!strstr(tmp, ":")) {
6334 headername_wcolon = g_strconcat(tmp, ":", NULL);
6335 headername = g_strdup(tmp);
6337 headername_wcolon = g_strdup(tmp);
6338 headername = g_strdup(strtok(tmp, ":"));
6342 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6343 Xstrdup_a(headervalue, entry_str, return NULL);
6344 subst_char(headervalue, '\r', ' ');
6345 subst_char(headervalue, '\n', ' ');
6346 string = std_headers;
6347 while (*string != NULL) {
6348 headername_trans = prefs_common_translated_header_name(*string);
6349 if (!strcmp(headername_trans, headername_wcolon))
6350 standard_header = TRUE;
6353 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6354 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6357 g_free(headername_wcolon);
6361 g_string_free(header, FALSE);
6366 #undef IS_IN_CUSTOM_HEADER
6368 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6369 gint header_len, gboolean addr_field)
6371 gchar *tmpstr = NULL;
6372 const gchar *out_codeset = NULL;
6374 cm_return_if_fail(src != NULL);
6375 cm_return_if_fail(dest != NULL);
6377 if (len < 1) return;
6379 tmpstr = g_strdup(src);
6381 subst_char(tmpstr, '\n', ' ');
6382 subst_char(tmpstr, '\r', ' ');
6385 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6386 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6387 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6392 codeconv_set_strict(TRUE);
6393 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6394 conv_get_charset_str(compose->out_encoding));
6395 codeconv_set_strict(FALSE);
6397 if (!dest || *dest == '\0') {
6398 gchar *test_conv_global_out = NULL;
6399 gchar *test_conv_reply = NULL;
6401 /* automatic mode. be automatic. */
6402 codeconv_set_strict(TRUE);
6404 out_codeset = conv_get_outgoing_charset_str();
6406 debug_print("trying to convert to %s\n", out_codeset);
6407 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6410 if (!test_conv_global_out && compose->orig_charset
6411 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6412 out_codeset = compose->orig_charset;
6413 debug_print("failure; trying to convert to %s\n", out_codeset);
6414 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6417 if (!test_conv_global_out && !test_conv_reply) {
6419 out_codeset = CS_INTERNAL;
6420 debug_print("finally using %s\n", out_codeset);
6422 g_free(test_conv_global_out);
6423 g_free(test_conv_reply);
6424 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6426 codeconv_set_strict(FALSE);
6431 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6435 cm_return_if_fail(user_data != NULL);
6437 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6438 g_strstrip(address);
6439 if (*address != '\0') {
6440 gchar *name = procheader_get_fromname(address);
6441 extract_address(address);
6442 addressbook_add_contact(name, address, NULL, NULL);
6447 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6449 GtkWidget *menuitem;
6452 cm_return_if_fail(menu != NULL);
6453 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6455 menuitem = gtk_separator_menu_item_new();
6456 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6457 gtk_widget_show(menuitem);
6459 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6460 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6462 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6463 g_strstrip(address);
6464 if (*address == '\0') {
6465 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6468 g_signal_connect(G_OBJECT(menuitem), "activate",
6469 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6470 gtk_widget_show(menuitem);
6473 static void compose_create_header_entry(Compose *compose)
6475 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6482 const gchar *header = NULL;
6483 ComposeHeaderEntry *headerentry;
6484 gboolean standard_header = FALSE;
6485 GtkListStore *model;
6487 #if !(GTK_CHECK_VERSION(2,12,0))
6488 GtkTooltips *tips = compose->tooltips;
6491 headerentry = g_new0(ComposeHeaderEntry, 1);
6494 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6495 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6496 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6498 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6500 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6502 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6503 COMPOSE_NEWSGROUPS);
6504 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6506 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6507 COMPOSE_FOLLOWUPTO);
6509 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6510 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6511 G_CALLBACK(compose_grab_focus_cb), compose);
6512 gtk_widget_show(combo);
6513 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6514 compose->header_nextrow, compose->header_nextrow+1,
6515 GTK_SHRINK, GTK_FILL, 0, 0);
6516 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6517 const gchar *last_header_entry = gtk_entry_get_text(
6518 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6520 while (*string != NULL) {
6521 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6522 standard_header = TRUE;
6525 if (standard_header)
6526 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6528 if (!compose->header_last || !standard_header) {
6529 switch(compose->account->protocol) {
6531 header = prefs_common_translated_header_name("Newsgroups:");
6534 header = prefs_common_translated_header_name("To:");
6539 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6541 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6542 G_CALLBACK(compose_grab_focus_cb), compose);
6544 /* Entry field with cleanup button */
6545 button = gtk_button_new();
6546 gtk_button_set_image(GTK_BUTTON(button),
6547 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6548 gtk_widget_show(button);
6549 CLAWS_SET_TIP(button,
6550 _("Delete entry contents"));
6551 entry = gtk_entry_new();
6552 gtk_widget_show(entry);
6553 CLAWS_SET_TIP(entry,
6554 _("Use <tab> to autocomplete from addressbook"));
6555 hbox = gtk_hbox_new (FALSE, 0);
6556 gtk_widget_show(hbox);
6557 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6558 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6559 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6560 compose->header_nextrow, compose->header_nextrow+1,
6561 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6563 g_signal_connect(G_OBJECT(entry), "key-press-event",
6564 G_CALLBACK(compose_headerentry_key_press_event_cb),
6566 g_signal_connect(G_OBJECT(entry), "changed",
6567 G_CALLBACK(compose_headerentry_changed_cb),
6569 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6570 G_CALLBACK(compose_grab_focus_cb), compose);
6572 g_signal_connect(G_OBJECT(button), "clicked",
6573 G_CALLBACK(compose_headerentry_button_clicked_cb),
6577 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6578 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6579 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6580 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6581 G_CALLBACK(compose_header_drag_received_cb),
6583 g_signal_connect(G_OBJECT(entry), "drag-drop",
6584 G_CALLBACK(compose_drag_drop),
6586 g_signal_connect(G_OBJECT(entry), "populate-popup",
6587 G_CALLBACK(compose_entry_popup_extend),
6590 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6592 headerentry->compose = compose;
6593 headerentry->combo = combo;
6594 headerentry->entry = entry;
6595 headerentry->button = button;
6596 headerentry->hbox = hbox;
6597 headerentry->headernum = compose->header_nextrow;
6598 headerentry->type = PREF_NONE;
6600 compose->header_nextrow++;
6601 compose->header_last = headerentry;
6602 compose->header_list =
6603 g_slist_append(compose->header_list,
6607 static void compose_add_header_entry(Compose *compose, const gchar *header,
6608 gchar *text, ComposePrefType pref_type)
6610 ComposeHeaderEntry *last_header = compose->header_last;
6611 gchar *tmp = g_strdup(text), *email;
6612 gboolean replyto_hdr;
6614 replyto_hdr = (!strcasecmp(header,
6615 prefs_common_translated_header_name("Reply-To:")) ||
6617 prefs_common_translated_header_name("Followup-To:")) ||
6619 prefs_common_translated_header_name("In-Reply-To:")));
6621 extract_address(tmp);
6622 email = g_utf8_strdown(tmp, -1);
6624 if (replyto_hdr == FALSE &&
6625 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6627 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6628 header, text, (gint) pref_type);
6634 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6635 gtk_entry_set_text(GTK_ENTRY(
6636 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6638 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6639 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6640 last_header->type = pref_type;
6642 if (replyto_hdr == FALSE)
6643 g_hash_table_insert(compose->email_hashtable, email,
6644 GUINT_TO_POINTER(1));
6651 static void compose_destroy_headerentry(Compose *compose,
6652 ComposeHeaderEntry *headerentry)
6654 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6657 extract_address(text);
6658 email = g_utf8_strdown(text, -1);
6659 g_hash_table_remove(compose->email_hashtable, email);
6663 gtk_widget_destroy(headerentry->combo);
6664 gtk_widget_destroy(headerentry->entry);
6665 gtk_widget_destroy(headerentry->button);
6666 gtk_widget_destroy(headerentry->hbox);
6667 g_free(headerentry);
6670 static void compose_remove_header_entries(Compose *compose)
6673 for (list = compose->header_list; list; list = list->next)
6674 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6676 compose->header_last = NULL;
6677 g_slist_free(compose->header_list);
6678 compose->header_list = NULL;
6679 compose->header_nextrow = 1;
6680 compose_create_header_entry(compose);
6683 static GtkWidget *compose_create_header(Compose *compose)
6685 GtkWidget *from_optmenu_hbox;
6686 GtkWidget *header_scrolledwin;
6687 GtkWidget *header_table;
6691 /* header labels and entries */
6692 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6693 gtk_widget_show(header_scrolledwin);
6694 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6696 header_table = gtk_table_new(2, 2, FALSE);
6697 gtk_widget_show(header_table);
6698 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6699 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6700 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6703 /* option menu for selecting accounts */
6704 from_optmenu_hbox = compose_account_option_menu_create(compose);
6705 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6706 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6709 compose->header_table = header_table;
6710 compose->header_list = NULL;
6711 compose->header_nextrow = count;
6713 compose_create_header_entry(compose);
6715 compose->table = NULL;
6717 return header_scrolledwin ;
6720 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6722 Compose *compose = (Compose *)data;
6723 GdkEventButton event;
6726 event.time = gtk_get_current_event_time();
6728 return attach_button_pressed(compose->attach_clist, &event, compose);
6731 static GtkWidget *compose_create_attach(Compose *compose)
6733 GtkWidget *attach_scrwin;
6734 GtkWidget *attach_clist;
6736 GtkListStore *store;
6737 GtkCellRenderer *renderer;
6738 GtkTreeViewColumn *column;
6739 GtkTreeSelection *selection;
6741 /* attachment list */
6742 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6743 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6744 GTK_POLICY_AUTOMATIC,
6745 GTK_POLICY_AUTOMATIC);
6746 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6748 store = gtk_list_store_new(N_ATTACH_COLS,
6754 G_TYPE_AUTO_POINTER,
6756 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6757 (GTK_TREE_MODEL(store)));
6758 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6759 g_object_unref(store);
6761 renderer = gtk_cell_renderer_text_new();
6762 column = gtk_tree_view_column_new_with_attributes
6763 (_("Mime type"), renderer, "text",
6764 COL_MIMETYPE, NULL);
6765 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6767 renderer = gtk_cell_renderer_text_new();
6768 column = gtk_tree_view_column_new_with_attributes
6769 (_("Size"), renderer, "text",
6771 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6773 renderer = gtk_cell_renderer_text_new();
6774 column = gtk_tree_view_column_new_with_attributes
6775 (_("Name"), renderer, "text",
6777 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6779 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6780 prefs_common.use_stripes_everywhere);
6781 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6782 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6784 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6785 G_CALLBACK(attach_selected), compose);
6786 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6787 G_CALLBACK(attach_button_pressed), compose);
6789 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6790 G_CALLBACK(popup_attach_button_pressed), compose);
6792 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6793 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6794 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6795 G_CALLBACK(popup_attach_button_pressed), compose);
6797 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6798 G_CALLBACK(attach_key_pressed), compose);
6801 gtk_drag_dest_set(attach_clist,
6802 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6803 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6804 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6805 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6806 G_CALLBACK(compose_attach_drag_received_cb),
6808 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6809 G_CALLBACK(compose_drag_drop),
6812 compose->attach_scrwin = attach_scrwin;
6813 compose->attach_clist = attach_clist;
6815 return attach_scrwin;
6818 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6819 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6821 static GtkWidget *compose_create_others(Compose *compose)
6824 GtkWidget *savemsg_checkbtn;
6825 GtkWidget *savemsg_combo;
6826 GtkWidget *savemsg_select;
6829 gchar *folderidentifier;
6831 /* Table for settings */
6832 table = gtk_table_new(3, 1, FALSE);
6833 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6834 gtk_widget_show(table);
6835 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6838 /* Save Message to folder */
6839 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6840 gtk_widget_show(savemsg_checkbtn);
6841 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6842 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6843 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6845 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6846 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6848 savemsg_combo = gtk_combo_box_entry_new_text();
6849 compose->savemsg_checkbtn = savemsg_checkbtn;
6850 compose->savemsg_combo = savemsg_combo;
6851 gtk_widget_show(savemsg_combo);
6853 if (prefs_common.compose_save_to_history)
6854 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6855 prefs_common.compose_save_to_history);
6857 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6858 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6859 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6860 G_CALLBACK(compose_grab_focus_cb), compose);
6861 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6862 folderidentifier = folder_item_get_identifier(account_get_special_folder
6863 (compose->account, F_OUTBOX));
6864 compose_set_save_to(compose, folderidentifier);
6865 g_free(folderidentifier);
6868 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6869 gtk_widget_show(savemsg_select);
6870 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6871 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6872 G_CALLBACK(compose_savemsg_select_cb),
6880 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6882 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6883 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6886 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6891 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6894 path = folder_item_get_identifier(dest);
6896 compose_set_save_to(compose, path);
6900 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6901 GdkAtom clip, GtkTextIter *insert_place);
6904 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6908 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6910 if (event->button == 3) {
6912 GtkTextIter sel_start, sel_end;
6913 gboolean stuff_selected;
6915 /* move the cursor to allow GtkAspell to check the word
6916 * under the mouse */
6917 if (event->x && event->y) {
6918 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6919 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6921 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6924 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6925 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6928 stuff_selected = gtk_text_buffer_get_selection_bounds(
6930 &sel_start, &sel_end);
6932 gtk_text_buffer_place_cursor (buffer, &iter);
6933 /* reselect stuff */
6935 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6936 gtk_text_buffer_select_range(buffer,
6937 &sel_start, &sel_end);
6939 return FALSE; /* pass the event so that the right-click goes through */
6942 if (event->button == 2) {
6947 /* get the middle-click position to paste at the correct place */
6948 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6949 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6951 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6954 entry_paste_clipboard(compose, text,
6955 prefs_common.linewrap_pastes,
6956 GDK_SELECTION_PRIMARY, &iter);
6964 static void compose_spell_menu_changed(void *data)
6966 Compose *compose = (Compose *)data;
6968 GtkWidget *menuitem;
6969 GtkWidget *parent_item;
6970 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6973 if (compose->gtkaspell == NULL)
6976 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6977 "/Menu/Spelling/Options");
6979 /* setting the submenu removes /Spelling/Options from the factory
6980 * so we need to save it */
6982 if (parent_item == NULL) {
6983 parent_item = compose->aspell_options_menu;
6984 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6986 compose->aspell_options_menu = parent_item;
6988 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6990 spell_menu = g_slist_reverse(spell_menu);
6991 for (items = spell_menu;
6992 items; items = items->next) {
6993 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6994 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6995 gtk_widget_show(GTK_WIDGET(menuitem));
6997 g_slist_free(spell_menu);
6999 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7000 gtk_widget_show(parent_item);
7003 static void compose_dict_changed(void *data)
7005 Compose *compose = (Compose *) data;
7007 if(compose->gtkaspell &&
7008 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7011 gtkaspell_highlight_all(compose->gtkaspell);
7012 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7016 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7018 Compose *compose = (Compose *)data;
7019 GdkEventButton event;
7022 event.time = gtk_get_current_event_time();
7026 return text_clicked(compose->text, &event, compose);
7029 static gboolean compose_force_window_origin = TRUE;
7030 static Compose *compose_create(PrefsAccount *account,
7039 GtkWidget *handlebox;
7041 GtkWidget *notebook;
7043 GtkWidget *attach_hbox;
7044 GtkWidget *attach_lab1;
7045 GtkWidget *attach_lab2;
7050 GtkWidget *subject_hbox;
7051 GtkWidget *subject_frame;
7052 GtkWidget *subject_entry;
7056 GtkWidget *edit_vbox;
7057 #if !GTK_CHECK_VERSION(2,24,0)
7058 GtkWidget *ruler_hbox;
7061 GtkWidget *scrolledwin;
7063 GtkTextBuffer *buffer;
7064 GtkClipboard *clipboard;
7067 UndoMain *undostruct;
7069 gchar *titles[N_ATTACH_COLS];
7070 GtkWidget *popupmenu;
7071 GtkWidget *tmpl_menu;
7072 GtkActionGroup *action_group = NULL;
7075 GtkAspell * gtkaspell = NULL;
7078 static GdkGeometry geometry;
7080 cm_return_val_if_fail(account != NULL, NULL);
7082 debug_print("Creating compose window...\n");
7083 compose = g_new0(Compose, 1);
7085 titles[COL_MIMETYPE] = _("MIME type");
7086 titles[COL_SIZE] = _("Size");
7087 titles[COL_NAME] = _("Name");
7088 titles[COL_CHARSET] = _("Charset");
7090 compose->batch = batch;
7091 compose->account = account;
7092 compose->folder = folder;
7094 compose->mutex = g_mutex_new();
7095 compose->set_cursor_pos = -1;
7097 #if !(GTK_CHECK_VERSION(2,12,0))
7098 compose->tooltips = tips;
7101 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7103 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7104 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7106 if (!geometry.max_width) {
7107 geometry.max_width = gdk_screen_width();
7108 geometry.max_height = gdk_screen_height();
7111 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7112 &geometry, GDK_HINT_MAX_SIZE);
7113 if (!geometry.min_width) {
7114 geometry.min_width = 600;
7115 geometry.min_height = 440;
7117 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7118 &geometry, GDK_HINT_MIN_SIZE);
7120 #ifndef GENERIC_UMPC
7121 if (compose_force_window_origin)
7122 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7123 prefs_common.compose_y);
7125 g_signal_connect(G_OBJECT(window), "delete_event",
7126 G_CALLBACK(compose_delete_cb), compose);
7127 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7128 gtk_widget_realize(window);
7130 gtkut_widget_set_composer_icon(window);
7132 vbox = gtk_vbox_new(FALSE, 0);
7133 gtk_container_add(GTK_CONTAINER(window), vbox);
7135 compose->ui_manager = gtk_ui_manager_new();
7136 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7137 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7138 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7139 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7140 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7141 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7142 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7143 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7144 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7145 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7235 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7254 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7260 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7265 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)
7266 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)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7272 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)
7273 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)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7277 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7278 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)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7282 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)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7288 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)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7294 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)
7295 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)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7304 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7308 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)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7326 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7327 gtk_widget_show_all(menubar);
7329 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7331 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7333 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7336 if (prefs_common.toolbar_detachable) {
7337 handlebox = gtk_handle_box_new();
7339 handlebox = gtk_hbox_new(FALSE, 0);
7341 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7343 gtk_widget_realize(handlebox);
7345 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7348 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7352 vbox2 = gtk_vbox_new(FALSE, 2);
7353 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7354 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7357 notebook = gtk_notebook_new();
7358 gtk_widget_set_size_request(notebook, -1, 130);
7359 gtk_widget_show(notebook);
7361 /* header labels and entries */
7362 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7363 compose_create_header(compose),
7364 gtk_label_new_with_mnemonic(_("Hea_der")));
7365 /* attachment list */
7366 attach_hbox = gtk_hbox_new(FALSE, 0);
7367 gtk_widget_show(attach_hbox);
7369 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7370 gtk_widget_show(attach_lab1);
7371 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7373 attach_lab2 = gtk_label_new("");
7374 gtk_widget_show(attach_lab2);
7375 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7377 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7378 compose_create_attach(compose),
7381 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7382 compose_create_others(compose),
7383 gtk_label_new_with_mnemonic(_("Othe_rs")));
7386 subject_hbox = gtk_hbox_new(FALSE, 0);
7387 gtk_widget_show(subject_hbox);
7389 subject_frame = gtk_frame_new(NULL);
7390 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7391 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7392 gtk_widget_show(subject_frame);
7394 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7395 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7396 gtk_widget_show(subject);
7398 label = gtk_label_new(_("Subject:"));
7399 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7400 gtk_widget_show(label);
7403 subject_entry = claws_spell_entry_new();
7405 subject_entry = gtk_entry_new();
7407 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7408 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7409 G_CALLBACK(compose_grab_focus_cb), compose);
7410 gtk_widget_show(subject_entry);
7411 compose->subject_entry = subject_entry;
7412 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7414 edit_vbox = gtk_vbox_new(FALSE, 0);
7416 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7418 #if !GTK_CHECK_VERSION(2,24,0)
7420 ruler_hbox = gtk_hbox_new(FALSE, 0);
7421 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7423 ruler = gtk_shruler_new();
7424 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7425 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7429 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7430 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7431 GTK_POLICY_AUTOMATIC,
7432 GTK_POLICY_AUTOMATIC);
7433 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7435 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7436 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7438 text = gtk_text_view_new();
7439 if (prefs_common.show_compose_margin) {
7440 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7441 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7443 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7444 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7445 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7446 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7447 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7449 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7451 #if !GTK_CHECK_VERSION(2,24,0)
7452 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7453 G_CALLBACK(compose_edit_size_alloc),
7456 g_signal_connect(G_OBJECT(buffer), "changed",
7457 G_CALLBACK(compose_changed_cb), compose);
7458 g_signal_connect(G_OBJECT(text), "grab_focus",
7459 G_CALLBACK(compose_grab_focus_cb), compose);
7460 g_signal_connect(G_OBJECT(buffer), "insert_text",
7461 G_CALLBACK(text_inserted), compose);
7462 g_signal_connect(G_OBJECT(text), "button_press_event",
7463 G_CALLBACK(text_clicked), compose);
7465 g_signal_connect(G_OBJECT(text), "popup-menu",
7466 G_CALLBACK(compose_popup_menu), compose);
7468 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7469 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7470 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7471 G_CALLBACK(compose_popup_menu), compose);
7473 g_signal_connect(G_OBJECT(subject_entry), "changed",
7474 G_CALLBACK(compose_changed_cb), compose);
7477 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7478 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7479 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7480 g_signal_connect(G_OBJECT(text), "drag_data_received",
7481 G_CALLBACK(compose_insert_drag_received_cb),
7483 g_signal_connect(G_OBJECT(text), "drag-drop",
7484 G_CALLBACK(compose_drag_drop),
7486 gtk_widget_show_all(vbox);
7488 /* pane between attach clist and text */
7489 paned = gtk_vpaned_new();
7490 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7492 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7493 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7495 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7497 gtk_paned_add1(GTK_PANED(paned), notebook);
7498 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7499 gtk_widget_show_all(paned);
7502 if (prefs_common.textfont) {
7503 PangoFontDescription *font_desc;
7505 font_desc = pango_font_description_from_string
7506 (prefs_common.textfont);
7508 gtk_widget_modify_font(text, font_desc);
7509 pango_font_description_free(font_desc);
7513 gtk_action_group_add_actions(action_group, compose_popup_entries,
7514 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7515 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7516 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7517 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7518 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7519 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7520 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7522 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7524 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7525 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7526 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7528 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7530 undostruct = undo_init(text);
7531 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7534 address_completion_start(window);
7536 compose->window = window;
7537 compose->vbox = vbox;
7538 compose->menubar = menubar;
7539 compose->handlebox = handlebox;
7541 compose->vbox2 = vbox2;
7543 compose->paned = paned;
7545 compose->attach_label = attach_lab2;
7547 compose->notebook = notebook;
7548 compose->edit_vbox = edit_vbox;
7549 #if !GTK_CHECK_VERSION(2,24,0)
7550 compose->ruler_hbox = ruler_hbox;
7551 compose->ruler = ruler;
7553 compose->scrolledwin = scrolledwin;
7554 compose->text = text;
7556 compose->focused_editable = NULL;
7558 compose->popupmenu = popupmenu;
7560 compose->tmpl_menu = tmpl_menu;
7562 compose->mode = mode;
7563 compose->rmode = mode;
7565 compose->targetinfo = NULL;
7566 compose->replyinfo = NULL;
7567 compose->fwdinfo = NULL;
7569 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7570 g_str_equal, (GDestroyNotify) g_free, NULL);
7572 compose->replyto = NULL;
7574 compose->bcc = NULL;
7575 compose->followup_to = NULL;
7577 compose->ml_post = NULL;
7579 compose->inreplyto = NULL;
7580 compose->references = NULL;
7581 compose->msgid = NULL;
7582 compose->boundary = NULL;
7584 compose->autowrap = prefs_common.autowrap;
7585 compose->autoindent = prefs_common.auto_indent;
7586 compose->use_signing = FALSE;
7587 compose->use_encryption = FALSE;
7588 compose->privacy_system = NULL;
7590 compose->modified = FALSE;
7592 compose->return_receipt = FALSE;
7594 compose->to_list = NULL;
7595 compose->newsgroup_list = NULL;
7597 compose->undostruct = undostruct;
7599 compose->sig_str = NULL;
7601 compose->exteditor_file = NULL;
7602 compose->exteditor_pid = -1;
7603 compose->exteditor_tag = -1;
7604 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7607 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7608 if (mode != COMPOSE_REDIRECT) {
7609 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7610 strcmp(prefs_common.dictionary, "")) {
7611 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7612 prefs_common.alt_dictionary,
7613 conv_get_locale_charset_str(),
7614 prefs_common.misspelled_col,
7615 prefs_common.check_while_typing,
7616 prefs_common.recheck_when_changing_dict,
7617 prefs_common.use_alternate,
7618 prefs_common.use_both_dicts,
7619 GTK_TEXT_VIEW(text),
7620 GTK_WINDOW(compose->window),
7621 compose_dict_changed,
7622 compose_spell_menu_changed,
7625 alertpanel_error(_("Spell checker could not "
7627 gtkaspell_checkers_strerror());
7628 gtkaspell_checkers_reset_error();
7630 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7634 compose->gtkaspell = gtkaspell;
7635 compose_spell_menu_changed(compose);
7636 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7639 compose_select_account(compose, account, TRUE);
7641 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7642 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7644 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7645 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7647 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7648 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7650 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7651 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7653 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7654 if (account->protocol != A_NNTP)
7655 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7656 prefs_common_translated_header_name("To:"));
7658 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7659 prefs_common_translated_header_name("Newsgroups:"));
7661 addressbook_set_target_compose(compose);
7663 if (mode != COMPOSE_REDIRECT)
7664 compose_set_template_menu(compose);
7666 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7669 compose_list = g_list_append(compose_list, compose);
7671 #if !GTK_CHECK_VERSION(2,24,0)
7672 if (!prefs_common.show_ruler)
7673 gtk_widget_hide(ruler_hbox);
7675 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7678 compose->priority = PRIORITY_NORMAL;
7679 compose_update_priority_menu_item(compose);
7681 compose_set_out_encoding(compose);
7684 compose_update_actions_menu(compose);
7686 /* Privacy Systems menu */
7687 compose_update_privacy_systems_menu(compose);
7689 activate_privacy_system(compose, account, TRUE);
7690 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7692 gtk_widget_realize(window);
7694 gtk_widget_show(window);
7696 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7697 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7704 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7709 GtkWidget *optmenubox;
7712 GtkWidget *from_name = NULL;
7713 #if !(GTK_CHECK_VERSION(2,12,0))
7714 GtkTooltips *tips = compose->tooltips;
7717 gint num = 0, def_menu = 0;
7719 accounts = account_get_list();
7720 cm_return_val_if_fail(accounts != NULL, NULL);
7722 optmenubox = gtk_event_box_new();
7723 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7724 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7726 hbox = gtk_hbox_new(FALSE, 6);
7727 from_name = gtk_entry_new();
7729 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7730 G_CALLBACK(compose_grab_focus_cb), compose);
7732 for (; accounts != NULL; accounts = accounts->next, num++) {
7733 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7734 gchar *name, *from = NULL;
7736 if (ac == compose->account) def_menu = num;
7738 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7741 if (ac == compose->account) {
7742 if (ac->name && *ac->name) {
7744 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7745 from = g_strdup_printf("%s <%s>",
7747 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7749 from = g_strdup_printf("%s",
7751 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7754 COMBOBOX_ADD(menu, name, ac->account_id);
7759 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7761 g_signal_connect(G_OBJECT(optmenu), "changed",
7762 G_CALLBACK(account_activated),
7764 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7765 G_CALLBACK(compose_entry_popup_extend),
7768 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7769 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7771 CLAWS_SET_TIP(optmenubox,
7772 _("Account to use for this email"));
7773 CLAWS_SET_TIP(from_name,
7774 _("Sender address to be used"));
7776 compose->account_combo = optmenu;
7777 compose->from_name = from_name;
7782 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7784 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7785 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7786 Compose *compose = (Compose *) data;
7788 compose->priority = value;
7792 static void compose_reply_change_mode(Compose *compose,
7795 gboolean was_modified = compose->modified;
7797 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7799 cm_return_if_fail(compose->replyinfo != NULL);
7801 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7803 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7805 if (action == COMPOSE_REPLY_TO_ALL)
7807 if (action == COMPOSE_REPLY_TO_SENDER)
7809 if (action == COMPOSE_REPLY_TO_LIST)
7812 compose_remove_header_entries(compose);
7813 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7814 if (compose->account->set_autocc && compose->account->auto_cc)
7815 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7817 if (compose->account->set_autobcc && compose->account->auto_bcc)
7818 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7820 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7821 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7822 compose_show_first_last_header(compose, TRUE);
7823 compose->modified = was_modified;
7824 compose_set_title(compose);
7827 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7829 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7830 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7831 Compose *compose = (Compose *) data;
7834 compose_reply_change_mode(compose, value);
7837 static void compose_update_priority_menu_item(Compose * compose)
7839 GtkWidget *menuitem = NULL;
7840 switch (compose->priority) {
7841 case PRIORITY_HIGHEST:
7842 menuitem = gtk_ui_manager_get_widget
7843 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7846 menuitem = gtk_ui_manager_get_widget
7847 (compose->ui_manager, "/Menu/Options/Priority/High");
7849 case PRIORITY_NORMAL:
7850 menuitem = gtk_ui_manager_get_widget
7851 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7854 menuitem = gtk_ui_manager_get_widget
7855 (compose->ui_manager, "/Menu/Options/Priority/Low");
7857 case PRIORITY_LOWEST:
7858 menuitem = gtk_ui_manager_get_widget
7859 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7862 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7865 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7867 Compose *compose = (Compose *) data;
7869 gboolean can_sign = FALSE, can_encrypt = FALSE;
7871 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7873 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7876 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7877 g_free(compose->privacy_system);
7878 compose->privacy_system = NULL;
7879 if (systemid != NULL) {
7880 compose->privacy_system = g_strdup(systemid);
7882 can_sign = privacy_system_can_sign(systemid);
7883 can_encrypt = privacy_system_can_encrypt(systemid);
7886 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7888 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7889 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7892 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7894 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7895 GtkWidget *menuitem = NULL;
7897 gboolean can_sign = FALSE, can_encrypt = FALSE;
7898 gboolean found = FALSE;
7900 if (compose->privacy_system != NULL) {
7902 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7903 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7904 cm_return_if_fail(menuitem != NULL);
7906 amenu = GTK_MENU_SHELL(menuitem)->children;
7908 while (amenu != NULL) {
7909 GList *alist = amenu->next;
7911 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7912 if (systemid != NULL) {
7913 if (strcmp(systemid, compose->privacy_system) == 0 &&
7914 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7915 menuitem = GTK_WIDGET(amenu->data);
7917 can_sign = privacy_system_can_sign(systemid);
7918 can_encrypt = privacy_system_can_encrypt(systemid);
7922 } else if (strlen(compose->privacy_system) == 0 &&
7923 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7924 menuitem = GTK_WIDGET(amenu->data);
7927 can_encrypt = FALSE;
7934 if (menuitem != NULL)
7935 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7937 if (warn && !found && strlen(compose->privacy_system)) {
7938 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7939 "will not be able to sign or encrypt this message."),
7940 compose->privacy_system);
7944 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7945 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7948 static void compose_set_out_encoding(Compose *compose)
7950 CharSet out_encoding;
7951 const gchar *branch = NULL;
7952 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7954 switch(out_encoding) {
7955 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7956 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7957 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7958 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7959 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7960 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7961 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7962 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7963 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7964 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7965 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7966 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7967 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7968 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7969 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7970 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7971 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7972 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7973 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7974 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7975 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7976 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7977 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7978 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7979 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7980 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7981 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7982 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7983 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7984 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7985 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7986 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7987 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7989 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7992 static void compose_set_template_menu(Compose *compose)
7994 GSList *tmpl_list, *cur;
7998 tmpl_list = template_get_config();
8000 menu = gtk_menu_new();
8002 gtk_menu_set_accel_group (GTK_MENU (menu),
8003 gtk_ui_manager_get_accel_group(compose->ui_manager));
8004 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8005 Template *tmpl = (Template *)cur->data;
8006 gchar *accel_path = NULL;
8007 item = gtk_menu_item_new_with_label(tmpl->name);
8008 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8009 g_signal_connect(G_OBJECT(item), "activate",
8010 G_CALLBACK(compose_template_activate_cb),
8012 g_object_set_data(G_OBJECT(item), "template", tmpl);
8013 gtk_widget_show(item);
8014 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8015 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8019 gtk_widget_show(menu);
8020 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8023 void compose_update_actions_menu(Compose *compose)
8025 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8028 static void compose_update_privacy_systems_menu(Compose *compose)
8030 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8031 GSList *systems, *cur;
8033 GtkWidget *system_none;
8035 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8036 GtkWidget *privacy_menu = gtk_menu_new();
8038 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8039 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8041 g_signal_connect(G_OBJECT(system_none), "activate",
8042 G_CALLBACK(compose_set_privacy_system_cb), compose);
8044 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8045 gtk_widget_show(system_none);
8047 systems = privacy_get_system_ids();
8048 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8049 gchar *systemid = cur->data;
8051 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8052 widget = gtk_radio_menu_item_new_with_label(group,
8053 privacy_system_get_name(systemid));
8054 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8055 g_strdup(systemid), g_free);
8056 g_signal_connect(G_OBJECT(widget), "activate",
8057 G_CALLBACK(compose_set_privacy_system_cb), compose);
8059 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8060 gtk_widget_show(widget);
8063 g_slist_free(systems);
8064 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8065 gtk_widget_show_all(privacy_menu);
8066 gtk_widget_show_all(privacy_menuitem);
8069 void compose_reflect_prefs_all(void)
8074 for (cur = compose_list; cur != NULL; cur = cur->next) {
8075 compose = (Compose *)cur->data;
8076 compose_set_template_menu(compose);
8080 void compose_reflect_prefs_pixmap_theme(void)
8085 for (cur = compose_list; cur != NULL; cur = cur->next) {
8086 compose = (Compose *)cur->data;
8087 toolbar_update(TOOLBAR_COMPOSE, compose);
8091 static const gchar *compose_quote_char_from_context(Compose *compose)
8093 const gchar *qmark = NULL;
8095 cm_return_val_if_fail(compose != NULL, NULL);
8097 switch (compose->mode) {
8098 /* use forward-specific quote char */
8099 case COMPOSE_FORWARD:
8100 case COMPOSE_FORWARD_AS_ATTACH:
8101 case COMPOSE_FORWARD_INLINE:
8102 if (compose->folder && compose->folder->prefs &&
8103 compose->folder->prefs->forward_with_format)
8104 qmark = compose->folder->prefs->forward_quotemark;
8105 else if (compose->account->forward_with_format)
8106 qmark = compose->account->forward_quotemark;
8108 qmark = prefs_common.fw_quotemark;
8111 /* use reply-specific quote char in all other modes */
8113 if (compose->folder && compose->folder->prefs &&
8114 compose->folder->prefs->reply_with_format)
8115 qmark = compose->folder->prefs->reply_quotemark;
8116 else if (compose->account->reply_with_format)
8117 qmark = compose->account->reply_quotemark;
8119 qmark = prefs_common.quotemark;
8123 if (qmark == NULL || *qmark == '\0')
8129 static void compose_template_apply(Compose *compose, Template *tmpl,
8133 GtkTextBuffer *buffer;
8137 gchar *parsed_str = NULL;
8138 gint cursor_pos = 0;
8139 const gchar *err_msg = _("The body of the template has an error at line %d.");
8142 /* process the body */
8144 text = GTK_TEXT_VIEW(compose->text);
8145 buffer = gtk_text_view_get_buffer(text);
8148 qmark = compose_quote_char_from_context(compose);
8150 if (compose->replyinfo != NULL) {
8153 gtk_text_buffer_set_text(buffer, "", -1);
8154 mark = gtk_text_buffer_get_insert(buffer);
8155 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8157 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8158 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8160 } else if (compose->fwdinfo != NULL) {
8163 gtk_text_buffer_set_text(buffer, "", -1);
8164 mark = gtk_text_buffer_get_insert(buffer);
8165 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8167 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8168 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8171 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8173 GtkTextIter start, end;
8176 gtk_text_buffer_get_start_iter(buffer, &start);
8177 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8178 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8180 /* clear the buffer now */
8182 gtk_text_buffer_set_text(buffer, "", -1);
8184 parsed_str = compose_quote_fmt(compose, dummyinfo,
8185 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8186 procmsg_msginfo_free( dummyinfo );
8192 gtk_text_buffer_set_text(buffer, "", -1);
8193 mark = gtk_text_buffer_get_insert(buffer);
8194 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8197 if (replace && parsed_str && compose->account->auto_sig)
8198 compose_insert_sig(compose, FALSE);
8200 if (replace && parsed_str) {
8201 gtk_text_buffer_get_start_iter(buffer, &iter);
8202 gtk_text_buffer_place_cursor(buffer, &iter);
8206 cursor_pos = quote_fmt_get_cursor_pos();
8207 compose->set_cursor_pos = cursor_pos;
8208 if (cursor_pos == -1)
8210 gtk_text_buffer_get_start_iter(buffer, &iter);
8211 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8212 gtk_text_buffer_place_cursor(buffer, &iter);
8215 /* process the other fields */
8217 compose_template_apply_fields(compose, tmpl);
8218 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8219 quote_fmt_reset_vartable();
8220 compose_changed_cb(NULL, compose);
8223 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8224 gtkaspell_highlight_all(compose->gtkaspell);
8228 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8230 MsgInfo* dummyinfo = NULL;
8231 MsgInfo *msginfo = NULL;
8234 if (compose->replyinfo != NULL)
8235 msginfo = compose->replyinfo;
8236 else if (compose->fwdinfo != NULL)
8237 msginfo = compose->fwdinfo;
8239 dummyinfo = compose_msginfo_new_from_compose(compose);
8240 msginfo = dummyinfo;
8243 if (tmpl->from && *tmpl->from != '\0') {
8245 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8246 compose->gtkaspell);
8248 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8250 quote_fmt_scan_string(tmpl->from);
8253 buf = quote_fmt_get_buffer();
8255 alertpanel_error(_("Template From format error."));
8257 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8261 if (tmpl->to && *tmpl->to != '\0') {
8263 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8264 compose->gtkaspell);
8266 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8268 quote_fmt_scan_string(tmpl->to);
8271 buf = quote_fmt_get_buffer();
8273 alertpanel_error(_("Template To format error."));
8275 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8279 if (tmpl->cc && *tmpl->cc != '\0') {
8281 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8282 compose->gtkaspell);
8284 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8286 quote_fmt_scan_string(tmpl->cc);
8289 buf = quote_fmt_get_buffer();
8291 alertpanel_error(_("Template Cc format error."));
8293 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8297 if (tmpl->bcc && *tmpl->bcc != '\0') {
8299 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8300 compose->gtkaspell);
8302 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8304 quote_fmt_scan_string(tmpl->bcc);
8307 buf = quote_fmt_get_buffer();
8309 alertpanel_error(_("Template Bcc format error."));
8311 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8315 /* process the subject */
8316 if (tmpl->subject && *tmpl->subject != '\0') {
8318 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8319 compose->gtkaspell);
8321 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8323 quote_fmt_scan_string(tmpl->subject);
8326 buf = quote_fmt_get_buffer();
8328 alertpanel_error(_("Template subject format error."));
8330 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8334 procmsg_msginfo_free( dummyinfo );
8337 static void compose_destroy(Compose *compose)
8339 GtkTextBuffer *buffer;
8340 GtkClipboard *clipboard;
8342 compose_list = g_list_remove(compose_list, compose);
8344 if (compose->updating) {
8345 debug_print("danger, not destroying anything now\n");
8346 compose->deferred_destroy = TRUE;
8349 /* NOTE: address_completion_end() does nothing with the window
8350 * however this may change. */
8351 address_completion_end(compose->window);
8353 slist_free_strings(compose->to_list);
8354 g_slist_free(compose->to_list);
8355 slist_free_strings(compose->newsgroup_list);
8356 g_slist_free(compose->newsgroup_list);
8357 slist_free_strings(compose->header_list);
8358 g_slist_free(compose->header_list);
8360 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8362 g_hash_table_destroy(compose->email_hashtable);
8364 procmsg_msginfo_free(compose->targetinfo);
8365 procmsg_msginfo_free(compose->replyinfo);
8366 procmsg_msginfo_free(compose->fwdinfo);
8368 g_free(compose->replyto);
8369 g_free(compose->cc);
8370 g_free(compose->bcc);
8371 g_free(compose->newsgroups);
8372 g_free(compose->followup_to);
8374 g_free(compose->ml_post);
8376 g_free(compose->inreplyto);
8377 g_free(compose->references);
8378 g_free(compose->msgid);
8379 g_free(compose->boundary);
8381 g_free(compose->redirect_filename);
8382 if (compose->undostruct)
8383 undo_destroy(compose->undostruct);
8385 g_free(compose->sig_str);
8387 g_free(compose->exteditor_file);
8389 g_free(compose->orig_charset);
8391 g_free(compose->privacy_system);
8393 if (addressbook_get_target_compose() == compose)
8394 addressbook_set_target_compose(NULL);
8397 if (compose->gtkaspell) {
8398 gtkaspell_delete(compose->gtkaspell);
8399 compose->gtkaspell = NULL;
8403 if (!compose->batch) {
8404 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8405 prefs_common.compose_height = compose->window->allocation.height;
8408 if (!gtk_widget_get_parent(compose->paned))
8409 gtk_widget_destroy(compose->paned);
8410 gtk_widget_destroy(compose->popupmenu);
8412 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8413 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8414 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8416 gtk_widget_destroy(compose->window);
8417 toolbar_destroy(compose->toolbar);
8418 g_free(compose->toolbar);
8419 g_mutex_free(compose->mutex);
8423 static void compose_attach_info_free(AttachInfo *ainfo)
8425 g_free(ainfo->file);
8426 g_free(ainfo->content_type);
8427 g_free(ainfo->name);
8428 g_free(ainfo->charset);
8432 static void compose_attach_update_label(Compose *compose)
8437 GtkTreeModel *model;
8442 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8443 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8444 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8448 while(gtk_tree_model_iter_next(model, &iter))
8451 text = g_strdup_printf("(%d)", i);
8452 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8456 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8458 Compose *compose = (Compose *)data;
8459 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8460 GtkTreeSelection *selection;
8462 GtkTreeModel *model;
8464 selection = gtk_tree_view_get_selection(tree_view);
8465 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8470 for (cur = sel; cur != NULL; cur = cur->next) {
8471 GtkTreePath *path = cur->data;
8472 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8475 gtk_tree_path_free(path);
8478 for (cur = sel; cur != NULL; cur = cur->next) {
8479 GtkTreeRowReference *ref = cur->data;
8480 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8483 if (gtk_tree_model_get_iter(model, &iter, path))
8484 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8486 gtk_tree_path_free(path);
8487 gtk_tree_row_reference_free(ref);
8491 compose_attach_update_label(compose);
8494 static struct _AttachProperty
8497 GtkWidget *mimetype_entry;
8498 GtkWidget *encoding_optmenu;
8499 GtkWidget *path_entry;
8500 GtkWidget *filename_entry;
8502 GtkWidget *cancel_btn;
8505 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8507 gtk_tree_path_free((GtkTreePath *)ptr);
8510 static void compose_attach_property(GtkAction *action, gpointer data)
8512 Compose *compose = (Compose *)data;
8513 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8515 GtkComboBox *optmenu;
8516 GtkTreeSelection *selection;
8518 GtkTreeModel *model;
8521 static gboolean cancelled;
8523 /* only if one selected */
8524 selection = gtk_tree_view_get_selection(tree_view);
8525 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8528 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8532 path = (GtkTreePath *) sel->data;
8533 gtk_tree_model_get_iter(model, &iter, path);
8534 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8537 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8543 if (!attach_prop.window)
8544 compose_attach_property_create(&cancelled);
8545 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8546 gtk_widget_grab_focus(attach_prop.ok_btn);
8547 gtk_widget_show(attach_prop.window);
8548 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8550 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8551 if (ainfo->encoding == ENC_UNKNOWN)
8552 combobox_select_by_data(optmenu, ENC_BASE64);
8554 combobox_select_by_data(optmenu, ainfo->encoding);
8556 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8557 ainfo->content_type ? ainfo->content_type : "");
8558 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8559 ainfo->file ? ainfo->file : "");
8560 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8561 ainfo->name ? ainfo->name : "");
8564 const gchar *entry_text;
8566 gchar *cnttype = NULL;
8573 gtk_widget_hide(attach_prop.window);
8574 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8579 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8580 if (*entry_text != '\0') {
8583 text = g_strstrip(g_strdup(entry_text));
8584 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8585 cnttype = g_strdup(text);
8588 alertpanel_error(_("Invalid MIME type."));
8594 ainfo->encoding = combobox_get_active_data(optmenu);
8596 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8597 if (*entry_text != '\0') {
8598 if (is_file_exist(entry_text) &&
8599 (size = get_file_size(entry_text)) > 0)
8600 file = g_strdup(entry_text);
8603 (_("File doesn't exist or is empty."));
8609 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8610 if (*entry_text != '\0') {
8611 g_free(ainfo->name);
8612 ainfo->name = g_strdup(entry_text);
8616 g_free(ainfo->content_type);
8617 ainfo->content_type = cnttype;
8620 g_free(ainfo->file);
8624 ainfo->size = (goffset)size;
8626 /* update tree store */
8627 text = to_human_readable(ainfo->size);
8628 gtk_tree_model_get_iter(model, &iter, path);
8629 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8630 COL_MIMETYPE, ainfo->content_type,
8632 COL_NAME, ainfo->name,
8633 COL_CHARSET, ainfo->charset,
8639 gtk_tree_path_free(path);
8642 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8644 label = gtk_label_new(str); \
8645 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8646 GTK_FILL, 0, 0, 0); \
8647 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8649 entry = gtk_entry_new(); \
8650 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8651 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8654 static void compose_attach_property_create(gboolean *cancelled)
8660 GtkWidget *mimetype_entry;
8663 GtkListStore *optmenu_menu;
8664 GtkWidget *path_entry;
8665 GtkWidget *filename_entry;
8668 GtkWidget *cancel_btn;
8669 GList *mime_type_list, *strlist;
8672 debug_print("Creating attach_property window...\n");
8674 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8675 gtk_widget_set_size_request(window, 480, -1);
8676 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8677 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8678 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8679 g_signal_connect(G_OBJECT(window), "delete_event",
8680 G_CALLBACK(attach_property_delete_event),
8682 g_signal_connect(G_OBJECT(window), "key_press_event",
8683 G_CALLBACK(attach_property_key_pressed),
8686 vbox = gtk_vbox_new(FALSE, 8);
8687 gtk_container_add(GTK_CONTAINER(window), vbox);
8689 table = gtk_table_new(4, 2, FALSE);
8690 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8691 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8692 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8694 label = gtk_label_new(_("MIME type"));
8695 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8697 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8698 mimetype_entry = gtk_combo_box_entry_new_text();
8699 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8700 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8702 /* stuff with list */
8703 mime_type_list = procmime_get_mime_type_list();
8705 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8706 MimeType *type = (MimeType *) mime_type_list->data;
8709 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8711 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8714 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8715 (GCompareFunc)strcmp2);
8718 for (mime_type_list = strlist; mime_type_list != NULL;
8719 mime_type_list = mime_type_list->next) {
8720 gtk_combo_box_text_append_text(
8721 GTK_COMBO_BOX_TEXT(mimetype_entry),
8722 mime_type_list->data);
8723 g_free(mime_type_list->data);
8725 g_list_free(strlist);
8726 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8727 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8729 label = gtk_label_new(_("Encoding"));
8730 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8732 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8734 hbox = gtk_hbox_new(FALSE, 0);
8735 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8736 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8738 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8739 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8741 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8742 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8743 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8744 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8745 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8747 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8749 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8750 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8752 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8753 &ok_btn, GTK_STOCK_OK,
8755 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8756 gtk_widget_grab_default(ok_btn);
8758 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8759 G_CALLBACK(attach_property_ok),
8761 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8762 G_CALLBACK(attach_property_cancel),
8765 gtk_widget_show_all(vbox);
8767 attach_prop.window = window;
8768 attach_prop.mimetype_entry = mimetype_entry;
8769 attach_prop.encoding_optmenu = optmenu;
8770 attach_prop.path_entry = path_entry;
8771 attach_prop.filename_entry = filename_entry;
8772 attach_prop.ok_btn = ok_btn;
8773 attach_prop.cancel_btn = cancel_btn;
8776 #undef SET_LABEL_AND_ENTRY
8778 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8784 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8790 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8791 gboolean *cancelled)
8799 static gboolean attach_property_key_pressed(GtkWidget *widget,
8801 gboolean *cancelled)
8803 if (event && event->keyval == GDK_Escape) {
8807 if (event && event->keyval == GDK_Return) {
8815 static void compose_exec_ext_editor(Compose *compose)
8822 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8823 G_DIR_SEPARATOR, compose);
8825 if (pipe(pipe_fds) < 0) {
8831 if ((pid = fork()) < 0) {
8838 /* close the write side of the pipe */
8841 compose->exteditor_file = g_strdup(tmp);
8842 compose->exteditor_pid = pid;
8844 compose_set_ext_editor_sensitive(compose, FALSE);
8847 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8849 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8851 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8855 } else { /* process-monitoring process */
8861 /* close the read side of the pipe */
8864 if (compose_write_body_to_file(compose, tmp) < 0) {
8865 fd_write_all(pipe_fds[1], "2\n", 2);
8869 pid_ed = compose_exec_ext_editor_real(tmp);
8871 fd_write_all(pipe_fds[1], "1\n", 2);
8875 /* wait until editor is terminated */
8876 waitpid(pid_ed, NULL, 0);
8878 fd_write_all(pipe_fds[1], "0\n", 2);
8885 #endif /* G_OS_UNIX */
8889 static gint compose_exec_ext_editor_real(const gchar *file)
8896 cm_return_val_if_fail(file != NULL, -1);
8898 if ((pid = fork()) < 0) {
8903 if (pid != 0) return pid;
8905 /* grandchild process */
8907 if (setpgid(0, getppid()))
8910 if (prefs_common_get_ext_editor_cmd() &&
8911 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8912 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8913 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8915 if (prefs_common_get_ext_editor_cmd())
8916 g_warning("External editor command-line is invalid: '%s'\n",
8917 prefs_common_get_ext_editor_cmd());
8918 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8921 cmdline = strsplit_with_quote(buf, " ", 1024);
8922 execvp(cmdline[0], cmdline);
8925 g_strfreev(cmdline);
8930 static gboolean compose_ext_editor_kill(Compose *compose)
8932 pid_t pgid = compose->exteditor_pid * -1;
8935 ret = kill(pgid, 0);
8937 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8941 msg = g_strdup_printf
8942 (_("The external editor is still working.\n"
8943 "Force terminating the process?\n"
8944 "process group id: %d"), -pgid);
8945 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8946 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8950 if (val == G_ALERTALTERNATE) {
8951 g_source_remove(compose->exteditor_tag);
8952 g_io_channel_shutdown(compose->exteditor_ch,
8954 g_io_channel_unref(compose->exteditor_ch);
8956 if (kill(pgid, SIGTERM) < 0) perror("kill");
8957 waitpid(compose->exteditor_pid, NULL, 0);
8959 g_warning("Terminated process group id: %d", -pgid);
8960 g_warning("Temporary file: %s",
8961 compose->exteditor_file);
8963 compose_set_ext_editor_sensitive(compose, TRUE);
8965 g_free(compose->exteditor_file);
8966 compose->exteditor_file = NULL;
8967 compose->exteditor_pid = -1;
8968 compose->exteditor_ch = NULL;
8969 compose->exteditor_tag = -1;
8977 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8981 Compose *compose = (Compose *)data;
8984 debug_print("Compose: input from monitoring process\n");
8986 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8988 g_io_channel_shutdown(source, FALSE, NULL);
8989 g_io_channel_unref(source);
8991 waitpid(compose->exteditor_pid, NULL, 0);
8993 if (buf[0] == '0') { /* success */
8994 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8995 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8997 gtk_text_buffer_set_text(buffer, "", -1);
8998 compose_insert_file(compose, compose->exteditor_file);
8999 compose_changed_cb(NULL, compose);
9000 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9002 if (claws_unlink(compose->exteditor_file) < 0)
9003 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9004 } else if (buf[0] == '1') { /* failed */
9005 g_warning("Couldn't exec external editor\n");
9006 if (claws_unlink(compose->exteditor_file) < 0)
9007 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9008 } else if (buf[0] == '2') {
9009 g_warning("Couldn't write to file\n");
9010 } else if (buf[0] == '3') {
9011 g_warning("Pipe read failed\n");
9014 compose_set_ext_editor_sensitive(compose, TRUE);
9016 g_free(compose->exteditor_file);
9017 compose->exteditor_file = NULL;
9018 compose->exteditor_pid = -1;
9019 compose->exteditor_ch = NULL;
9020 compose->exteditor_tag = -1;
9025 static void compose_set_ext_editor_sensitive(Compose *compose,
9028 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9029 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9030 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9031 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9032 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9033 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9034 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9036 gtk_widget_set_sensitive(compose->text, sensitive);
9037 if (compose->toolbar->send_btn)
9038 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9039 if (compose->toolbar->sendl_btn)
9040 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9041 if (compose->toolbar->draft_btn)
9042 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9043 if (compose->toolbar->insert_btn)
9044 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9045 if (compose->toolbar->sig_btn)
9046 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9047 if (compose->toolbar->exteditor_btn)
9048 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9049 if (compose->toolbar->linewrap_current_btn)
9050 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9051 if (compose->toolbar->linewrap_all_btn)
9052 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9054 #endif /* G_OS_UNIX */
9057 * compose_undo_state_changed:
9059 * Change the sensivity of the menuentries undo and redo
9061 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9062 gint redo_state, gpointer data)
9064 Compose *compose = (Compose *)data;
9066 switch (undo_state) {
9067 case UNDO_STATE_TRUE:
9068 if (!undostruct->undo_state) {
9069 undostruct->undo_state = TRUE;
9070 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9073 case UNDO_STATE_FALSE:
9074 if (undostruct->undo_state) {
9075 undostruct->undo_state = FALSE;
9076 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9079 case UNDO_STATE_UNCHANGED:
9081 case UNDO_STATE_REFRESH:
9082 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9085 g_warning("Undo state not recognized");
9089 switch (redo_state) {
9090 case UNDO_STATE_TRUE:
9091 if (!undostruct->redo_state) {
9092 undostruct->redo_state = TRUE;
9093 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9096 case UNDO_STATE_FALSE:
9097 if (undostruct->redo_state) {
9098 undostruct->redo_state = FALSE;
9099 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9102 case UNDO_STATE_UNCHANGED:
9104 case UNDO_STATE_REFRESH:
9105 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9108 g_warning("Redo state not recognized");
9113 /* callback functions */
9115 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9116 * includes "non-client" (windows-izm) in calculation, so this calculation
9117 * may not be accurate.
9119 #if !GTK_CHECK_VERSION(2,24,0)
9120 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9121 GtkAllocation *allocation,
9122 GtkSHRuler *shruler)
9124 if (prefs_common.show_ruler) {
9125 gint char_width = 0, char_height = 0;
9126 gint line_width_in_chars;
9128 gtkut_get_font_size(GTK_WIDGET(widget),
9129 &char_width, &char_height);
9130 line_width_in_chars =
9131 (allocation->width - allocation->x) / char_width;
9133 /* got the maximum */
9134 gtk_ruler_set_range(GTK_RULER(shruler),
9135 0.0, line_width_in_chars, 0,
9136 /*line_width_in_chars*/ char_width);
9145 ComposePrefType type;
9146 gboolean entry_marked;
9149 static void account_activated(GtkComboBox *optmenu, gpointer data)
9151 Compose *compose = (Compose *)data;
9154 gchar *folderidentifier;
9155 gint account_id = 0;
9158 GSList *list, *saved_list = NULL;
9159 HeaderEntryState *state;
9160 GtkRcStyle *style = NULL;
9161 static GdkColor yellow;
9162 static gboolean color_set = FALSE;
9164 /* Get ID of active account in the combo box */
9165 menu = gtk_combo_box_get_model(optmenu);
9166 gtk_combo_box_get_active_iter(optmenu, &iter);
9167 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9169 ac = account_find_from_id(account_id);
9170 cm_return_if_fail(ac != NULL);
9172 if (ac != compose->account) {
9173 compose_select_account(compose, ac, FALSE);
9175 for (list = compose->header_list; list; list = list->next) {
9176 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9178 if (hentry->type == PREF_ACCOUNT || !list->next) {
9179 compose_destroy_headerentry(compose, hentry);
9183 state = g_malloc0(sizeof(HeaderEntryState));
9184 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9185 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9186 state->entry = gtk_editable_get_chars(
9187 GTK_EDITABLE(hentry->entry), 0, -1);
9188 state->type = hentry->type;
9191 gdk_color_parse("#f5f6be", &yellow);
9192 color_set = gdk_colormap_alloc_color(
9193 gdk_colormap_get_system(),
9194 &yellow, FALSE, TRUE);
9197 style = gtk_widget_get_modifier_style(hentry->entry);
9198 state->entry_marked = gdk_color_equal(&yellow,
9199 &style->base[GTK_STATE_NORMAL]);
9201 saved_list = g_slist_append(saved_list, state);
9202 compose_destroy_headerentry(compose, hentry);
9205 compose->header_last = NULL;
9206 g_slist_free(compose->header_list);
9207 compose->header_list = NULL;
9208 compose->header_nextrow = 1;
9209 compose_create_header_entry(compose);
9211 if (ac->set_autocc && ac->auto_cc)
9212 compose_entry_append(compose, ac->auto_cc,
9213 COMPOSE_CC, PREF_ACCOUNT);
9215 if (ac->set_autobcc && ac->auto_bcc)
9216 compose_entry_append(compose, ac->auto_bcc,
9217 COMPOSE_BCC, PREF_ACCOUNT);
9219 if (ac->set_autoreplyto && ac->auto_replyto)
9220 compose_entry_append(compose, ac->auto_replyto,
9221 COMPOSE_REPLYTO, PREF_ACCOUNT);
9223 for (list = saved_list; list; list = list->next) {
9224 state = (HeaderEntryState *) list->data;
9226 compose_add_header_entry(compose, state->header,
9227 state->entry, state->type);
9228 if (state->entry_marked)
9229 compose_entry_mark_default_to(compose, state->entry);
9231 g_free(state->header);
9232 g_free(state->entry);
9235 g_slist_free(saved_list);
9237 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9238 (ac->protocol == A_NNTP) ?
9239 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9242 /* Set message save folder */
9243 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9244 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9246 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9247 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9249 compose_set_save_to(compose, NULL);
9250 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9251 folderidentifier = folder_item_get_identifier(account_get_special_folder
9252 (compose->account, F_OUTBOX));
9253 compose_set_save_to(compose, folderidentifier);
9254 g_free(folderidentifier);
9258 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9259 GtkTreeViewColumn *column, Compose *compose)
9261 compose_attach_property(NULL, compose);
9264 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9267 Compose *compose = (Compose *)data;
9268 GtkTreeSelection *attach_selection;
9269 gint attach_nr_selected;
9271 if (!event) return FALSE;
9273 if (event->button == 3) {
9274 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9275 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9277 if (attach_nr_selected > 0)
9279 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9280 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9282 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9283 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9286 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9287 NULL, NULL, event->button, event->time);
9294 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9297 Compose *compose = (Compose *)data;
9299 if (!event) return FALSE;
9301 switch (event->keyval) {
9303 compose_attach_remove_selected(NULL, compose);
9309 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9311 toolbar_comp_set_sensitive(compose, allow);
9312 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9313 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9315 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9317 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9318 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9319 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9321 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9325 static void compose_send_cb(GtkAction *action, gpointer data)
9327 Compose *compose = (Compose *)data;
9329 if (prefs_common.work_offline &&
9330 !inc_offline_should_override(TRUE,
9331 _("Claws Mail needs network access in order "
9332 "to send this email.")))
9335 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9336 g_source_remove(compose->draft_timeout_tag);
9337 compose->draft_timeout_tag = -1;
9340 compose_send(compose);
9343 static void compose_send_later_cb(GtkAction *action, gpointer data)
9345 Compose *compose = (Compose *)data;
9349 compose_allow_user_actions(compose, FALSE);
9350 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9351 compose_allow_user_actions(compose, TRUE);
9355 compose_close(compose);
9356 } else if (val == -1) {
9357 alertpanel_error(_("Could not queue message."));
9358 } else if (val == -2) {
9359 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9360 } else if (val == -3) {
9361 if (privacy_peek_error())
9362 alertpanel_error(_("Could not queue message for sending:\n\n"
9363 "Signature failed: %s"), privacy_get_error());
9364 } else if (val == -4) {
9365 alertpanel_error(_("Could not queue message for sending:\n\n"
9366 "Charset conversion failed."));
9367 } else if (val == -5) {
9368 alertpanel_error(_("Could not queue message for sending:\n\n"
9369 "Couldn't get recipient encryption key."));
9370 } else if (val == -6) {
9373 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9376 #define DRAFTED_AT_EXIT "drafted_at_exit"
9377 static void compose_register_draft(MsgInfo *info)
9379 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9380 DRAFTED_AT_EXIT, NULL);
9381 FILE *fp = g_fopen(filepath, "ab");
9384 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9392 gboolean compose_draft (gpointer data, guint action)
9394 Compose *compose = (Compose *)data;
9398 MsgFlags flag = {0, 0};
9399 static gboolean lock = FALSE;
9400 MsgInfo *newmsginfo;
9402 gboolean target_locked = FALSE;
9403 gboolean err = FALSE;
9405 if (lock) return FALSE;
9407 if (compose->sending)
9410 draft = account_get_special_folder(compose->account, F_DRAFT);
9411 cm_return_val_if_fail(draft != NULL, FALSE);
9413 if (!g_mutex_trylock(compose->mutex)) {
9414 /* we don't want to lock the mutex once it's available,
9415 * because as the only other part of compose.c locking
9416 * it is compose_close - which means once unlocked,
9417 * the compose struct will be freed */
9418 debug_print("couldn't lock mutex, probably sending\n");
9424 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9425 G_DIR_SEPARATOR, compose);
9426 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9427 FILE_OP_ERROR(tmp, "fopen");
9431 /* chmod for security */
9432 if (change_file_mode_rw(fp, tmp) < 0) {
9433 FILE_OP_ERROR(tmp, "chmod");
9434 g_warning("can't change file mode\n");
9437 /* Save draft infos */
9438 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9439 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9441 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9442 gchar *savefolderid;
9444 savefolderid = compose_get_save_to(compose);
9445 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9446 g_free(savefolderid);
9448 if (compose->return_receipt) {
9449 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9451 if (compose->privacy_system) {
9452 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9453 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9454 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9457 /* Message-ID of message replying to */
9458 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9461 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9462 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9465 /* Message-ID of message forwarding to */
9466 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9469 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9470 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9474 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9475 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9477 /* end of headers */
9478 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9485 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9489 if (fclose(fp) == EOF) {
9493 if (compose->targetinfo) {
9494 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9495 flag.perm_flags = target_locked?MSG_LOCKED:0;
9497 flag.tmp_flags = MSG_DRAFT;
9499 folder_item_scan(draft);
9500 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9501 MsgInfo *tmpinfo = NULL;
9502 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9503 if (compose->msgid) {
9504 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9507 msgnum = tmpinfo->msgnum;
9508 procmsg_msginfo_free(tmpinfo);
9509 debug_print("got draft msgnum %d from scanning\n", msgnum);
9511 debug_print("didn't get draft msgnum after scanning\n");
9514 debug_print("got draft msgnum %d from adding\n", msgnum);
9520 if (action != COMPOSE_AUTO_SAVE) {
9521 if (action != COMPOSE_DRAFT_FOR_EXIT)
9522 alertpanel_error(_("Could not save draft."));
9525 gtkut_window_popup(compose->window);
9526 val = alertpanel_full(_("Could not save draft"),
9527 _("Could not save draft.\n"
9528 "Do you want to cancel exit or discard this email?"),
9529 _("_Cancel exit"), _("_Discard email"), NULL,
9530 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9531 if (val == G_ALERTALTERNATE) {
9533 g_mutex_unlock(compose->mutex); /* must be done before closing */
9534 compose_close(compose);
9538 g_mutex_unlock(compose->mutex); /* must be done before closing */
9547 if (compose->mode == COMPOSE_REEDIT) {
9548 compose_remove_reedit_target(compose, TRUE);
9551 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9554 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9556 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9558 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9559 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9560 procmsg_msginfo_set_flags(newmsginfo, 0,
9561 MSG_HAS_ATTACHMENT);
9563 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9564 compose_register_draft(newmsginfo);
9566 procmsg_msginfo_free(newmsginfo);
9569 folder_item_scan(draft);
9571 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9573 g_mutex_unlock(compose->mutex); /* must be done before closing */
9574 compose_close(compose);
9580 path = folder_item_fetch_msg(draft, msgnum);
9582 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9585 if (g_stat(path, &s) < 0) {
9586 FILE_OP_ERROR(path, "stat");
9592 procmsg_msginfo_free(compose->targetinfo);
9593 compose->targetinfo = procmsg_msginfo_new();
9594 compose->targetinfo->msgnum = msgnum;
9595 compose->targetinfo->size = (goffset)s.st_size;
9596 compose->targetinfo->mtime = s.st_mtime;
9597 compose->targetinfo->folder = draft;
9599 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9600 compose->mode = COMPOSE_REEDIT;
9602 if (action == COMPOSE_AUTO_SAVE) {
9603 compose->autosaved_draft = compose->targetinfo;
9605 compose->modified = FALSE;
9606 compose_set_title(compose);
9610 g_mutex_unlock(compose->mutex);
9614 void compose_clear_exit_drafts(void)
9616 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9617 DRAFTED_AT_EXIT, NULL);
9618 if (is_file_exist(filepath))
9619 claws_unlink(filepath);
9624 void compose_reopen_exit_drafts(void)
9626 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9627 DRAFTED_AT_EXIT, NULL);
9628 FILE *fp = g_fopen(filepath, "rb");
9632 while (fgets(buf, sizeof(buf), fp)) {
9633 gchar **parts = g_strsplit(buf, "\t", 2);
9634 const gchar *folder = parts[0];
9635 int msgnum = parts[1] ? atoi(parts[1]):-1;
9637 if (folder && *folder && msgnum > -1) {
9638 FolderItem *item = folder_find_item_from_identifier(folder);
9639 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9641 compose_reedit(info, FALSE);
9648 compose_clear_exit_drafts();
9651 static void compose_save_cb(GtkAction *action, gpointer data)
9653 Compose *compose = (Compose *)data;
9654 compose_draft(compose, COMPOSE_KEEP_EDITING);
9655 compose->rmode = COMPOSE_REEDIT;
9658 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9660 if (compose && file_list) {
9663 for ( tmp = file_list; tmp; tmp = tmp->next) {
9664 gchar *file = (gchar *) tmp->data;
9665 gchar *utf8_filename = conv_filename_to_utf8(file);
9666 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9667 compose_changed_cb(NULL, compose);
9672 g_free(utf8_filename);
9677 static void compose_attach_cb(GtkAction *action, gpointer data)
9679 Compose *compose = (Compose *)data;
9682 if (compose->redirect_filename != NULL)
9685 file_list = filesel_select_multiple_files_open(_("Select file"));
9688 compose_attach_from_list(compose, file_list, TRUE);
9689 g_list_free(file_list);
9693 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9695 Compose *compose = (Compose *)data;
9697 gint files_inserted = 0;
9699 file_list = filesel_select_multiple_files_open(_("Select file"));
9704 for ( tmp = file_list; tmp; tmp = tmp->next) {
9705 gchar *file = (gchar *) tmp->data;
9706 gchar *filedup = g_strdup(file);
9707 gchar *shortfile = g_path_get_basename(filedup);
9708 ComposeInsertResult res;
9709 /* insert the file if the file is short or if the user confirmed that
9710 he/she wants to insert the large file */
9711 res = compose_insert_file(compose, file);
9712 if (res == COMPOSE_INSERT_READ_ERROR) {
9713 alertpanel_error(_("File '%s' could not be read."), shortfile);
9714 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9715 alertpanel_error(_("File '%s' contained invalid characters\n"
9716 "for the current encoding, insertion may be incorrect."),
9718 } else if (res == COMPOSE_INSERT_SUCCESS)
9725 g_list_free(file_list);
9729 if (files_inserted > 0 && compose->gtkaspell &&
9730 compose->gtkaspell->check_while_typing)
9731 gtkaspell_highlight_all(compose->gtkaspell);
9735 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9737 Compose *compose = (Compose *)data;
9739 compose_insert_sig(compose, FALSE);
9742 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9746 Compose *compose = (Compose *)data;
9748 gtkut_widget_get_uposition(widget, &x, &y);
9749 if (!compose->batch) {
9750 prefs_common.compose_x = x;
9751 prefs_common.compose_y = y;
9753 if (compose->sending || compose->updating)
9755 compose_close_cb(NULL, compose);
9759 void compose_close_toolbar(Compose *compose)
9761 compose_close_cb(NULL, compose);
9764 static void compose_close_cb(GtkAction *action, gpointer data)
9766 Compose *compose = (Compose *)data;
9770 if (compose->exteditor_tag != -1) {
9771 if (!compose_ext_editor_kill(compose))
9776 if (compose->modified) {
9777 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9778 if (!g_mutex_trylock(compose->mutex)) {
9779 /* we don't want to lock the mutex once it's available,
9780 * because as the only other part of compose.c locking
9781 * it is compose_close - which means once unlocked,
9782 * the compose struct will be freed */
9783 debug_print("couldn't lock mutex, probably sending\n");
9787 val = alertpanel(_("Discard message"),
9788 _("This message has been modified. Discard it?"),
9789 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9791 val = alertpanel(_("Save changes"),
9792 _("This message has been modified. Save the latest changes?"),
9793 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9795 g_mutex_unlock(compose->mutex);
9797 case G_ALERTDEFAULT:
9798 if (prefs_common.autosave && !reedit)
9799 compose_remove_draft(compose);
9801 case G_ALERTALTERNATE:
9802 compose_draft(data, COMPOSE_QUIT_EDITING);
9809 compose_close(compose);
9812 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9814 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9815 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9816 Compose *compose = (Compose *) data;
9819 compose->out_encoding = (CharSet)value;
9822 static void compose_address_cb(GtkAction *action, gpointer data)
9824 Compose *compose = (Compose *)data;
9826 addressbook_open(compose);
9829 static void about_show_cb(GtkAction *action, gpointer data)
9834 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9836 Compose *compose = (Compose *)data;
9841 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9842 cm_return_if_fail(tmpl != NULL);
9844 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9846 val = alertpanel(_("Apply template"), msg,
9847 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9850 if (val == G_ALERTDEFAULT)
9851 compose_template_apply(compose, tmpl, TRUE);
9852 else if (val == G_ALERTALTERNATE)
9853 compose_template_apply(compose, tmpl, FALSE);
9856 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9858 Compose *compose = (Compose *)data;
9860 compose_exec_ext_editor(compose);
9863 static void compose_undo_cb(GtkAction *action, gpointer data)
9865 Compose *compose = (Compose *)data;
9866 gboolean prev_autowrap = compose->autowrap;
9868 compose->autowrap = FALSE;
9869 undo_undo(compose->undostruct);
9870 compose->autowrap = prev_autowrap;
9873 static void compose_redo_cb(GtkAction *action, gpointer data)
9875 Compose *compose = (Compose *)data;
9876 gboolean prev_autowrap = compose->autowrap;
9878 compose->autowrap = FALSE;
9879 undo_redo(compose->undostruct);
9880 compose->autowrap = prev_autowrap;
9883 static void entry_cut_clipboard(GtkWidget *entry)
9885 if (GTK_IS_EDITABLE(entry))
9886 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9887 else if (GTK_IS_TEXT_VIEW(entry))
9888 gtk_text_buffer_cut_clipboard(
9889 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9890 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9894 static void entry_copy_clipboard(GtkWidget *entry)
9896 if (GTK_IS_EDITABLE(entry))
9897 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9898 else if (GTK_IS_TEXT_VIEW(entry))
9899 gtk_text_buffer_copy_clipboard(
9900 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9901 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9904 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9905 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9907 if (GTK_IS_TEXT_VIEW(entry)) {
9908 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9909 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9910 GtkTextIter start_iter, end_iter;
9912 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9914 if (contents == NULL)
9917 /* we shouldn't delete the selection when middle-click-pasting, or we
9918 * can't mid-click-paste our own selection */
9919 if (clip != GDK_SELECTION_PRIMARY) {
9920 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9921 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9924 if (insert_place == NULL) {
9925 /* if insert_place isn't specified, insert at the cursor.
9926 * used for Ctrl-V pasting */
9927 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9928 start = gtk_text_iter_get_offset(&start_iter);
9929 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9931 /* if insert_place is specified, paste here.
9932 * used for mid-click-pasting */
9933 start = gtk_text_iter_get_offset(insert_place);
9934 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9935 if (prefs_common.primary_paste_unselects)
9936 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9940 /* paste unwrapped: mark the paste so it's not wrapped later */
9941 end = start + strlen(contents);
9942 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9943 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9944 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9945 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9946 /* rewrap paragraph now (after a mid-click-paste) */
9947 mark_start = gtk_text_buffer_get_insert(buffer);
9948 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9949 gtk_text_iter_backward_char(&start_iter);
9950 compose_beautify_paragraph(compose, &start_iter, TRUE);
9952 } else if (GTK_IS_EDITABLE(entry))
9953 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9955 compose->modified = TRUE;
9958 static void entry_allsel(GtkWidget *entry)
9960 if (GTK_IS_EDITABLE(entry))
9961 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9962 else if (GTK_IS_TEXT_VIEW(entry)) {
9963 GtkTextIter startiter, enditer;
9964 GtkTextBuffer *textbuf;
9966 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9967 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9968 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9970 gtk_text_buffer_move_mark_by_name(textbuf,
9971 "selection_bound", &startiter);
9972 gtk_text_buffer_move_mark_by_name(textbuf,
9973 "insert", &enditer);
9977 static void compose_cut_cb(GtkAction *action, gpointer data)
9979 Compose *compose = (Compose *)data;
9980 if (compose->focused_editable
9981 #ifndef GENERIC_UMPC
9982 && gtkut_widget_has_focus(compose->focused_editable)
9985 entry_cut_clipboard(compose->focused_editable);
9988 static void compose_copy_cb(GtkAction *action, gpointer data)
9990 Compose *compose = (Compose *)data;
9991 if (compose->focused_editable
9992 #ifndef GENERIC_UMPC
9993 && gtkut_widget_has_focus(compose->focused_editable)
9996 entry_copy_clipboard(compose->focused_editable);
9999 static void compose_paste_cb(GtkAction *action, gpointer data)
10001 Compose *compose = (Compose *)data;
10002 gint prev_autowrap;
10003 GtkTextBuffer *buffer;
10005 if (compose->focused_editable &&
10006 gtkut_widget_has_focus(compose->focused_editable))
10007 entry_paste_clipboard(compose, compose->focused_editable,
10008 prefs_common.linewrap_pastes,
10009 GDK_SELECTION_CLIPBOARD, NULL);
10013 if (gtkut_widget_has_focus(compose->text) &&
10014 compose->gtkaspell &&
10015 compose->gtkaspell->check_while_typing)
10016 gtkaspell_highlight_all(compose->gtkaspell);
10020 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10022 Compose *compose = (Compose *)data;
10023 gint wrap_quote = prefs_common.linewrap_quote;
10024 if (compose->focused_editable
10025 #ifndef GENERIC_UMPC
10026 && gtkut_widget_has_focus(compose->focused_editable)
10029 /* let text_insert() (called directly or at a later time
10030 * after the gtk_editable_paste_clipboard) know that
10031 * text is to be inserted as a quotation. implemented
10032 * by using a simple refcount... */
10033 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10034 G_OBJECT(compose->focused_editable),
10035 "paste_as_quotation"));
10036 g_object_set_data(G_OBJECT(compose->focused_editable),
10037 "paste_as_quotation",
10038 GINT_TO_POINTER(paste_as_quotation + 1));
10039 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10040 entry_paste_clipboard(compose, compose->focused_editable,
10041 prefs_common.linewrap_pastes,
10042 GDK_SELECTION_CLIPBOARD, NULL);
10043 prefs_common.linewrap_quote = wrap_quote;
10047 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10049 Compose *compose = (Compose *)data;
10050 gint prev_autowrap;
10051 GtkTextBuffer *buffer;
10053 if (compose->focused_editable
10054 #ifndef GENERIC_UMPC
10055 && gtkut_widget_has_focus(compose->focused_editable)
10058 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10059 GDK_SELECTION_CLIPBOARD, NULL);
10063 if (gtkut_widget_has_focus(compose->text) &&
10064 compose->gtkaspell &&
10065 compose->gtkaspell->check_while_typing)
10066 gtkaspell_highlight_all(compose->gtkaspell);
10070 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10072 Compose *compose = (Compose *)data;
10073 gint prev_autowrap;
10074 GtkTextBuffer *buffer;
10076 if (compose->focused_editable
10077 #ifndef GENERIC_UMPC
10078 && gtkut_widget_has_focus(compose->focused_editable)
10081 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10082 GDK_SELECTION_CLIPBOARD, NULL);
10086 if (gtkut_widget_has_focus(compose->text) &&
10087 compose->gtkaspell &&
10088 compose->gtkaspell->check_while_typing)
10089 gtkaspell_highlight_all(compose->gtkaspell);
10093 static void compose_allsel_cb(GtkAction *action, gpointer data)
10095 Compose *compose = (Compose *)data;
10096 if (compose->focused_editable
10097 #ifndef GENERIC_UMPC
10098 && gtkut_widget_has_focus(compose->focused_editable)
10101 entry_allsel(compose->focused_editable);
10104 static void textview_move_beginning_of_line (GtkTextView *text)
10106 GtkTextBuffer *buffer;
10110 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10112 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10113 mark = gtk_text_buffer_get_insert(buffer);
10114 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10115 gtk_text_iter_set_line_offset(&ins, 0);
10116 gtk_text_buffer_place_cursor(buffer, &ins);
10119 static void textview_move_forward_character (GtkTextView *text)
10121 GtkTextBuffer *buffer;
10125 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10127 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10128 mark = gtk_text_buffer_get_insert(buffer);
10129 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10130 if (gtk_text_iter_forward_cursor_position(&ins))
10131 gtk_text_buffer_place_cursor(buffer, &ins);
10134 static void textview_move_backward_character (GtkTextView *text)
10136 GtkTextBuffer *buffer;
10140 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10142 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10143 mark = gtk_text_buffer_get_insert(buffer);
10144 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10145 if (gtk_text_iter_backward_cursor_position(&ins))
10146 gtk_text_buffer_place_cursor(buffer, &ins);
10149 static void textview_move_forward_word (GtkTextView *text)
10151 GtkTextBuffer *buffer;
10156 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10158 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10159 mark = gtk_text_buffer_get_insert(buffer);
10160 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10161 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10162 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10163 gtk_text_iter_backward_word_start(&ins);
10164 gtk_text_buffer_place_cursor(buffer, &ins);
10168 static void textview_move_backward_word (GtkTextView *text)
10170 GtkTextBuffer *buffer;
10175 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10177 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10178 mark = gtk_text_buffer_get_insert(buffer);
10179 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10180 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10181 if (gtk_text_iter_backward_word_starts(&ins, 1))
10182 gtk_text_buffer_place_cursor(buffer, &ins);
10185 static void textview_move_end_of_line (GtkTextView *text)
10187 GtkTextBuffer *buffer;
10191 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10193 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10194 mark = gtk_text_buffer_get_insert(buffer);
10195 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10196 if (gtk_text_iter_forward_to_line_end(&ins))
10197 gtk_text_buffer_place_cursor(buffer, &ins);
10200 static void textview_move_next_line (GtkTextView *text)
10202 GtkTextBuffer *buffer;
10207 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10209 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10210 mark = gtk_text_buffer_get_insert(buffer);
10211 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10212 offset = gtk_text_iter_get_line_offset(&ins);
10213 if (gtk_text_iter_forward_line(&ins)) {
10214 gtk_text_iter_set_line_offset(&ins, offset);
10215 gtk_text_buffer_place_cursor(buffer, &ins);
10219 static void textview_move_previous_line (GtkTextView *text)
10221 GtkTextBuffer *buffer;
10226 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10228 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10229 mark = gtk_text_buffer_get_insert(buffer);
10230 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10231 offset = gtk_text_iter_get_line_offset(&ins);
10232 if (gtk_text_iter_backward_line(&ins)) {
10233 gtk_text_iter_set_line_offset(&ins, offset);
10234 gtk_text_buffer_place_cursor(buffer, &ins);
10238 static void textview_delete_forward_character (GtkTextView *text)
10240 GtkTextBuffer *buffer;
10242 GtkTextIter ins, end_iter;
10244 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10246 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10247 mark = gtk_text_buffer_get_insert(buffer);
10248 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10250 if (gtk_text_iter_forward_char(&end_iter)) {
10251 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10255 static void textview_delete_backward_character (GtkTextView *text)
10257 GtkTextBuffer *buffer;
10259 GtkTextIter ins, end_iter;
10261 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10263 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10264 mark = gtk_text_buffer_get_insert(buffer);
10265 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10267 if (gtk_text_iter_backward_char(&end_iter)) {
10268 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10272 static void textview_delete_forward_word (GtkTextView *text)
10274 GtkTextBuffer *buffer;
10276 GtkTextIter ins, end_iter;
10278 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10280 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10281 mark = gtk_text_buffer_get_insert(buffer);
10282 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10284 if (gtk_text_iter_forward_word_end(&end_iter)) {
10285 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10289 static void textview_delete_backward_word (GtkTextView *text)
10291 GtkTextBuffer *buffer;
10293 GtkTextIter ins, end_iter;
10295 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10297 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10298 mark = gtk_text_buffer_get_insert(buffer);
10299 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10301 if (gtk_text_iter_backward_word_start(&end_iter)) {
10302 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10306 static void textview_delete_line (GtkTextView *text)
10308 GtkTextBuffer *buffer;
10310 GtkTextIter ins, start_iter, end_iter;
10312 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10314 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10315 mark = gtk_text_buffer_get_insert(buffer);
10316 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10319 gtk_text_iter_set_line_offset(&start_iter, 0);
10322 if (gtk_text_iter_ends_line(&end_iter)){
10323 if (!gtk_text_iter_forward_char(&end_iter))
10324 gtk_text_iter_backward_char(&start_iter);
10327 gtk_text_iter_forward_to_line_end(&end_iter);
10328 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10331 static void textview_delete_to_line_end (GtkTextView *text)
10333 GtkTextBuffer *buffer;
10335 GtkTextIter ins, end_iter;
10337 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10339 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10340 mark = gtk_text_buffer_get_insert(buffer);
10341 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10343 if (gtk_text_iter_ends_line(&end_iter))
10344 gtk_text_iter_forward_char(&end_iter);
10346 gtk_text_iter_forward_to_line_end(&end_iter);
10347 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10350 #define DO_ACTION(name, act) { \
10351 if(!strcmp(name, a_name)) { \
10355 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10357 const gchar *a_name = gtk_action_get_name(action);
10358 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10359 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10360 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10361 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10362 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10363 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10364 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10365 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10366 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10367 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10368 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10369 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10370 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10371 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10375 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10377 Compose *compose = (Compose *)data;
10378 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10379 ComposeCallAdvancedAction action = -1;
10381 action = compose_call_advanced_action_from_path(gaction);
10384 void (*do_action) (GtkTextView *text);
10385 } action_table[] = {
10386 {textview_move_beginning_of_line},
10387 {textview_move_forward_character},
10388 {textview_move_backward_character},
10389 {textview_move_forward_word},
10390 {textview_move_backward_word},
10391 {textview_move_end_of_line},
10392 {textview_move_next_line},
10393 {textview_move_previous_line},
10394 {textview_delete_forward_character},
10395 {textview_delete_backward_character},
10396 {textview_delete_forward_word},
10397 {textview_delete_backward_word},
10398 {textview_delete_line},
10399 {textview_delete_to_line_end}
10402 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10404 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10405 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10406 if (action_table[action].do_action)
10407 action_table[action].do_action(text);
10409 g_warning("Not implemented yet.");
10413 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10417 if (GTK_IS_EDITABLE(widget)) {
10418 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10419 gtk_editable_set_position(GTK_EDITABLE(widget),
10422 if (widget->parent && widget->parent->parent
10423 && widget->parent->parent->parent) {
10424 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10425 gint y = widget->allocation.y;
10426 gint height = widget->allocation.height;
10427 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10428 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10430 if (y < (int)shown->value) {
10431 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10433 if (y + height > (int)shown->value + (int)shown->page_size) {
10434 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10435 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10436 y + height - (int)shown->page_size - 1);
10438 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10439 (int)shown->upper - (int)shown->page_size - 1);
10446 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10447 compose->focused_editable = widget;
10449 #ifdef GENERIC_UMPC
10450 if (GTK_IS_TEXT_VIEW(widget)
10451 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10452 g_object_ref(compose->notebook);
10453 g_object_ref(compose->edit_vbox);
10454 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10455 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10456 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10457 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10458 g_object_unref(compose->notebook);
10459 g_object_unref(compose->edit_vbox);
10460 g_signal_handlers_block_by_func(G_OBJECT(widget),
10461 G_CALLBACK(compose_grab_focus_cb),
10463 gtk_widget_grab_focus(widget);
10464 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10465 G_CALLBACK(compose_grab_focus_cb),
10467 } else if (!GTK_IS_TEXT_VIEW(widget)
10468 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10469 g_object_ref(compose->notebook);
10470 g_object_ref(compose->edit_vbox);
10471 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10472 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10473 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10474 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10475 g_object_unref(compose->notebook);
10476 g_object_unref(compose->edit_vbox);
10477 g_signal_handlers_block_by_func(G_OBJECT(widget),
10478 G_CALLBACK(compose_grab_focus_cb),
10480 gtk_widget_grab_focus(widget);
10481 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10482 G_CALLBACK(compose_grab_focus_cb),
10488 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10490 compose->modified = TRUE;
10491 // compose_beautify_paragraph(compose, NULL, TRUE);
10492 #ifndef GENERIC_UMPC
10493 compose_set_title(compose);
10497 static void compose_wrap_cb(GtkAction *action, gpointer data)
10499 Compose *compose = (Compose *)data;
10500 compose_beautify_paragraph(compose, NULL, TRUE);
10503 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10505 Compose *compose = (Compose *)data;
10506 compose_wrap_all_full(compose, TRUE);
10509 static void compose_find_cb(GtkAction *action, gpointer data)
10511 Compose *compose = (Compose *)data;
10513 message_search_compose(compose);
10516 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10519 Compose *compose = (Compose *)data;
10520 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10521 if (compose->autowrap)
10522 compose_wrap_all_full(compose, TRUE);
10523 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10526 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10529 Compose *compose = (Compose *)data;
10530 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10533 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10535 Compose *compose = (Compose *)data;
10537 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10540 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10542 Compose *compose = (Compose *)data;
10544 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10547 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10549 g_free(compose->privacy_system);
10551 compose->privacy_system = g_strdup(account->default_privacy_system);
10552 compose_update_privacy_system_menu_item(compose, warn);
10555 #if !GTK_CHECK_VERSION(2,24,0)
10556 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10558 Compose *compose = (Compose *)data;
10560 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10561 gtk_widget_show(compose->ruler_hbox);
10562 prefs_common.show_ruler = TRUE;
10564 gtk_widget_hide(compose->ruler_hbox);
10565 gtk_widget_queue_resize(compose->edit_vbox);
10566 prefs_common.show_ruler = FALSE;
10570 static void compose_attach_drag_received_cb (GtkWidget *widget,
10571 GdkDragContext *context,
10574 GtkSelectionData *data,
10577 gpointer user_data)
10579 Compose *compose = (Compose *)user_data;
10582 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10584 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10586 ) && gtk_drag_get_source_widget(context) !=
10587 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10588 list = uri_list_extract_filenames((const gchar *)data->data);
10589 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10590 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10591 compose_attach_append
10592 (compose, (const gchar *)tmp->data,
10593 utf8_filename, NULL, NULL);
10594 g_free(utf8_filename);
10596 if (list) compose_changed_cb(NULL, compose);
10597 list_free_strings(list);
10599 } else if (gtk_drag_get_source_widget(context)
10600 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10601 /* comes from our summaryview */
10602 SummaryView * summaryview = NULL;
10603 GSList * list = NULL, *cur = NULL;
10605 if (mainwindow_get_mainwindow())
10606 summaryview = mainwindow_get_mainwindow()->summaryview;
10609 list = summary_get_selected_msg_list(summaryview);
10611 for (cur = list; cur; cur = cur->next) {
10612 MsgInfo *msginfo = (MsgInfo *)cur->data;
10613 gchar *file = NULL;
10615 file = procmsg_get_message_file_full(msginfo,
10618 compose_attach_append(compose, (const gchar *)file,
10619 (const gchar *)file, "message/rfc822", NULL);
10623 g_slist_free(list);
10627 static gboolean compose_drag_drop(GtkWidget *widget,
10628 GdkDragContext *drag_context,
10630 guint time, gpointer user_data)
10632 /* not handling this signal makes compose_insert_drag_received_cb
10637 static void compose_insert_drag_received_cb (GtkWidget *widget,
10638 GdkDragContext *drag_context,
10641 GtkSelectionData *data,
10644 gpointer user_data)
10646 Compose *compose = (Compose *)user_data;
10649 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10652 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10654 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10656 AlertValue val = G_ALERTDEFAULT;
10658 list = uri_list_extract_filenames((const gchar *)data->data);
10659 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10660 /* Assume a list of no files, and data has ://, is a remote link */
10661 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10662 gchar *tmpfile = get_tmp_file();
10663 str_write_to_file(tmpdata, tmpfile);
10665 compose_insert_file(compose, tmpfile);
10666 claws_unlink(tmpfile);
10668 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10669 compose_beautify_paragraph(compose, NULL, TRUE);
10672 switch (prefs_common.compose_dnd_mode) {
10673 case COMPOSE_DND_ASK:
10674 val = alertpanel_full(_("Insert or attach?"),
10675 _("Do you want to insert the contents of the file(s) "
10676 "into the message body, or attach it to the email?"),
10677 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10678 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10680 case COMPOSE_DND_INSERT:
10681 val = G_ALERTALTERNATE;
10683 case COMPOSE_DND_ATTACH:
10684 val = G_ALERTOTHER;
10687 /* unexpected case */
10688 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10691 if (val & G_ALERTDISABLE) {
10692 val &= ~G_ALERTDISABLE;
10693 /* remember what action to perform by default, only if we don't click Cancel */
10694 if (val == G_ALERTALTERNATE)
10695 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10696 else if (val == G_ALERTOTHER)
10697 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10700 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10701 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10702 list_free_strings(list);
10705 } else if (val == G_ALERTOTHER) {
10706 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10707 list_free_strings(list);
10712 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10713 compose_insert_file(compose, (const gchar *)tmp->data);
10715 list_free_strings(list);
10717 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10722 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10725 static void compose_header_drag_received_cb (GtkWidget *widget,
10726 GdkDragContext *drag_context,
10729 GtkSelectionData *data,
10732 gpointer user_data)
10734 GtkEditable *entry = (GtkEditable *)user_data;
10735 gchar *email = (gchar *)data->data;
10737 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10740 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10741 gchar *decoded=g_new(gchar, strlen(email));
10744 email += strlen("mailto:");
10745 decode_uri(decoded, email); /* will fit */
10746 gtk_editable_delete_text(entry, 0, -1);
10747 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10748 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10752 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10755 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10757 Compose *compose = (Compose *)data;
10759 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10760 compose->return_receipt = TRUE;
10762 compose->return_receipt = FALSE;
10765 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10767 Compose *compose = (Compose *)data;
10769 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10770 compose->remove_references = TRUE;
10772 compose->remove_references = FALSE;
10775 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10776 ComposeHeaderEntry *headerentry)
10778 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10782 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10783 GdkEventKey *event,
10784 ComposeHeaderEntry *headerentry)
10786 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10787 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10788 !(event->state & GDK_MODIFIER_MASK) &&
10789 (event->keyval == GDK_BackSpace) &&
10790 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10791 gtk_container_remove
10792 (GTK_CONTAINER(headerentry->compose->header_table),
10793 headerentry->combo);
10794 gtk_container_remove
10795 (GTK_CONTAINER(headerentry->compose->header_table),
10796 headerentry->entry);
10797 headerentry->compose->header_list =
10798 g_slist_remove(headerentry->compose->header_list,
10800 g_free(headerentry);
10801 } else if (event->keyval == GDK_Tab) {
10802 if (headerentry->compose->header_last == headerentry) {
10803 /* Override default next focus, and give it to subject_entry
10804 * instead of notebook tabs
10806 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10807 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10814 static gboolean scroll_postpone(gpointer data)
10816 Compose *compose = (Compose *)data;
10818 cm_return_val_if_fail(!compose->batch, FALSE);
10820 GTK_EVENTS_FLUSH();
10821 compose_show_first_last_header(compose, FALSE);
10825 static void compose_headerentry_changed_cb(GtkWidget *entry,
10826 ComposeHeaderEntry *headerentry)
10828 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10829 compose_create_header_entry(headerentry->compose);
10830 g_signal_handlers_disconnect_matched
10831 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10832 0, 0, NULL, NULL, headerentry);
10834 if (!headerentry->compose->batch)
10835 g_timeout_add(0, scroll_postpone, headerentry->compose);
10839 static gboolean compose_defer_auto_save_draft(Compose *compose)
10841 compose->draft_timeout_tag = -1;
10842 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10846 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10848 GtkAdjustment *vadj;
10850 cm_return_if_fail(compose);
10851 cm_return_if_fail(!compose->batch);
10852 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10853 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10854 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10855 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10856 gtk_adjustment_changed(vadj);
10859 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10860 const gchar *text, gint len, Compose *compose)
10862 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10863 (G_OBJECT(compose->text), "paste_as_quotation"));
10866 cm_return_if_fail(text != NULL);
10868 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10869 G_CALLBACK(text_inserted),
10871 if (paste_as_quotation) {
10873 const gchar *qmark;
10875 GtkTextIter start_iter;
10878 len = strlen(text);
10880 new_text = g_strndup(text, len);
10882 qmark = compose_quote_char_from_context(compose);
10884 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10885 gtk_text_buffer_place_cursor(buffer, iter);
10887 pos = gtk_text_iter_get_offset(iter);
10889 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10890 _("Quote format error at line %d."));
10891 quote_fmt_reset_vartable();
10893 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10894 GINT_TO_POINTER(paste_as_quotation - 1));
10896 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10897 gtk_text_buffer_place_cursor(buffer, iter);
10898 gtk_text_buffer_delete_mark(buffer, mark);
10900 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10901 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10902 compose_beautify_paragraph(compose, &start_iter, FALSE);
10903 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10904 gtk_text_buffer_delete_mark(buffer, mark);
10906 if (strcmp(text, "\n") || compose->automatic_break
10907 || gtk_text_iter_starts_line(iter)) {
10908 GtkTextIter before_ins;
10909 gtk_text_buffer_insert(buffer, iter, text, len);
10910 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10911 before_ins = *iter;
10912 gtk_text_iter_backward_chars(&before_ins, len);
10913 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10916 /* check if the preceding is just whitespace or quote */
10917 GtkTextIter start_line;
10918 gchar *tmp = NULL, *quote = NULL;
10919 gint quote_len = 0, is_normal = 0;
10920 start_line = *iter;
10921 gtk_text_iter_set_line_offset(&start_line, 0);
10922 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10925 if (*tmp == '\0') {
10928 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10936 gtk_text_buffer_insert(buffer, iter, text, len);
10938 gtk_text_buffer_insert_with_tags_by_name(buffer,
10939 iter, text, len, "no_join", NULL);
10944 if (!paste_as_quotation) {
10945 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10946 compose_beautify_paragraph(compose, iter, FALSE);
10947 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10948 gtk_text_buffer_delete_mark(buffer, mark);
10951 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10952 G_CALLBACK(text_inserted),
10954 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10956 if (prefs_common.autosave &&
10957 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10958 compose->draft_timeout_tag != -2 /* disabled while loading */)
10959 compose->draft_timeout_tag = g_timeout_add
10960 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
10964 static void compose_check_all(GtkAction *action, gpointer data)
10966 Compose *compose = (Compose *)data;
10967 if (!compose->gtkaspell)
10970 if (gtkut_widget_has_focus(compose->subject_entry))
10971 claws_spell_entry_check_all(
10972 CLAWS_SPELL_ENTRY(compose->subject_entry));
10974 gtkaspell_check_all(compose->gtkaspell);
10977 static void compose_highlight_all(GtkAction *action, gpointer data)
10979 Compose *compose = (Compose *)data;
10980 if (compose->gtkaspell) {
10981 claws_spell_entry_recheck_all(
10982 CLAWS_SPELL_ENTRY(compose->subject_entry));
10983 gtkaspell_highlight_all(compose->gtkaspell);
10987 static void compose_check_backwards(GtkAction *action, gpointer data)
10989 Compose *compose = (Compose *)data;
10990 if (!compose->gtkaspell) {
10991 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10995 if (gtkut_widget_has_focus(compose->subject_entry))
10996 claws_spell_entry_check_backwards(
10997 CLAWS_SPELL_ENTRY(compose->subject_entry));
10999 gtkaspell_check_backwards(compose->gtkaspell);
11002 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11004 Compose *compose = (Compose *)data;
11005 if (!compose->gtkaspell) {
11006 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11010 if (gtkut_widget_has_focus(compose->subject_entry))
11011 claws_spell_entry_check_forwards_go(
11012 CLAWS_SPELL_ENTRY(compose->subject_entry));
11014 gtkaspell_check_forwards_go(compose->gtkaspell);
11019 *\brief Guess originating forward account from MsgInfo and several
11020 * "common preference" settings. Return NULL if no guess.
11022 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11024 PrefsAccount *account = NULL;
11026 cm_return_val_if_fail(msginfo, NULL);
11027 cm_return_val_if_fail(msginfo->folder, NULL);
11028 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11030 if (msginfo->folder->prefs->enable_default_account)
11031 account = account_find_from_id(msginfo->folder->prefs->default_account);
11034 account = msginfo->folder->folder->account;
11036 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11038 Xstrdup_a(to, msginfo->to, return NULL);
11039 extract_address(to);
11040 account = account_find_from_address(to, FALSE);
11043 if (!account && prefs_common.forward_account_autosel) {
11044 gchar cc[BUFFSIZE];
11045 if (!procheader_get_header_from_msginfo
11046 (msginfo, cc,sizeof cc , "Cc:")) {
11047 gchar *buf = cc + strlen("Cc:");
11048 extract_address(buf);
11049 account = account_find_from_address(buf, FALSE);
11053 if (!account && prefs_common.forward_account_autosel) {
11054 gchar deliveredto[BUFFSIZE];
11055 if (!procheader_get_header_from_msginfo
11056 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11057 gchar *buf = deliveredto + strlen("Delivered-To:");
11058 extract_address(buf);
11059 account = account_find_from_address(buf, FALSE);
11066 gboolean compose_close(Compose *compose)
11070 if (!g_mutex_trylock(compose->mutex)) {
11071 /* we have to wait for the (possibly deferred by auto-save)
11072 * drafting to be done, before destroying the compose under
11074 debug_print("waiting for drafting to finish...\n");
11075 compose_allow_user_actions(compose, FALSE);
11076 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11079 cm_return_val_if_fail(compose, FALSE);
11080 gtkut_widget_get_uposition(compose->window, &x, &y);
11081 if (!compose->batch) {
11082 prefs_common.compose_x = x;
11083 prefs_common.compose_y = y;
11085 g_mutex_unlock(compose->mutex);
11086 compose_destroy(compose);
11091 * Add entry field for each address in list.
11092 * \param compose E-Mail composition object.
11093 * \param listAddress List of (formatted) E-Mail addresses.
11095 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11098 node = listAddress;
11100 addr = ( gchar * ) node->data;
11101 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11102 node = g_list_next( node );
11106 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11107 guint action, gboolean opening_multiple)
11109 gchar *body = NULL;
11110 GSList *new_msglist = NULL;
11111 MsgInfo *tmp_msginfo = NULL;
11112 gboolean originally_enc = FALSE;
11113 gboolean originally_sig = FALSE;
11114 Compose *compose = NULL;
11115 gchar *s_system = NULL;
11117 cm_return_if_fail(msgview != NULL);
11119 cm_return_if_fail(msginfo_list != NULL);
11121 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11122 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11123 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11125 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11126 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11127 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11128 orig_msginfo, mimeinfo);
11129 if (tmp_msginfo != NULL) {
11130 new_msglist = g_slist_append(NULL, tmp_msginfo);
11132 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11133 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11134 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11136 tmp_msginfo->folder = orig_msginfo->folder;
11137 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11138 if (orig_msginfo->tags) {
11139 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11140 tmp_msginfo->folder->tags_dirty = TRUE;
11146 if (!opening_multiple)
11147 body = messageview_get_selection(msgview);
11150 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11151 procmsg_msginfo_free(tmp_msginfo);
11152 g_slist_free(new_msglist);
11154 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11156 if (compose && originally_enc) {
11157 compose_force_encryption(compose, compose->account, FALSE, s_system);
11160 if (compose && originally_sig && compose->account->default_sign_reply) {
11161 compose_force_signing(compose, compose->account, s_system);
11165 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11168 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11171 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11172 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11173 GSList *cur = msginfo_list;
11174 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11175 "messages. Opening the windows "
11176 "could take some time. Do you "
11177 "want to continue?"),
11178 g_slist_length(msginfo_list));
11179 if (g_slist_length(msginfo_list) > 9
11180 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11181 != G_ALERTALTERNATE) {
11186 /* We'll open multiple compose windows */
11187 /* let the WM place the next windows */
11188 compose_force_window_origin = FALSE;
11189 for (; cur; cur = cur->next) {
11191 tmplist.data = cur->data;
11192 tmplist.next = NULL;
11193 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11195 compose_force_window_origin = TRUE;
11197 /* forwarding multiple mails as attachments is done via a
11198 * single compose window */
11199 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11203 void compose_check_for_email_account(Compose *compose)
11205 PrefsAccount *ac = NULL, *curr = NULL;
11211 if (compose->account && compose->account->protocol == A_NNTP) {
11212 ac = account_get_cur_account();
11213 if (ac->protocol == A_NNTP) {
11214 list = account_get_list();
11216 for( ; list != NULL ; list = g_list_next(list)) {
11217 curr = (PrefsAccount *) list->data;
11218 if (curr->protocol != A_NNTP) {
11224 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11229 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11230 const gchar *address)
11232 GSList *msginfo_list = NULL;
11233 gchar *body = messageview_get_selection(msgview);
11236 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11238 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11239 compose_check_for_email_account(compose);
11240 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11241 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11242 compose_reply_set_subject(compose, msginfo);
11245 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11248 void compose_set_position(Compose *compose, gint pos)
11250 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11252 gtkut_text_view_set_position(text, pos);
11255 gboolean compose_search_string(Compose *compose,
11256 const gchar *str, gboolean case_sens)
11258 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11260 return gtkut_text_view_search_string(text, str, case_sens);
11263 gboolean compose_search_string_backward(Compose *compose,
11264 const gchar *str, gboolean case_sens)
11266 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11268 return gtkut_text_view_search_string_backward(text, str, case_sens);
11271 /* allocate a msginfo structure and populate its data from a compose data structure */
11272 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11274 MsgInfo *newmsginfo;
11276 gchar buf[BUFFSIZE];
11278 cm_return_val_if_fail( compose != NULL, NULL );
11280 newmsginfo = procmsg_msginfo_new();
11283 get_rfc822_date(buf, sizeof(buf));
11284 newmsginfo->date = g_strdup(buf);
11287 if (compose->from_name) {
11288 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11289 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11293 if (compose->subject_entry)
11294 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11296 /* to, cc, reply-to, newsgroups */
11297 for (list = compose->header_list; list; list = list->next) {
11298 gchar *header = gtk_editable_get_chars(
11300 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11301 gchar *entry = gtk_editable_get_chars(
11302 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11304 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11305 if ( newmsginfo->to == NULL ) {
11306 newmsginfo->to = g_strdup(entry);
11307 } else if (entry && *entry) {
11308 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11309 g_free(newmsginfo->to);
11310 newmsginfo->to = tmp;
11313 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11314 if ( newmsginfo->cc == NULL ) {
11315 newmsginfo->cc = g_strdup(entry);
11316 } else if (entry && *entry) {
11317 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11318 g_free(newmsginfo->cc);
11319 newmsginfo->cc = tmp;
11322 if ( strcasecmp(header,
11323 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11324 if ( newmsginfo->newsgroups == NULL ) {
11325 newmsginfo->newsgroups = g_strdup(entry);
11326 } else if (entry && *entry) {
11327 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11328 g_free(newmsginfo->newsgroups);
11329 newmsginfo->newsgroups = tmp;
11337 /* other data is unset */
11343 /* update compose's dictionaries from folder dict settings */
11344 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11345 FolderItem *folder_item)
11347 cm_return_if_fail(compose != NULL);
11349 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11350 FolderItemPrefs *prefs = folder_item->prefs;
11352 if (prefs->enable_default_dictionary)
11353 gtkaspell_change_dict(compose->gtkaspell,
11354 prefs->default_dictionary, FALSE);
11355 if (folder_item->prefs->enable_default_alt_dictionary)
11356 gtkaspell_change_alt_dict(compose->gtkaspell,
11357 prefs->default_alt_dictionary);
11358 if (prefs->enable_default_dictionary
11359 || prefs->enable_default_alt_dictionary)
11360 compose_spell_menu_changed(compose);