2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static gchar *compose_get_signature_str (Compose *compose);
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static void compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
338 static void compose_add_field_list ( Compose *compose,
339 GList *listAddress );
341 /* callback functions */
343 static gboolean compose_edit_size_alloc (GtkEditable *widget,
344 GtkAllocation *allocation,
345 GtkSHRuler *shruler);
346 static void account_activated (GtkComboBox *optmenu,
348 static void attach_selected (GtkTreeView *tree_view,
349 GtkTreePath *tree_path,
350 GtkTreeViewColumn *column,
352 static gboolean attach_button_pressed (GtkWidget *widget,
353 GdkEventButton *event,
355 static gboolean attach_key_pressed (GtkWidget *widget,
358 static void compose_send_cb (GtkAction *action, gpointer data);
359 static void compose_send_later_cb (GtkAction *action, gpointer data);
361 static void compose_save_cb (GtkAction *action,
364 static void compose_attach_cb (GtkAction *action,
366 static void compose_insert_file_cb (GtkAction *action,
368 static void compose_insert_sig_cb (GtkAction *action,
371 static void compose_close_cb (GtkAction *action,
374 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
376 static void compose_address_cb (GtkAction *action,
378 static void about_show_cb (GtkAction *action,
380 static void compose_template_activate_cb(GtkWidget *widget,
383 static void compose_ext_editor_cb (GtkAction *action,
386 static gint compose_delete_cb (GtkWidget *widget,
390 static void compose_undo_cb (GtkAction *action,
392 static void compose_redo_cb (GtkAction *action,
394 static void compose_cut_cb (GtkAction *action,
396 static void compose_copy_cb (GtkAction *action,
398 static void compose_paste_cb (GtkAction *action,
400 static void compose_paste_as_quote_cb (GtkAction *action,
402 static void compose_paste_no_wrap_cb (GtkAction *action,
404 static void compose_paste_wrap_cb (GtkAction *action,
406 static void compose_allsel_cb (GtkAction *action,
409 static void compose_advanced_action_cb (GtkAction *action,
412 static void compose_grab_focus_cb (GtkWidget *widget,
415 static void compose_changed_cb (GtkTextBuffer *textbuf,
418 static void compose_wrap_cb (GtkAction *action,
420 static void compose_wrap_all_cb (GtkAction *action,
422 static void compose_find_cb (GtkAction *action,
424 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
426 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
429 static void compose_toggle_ruler_cb (GtkToggleAction *action,
431 static void compose_toggle_sign_cb (GtkToggleAction *action,
433 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
435 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
436 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
437 static void activate_privacy_system (Compose *compose,
438 PrefsAccount *account,
440 static void compose_use_signing(Compose *compose, gboolean use_signing);
441 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
442 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
444 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
446 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
447 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
448 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
450 static void compose_attach_drag_received_cb (GtkWidget *widget,
451 GdkDragContext *drag_context,
454 GtkSelectionData *data,
458 static void compose_insert_drag_received_cb (GtkWidget *widget,
459 GdkDragContext *drag_context,
462 GtkSelectionData *data,
466 static void compose_header_drag_received_cb (GtkWidget *widget,
467 GdkDragContext *drag_context,
470 GtkSelectionData *data,
475 static gboolean compose_drag_drop (GtkWidget *widget,
476 GdkDragContext *drag_context,
478 guint time, gpointer user_data);
480 static void text_inserted (GtkTextBuffer *buffer,
485 static Compose *compose_generic_reply(MsgInfo *msginfo,
486 ComposeQuoteMode quote_mode,
490 gboolean followup_and_reply_to,
493 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
494 ComposeHeaderEntry *headerentry);
495 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
497 ComposeHeaderEntry *headerentry);
499 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
501 static void compose_allow_user_actions (Compose *compose, gboolean allow);
503 static void compose_nothing_cb (GtkAction *action, gpointer data)
509 static void compose_check_all (GtkAction *action, gpointer data);
510 static void compose_highlight_all (GtkAction *action, gpointer data);
511 static void compose_check_backwards (GtkAction *action, gpointer data);
512 static void compose_check_forwards_go (GtkAction *action, gpointer data);
515 static gint compose_defer_auto_save_draft (Compose *compose);
516 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
518 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
521 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
522 FolderItem *folder_item);
524 static void compose_attach_update_label(Compose *compose);
526 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
528 static GtkActionEntry compose_popup_entries[] =
530 {"Compose", NULL, "Compose" },
531 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
532 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
533 {"Compose/---", NULL, "---", NULL, NULL, NULL },
534 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
537 static GtkActionEntry compose_entries[] =
539 {"Menu", NULL, "Menu" },
541 {"Message", NULL, N_("_Message") },
542 {"Edit", NULL, N_("_Edit") },
544 {"Spelling", NULL, N_("_Spelling") },
546 {"Options", NULL, N_("_Options") },
547 {"Tools", NULL, N_("_Tools") },
548 {"Help", NULL, N_("_Help") },
550 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
551 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
552 {"Message/---", NULL, "---" },
554 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
555 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
556 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
557 /* {"Message/---", NULL, "---" }, */
558 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
559 /* {"Message/---", NULL, "---" }, */
560 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
563 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
564 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
565 {"Edit/---", NULL, "---" },
567 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
568 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
569 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
571 {"Edit/SpecialPaste", NULL, N_("Special paste") },
572 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
573 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
574 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
576 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
578 {"Edit/Advanced", NULL, N_("A_dvanced") },
579 {"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*/
580 {"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*/
581 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
582 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
590 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
591 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
592 {"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*/
594 /* {"Edit/---", NULL, "---" }, */
595 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
597 /* {"Edit/---", NULL, "---" }, */
598 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
599 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
600 /* {"Edit/---", NULL, "---" }, */
601 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
604 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
605 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
606 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
607 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
609 {"Spelling/---", NULL, "---" },
610 {"Spelling/Options", NULL, N_("_Options") },
615 {"Options/ReplyMode", NULL, N_("Reply _mode") },
616 {"Options/---", NULL, "---" },
617 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
618 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
620 /* {"Options/---", NULL, "---" }, */
622 {"Options/Priority", NULL, N_("_Priority") },
624 {"Options/Encoding", NULL, N_("Character _encoding") },
625 {"Options/Encoding/---", NULL, "---" },
626 #define ENC_ACTION(cs_char,c_char,string) \
627 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
629 {"Options/Encoding/Western", NULL, N_("Western European") },
630 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
631 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
632 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
633 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
634 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
635 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
636 {"Options/Encoding/Korean", NULL, N_("Korean") },
637 {"Options/Encoding/Thai", NULL, N_("Thai") },
640 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
642 {"Tools/Template", NULL, N_("_Template") },
643 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
644 {"Tools/Actions", NULL, N_("Actio_ns") },
645 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
648 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
651 static GtkToggleActionEntry compose_toggle_entries[] =
653 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
654 {"Edit/AutoIndent", NULL, N_("Follow _indentation"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
655 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
656 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
657 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
658 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
659 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
662 static GtkRadioActionEntry compose_radio_rm_entries[] =
664 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
667 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
670 static GtkRadioActionEntry compose_radio_prio_entries[] =
672 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
679 static GtkRadioActionEntry compose_radio_enc_entries[] =
681 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
714 static GtkTargetEntry compose_mime_types[] =
716 {"text/uri-list", 0, 0},
717 {"UTF8_STRING", 0, 0},
721 static gboolean compose_put_existing_to_front(MsgInfo *info)
723 GList *compose_list = compose_get_compose_list();
727 for (elem = compose_list; elem != NULL && elem->data != NULL;
729 Compose *c = (Compose*)elem->data;
731 if (!c->targetinfo || !c->targetinfo->msgid ||
735 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
736 gtkut_window_popup(c->window);
744 static GdkColor quote_color1 =
745 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
746 static GdkColor quote_color2 =
747 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
748 static GdkColor quote_color3 =
749 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
751 static GdkColor quote_bgcolor1 =
752 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
753 static GdkColor quote_bgcolor2 =
754 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor quote_bgcolor3 =
756 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor signature_color = {
765 static GdkColor uri_color = {
772 static void compose_create_tags(GtkTextView *text, Compose *compose)
774 GtkTextBuffer *buffer;
775 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
781 buffer = gtk_text_view_get_buffer(text);
783 if (prefs_common.enable_color) {
784 /* grab the quote colors, converting from an int to a GdkColor */
785 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
787 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
789 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
791 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
793 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
795 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
797 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
799 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
802 signature_color = quote_color1 = quote_color2 = quote_color3 =
803 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
806 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
807 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
808 "foreground-gdk", "e_color1,
809 "paragraph-background-gdk", "e_bgcolor1,
811 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
812 "foreground-gdk", "e_color2,
813 "paragraph-background-gdk", "e_bgcolor2,
815 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
816 "foreground-gdk", "e_color3,
817 "paragraph-background-gdk", "e_bgcolor3,
820 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
821 "foreground-gdk", "e_color1,
823 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
824 "foreground-gdk", "e_color2,
826 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
827 "foreground-gdk", "e_color3,
831 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
832 "foreground-gdk", &signature_color,
835 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
836 "foreground-gdk", &uri_color,
838 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
839 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
841 color[0] = quote_color1;
842 color[1] = quote_color2;
843 color[2] = quote_color3;
844 color[3] = quote_bgcolor1;
845 color[4] = quote_bgcolor2;
846 color[5] = quote_bgcolor3;
847 color[6] = signature_color;
848 color[7] = uri_color;
849 cmap = gdk_drawable_get_colormap(compose->window->window);
850 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
852 for (i = 0; i < 8; i++) {
853 if (success[i] == FALSE) {
856 g_warning("Compose: color allocation failed.\n");
857 style = gtk_widget_get_style(GTK_WIDGET(text));
858 quote_color1 = quote_color2 = quote_color3 =
859 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
860 signature_color = uri_color = black;
865 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
866 GPtrArray *attach_files)
868 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
871 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
873 return compose_generic_new(account, mailto, item, NULL, NULL);
876 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
878 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
881 #define SCROLL_TO_CURSOR(compose) { \
882 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
883 gtk_text_view_get_buffer( \
884 GTK_TEXT_VIEW(compose->text))); \
885 gtk_text_view_scroll_mark_onscreen( \
886 GTK_TEXT_VIEW(compose->text), \
890 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
891 GPtrArray *attach_files, GList *listAddress )
894 GtkTextView *textview;
895 GtkTextBuffer *textbuf;
897 const gchar *subject_format = NULL;
898 const gchar *body_format = NULL;
899 gchar *mailto_from = NULL;
900 PrefsAccount *mailto_account = NULL;
901 MsgInfo* dummyinfo = NULL;
903 /* check if mailto defines a from */
904 if (mailto && *mailto != '\0') {
905 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
906 /* mailto defines a from, check if we can get account prefs from it,
907 if not, the account prefs will be guessed using other ways, but we'll keep
910 mailto_account = account_find_from_address(mailto_from, TRUE);
912 account = mailto_account;
915 /* if no account prefs set from mailto, set if from folder prefs (if any) */
916 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
917 account = account_find_from_id(item->prefs->default_account);
919 /* if no account prefs set, fallback to the current one */
920 if (!account) account = cur_account;
921 g_return_val_if_fail(account != NULL, NULL);
923 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
925 /* override from name if mailto asked for it */
927 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
930 /* override from name according to folder properties */
931 if (item && item->prefs &&
932 item->prefs->compose_with_format &&
933 item->prefs->compose_override_from_format &&
934 *item->prefs->compose_override_from_format != '\0') {
939 dummyinfo = compose_msginfo_new_from_compose(compose);
941 /* decode \-escape sequences in the internal representation of the quote format */
942 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
943 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
946 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
949 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
951 quote_fmt_scan_string(tmp);
954 buf = quote_fmt_get_buffer();
956 alertpanel_error(_("New message From format error."));
958 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
959 quote_fmt_reset_vartable();
964 compose->replyinfo = NULL;
965 compose->fwdinfo = NULL;
967 textview = GTK_TEXT_VIEW(compose->text);
968 textbuf = gtk_text_view_get_buffer(textview);
969 compose_create_tags(textview, compose);
971 undo_block(compose->undostruct);
973 compose_set_dictionaries_from_folder_prefs(compose, item);
976 if (account->auto_sig)
977 compose_insert_sig(compose, FALSE);
978 gtk_text_buffer_get_start_iter(textbuf, &iter);
979 gtk_text_buffer_place_cursor(textbuf, &iter);
981 if (account->protocol != A_NNTP) {
982 if (mailto && *mailto != '\0') {
983 compose_entries_set(compose, mailto, COMPOSE_TO);
985 } else if (item && item->prefs->enable_default_to) {
986 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
987 compose_entry_mark_default_to(compose, item->prefs->default_to);
989 if (item && item->ret_rcpt) {
990 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
993 if (mailto && *mailto != '\0') {
994 if (!strchr(mailto, '@'))
995 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
997 compose_entries_set(compose, mailto, COMPOSE_TO);
998 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
999 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1002 * CLAWS: just don't allow return receipt request, even if the user
1003 * may want to send an email. simple but foolproof.
1005 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1007 compose_add_field_list( compose, listAddress );
1009 if (item && item->prefs && item->prefs->compose_with_format) {
1010 subject_format = item->prefs->compose_subject_format;
1011 body_format = item->prefs->compose_body_format;
1012 } else if (account->compose_with_format) {
1013 subject_format = account->compose_subject_format;
1014 body_format = account->compose_body_format;
1015 } else if (prefs_common.compose_with_format) {
1016 subject_format = prefs_common.compose_subject_format;
1017 body_format = prefs_common.compose_body_format;
1020 if (subject_format || body_format) {
1023 && *subject_format != '\0' )
1025 gchar *subject = NULL;
1030 dummyinfo = compose_msginfo_new_from_compose(compose);
1032 /* decode \-escape sequences in the internal representation of the quote format */
1033 tmp = malloc(strlen(subject_format)+1);
1034 pref_get_unescaped_pref(tmp, subject_format);
1036 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1038 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1039 compose->gtkaspell);
1041 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1043 quote_fmt_scan_string(tmp);
1046 buf = quote_fmt_get_buffer();
1048 alertpanel_error(_("New message subject format error."));
1050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1051 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1052 quote_fmt_reset_vartable();
1059 && *body_format != '\0' )
1062 GtkTextBuffer *buffer;
1063 GtkTextIter start, end;
1067 dummyinfo = compose_msginfo_new_from_compose(compose);
1069 text = GTK_TEXT_VIEW(compose->text);
1070 buffer = gtk_text_view_get_buffer(text);
1071 gtk_text_buffer_get_start_iter(buffer, &start);
1072 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1073 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1075 compose_quote_fmt(compose, dummyinfo,
1077 NULL, tmp, FALSE, TRUE,
1078 _("New message body format error at line %d."));
1079 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1080 quote_fmt_reset_vartable();
1086 procmsg_msginfo_free( dummyinfo );
1092 for (i = 0; i < attach_files->len; i++) {
1093 file = g_ptr_array_index(attach_files, i);
1094 compose_attach_append(compose, file, file, NULL);
1098 compose_show_first_last_header(compose, TRUE);
1100 /* Set save folder */
1101 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1102 gchar *folderidentifier;
1104 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1105 folderidentifier = folder_item_get_identifier(item);
1106 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1107 g_free(folderidentifier);
1110 gtk_widget_grab_focus(compose->header_last->entry);
1112 undo_unblock(compose->undostruct);
1114 if (prefs_common.auto_exteditor)
1115 compose_exec_ext_editor(compose);
1117 compose->draft_timeout_tag = -1;
1118 SCROLL_TO_CURSOR(compose);
1120 compose->modified = FALSE;
1121 compose_set_title(compose);
1125 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1126 gboolean override_pref)
1128 gchar *privacy = NULL;
1130 g_return_if_fail(compose != NULL);
1131 g_return_if_fail(account != NULL);
1133 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1136 if (account->default_privacy_system
1137 && strlen(account->default_privacy_system)) {
1138 privacy = account->default_privacy_system;
1140 GSList *privacy_avail = privacy_get_system_ids();
1141 if (privacy_avail && g_slist_length(privacy_avail)) {
1142 privacy = (gchar *)(privacy_avail->data);
1145 if (privacy != NULL) {
1146 if (compose->privacy_system == NULL)
1147 compose->privacy_system = g_strdup(privacy);
1148 else if (*(compose->privacy_system) == '\0') {
1149 g_free(compose->privacy_system);
1150 compose->privacy_system = g_strdup(privacy);
1152 compose_update_privacy_system_menu_item(compose, FALSE);
1153 compose_use_encryption(compose, TRUE);
1157 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1159 gchar *privacy = NULL;
1161 if (account->default_privacy_system
1162 && strlen(account->default_privacy_system)) {
1163 privacy = account->default_privacy_system;
1165 GSList *privacy_avail = privacy_get_system_ids();
1166 if (privacy_avail && g_slist_length(privacy_avail)) {
1167 privacy = (gchar *)(privacy_avail->data);
1170 if (privacy != NULL) {
1171 if (compose->privacy_system == NULL)
1172 compose->privacy_system = g_strdup(privacy);
1173 compose_update_privacy_system_menu_item(compose, FALSE);
1174 compose_use_signing(compose, TRUE);
1178 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1182 Compose *compose = NULL;
1184 g_return_val_if_fail(msginfo_list != NULL, NULL);
1186 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1187 g_return_val_if_fail(msginfo != NULL, NULL);
1189 list_len = g_slist_length(msginfo_list);
1193 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1194 FALSE, prefs_common.default_reply_list, FALSE, body);
1196 case COMPOSE_REPLY_WITH_QUOTE:
1197 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1198 FALSE, prefs_common.default_reply_list, FALSE, body);
1200 case COMPOSE_REPLY_WITHOUT_QUOTE:
1201 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1202 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1204 case COMPOSE_REPLY_TO_SENDER:
1205 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1206 FALSE, FALSE, TRUE, body);
1208 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1209 compose = compose_followup_and_reply_to(msginfo,
1210 COMPOSE_QUOTE_CHECK,
1211 FALSE, FALSE, body);
1213 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1214 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1215 FALSE, FALSE, TRUE, body);
1217 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1218 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1219 FALSE, FALSE, TRUE, NULL);
1221 case COMPOSE_REPLY_TO_ALL:
1222 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1223 TRUE, FALSE, FALSE, body);
1225 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1226 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1227 TRUE, FALSE, FALSE, body);
1229 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1230 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1231 TRUE, FALSE, FALSE, NULL);
1233 case COMPOSE_REPLY_TO_LIST:
1234 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1235 FALSE, TRUE, FALSE, body);
1237 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1238 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1239 FALSE, TRUE, FALSE, body);
1241 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1242 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1243 FALSE, TRUE, FALSE, NULL);
1245 case COMPOSE_FORWARD:
1246 if (prefs_common.forward_as_attachment) {
1247 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1250 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1254 case COMPOSE_FORWARD_INLINE:
1255 /* check if we reply to more than one Message */
1256 if (list_len == 1) {
1257 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1260 /* more messages FALL THROUGH */
1261 case COMPOSE_FORWARD_AS_ATTACH:
1262 compose = compose_forward_multiple(NULL, msginfo_list);
1264 case COMPOSE_REDIRECT:
1265 compose = compose_redirect(NULL, msginfo, FALSE);
1268 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1271 if (compose == NULL) {
1272 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1276 compose->rmode = mode;
1277 switch (compose->rmode) {
1279 case COMPOSE_REPLY_WITH_QUOTE:
1280 case COMPOSE_REPLY_WITHOUT_QUOTE:
1281 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1282 debug_print("reply mode Normal\n");
1283 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1284 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1286 case COMPOSE_REPLY_TO_SENDER:
1287 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1288 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1289 debug_print("reply mode Sender\n");
1290 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1292 case COMPOSE_REPLY_TO_ALL:
1293 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1294 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1295 debug_print("reply mode All\n");
1296 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1298 case COMPOSE_REPLY_TO_LIST:
1299 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1300 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1301 debug_print("reply mode List\n");
1302 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1310 static Compose *compose_reply(MsgInfo *msginfo,
1311 ComposeQuoteMode quote_mode,
1317 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1318 to_sender, FALSE, body);
1321 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1322 ComposeQuoteMode quote_mode,
1327 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1328 to_sender, TRUE, body);
1331 static void compose_extract_original_charset(Compose *compose)
1333 MsgInfo *info = NULL;
1334 if (compose->replyinfo) {
1335 info = compose->replyinfo;
1336 } else if (compose->fwdinfo) {
1337 info = compose->fwdinfo;
1338 } else if (compose->targetinfo) {
1339 info = compose->targetinfo;
1342 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1343 MimeInfo *partinfo = mimeinfo;
1344 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1345 partinfo = procmime_mimeinfo_next(partinfo);
1347 compose->orig_charset =
1348 g_strdup(procmime_mimeinfo_get_parameter(
1349 partinfo, "charset"));
1351 procmime_mimeinfo_free_all(mimeinfo);
1355 #define SIGNAL_BLOCK(buffer) { \
1356 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1357 G_CALLBACK(compose_changed_cb), \
1359 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1360 G_CALLBACK(text_inserted), \
1364 #define SIGNAL_UNBLOCK(buffer) { \
1365 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1366 G_CALLBACK(compose_changed_cb), \
1368 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1369 G_CALLBACK(text_inserted), \
1373 static Compose *compose_generic_reply(MsgInfo *msginfo,
1374 ComposeQuoteMode quote_mode,
1375 gboolean to_all, gboolean to_ml,
1377 gboolean followup_and_reply_to,
1381 PrefsAccount *account = NULL;
1382 GtkTextView *textview;
1383 GtkTextBuffer *textbuf;
1384 gboolean quote = FALSE;
1385 const gchar *qmark = NULL;
1386 const gchar *body_fmt = NULL;
1388 g_return_val_if_fail(msginfo != NULL, NULL);
1389 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1391 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1393 g_return_val_if_fail(account != NULL, NULL);
1395 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1397 compose->updating = TRUE;
1399 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1400 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1402 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1403 if (!compose->replyinfo)
1404 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1406 compose_extract_original_charset(compose);
1408 if (msginfo->folder && msginfo->folder->ret_rcpt)
1409 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1411 /* Set save folder */
1412 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1413 gchar *folderidentifier;
1415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1416 folderidentifier = folder_item_get_identifier(msginfo->folder);
1417 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1418 g_free(folderidentifier);
1421 if (compose_parse_header(compose, msginfo) < 0) {
1422 compose->updating = FALSE;
1423 compose_destroy(compose);
1427 /* override from name according to folder properties */
1428 if (msginfo->folder && msginfo->folder->prefs &&
1429 msginfo->folder->prefs->reply_with_format &&
1430 msginfo->folder->prefs->reply_override_from_format &&
1431 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1436 /* decode \-escape sequences in the internal representation of the quote format */
1437 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1438 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1441 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1442 compose->gtkaspell);
1444 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1446 quote_fmt_scan_string(tmp);
1449 buf = quote_fmt_get_buffer();
1451 alertpanel_error(_("Message reply From format error."));
1453 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1454 quote_fmt_reset_vartable();
1459 textview = (GTK_TEXT_VIEW(compose->text));
1460 textbuf = gtk_text_view_get_buffer(textview);
1461 compose_create_tags(textview, compose);
1463 undo_block(compose->undostruct);
1465 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1468 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1469 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1470 /* use the reply format of folder (if enabled), or the account's one
1471 (if enabled) or fallback to the global reply format, which is always
1472 enabled (even if empty), and use the relevant quotemark */
1474 if (msginfo->folder && msginfo->folder->prefs &&
1475 msginfo->folder->prefs->reply_with_format) {
1476 qmark = msginfo->folder->prefs->reply_quotemark;
1477 body_fmt = msginfo->folder->prefs->reply_body_format;
1479 } else if (account->reply_with_format) {
1480 qmark = account->reply_quotemark;
1481 body_fmt = account->reply_body_format;
1484 qmark = prefs_common.quotemark;
1485 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1486 body_fmt = gettext(prefs_common.quotefmt);
1493 /* empty quotemark is not allowed */
1494 if (qmark == NULL || *qmark == '\0')
1496 compose_quote_fmt(compose, compose->replyinfo,
1497 body_fmt, qmark, body, FALSE, TRUE,
1498 _("Message reply format error at line %d."));
1499 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1500 quote_fmt_reset_vartable();
1503 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1504 compose_force_encryption(compose, account, FALSE);
1507 SIGNAL_BLOCK(textbuf);
1509 if (account->auto_sig)
1510 compose_insert_sig(compose, FALSE);
1512 compose_wrap_all(compose);
1514 SIGNAL_UNBLOCK(textbuf);
1516 gtk_widget_grab_focus(compose->text);
1518 undo_unblock(compose->undostruct);
1520 if (prefs_common.auto_exteditor)
1521 compose_exec_ext_editor(compose);
1523 compose->modified = FALSE;
1524 compose_set_title(compose);
1526 compose->updating = FALSE;
1527 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1528 SCROLL_TO_CURSOR(compose);
1530 if (compose->deferred_destroy) {
1531 compose_destroy(compose);
1538 #define INSERT_FW_HEADER(var, hdr) \
1539 if (msginfo->var && *msginfo->var) { \
1540 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1541 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1542 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1545 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1546 gboolean as_attach, const gchar *body,
1547 gboolean no_extedit,
1551 GtkTextView *textview;
1552 GtkTextBuffer *textbuf;
1555 g_return_val_if_fail(msginfo != NULL, NULL);
1556 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1559 !(account = compose_guess_forward_account_from_msginfo
1561 account = cur_account;
1563 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1565 compose->updating = TRUE;
1566 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1567 if (!compose->fwdinfo)
1568 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1570 compose_extract_original_charset(compose);
1572 if (msginfo->subject && *msginfo->subject) {
1573 gchar *buf, *buf2, *p;
1575 buf = p = g_strdup(msginfo->subject);
1576 p += subject_get_prefix_length(p);
1577 memmove(buf, p, strlen(p) + 1);
1579 buf2 = g_strdup_printf("Fw: %s", buf);
1580 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1586 /* override from name according to folder properties */
1587 if (msginfo->folder && msginfo->folder->prefs &&
1588 msginfo->folder->prefs->forward_with_format &&
1589 msginfo->folder->prefs->forward_override_from_format &&
1590 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1594 MsgInfo *full_msginfo = NULL;
1597 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1599 full_msginfo = procmsg_msginfo_copy(msginfo);
1601 /* decode \-escape sequences in the internal representation of the quote format */
1602 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1603 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1606 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1607 compose->gtkaspell);
1609 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1611 quote_fmt_scan_string(tmp);
1614 buf = quote_fmt_get_buffer();
1616 alertpanel_error(_("Message forward From format error."));
1618 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1619 quote_fmt_reset_vartable();
1622 procmsg_msginfo_free(full_msginfo);
1625 textview = GTK_TEXT_VIEW(compose->text);
1626 textbuf = gtk_text_view_get_buffer(textview);
1627 compose_create_tags(textview, compose);
1629 undo_block(compose->undostruct);
1633 msgfile = procmsg_get_message_file(msginfo);
1634 if (!is_file_exist(msgfile))
1635 g_warning("%s: file not exist\n", msgfile);
1637 compose_attach_append(compose, msgfile, msgfile,
1642 const gchar *qmark = NULL;
1643 const gchar *body_fmt = NULL;
1644 MsgInfo *full_msginfo;
1646 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1647 body_fmt = gettext(prefs_common.fw_quotefmt);
1651 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1653 full_msginfo = procmsg_msginfo_copy(msginfo);
1655 /* use the forward format of folder (if enabled), or the account's one
1656 (if enabled) or fallback to the global forward format, which is always
1657 enabled (even if empty), and use the relevant quotemark */
1658 if (msginfo->folder && msginfo->folder->prefs &&
1659 msginfo->folder->prefs->forward_with_format) {
1660 qmark = msginfo->folder->prefs->forward_quotemark;
1661 body_fmt = msginfo->folder->prefs->forward_body_format;
1663 } else if (account->forward_with_format) {
1664 qmark = account->forward_quotemark;
1665 body_fmt = account->forward_body_format;
1668 qmark = prefs_common.fw_quotemark;
1669 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1670 body_fmt = gettext(prefs_common.fw_quotefmt);
1675 /* empty quotemark is not allowed */
1676 if (qmark == NULL || *qmark == '\0')
1679 compose_quote_fmt(compose, full_msginfo,
1680 body_fmt, qmark, body, FALSE, TRUE,
1681 _("Message forward format error at line %d."));
1682 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1683 quote_fmt_reset_vartable();
1684 compose_attach_parts(compose, msginfo);
1686 procmsg_msginfo_free(full_msginfo);
1689 SIGNAL_BLOCK(textbuf);
1691 if (account->auto_sig)
1692 compose_insert_sig(compose, FALSE);
1694 compose_wrap_all(compose);
1696 SIGNAL_UNBLOCK(textbuf);
1698 gtk_text_buffer_get_start_iter(textbuf, &iter);
1699 gtk_text_buffer_place_cursor(textbuf, &iter);
1701 gtk_widget_grab_focus(compose->header_last->entry);
1703 if (!no_extedit && prefs_common.auto_exteditor)
1704 compose_exec_ext_editor(compose);
1707 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1708 gchar *folderidentifier;
1710 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1711 folderidentifier = folder_item_get_identifier(msginfo->folder);
1712 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1713 g_free(folderidentifier);
1716 undo_unblock(compose->undostruct);
1718 compose->modified = FALSE;
1719 compose_set_title(compose);
1721 compose->updating = FALSE;
1722 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1723 SCROLL_TO_CURSOR(compose);
1725 if (compose->deferred_destroy) {
1726 compose_destroy(compose);
1733 #undef INSERT_FW_HEADER
1735 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1738 GtkTextView *textview;
1739 GtkTextBuffer *textbuf;
1743 gboolean single_mail = TRUE;
1745 g_return_val_if_fail(msginfo_list != NULL, NULL);
1747 if (g_slist_length(msginfo_list) > 1)
1748 single_mail = FALSE;
1750 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1751 if (((MsgInfo *)msginfo->data)->folder == NULL)
1754 /* guess account from first selected message */
1756 !(account = compose_guess_forward_account_from_msginfo
1757 (msginfo_list->data)))
1758 account = cur_account;
1760 g_return_val_if_fail(account != NULL, NULL);
1762 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1763 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1764 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1767 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1769 compose->updating = TRUE;
1771 /* override from name according to folder properties */
1772 if (msginfo_list->data) {
1773 MsgInfo *msginfo = msginfo_list->data;
1775 if (msginfo->folder && msginfo->folder->prefs &&
1776 msginfo->folder->prefs->forward_with_format &&
1777 msginfo->folder->prefs->forward_override_from_format &&
1778 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1783 /* decode \-escape sequences in the internal representation of the quote format */
1784 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1785 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1788 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1789 compose->gtkaspell);
1791 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1793 quote_fmt_scan_string(tmp);
1796 buf = quote_fmt_get_buffer();
1798 alertpanel_error(_("Message forward From format error."));
1800 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1801 quote_fmt_reset_vartable();
1807 textview = GTK_TEXT_VIEW(compose->text);
1808 textbuf = gtk_text_view_get_buffer(textview);
1809 compose_create_tags(textview, compose);
1811 undo_block(compose->undostruct);
1812 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1813 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1815 if (!is_file_exist(msgfile))
1816 g_warning("%s: file not exist\n", msgfile);
1818 compose_attach_append(compose, msgfile, msgfile,
1824 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1825 if (info->subject && *info->subject) {
1826 gchar *buf, *buf2, *p;
1828 buf = p = g_strdup(info->subject);
1829 p += subject_get_prefix_length(p);
1830 memmove(buf, p, strlen(p) + 1);
1832 buf2 = g_strdup_printf("Fw: %s", buf);
1833 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1839 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1840 _("Fw: multiple emails"));
1843 SIGNAL_BLOCK(textbuf);
1845 if (account->auto_sig)
1846 compose_insert_sig(compose, FALSE);
1848 compose_wrap_all(compose);
1850 SIGNAL_UNBLOCK(textbuf);
1852 gtk_text_buffer_get_start_iter(textbuf, &iter);
1853 gtk_text_buffer_place_cursor(textbuf, &iter);
1855 gtk_widget_grab_focus(compose->header_last->entry);
1856 undo_unblock(compose->undostruct);
1857 compose->modified = FALSE;
1858 compose_set_title(compose);
1860 compose->updating = FALSE;
1861 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1862 SCROLL_TO_CURSOR(compose);
1864 if (compose->deferred_destroy) {
1865 compose_destroy(compose);
1872 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1874 GtkTextIter start = *iter;
1875 GtkTextIter end_iter;
1876 int start_pos = gtk_text_iter_get_offset(&start);
1878 if (!compose->account->sig_sep)
1881 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1882 start_pos+strlen(compose->account->sig_sep));
1884 /* check sig separator */
1885 str = gtk_text_iter_get_text(&start, &end_iter);
1886 if (!strcmp(str, compose->account->sig_sep)) {
1888 /* check end of line (\n) */
1889 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1890 start_pos+strlen(compose->account->sig_sep));
1891 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1892 start_pos+strlen(compose->account->sig_sep)+1);
1893 tmp = gtk_text_iter_get_text(&start, &end_iter);
1894 if (!strcmp(tmp,"\n")) {
1906 static void compose_colorize_signature(Compose *compose)
1908 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1910 GtkTextIter end_iter;
1911 gtk_text_buffer_get_start_iter(buffer, &iter);
1912 while (gtk_text_iter_forward_line(&iter))
1913 if (compose_is_sig_separator(compose, buffer, &iter)) {
1914 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1915 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1919 #define BLOCK_WRAP() { \
1920 prev_autowrap = compose->autowrap; \
1921 buffer = gtk_text_view_get_buffer( \
1922 GTK_TEXT_VIEW(compose->text)); \
1923 compose->autowrap = FALSE; \
1925 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1926 G_CALLBACK(compose_changed_cb), \
1928 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1929 G_CALLBACK(text_inserted), \
1932 #define UNBLOCK_WRAP() { \
1933 compose->autowrap = prev_autowrap; \
1934 if (compose->autowrap) { \
1935 gint old = compose->draft_timeout_tag; \
1936 compose->draft_timeout_tag = -2; \
1937 compose_wrap_all(compose); \
1938 compose->draft_timeout_tag = old; \
1941 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1942 G_CALLBACK(compose_changed_cb), \
1944 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1945 G_CALLBACK(text_inserted), \
1949 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1951 Compose *compose = NULL;
1952 PrefsAccount *account = NULL;
1953 GtkTextView *textview;
1954 GtkTextBuffer *textbuf;
1958 gchar buf[BUFFSIZE];
1959 gboolean use_signing = FALSE;
1960 gboolean use_encryption = FALSE;
1961 gchar *privacy_system = NULL;
1962 int priority = PRIORITY_NORMAL;
1963 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1964 gboolean autowrap = prefs_common.autowrap;
1965 gboolean autoindent = prefs_common.auto_indent;
1967 g_return_val_if_fail(msginfo != NULL, NULL);
1968 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1970 if (compose_put_existing_to_front(msginfo)) {
1974 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1975 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1976 gchar queueheader_buf[BUFFSIZE];
1979 /* Select Account from queue headers */
1980 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1981 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1982 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1983 account = account_find_from_id(id);
1985 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1986 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1987 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1988 account = account_find_from_id(id);
1990 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1991 sizeof(queueheader_buf), "NAID:")) {
1992 id = atoi(&queueheader_buf[strlen("NAID:")]);
1993 account = account_find_from_id(id);
1995 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1996 sizeof(queueheader_buf), "MAID:")) {
1997 id = atoi(&queueheader_buf[strlen("MAID:")]);
1998 account = account_find_from_id(id);
2000 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2001 sizeof(queueheader_buf), "S:")) {
2002 account = account_find_from_address(queueheader_buf, FALSE);
2004 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2005 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2006 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2007 use_signing = param;
2010 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2011 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2012 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2013 use_signing = param;
2016 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2017 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2018 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2019 use_encryption = param;
2021 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2022 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2023 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2024 use_encryption = param;
2026 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2027 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2028 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2032 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2033 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2036 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2037 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2038 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2042 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2044 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2045 sizeof(queueheader_buf), "X-Priority: ")) {
2046 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2049 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2050 sizeof(queueheader_buf), "RMID:")) {
2051 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2052 if (tokens[0] && tokens[1] && tokens[2]) {
2053 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2054 if (orig_item != NULL) {
2055 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2060 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2061 sizeof(queueheader_buf), "FMID:")) {
2062 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2063 if (tokens[0] && tokens[1] && tokens[2]) {
2064 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2065 if (orig_item != NULL) {
2066 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2072 account = msginfo->folder->folder->account;
2075 if (!account && prefs_common.reedit_account_autosel) {
2076 gchar from[BUFFSIZE];
2077 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2078 extract_address(from);
2079 account = account_find_from_address(from, FALSE);
2083 account = cur_account;
2085 g_return_val_if_fail(account != NULL, NULL);
2087 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2089 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2090 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2091 compose->autowrap = autowrap;
2092 compose->replyinfo = replyinfo;
2093 compose->fwdinfo = fwdinfo;
2095 compose->updating = TRUE;
2096 compose->priority = priority;
2098 if (privacy_system != NULL) {
2099 compose->privacy_system = privacy_system;
2100 compose_use_signing(compose, use_signing);
2101 compose_use_encryption(compose, use_encryption);
2102 compose_update_privacy_system_menu_item(compose, FALSE);
2104 activate_privacy_system(compose, account, FALSE);
2107 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2109 compose_extract_original_charset(compose);
2111 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2112 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2113 gchar queueheader_buf[BUFFSIZE];
2115 /* Set message save folder */
2116 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2119 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2120 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2121 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2123 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2124 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2126 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2131 if (compose_parse_header(compose, msginfo) < 0) {
2132 compose->updating = FALSE;
2133 compose_destroy(compose);
2136 compose_reedit_set_entry(compose, msginfo);
2138 textview = GTK_TEXT_VIEW(compose->text);
2139 textbuf = gtk_text_view_get_buffer(textview);
2140 compose_create_tags(textview, compose);
2142 mark = gtk_text_buffer_get_insert(textbuf);
2143 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2145 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2146 G_CALLBACK(compose_changed_cb),
2149 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2150 fp = procmime_get_first_encrypted_text_content(msginfo);
2152 compose_force_encryption(compose, account, TRUE);
2155 fp = procmime_get_first_text_content(msginfo);
2158 g_warning("Can't get text part\n");
2162 gboolean prev_autowrap = compose->autowrap;
2163 GtkTextBuffer *buffer = textbuf;
2165 while (fgets(buf, sizeof(buf), fp) != NULL) {
2167 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2173 compose_attach_parts(compose, msginfo);
2175 compose_colorize_signature(compose);
2177 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2178 G_CALLBACK(compose_changed_cb),
2181 gtk_widget_grab_focus(compose->text);
2183 if (prefs_common.auto_exteditor) {
2184 compose_exec_ext_editor(compose);
2186 compose->modified = FALSE;
2187 compose_set_title(compose);
2189 compose->updating = FALSE;
2190 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2191 SCROLL_TO_CURSOR(compose);
2193 if (compose->deferred_destroy) {
2194 compose_destroy(compose);
2198 compose->sig_str = compose_get_signature_str(compose);
2203 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2210 g_return_val_if_fail(msginfo != NULL, NULL);
2213 account = account_get_reply_account(msginfo,
2214 prefs_common.reply_account_autosel);
2215 g_return_val_if_fail(account != NULL, NULL);
2217 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2219 compose->updating = TRUE;
2221 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2222 compose->replyinfo = NULL;
2223 compose->fwdinfo = NULL;
2225 compose_show_first_last_header(compose, TRUE);
2227 gtk_widget_grab_focus(compose->header_last->entry);
2229 filename = procmsg_get_message_file(msginfo);
2231 if (filename == NULL) {
2232 compose->updating = FALSE;
2233 compose_destroy(compose);
2238 compose->redirect_filename = filename;
2240 /* Set save folder */
2241 item = msginfo->folder;
2242 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2243 gchar *folderidentifier;
2245 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2246 folderidentifier = folder_item_get_identifier(item);
2247 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2248 g_free(folderidentifier);
2251 compose_attach_parts(compose, msginfo);
2253 if (msginfo->subject)
2254 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2256 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2258 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2259 _("Message redirect format error at line %d."));
2260 quote_fmt_reset_vartable();
2261 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2263 compose_colorize_signature(compose);
2266 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2267 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2268 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2270 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2271 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2272 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2273 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2277 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2279 if (compose->toolbar->draft_btn)
2280 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2281 if (compose->toolbar->insert_btn)
2282 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2283 if (compose->toolbar->attach_btn)
2284 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2285 if (compose->toolbar->sig_btn)
2286 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2287 if (compose->toolbar->exteditor_btn)
2288 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2289 if (compose->toolbar->linewrap_current_btn)
2290 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2291 if (compose->toolbar->linewrap_all_btn)
2292 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2294 compose->modified = FALSE;
2295 compose_set_title(compose);
2296 compose->updating = FALSE;
2297 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2298 SCROLL_TO_CURSOR(compose);
2300 if (compose->deferred_destroy) {
2301 compose_destroy(compose);
2308 GList *compose_get_compose_list(void)
2310 return compose_list;
2313 void compose_entry_append(Compose *compose, const gchar *address,
2314 ComposeEntryType type)
2316 const gchar *header;
2318 gboolean in_quote = FALSE;
2319 if (!address || *address == '\0') return;
2326 header = N_("Bcc:");
2328 case COMPOSE_REPLYTO:
2329 header = N_("Reply-To:");
2331 case COMPOSE_NEWSGROUPS:
2332 header = N_("Newsgroups:");
2334 case COMPOSE_FOLLOWUPTO:
2335 header = N_( "Followup-To:");
2342 header = prefs_common_translated_header_name(header);
2344 cur = begin = (gchar *)address;
2346 /* we separate the line by commas, but not if we're inside a quoted
2348 while (*cur != '\0') {
2350 in_quote = !in_quote;
2351 if (*cur == ',' && !in_quote) {
2352 gchar *tmp = g_strdup(begin);
2354 tmp[cur-begin]='\0';
2357 while (*tmp == ' ' || *tmp == '\t')
2359 compose_add_header_entry(compose, header, tmp);
2366 gchar *tmp = g_strdup(begin);
2368 tmp[cur-begin]='\0';
2371 while (*tmp == ' ' || *tmp == '\t')
2373 compose_add_header_entry(compose, header, tmp);
2378 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2380 static GdkColor yellow;
2381 static GdkColor black;
2382 static gboolean yellow_initialised = FALSE;
2386 if (!yellow_initialised) {
2387 gdk_color_parse("#f5f6be", &yellow);
2388 gdk_color_parse("#000000", &black);
2389 yellow_initialised = gdk_colormap_alloc_color(
2390 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2391 yellow_initialised &= gdk_colormap_alloc_color(
2392 gdk_colormap_get_system(), &black, FALSE, TRUE);
2395 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2396 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2397 if (gtk_entry_get_text(entry) &&
2398 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2399 if (yellow_initialised) {
2400 gtk_widget_modify_base(
2401 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2402 GTK_STATE_NORMAL, &yellow);
2403 gtk_widget_modify_text(
2404 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2405 GTK_STATE_NORMAL, &black);
2411 void compose_toolbar_cb(gint action, gpointer data)
2413 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2414 Compose *compose = (Compose*)toolbar_item->parent;
2416 g_return_if_fail(compose != NULL);
2420 compose_send_cb(NULL, compose);
2423 compose_send_later_cb(NULL, compose);
2426 compose_draft(compose, COMPOSE_QUIT_EDITING);
2429 compose_insert_file_cb(NULL, compose);
2432 compose_attach_cb(NULL, compose);
2435 compose_insert_sig(compose, FALSE);
2438 compose_ext_editor_cb(NULL, compose);
2440 case A_LINEWRAP_CURRENT:
2441 compose_beautify_paragraph(compose, NULL, TRUE);
2443 case A_LINEWRAP_ALL:
2444 compose_wrap_all_full(compose, TRUE);
2447 compose_address_cb(NULL, compose);
2450 case A_CHECK_SPELLING:
2451 compose_check_all(NULL, compose);
2459 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2464 gchar *subject = NULL;
2468 gchar **attach = NULL;
2470 /* get mailto parts but skip from */
2471 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2474 compose_entry_append(compose, to, to_type);
2476 compose_entry_append(compose, cc, COMPOSE_CC);
2478 compose_entry_append(compose, bcc, COMPOSE_BCC);
2480 if (!g_utf8_validate (subject, -1, NULL)) {
2481 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2482 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2485 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2489 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2490 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2493 gboolean prev_autowrap = compose->autowrap;
2495 compose->autowrap = FALSE;
2497 mark = gtk_text_buffer_get_insert(buffer);
2498 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2500 if (!g_utf8_validate (body, -1, NULL)) {
2501 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2502 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2505 gtk_text_buffer_insert(buffer, &iter, body, -1);
2507 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2509 compose->autowrap = prev_autowrap;
2510 if (compose->autowrap)
2511 compose_wrap_all(compose);
2515 gint i = 0, att = 0;
2516 gchar *warn_files = NULL;
2517 while (attach[i] != NULL) {
2518 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2519 if (utf8_filename) {
2520 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2521 gchar *tmp = g_strdup_printf("%s%s\n",
2522 warn_files?warn_files:"",
2528 g_free(utf8_filename);
2530 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2535 alertpanel_notice(ngettext(
2536 "The following file has been attached: \n%s",
2537 "The following files have been attached: \n%s", att), warn_files);
2549 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2551 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2552 {"Cc:", NULL, TRUE},
2553 {"References:", NULL, FALSE},
2554 {"Bcc:", NULL, TRUE},
2555 {"Newsgroups:", NULL, TRUE},
2556 {"Followup-To:", NULL, TRUE},
2557 {"List-Post:", NULL, FALSE},
2558 {"X-Priority:", NULL, FALSE},
2559 {NULL, NULL, FALSE}};
2575 g_return_val_if_fail(msginfo != NULL, -1);
2577 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2578 procheader_get_header_fields(fp, hentry);
2581 if (hentry[H_REPLY_TO].body != NULL) {
2582 if (hentry[H_REPLY_TO].body[0] != '\0') {
2584 conv_unmime_header(hentry[H_REPLY_TO].body,
2587 g_free(hentry[H_REPLY_TO].body);
2588 hentry[H_REPLY_TO].body = NULL;
2590 if (hentry[H_CC].body != NULL) {
2591 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2592 g_free(hentry[H_CC].body);
2593 hentry[H_CC].body = NULL;
2595 if (hentry[H_REFERENCES].body != NULL) {
2596 if (compose->mode == COMPOSE_REEDIT)
2597 compose->references = hentry[H_REFERENCES].body;
2599 compose->references = compose_parse_references
2600 (hentry[H_REFERENCES].body, msginfo->msgid);
2601 g_free(hentry[H_REFERENCES].body);
2603 hentry[H_REFERENCES].body = NULL;
2605 if (hentry[H_BCC].body != NULL) {
2606 if (compose->mode == COMPOSE_REEDIT)
2608 conv_unmime_header(hentry[H_BCC].body, NULL);
2609 g_free(hentry[H_BCC].body);
2610 hentry[H_BCC].body = NULL;
2612 if (hentry[H_NEWSGROUPS].body != NULL) {
2613 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2614 hentry[H_NEWSGROUPS].body = NULL;
2616 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2617 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2618 compose->followup_to =
2619 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2622 g_free(hentry[H_FOLLOWUP_TO].body);
2623 hentry[H_FOLLOWUP_TO].body = NULL;
2625 if (hentry[H_LIST_POST].body != NULL) {
2628 extract_address(hentry[H_LIST_POST].body);
2629 if (hentry[H_LIST_POST].body[0] != '\0') {
2630 scan_mailto_url(hentry[H_LIST_POST].body,
2631 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2633 g_free(compose->ml_post);
2634 compose->ml_post = to;
2637 g_free(hentry[H_LIST_POST].body);
2638 hentry[H_LIST_POST].body = NULL;
2641 /* CLAWS - X-Priority */
2642 if (compose->mode == COMPOSE_REEDIT)
2643 if (hentry[H_X_PRIORITY].body != NULL) {
2646 priority = atoi(hentry[H_X_PRIORITY].body);
2647 g_free(hentry[H_X_PRIORITY].body);
2649 hentry[H_X_PRIORITY].body = NULL;
2651 if (priority < PRIORITY_HIGHEST ||
2652 priority > PRIORITY_LOWEST)
2653 priority = PRIORITY_NORMAL;
2655 compose->priority = priority;
2658 if (compose->mode == COMPOSE_REEDIT) {
2659 if (msginfo->inreplyto && *msginfo->inreplyto)
2660 compose->inreplyto = g_strdup(msginfo->inreplyto);
2664 if (msginfo->msgid && *msginfo->msgid)
2665 compose->inreplyto = g_strdup(msginfo->msgid);
2667 if (!compose->references) {
2668 if (msginfo->msgid && *msginfo->msgid) {
2669 if (msginfo->inreplyto && *msginfo->inreplyto)
2670 compose->references =
2671 g_strdup_printf("<%s>\n\t<%s>",
2675 compose->references =
2676 g_strconcat("<", msginfo->msgid, ">",
2678 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2679 compose->references =
2680 g_strconcat("<", msginfo->inreplyto, ">",
2688 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2690 GSList *ref_id_list, *cur;
2694 ref_id_list = references_list_append(NULL, ref);
2695 if (!ref_id_list) return NULL;
2696 if (msgid && *msgid)
2697 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2702 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2703 /* "<" + Message-ID + ">" + CR+LF+TAB */
2704 len += strlen((gchar *)cur->data) + 5;
2706 if (len > MAX_REFERENCES_LEN) {
2707 /* remove second message-ID */
2708 if (ref_id_list && ref_id_list->next &&
2709 ref_id_list->next->next) {
2710 g_free(ref_id_list->next->data);
2711 ref_id_list = g_slist_remove
2712 (ref_id_list, ref_id_list->next->data);
2714 slist_free_strings(ref_id_list);
2715 g_slist_free(ref_id_list);
2722 new_ref = g_string_new("");
2723 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2724 if (new_ref->len > 0)
2725 g_string_append(new_ref, "\n\t");
2726 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2729 slist_free_strings(ref_id_list);
2730 g_slist_free(ref_id_list);
2732 new_ref_str = new_ref->str;
2733 g_string_free(new_ref, FALSE);
2738 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2739 const gchar *fmt, const gchar *qmark,
2740 const gchar *body, gboolean rewrap,
2741 gboolean need_unescape,
2742 const gchar *err_msg)
2744 MsgInfo* dummyinfo = NULL;
2745 gchar *quote_str = NULL;
2747 gboolean prev_autowrap;
2748 const gchar *trimmed_body = body;
2749 gint cursor_pos = -1;
2750 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2751 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2756 SIGNAL_BLOCK(buffer);
2759 dummyinfo = compose_msginfo_new_from_compose(compose);
2760 msginfo = dummyinfo;
2763 if (qmark != NULL) {
2765 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2766 compose->gtkaspell);
2768 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2770 quote_fmt_scan_string(qmark);
2773 buf = quote_fmt_get_buffer();
2775 alertpanel_error(_("Quote mark format error."));
2777 Xstrdup_a(quote_str, buf, goto error)
2780 if (fmt && *fmt != '\0') {
2783 while (*trimmed_body == '\n')
2787 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2788 compose->gtkaspell);
2790 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2792 if (need_unescape) {
2795 /* decode \-escape sequences in the internal representation of the quote format */
2796 tmp = malloc(strlen(fmt)+1);
2797 pref_get_unescaped_pref(tmp, fmt);
2798 quote_fmt_scan_string(tmp);
2802 quote_fmt_scan_string(fmt);
2806 buf = quote_fmt_get_buffer();
2808 gint line = quote_fmt_get_line();
2809 alertpanel_error(err_msg, line);
2815 prev_autowrap = compose->autowrap;
2816 compose->autowrap = FALSE;
2818 mark = gtk_text_buffer_get_insert(buffer);
2819 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2820 if (g_utf8_validate(buf, -1, NULL)) {
2821 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2823 gchar *tmpout = NULL;
2824 tmpout = conv_codeset_strdup
2825 (buf, conv_get_locale_charset_str_no_utf8(),
2827 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2829 tmpout = g_malloc(strlen(buf)*2+1);
2830 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2832 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2836 cursor_pos = quote_fmt_get_cursor_pos();
2837 if (cursor_pos == -1)
2838 cursor_pos = gtk_text_iter_get_offset(&iter);
2839 compose->set_cursor_pos = cursor_pos;
2841 gtk_text_buffer_get_start_iter(buffer, &iter);
2842 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2843 gtk_text_buffer_place_cursor(buffer, &iter);
2845 compose->autowrap = prev_autowrap;
2846 if (compose->autowrap && rewrap)
2847 compose_wrap_all(compose);
2854 SIGNAL_UNBLOCK(buffer);
2856 procmsg_msginfo_free( dummyinfo );
2861 /* if ml_post is of type addr@host and from is of type
2862 * addr-anything@host, return TRUE
2864 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2866 gchar *left_ml = NULL;
2867 gchar *right_ml = NULL;
2868 gchar *left_from = NULL;
2869 gchar *right_from = NULL;
2870 gboolean result = FALSE;
2872 if (!ml_post || !from)
2875 left_ml = g_strdup(ml_post);
2876 if (strstr(left_ml, "@")) {
2877 right_ml = strstr(left_ml, "@")+1;
2878 *(strstr(left_ml, "@")) = '\0';
2881 left_from = g_strdup(from);
2882 if (strstr(left_from, "@")) {
2883 right_from = strstr(left_from, "@")+1;
2884 *(strstr(left_from, "@")) = '\0';
2887 if (left_ml && left_from && right_ml && right_from
2888 && !strncmp(left_from, left_ml, strlen(left_ml))
2889 && !strcmp(right_from, right_ml)) {
2898 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2900 gchar *my_addr1, *my_addr2;
2902 if (!addr1 || !addr2)
2905 Xstrdup_a(my_addr1, addr1, return FALSE);
2906 Xstrdup_a(my_addr2, addr2, return FALSE);
2908 extract_address(my_addr1);
2909 extract_address(my_addr2);
2911 return !strcasecmp(my_addr1, my_addr2);
2914 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2915 gboolean to_all, gboolean to_ml,
2917 gboolean followup_and_reply_to)
2919 GSList *cc_list = NULL;
2922 gchar *replyto = NULL;
2923 GHashTable *to_table;
2925 gboolean reply_to_ml = FALSE;
2926 gboolean default_reply_to = FALSE;
2928 g_return_if_fail(compose->account != NULL);
2929 g_return_if_fail(msginfo != NULL);
2931 reply_to_ml = to_ml && compose->ml_post;
2933 default_reply_to = msginfo->folder &&
2934 msginfo->folder->prefs->enable_default_reply_to;
2936 if (compose->account->protocol != A_NNTP) {
2937 if (reply_to_ml && !default_reply_to) {
2939 gboolean is_subscr = is_subscription(compose->ml_post,
2942 /* normal answer to ml post with a reply-to */
2943 compose_entry_append(compose,
2946 if (compose->replyto
2947 && !same_address(compose->ml_post, compose->replyto))
2948 compose_entry_append(compose,
2952 /* answer to subscription confirmation */
2953 if (compose->replyto)
2954 compose_entry_append(compose,
2957 else if (msginfo->from)
2958 compose_entry_append(compose,
2963 else if (!(to_all || to_sender) && default_reply_to) {
2964 compose_entry_append(compose,
2965 msginfo->folder->prefs->default_reply_to,
2967 compose_entry_mark_default_to(compose,
2968 msginfo->folder->prefs->default_reply_to);
2973 Xstrdup_a(tmp1, msginfo->from, return);
2974 extract_address(tmp1);
2975 if (to_all || to_sender ||
2976 !account_find_from_address(tmp1, FALSE))
2977 compose_entry_append(compose,
2978 (compose->replyto && !to_sender)
2979 ? compose->replyto :
2980 msginfo->from ? msginfo->from : "",
2982 else if (!to_all && !to_sender) {
2983 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2984 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2985 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2986 if (compose->replyto) {
2987 compose_entry_append(compose,
2991 compose_entry_append(compose,
2992 msginfo->from ? msginfo->from : "",
2996 /* replying to own mail, use original recp */
2997 compose_entry_append(compose,
2998 msginfo->to ? msginfo->to : "",
3000 compose_entry_append(compose,
3001 msginfo->cc ? msginfo->cc : "",
3007 if (to_sender || (compose->followup_to &&
3008 !strncmp(compose->followup_to, "poster", 6)))
3009 compose_entry_append
3011 (compose->replyto ? compose->replyto :
3012 msginfo->from ? msginfo->from : ""),
3015 else if (followup_and_reply_to || to_all) {
3016 compose_entry_append
3018 (compose->replyto ? compose->replyto :
3019 msginfo->from ? msginfo->from : ""),
3022 compose_entry_append
3024 compose->followup_to ? compose->followup_to :
3025 compose->newsgroups ? compose->newsgroups : "",
3026 COMPOSE_NEWSGROUPS);
3029 compose_entry_append
3031 compose->followup_to ? compose->followup_to :
3032 compose->newsgroups ? compose->newsgroups : "",
3033 COMPOSE_NEWSGROUPS);
3036 if (msginfo->subject && *msginfo->subject) {
3040 buf = p = g_strdup(msginfo->subject);
3041 p += subject_get_prefix_length(p);
3042 memmove(buf, p, strlen(p) + 1);
3044 buf2 = g_strdup_printf("Re: %s", buf);
3045 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3052 if (to_ml && compose->ml_post) return;
3053 if (!to_all || compose->account->protocol == A_NNTP) return;
3055 if (compose->replyto) {
3056 Xstrdup_a(replyto, compose->replyto, return);
3057 extract_address(replyto);
3059 if (msginfo->from) {
3060 Xstrdup_a(from, msginfo->from, return);
3061 extract_address(from);
3064 if (replyto && from)
3065 cc_list = address_list_append_with_comments(cc_list, from);
3066 if (to_all && msginfo->folder &&
3067 msginfo->folder->prefs->enable_default_reply_to)
3068 cc_list = address_list_append_with_comments(cc_list,
3069 msginfo->folder->prefs->default_reply_to);
3070 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3071 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3073 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3075 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3076 if (compose->account) {
3077 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3078 GINT_TO_POINTER(1));
3080 /* remove address on To: and that of current account */
3081 for (cur = cc_list; cur != NULL; ) {
3082 GSList *next = cur->next;
3085 addr = g_utf8_strdown(cur->data, -1);
3086 extract_address(addr);
3088 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3089 cc_list = g_slist_remove(cc_list, cur->data);
3091 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3095 hash_free_strings(to_table);
3096 g_hash_table_destroy(to_table);
3099 for (cur = cc_list; cur != NULL; cur = cur->next)
3100 compose_entry_append(compose, (gchar *)cur->data,
3102 slist_free_strings(cc_list);
3103 g_slist_free(cc_list);
3108 #define SET_ENTRY(entry, str) \
3111 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3114 #define SET_ADDRESS(type, str) \
3117 compose_entry_append(compose, str, type); \
3120 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3122 g_return_if_fail(msginfo != NULL);
3124 SET_ENTRY(subject_entry, msginfo->subject);
3125 SET_ENTRY(from_name, msginfo->from);
3126 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3127 SET_ADDRESS(COMPOSE_CC, compose->cc);
3128 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3129 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3130 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3131 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3133 compose_update_priority_menu_item(compose);
3134 compose_update_privacy_system_menu_item(compose, FALSE);
3135 compose_show_first_last_header(compose, TRUE);
3141 static void compose_insert_sig(Compose *compose, gboolean replace)
3143 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3144 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3146 GtkTextIter iter, iter_end;
3147 gint cur_pos, ins_pos;
3148 gboolean prev_autowrap;
3149 gboolean found = FALSE;
3150 gboolean exists = FALSE;
3152 g_return_if_fail(compose->account != NULL);
3156 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3157 G_CALLBACK(compose_changed_cb),
3160 mark = gtk_text_buffer_get_insert(buffer);
3161 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3162 cur_pos = gtk_text_iter_get_offset (&iter);
3165 gtk_text_buffer_get_end_iter(buffer, &iter);
3167 exists = (compose->sig_str != NULL);
3170 GtkTextIter first_iter, start_iter, end_iter;
3172 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3174 if (!exists || compose->sig_str[0] == '\0')
3177 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3178 compose->signature_tag);
3181 /* include previous \n\n */
3182 gtk_text_iter_backward_chars(&first_iter, 1);
3183 start_iter = first_iter;
3184 end_iter = first_iter;
3186 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3187 compose->signature_tag);
3188 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3189 compose->signature_tag);
3191 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3197 g_free(compose->sig_str);
3198 compose->sig_str = compose_get_signature_str(compose);
3200 cur_pos = gtk_text_iter_get_offset(&iter);
3202 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3203 g_free(compose->sig_str);
3204 compose->sig_str = NULL;
3206 if (compose->sig_inserted == FALSE)
3207 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3208 compose->sig_inserted = TRUE;
3210 cur_pos = gtk_text_iter_get_offset(&iter);
3211 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3213 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3214 gtk_text_iter_forward_chars(&iter, 1);
3215 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3216 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3218 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3219 cur_pos = gtk_text_buffer_get_char_count (buffer);
3222 /* put the cursor where it should be
3223 * either where the quote_fmt says, either where it was */
3224 if (compose->set_cursor_pos < 0)
3225 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3227 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3228 compose->set_cursor_pos);
3230 gtk_text_buffer_place_cursor(buffer, &iter);
3231 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3232 G_CALLBACK(compose_changed_cb),
3238 static gchar *compose_get_signature_str(Compose *compose)
3240 gchar *sig_body = NULL;
3241 gchar *sig_str = NULL;
3242 gchar *utf8_sig_str = NULL;
3244 g_return_val_if_fail(compose->account != NULL, NULL);
3246 if (!compose->account->sig_path)
3249 if (compose->account->sig_type == SIG_FILE) {
3250 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3251 g_warning("can't open signature file: %s\n",
3252 compose->account->sig_path);
3257 if (compose->account->sig_type == SIG_COMMAND)
3258 sig_body = get_command_output(compose->account->sig_path);
3262 tmp = file_read_to_str(compose->account->sig_path);
3265 sig_body = normalize_newlines(tmp);
3269 if (compose->account->sig_sep) {
3270 sig_str = g_strconcat("\n", compose->account->sig_sep, "\n", sig_body,
3274 sig_str = g_strconcat("\n", sig_body, NULL);
3277 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3278 utf8_sig_str = sig_str;
3280 utf8_sig_str = conv_codeset_strdup
3281 (sig_str, conv_get_locale_charset_str_no_utf8(),
3287 return utf8_sig_str;
3290 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3293 GtkTextBuffer *buffer;
3296 const gchar *cur_encoding;
3297 gchar buf[BUFFSIZE];
3300 gboolean prev_autowrap;
3301 gboolean badtxt = FALSE;
3303 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3305 if ((fp = g_fopen(file, "rb")) == NULL) {
3306 FILE_OP_ERROR(file, "fopen");
3307 return COMPOSE_INSERT_READ_ERROR;
3310 prev_autowrap = compose->autowrap;
3311 compose->autowrap = FALSE;
3313 text = GTK_TEXT_VIEW(compose->text);
3314 buffer = gtk_text_view_get_buffer(text);
3315 mark = gtk_text_buffer_get_insert(buffer);
3316 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3318 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3319 G_CALLBACK(text_inserted),
3322 cur_encoding = conv_get_locale_charset_str_no_utf8();
3324 while (fgets(buf, sizeof(buf), fp) != NULL) {
3327 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3328 str = g_strdup(buf);
3330 str = conv_codeset_strdup
3331 (buf, cur_encoding, CS_INTERNAL);
3334 /* strip <CR> if DOS/Windows file,
3335 replace <CR> with <LF> if Macintosh file. */
3338 if (len > 0 && str[len - 1] != '\n') {
3340 if (str[len] == '\r') str[len] = '\n';
3343 gtk_text_buffer_insert(buffer, &iter, str, -1);
3347 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3348 G_CALLBACK(text_inserted),
3350 compose->autowrap = prev_autowrap;
3351 if (compose->autowrap)
3352 compose_wrap_all(compose);
3357 return COMPOSE_INSERT_INVALID_CHARACTER;
3359 return COMPOSE_INSERT_SUCCESS;
3362 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3363 const gchar *filename,
3364 const gchar *content_type)
3372 GtkListStore *store;
3374 gboolean has_binary = FALSE;
3376 if (!is_file_exist(file)) {
3377 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3378 gboolean result = FALSE;
3379 if (file_from_uri && is_file_exist(file_from_uri)) {
3380 result = compose_attach_append(
3381 compose, file_from_uri,
3385 g_free(file_from_uri);
3388 alertpanel_error("File %s doesn't exist\n", filename);
3391 if ((size = get_file_size(file)) < 0) {
3392 alertpanel_error("Can't get file size of %s\n", filename);
3396 alertpanel_error(_("File %s is empty."), filename);
3399 if ((fp = g_fopen(file, "rb")) == NULL) {
3400 alertpanel_error(_("Can't read %s."), filename);
3405 ainfo = g_new0(AttachInfo, 1);
3406 auto_ainfo = g_auto_pointer_new_with_free
3407 (ainfo, (GFreeFunc) compose_attach_info_free);
3408 ainfo->file = g_strdup(file);
3411 ainfo->content_type = g_strdup(content_type);
3412 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3414 MsgFlags flags = {0, 0};
3416 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3417 ainfo->encoding = ENC_7BIT;
3419 ainfo->encoding = ENC_8BIT;
3421 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3422 if (msginfo && msginfo->subject)
3423 name = g_strdup(msginfo->subject);
3425 name = g_path_get_basename(filename ? filename : file);
3427 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3429 procmsg_msginfo_free(msginfo);
3431 if (!g_ascii_strncasecmp(content_type, "text", 4))
3432 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3434 ainfo->encoding = ENC_BASE64;
3435 name = g_path_get_basename(filename ? filename : file);
3436 ainfo->name = g_strdup(name);
3440 ainfo->content_type = procmime_get_mime_type(file);
3441 if (!ainfo->content_type) {
3442 ainfo->content_type =
3443 g_strdup("application/octet-stream");
3444 ainfo->encoding = ENC_BASE64;
3445 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3447 procmime_get_encoding_for_text_file(file, &has_binary);
3449 ainfo->encoding = ENC_BASE64;
3450 name = g_path_get_basename(filename ? filename : file);
3451 ainfo->name = g_strdup(name);
3455 if (ainfo->name != NULL
3456 && !strcmp(ainfo->name, ".")) {
3457 g_free(ainfo->name);
3461 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3462 g_free(ainfo->content_type);
3463 ainfo->content_type = g_strdup("application/octet-stream");
3466 ainfo->size = (goffset)size;
3467 size_text = to_human_readable((goffset)size);
3469 store = GTK_LIST_STORE(gtk_tree_view_get_model
3470 (GTK_TREE_VIEW(compose->attach_clist)));
3472 gtk_list_store_append(store, &iter);
3473 gtk_list_store_set(store, &iter,
3474 COL_MIMETYPE, ainfo->content_type,
3475 COL_SIZE, size_text,
3476 COL_NAME, ainfo->name,
3478 COL_AUTODATA, auto_ainfo,
3481 g_auto_pointer_free(auto_ainfo);
3482 compose_attach_update_label(compose);
3486 static void compose_use_signing(Compose *compose, gboolean use_signing)
3488 compose->use_signing = use_signing;
3489 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3492 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3494 compose->use_encryption = use_encryption;
3495 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3498 #define NEXT_PART_NOT_CHILD(info) \
3500 node = info->node; \
3501 while (node->children) \
3502 node = g_node_last_child(node); \
3503 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3506 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3510 MimeInfo *firsttext = NULL;
3511 MimeInfo *encrypted = NULL;
3514 const gchar *partname = NULL;
3516 mimeinfo = procmime_scan_message(msginfo);
3517 if (!mimeinfo) return;
3519 if (mimeinfo->node->children == NULL) {
3520 procmime_mimeinfo_free_all(mimeinfo);
3524 /* find first content part */
3525 child = (MimeInfo *) mimeinfo->node->children->data;
3526 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3527 child = (MimeInfo *)child->node->children->data;
3529 if (child->type == MIMETYPE_TEXT) {
3531 debug_print("First text part found\n");
3532 } else if (compose->mode == COMPOSE_REEDIT &&
3533 child->type == MIMETYPE_APPLICATION &&
3534 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3535 encrypted = (MimeInfo *)child->node->parent->data;
3538 child = (MimeInfo *) mimeinfo->node->children->data;
3539 while (child != NULL) {
3542 if (child == encrypted) {
3543 /* skip this part of tree */
3544 NEXT_PART_NOT_CHILD(child);
3548 if (child->type == MIMETYPE_MULTIPART) {
3549 /* get the actual content */
3550 child = procmime_mimeinfo_next(child);
3554 if (child == firsttext) {
3555 child = procmime_mimeinfo_next(child);
3559 outfile = procmime_get_tmp_file_name(child);
3560 if ((err = procmime_get_part(outfile, child)) < 0)
3561 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3563 gchar *content_type;
3565 content_type = procmime_get_content_type_str(child->type, child->subtype);
3567 /* if we meet a pgp signature, we don't attach it, but
3568 * we force signing. */
3569 if ((strcmp(content_type, "application/pgp-signature") &&
3570 strcmp(content_type, "application/pkcs7-signature") &&
3571 strcmp(content_type, "application/x-pkcs7-signature"))
3572 || compose->mode == COMPOSE_REDIRECT) {
3573 partname = procmime_mimeinfo_get_parameter(child, "filename");
3574 if (partname == NULL)
3575 partname = procmime_mimeinfo_get_parameter(child, "name");
3576 if (partname == NULL)
3578 compose_attach_append(compose, outfile,
3579 partname, content_type);
3581 compose_force_signing(compose, compose->account);
3583 g_free(content_type);
3586 NEXT_PART_NOT_CHILD(child);
3588 procmime_mimeinfo_free_all(mimeinfo);
3591 #undef NEXT_PART_NOT_CHILD
3596 WAIT_FOR_INDENT_CHAR,
3597 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3600 /* return indent length, we allow:
3601 indent characters followed by indent characters or spaces/tabs,
3602 alphabets and numbers immediately followed by indent characters,
3603 and the repeating sequences of the above
3604 If quote ends with multiple spaces, only the first one is included. */
3605 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3606 const GtkTextIter *start, gint *len)
3608 GtkTextIter iter = *start;
3612 IndentState state = WAIT_FOR_INDENT_CHAR;
3615 gint alnum_count = 0;
3616 gint space_count = 0;
3619 if (prefs_common.quote_chars == NULL) {
3623 while (!gtk_text_iter_ends_line(&iter)) {
3624 wc = gtk_text_iter_get_char(&iter);
3625 if (g_unichar_iswide(wc))
3627 clen = g_unichar_to_utf8(wc, ch);
3631 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3632 is_space = g_unichar_isspace(wc);
3634 if (state == WAIT_FOR_INDENT_CHAR) {
3635 if (!is_indent && !g_unichar_isalnum(wc))
3638 quote_len += alnum_count + space_count + 1;
3639 alnum_count = space_count = 0;
3640 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3643 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3644 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3648 else if (is_indent) {
3649 quote_len += alnum_count + space_count + 1;
3650 alnum_count = space_count = 0;
3653 state = WAIT_FOR_INDENT_CHAR;
3657 gtk_text_iter_forward_char(&iter);
3660 if (quote_len > 0 && space_count > 0)
3666 if (quote_len > 0) {
3668 gtk_text_iter_forward_chars(&iter, quote_len);
3669 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3675 /* return >0 if the line is itemized */
3676 static int compose_itemized_length(GtkTextBuffer *buffer,
3677 const GtkTextIter *start)
3679 GtkTextIter iter = *start;
3684 if (gtk_text_iter_ends_line(&iter))
3689 wc = gtk_text_iter_get_char(&iter);
3690 if (!g_unichar_isspace(wc))
3692 gtk_text_iter_forward_char(&iter);
3693 if (gtk_text_iter_ends_line(&iter))
3697 clen = g_unichar_to_utf8(wc, ch);
3701 if (!strchr("*-+", ch[0]))
3704 gtk_text_iter_forward_char(&iter);
3705 if (gtk_text_iter_ends_line(&iter))
3707 wc = gtk_text_iter_get_char(&iter);
3708 if (g_unichar_isspace(wc)) {
3714 /* return the string at the start of the itemization */
3715 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3716 const GtkTextIter *start)
3718 GtkTextIter iter = *start;
3721 GString *item_chars = g_string_new("");
3724 if (gtk_text_iter_ends_line(&iter))
3729 wc = gtk_text_iter_get_char(&iter);
3730 if (!g_unichar_isspace(wc))
3732 gtk_text_iter_forward_char(&iter);
3733 if (gtk_text_iter_ends_line(&iter))
3735 g_string_append_unichar(item_chars, wc);
3738 str = item_chars->str;
3739 g_string_free(item_chars, FALSE);
3743 /* return the number of spaces at a line's start */
3744 static int compose_left_offset_length(GtkTextBuffer *buffer,
3745 const GtkTextIter *start)
3747 GtkTextIter iter = *start;
3750 if (gtk_text_iter_ends_line(&iter))
3754 wc = gtk_text_iter_get_char(&iter);
3755 if (!g_unichar_isspace(wc))
3758 gtk_text_iter_forward_char(&iter);
3759 if (gtk_text_iter_ends_line(&iter))
3763 gtk_text_iter_forward_char(&iter);
3764 if (gtk_text_iter_ends_line(&iter))
3769 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3770 const GtkTextIter *start,
3771 GtkTextIter *break_pos,
3775 GtkTextIter iter = *start, line_end = *start;
3776 PangoLogAttr *attrs;
3783 gboolean can_break = FALSE;
3784 gboolean do_break = FALSE;
3785 gboolean was_white = FALSE;
3786 gboolean prev_dont_break = FALSE;
3788 gtk_text_iter_forward_to_line_end(&line_end);
3789 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3790 len = g_utf8_strlen(str, -1);
3794 g_warning("compose_get_line_break_pos: len = 0!\n");
3798 /* g_print("breaking line: %d: %s (len = %d)\n",
3799 gtk_text_iter_get_line(&iter), str, len); */
3801 attrs = g_new(PangoLogAttr, len + 1);
3803 pango_default_break(str, -1, NULL, attrs, len + 1);
3807 /* skip quote and leading spaces */
3808 for (i = 0; *p != '\0' && i < len; i++) {
3811 wc = g_utf8_get_char(p);
3812 if (i >= quote_len && !g_unichar_isspace(wc))
3814 if (g_unichar_iswide(wc))
3816 else if (*p == '\t')
3820 p = g_utf8_next_char(p);
3823 for (; *p != '\0' && i < len; i++) {
3824 PangoLogAttr *attr = attrs + i;
3828 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3831 was_white = attr->is_white;
3833 /* don't wrap URI */
3834 if ((uri_len = get_uri_len(p)) > 0) {
3836 if (pos > 0 && col > max_col) {
3846 wc = g_utf8_get_char(p);
3847 if (g_unichar_iswide(wc)) {
3849 if (prev_dont_break && can_break && attr->is_line_break)
3851 } else if (*p == '\t')
3855 if (pos > 0 && col > max_col) {
3860 if (*p == '-' || *p == '/')
3861 prev_dont_break = TRUE;
3863 prev_dont_break = FALSE;
3865 p = g_utf8_next_char(p);
3869 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3874 *break_pos = *start;
3875 gtk_text_iter_set_line_offset(break_pos, pos);
3880 static gboolean compose_join_next_line(Compose *compose,
3881 GtkTextBuffer *buffer,
3883 const gchar *quote_str)
3885 GtkTextIter iter_ = *iter, cur, prev, next, end;
3886 PangoLogAttr attrs[3];
3888 gchar *next_quote_str;
3891 gboolean keep_cursor = FALSE;
3893 if (!gtk_text_iter_forward_line(&iter_) ||
3894 gtk_text_iter_ends_line(&iter_)) {
3897 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3899 if ((quote_str || next_quote_str) &&
3900 strcmp2(quote_str, next_quote_str) != 0) {
3901 g_free(next_quote_str);
3904 g_free(next_quote_str);
3907 if (quote_len > 0) {
3908 gtk_text_iter_forward_chars(&end, quote_len);
3909 if (gtk_text_iter_ends_line(&end)) {
3914 /* don't join itemized lines */
3915 if (compose_itemized_length(buffer, &end) > 0) {
3919 /* don't join signature separator */
3920 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3923 /* delete quote str */
3925 gtk_text_buffer_delete(buffer, &iter_, &end);
3927 /* don't join line breaks put by the user */
3929 gtk_text_iter_backward_char(&cur);
3930 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3931 gtk_text_iter_forward_char(&cur);
3935 gtk_text_iter_forward_char(&cur);
3936 /* delete linebreak and extra spaces */
3937 while (gtk_text_iter_backward_char(&cur)) {
3938 wc1 = gtk_text_iter_get_char(&cur);
3939 if (!g_unichar_isspace(wc1))
3944 while (!gtk_text_iter_ends_line(&cur)) {
3945 wc1 = gtk_text_iter_get_char(&cur);
3946 if (!g_unichar_isspace(wc1))
3948 gtk_text_iter_forward_char(&cur);
3951 if (!gtk_text_iter_equal(&prev, &next)) {
3954 mark = gtk_text_buffer_get_insert(buffer);
3955 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3956 if (gtk_text_iter_equal(&prev, &cur))
3958 gtk_text_buffer_delete(buffer, &prev, &next);
3962 /* insert space if required */
3963 gtk_text_iter_backward_char(&prev);
3964 wc1 = gtk_text_iter_get_char(&prev);
3965 wc2 = gtk_text_iter_get_char(&next);
3966 gtk_text_iter_forward_char(&next);
3967 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3968 pango_default_break(str, -1, NULL, attrs, 3);
3969 if (!attrs[1].is_line_break ||
3970 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3971 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3973 gtk_text_iter_backward_char(&iter_);
3974 gtk_text_buffer_place_cursor(buffer, &iter_);
3983 #define ADD_TXT_POS(bp_, ep_, pti_) \
3984 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3985 last = last->next; \
3986 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3987 last->next = NULL; \
3989 g_warning("alloc error scanning URIs\n"); \
3992 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3994 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3995 GtkTextBuffer *buffer;
3996 GtkTextIter iter, break_pos, end_of_line;
3997 gchar *quote_str = NULL;
3999 gboolean wrap_quote = prefs_common.linewrap_quote;
4000 gboolean prev_autowrap = compose->autowrap;
4001 gint startq_offset = -1, noq_offset = -1;
4002 gint uri_start = -1, uri_stop = -1;
4003 gint nouri_start = -1, nouri_stop = -1;
4004 gint num_blocks = 0;
4005 gint quotelevel = -1;
4006 gboolean modified = force;
4007 gboolean removed = FALSE;
4008 gboolean modified_before_remove = FALSE;
4010 gboolean start = TRUE;
4011 gint itemized_len = 0, rem_item_len = 0;
4012 gchar *itemized_chars = NULL;
4013 gboolean item_continuation = FALSE;
4018 if (compose->draft_timeout_tag == -2) {
4022 compose->autowrap = FALSE;
4024 buffer = gtk_text_view_get_buffer(text);
4025 undo_wrapping(compose->undostruct, TRUE);
4030 mark = gtk_text_buffer_get_insert(buffer);
4031 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4035 if (compose->draft_timeout_tag == -2) {
4036 if (gtk_text_iter_ends_line(&iter)) {
4037 while (gtk_text_iter_ends_line(&iter) &&
4038 gtk_text_iter_forward_line(&iter))
4041 while (gtk_text_iter_backward_line(&iter)) {
4042 if (gtk_text_iter_ends_line(&iter)) {
4043 gtk_text_iter_forward_line(&iter);
4049 /* move to line start */
4050 gtk_text_iter_set_line_offset(&iter, 0);
4053 itemized_len = compose_itemized_length(buffer, &iter);
4055 if (!itemized_len) {
4056 itemized_len = compose_left_offset_length(buffer, &iter);
4057 item_continuation = TRUE;
4061 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4063 /* go until paragraph end (empty line) */
4064 while (start || !gtk_text_iter_ends_line(&iter)) {
4065 gchar *scanpos = NULL;
4066 /* parse table - in order of priority */
4068 const gchar *needle; /* token */
4070 /* token search function */
4071 gchar *(*search) (const gchar *haystack,
4072 const gchar *needle);
4073 /* part parsing function */
4074 gboolean (*parse) (const gchar *start,
4075 const gchar *scanpos,
4079 /* part to URI function */
4080 gchar *(*build_uri) (const gchar *bp,
4084 static struct table parser[] = {
4085 {"http://", strcasestr, get_uri_part, make_uri_string},
4086 {"https://", strcasestr, get_uri_part, make_uri_string},
4087 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4088 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4089 {"www.", strcasestr, get_uri_part, make_http_string},
4090 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4091 {"@", strcasestr, get_email_part, make_email_string}
4093 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4094 gint last_index = PARSE_ELEMS;
4096 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4100 if (!prev_autowrap && num_blocks == 0) {
4102 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4103 G_CALLBACK(text_inserted),
4106 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4109 uri_start = uri_stop = -1;
4111 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4114 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4115 if (startq_offset == -1)
4116 startq_offset = gtk_text_iter_get_offset(&iter);
4117 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4118 if (quotelevel > 2) {
4119 /* recycle colors */
4120 if (prefs_common.recycle_quote_colors)
4129 if (startq_offset == -1)
4130 noq_offset = gtk_text_iter_get_offset(&iter);
4134 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4137 if (gtk_text_iter_ends_line(&iter)) {
4139 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4140 prefs_common.linewrap_len,
4142 GtkTextIter prev, next, cur;
4143 if (prev_autowrap != FALSE || force) {
4144 compose->automatic_break = TRUE;
4146 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4147 compose->automatic_break = FALSE;
4148 if (itemized_len && compose->autoindent) {
4149 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4150 if (!item_continuation)
4151 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4153 } else if (quote_str && wrap_quote) {
4154 compose->automatic_break = TRUE;
4156 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4157 compose->automatic_break = FALSE;
4158 if (itemized_len && compose->autoindent) {
4159 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4160 if (!item_continuation)
4161 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4165 /* remove trailing spaces */
4167 rem_item_len = itemized_len;
4168 while (compose->autoindent && rem_item_len-- > 0)
4169 gtk_text_iter_backward_char(&cur);
4170 gtk_text_iter_backward_char(&cur);
4173 while (!gtk_text_iter_starts_line(&cur)) {
4176 gtk_text_iter_backward_char(&cur);
4177 wc = gtk_text_iter_get_char(&cur);
4178 if (!g_unichar_isspace(wc))
4182 if (!gtk_text_iter_equal(&prev, &next)) {
4183 gtk_text_buffer_delete(buffer, &prev, &next);
4185 gtk_text_iter_forward_char(&break_pos);
4189 gtk_text_buffer_insert(buffer, &break_pos,
4193 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4195 /* move iter to current line start */
4196 gtk_text_iter_set_line_offset(&iter, 0);
4203 /* move iter to next line start */
4209 if (!prev_autowrap && num_blocks > 0) {
4211 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4212 G_CALLBACK(text_inserted),
4216 while (!gtk_text_iter_ends_line(&end_of_line)) {
4217 gtk_text_iter_forward_char(&end_of_line);
4219 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4221 nouri_start = gtk_text_iter_get_offset(&iter);
4222 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4224 walk_pos = gtk_text_iter_get_offset(&iter);
4225 /* FIXME: this looks phony. scanning for anything in the parse table */
4226 for (n = 0; n < PARSE_ELEMS; n++) {
4229 tmp = parser[n].search(walk, parser[n].needle);
4231 if (scanpos == NULL || tmp < scanpos) {
4240 /* check if URI can be parsed */
4241 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4242 (const gchar **)&ep, FALSE)
4243 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4247 strlen(parser[last_index].needle);
4250 uri_start = walk_pos + (bp - o_walk);
4251 uri_stop = walk_pos + (ep - o_walk);
4255 gtk_text_iter_forward_line(&iter);
4258 if (startq_offset != -1) {
4259 GtkTextIter startquote, endquote;
4260 gtk_text_buffer_get_iter_at_offset(
4261 buffer, &startquote, startq_offset);
4264 switch (quotelevel) {
4266 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4267 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4268 gtk_text_buffer_apply_tag_by_name(
4269 buffer, "quote0", &startquote, &endquote);
4270 gtk_text_buffer_remove_tag_by_name(
4271 buffer, "quote1", &startquote, &endquote);
4272 gtk_text_buffer_remove_tag_by_name(
4273 buffer, "quote2", &startquote, &endquote);
4278 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4279 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4280 gtk_text_buffer_apply_tag_by_name(
4281 buffer, "quote1", &startquote, &endquote);
4282 gtk_text_buffer_remove_tag_by_name(
4283 buffer, "quote0", &startquote, &endquote);
4284 gtk_text_buffer_remove_tag_by_name(
4285 buffer, "quote2", &startquote, &endquote);
4290 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4291 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4292 gtk_text_buffer_apply_tag_by_name(
4293 buffer, "quote2", &startquote, &endquote);
4294 gtk_text_buffer_remove_tag_by_name(
4295 buffer, "quote0", &startquote, &endquote);
4296 gtk_text_buffer_remove_tag_by_name(
4297 buffer, "quote1", &startquote, &endquote);
4303 } else if (noq_offset != -1) {
4304 GtkTextIter startnoquote, endnoquote;
4305 gtk_text_buffer_get_iter_at_offset(
4306 buffer, &startnoquote, noq_offset);
4309 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4310 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4311 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4312 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4313 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4314 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4315 gtk_text_buffer_remove_tag_by_name(
4316 buffer, "quote0", &startnoquote, &endnoquote);
4317 gtk_text_buffer_remove_tag_by_name(
4318 buffer, "quote1", &startnoquote, &endnoquote);
4319 gtk_text_buffer_remove_tag_by_name(
4320 buffer, "quote2", &startnoquote, &endnoquote);
4326 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4327 GtkTextIter nouri_start_iter, nouri_end_iter;
4328 gtk_text_buffer_get_iter_at_offset(
4329 buffer, &nouri_start_iter, nouri_start);
4330 gtk_text_buffer_get_iter_at_offset(
4331 buffer, &nouri_end_iter, nouri_stop);
4332 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4333 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4334 gtk_text_buffer_remove_tag_by_name(
4335 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4336 modified_before_remove = modified;
4341 if (uri_start >= 0 && uri_stop > 0) {
4342 GtkTextIter uri_start_iter, uri_end_iter, back;
4343 gtk_text_buffer_get_iter_at_offset(
4344 buffer, &uri_start_iter, uri_start);
4345 gtk_text_buffer_get_iter_at_offset(
4346 buffer, &uri_end_iter, uri_stop);
4347 back = uri_end_iter;
4348 gtk_text_iter_backward_char(&back);
4349 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4350 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4351 gtk_text_buffer_apply_tag_by_name(
4352 buffer, "link", &uri_start_iter, &uri_end_iter);
4354 if (removed && !modified_before_remove) {
4360 debug_print("not modified, out after %d lines\n", lines);
4364 debug_print("modified, out after %d lines\n", lines);
4366 g_free(itemized_chars);
4369 undo_wrapping(compose->undostruct, FALSE);
4370 compose->autowrap = prev_autowrap;
4375 void compose_action_cb(void *data)
4377 Compose *compose = (Compose *)data;
4378 compose_wrap_all(compose);
4381 static void compose_wrap_all(Compose *compose)
4383 compose_wrap_all_full(compose, FALSE);
4386 static void compose_wrap_all_full(Compose *compose, gboolean force)
4388 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4389 GtkTextBuffer *buffer;
4391 gboolean modified = TRUE;
4393 buffer = gtk_text_view_get_buffer(text);
4395 gtk_text_buffer_get_start_iter(buffer, &iter);
4396 while (!gtk_text_iter_is_end(&iter) && modified)
4397 modified = compose_beautify_paragraph(compose, &iter, force);
4401 static void compose_set_title(Compose *compose)
4407 edited = compose->modified ? _(" [Edited]") : "";
4409 subject = gtk_editable_get_chars(
4410 GTK_EDITABLE(compose->subject_entry), 0, -1);
4412 #ifndef GENERIC_UMPC
4413 if (subject && strlen(subject))
4414 str = g_strdup_printf(_("%s - Compose message%s"),
4417 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4419 str = g_strdup(_("Compose message"));
4422 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4428 * compose_current_mail_account:
4430 * Find a current mail account (the currently selected account, or the
4431 * default account, if a news account is currently selected). If a
4432 * mail account cannot be found, display an error message.
4434 * Return value: Mail account, or NULL if not found.
4436 static PrefsAccount *
4437 compose_current_mail_account(void)
4441 if (cur_account && cur_account->protocol != A_NNTP)
4444 ac = account_get_default();
4445 if (!ac || ac->protocol == A_NNTP) {
4446 alertpanel_error(_("Account for sending mail is not specified.\n"
4447 "Please select a mail account before sending."));
4454 #define QUOTE_IF_REQUIRED(out, str) \
4456 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4460 len = strlen(str) + 3; \
4461 if ((__tmp = alloca(len)) == NULL) { \
4462 g_warning("can't allocate memory\n"); \
4463 g_string_free(header, TRUE); \
4466 g_snprintf(__tmp, len, "\"%s\"", str); \
4471 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4472 g_warning("can't allocate memory\n"); \
4473 g_string_free(header, TRUE); \
4476 strcpy(__tmp, str); \
4482 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4484 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4488 len = strlen(str) + 3; \
4489 if ((__tmp = alloca(len)) == NULL) { \
4490 g_warning("can't allocate memory\n"); \
4493 g_snprintf(__tmp, len, "\"%s\"", str); \
4498 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4499 g_warning("can't allocate memory\n"); \
4502 strcpy(__tmp, str); \
4508 static void compose_select_account(Compose *compose, PrefsAccount *account,
4513 g_return_if_fail(account != NULL);
4515 compose->account = account;
4517 if (account->name && *account->name) {
4519 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4520 from = g_strdup_printf("%s <%s>",
4521 buf, account->address);
4522 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4524 from = g_strdup_printf("<%s>",
4526 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4531 compose_set_title(compose);
4533 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4534 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4536 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4537 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4538 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4540 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4542 activate_privacy_system(compose, account, FALSE);
4544 if (!init && compose->mode != COMPOSE_REDIRECT) {
4545 undo_block(compose->undostruct);
4546 compose_insert_sig(compose, TRUE);
4547 undo_unblock(compose->undostruct);
4551 /* use account's dict info if set */
4552 if (compose->gtkaspell) {
4553 if (account->enable_default_dictionary)
4554 gtkaspell_change_dict(compose->gtkaspell,
4555 account->default_dictionary, FALSE);
4556 if (account->enable_default_alt_dictionary)
4557 gtkaspell_change_alt_dict(compose->gtkaspell,
4558 account->default_alt_dictionary);
4559 if (account->enable_default_dictionary
4560 || account->enable_default_alt_dictionary)
4561 compose_spell_menu_changed(compose);
4566 gboolean compose_check_for_valid_recipient(Compose *compose) {
4567 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4568 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4569 gboolean recipient_found = FALSE;
4573 /* free to and newsgroup list */
4574 slist_free_strings(compose->to_list);
4575 g_slist_free(compose->to_list);
4576 compose->to_list = NULL;
4578 slist_free_strings(compose->newsgroup_list);
4579 g_slist_free(compose->newsgroup_list);
4580 compose->newsgroup_list = NULL;
4582 /* search header entries for to and newsgroup entries */
4583 for (list = compose->header_list; list; list = list->next) {
4586 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4587 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4590 if (entry[0] != '\0') {
4591 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4592 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4593 compose->to_list = address_list_append(compose->to_list, entry);
4594 recipient_found = TRUE;
4597 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4598 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4599 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4600 recipient_found = TRUE;
4607 return recipient_found;
4610 static gboolean compose_check_for_set_recipients(Compose *compose)
4612 if (compose->account->set_autocc && compose->account->auto_cc) {
4613 gboolean found_other = FALSE;
4615 /* search header entries for to and newsgroup entries */
4616 for (list = compose->header_list; list; list = list->next) {
4619 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4620 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4623 if (strcmp(entry, compose->account->auto_cc)
4624 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4634 if (compose->batch) {
4635 gtk_widget_show_all(compose->window);
4637 aval = alertpanel(_("Send"),
4638 _("The only recipient is the default CC address. Send anyway?"),
4639 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4640 if (aval != G_ALERTALTERNATE)
4644 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4645 gboolean found_other = FALSE;
4647 /* search header entries for to and newsgroup entries */
4648 for (list = compose->header_list; list; list = list->next) {
4651 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4652 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4655 if (strcmp(entry, compose->account->auto_bcc)
4656 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4666 if (compose->batch) {
4667 gtk_widget_show_all(compose->window);
4669 aval = alertpanel(_("Send"),
4670 _("The only recipient is the default BCC address. Send anyway?"),
4671 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4672 if (aval != G_ALERTALTERNATE)
4679 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4683 if (compose_check_for_valid_recipient(compose) == FALSE) {
4684 if (compose->batch) {
4685 gtk_widget_show_all(compose->window);
4687 alertpanel_error(_("Recipient is not specified."));
4691 if (compose_check_for_set_recipients(compose) == FALSE) {
4695 if (!compose->batch) {
4696 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4697 if (*str == '\0' && check_everything == TRUE &&
4698 compose->mode != COMPOSE_REDIRECT) {
4700 gchar *button_label;
4703 if (compose->sending)
4704 button_label = _("+_Send");
4706 button_label = _("+_Queue");
4707 message = g_strdup_printf(_("Subject is empty. %s"),
4708 compose->sending?_("Send it anyway?"):
4709 _("Queue it anyway?"));
4711 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4712 GTK_STOCK_CANCEL, button_label, NULL);
4714 if (aval != G_ALERTALTERNATE)
4719 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4725 gint compose_send(Compose *compose)
4728 FolderItem *folder = NULL;
4730 gchar *msgpath = NULL;
4731 gboolean discard_window = FALSE;
4732 gchar *errstr = NULL;
4733 gchar *tmsgid = NULL;
4734 MainWindow *mainwin = mainwindow_get_mainwindow();
4735 gboolean queued_removed = FALSE;
4737 if (prefs_common.send_dialog_invisible
4738 || compose->batch == TRUE)
4739 discard_window = TRUE;
4741 compose_allow_user_actions (compose, FALSE);
4742 compose->sending = TRUE;
4744 if (compose_check_entries(compose, TRUE) == FALSE) {
4745 if (compose->batch) {
4746 gtk_widget_show_all(compose->window);
4752 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4755 if (compose->batch) {
4756 gtk_widget_show_all(compose->window);
4759 alertpanel_error(_("Could not queue message for sending:\n\n"
4760 "Charset conversion failed."));
4761 } else if (val == -5) {
4762 alertpanel_error(_("Could not queue message for sending:\n\n"
4763 "Couldn't get recipient encryption key."));
4764 } else if (val == -6) {
4766 } else if (val == -3) {
4767 if (privacy_peek_error())
4768 alertpanel_error(_("Could not queue message for sending:\n\n"
4769 "Signature failed: %s"), privacy_get_error());
4770 } else if (val == -2 && errno != 0) {
4771 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4773 alertpanel_error(_("Could not queue message for sending."));
4778 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4779 if (discard_window) {
4780 compose->sending = FALSE;
4781 compose_close(compose);
4782 /* No more compose access in the normal codepath
4783 * after this point! */
4788 alertpanel_error(_("The message was queued but could not be "
4789 "sent.\nUse \"Send queued messages\" from "
4790 "the main window to retry."));
4791 if (!discard_window) {
4798 if (msgpath == NULL) {
4799 msgpath = folder_item_fetch_msg(folder, msgnum);
4800 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4803 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4804 claws_unlink(msgpath);
4807 if (!discard_window) {
4809 if (!queued_removed)
4810 folder_item_remove_msg(folder, msgnum);
4811 folder_item_scan(folder);
4813 /* make sure we delete that */
4814 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4816 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4817 folder_item_remove_msg(folder, tmp->msgnum);
4818 procmsg_msginfo_free(tmp);
4825 if (!queued_removed)
4826 folder_item_remove_msg(folder, msgnum);
4827 folder_item_scan(folder);
4829 /* make sure we delete that */
4830 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4832 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4833 folder_item_remove_msg(folder, tmp->msgnum);
4834 procmsg_msginfo_free(tmp);
4837 if (!discard_window) {
4838 compose->sending = FALSE;
4839 compose_allow_user_actions (compose, TRUE);
4840 compose_close(compose);
4844 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4845 "the main window to retry."), errstr);
4848 alertpanel_error_log(_("The message was queued but could not be "
4849 "sent.\nUse \"Send queued messages\" from "
4850 "the main window to retry."));
4852 if (!discard_window) {
4861 toolbar_main_set_sensitive(mainwin);
4862 main_window_set_menu_sensitive(mainwin);
4868 compose_allow_user_actions (compose, TRUE);
4869 compose->sending = FALSE;
4870 compose->modified = TRUE;
4871 toolbar_main_set_sensitive(mainwin);
4872 main_window_set_menu_sensitive(mainwin);
4877 static gboolean compose_use_attach(Compose *compose)
4879 GtkTreeModel *model = gtk_tree_view_get_model
4880 (GTK_TREE_VIEW(compose->attach_clist));
4881 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4884 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4887 gchar buf[BUFFSIZE];
4889 gboolean first_to_address;
4890 gboolean first_cc_address;
4892 ComposeHeaderEntry *headerentry;
4893 const gchar *headerentryname;
4894 const gchar *cc_hdr;
4895 const gchar *to_hdr;
4896 gboolean err = FALSE;
4898 debug_print("Writing redirect header\n");
4900 cc_hdr = prefs_common_translated_header_name("Cc:");
4901 to_hdr = prefs_common_translated_header_name("To:");
4903 first_to_address = TRUE;
4904 for (list = compose->header_list; list; list = list->next) {
4905 headerentry = ((ComposeHeaderEntry *)list->data);
4906 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4908 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4909 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4910 Xstrdup_a(str, entstr, return -1);
4912 if (str[0] != '\0') {
4913 compose_convert_header
4914 (compose, buf, sizeof(buf), str,
4915 strlen("Resent-To") + 2, TRUE);
4917 if (first_to_address) {
4918 err |= (fprintf(fp, "Resent-To: ") < 0);
4919 first_to_address = FALSE;
4921 err |= (fprintf(fp, ",") < 0);
4923 err |= (fprintf(fp, "%s", buf) < 0);
4927 if (!first_to_address) {
4928 err |= (fprintf(fp, "\n") < 0);
4931 first_cc_address = TRUE;
4932 for (list = compose->header_list; list; list = list->next) {
4933 headerentry = ((ComposeHeaderEntry *)list->data);
4934 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4936 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4937 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4938 Xstrdup_a(str, strg, return -1);
4940 if (str[0] != '\0') {
4941 compose_convert_header
4942 (compose, buf, sizeof(buf), str,
4943 strlen("Resent-Cc") + 2, TRUE);
4945 if (first_cc_address) {
4946 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4947 first_cc_address = FALSE;
4949 err |= (fprintf(fp, ",") < 0);
4951 err |= (fprintf(fp, "%s", buf) < 0);
4955 if (!first_cc_address) {
4956 err |= (fprintf(fp, "\n") < 0);
4959 return (err ? -1:0);
4962 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4964 gchar buf[BUFFSIZE];
4966 const gchar *entstr;
4967 /* struct utsname utsbuf; */
4968 gboolean err = FALSE;
4970 g_return_val_if_fail(fp != NULL, -1);
4971 g_return_val_if_fail(compose->account != NULL, -1);
4972 g_return_val_if_fail(compose->account->address != NULL, -1);
4975 get_rfc822_date(buf, sizeof(buf));
4976 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4979 if (compose->account->name && *compose->account->name) {
4980 compose_convert_header
4981 (compose, buf, sizeof(buf), compose->account->name,
4982 strlen("From: "), TRUE);
4983 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4984 buf, compose->account->address) < 0);
4986 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4989 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4990 if (*entstr != '\0') {
4991 Xstrdup_a(str, entstr, return -1);
4994 compose_convert_header(compose, buf, sizeof(buf), str,
4995 strlen("Subject: "), FALSE);
4996 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5000 /* Resent-Message-ID */
5001 if (compose->account->set_domain && compose->account->domain) {
5002 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5003 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5004 g_snprintf(buf, sizeof(buf), "%s",
5005 strchr(compose->account->address, '@') ?
5006 strchr(compose->account->address, '@')+1 :
5007 compose->account->address);
5009 g_snprintf(buf, sizeof(buf), "%s", "");
5012 if (compose->account->gen_msgid) {
5013 generate_msgid(buf, sizeof(buf));
5014 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5015 compose->msgid = g_strdup(buf);
5017 compose->msgid = NULL;
5020 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5023 /* separator between header and body */
5024 err |= (fputs("\n", fp) == EOF);
5026 return (err ? -1:0);
5029 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5033 gchar buf[BUFFSIZE];
5035 gboolean skip = FALSE;
5036 gboolean err = FALSE;
5037 gchar *not_included[]={
5038 "Return-Path:", "Delivered-To:", "Received:",
5039 "Subject:", "X-UIDL:", "AF:",
5040 "NF:", "PS:", "SRH:",
5041 "SFN:", "DSR:", "MID:",
5042 "CFG:", "PT:", "S:",
5043 "RQ:", "SSV:", "NSV:",
5044 "SSH:", "R:", "MAID:",
5045 "NAID:", "RMID:", "FMID:",
5046 "SCF:", "RRCPT:", "NG:",
5047 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5048 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5049 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5050 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5051 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5054 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5055 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5059 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5061 for (i = 0; not_included[i] != NULL; i++) {
5062 if (g_ascii_strncasecmp(buf, not_included[i],
5063 strlen(not_included[i])) == 0) {
5070 if (fputs(buf, fdest) == -1)
5073 if (!prefs_common.redirect_keep_from) {
5074 if (g_ascii_strncasecmp(buf, "From:",
5075 strlen("From:")) == 0) {
5076 err |= (fputs(" (by way of ", fdest) == EOF);
5077 if (compose->account->name
5078 && *compose->account->name) {
5079 compose_convert_header
5080 (compose, buf, sizeof(buf),
5081 compose->account->name,
5084 err |= (fprintf(fdest, "%s <%s>",
5086 compose->account->address) < 0);
5088 err |= (fprintf(fdest, "%s",
5089 compose->account->address) < 0);
5090 err |= (fputs(")", fdest) == EOF);
5094 if (fputs("\n", fdest) == -1)
5101 if (compose_redirect_write_headers(compose, fdest))
5104 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5105 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5118 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5120 GtkTextBuffer *buffer;
5121 GtkTextIter start, end;
5124 const gchar *out_codeset;
5125 EncodingType encoding = ENC_UNKNOWN;
5126 MimeInfo *mimemsg, *mimetext;
5128 const gchar *src_codeset = CS_INTERNAL;
5130 if (action == COMPOSE_WRITE_FOR_SEND)
5131 attach_parts = TRUE;
5133 /* create message MimeInfo */
5134 mimemsg = procmime_mimeinfo_new();
5135 mimemsg->type = MIMETYPE_MESSAGE;
5136 mimemsg->subtype = g_strdup("rfc822");
5137 mimemsg->content = MIMECONTENT_MEM;
5138 mimemsg->tmp = TRUE; /* must free content later */
5139 mimemsg->data.mem = compose_get_header(compose);
5141 /* Create text part MimeInfo */
5142 /* get all composed text */
5143 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5144 gtk_text_buffer_get_start_iter(buffer, &start);
5145 gtk_text_buffer_get_end_iter(buffer, &end);
5146 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5148 out_codeset = conv_get_charset_str(compose->out_encoding);
5150 if (!out_codeset && is_ascii_str(chars)) {
5151 out_codeset = CS_US_ASCII;
5152 } else if (prefs_common.outgoing_fallback_to_ascii &&
5153 is_ascii_str(chars)) {
5154 out_codeset = CS_US_ASCII;
5155 encoding = ENC_7BIT;
5159 gchar *test_conv_global_out = NULL;
5160 gchar *test_conv_reply = NULL;
5162 /* automatic mode. be automatic. */
5163 codeconv_set_strict(TRUE);
5165 out_codeset = conv_get_outgoing_charset_str();
5167 debug_print("trying to convert to %s\n", out_codeset);
5168 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5171 if (!test_conv_global_out && compose->orig_charset
5172 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5173 out_codeset = compose->orig_charset;
5174 debug_print("failure; trying to convert to %s\n", out_codeset);
5175 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5178 if (!test_conv_global_out && !test_conv_reply) {
5180 out_codeset = CS_INTERNAL;
5181 debug_print("failure; finally using %s\n", out_codeset);
5183 g_free(test_conv_global_out);
5184 g_free(test_conv_reply);
5185 codeconv_set_strict(FALSE);
5188 if (encoding == ENC_UNKNOWN) {
5189 if (prefs_common.encoding_method == CTE_BASE64)
5190 encoding = ENC_BASE64;
5191 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5192 encoding = ENC_QUOTED_PRINTABLE;
5193 else if (prefs_common.encoding_method == CTE_8BIT)
5194 encoding = ENC_8BIT;
5196 encoding = procmime_get_encoding_for_charset(out_codeset);
5199 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5200 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5202 if (action == COMPOSE_WRITE_FOR_SEND) {
5203 codeconv_set_strict(TRUE);
5204 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5205 codeconv_set_strict(FALSE);
5211 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5212 "to the specified %s charset.\n"
5213 "Send it as %s?"), out_codeset, src_codeset);
5214 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5215 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5218 if (aval != G_ALERTALTERNATE) {
5223 out_codeset = src_codeset;
5229 out_codeset = src_codeset;
5234 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5235 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5236 strstr(buf, "\nFrom ") != NULL) {
5237 encoding = ENC_QUOTED_PRINTABLE;
5241 mimetext = procmime_mimeinfo_new();
5242 mimetext->content = MIMECONTENT_MEM;
5243 mimetext->tmp = TRUE; /* must free content later */
5244 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5245 * and free the data, which we need later. */
5246 mimetext->data.mem = g_strdup(buf);
5247 mimetext->type = MIMETYPE_TEXT;
5248 mimetext->subtype = g_strdup("plain");
5249 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5250 g_strdup(out_codeset));
5252 /* protect trailing spaces when signing message */
5253 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5254 privacy_system_can_sign(compose->privacy_system)) {
5255 encoding = ENC_QUOTED_PRINTABLE;
5258 debug_print("main text: %zd bytes encoded as %s in %d\n",
5259 strlen(buf), out_codeset, encoding);
5261 /* check for line length limit */
5262 if (action == COMPOSE_WRITE_FOR_SEND &&
5263 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5264 check_line_length(buf, 1000, &line) < 0) {
5268 msg = g_strdup_printf
5269 (_("Line %d exceeds the line length limit (998 bytes).\n"
5270 "The contents of the message might be broken on the way to the delivery.\n"
5272 "Send it anyway?"), line + 1);
5273 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5275 if (aval != G_ALERTALTERNATE) {
5281 if (encoding != ENC_UNKNOWN)
5282 procmime_encode_content(mimetext, encoding);
5284 /* append attachment parts */
5285 if (compose_use_attach(compose) && attach_parts) {
5286 MimeInfo *mimempart;
5287 gchar *boundary = NULL;
5288 mimempart = procmime_mimeinfo_new();
5289 mimempart->content = MIMECONTENT_EMPTY;
5290 mimempart->type = MIMETYPE_MULTIPART;
5291 mimempart->subtype = g_strdup("mixed");
5295 boundary = generate_mime_boundary(NULL);
5296 } while (strstr(buf, boundary) != NULL);
5298 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5301 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5303 g_node_append(mimempart->node, mimetext->node);
5304 g_node_append(mimemsg->node, mimempart->node);
5306 compose_add_attachments(compose, mimempart);
5308 g_node_append(mimemsg->node, mimetext->node);
5312 /* sign message if sending */
5313 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5314 privacy_system_can_sign(compose->privacy_system))
5315 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5318 procmime_write_mimeinfo(mimemsg, fp);
5320 procmime_mimeinfo_free_all(mimemsg);
5325 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5327 GtkTextBuffer *buffer;
5328 GtkTextIter start, end;
5333 if ((fp = g_fopen(file, "wb")) == NULL) {
5334 FILE_OP_ERROR(file, "fopen");
5338 /* chmod for security */
5339 if (change_file_mode_rw(fp, file) < 0) {
5340 FILE_OP_ERROR(file, "chmod");
5341 g_warning("can't change file mode\n");
5344 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5345 gtk_text_buffer_get_start_iter(buffer, &start);
5346 gtk_text_buffer_get_end_iter(buffer, &end);
5347 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5349 chars = conv_codeset_strdup
5350 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5353 if (!chars) return -1;
5356 len = strlen(chars);
5357 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5358 FILE_OP_ERROR(file, "fwrite");
5367 if (fclose(fp) == EOF) {
5368 FILE_OP_ERROR(file, "fclose");
5375 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5378 MsgInfo *msginfo = compose->targetinfo;
5380 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5381 if (!msginfo) return -1;
5383 if (!force && MSG_IS_LOCKED(msginfo->flags))
5386 item = msginfo->folder;
5387 g_return_val_if_fail(item != NULL, -1);
5389 if (procmsg_msg_exist(msginfo) &&
5390 (folder_has_parent_of_type(item, F_QUEUE) ||
5391 folder_has_parent_of_type(item, F_DRAFT)
5392 || msginfo == compose->autosaved_draft)) {
5393 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5394 g_warning("can't remove the old message\n");
5397 debug_print("removed reedit target %d\n", msginfo->msgnum);
5404 static void compose_remove_draft(Compose *compose)
5407 MsgInfo *msginfo = compose->targetinfo;
5408 drafts = account_get_special_folder(compose->account, F_DRAFT);
5410 if (procmsg_msg_exist(msginfo)) {
5411 folder_item_remove_msg(drafts, msginfo->msgnum);
5416 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5417 gboolean remove_reedit_target)
5419 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5422 static gboolean compose_warn_encryption(Compose *compose)
5424 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5425 AlertValue val = G_ALERTALTERNATE;
5427 if (warning == NULL)
5430 val = alertpanel_full(_("Encryption warning"), warning,
5431 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5432 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5433 if (val & G_ALERTDISABLE) {
5434 val &= ~G_ALERTDISABLE;
5435 if (val == G_ALERTALTERNATE)
5436 privacy_inhibit_encrypt_warning(compose->privacy_system,
5440 if (val == G_ALERTALTERNATE) {
5447 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5448 gchar **msgpath, gboolean check_subject,
5449 gboolean remove_reedit_target)
5456 static gboolean lock = FALSE;
5457 PrefsAccount *mailac = NULL, *newsac = NULL;
5458 gboolean err = FALSE;
5460 debug_print("queueing message...\n");
5461 g_return_val_if_fail(compose->account != NULL, -1);
5465 if (compose_check_entries(compose, check_subject) == FALSE) {
5467 if (compose->batch) {
5468 gtk_widget_show_all(compose->window);
5473 if (!compose->to_list && !compose->newsgroup_list) {
5474 g_warning("can't get recipient list.");
5479 if (compose->to_list) {
5480 if (compose->account->protocol != A_NNTP)
5481 mailac = compose->account;
5482 else if (cur_account && cur_account->protocol != A_NNTP)
5483 mailac = cur_account;
5484 else if (!(mailac = compose_current_mail_account())) {
5486 alertpanel_error(_("No account for sending mails available!"));
5491 if (compose->newsgroup_list) {
5492 if (compose->account->protocol == A_NNTP)
5493 newsac = compose->account;
5494 else if (!newsac->protocol != A_NNTP) {
5496 alertpanel_error(_("No account for posting news available!"));
5501 /* write queue header */
5502 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5503 G_DIR_SEPARATOR, compose, (guint) rand());
5504 debug_print("queuing to %s\n", tmp);
5505 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5506 FILE_OP_ERROR(tmp, "fopen");
5512 if (change_file_mode_rw(fp, tmp) < 0) {
5513 FILE_OP_ERROR(tmp, "chmod");
5514 g_warning("can't change file mode\n");
5517 /* queueing variables */
5518 err |= (fprintf(fp, "AF:\n") < 0);
5519 err |= (fprintf(fp, "NF:0\n") < 0);
5520 err |= (fprintf(fp, "PS:10\n") < 0);
5521 err |= (fprintf(fp, "SRH:1\n") < 0);
5522 err |= (fprintf(fp, "SFN:\n") < 0);
5523 err |= (fprintf(fp, "DSR:\n") < 0);
5525 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5527 err |= (fprintf(fp, "MID:\n") < 0);
5528 err |= (fprintf(fp, "CFG:\n") < 0);
5529 err |= (fprintf(fp, "PT:0\n") < 0);
5530 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5531 err |= (fprintf(fp, "RQ:\n") < 0);
5533 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5535 err |= (fprintf(fp, "SSV:\n") < 0);
5537 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5539 err |= (fprintf(fp, "NSV:\n") < 0);
5540 err |= (fprintf(fp, "SSH:\n") < 0);
5541 /* write recepient list */
5542 if (compose->to_list) {
5543 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5544 for (cur = compose->to_list->next; cur != NULL;
5546 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5547 err |= (fprintf(fp, "\n") < 0);
5549 /* write newsgroup list */
5550 if (compose->newsgroup_list) {
5551 err |= (fprintf(fp, "NG:") < 0);
5552 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5553 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5554 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5555 err |= (fprintf(fp, "\n") < 0);
5557 /* Sylpheed account IDs */
5559 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5561 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5564 if (compose->privacy_system != NULL) {
5565 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5566 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5567 if (compose->use_encryption) {
5569 if (!compose_warn_encryption(compose)) {
5576 if (mailac && mailac->encrypt_to_self) {
5577 GSList *tmp_list = g_slist_copy(compose->to_list);
5578 tmp_list = g_slist_append(tmp_list, compose->account->address);
5579 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5580 g_slist_free(tmp_list);
5582 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5584 if (encdata != NULL) {
5585 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5586 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5587 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5589 } /* else we finally dont want to encrypt */
5591 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5592 /* and if encdata was null, it means there's been a problem in
5604 /* Save copy folder */
5605 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5606 gchar *savefolderid;
5608 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5609 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5610 g_free(savefolderid);
5612 /* Save copy folder */
5613 if (compose->return_receipt) {
5614 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5616 /* Message-ID of message replying to */
5617 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5620 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5621 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5624 /* Message-ID of message forwarding to */
5625 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5628 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5629 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5633 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5634 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5636 /* end of headers */
5637 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5639 if (compose->redirect_filename != NULL) {
5640 if (compose_redirect_write_to_file(compose, fp) < 0) {
5649 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5654 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5658 g_warning("failed to write queue message\n");
5665 if (fclose(fp) == EOF) {
5666 FILE_OP_ERROR(tmp, "fclose");
5673 if (item && *item) {
5676 queue = account_get_special_folder(compose->account, F_QUEUE);
5679 g_warning("can't find queue folder\n");
5685 folder_item_scan(queue);
5686 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5687 g_warning("can't queue the message\n");
5694 if (msgpath == NULL) {
5700 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5701 compose_remove_reedit_target(compose, FALSE);
5704 if ((msgnum != NULL) && (item != NULL)) {
5712 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5715 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5717 struct stat statbuf;
5718 gchar *type, *subtype;
5719 GtkTreeModel *model;
5722 model = gtk_tree_view_get_model(tree_view);
5724 if (!gtk_tree_model_get_iter_first(model, &iter))
5727 gtk_tree_model_get(model, &iter,
5731 mimepart = procmime_mimeinfo_new();
5732 mimepart->content = MIMECONTENT_FILE;
5733 mimepart->data.filename = g_strdup(ainfo->file);
5734 mimepart->tmp = FALSE; /* or we destroy our attachment */
5735 mimepart->offset = 0;
5737 stat(ainfo->file, &statbuf);
5738 mimepart->length = statbuf.st_size;
5740 type = g_strdup(ainfo->content_type);
5742 if (!strchr(type, '/')) {
5744 type = g_strdup("application/octet-stream");
5747 subtype = strchr(type, '/') + 1;
5748 *(subtype - 1) = '\0';
5749 mimepart->type = procmime_get_media_type(type);
5750 mimepart->subtype = g_strdup(subtype);
5753 if (mimepart->type == MIMETYPE_MESSAGE &&
5754 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5755 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5758 g_hash_table_insert(mimepart->typeparameters,
5759 g_strdup("name"), g_strdup(ainfo->name));
5760 g_hash_table_insert(mimepart->dispositionparameters,
5761 g_strdup("filename"), g_strdup(ainfo->name));
5762 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5766 if (compose->use_signing) {
5767 if (ainfo->encoding == ENC_7BIT)
5768 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5769 else if (ainfo->encoding == ENC_8BIT)
5770 ainfo->encoding = ENC_BASE64;
5773 procmime_encode_content(mimepart, ainfo->encoding);
5775 g_node_append(parent->node, mimepart->node);
5776 } while (gtk_tree_model_iter_next(model, &iter));
5779 #define IS_IN_CUSTOM_HEADER(header) \
5780 (compose->account->add_customhdr && \
5781 custom_header_find(compose->account->customhdr_list, header) != NULL)
5783 static void compose_add_headerfield_from_headerlist(Compose *compose,
5785 const gchar *fieldname,
5786 const gchar *seperator)
5788 gchar *str, *fieldname_w_colon;
5789 gboolean add_field = FALSE;
5791 ComposeHeaderEntry *headerentry;
5792 const gchar *headerentryname;
5793 const gchar *trans_fieldname;
5796 if (IS_IN_CUSTOM_HEADER(fieldname))
5799 debug_print("Adding %s-fields\n", fieldname);
5801 fieldstr = g_string_sized_new(64);
5803 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5804 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5806 for (list = compose->header_list; list; list = list->next) {
5807 headerentry = ((ComposeHeaderEntry *)list->data);
5808 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5810 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5811 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5813 if (str[0] != '\0') {
5815 g_string_append(fieldstr, seperator);
5816 g_string_append(fieldstr, str);
5825 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5826 compose_convert_header
5827 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5828 strlen(fieldname) + 2, TRUE);
5829 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5833 g_free(fieldname_w_colon);
5834 g_string_free(fieldstr, TRUE);
5839 static gchar *compose_get_header(Compose *compose)
5841 gchar buf[BUFFSIZE];
5842 const gchar *entry_str;
5846 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5848 gchar *from_name = NULL, *from_address = NULL;
5851 g_return_val_if_fail(compose->account != NULL, NULL);
5852 g_return_val_if_fail(compose->account->address != NULL, NULL);
5854 header = g_string_sized_new(64);
5857 get_rfc822_date(buf, sizeof(buf));
5858 g_string_append_printf(header, "Date: %s\n", buf);
5862 if (compose->account->name && *compose->account->name) {
5864 QUOTE_IF_REQUIRED(buf, compose->account->name);
5865 tmp = g_strdup_printf("%s <%s>",
5866 buf, compose->account->address);
5868 tmp = g_strdup_printf("%s",
5869 compose->account->address);
5871 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5872 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5874 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5875 from_address = g_strdup(compose->account->address);
5877 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5878 /* extract name and address */
5879 if (strstr(spec, " <") && strstr(spec, ">")) {
5880 from_address = g_strdup(strrchr(spec, '<')+1);
5881 *(strrchr(from_address, '>')) = '\0';
5882 from_name = g_strdup(spec);
5883 *(strrchr(from_name, '<')) = '\0';
5886 from_address = g_strdup(spec);
5893 if (from_name && *from_name) {
5894 compose_convert_header
5895 (compose, buf, sizeof(buf), from_name,
5896 strlen("From: "), TRUE);
5897 QUOTE_IF_REQUIRED(name, buf);
5899 g_string_append_printf(header, "From: %s <%s>\n",
5900 name, from_address);
5902 g_string_append_printf(header, "From: %s\n", from_address);
5905 g_free(from_address);
5908 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5911 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5914 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5917 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5920 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5922 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5925 compose_convert_header(compose, buf, sizeof(buf), str,
5926 strlen("Subject: "), FALSE);
5927 g_string_append_printf(header, "Subject: %s\n", buf);
5933 if (compose->account->set_domain && compose->account->domain) {
5934 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5935 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5936 g_snprintf(buf, sizeof(buf), "%s",
5937 strchr(compose->account->address, '@') ?
5938 strchr(compose->account->address, '@')+1 :
5939 compose->account->address);
5941 g_snprintf(buf, sizeof(buf), "%s", "");
5944 if (compose->account->gen_msgid) {
5945 generate_msgid(buf, sizeof(buf));
5946 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5947 compose->msgid = g_strdup(buf);
5949 compose->msgid = NULL;
5952 if (compose->remove_references == FALSE) {
5954 if (compose->inreplyto && compose->to_list)
5955 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5958 if (compose->references)
5959 g_string_append_printf(header, "References: %s\n", compose->references);
5963 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5966 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5969 if (compose->account->organization &&
5970 strlen(compose->account->organization) &&
5971 !IS_IN_CUSTOM_HEADER("Organization")) {
5972 compose_convert_header(compose, buf, sizeof(buf),
5973 compose->account->organization,
5974 strlen("Organization: "), FALSE);
5975 g_string_append_printf(header, "Organization: %s\n", buf);
5978 /* Program version and system info */
5979 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5980 !compose->newsgroup_list) {
5981 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5983 gtk_major_version, gtk_minor_version, gtk_micro_version,
5986 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5987 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5989 gtk_major_version, gtk_minor_version, gtk_micro_version,
5993 /* custom headers */
5994 if (compose->account->add_customhdr) {
5997 for (cur = compose->account->customhdr_list; cur != NULL;
5999 CustomHeader *chdr = (CustomHeader *)cur->data;
6001 if (custom_header_is_allowed(chdr->name)) {
6002 compose_convert_header
6003 (compose, buf, sizeof(buf),
6004 chdr->value ? chdr->value : "",
6005 strlen(chdr->name) + 2, FALSE);
6006 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6011 /* Automatic Faces and X-Faces */
6012 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6013 g_string_append_printf(header, "X-Face: %s\n", buf);
6015 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6016 g_string_append_printf(header, "X-Face: %s\n", buf);
6018 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6019 g_string_append_printf(header, "Face: %s\n", buf);
6021 else if (get_default_face (buf, sizeof(buf)) == 0) {
6022 g_string_append_printf(header, "Face: %s\n", buf);
6026 switch (compose->priority) {
6027 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6028 "X-Priority: 1 (Highest)\n");
6030 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6031 "X-Priority: 2 (High)\n");
6033 case PRIORITY_NORMAL: break;
6034 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6035 "X-Priority: 4 (Low)\n");
6037 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6038 "X-Priority: 5 (Lowest)\n");
6040 default: debug_print("compose: priority unknown : %d\n",
6044 /* Request Return Receipt */
6045 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6046 if (compose->return_receipt) {
6047 if (compose->account->name
6048 && *compose->account->name) {
6049 compose_convert_header(compose, buf, sizeof(buf),
6050 compose->account->name,
6051 strlen("Disposition-Notification-To: "),
6053 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6055 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6059 /* get special headers */
6060 for (list = compose->header_list; list; list = list->next) {
6061 ComposeHeaderEntry *headerentry;
6064 gchar *headername_wcolon;
6065 const gchar *headername_trans;
6068 gboolean standard_header = FALSE;
6070 headerentry = ((ComposeHeaderEntry *)list->data);
6072 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6074 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6079 if (!strstr(tmp, ":")) {
6080 headername_wcolon = g_strconcat(tmp, ":", NULL);
6081 headername = g_strdup(tmp);
6083 headername_wcolon = g_strdup(tmp);
6084 headername = g_strdup(strtok(tmp, ":"));
6088 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6089 Xstrdup_a(headervalue, entry_str, return NULL);
6090 subst_char(headervalue, '\r', ' ');
6091 subst_char(headervalue, '\n', ' ');
6092 string = std_headers;
6093 while (*string != NULL) {
6094 headername_trans = prefs_common_translated_header_name(*string);
6095 if (!strcmp(headername_trans, headername_wcolon))
6096 standard_header = TRUE;
6099 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6100 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6103 g_free(headername_wcolon);
6107 g_string_free(header, FALSE);
6112 #undef IS_IN_CUSTOM_HEADER
6114 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6115 gint header_len, gboolean addr_field)
6117 gchar *tmpstr = NULL;
6118 const gchar *out_codeset = NULL;
6120 g_return_if_fail(src != NULL);
6121 g_return_if_fail(dest != NULL);
6123 if (len < 1) return;
6125 tmpstr = g_strdup(src);
6127 subst_char(tmpstr, '\n', ' ');
6128 subst_char(tmpstr, '\r', ' ');
6131 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6132 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6133 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6138 codeconv_set_strict(TRUE);
6139 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6140 conv_get_charset_str(compose->out_encoding));
6141 codeconv_set_strict(FALSE);
6143 if (!dest || *dest == '\0') {
6144 gchar *test_conv_global_out = NULL;
6145 gchar *test_conv_reply = NULL;
6147 /* automatic mode. be automatic. */
6148 codeconv_set_strict(TRUE);
6150 out_codeset = conv_get_outgoing_charset_str();
6152 debug_print("trying to convert to %s\n", out_codeset);
6153 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6156 if (!test_conv_global_out && compose->orig_charset
6157 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6158 out_codeset = compose->orig_charset;
6159 debug_print("failure; trying to convert to %s\n", out_codeset);
6160 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6163 if (!test_conv_global_out && !test_conv_reply) {
6165 out_codeset = CS_INTERNAL;
6166 debug_print("finally using %s\n", out_codeset);
6168 g_free(test_conv_global_out);
6169 g_free(test_conv_reply);
6170 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6172 codeconv_set_strict(FALSE);
6177 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6181 g_return_if_fail(user_data != NULL);
6183 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6184 g_strstrip(address);
6185 if (*address != '\0') {
6186 gchar *name = procheader_get_fromname(address);
6187 extract_address(address);
6188 addressbook_add_contact(name, address, NULL, NULL);
6193 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6195 GtkWidget *menuitem;
6198 g_return_if_fail(menu != NULL);
6199 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6201 menuitem = gtk_separator_menu_item_new();
6202 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6203 gtk_widget_show(menuitem);
6205 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6206 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6208 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6209 g_strstrip(address);
6210 if (*address == '\0') {
6211 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6214 g_signal_connect(G_OBJECT(menuitem), "activate",
6215 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6216 gtk_widget_show(menuitem);
6219 static void compose_create_header_entry(Compose *compose)
6221 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6226 const gchar *header = NULL;
6227 ComposeHeaderEntry *headerentry;
6228 gboolean standard_header = FALSE;
6229 #if !(GTK_CHECK_VERSION(2,12,0))
6230 GtkTooltips *tips = compose->tooltips;
6233 headerentry = g_new0(ComposeHeaderEntry, 1);
6236 combo = gtk_combo_box_entry_new_text();
6238 while(*string != NULL) {
6239 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6240 (gchar*)prefs_common_translated_header_name(*string));
6243 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6244 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6245 G_CALLBACK(compose_grab_focus_cb), compose);
6246 gtk_widget_show(combo);
6247 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6248 compose->header_nextrow, compose->header_nextrow+1,
6249 GTK_SHRINK, GTK_FILL, 0, 0);
6250 if (compose->header_last) {
6251 const gchar *last_header_entry = gtk_entry_get_text(
6252 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6254 while (*string != NULL) {
6255 if (!strcmp(*string, last_header_entry))
6256 standard_header = TRUE;
6259 if (standard_header)
6260 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6262 if (!compose->header_last || !standard_header) {
6263 switch(compose->account->protocol) {
6265 header = prefs_common_translated_header_name("Newsgroups:");
6268 header = prefs_common_translated_header_name("To:");
6273 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6275 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6276 G_CALLBACK(compose_grab_focus_cb), compose);
6279 entry = gtk_entry_new();
6280 gtk_widget_show(entry);
6281 CLAWS_SET_TIP(entry,
6282 _("Use <tab> to autocomplete from addressbook"));
6283 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6284 compose->header_nextrow, compose->header_nextrow+1,
6285 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6287 g_signal_connect(G_OBJECT(entry), "key-press-event",
6288 G_CALLBACK(compose_headerentry_key_press_event_cb),
6290 g_signal_connect(G_OBJECT(entry), "changed",
6291 G_CALLBACK(compose_headerentry_changed_cb),
6293 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6294 G_CALLBACK(compose_grab_focus_cb), compose);
6297 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6298 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6299 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6300 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6301 G_CALLBACK(compose_header_drag_received_cb),
6303 g_signal_connect(G_OBJECT(entry), "drag-drop",
6304 G_CALLBACK(compose_drag_drop),
6306 g_signal_connect(G_OBJECT(entry), "populate-popup",
6307 G_CALLBACK(compose_entry_popup_extend),
6310 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6312 headerentry->compose = compose;
6313 headerentry->combo = combo;
6314 headerentry->entry = entry;
6315 headerentry->headernum = compose->header_nextrow;
6317 compose->header_nextrow++;
6318 compose->header_last = headerentry;
6319 compose->header_list =
6320 g_slist_append(compose->header_list,
6324 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6326 ComposeHeaderEntry *last_header;
6328 last_header = compose->header_last;
6330 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6331 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6334 static void compose_remove_header_entries(Compose *compose)
6337 for (list = compose->header_list; list; list = list->next) {
6338 ComposeHeaderEntry *headerentry =
6339 (ComposeHeaderEntry *)list->data;
6340 gtk_widget_destroy(headerentry->combo);
6341 gtk_widget_destroy(headerentry->entry);
6342 g_free(headerentry);
6344 compose->header_last = NULL;
6345 g_slist_free(compose->header_list);
6346 compose->header_list = NULL;
6347 compose->header_nextrow = 1;
6348 compose_create_header_entry(compose);
6351 static GtkWidget *compose_create_header(Compose *compose)
6353 GtkWidget *from_optmenu_hbox;
6354 GtkWidget *header_scrolledwin;
6355 GtkWidget *header_table;
6359 /* header labels and entries */
6360 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6361 gtk_widget_show(header_scrolledwin);
6362 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6364 header_table = gtk_table_new(2, 2, FALSE);
6365 gtk_widget_show(header_table);
6366 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6367 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6368 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6371 /* option menu for selecting accounts */
6372 from_optmenu_hbox = compose_account_option_menu_create(compose);
6373 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6374 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6377 compose->header_table = header_table;
6378 compose->header_list = NULL;
6379 compose->header_nextrow = count;
6381 compose_create_header_entry(compose);
6383 compose->table = NULL;
6385 return header_scrolledwin ;
6388 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6390 Compose *compose = (Compose *)data;
6391 GdkEventButton event;
6394 event.time = gtk_get_current_event_time();
6396 return attach_button_pressed(compose->attach_clist, &event, compose);
6399 static GtkWidget *compose_create_attach(Compose *compose)
6401 GtkWidget *attach_scrwin;
6402 GtkWidget *attach_clist;
6404 GtkListStore *store;
6405 GtkCellRenderer *renderer;
6406 GtkTreeViewColumn *column;
6407 GtkTreeSelection *selection;
6409 /* attachment list */
6410 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6411 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6412 GTK_POLICY_AUTOMATIC,
6413 GTK_POLICY_AUTOMATIC);
6414 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6416 store = gtk_list_store_new(N_ATTACH_COLS,
6421 G_TYPE_AUTO_POINTER,
6423 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6424 (GTK_TREE_MODEL(store)));
6425 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6426 g_object_unref(store);
6428 renderer = gtk_cell_renderer_text_new();
6429 column = gtk_tree_view_column_new_with_attributes
6430 (_("Mime type"), renderer, "text",
6431 COL_MIMETYPE, NULL);
6432 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6434 renderer = gtk_cell_renderer_text_new();
6435 column = gtk_tree_view_column_new_with_attributes
6436 (_("Size"), renderer, "text",
6438 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6440 renderer = gtk_cell_renderer_text_new();
6441 column = gtk_tree_view_column_new_with_attributes
6442 (_("Name"), renderer, "text",
6444 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6446 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6447 prefs_common.use_stripes_everywhere);
6448 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6449 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6451 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6452 G_CALLBACK(attach_selected), compose);
6453 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6454 G_CALLBACK(attach_button_pressed), compose);
6456 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6457 G_CALLBACK(popup_attach_button_pressed), compose);
6459 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6460 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6461 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6462 G_CALLBACK(popup_attach_button_pressed), compose);
6464 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6465 G_CALLBACK(attach_key_pressed), compose);
6468 gtk_drag_dest_set(attach_clist,
6469 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6470 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6471 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6472 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6473 G_CALLBACK(compose_attach_drag_received_cb),
6475 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6476 G_CALLBACK(compose_drag_drop),
6479 compose->attach_scrwin = attach_scrwin;
6480 compose->attach_clist = attach_clist;
6482 return attach_scrwin;
6485 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6486 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6488 static GtkWidget *compose_create_others(Compose *compose)
6491 GtkWidget *savemsg_checkbtn;
6492 GtkWidget *savemsg_entry;
6493 GtkWidget *savemsg_select;
6496 gchar *folderidentifier;
6498 /* Table for settings */
6499 table = gtk_table_new(3, 1, FALSE);
6500 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6501 gtk_widget_show(table);
6502 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6505 /* Save Message to folder */
6506 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6507 gtk_widget_show(savemsg_checkbtn);
6508 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6509 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6510 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6512 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6513 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6515 savemsg_entry = gtk_entry_new();
6516 gtk_widget_show(savemsg_entry);
6517 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6518 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6519 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6520 G_CALLBACK(compose_grab_focus_cb), compose);
6521 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6522 folderidentifier = folder_item_get_identifier(account_get_special_folder
6523 (compose->account, F_OUTBOX));
6524 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6525 g_free(folderidentifier);
6528 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6529 gtk_widget_show(savemsg_select);
6530 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6531 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6532 G_CALLBACK(compose_savemsg_select_cb),
6537 compose->savemsg_checkbtn = savemsg_checkbtn;
6538 compose->savemsg_entry = savemsg_entry;
6543 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6545 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6546 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6549 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6554 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6557 path = folder_item_get_identifier(dest);
6559 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6563 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6564 GdkAtom clip, GtkTextIter *insert_place);
6567 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6571 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6573 if (event->button == 3) {
6575 GtkTextIter sel_start, sel_end;
6576 gboolean stuff_selected;
6578 /* move the cursor to allow GtkAspell to check the word
6579 * under the mouse */
6580 if (event->x && event->y) {
6581 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6582 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6584 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6587 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6588 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6591 stuff_selected = gtk_text_buffer_get_selection_bounds(
6593 &sel_start, &sel_end);
6595 gtk_text_buffer_place_cursor (buffer, &iter);
6596 /* reselect stuff */
6598 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6599 gtk_text_buffer_select_range(buffer,
6600 &sel_start, &sel_end);
6602 return FALSE; /* pass the event so that the right-click goes through */
6605 if (event->button == 2) {
6610 /* get the middle-click position to paste at the correct place */
6611 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6612 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6614 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6617 entry_paste_clipboard(compose, text,
6618 prefs_common.linewrap_pastes,
6619 GDK_SELECTION_PRIMARY, &iter);
6627 static void compose_spell_menu_changed(void *data)
6629 Compose *compose = (Compose *)data;
6631 GtkWidget *menuitem;
6632 GtkWidget *parent_item;
6633 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6636 if (compose->gtkaspell == NULL)
6639 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6640 "/Menu/Spelling/Options");
6642 /* setting the submenu removes /Spelling/Options from the factory
6643 * so we need to save it */
6645 if (parent_item == NULL) {
6646 parent_item = compose->aspell_options_menu;
6647 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6649 compose->aspell_options_menu = parent_item;
6651 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6653 spell_menu = g_slist_reverse(spell_menu);
6654 for (items = spell_menu;
6655 items; items = items->next) {
6656 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6657 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6658 gtk_widget_show(GTK_WIDGET(menuitem));
6660 g_slist_free(spell_menu);
6662 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6667 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6669 Compose *compose = (Compose *)data;
6670 GdkEventButton event;
6673 event.time = gtk_get_current_event_time();
6677 return text_clicked(compose->text, &event, compose);
6680 static gboolean compose_force_window_origin = TRUE;
6681 static Compose *compose_create(PrefsAccount *account,
6690 GtkWidget *handlebox;
6692 GtkWidget *notebook;
6694 GtkWidget *attach_hbox;
6695 GtkWidget *attach_lab1;
6696 GtkWidget *attach_lab2;
6701 GtkWidget *subject_hbox;
6702 GtkWidget *subject_frame;
6703 GtkWidget *subject_entry;
6707 GtkWidget *edit_vbox;
6708 GtkWidget *ruler_hbox;
6710 GtkWidget *scrolledwin;
6712 GtkTextBuffer *buffer;
6713 GtkClipboard *clipboard;
6716 UndoMain *undostruct;
6718 gchar *titles[N_ATTACH_COLS];
6719 GtkWidget *popupmenu;
6720 GtkWidget *tmpl_menu;
6721 GtkActionGroup *action_group = NULL;
6724 GtkAspell * gtkaspell = NULL;
6727 static GdkGeometry geometry;
6729 g_return_val_if_fail(account != NULL, NULL);
6731 debug_print("Creating compose window...\n");
6732 compose = g_new0(Compose, 1);
6734 titles[COL_MIMETYPE] = _("MIME type");
6735 titles[COL_SIZE] = _("Size");
6736 titles[COL_NAME] = _("Name");
6738 compose->batch = batch;
6739 compose->account = account;
6740 compose->folder = folder;
6742 compose->mutex = g_mutex_new();
6743 compose->set_cursor_pos = -1;
6745 #if !(GTK_CHECK_VERSION(2,12,0))
6746 compose->tooltips = tips;
6749 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6751 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6752 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6754 if (!geometry.max_width) {
6755 geometry.max_width = gdk_screen_width();
6756 geometry.max_height = gdk_screen_height();
6759 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6760 &geometry, GDK_HINT_MAX_SIZE);
6761 if (!geometry.min_width) {
6762 geometry.min_width = 600;
6763 geometry.min_height = 480;
6765 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6766 &geometry, GDK_HINT_MIN_SIZE);
6768 #ifndef GENERIC_UMPC
6769 if (compose_force_window_origin)
6770 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6771 prefs_common.compose_y);
6773 g_signal_connect(G_OBJECT(window), "delete_event",
6774 G_CALLBACK(compose_delete_cb), compose);
6775 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6776 gtk_widget_realize(window);
6778 gtkut_widget_set_composer_icon(window);
6780 vbox = gtk_vbox_new(FALSE, 0);
6781 gtk_container_add(GTK_CONTAINER(window), vbox);
6783 compose->ui_manager = gtk_ui_manager_new();
6784 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6785 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6786 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6787 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6788 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6789 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6790 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6791 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6792 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6793 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6808 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6814 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6815 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6816 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6818 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6819 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6820 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6827 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6842 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6849 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6864 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6870 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6873 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6877 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6879 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6880 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6881 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6885 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6887 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6893 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6894 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6901 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6913 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)
6914 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)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6920 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)
6921 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)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6925 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6926 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)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6930 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)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6936 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)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6942 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)
6943 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)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6955 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)
6957 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6973 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6974 gtk_widget_show_all(menubar);
6976 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6978 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6980 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6983 if (prefs_common.toolbar_detachable) {
6984 handlebox = gtk_handle_box_new();
6986 handlebox = gtk_hbox_new(FALSE, 0);
6988 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6990 gtk_widget_realize(handlebox);
6992 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6995 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6999 vbox2 = gtk_vbox_new(FALSE, 2);
7000 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7001 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7004 notebook = gtk_notebook_new();
7005 gtk_widget_set_size_request(notebook, -1, 130);
7006 gtk_widget_show(notebook);
7008 /* header labels and entries */
7009 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7010 compose_create_header(compose),
7011 gtk_label_new_with_mnemonic(_("Hea_der")));
7012 /* attachment list */
7013 attach_hbox = gtk_hbox_new(FALSE, 0);
7014 gtk_widget_show(attach_hbox);
7016 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7017 gtk_widget_show(attach_lab1);
7018 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7020 attach_lab2 = gtk_label_new("");
7021 gtk_widget_show(attach_lab2);
7022 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7024 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7025 compose_create_attach(compose),
7028 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7029 compose_create_others(compose),
7030 gtk_label_new_with_mnemonic(_("Othe_rs")));
7033 subject_hbox = gtk_hbox_new(FALSE, 0);
7034 gtk_widget_show(subject_hbox);
7036 subject_frame = gtk_frame_new(NULL);
7037 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7038 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7039 gtk_widget_show(subject_frame);
7041 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7042 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7043 gtk_widget_show(subject);
7045 label = gtk_label_new(_("Subject:"));
7046 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7047 gtk_widget_show(label);
7049 subject_entry = gtk_entry_new();
7050 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7051 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7052 G_CALLBACK(compose_grab_focus_cb), compose);
7053 gtk_widget_show(subject_entry);
7054 compose->subject_entry = subject_entry;
7055 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7057 edit_vbox = gtk_vbox_new(FALSE, 0);
7059 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7062 ruler_hbox = gtk_hbox_new(FALSE, 0);
7063 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7065 ruler = gtk_shruler_new();
7066 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7067 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7071 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7072 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7073 GTK_POLICY_AUTOMATIC,
7074 GTK_POLICY_AUTOMATIC);
7075 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7077 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7078 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7080 text = gtk_text_view_new();
7081 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7082 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7083 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7084 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7085 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7087 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7089 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7090 G_CALLBACK(compose_edit_size_alloc),
7092 g_signal_connect(G_OBJECT(buffer), "changed",
7093 G_CALLBACK(compose_changed_cb), compose);
7094 g_signal_connect(G_OBJECT(text), "grab_focus",
7095 G_CALLBACK(compose_grab_focus_cb), compose);
7096 g_signal_connect(G_OBJECT(buffer), "insert_text",
7097 G_CALLBACK(text_inserted), compose);
7098 g_signal_connect(G_OBJECT(text), "button_press_event",
7099 G_CALLBACK(text_clicked), compose);
7101 g_signal_connect(G_OBJECT(text), "popup-menu",
7102 G_CALLBACK(compose_popup_menu), compose);
7104 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7105 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7106 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7107 G_CALLBACK(compose_popup_menu), compose);
7109 g_signal_connect(G_OBJECT(subject_entry), "changed",
7110 G_CALLBACK(compose_changed_cb), compose);
7113 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7114 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7115 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7116 g_signal_connect(G_OBJECT(text), "drag_data_received",
7117 G_CALLBACK(compose_insert_drag_received_cb),
7119 g_signal_connect(G_OBJECT(text), "drag-drop",
7120 G_CALLBACK(compose_drag_drop),
7122 gtk_widget_show_all(vbox);
7124 /* pane between attach clist and text */
7125 paned = gtk_vpaned_new();
7126 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7128 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7129 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7131 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7133 gtk_paned_add1(GTK_PANED(paned), notebook);
7134 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7135 gtk_widget_show_all(paned);
7138 if (prefs_common.textfont) {
7139 PangoFontDescription *font_desc;
7141 font_desc = pango_font_description_from_string
7142 (prefs_common.textfont);
7144 gtk_widget_modify_font(text, font_desc);
7145 pango_font_description_free(font_desc);
7149 gtk_action_group_add_actions(action_group, compose_popup_entries,
7150 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7158 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7160 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7161 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7162 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7164 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7166 undostruct = undo_init(text);
7167 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7170 address_completion_start(window);
7172 compose->window = window;
7173 compose->vbox = vbox;
7174 compose->menubar = menubar;
7175 compose->handlebox = handlebox;
7177 compose->vbox2 = vbox2;
7179 compose->paned = paned;
7181 compose->attach_label = attach_lab2;
7183 compose->notebook = notebook;
7184 compose->edit_vbox = edit_vbox;
7185 compose->ruler_hbox = ruler_hbox;
7186 compose->ruler = ruler;
7187 compose->scrolledwin = scrolledwin;
7188 compose->text = text;
7190 compose->focused_editable = NULL;
7192 compose->popupmenu = popupmenu;
7194 compose->tmpl_menu = tmpl_menu;
7196 compose->mode = mode;
7197 compose->rmode = mode;
7199 compose->targetinfo = NULL;
7200 compose->replyinfo = NULL;
7201 compose->fwdinfo = NULL;
7203 compose->replyto = NULL;
7205 compose->bcc = NULL;
7206 compose->followup_to = NULL;
7208 compose->ml_post = NULL;
7210 compose->inreplyto = NULL;
7211 compose->references = NULL;
7212 compose->msgid = NULL;
7213 compose->boundary = NULL;
7215 compose->autowrap = prefs_common.autowrap;
7216 compose->autoindent = prefs_common.auto_indent;
7217 compose->use_signing = FALSE;
7218 compose->use_encryption = FALSE;
7219 compose->privacy_system = NULL;
7221 compose->modified = FALSE;
7223 compose->return_receipt = FALSE;
7225 compose->to_list = NULL;
7226 compose->newsgroup_list = NULL;
7228 compose->undostruct = undostruct;
7230 compose->sig_str = NULL;
7232 compose->exteditor_file = NULL;
7233 compose->exteditor_pid = -1;
7234 compose->exteditor_tag = -1;
7235 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7238 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7239 if (mode != COMPOSE_REDIRECT) {
7240 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7241 strcmp(prefs_common.dictionary, "")) {
7242 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7243 prefs_common.dictionary,
7244 prefs_common.alt_dictionary,
7245 conv_get_locale_charset_str(),
7246 prefs_common.misspelled_col,
7247 prefs_common.check_while_typing,
7248 prefs_common.recheck_when_changing_dict,
7249 prefs_common.use_alternate,
7250 prefs_common.use_both_dicts,
7251 GTK_TEXT_VIEW(text),
7252 GTK_WINDOW(compose->window),
7253 compose_spell_menu_changed,
7256 alertpanel_error(_("Spell checker could not "
7258 gtkaspell_checkers_strerror());
7259 gtkaspell_checkers_reset_error();
7261 if (!gtkaspell_set_sug_mode(gtkaspell,
7262 prefs_common.aspell_sugmode)) {
7263 debug_print("Aspell: could not set "
7264 "suggestion mode %s\n",
7265 gtkaspell_checkers_strerror());
7266 gtkaspell_checkers_reset_error();
7269 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7273 compose->gtkaspell = gtkaspell;
7274 compose_spell_menu_changed(compose);
7277 compose_select_account(compose, account, TRUE);
7279 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7282 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7283 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7285 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7286 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7288 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7289 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7291 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7292 if (account->protocol != A_NNTP)
7293 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7294 prefs_common_translated_header_name("To:"));
7296 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7297 prefs_common_translated_header_name("Newsgroups:"));
7299 addressbook_set_target_compose(compose);
7301 if (mode != COMPOSE_REDIRECT)
7302 compose_set_template_menu(compose);
7304 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7307 compose_list = g_list_append(compose_list, compose);
7309 if (!prefs_common.show_ruler)
7310 gtk_widget_hide(ruler_hbox);
7312 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7315 compose->priority = PRIORITY_NORMAL;
7316 compose_update_priority_menu_item(compose);
7318 compose_set_out_encoding(compose);
7321 compose_update_actions_menu(compose);
7323 /* Privacy Systems menu */
7324 compose_update_privacy_systems_menu(compose);
7326 activate_privacy_system(compose, account, TRUE);
7327 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7329 gtk_widget_realize(window);
7331 gtk_widget_show(window);
7333 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7334 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7341 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7346 GtkWidget *optmenubox;
7349 GtkWidget *from_name = NULL;
7350 #if !(GTK_CHECK_VERSION(2,12,0))
7351 GtkTooltips *tips = compose->tooltips;
7354 gint num = 0, def_menu = 0;
7356 accounts = account_get_list();
7357 g_return_val_if_fail(accounts != NULL, NULL);
7359 optmenubox = gtk_event_box_new();
7360 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7361 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7363 hbox = gtk_hbox_new(FALSE, 6);
7364 from_name = gtk_entry_new();
7366 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7367 G_CALLBACK(compose_grab_focus_cb), compose);
7369 for (; accounts != NULL; accounts = accounts->next, num++) {
7370 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7371 gchar *name, *from = NULL;
7373 if (ac == compose->account) def_menu = num;
7375 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7378 if (ac == compose->account) {
7379 if (ac->name && *ac->name) {
7381 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7382 from = g_strdup_printf("%s <%s>",
7384 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7386 from = g_strdup_printf("%s",
7388 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7391 COMBOBOX_ADD(menu, name, ac->account_id);
7396 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7398 g_signal_connect(G_OBJECT(optmenu), "changed",
7399 G_CALLBACK(account_activated),
7401 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7402 G_CALLBACK(compose_entry_popup_extend),
7405 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7406 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7408 CLAWS_SET_TIP(optmenubox,
7409 _("Account to use for this email"));
7410 CLAWS_SET_TIP(from_name,
7411 _("Sender address to be used"));
7413 compose->from_name = from_name;
7418 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7420 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7421 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7422 Compose *compose = (Compose *) data;
7424 compose->priority = value;
7428 static void compose_reply_change_mode(Compose *compose,
7431 gboolean was_modified = compose->modified;
7433 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7435 g_return_if_fail(compose->replyinfo != NULL);
7437 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7439 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7441 if (action == COMPOSE_REPLY_TO_ALL)
7443 if (action == COMPOSE_REPLY_TO_SENDER)
7445 if (action == COMPOSE_REPLY_TO_LIST)
7448 compose_remove_header_entries(compose);
7449 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7450 if (compose->account->set_autocc && compose->account->auto_cc)
7451 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7453 if (compose->account->set_autobcc && compose->account->auto_bcc)
7454 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7456 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7457 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7458 compose_show_first_last_header(compose, TRUE);
7459 compose->modified = was_modified;
7460 compose_set_title(compose);
7463 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7465 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7466 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7467 Compose *compose = (Compose *) data;
7470 compose_reply_change_mode(compose, value);
7473 static void compose_update_priority_menu_item(Compose * compose)
7475 GtkWidget *menuitem = NULL;
7476 switch (compose->priority) {
7477 case PRIORITY_HIGHEST:
7478 menuitem = gtk_ui_manager_get_widget
7479 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7482 menuitem = gtk_ui_manager_get_widget
7483 (compose->ui_manager, "/Menu/Options/Priority/High");
7485 case PRIORITY_NORMAL:
7486 menuitem = gtk_ui_manager_get_widget
7487 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7490 menuitem = gtk_ui_manager_get_widget
7491 (compose->ui_manager, "/Menu/Options/Priority/Low");
7493 case PRIORITY_LOWEST:
7494 menuitem = gtk_ui_manager_get_widget
7495 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7498 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7501 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7503 Compose *compose = (Compose *) data;
7505 gboolean can_sign = FALSE, can_encrypt = FALSE;
7507 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7509 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7512 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7513 g_free(compose->privacy_system);
7514 compose->privacy_system = NULL;
7515 if (systemid != NULL) {
7516 compose->privacy_system = g_strdup(systemid);
7518 can_sign = privacy_system_can_sign(systemid);
7519 can_encrypt = privacy_system_can_encrypt(systemid);
7522 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7524 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7525 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7528 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7530 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7531 GtkWidget *menuitem = NULL;
7533 gboolean can_sign = FALSE, can_encrypt = FALSE;
7534 gboolean found = FALSE;
7536 if (compose->privacy_system != NULL) {
7538 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7539 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7540 g_return_if_fail(menuitem != NULL);
7542 amenu = GTK_MENU_SHELL(menuitem)->children;
7544 while (amenu != NULL) {
7545 GList *alist = amenu->next;
7547 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7548 if (systemid != NULL) {
7549 if (strcmp(systemid, compose->privacy_system) == 0 &&
7550 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7551 menuitem = GTK_WIDGET(amenu->data);
7553 can_sign = privacy_system_can_sign(systemid);
7554 can_encrypt = privacy_system_can_encrypt(systemid);
7558 } else if (strlen(compose->privacy_system) == 0 &&
7559 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7560 menuitem = GTK_WIDGET(amenu->data);
7563 can_encrypt = FALSE;
7570 if (menuitem != NULL)
7571 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7573 if (warn && !found && strlen(compose->privacy_system)) {
7574 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7575 "will not be able to sign or encrypt this message."),
7576 compose->privacy_system);
7580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7581 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7584 static void compose_set_out_encoding(Compose *compose)
7586 CharSet out_encoding;
7587 const gchar *branch = NULL;
7588 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7590 switch(out_encoding) {
7591 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7592 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7593 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7594 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7595 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7596 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7597 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7598 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7599 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7600 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7601 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7602 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7603 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7604 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7605 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7606 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7607 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7608 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7609 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7610 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7611 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7612 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7613 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7614 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7615 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7616 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7617 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7618 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7619 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7620 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7621 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7622 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7624 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7627 static void compose_set_template_menu(Compose *compose)
7629 GSList *tmpl_list, *cur;
7633 tmpl_list = template_get_config();
7635 menu = gtk_menu_new();
7637 gtk_menu_set_accel_group (GTK_MENU (menu),
7638 gtk_ui_manager_get_accel_group(compose->ui_manager));
7639 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7640 Template *tmpl = (Template *)cur->data;
7641 gchar *accel_path = NULL;
7642 item = gtk_menu_item_new_with_label(tmpl->name);
7643 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7644 g_signal_connect(G_OBJECT(item), "activate",
7645 G_CALLBACK(compose_template_activate_cb),
7647 g_object_set_data(G_OBJECT(item), "template", tmpl);
7648 gtk_widget_show(item);
7649 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7650 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7654 gtk_widget_show(menu);
7655 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7658 void compose_update_actions_menu(Compose *compose)
7660 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7663 static void compose_update_privacy_systems_menu(Compose *compose)
7665 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7666 GSList *systems, *cur;
7668 GtkWidget *system_none;
7670 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7671 GtkWidget *privacy_menu = gtk_menu_new();
7673 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7674 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7676 g_signal_connect(G_OBJECT(system_none), "activate",
7677 G_CALLBACK(compose_set_privacy_system_cb), compose);
7679 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7680 gtk_widget_show(system_none);
7682 systems = privacy_get_system_ids();
7683 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7684 gchar *systemid = cur->data;
7686 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7687 widget = gtk_radio_menu_item_new_with_label(group,
7688 privacy_system_get_name(systemid));
7689 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7690 g_strdup(systemid), g_free);
7691 g_signal_connect(G_OBJECT(widget), "activate",
7692 G_CALLBACK(compose_set_privacy_system_cb), compose);
7694 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7695 gtk_widget_show(widget);
7698 g_slist_free(systems);
7699 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7700 gtk_widget_show_all(privacy_menu);
7701 gtk_widget_show_all(privacy_menuitem);
7704 void compose_reflect_prefs_all(void)
7709 for (cur = compose_list; cur != NULL; cur = cur->next) {
7710 compose = (Compose *)cur->data;
7711 compose_set_template_menu(compose);
7715 void compose_reflect_prefs_pixmap_theme(void)
7720 for (cur = compose_list; cur != NULL; cur = cur->next) {
7721 compose = (Compose *)cur->data;
7722 toolbar_update(TOOLBAR_COMPOSE, compose);
7726 static const gchar *compose_quote_char_from_context(Compose *compose)
7728 const gchar *qmark = NULL;
7730 g_return_val_if_fail(compose != NULL, NULL);
7732 switch (compose->mode) {
7733 /* use forward-specific quote char */
7734 case COMPOSE_FORWARD:
7735 case COMPOSE_FORWARD_AS_ATTACH:
7736 case COMPOSE_FORWARD_INLINE:
7737 if (compose->folder && compose->folder->prefs &&
7738 compose->folder->prefs->forward_with_format)
7739 qmark = compose->folder->prefs->forward_quotemark;
7740 else if (compose->account->forward_with_format)
7741 qmark = compose->account->forward_quotemark;
7743 qmark = prefs_common.fw_quotemark;
7746 /* use reply-specific quote char in all other modes */
7748 if (compose->folder && compose->folder->prefs &&
7749 compose->folder->prefs->reply_with_format)
7750 qmark = compose->folder->prefs->reply_quotemark;
7751 else if (compose->account->reply_with_format)
7752 qmark = compose->account->reply_quotemark;
7754 qmark = prefs_common.quotemark;
7758 if (qmark == NULL || *qmark == '\0')
7764 static void compose_template_apply(Compose *compose, Template *tmpl,
7768 GtkTextBuffer *buffer;
7772 gchar *parsed_str = NULL;
7773 gint cursor_pos = 0;
7774 const gchar *err_msg = _("Template body format error at line %d.");
7777 /* process the body */
7779 text = GTK_TEXT_VIEW(compose->text);
7780 buffer = gtk_text_view_get_buffer(text);
7783 qmark = compose_quote_char_from_context(compose);
7785 if (compose->replyinfo != NULL) {
7788 gtk_text_buffer_set_text(buffer, "", -1);
7789 mark = gtk_text_buffer_get_insert(buffer);
7790 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7792 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7793 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7795 } else if (compose->fwdinfo != NULL) {
7798 gtk_text_buffer_set_text(buffer, "", -1);
7799 mark = gtk_text_buffer_get_insert(buffer);
7800 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7802 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7803 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7806 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7808 GtkTextIter start, end;
7811 gtk_text_buffer_get_start_iter(buffer, &start);
7812 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7813 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7815 /* clear the buffer now */
7817 gtk_text_buffer_set_text(buffer, "", -1);
7819 parsed_str = compose_quote_fmt(compose, dummyinfo,
7820 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7821 procmsg_msginfo_free( dummyinfo );
7827 gtk_text_buffer_set_text(buffer, "", -1);
7828 mark = gtk_text_buffer_get_insert(buffer);
7829 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7832 if (replace && parsed_str && compose->account->auto_sig)
7833 compose_insert_sig(compose, FALSE);
7835 if (replace && parsed_str) {
7836 gtk_text_buffer_get_start_iter(buffer, &iter);
7837 gtk_text_buffer_place_cursor(buffer, &iter);
7841 cursor_pos = quote_fmt_get_cursor_pos();
7842 compose->set_cursor_pos = cursor_pos;
7843 if (cursor_pos == -1)
7845 gtk_text_buffer_get_start_iter(buffer, &iter);
7846 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7847 gtk_text_buffer_place_cursor(buffer, &iter);
7850 /* process the other fields */
7852 compose_template_apply_fields(compose, tmpl);
7853 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7854 quote_fmt_reset_vartable();
7855 compose_changed_cb(NULL, compose);
7858 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7860 MsgInfo* dummyinfo = NULL;
7861 MsgInfo *msginfo = NULL;
7864 if (compose->replyinfo != NULL)
7865 msginfo = compose->replyinfo;
7866 else if (compose->fwdinfo != NULL)
7867 msginfo = compose->fwdinfo;
7869 dummyinfo = compose_msginfo_new_from_compose(compose);
7870 msginfo = dummyinfo;
7873 if (tmpl->from && *tmpl->from != '\0') {
7875 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7876 compose->gtkaspell);
7878 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7880 quote_fmt_scan_string(tmpl->from);
7883 buf = quote_fmt_get_buffer();
7885 alertpanel_error(_("Template From format error."));
7887 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7891 if (tmpl->to && *tmpl->to != '\0') {
7893 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7894 compose->gtkaspell);
7896 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7898 quote_fmt_scan_string(tmpl->to);
7901 buf = quote_fmt_get_buffer();
7903 alertpanel_error(_("Template To format error."));
7905 compose_entry_append(compose, buf, COMPOSE_TO);
7909 if (tmpl->cc && *tmpl->cc != '\0') {
7911 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7912 compose->gtkaspell);
7914 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7916 quote_fmt_scan_string(tmpl->cc);
7919 buf = quote_fmt_get_buffer();
7921 alertpanel_error(_("Template Cc format error."));
7923 compose_entry_append(compose, buf, COMPOSE_CC);
7927 if (tmpl->bcc && *tmpl->bcc != '\0') {
7929 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7930 compose->gtkaspell);
7932 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7934 quote_fmt_scan_string(tmpl->bcc);
7937 buf = quote_fmt_get_buffer();
7939 alertpanel_error(_("Template Bcc format error."));
7941 compose_entry_append(compose, buf, COMPOSE_BCC);
7945 /* process the subject */
7946 if (tmpl->subject && *tmpl->subject != '\0') {
7948 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7949 compose->gtkaspell);
7951 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7953 quote_fmt_scan_string(tmpl->subject);
7956 buf = quote_fmt_get_buffer();
7958 alertpanel_error(_("Template subject format error."));
7960 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7964 procmsg_msginfo_free( dummyinfo );
7967 static void compose_destroy(Compose *compose)
7969 GtkTextBuffer *buffer;
7970 GtkClipboard *clipboard;
7972 compose_list = g_list_remove(compose_list, compose);
7974 if (compose->updating) {
7975 debug_print("danger, not destroying anything now\n");
7976 compose->deferred_destroy = TRUE;
7979 /* NOTE: address_completion_end() does nothing with the window
7980 * however this may change. */
7981 address_completion_end(compose->window);
7983 slist_free_strings(compose->to_list);
7984 g_slist_free(compose->to_list);
7985 slist_free_strings(compose->newsgroup_list);
7986 g_slist_free(compose->newsgroup_list);
7987 slist_free_strings(compose->header_list);
7988 g_slist_free(compose->header_list);
7990 procmsg_msginfo_free(compose->targetinfo);
7991 procmsg_msginfo_free(compose->replyinfo);
7992 procmsg_msginfo_free(compose->fwdinfo);
7994 g_free(compose->replyto);
7995 g_free(compose->cc);
7996 g_free(compose->bcc);
7997 g_free(compose->newsgroups);
7998 g_free(compose->followup_to);
8000 g_free(compose->ml_post);
8002 g_free(compose->inreplyto);
8003 g_free(compose->references);
8004 g_free(compose->msgid);
8005 g_free(compose->boundary);
8007 g_free(compose->redirect_filename);
8008 if (compose->undostruct)
8009 undo_destroy(compose->undostruct);
8011 g_free(compose->sig_str);
8013 g_free(compose->exteditor_file);
8015 g_free(compose->orig_charset);
8017 g_free(compose->privacy_system);
8019 if (addressbook_get_target_compose() == compose)
8020 addressbook_set_target_compose(NULL);
8023 if (compose->gtkaspell) {
8024 gtkaspell_delete(compose->gtkaspell);
8025 compose->gtkaspell = NULL;
8029 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8030 prefs_common.compose_height = compose->window->allocation.height;
8032 if (!gtk_widget_get_parent(compose->paned))
8033 gtk_widget_destroy(compose->paned);
8034 gtk_widget_destroy(compose->popupmenu);
8036 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8037 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8038 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8040 gtk_widget_destroy(compose->window);
8041 toolbar_destroy(compose->toolbar);
8042 g_free(compose->toolbar);
8043 g_mutex_free(compose->mutex);
8047 static void compose_attach_info_free(AttachInfo *ainfo)
8049 g_free(ainfo->file);
8050 g_free(ainfo->content_type);
8051 g_free(ainfo->name);
8055 static void compose_attach_update_label(Compose *compose)
8060 GtkTreeModel *model;
8065 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8066 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8067 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8071 while(gtk_tree_model_iter_next(model, &iter))
8074 text = g_strdup_printf("(%d)", i);
8075 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8079 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8081 Compose *compose = (Compose *)data;
8082 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8083 GtkTreeSelection *selection;
8085 GtkTreeModel *model;
8087 selection = gtk_tree_view_get_selection(tree_view);
8088 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8093 for (cur = sel; cur != NULL; cur = cur->next) {
8094 GtkTreePath *path = cur->data;
8095 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8098 gtk_tree_path_free(path);
8101 for (cur = sel; cur != NULL; cur = cur->next) {
8102 GtkTreeRowReference *ref = cur->data;
8103 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8106 if (gtk_tree_model_get_iter(model, &iter, path))
8107 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8109 gtk_tree_path_free(path);
8110 gtk_tree_row_reference_free(ref);
8114 compose_attach_update_label(compose);
8117 static struct _AttachProperty
8120 GtkWidget *mimetype_entry;
8121 GtkWidget *encoding_optmenu;
8122 GtkWidget *path_entry;
8123 GtkWidget *filename_entry;
8125 GtkWidget *cancel_btn;
8128 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8130 gtk_tree_path_free((GtkTreePath *)ptr);
8133 static void compose_attach_property(GtkAction *action, gpointer data)
8135 Compose *compose = (Compose *)data;
8136 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8138 GtkComboBox *optmenu;
8139 GtkTreeSelection *selection;
8141 GtkTreeModel *model;
8144 static gboolean cancelled;
8146 /* only if one selected */
8147 selection = gtk_tree_view_get_selection(tree_view);
8148 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8151 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8155 path = (GtkTreePath *) sel->data;
8156 gtk_tree_model_get_iter(model, &iter, path);
8157 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8160 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8166 if (!attach_prop.window)
8167 compose_attach_property_create(&cancelled);
8168 gtk_widget_grab_focus(attach_prop.ok_btn);
8169 gtk_widget_show(attach_prop.window);
8170 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8172 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8173 if (ainfo->encoding == ENC_UNKNOWN)
8174 combobox_select_by_data(optmenu, ENC_BASE64);
8176 combobox_select_by_data(optmenu, ainfo->encoding);
8178 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8179 ainfo->content_type ? ainfo->content_type : "");
8180 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8181 ainfo->file ? ainfo->file : "");
8182 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8183 ainfo->name ? ainfo->name : "");
8186 const gchar *entry_text;
8188 gchar *cnttype = NULL;
8195 gtk_widget_hide(attach_prop.window);
8200 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8201 if (*entry_text != '\0') {
8204 text = g_strstrip(g_strdup(entry_text));
8205 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8206 cnttype = g_strdup(text);
8209 alertpanel_error(_("Invalid MIME type."));
8215 ainfo->encoding = combobox_get_active_data(optmenu);
8217 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8218 if (*entry_text != '\0') {
8219 if (is_file_exist(entry_text) &&
8220 (size = get_file_size(entry_text)) > 0)
8221 file = g_strdup(entry_text);
8224 (_("File doesn't exist or is empty."));
8230 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8231 if (*entry_text != '\0') {
8232 g_free(ainfo->name);
8233 ainfo->name = g_strdup(entry_text);
8237 g_free(ainfo->content_type);
8238 ainfo->content_type = cnttype;
8241 g_free(ainfo->file);
8245 ainfo->size = (goffset)size;
8247 /* update tree store */
8248 text = to_human_readable(ainfo->size);
8249 gtk_tree_model_get_iter(model, &iter, path);
8250 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8251 COL_MIMETYPE, ainfo->content_type,
8253 COL_NAME, ainfo->name,
8259 gtk_tree_path_free(path);
8262 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8264 label = gtk_label_new(str); \
8265 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8266 GTK_FILL, 0, 0, 0); \
8267 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8269 entry = gtk_entry_new(); \
8270 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8271 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8274 static void compose_attach_property_create(gboolean *cancelled)
8280 GtkWidget *mimetype_entry;
8283 GtkListStore *optmenu_menu;
8284 GtkWidget *path_entry;
8285 GtkWidget *filename_entry;
8288 GtkWidget *cancel_btn;
8289 GList *mime_type_list, *strlist;
8292 debug_print("Creating attach_property window...\n");
8294 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8295 gtk_widget_set_size_request(window, 480, -1);
8296 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8297 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8298 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8299 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8300 g_signal_connect(G_OBJECT(window), "delete_event",
8301 G_CALLBACK(attach_property_delete_event),
8303 g_signal_connect(G_OBJECT(window), "key_press_event",
8304 G_CALLBACK(attach_property_key_pressed),
8307 vbox = gtk_vbox_new(FALSE, 8);
8308 gtk_container_add(GTK_CONTAINER(window), vbox);
8310 table = gtk_table_new(4, 2, FALSE);
8311 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8312 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8313 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8315 label = gtk_label_new(_("MIME type"));
8316 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8318 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8319 mimetype_entry = gtk_combo_box_entry_new_text();
8320 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8321 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8323 /* stuff with list */
8324 mime_type_list = procmime_get_mime_type_list();
8326 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8327 MimeType *type = (MimeType *) mime_type_list->data;
8330 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8332 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8335 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8336 (GCompareFunc)strcmp2);
8339 for (mime_type_list = strlist; mime_type_list != NULL;
8340 mime_type_list = mime_type_list->next) {
8341 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8342 g_free(mime_type_list->data);
8344 g_list_free(strlist);
8345 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8346 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8348 label = gtk_label_new(_("Encoding"));
8349 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8351 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8353 hbox = gtk_hbox_new(FALSE, 0);
8354 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8355 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8357 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8358 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8360 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8361 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8362 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8363 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8364 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8366 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8368 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8369 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8371 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8372 &ok_btn, GTK_STOCK_OK,
8374 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8375 gtk_widget_grab_default(ok_btn);
8377 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8378 G_CALLBACK(attach_property_ok),
8380 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8381 G_CALLBACK(attach_property_cancel),
8384 gtk_widget_show_all(vbox);
8386 attach_prop.window = window;
8387 attach_prop.mimetype_entry = mimetype_entry;
8388 attach_prop.encoding_optmenu = optmenu;
8389 attach_prop.path_entry = path_entry;
8390 attach_prop.filename_entry = filename_entry;
8391 attach_prop.ok_btn = ok_btn;
8392 attach_prop.cancel_btn = cancel_btn;
8395 #undef SET_LABEL_AND_ENTRY
8397 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8403 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8409 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8410 gboolean *cancelled)
8418 static gboolean attach_property_key_pressed(GtkWidget *widget,
8420 gboolean *cancelled)
8422 if (event && event->keyval == GDK_Escape) {
8429 static void compose_exec_ext_editor(Compose *compose)
8436 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8437 G_DIR_SEPARATOR, compose);
8439 if (pipe(pipe_fds) < 0) {
8445 if ((pid = fork()) < 0) {
8452 /* close the write side of the pipe */
8455 compose->exteditor_file = g_strdup(tmp);
8456 compose->exteditor_pid = pid;
8458 compose_set_ext_editor_sensitive(compose, FALSE);
8460 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8461 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8465 } else { /* process-monitoring process */
8471 /* close the read side of the pipe */
8474 if (compose_write_body_to_file(compose, tmp) < 0) {
8475 fd_write_all(pipe_fds[1], "2\n", 2);
8479 pid_ed = compose_exec_ext_editor_real(tmp);
8481 fd_write_all(pipe_fds[1], "1\n", 2);
8485 /* wait until editor is terminated */
8486 waitpid(pid_ed, NULL, 0);
8488 fd_write_all(pipe_fds[1], "0\n", 2);
8495 #endif /* G_OS_UNIX */
8499 static gint compose_exec_ext_editor_real(const gchar *file)
8506 g_return_val_if_fail(file != NULL, -1);
8508 if ((pid = fork()) < 0) {
8513 if (pid != 0) return pid;
8515 /* grandchild process */
8517 if (setpgid(0, getppid()))
8520 if (prefs_common_get_ext_editor_cmd() &&
8521 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8522 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8523 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8525 if (prefs_common_get_ext_editor_cmd())
8526 g_warning("External editor command line is invalid: '%s'\n",
8527 prefs_common_get_ext_editor_cmd());
8528 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8531 cmdline = strsplit_with_quote(buf, " ", 1024);
8532 execvp(cmdline[0], cmdline);
8535 g_strfreev(cmdline);
8540 static gboolean compose_ext_editor_kill(Compose *compose)
8542 pid_t pgid = compose->exteditor_pid * -1;
8545 ret = kill(pgid, 0);
8547 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8551 msg = g_strdup_printf
8552 (_("The external editor is still working.\n"
8553 "Force terminating the process?\n"
8554 "process group id: %d"), -pgid);
8555 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8556 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8560 if (val == G_ALERTALTERNATE) {
8561 g_source_remove(compose->exteditor_tag);
8562 g_io_channel_shutdown(compose->exteditor_ch,
8564 g_io_channel_unref(compose->exteditor_ch);
8566 if (kill(pgid, SIGTERM) < 0) perror("kill");
8567 waitpid(compose->exteditor_pid, NULL, 0);
8569 g_warning("Terminated process group id: %d", -pgid);
8570 g_warning("Temporary file: %s",
8571 compose->exteditor_file);
8573 compose_set_ext_editor_sensitive(compose, TRUE);
8575 g_free(compose->exteditor_file);
8576 compose->exteditor_file = NULL;
8577 compose->exteditor_pid = -1;
8578 compose->exteditor_ch = NULL;
8579 compose->exteditor_tag = -1;
8587 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8591 Compose *compose = (Compose *)data;
8594 debug_print(_("Compose: input from monitoring process\n"));
8596 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8598 g_io_channel_shutdown(source, FALSE, NULL);
8599 g_io_channel_unref(source);
8601 waitpid(compose->exteditor_pid, NULL, 0);
8603 if (buf[0] == '0') { /* success */
8604 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8605 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8607 gtk_text_buffer_set_text(buffer, "", -1);
8608 compose_insert_file(compose, compose->exteditor_file);
8609 compose_changed_cb(NULL, compose);
8611 if (claws_unlink(compose->exteditor_file) < 0)
8612 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8613 } else if (buf[0] == '1') { /* failed */
8614 g_warning("Couldn't exec external editor\n");
8615 if (claws_unlink(compose->exteditor_file) < 0)
8616 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8617 } else if (buf[0] == '2') {
8618 g_warning("Couldn't write to file\n");
8619 } else if (buf[0] == '3') {
8620 g_warning("Pipe read failed\n");
8623 compose_set_ext_editor_sensitive(compose, TRUE);
8625 g_free(compose->exteditor_file);
8626 compose->exteditor_file = NULL;
8627 compose->exteditor_pid = -1;
8628 compose->exteditor_ch = NULL;
8629 compose->exteditor_tag = -1;
8634 static void compose_set_ext_editor_sensitive(Compose *compose,
8637 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8638 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8639 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8640 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8641 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8642 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8643 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8645 gtk_widget_set_sensitive(compose->text, sensitive);
8646 if (compose->toolbar->send_btn)
8647 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8648 if (compose->toolbar->sendl_btn)
8649 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8650 if (compose->toolbar->draft_btn)
8651 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8652 if (compose->toolbar->insert_btn)
8653 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8654 if (compose->toolbar->sig_btn)
8655 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8656 if (compose->toolbar->exteditor_btn)
8657 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8658 if (compose->toolbar->linewrap_current_btn)
8659 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8660 if (compose->toolbar->linewrap_all_btn)
8661 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8663 #endif /* G_OS_UNIX */
8666 * compose_undo_state_changed:
8668 * Change the sensivity of the menuentries undo and redo
8670 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8671 gint redo_state, gpointer data)
8673 Compose *compose = (Compose *)data;
8675 switch (undo_state) {
8676 case UNDO_STATE_TRUE:
8677 if (!undostruct->undo_state) {
8678 undostruct->undo_state = TRUE;
8679 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8682 case UNDO_STATE_FALSE:
8683 if (undostruct->undo_state) {
8684 undostruct->undo_state = FALSE;
8685 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8688 case UNDO_STATE_UNCHANGED:
8690 case UNDO_STATE_REFRESH:
8691 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8694 g_warning("Undo state not recognized");
8698 switch (redo_state) {
8699 case UNDO_STATE_TRUE:
8700 if (!undostruct->redo_state) {
8701 undostruct->redo_state = TRUE;
8702 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8705 case UNDO_STATE_FALSE:
8706 if (undostruct->redo_state) {
8707 undostruct->redo_state = FALSE;
8708 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8711 case UNDO_STATE_UNCHANGED:
8713 case UNDO_STATE_REFRESH:
8714 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8717 g_warning("Redo state not recognized");
8722 /* callback functions */
8724 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8725 * includes "non-client" (windows-izm) in calculation, so this calculation
8726 * may not be accurate.
8728 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8729 GtkAllocation *allocation,
8730 GtkSHRuler *shruler)
8732 if (prefs_common.show_ruler) {
8733 gint char_width = 0, char_height = 0;
8734 gint line_width_in_chars;
8736 gtkut_get_font_size(GTK_WIDGET(widget),
8737 &char_width, &char_height);
8738 line_width_in_chars =
8739 (allocation->width - allocation->x) / char_width;
8741 /* got the maximum */
8742 gtk_ruler_set_range(GTK_RULER(shruler),
8743 0.0, line_width_in_chars, 0,
8744 /*line_width_in_chars*/ char_width);
8750 static void account_activated(GtkComboBox *optmenu, gpointer data)
8752 Compose *compose = (Compose *)data;
8755 gchar *folderidentifier;
8756 gint account_id = 0;
8760 /* Get ID of active account in the combo box */
8761 menu = gtk_combo_box_get_model(optmenu);
8762 gtk_combo_box_get_active_iter(optmenu, &iter);
8763 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8765 ac = account_find_from_id(account_id);
8766 g_return_if_fail(ac != NULL);
8768 if (ac != compose->account)
8769 compose_select_account(compose, ac, FALSE);
8771 /* Set message save folder */
8772 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8773 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8775 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8776 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8778 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8779 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8780 folderidentifier = folder_item_get_identifier(account_get_special_folder
8781 (compose->account, F_OUTBOX));
8782 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8783 g_free(folderidentifier);
8787 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8788 GtkTreeViewColumn *column, Compose *compose)
8790 compose_attach_property(NULL, compose);
8793 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8796 Compose *compose = (Compose *)data;
8797 GtkTreeSelection *attach_selection;
8798 gint attach_nr_selected;
8800 if (!event) return FALSE;
8802 if (event->button == 3) {
8803 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8804 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8806 if (attach_nr_selected > 0)
8808 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8809 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8811 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8812 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8815 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8816 NULL, NULL, event->button, event->time);
8823 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8826 Compose *compose = (Compose *)data;
8828 if (!event) return FALSE;
8830 switch (event->keyval) {
8832 compose_attach_remove_selected(NULL, compose);
8838 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8840 toolbar_comp_set_sensitive(compose, allow);
8841 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8842 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8844 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8846 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8847 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8848 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8850 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8854 static void compose_send_cb(GtkAction *action, gpointer data)
8856 Compose *compose = (Compose *)data;
8858 if (prefs_common.work_offline &&
8859 !inc_offline_should_override(TRUE,
8860 _("Claws Mail needs network access in order "
8861 "to send this email.")))
8864 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8865 g_source_remove(compose->draft_timeout_tag);
8866 compose->draft_timeout_tag = -1;
8869 compose_send(compose);
8872 static void compose_send_later_cb(GtkAction *action, gpointer data)
8874 Compose *compose = (Compose *)data;
8878 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8882 compose_close(compose);
8883 } else if (val == -1) {
8884 alertpanel_error(_("Could not queue message."));
8885 } else if (val == -2) {
8886 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8887 } else if (val == -3) {
8888 if (privacy_peek_error())
8889 alertpanel_error(_("Could not queue message for sending:\n\n"
8890 "Signature failed: %s"), privacy_get_error());
8891 } else if (val == -4) {
8892 alertpanel_error(_("Could not queue message for sending:\n\n"
8893 "Charset conversion failed."));
8894 } else if (val == -5) {
8895 alertpanel_error(_("Could not queue message for sending:\n\n"
8896 "Couldn't get recipient encryption key."));
8897 } else if (val == -6) {
8900 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8903 #define DRAFTED_AT_EXIT "drafted_at_exit"
8904 static void compose_register_draft(MsgInfo *info)
8906 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8907 DRAFTED_AT_EXIT, NULL);
8908 FILE *fp = fopen(filepath, "ab");
8911 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8919 gboolean compose_draft (gpointer data, guint action)
8921 Compose *compose = (Compose *)data;
8925 MsgFlags flag = {0, 0};
8926 static gboolean lock = FALSE;
8927 MsgInfo *newmsginfo;
8929 gboolean target_locked = FALSE;
8930 gboolean err = FALSE;
8932 if (lock) return FALSE;
8934 if (compose->sending)
8937 draft = account_get_special_folder(compose->account, F_DRAFT);
8938 g_return_val_if_fail(draft != NULL, FALSE);
8940 if (!g_mutex_trylock(compose->mutex)) {
8941 /* we don't want to lock the mutex once it's available,
8942 * because as the only other part of compose.c locking
8943 * it is compose_close - which means once unlocked,
8944 * the compose struct will be freed */
8945 debug_print("couldn't lock mutex, probably sending\n");
8951 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8952 G_DIR_SEPARATOR, compose);
8953 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8954 FILE_OP_ERROR(tmp, "fopen");
8958 /* chmod for security */
8959 if (change_file_mode_rw(fp, tmp) < 0) {
8960 FILE_OP_ERROR(tmp, "chmod");
8961 g_warning("can't change file mode\n");
8964 /* Save draft infos */
8965 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8966 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8968 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8969 gchar *savefolderid;
8971 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8972 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8973 g_free(savefolderid);
8975 if (compose->return_receipt) {
8976 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8978 if (compose->privacy_system) {
8979 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8980 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8981 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8984 /* Message-ID of message replying to */
8985 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8988 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8989 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8992 /* Message-ID of message forwarding to */
8993 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8996 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8997 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9001 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9002 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9004 /* end of headers */
9005 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9012 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9016 if (fclose(fp) == EOF) {
9020 if (compose->targetinfo) {
9021 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9022 flag.perm_flags = target_locked?MSG_LOCKED:0;
9024 flag.tmp_flags = MSG_DRAFT;
9026 folder_item_scan(draft);
9027 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9028 MsgInfo *tmpinfo = NULL;
9029 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9030 if (compose->msgid) {
9031 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9034 msgnum = tmpinfo->msgnum;
9035 procmsg_msginfo_free(tmpinfo);
9036 debug_print("got draft msgnum %d from scanning\n", msgnum);
9038 debug_print("didn't get draft msgnum after scanning\n");
9041 debug_print("got draft msgnum %d from adding\n", msgnum);
9047 if (action != COMPOSE_AUTO_SAVE) {
9048 if (action != COMPOSE_DRAFT_FOR_EXIT)
9049 alertpanel_error(_("Could not save draft."));
9052 gtkut_window_popup(compose->window);
9053 val = alertpanel_full(_("Could not save draft"),
9054 _("Could not save draft.\n"
9055 "Do you want to cancel exit or discard this email?"),
9056 _("_Cancel exit"), _("_Discard email"), NULL,
9057 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9058 if (val == G_ALERTALTERNATE) {
9060 g_mutex_unlock(compose->mutex); /* must be done before closing */
9061 compose_close(compose);
9065 g_mutex_unlock(compose->mutex); /* must be done before closing */
9074 if (compose->mode == COMPOSE_REEDIT) {
9075 compose_remove_reedit_target(compose, TRUE);
9078 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9081 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9083 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9085 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9086 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9087 procmsg_msginfo_set_flags(newmsginfo, 0,
9088 MSG_HAS_ATTACHMENT);
9090 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9091 compose_register_draft(newmsginfo);
9093 procmsg_msginfo_free(newmsginfo);
9096 folder_item_scan(draft);
9098 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9100 g_mutex_unlock(compose->mutex); /* must be done before closing */
9101 compose_close(compose);
9107 path = folder_item_fetch_msg(draft, msgnum);
9109 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9112 if (g_stat(path, &s) < 0) {
9113 FILE_OP_ERROR(path, "stat");
9119 procmsg_msginfo_free(compose->targetinfo);
9120 compose->targetinfo = procmsg_msginfo_new();
9121 compose->targetinfo->msgnum = msgnum;
9122 compose->targetinfo->size = (goffset)s.st_size;
9123 compose->targetinfo->mtime = s.st_mtime;
9124 compose->targetinfo->folder = draft;
9126 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9127 compose->mode = COMPOSE_REEDIT;
9129 if (action == COMPOSE_AUTO_SAVE) {
9130 compose->autosaved_draft = compose->targetinfo;
9132 compose->modified = FALSE;
9133 compose_set_title(compose);
9137 g_mutex_unlock(compose->mutex);
9141 void compose_clear_exit_drafts(void)
9143 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9144 DRAFTED_AT_EXIT, NULL);
9145 if (is_file_exist(filepath))
9146 claws_unlink(filepath);
9151 void compose_reopen_exit_drafts(void)
9153 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9154 DRAFTED_AT_EXIT, NULL);
9155 FILE *fp = fopen(filepath, "rb");
9159 while (fgets(buf, sizeof(buf), fp)) {
9160 gchar **parts = g_strsplit(buf, "\t", 2);
9161 const gchar *folder = parts[0];
9162 int msgnum = parts[1] ? atoi(parts[1]):-1;
9164 if (folder && *folder && msgnum > -1) {
9165 FolderItem *item = folder_find_item_from_identifier(folder);
9166 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9168 compose_reedit(info, FALSE);
9175 compose_clear_exit_drafts();
9178 static void compose_save_cb(GtkAction *action, gpointer data)
9180 compose_draft(data, COMPOSE_KEEP_EDITING);
9183 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9185 if (compose && file_list) {
9188 for ( tmp = file_list; tmp; tmp = tmp->next) {
9189 gchar *file = (gchar *) tmp->data;
9190 gchar *utf8_filename = conv_filename_to_utf8(file);
9191 compose_attach_append(compose, file, utf8_filename, NULL);
9192 compose_changed_cb(NULL, compose);
9197 g_free(utf8_filename);
9202 static void compose_attach_cb(GtkAction *action, gpointer data)
9204 Compose *compose = (Compose *)data;
9207 if (compose->redirect_filename != NULL)
9210 file_list = filesel_select_multiple_files_open(_("Select file"));
9213 compose_attach_from_list(compose, file_list, TRUE);
9214 g_list_free(file_list);
9218 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9220 Compose *compose = (Compose *)data;
9223 file_list = filesel_select_multiple_files_open(_("Select file"));
9228 for ( tmp = file_list; tmp; tmp = tmp->next) {
9229 gchar *file = (gchar *) tmp->data;
9230 gchar *filedup = g_strdup(file);
9231 gchar *shortfile = g_path_get_basename(filedup);
9232 ComposeInsertResult res;
9234 res = compose_insert_file(compose, file);
9235 if (res == COMPOSE_INSERT_READ_ERROR) {
9236 alertpanel_error(_("File '%s' could not be read."), shortfile);
9237 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9238 alertpanel_error(_("File '%s' contained invalid characters\n"
9239 "for the current encoding, insertion may be incorrect."), shortfile);
9245 g_list_free(file_list);
9249 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9251 Compose *compose = (Compose *)data;
9253 compose_insert_sig(compose, FALSE);
9256 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9260 Compose *compose = (Compose *)data;
9262 gtkut_widget_get_uposition(widget, &x, &y);
9263 prefs_common.compose_x = x;
9264 prefs_common.compose_y = y;
9266 if (compose->sending || compose->updating)
9268 compose_close_cb(NULL, compose);
9272 void compose_close_toolbar(Compose *compose)
9274 compose_close_cb(NULL, compose);
9277 static void compose_close_cb(GtkAction *action, gpointer data)
9279 Compose *compose = (Compose *)data;
9283 if (compose->exteditor_tag != -1) {
9284 if (!compose_ext_editor_kill(compose))
9289 if (compose->modified) {
9290 if (!g_mutex_trylock(compose->mutex)) {
9291 /* we don't want to lock the mutex once it's available,
9292 * because as the only other part of compose.c locking
9293 * it is compose_close - which means once unlocked,
9294 * the compose struct will be freed */
9295 debug_print("couldn't lock mutex, probably sending\n");
9298 val = alertpanel(_("Discard message"),
9299 _("This message has been modified. Discard it?"),
9300 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9301 g_mutex_unlock(compose->mutex);
9303 case G_ALERTDEFAULT:
9304 if (prefs_common.autosave)
9305 compose_remove_draft(compose);
9307 case G_ALERTALTERNATE:
9308 compose_draft(data, COMPOSE_QUIT_EDITING);
9315 compose_close(compose);
9318 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9320 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9321 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9322 Compose *compose = (Compose *) data;
9325 compose->out_encoding = (CharSet)value;
9328 static void compose_address_cb(GtkAction *action, gpointer data)
9330 Compose *compose = (Compose *)data;
9332 addressbook_open(compose);
9335 static void about_show_cb(GtkAction *action, gpointer data)
9340 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9342 Compose *compose = (Compose *)data;
9347 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9348 g_return_if_fail(tmpl != NULL);
9350 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9352 val = alertpanel(_("Apply template"), msg,
9353 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9356 if (val == G_ALERTDEFAULT)
9357 compose_template_apply(compose, tmpl, TRUE);
9358 else if (val == G_ALERTALTERNATE)
9359 compose_template_apply(compose, tmpl, FALSE);
9362 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9364 Compose *compose = (Compose *)data;
9366 compose_exec_ext_editor(compose);
9369 static void compose_undo_cb(GtkAction *action, gpointer data)
9371 Compose *compose = (Compose *)data;
9372 gboolean prev_autowrap = compose->autowrap;
9374 compose->autowrap = FALSE;
9375 undo_undo(compose->undostruct);
9376 compose->autowrap = prev_autowrap;
9379 static void compose_redo_cb(GtkAction *action, gpointer data)
9381 Compose *compose = (Compose *)data;
9382 gboolean prev_autowrap = compose->autowrap;
9384 compose->autowrap = FALSE;
9385 undo_redo(compose->undostruct);
9386 compose->autowrap = prev_autowrap;
9389 static void entry_cut_clipboard(GtkWidget *entry)
9391 if (GTK_IS_EDITABLE(entry))
9392 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9393 else if (GTK_IS_TEXT_VIEW(entry))
9394 gtk_text_buffer_cut_clipboard(
9395 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9396 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9400 static void entry_copy_clipboard(GtkWidget *entry)
9402 if (GTK_IS_EDITABLE(entry))
9403 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9404 else if (GTK_IS_TEXT_VIEW(entry))
9405 gtk_text_buffer_copy_clipboard(
9406 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9407 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9410 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9411 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9413 if (GTK_IS_TEXT_VIEW(entry)) {
9414 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9415 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9416 GtkTextIter start_iter, end_iter;
9418 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9420 if (contents == NULL)
9423 /* we shouldn't delete the selection when middle-click-pasting, or we
9424 * can't mid-click-paste our own selection */
9425 if (clip != GDK_SELECTION_PRIMARY) {
9426 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9427 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9430 if (insert_place == NULL) {
9431 /* if insert_place isn't specified, insert at the cursor.
9432 * used for Ctrl-V pasting */
9433 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9434 start = gtk_text_iter_get_offset(&start_iter);
9435 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9437 /* if insert_place is specified, paste here.
9438 * used for mid-click-pasting */
9439 start = gtk_text_iter_get_offset(insert_place);
9440 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9444 /* paste unwrapped: mark the paste so it's not wrapped later */
9445 end = start + strlen(contents);
9446 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9447 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9448 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9449 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9450 /* rewrap paragraph now (after a mid-click-paste) */
9451 mark_start = gtk_text_buffer_get_insert(buffer);
9452 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9453 gtk_text_iter_backward_char(&start_iter);
9454 compose_beautify_paragraph(compose, &start_iter, TRUE);
9456 } else if (GTK_IS_EDITABLE(entry))
9457 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9459 compose->modified = TRUE;
9462 static void entry_allsel(GtkWidget *entry)
9464 if (GTK_IS_EDITABLE(entry))
9465 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9466 else if (GTK_IS_TEXT_VIEW(entry)) {
9467 GtkTextIter startiter, enditer;
9468 GtkTextBuffer *textbuf;
9470 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9471 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9472 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9474 gtk_text_buffer_move_mark_by_name(textbuf,
9475 "selection_bound", &startiter);
9476 gtk_text_buffer_move_mark_by_name(textbuf,
9477 "insert", &enditer);
9481 static void compose_cut_cb(GtkAction *action, gpointer data)
9483 Compose *compose = (Compose *)data;
9484 if (compose->focused_editable
9485 #ifndef GENERIC_UMPC
9486 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9489 entry_cut_clipboard(compose->focused_editable);
9492 static void compose_copy_cb(GtkAction *action, gpointer data)
9494 Compose *compose = (Compose *)data;
9495 if (compose->focused_editable
9496 #ifndef GENERIC_UMPC
9497 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9500 entry_copy_clipboard(compose->focused_editable);
9503 static void compose_paste_cb(GtkAction *action, gpointer data)
9505 Compose *compose = (Compose *)data;
9507 GtkTextBuffer *buffer;
9509 if (compose->focused_editable &&
9510 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9511 entry_paste_clipboard(compose, compose->focused_editable,
9512 prefs_common.linewrap_pastes,
9513 GDK_SELECTION_CLIPBOARD, NULL);
9517 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9519 Compose *compose = (Compose *)data;
9520 gint wrap_quote = prefs_common.linewrap_quote;
9521 if (compose->focused_editable
9522 #ifndef GENERIC_UMPC
9523 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9526 /* let text_insert() (called directly or at a later time
9527 * after the gtk_editable_paste_clipboard) know that
9528 * text is to be inserted as a quotation. implemented
9529 * by using a simple refcount... */
9530 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9531 G_OBJECT(compose->focused_editable),
9532 "paste_as_quotation"));
9533 g_object_set_data(G_OBJECT(compose->focused_editable),
9534 "paste_as_quotation",
9535 GINT_TO_POINTER(paste_as_quotation + 1));
9536 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9537 entry_paste_clipboard(compose, compose->focused_editable,
9538 prefs_common.linewrap_pastes,
9539 GDK_SELECTION_CLIPBOARD, NULL);
9540 prefs_common.linewrap_quote = wrap_quote;
9544 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9546 Compose *compose = (Compose *)data;
9548 GtkTextBuffer *buffer;
9550 if (compose->focused_editable
9551 #ifndef GENERIC_UMPC
9552 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9555 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9556 GDK_SELECTION_CLIPBOARD, NULL);
9560 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9562 Compose *compose = (Compose *)data;
9564 GtkTextBuffer *buffer;
9566 if (compose->focused_editable
9567 #ifndef GENERIC_UMPC
9568 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9571 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9572 GDK_SELECTION_CLIPBOARD, NULL);
9576 static void compose_allsel_cb(GtkAction *action, gpointer data)
9578 Compose *compose = (Compose *)data;
9579 if (compose->focused_editable
9580 #ifndef GENERIC_UMPC
9581 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9584 entry_allsel(compose->focused_editable);
9587 static void textview_move_beginning_of_line (GtkTextView *text)
9589 GtkTextBuffer *buffer;
9593 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9595 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9596 mark = gtk_text_buffer_get_insert(buffer);
9597 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9598 gtk_text_iter_set_line_offset(&ins, 0);
9599 gtk_text_buffer_place_cursor(buffer, &ins);
9602 static void textview_move_forward_character (GtkTextView *text)
9604 GtkTextBuffer *buffer;
9608 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9610 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9611 mark = gtk_text_buffer_get_insert(buffer);
9612 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9613 if (gtk_text_iter_forward_cursor_position(&ins))
9614 gtk_text_buffer_place_cursor(buffer, &ins);
9617 static void textview_move_backward_character (GtkTextView *text)
9619 GtkTextBuffer *buffer;
9623 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9625 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9626 mark = gtk_text_buffer_get_insert(buffer);
9627 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9628 if (gtk_text_iter_backward_cursor_position(&ins))
9629 gtk_text_buffer_place_cursor(buffer, &ins);
9632 static void textview_move_forward_word (GtkTextView *text)
9634 GtkTextBuffer *buffer;
9639 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9641 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9642 mark = gtk_text_buffer_get_insert(buffer);
9643 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9644 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9645 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9646 gtk_text_iter_backward_word_start(&ins);
9647 gtk_text_buffer_place_cursor(buffer, &ins);
9651 static void textview_move_backward_word (GtkTextView *text)
9653 GtkTextBuffer *buffer;
9658 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9660 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9661 mark = gtk_text_buffer_get_insert(buffer);
9662 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9663 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9664 if (gtk_text_iter_backward_word_starts(&ins, 1))
9665 gtk_text_buffer_place_cursor(buffer, &ins);
9668 static void textview_move_end_of_line (GtkTextView *text)
9670 GtkTextBuffer *buffer;
9674 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9676 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9677 mark = gtk_text_buffer_get_insert(buffer);
9678 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9679 if (gtk_text_iter_forward_to_line_end(&ins))
9680 gtk_text_buffer_place_cursor(buffer, &ins);
9683 static void textview_move_next_line (GtkTextView *text)
9685 GtkTextBuffer *buffer;
9690 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9692 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9693 mark = gtk_text_buffer_get_insert(buffer);
9694 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9695 offset = gtk_text_iter_get_line_offset(&ins);
9696 if (gtk_text_iter_forward_line(&ins)) {
9697 gtk_text_iter_set_line_offset(&ins, offset);
9698 gtk_text_buffer_place_cursor(buffer, &ins);
9702 static void textview_move_previous_line (GtkTextView *text)
9704 GtkTextBuffer *buffer;
9709 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9711 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9712 mark = gtk_text_buffer_get_insert(buffer);
9713 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9714 offset = gtk_text_iter_get_line_offset(&ins);
9715 if (gtk_text_iter_backward_line(&ins)) {
9716 gtk_text_iter_set_line_offset(&ins, offset);
9717 gtk_text_buffer_place_cursor(buffer, &ins);
9721 static void textview_delete_forward_character (GtkTextView *text)
9723 GtkTextBuffer *buffer;
9725 GtkTextIter ins, end_iter;
9727 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9729 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9730 mark = gtk_text_buffer_get_insert(buffer);
9731 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9733 if (gtk_text_iter_forward_char(&end_iter)) {
9734 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9738 static void textview_delete_backward_character (GtkTextView *text)
9740 GtkTextBuffer *buffer;
9742 GtkTextIter ins, end_iter;
9744 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9746 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9747 mark = gtk_text_buffer_get_insert(buffer);
9748 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9750 if (gtk_text_iter_backward_char(&end_iter)) {
9751 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9755 static void textview_delete_forward_word (GtkTextView *text)
9757 GtkTextBuffer *buffer;
9759 GtkTextIter ins, end_iter;
9761 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9763 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9764 mark = gtk_text_buffer_get_insert(buffer);
9765 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9767 if (gtk_text_iter_forward_word_end(&end_iter)) {
9768 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9772 static void textview_delete_backward_word (GtkTextView *text)
9774 GtkTextBuffer *buffer;
9776 GtkTextIter ins, end_iter;
9778 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9780 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9781 mark = gtk_text_buffer_get_insert(buffer);
9782 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9784 if (gtk_text_iter_backward_word_start(&end_iter)) {
9785 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9789 static void textview_delete_line (GtkTextView *text)
9791 GtkTextBuffer *buffer;
9793 GtkTextIter ins, start_iter, end_iter;
9795 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9797 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9798 mark = gtk_text_buffer_get_insert(buffer);
9799 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9802 gtk_text_iter_set_line_offset(&start_iter, 0);
9805 if (gtk_text_iter_ends_line(&end_iter)){
9806 if (!gtk_text_iter_forward_char(&end_iter))
9807 gtk_text_iter_backward_char(&start_iter);
9810 gtk_text_iter_forward_to_line_end(&end_iter);
9811 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9814 static void textview_delete_to_line_end (GtkTextView *text)
9816 GtkTextBuffer *buffer;
9818 GtkTextIter ins, end_iter;
9820 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9822 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9823 mark = gtk_text_buffer_get_insert(buffer);
9824 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9826 if (gtk_text_iter_ends_line(&end_iter))
9827 gtk_text_iter_forward_char(&end_iter);
9829 gtk_text_iter_forward_to_line_end(&end_iter);
9830 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9833 #define DO_ACTION(name, act) { \
9834 if(!strcmp(name, a_name)) { \
9838 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9840 const gchar *a_name = gtk_action_get_name(action);
9841 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9842 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9843 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9844 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9845 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9846 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9847 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9848 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9849 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9850 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9851 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9852 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9853 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9854 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9858 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9860 Compose *compose = (Compose *)data;
9861 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9862 ComposeCallAdvancedAction action = -1;
9864 action = compose_call_advanced_action_from_path(gaction);
9867 void (*do_action) (GtkTextView *text);
9868 } action_table[] = {
9869 {textview_move_beginning_of_line},
9870 {textview_move_forward_character},
9871 {textview_move_backward_character},
9872 {textview_move_forward_word},
9873 {textview_move_backward_word},
9874 {textview_move_end_of_line},
9875 {textview_move_next_line},
9876 {textview_move_previous_line},
9877 {textview_delete_forward_character},
9878 {textview_delete_backward_character},
9879 {textview_delete_forward_word},
9880 {textview_delete_backward_word},
9881 {textview_delete_line},
9882 {textview_delete_to_line_end}
9885 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9887 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9888 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9889 if (action_table[action].do_action)
9890 action_table[action].do_action(text);
9892 g_warning("Not implemented yet.");
9896 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9900 if (GTK_IS_EDITABLE(widget)) {
9901 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9902 gtk_editable_set_position(GTK_EDITABLE(widget),
9905 if (widget->parent && widget->parent->parent
9906 && widget->parent->parent->parent) {
9907 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9908 gint y = widget->allocation.y;
9909 gint height = widget->allocation.height;
9910 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9911 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9913 if (y < (int)shown->value) {
9914 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9916 if (y + height > (int)shown->value + (int)shown->page_size) {
9917 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9918 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9919 y + height - (int)shown->page_size - 1);
9921 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9922 (int)shown->upper - (int)shown->page_size - 1);
9929 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9930 compose->focused_editable = widget;
9933 if (GTK_IS_TEXT_VIEW(widget)
9934 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9935 g_object_ref(compose->notebook);
9936 g_object_ref(compose->edit_vbox);
9937 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9938 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9939 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9940 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9941 g_object_unref(compose->notebook);
9942 g_object_unref(compose->edit_vbox);
9943 g_signal_handlers_block_by_func(G_OBJECT(widget),
9944 G_CALLBACK(compose_grab_focus_cb),
9946 gtk_widget_grab_focus(widget);
9947 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9948 G_CALLBACK(compose_grab_focus_cb),
9950 } else if (!GTK_IS_TEXT_VIEW(widget)
9951 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9952 g_object_ref(compose->notebook);
9953 g_object_ref(compose->edit_vbox);
9954 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9955 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9956 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9957 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9958 g_object_unref(compose->notebook);
9959 g_object_unref(compose->edit_vbox);
9960 g_signal_handlers_block_by_func(G_OBJECT(widget),
9961 G_CALLBACK(compose_grab_focus_cb),
9963 gtk_widget_grab_focus(widget);
9964 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9965 G_CALLBACK(compose_grab_focus_cb),
9971 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9973 compose->modified = TRUE;
9974 #ifndef GENERIC_UMPC
9975 compose_set_title(compose);
9979 static void compose_wrap_cb(GtkAction *action, gpointer data)
9981 Compose *compose = (Compose *)data;
9982 compose_beautify_paragraph(compose, NULL, TRUE);
9985 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9987 Compose *compose = (Compose *)data;
9988 compose_wrap_all_full(compose, TRUE);
9991 static void compose_find_cb(GtkAction *action, gpointer data)
9993 Compose *compose = (Compose *)data;
9995 message_search_compose(compose);
9998 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10001 Compose *compose = (Compose *)data;
10002 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10003 if (compose->autowrap)
10004 compose_wrap_all_full(compose, TRUE);
10005 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10008 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10011 Compose *compose = (Compose *)data;
10012 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10015 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10017 Compose *compose = (Compose *)data;
10019 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10022 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10024 Compose *compose = (Compose *)data;
10026 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10029 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10031 g_free(compose->privacy_system);
10033 compose->privacy_system = g_strdup(account->default_privacy_system);
10034 compose_update_privacy_system_menu_item(compose, warn);
10037 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10039 Compose *compose = (Compose *)data;
10041 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10042 gtk_widget_show(compose->ruler_hbox);
10043 prefs_common.show_ruler = TRUE;
10045 gtk_widget_hide(compose->ruler_hbox);
10046 gtk_widget_queue_resize(compose->edit_vbox);
10047 prefs_common.show_ruler = FALSE;
10051 static void compose_attach_drag_received_cb (GtkWidget *widget,
10052 GdkDragContext *context,
10055 GtkSelectionData *data,
10058 gpointer user_data)
10060 Compose *compose = (Compose *)user_data;
10063 if (gdk_atom_name(data->type) &&
10064 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10065 && gtk_drag_get_source_widget(context) !=
10066 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10067 list = uri_list_extract_filenames((const gchar *)data->data);
10068 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10069 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10070 compose_attach_append
10071 (compose, (const gchar *)tmp->data,
10072 utf8_filename, NULL);
10073 g_free(utf8_filename);
10075 if (list) compose_changed_cb(NULL, compose);
10076 list_free_strings(list);
10078 } else if (gtk_drag_get_source_widget(context)
10079 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10080 /* comes from our summaryview */
10081 SummaryView * summaryview = NULL;
10082 GSList * list = NULL, *cur = NULL;
10084 if (mainwindow_get_mainwindow())
10085 summaryview = mainwindow_get_mainwindow()->summaryview;
10088 list = summary_get_selected_msg_list(summaryview);
10090 for (cur = list; cur; cur = cur->next) {
10091 MsgInfo *msginfo = (MsgInfo *)cur->data;
10092 gchar *file = NULL;
10094 file = procmsg_get_message_file_full(msginfo,
10097 compose_attach_append(compose, (const gchar *)file,
10098 (const gchar *)file, "message/rfc822");
10102 g_slist_free(list);
10106 static gboolean compose_drag_drop(GtkWidget *widget,
10107 GdkDragContext *drag_context,
10109 guint time, gpointer user_data)
10111 /* not handling this signal makes compose_insert_drag_received_cb
10116 static void compose_insert_drag_received_cb (GtkWidget *widget,
10117 GdkDragContext *drag_context,
10120 GtkSelectionData *data,
10123 gpointer user_data)
10125 Compose *compose = (Compose *)user_data;
10128 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10130 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10131 AlertValue val = G_ALERTDEFAULT;
10133 list = uri_list_extract_filenames((const gchar *)data->data);
10135 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10136 /* Assume a list of no files, and data has ://, is a remote link */
10137 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10138 gchar *tmpfile = get_tmp_file();
10139 str_write_to_file(tmpdata, tmpfile);
10141 compose_insert_file(compose, tmpfile);
10142 claws_unlink(tmpfile);
10144 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10145 compose_beautify_paragraph(compose, NULL, TRUE);
10148 switch (prefs_common.compose_dnd_mode) {
10149 case COMPOSE_DND_ASK:
10150 val = alertpanel_full(_("Insert or attach?"),
10151 _("Do you want to insert the contents of the file(s) "
10152 "into the message body, or attach it to the email?"),
10153 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10154 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10156 case COMPOSE_DND_INSERT:
10157 val = G_ALERTALTERNATE;
10159 case COMPOSE_DND_ATTACH:
10160 val = G_ALERTOTHER;
10163 /* unexpected case */
10164 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10167 if (val & G_ALERTDISABLE) {
10168 val &= ~G_ALERTDISABLE;
10169 /* remember what action to perform by default, only if we don't click Cancel */
10170 if (val == G_ALERTALTERNATE)
10171 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10172 else if (val == G_ALERTOTHER)
10173 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10176 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10177 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10178 list_free_strings(list);
10181 } else if (val == G_ALERTOTHER) {
10182 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10183 list_free_strings(list);
10188 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10189 compose_insert_file(compose, (const gchar *)tmp->data);
10191 list_free_strings(list);
10193 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10196 #if GTK_CHECK_VERSION(2, 8, 0)
10197 /* do nothing, handled by GTK */
10199 gchar *tmpfile = get_tmp_file();
10200 str_write_to_file((const gchar *)data->data, tmpfile);
10201 compose_insert_file(compose, tmpfile);
10202 claws_unlink(tmpfile);
10204 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10208 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10211 static void compose_header_drag_received_cb (GtkWidget *widget,
10212 GdkDragContext *drag_context,
10215 GtkSelectionData *data,
10218 gpointer user_data)
10220 GtkEditable *entry = (GtkEditable *)user_data;
10221 gchar *email = (gchar *)data->data;
10223 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10226 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10227 gchar *decoded=g_new(gchar, strlen(email));
10230 email += strlen("mailto:");
10231 decode_uri(decoded, email); /* will fit */
10232 gtk_editable_delete_text(entry, 0, -1);
10233 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10234 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10238 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10241 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10243 Compose *compose = (Compose *)data;
10245 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10246 compose->return_receipt = TRUE;
10248 compose->return_receipt = FALSE;
10251 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10253 Compose *compose = (Compose *)data;
10255 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10256 compose->remove_references = TRUE;
10258 compose->remove_references = FALSE;
10261 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10262 GdkEventKey *event,
10263 ComposeHeaderEntry *headerentry)
10265 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10266 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10267 !(event->state & GDK_MODIFIER_MASK) &&
10268 (event->keyval == GDK_BackSpace) &&
10269 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10270 gtk_container_remove
10271 (GTK_CONTAINER(headerentry->compose->header_table),
10272 headerentry->combo);
10273 gtk_container_remove
10274 (GTK_CONTAINER(headerentry->compose->header_table),
10275 headerentry->entry);
10276 headerentry->compose->header_list =
10277 g_slist_remove(headerentry->compose->header_list,
10279 g_free(headerentry);
10280 } else if (event->keyval == GDK_Tab) {
10281 if (headerentry->compose->header_last == headerentry) {
10282 /* Override default next focus, and give it to subject_entry
10283 * instead of notebook tabs
10285 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10286 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10293 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10294 ComposeHeaderEntry *headerentry)
10296 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10297 compose_create_header_entry(headerentry->compose);
10298 g_signal_handlers_disconnect_matched
10299 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10300 0, 0, NULL, NULL, headerentry);
10302 /* Automatically scroll down */
10303 compose_show_first_last_header(headerentry->compose, FALSE);
10309 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10311 GtkAdjustment *vadj;
10313 g_return_if_fail(compose);
10314 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10315 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10317 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10318 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10319 gtk_adjustment_changed(vadj);
10322 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10323 const gchar *text, gint len, Compose *compose)
10325 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10326 (G_OBJECT(compose->text), "paste_as_quotation"));
10329 g_return_if_fail(text != NULL);
10331 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10332 G_CALLBACK(text_inserted),
10334 if (paste_as_quotation) {
10336 const gchar *qmark;
10338 GtkTextIter start_iter;
10341 len = strlen(text);
10343 new_text = g_strndup(text, len);
10345 qmark = compose_quote_char_from_context(compose);
10347 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10348 gtk_text_buffer_place_cursor(buffer, iter);
10350 pos = gtk_text_iter_get_offset(iter);
10352 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10353 _("Quote format error at line %d."));
10354 quote_fmt_reset_vartable();
10356 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10357 GINT_TO_POINTER(paste_as_quotation - 1));
10359 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10360 gtk_text_buffer_place_cursor(buffer, iter);
10361 gtk_text_buffer_delete_mark(buffer, mark);
10363 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10364 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10365 compose_beautify_paragraph(compose, &start_iter, FALSE);
10366 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10367 gtk_text_buffer_delete_mark(buffer, mark);
10369 if (strcmp(text, "\n") || compose->automatic_break
10370 || gtk_text_iter_starts_line(iter)) {
10371 GtkTextIter before_ins;
10372 gtk_text_buffer_insert(buffer, iter, text, len);
10373 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10374 before_ins = *iter;
10375 gtk_text_iter_backward_chars(&before_ins, len);
10376 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10379 /* check if the preceding is just whitespace or quote */
10380 GtkTextIter start_line;
10381 gchar *tmp = NULL, *quote = NULL;
10382 gint quote_len = 0, is_normal = 0;
10383 start_line = *iter;
10384 gtk_text_iter_set_line_offset(&start_line, 0);
10385 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10388 if (*tmp == '\0') {
10391 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10399 gtk_text_buffer_insert(buffer, iter, text, len);
10401 gtk_text_buffer_insert_with_tags_by_name(buffer,
10402 iter, text, len, "no_join", NULL);
10407 if (!paste_as_quotation) {
10408 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10409 compose_beautify_paragraph(compose, iter, FALSE);
10410 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10411 gtk_text_buffer_delete_mark(buffer, mark);
10414 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10415 G_CALLBACK(text_inserted),
10417 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10419 if (prefs_common.autosave &&
10420 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10421 compose->draft_timeout_tag != -2 /* disabled while loading */)
10422 compose->draft_timeout_tag = g_timeout_add
10423 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10425 static gint compose_defer_auto_save_draft(Compose *compose)
10427 compose->draft_timeout_tag = -1;
10428 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10433 static void compose_check_all(GtkAction *action, gpointer data)
10435 Compose *compose = (Compose *)data;
10436 if (compose->gtkaspell)
10437 gtkaspell_check_all(compose->gtkaspell);
10440 static void compose_highlight_all(GtkAction *action, gpointer data)
10442 Compose *compose = (Compose *)data;
10443 if (compose->gtkaspell)
10444 gtkaspell_highlight_all(compose->gtkaspell);
10447 static void compose_check_backwards(GtkAction *action, gpointer data)
10449 Compose *compose = (Compose *)data;
10450 if (compose->gtkaspell)
10451 gtkaspell_check_backwards(compose->gtkaspell);
10453 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10457 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10459 Compose *compose = (Compose *)data;
10460 if (compose->gtkaspell)
10461 gtkaspell_check_forwards_go(compose->gtkaspell);
10463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10469 *\brief Guess originating forward account from MsgInfo and several
10470 * "common preference" settings. Return NULL if no guess.
10472 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10474 PrefsAccount *account = NULL;
10476 g_return_val_if_fail(msginfo, NULL);
10477 g_return_val_if_fail(msginfo->folder, NULL);
10478 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10480 if (msginfo->folder->prefs->enable_default_account)
10481 account = account_find_from_id(msginfo->folder->prefs->default_account);
10484 account = msginfo->folder->folder->account;
10486 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10488 Xstrdup_a(to, msginfo->to, return NULL);
10489 extract_address(to);
10490 account = account_find_from_address(to, FALSE);
10493 if (!account && prefs_common.forward_account_autosel) {
10494 gchar cc[BUFFSIZE];
10495 if (!procheader_get_header_from_msginfo
10496 (msginfo, cc,sizeof cc , "Cc:")) {
10497 gchar *buf = cc + strlen("Cc:");
10498 extract_address(buf);
10499 account = account_find_from_address(buf, FALSE);
10503 if (!account && prefs_common.forward_account_autosel) {
10504 gchar deliveredto[BUFFSIZE];
10505 if (!procheader_get_header_from_msginfo
10506 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10507 gchar *buf = deliveredto + strlen("Delivered-To:");
10508 extract_address(buf);
10509 account = account_find_from_address(buf, FALSE);
10516 gboolean compose_close(Compose *compose)
10520 if (!g_mutex_trylock(compose->mutex)) {
10521 /* we have to wait for the (possibly deferred by auto-save)
10522 * drafting to be done, before destroying the compose under
10524 debug_print("waiting for drafting to finish...\n");
10525 compose_allow_user_actions(compose, FALSE);
10526 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10529 g_return_val_if_fail(compose, FALSE);
10530 gtkut_widget_get_uposition(compose->window, &x, &y);
10531 prefs_common.compose_x = x;
10532 prefs_common.compose_y = y;
10533 g_mutex_unlock(compose->mutex);
10534 compose_destroy(compose);
10539 * Add entry field for each address in list.
10540 * \param compose E-Mail composition object.
10541 * \param listAddress List of (formatted) E-Mail addresses.
10543 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10546 node = listAddress;
10548 addr = ( gchar * ) node->data;
10549 compose_entry_append( compose, addr, COMPOSE_TO );
10550 node = g_list_next( node );
10554 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10555 guint action, gboolean opening_multiple)
10557 gchar *body = NULL;
10558 GSList *new_msglist = NULL;
10559 MsgInfo *tmp_msginfo = NULL;
10560 gboolean originally_enc = FALSE;
10561 Compose *compose = NULL;
10563 g_return_if_fail(msgview != NULL);
10565 g_return_if_fail(msginfo_list != NULL);
10567 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10568 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10569 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10571 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10572 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10573 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10574 orig_msginfo, mimeinfo);
10575 if (tmp_msginfo != NULL) {
10576 new_msglist = g_slist_append(NULL, tmp_msginfo);
10578 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10579 tmp_msginfo->folder = orig_msginfo->folder;
10580 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10581 if (orig_msginfo->tags)
10582 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10587 if (!opening_multiple)
10588 body = messageview_get_selection(msgview);
10591 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10592 procmsg_msginfo_free(tmp_msginfo);
10593 g_slist_free(new_msglist);
10595 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10597 if (compose && originally_enc) {
10598 compose_force_encryption(compose, compose->account, FALSE);
10604 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10607 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10608 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10609 GSList *cur = msginfo_list;
10610 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10611 "messages. Opening the windows "
10612 "could take some time. Do you "
10613 "want to continue?"),
10614 g_slist_length(msginfo_list));
10615 if (g_slist_length(msginfo_list) > 9
10616 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10617 != G_ALERTALTERNATE) {
10622 /* We'll open multiple compose windows */
10623 /* let the WM place the next windows */
10624 compose_force_window_origin = FALSE;
10625 for (; cur; cur = cur->next) {
10627 tmplist.data = cur->data;
10628 tmplist.next = NULL;
10629 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10631 compose_force_window_origin = TRUE;
10633 /* forwarding multiple mails as attachments is done via a
10634 * single compose window */
10635 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10639 void compose_set_position(Compose *compose, gint pos)
10641 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10643 gtkut_text_view_set_position(text, pos);
10646 gboolean compose_search_string(Compose *compose,
10647 const gchar *str, gboolean case_sens)
10649 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10651 return gtkut_text_view_search_string(text, str, case_sens);
10654 gboolean compose_search_string_backward(Compose *compose,
10655 const gchar *str, gboolean case_sens)
10657 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10659 return gtkut_text_view_search_string_backward(text, str, case_sens);
10662 /* allocate a msginfo structure and populate its data from a compose data structure */
10663 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10665 MsgInfo *newmsginfo;
10667 gchar buf[BUFFSIZE];
10669 g_return_val_if_fail( compose != NULL, NULL );
10671 newmsginfo = procmsg_msginfo_new();
10674 get_rfc822_date(buf, sizeof(buf));
10675 newmsginfo->date = g_strdup(buf);
10678 if (compose->from_name) {
10679 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10680 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10684 if (compose->subject_entry)
10685 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10687 /* to, cc, reply-to, newsgroups */
10688 for (list = compose->header_list; list; list = list->next) {
10689 gchar *header = gtk_editable_get_chars(
10691 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10692 gchar *entry = gtk_editable_get_chars(
10693 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10695 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10696 if ( newmsginfo->to == NULL ) {
10697 newmsginfo->to = g_strdup(entry);
10698 } else if (entry && *entry) {
10699 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10700 g_free(newmsginfo->to);
10701 newmsginfo->to = tmp;
10704 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10705 if ( newmsginfo->cc == NULL ) {
10706 newmsginfo->cc = g_strdup(entry);
10707 } else if (entry && *entry) {
10708 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10709 g_free(newmsginfo->cc);
10710 newmsginfo->cc = tmp;
10713 if ( strcasecmp(header,
10714 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10715 if ( newmsginfo->newsgroups == NULL ) {
10716 newmsginfo->newsgroups = g_strdup(entry);
10717 } else if (entry && *entry) {
10718 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10719 g_free(newmsginfo->newsgroups);
10720 newmsginfo->newsgroups = tmp;
10728 /* other data is unset */
10734 /* update compose's dictionaries from folder dict settings */
10735 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10736 FolderItem *folder_item)
10738 g_return_if_fail(compose != NULL);
10740 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10741 FolderItemPrefs *prefs = folder_item->prefs;
10743 if (prefs->enable_default_dictionary)
10744 gtkaspell_change_dict(compose->gtkaspell,
10745 prefs->default_dictionary, FALSE);
10746 if (folder_item->prefs->enable_default_alt_dictionary)
10747 gtkaspell_change_alt_dict(compose->gtkaspell,
10748 prefs->default_alt_dictionary);
10749 if (prefs->enable_default_dictionary
10750 || prefs->enable_default_alt_dictionary)
10751 compose_spell_menu_changed(compose);