2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
165 #define B64_LINE_SIZE 57
166 #define B64_BUFFSIZE 77
168 #define MAX_REFERENCES_LEN 999
170 static GList *compose_list = NULL;
172 static Compose *compose_generic_new (PrefsAccount *account,
175 GPtrArray *attach_files,
176 GList *listAddress );
178 static Compose *compose_create (PrefsAccount *account,
183 static void compose_entry_mark_default_to (Compose *compose,
184 const gchar *address);
185 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
186 ComposeQuoteMode quote_mode,
190 static Compose *compose_forward_multiple (PrefsAccount *account,
191 GSList *msginfo_list);
192 static Compose *compose_reply (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
198 static Compose *compose_reply_mode (ComposeMode mode,
199 GSList *msginfo_list,
201 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
202 static void compose_update_privacy_systems_menu(Compose *compose);
204 static GtkWidget *compose_account_option_menu_create
206 static void compose_set_out_encoding (Compose *compose);
207 static void compose_set_template_menu (Compose *compose);
208 static void compose_destroy (Compose *compose);
210 static void compose_entries_set (Compose *compose,
212 ComposeEntryType to_type);
213 static gint compose_parse_header (Compose *compose,
215 static gchar *compose_parse_references (const gchar *ref,
218 static gchar *compose_quote_fmt (Compose *compose,
224 gboolean need_unescape,
225 const gchar *err_msg);
227 static void compose_reply_set_entry (Compose *compose,
233 followup_and_reply_to);
234 static void compose_reedit_set_entry (Compose *compose,
237 static void compose_insert_sig (Compose *compose,
239 static ComposeInsertResult compose_insert_file (Compose *compose,
242 static gboolean compose_attach_append (Compose *compose,
245 const gchar *content_type);
246 static void compose_attach_parts (Compose *compose,
249 static gboolean compose_beautify_paragraph (Compose *compose,
250 GtkTextIter *par_iter,
252 static void compose_wrap_all (Compose *compose);
253 static void compose_wrap_all_full (Compose *compose,
256 static void compose_set_title (Compose *compose);
257 static void compose_select_account (Compose *compose,
258 PrefsAccount *account,
261 static PrefsAccount *compose_current_mail_account(void);
262 /* static gint compose_send (Compose *compose); */
263 static gboolean compose_check_for_valid_recipient
265 static gboolean compose_check_entries (Compose *compose,
266 gboolean check_everything);
267 static gint compose_write_to_file (Compose *compose,
270 gboolean attach_parts);
271 static gint compose_write_body_to_file (Compose *compose,
273 static gint compose_remove_reedit_target (Compose *compose,
275 static void compose_remove_draft (Compose *compose);
276 static gint compose_queue_sub (Compose *compose,
280 gboolean check_subject,
281 gboolean remove_reedit_target);
282 static int compose_add_attachments (Compose *compose,
284 static gchar *compose_get_header (Compose *compose);
286 static void compose_convert_header (Compose *compose,
291 gboolean addr_field);
293 static void compose_attach_info_free (AttachInfo *ainfo);
294 static void compose_attach_remove_selected (GtkAction *action,
297 static void compose_attach_property (GtkAction *action,
299 static void compose_attach_property_create (gboolean *cancelled);
300 static void attach_property_ok (GtkWidget *widget,
301 gboolean *cancelled);
302 static void attach_property_cancel (GtkWidget *widget,
303 gboolean *cancelled);
304 static gint attach_property_delete_event (GtkWidget *widget,
306 gboolean *cancelled);
307 static gboolean attach_property_key_pressed (GtkWidget *widget,
309 gboolean *cancelled);
311 static void compose_exec_ext_editor (Compose *compose);
313 static gint compose_exec_ext_editor_real (const gchar *file);
314 static gboolean compose_ext_editor_kill (Compose *compose);
315 static gboolean compose_input_cb (GIOChannel *source,
316 GIOCondition condition,
318 static void compose_set_ext_editor_sensitive (Compose *compose,
320 #endif /* G_OS_UNIX */
322 static void compose_undo_state_changed (UndoMain *undostruct,
327 static void compose_create_header_entry (Compose *compose);
328 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
329 static void compose_remove_header_entries(Compose *compose);
331 static void compose_update_priority_menu_item(Compose * compose);
333 static void compose_spell_menu_changed (void *data);
334 static void compose_dict_changed (void *data);
336 static void compose_add_field_list ( Compose *compose,
337 GList *listAddress );
339 /* callback functions */
341 static gboolean compose_edit_size_alloc (GtkEditable *widget,
342 GtkAllocation *allocation,
343 GtkSHRuler *shruler);
344 static void account_activated (GtkComboBox *optmenu,
346 static void attach_selected (GtkTreeView *tree_view,
347 GtkTreePath *tree_path,
348 GtkTreeViewColumn *column,
350 static gboolean attach_button_pressed (GtkWidget *widget,
351 GdkEventButton *event,
353 static gboolean attach_key_pressed (GtkWidget *widget,
356 static void compose_send_cb (GtkAction *action, gpointer data);
357 static void compose_send_later_cb (GtkAction *action, gpointer data);
359 static void compose_save_cb (GtkAction *action,
362 static void compose_attach_cb (GtkAction *action,
364 static void compose_insert_file_cb (GtkAction *action,
366 static void compose_insert_sig_cb (GtkAction *action,
369 static void compose_close_cb (GtkAction *action,
372 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
374 static void compose_address_cb (GtkAction *action,
376 static void about_show_cb (GtkAction *action,
378 static void compose_template_activate_cb(GtkWidget *widget,
381 static void compose_ext_editor_cb (GtkAction *action,
384 static gint compose_delete_cb (GtkWidget *widget,
388 static void compose_undo_cb (GtkAction *action,
390 static void compose_redo_cb (GtkAction *action,
392 static void compose_cut_cb (GtkAction *action,
394 static void compose_copy_cb (GtkAction *action,
396 static void compose_paste_cb (GtkAction *action,
398 static void compose_paste_as_quote_cb (GtkAction *action,
400 static void compose_paste_no_wrap_cb (GtkAction *action,
402 static void compose_paste_wrap_cb (GtkAction *action,
404 static void compose_allsel_cb (GtkAction *action,
407 static void compose_advanced_action_cb (GtkAction *action,
410 static void compose_grab_focus_cb (GtkWidget *widget,
413 static void compose_changed_cb (GtkTextBuffer *textbuf,
416 static void compose_wrap_cb (GtkAction *action,
418 static void compose_wrap_all_cb (GtkAction *action,
420 static void compose_find_cb (GtkAction *action,
422 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
424 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
427 static void compose_toggle_ruler_cb (GtkToggleAction *action,
429 static void compose_toggle_sign_cb (GtkToggleAction *action,
431 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
433 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
434 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
435 static void activate_privacy_system (Compose *compose,
436 PrefsAccount *account,
438 static void compose_use_signing(Compose *compose, gboolean use_signing);
439 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
440 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
442 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
444 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
445 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
446 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_attach_drag_received_cb (GtkWidget *widget,
449 GdkDragContext *drag_context,
452 GtkSelectionData *data,
456 static void compose_insert_drag_received_cb (GtkWidget *widget,
457 GdkDragContext *drag_context,
460 GtkSelectionData *data,
464 static void compose_header_drag_received_cb (GtkWidget *widget,
465 GdkDragContext *drag_context,
468 GtkSelectionData *data,
473 static gboolean compose_drag_drop (GtkWidget *widget,
474 GdkDragContext *drag_context,
476 guint time, gpointer user_data);
478 static void text_inserted (GtkTextBuffer *buffer,
483 static Compose *compose_generic_reply(MsgInfo *msginfo,
484 ComposeQuoteMode quote_mode,
488 gboolean followup_and_reply_to,
491 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
492 ComposeHeaderEntry *headerentry);
493 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
496 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
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_("Auto _indent"), 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, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("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, N_("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, N_("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, N_("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 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
893 if (folderidentifier) {
894 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
895 prefs_common.compose_save_to_history = add_history(
896 prefs_common.compose_save_to_history, folderidentifier);
897 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
898 prefs_common.compose_save_to_history);
901 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
902 if (folderidentifier)
903 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
905 gtk_entry_set_text(GTK_ENTRY(entry), "");
908 static gchar *compose_get_save_to(Compose *compose)
911 gchar *result = NULL;
912 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
913 result = gtk_editable_get_chars(entry, 0, -1);
916 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
917 prefs_common.compose_save_to_history = add_history(
918 prefs_common.compose_save_to_history, result);
919 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
920 prefs_common.compose_save_to_history);
925 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
926 GPtrArray *attach_files, GList *listAddress )
929 GtkTextView *textview;
930 GtkTextBuffer *textbuf;
932 const gchar *subject_format = NULL;
933 const gchar *body_format = NULL;
934 gchar *mailto_from = NULL;
935 PrefsAccount *mailto_account = NULL;
936 MsgInfo* dummyinfo = NULL;
938 /* check if mailto defines a from */
939 if (mailto && *mailto != '\0') {
940 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
941 /* mailto defines a from, check if we can get account prefs from it,
942 if not, the account prefs will be guessed using other ways, but we'll keep
945 mailto_account = account_find_from_address(mailto_from, TRUE);
947 account = mailto_account;
950 /* if no account prefs set from mailto, set if from folder prefs (if any) */
951 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
952 account = account_find_from_id(item->prefs->default_account);
954 /* if no account prefs set, fallback to the current one */
955 if (!account) account = cur_account;
956 cm_return_val_if_fail(account != NULL, NULL);
958 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
960 /* override from name if mailto asked for it */
962 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
965 /* override from name according to folder properties */
966 if (item && item->prefs &&
967 item->prefs->compose_with_format &&
968 item->prefs->compose_override_from_format &&
969 *item->prefs->compose_override_from_format != '\0') {
974 dummyinfo = compose_msginfo_new_from_compose(compose);
976 /* decode \-escape sequences in the internal representation of the quote format */
977 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
978 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
981 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
984 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
986 quote_fmt_scan_string(tmp);
989 buf = quote_fmt_get_buffer();
991 alertpanel_error(_("New message From format error."));
993 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
994 quote_fmt_reset_vartable();
999 compose->replyinfo = NULL;
1000 compose->fwdinfo = NULL;
1002 textview = GTK_TEXT_VIEW(compose->text);
1003 textbuf = gtk_text_view_get_buffer(textview);
1004 compose_create_tags(textview, compose);
1006 undo_block(compose->undostruct);
1008 compose_set_dictionaries_from_folder_prefs(compose, item);
1011 if (account->auto_sig)
1012 compose_insert_sig(compose, FALSE);
1013 gtk_text_buffer_get_start_iter(textbuf, &iter);
1014 gtk_text_buffer_place_cursor(textbuf, &iter);
1016 if (account->protocol != A_NNTP) {
1017 if (mailto && *mailto != '\0') {
1018 compose_entries_set(compose, mailto, COMPOSE_TO);
1020 } else if (item && item->prefs) {
1021 if (item->prefs->enable_default_bcc) {
1022 compose_entry_append(compose, item->prefs->default_bcc, COMPOSE_BCC);
1024 if (item->prefs->enable_default_cc) {
1025 compose_entry_append(compose, item->prefs->default_cc, COMPOSE_CC);
1027 if (item->prefs->enable_default_replyto) {
1028 compose_entry_append(compose, item->prefs->default_replyto, COMPOSE_REPLYTO);
1030 if (item->prefs->enable_default_to) {
1031 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1032 compose_entry_mark_default_to(compose, item->prefs->default_to);
1035 if (item && item->ret_rcpt) {
1036 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1039 if (mailto && *mailto != '\0') {
1040 if (!strchr(mailto, '@'))
1041 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1043 compose_entries_set(compose, mailto, COMPOSE_TO);
1044 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1045 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1048 * CLAWS: just don't allow return receipt request, even if the user
1049 * may want to send an email. simple but foolproof.
1051 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1053 compose_add_field_list( compose, listAddress );
1055 if (item && item->prefs && item->prefs->compose_with_format) {
1056 subject_format = item->prefs->compose_subject_format;
1057 body_format = item->prefs->compose_body_format;
1058 } else if (account->compose_with_format) {
1059 subject_format = account->compose_subject_format;
1060 body_format = account->compose_body_format;
1061 } else if (prefs_common.compose_with_format) {
1062 subject_format = prefs_common.compose_subject_format;
1063 body_format = prefs_common.compose_body_format;
1066 if (subject_format || body_format) {
1069 && *subject_format != '\0' )
1071 gchar *subject = NULL;
1076 dummyinfo = compose_msginfo_new_from_compose(compose);
1078 /* decode \-escape sequences in the internal representation of the quote format */
1079 tmp = malloc(strlen(subject_format)+1);
1080 pref_get_unescaped_pref(tmp, subject_format);
1082 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1084 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1085 compose->gtkaspell);
1087 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1089 quote_fmt_scan_string(tmp);
1092 buf = quote_fmt_get_buffer();
1094 alertpanel_error(_("New message subject format error."));
1096 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1097 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1098 quote_fmt_reset_vartable();
1105 && *body_format != '\0' )
1108 GtkTextBuffer *buffer;
1109 GtkTextIter start, end;
1113 dummyinfo = compose_msginfo_new_from_compose(compose);
1115 text = GTK_TEXT_VIEW(compose->text);
1116 buffer = gtk_text_view_get_buffer(text);
1117 gtk_text_buffer_get_start_iter(buffer, &start);
1118 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1119 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1121 compose_quote_fmt(compose, dummyinfo,
1123 NULL, tmp, FALSE, TRUE,
1124 _("The body of the \"New message\" template has an error at line %d."));
1125 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1126 quote_fmt_reset_vartable();
1132 procmsg_msginfo_free( dummyinfo );
1138 for (i = 0; i < attach_files->len; i++) {
1139 file = g_ptr_array_index(attach_files, i);
1140 compose_attach_append(compose, file, file, NULL);
1144 compose_show_first_last_header(compose, TRUE);
1146 /* Set save folder */
1147 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1148 gchar *folderidentifier;
1150 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1151 folderidentifier = folder_item_get_identifier(item);
1152 compose_set_save_to(compose, folderidentifier);
1153 g_free(folderidentifier);
1156 gtk_widget_grab_focus(compose->header_last->entry);
1158 undo_unblock(compose->undostruct);
1160 if (prefs_common.auto_exteditor)
1161 compose_exec_ext_editor(compose);
1163 compose->draft_timeout_tag = -1;
1164 SCROLL_TO_CURSOR(compose);
1166 compose->modified = FALSE;
1167 compose_set_title(compose);
1171 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1172 gboolean override_pref, const gchar *system)
1174 const gchar *privacy = NULL;
1176 cm_return_if_fail(compose != NULL);
1177 cm_return_if_fail(account != NULL);
1179 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1184 else if (account->default_privacy_system
1185 && strlen(account->default_privacy_system)) {
1186 privacy = account->default_privacy_system;
1188 GSList *privacy_avail = privacy_get_system_ids();
1189 if (privacy_avail && g_slist_length(privacy_avail)) {
1190 privacy = (gchar *)(privacy_avail->data);
1193 if (privacy != NULL) {
1195 g_free(compose->privacy_system);
1196 compose->privacy_system = NULL;
1198 if (compose->privacy_system == NULL)
1199 compose->privacy_system = g_strdup(privacy);
1200 else if (*(compose->privacy_system) == '\0') {
1201 g_free(compose->privacy_system);
1202 compose->privacy_system = g_strdup(privacy);
1204 compose_update_privacy_system_menu_item(compose, FALSE);
1205 compose_use_encryption(compose, TRUE);
1209 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1211 const gchar *privacy = NULL;
1215 else if (account->default_privacy_system
1216 && strlen(account->default_privacy_system)) {
1217 privacy = account->default_privacy_system;
1219 GSList *privacy_avail = privacy_get_system_ids();
1220 if (privacy_avail && g_slist_length(privacy_avail)) {
1221 privacy = (gchar *)(privacy_avail->data);
1225 if (privacy != NULL) {
1227 g_free(compose->privacy_system);
1228 compose->privacy_system = NULL;
1230 if (compose->privacy_system == NULL)
1231 compose->privacy_system = g_strdup(privacy);
1232 compose_update_privacy_system_menu_item(compose, FALSE);
1233 compose_use_signing(compose, TRUE);
1237 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1241 Compose *compose = NULL;
1243 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1245 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1246 cm_return_val_if_fail(msginfo != NULL, NULL);
1248 list_len = g_slist_length(msginfo_list);
1252 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1253 FALSE, prefs_common.default_reply_list, FALSE, body);
1255 case COMPOSE_REPLY_WITH_QUOTE:
1256 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1257 FALSE, prefs_common.default_reply_list, FALSE, body);
1259 case COMPOSE_REPLY_WITHOUT_QUOTE:
1260 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1261 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1263 case COMPOSE_REPLY_TO_SENDER:
1264 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1265 FALSE, FALSE, TRUE, body);
1267 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1268 compose = compose_followup_and_reply_to(msginfo,
1269 COMPOSE_QUOTE_CHECK,
1270 FALSE, FALSE, body);
1272 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1273 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1274 FALSE, FALSE, TRUE, body);
1276 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1277 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1278 FALSE, FALSE, TRUE, NULL);
1280 case COMPOSE_REPLY_TO_ALL:
1281 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1282 TRUE, FALSE, FALSE, body);
1284 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1285 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1286 TRUE, FALSE, FALSE, body);
1288 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1289 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1290 TRUE, FALSE, FALSE, NULL);
1292 case COMPOSE_REPLY_TO_LIST:
1293 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1294 FALSE, TRUE, FALSE, body);
1296 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1297 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1298 FALSE, TRUE, FALSE, body);
1300 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1301 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1302 FALSE, TRUE, FALSE, NULL);
1304 case COMPOSE_FORWARD:
1305 if (prefs_common.forward_as_attachment) {
1306 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1309 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1313 case COMPOSE_FORWARD_INLINE:
1314 /* check if we reply to more than one Message */
1315 if (list_len == 1) {
1316 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1319 /* more messages FALL THROUGH */
1320 case COMPOSE_FORWARD_AS_ATTACH:
1321 compose = compose_forward_multiple(NULL, msginfo_list);
1323 case COMPOSE_REDIRECT:
1324 compose = compose_redirect(NULL, msginfo, FALSE);
1327 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1330 if (compose == NULL) {
1331 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1335 compose->rmode = mode;
1336 switch (compose->rmode) {
1338 case COMPOSE_REPLY_WITH_QUOTE:
1339 case COMPOSE_REPLY_WITHOUT_QUOTE:
1340 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1341 debug_print("reply mode Normal\n");
1342 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1343 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1345 case COMPOSE_REPLY_TO_SENDER:
1346 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1347 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1348 debug_print("reply mode Sender\n");
1349 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1351 case COMPOSE_REPLY_TO_ALL:
1352 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1353 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1354 debug_print("reply mode All\n");
1355 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1357 case COMPOSE_REPLY_TO_LIST:
1358 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1359 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1360 debug_print("reply mode List\n");
1361 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1369 static Compose *compose_reply(MsgInfo *msginfo,
1370 ComposeQuoteMode quote_mode,
1376 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1377 to_sender, FALSE, body);
1380 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1381 ComposeQuoteMode quote_mode,
1386 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1387 to_sender, TRUE, body);
1390 static void compose_extract_original_charset(Compose *compose)
1392 MsgInfo *info = NULL;
1393 if (compose->replyinfo) {
1394 info = compose->replyinfo;
1395 } else if (compose->fwdinfo) {
1396 info = compose->fwdinfo;
1397 } else if (compose->targetinfo) {
1398 info = compose->targetinfo;
1401 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1402 MimeInfo *partinfo = mimeinfo;
1403 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1404 partinfo = procmime_mimeinfo_next(partinfo);
1406 compose->orig_charset =
1407 g_strdup(procmime_mimeinfo_get_parameter(
1408 partinfo, "charset"));
1410 procmime_mimeinfo_free_all(mimeinfo);
1414 #define SIGNAL_BLOCK(buffer) { \
1415 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1416 G_CALLBACK(compose_changed_cb), \
1418 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1419 G_CALLBACK(text_inserted), \
1423 #define SIGNAL_UNBLOCK(buffer) { \
1424 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1425 G_CALLBACK(compose_changed_cb), \
1427 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1428 G_CALLBACK(text_inserted), \
1432 static Compose *compose_generic_reply(MsgInfo *msginfo,
1433 ComposeQuoteMode quote_mode,
1434 gboolean to_all, gboolean to_ml,
1436 gboolean followup_and_reply_to,
1440 PrefsAccount *account = NULL;
1441 GtkTextView *textview;
1442 GtkTextBuffer *textbuf;
1443 gboolean quote = FALSE;
1444 const gchar *qmark = NULL;
1445 const gchar *body_fmt = NULL;
1446 gchar *s_system = NULL;
1448 cm_return_val_if_fail(msginfo != NULL, NULL);
1449 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1451 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1453 cm_return_val_if_fail(account != NULL, NULL);
1455 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1457 compose->updating = TRUE;
1459 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1460 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1462 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1463 if (!compose->replyinfo)
1464 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1466 compose_extract_original_charset(compose);
1468 if (msginfo->folder && msginfo->folder->ret_rcpt)
1469 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1471 /* Set save folder */
1472 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1473 gchar *folderidentifier;
1475 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1476 folderidentifier = folder_item_get_identifier(msginfo->folder);
1477 compose_set_save_to(compose, folderidentifier);
1478 g_free(folderidentifier);
1481 if (compose_parse_header(compose, msginfo) < 0) {
1482 compose->updating = FALSE;
1483 compose_destroy(compose);
1487 /* override from name according to folder properties */
1488 if (msginfo->folder && msginfo->folder->prefs &&
1489 msginfo->folder->prefs->reply_with_format &&
1490 msginfo->folder->prefs->reply_override_from_format &&
1491 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1496 /* decode \-escape sequences in the internal representation of the quote format */
1497 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1498 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1501 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1502 compose->gtkaspell);
1504 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1506 quote_fmt_scan_string(tmp);
1509 buf = quote_fmt_get_buffer();
1511 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1513 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1514 quote_fmt_reset_vartable();
1519 textview = (GTK_TEXT_VIEW(compose->text));
1520 textbuf = gtk_text_view_get_buffer(textview);
1521 compose_create_tags(textview, compose);
1523 undo_block(compose->undostruct);
1525 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1528 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1529 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1530 /* use the reply format of folder (if enabled), or the account's one
1531 (if enabled) or fallback to the global reply format, which is always
1532 enabled (even if empty), and use the relevant quotemark */
1534 if (msginfo->folder && msginfo->folder->prefs &&
1535 msginfo->folder->prefs->reply_with_format) {
1536 qmark = msginfo->folder->prefs->reply_quotemark;
1537 body_fmt = msginfo->folder->prefs->reply_body_format;
1539 } else if (account->reply_with_format) {
1540 qmark = account->reply_quotemark;
1541 body_fmt = account->reply_body_format;
1544 qmark = prefs_common.quotemark;
1545 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1546 body_fmt = gettext(prefs_common.quotefmt);
1553 /* empty quotemark is not allowed */
1554 if (qmark == NULL || *qmark == '\0')
1556 compose_quote_fmt(compose, compose->replyinfo,
1557 body_fmt, qmark, body, FALSE, TRUE,
1558 _("The body of the \"Reply\" template has an error at line %d."));
1559 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1560 quote_fmt_reset_vartable();
1563 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1564 compose_force_encryption(compose, account, FALSE, s_system);
1567 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1568 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1569 compose_force_signing(compose, account, s_system);
1573 SIGNAL_BLOCK(textbuf);
1575 if (account->auto_sig)
1576 compose_insert_sig(compose, FALSE);
1578 compose_wrap_all(compose);
1580 SIGNAL_UNBLOCK(textbuf);
1582 gtk_widget_grab_focus(compose->text);
1584 undo_unblock(compose->undostruct);
1586 if (prefs_common.auto_exteditor)
1587 compose_exec_ext_editor(compose);
1589 compose->modified = FALSE;
1590 compose_set_title(compose);
1592 compose->updating = FALSE;
1593 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1594 SCROLL_TO_CURSOR(compose);
1596 if (compose->deferred_destroy) {
1597 compose_destroy(compose);
1604 #define INSERT_FW_HEADER(var, hdr) \
1605 if (msginfo->var && *msginfo->var) { \
1606 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1607 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1608 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1611 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1612 gboolean as_attach, const gchar *body,
1613 gboolean no_extedit,
1617 GtkTextView *textview;
1618 GtkTextBuffer *textbuf;
1621 cm_return_val_if_fail(msginfo != NULL, NULL);
1622 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1625 !(account = compose_guess_forward_account_from_msginfo
1627 account = cur_account;
1629 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1631 compose->updating = TRUE;
1632 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1633 if (!compose->fwdinfo)
1634 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1636 compose_extract_original_charset(compose);
1638 if (msginfo->subject && *msginfo->subject) {
1639 gchar *buf, *buf2, *p;
1641 buf = p = g_strdup(msginfo->subject);
1642 p += subject_get_prefix_length(p);
1643 memmove(buf, p, strlen(p) + 1);
1645 buf2 = g_strdup_printf("Fw: %s", buf);
1646 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1652 /* override from name according to folder properties */
1653 if (msginfo->folder && msginfo->folder->prefs &&
1654 msginfo->folder->prefs->forward_with_format &&
1655 msginfo->folder->prefs->forward_override_from_format &&
1656 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1660 MsgInfo *full_msginfo = NULL;
1663 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1665 full_msginfo = procmsg_msginfo_copy(msginfo);
1667 /* decode \-escape sequences in the internal representation of the quote format */
1668 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1669 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1672 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1673 compose->gtkaspell);
1675 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1677 quote_fmt_scan_string(tmp);
1680 buf = quote_fmt_get_buffer();
1682 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1684 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1685 quote_fmt_reset_vartable();
1688 procmsg_msginfo_free(full_msginfo);
1691 textview = GTK_TEXT_VIEW(compose->text);
1692 textbuf = gtk_text_view_get_buffer(textview);
1693 compose_create_tags(textview, compose);
1695 undo_block(compose->undostruct);
1699 msgfile = procmsg_get_message_file(msginfo);
1700 if (!is_file_exist(msgfile))
1701 g_warning("%s: file not exist\n", msgfile);
1703 compose_attach_append(compose, msgfile, msgfile,
1708 const gchar *qmark = NULL;
1709 const gchar *body_fmt = NULL;
1710 MsgInfo *full_msginfo;
1712 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1713 body_fmt = gettext(prefs_common.fw_quotefmt);
1717 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1719 full_msginfo = procmsg_msginfo_copy(msginfo);
1721 /* use the forward format of folder (if enabled), or the account's one
1722 (if enabled) or fallback to the global forward format, which is always
1723 enabled (even if empty), and use the relevant quotemark */
1724 if (msginfo->folder && msginfo->folder->prefs &&
1725 msginfo->folder->prefs->forward_with_format) {
1726 qmark = msginfo->folder->prefs->forward_quotemark;
1727 body_fmt = msginfo->folder->prefs->forward_body_format;
1729 } else if (account->forward_with_format) {
1730 qmark = account->forward_quotemark;
1731 body_fmt = account->forward_body_format;
1734 qmark = prefs_common.fw_quotemark;
1735 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1736 body_fmt = gettext(prefs_common.fw_quotefmt);
1741 /* empty quotemark is not allowed */
1742 if (qmark == NULL || *qmark == '\0')
1745 compose_quote_fmt(compose, full_msginfo,
1746 body_fmt, qmark, body, FALSE, TRUE,
1747 _("The body of the \"Forward\" template has an error at line %d."));
1748 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1749 quote_fmt_reset_vartable();
1750 compose_attach_parts(compose, msginfo);
1752 procmsg_msginfo_free(full_msginfo);
1755 SIGNAL_BLOCK(textbuf);
1757 if (account->auto_sig)
1758 compose_insert_sig(compose, FALSE);
1760 compose_wrap_all(compose);
1762 SIGNAL_UNBLOCK(textbuf);
1764 gtk_text_buffer_get_start_iter(textbuf, &iter);
1765 gtk_text_buffer_place_cursor(textbuf, &iter);
1767 gtk_widget_grab_focus(compose->header_last->entry);
1769 if (!no_extedit && prefs_common.auto_exteditor)
1770 compose_exec_ext_editor(compose);
1773 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1774 gchar *folderidentifier;
1776 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1777 folderidentifier = folder_item_get_identifier(msginfo->folder);
1778 compose_set_save_to(compose, folderidentifier);
1779 g_free(folderidentifier);
1782 undo_unblock(compose->undostruct);
1784 compose->modified = FALSE;
1785 compose_set_title(compose);
1787 compose->updating = FALSE;
1788 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1789 SCROLL_TO_CURSOR(compose);
1791 if (compose->deferred_destroy) {
1792 compose_destroy(compose);
1799 #undef INSERT_FW_HEADER
1801 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1804 GtkTextView *textview;
1805 GtkTextBuffer *textbuf;
1809 gboolean single_mail = TRUE;
1811 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1813 if (g_slist_length(msginfo_list) > 1)
1814 single_mail = FALSE;
1816 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1817 if (((MsgInfo *)msginfo->data)->folder == NULL)
1820 /* guess account from first selected message */
1822 !(account = compose_guess_forward_account_from_msginfo
1823 (msginfo_list->data)))
1824 account = cur_account;
1826 cm_return_val_if_fail(account != NULL, NULL);
1828 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1829 if (msginfo->data) {
1830 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1831 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1835 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1836 g_warning("no msginfo_list");
1840 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1842 compose->updating = TRUE;
1844 /* override from name according to folder properties */
1845 if (msginfo_list->data) {
1846 MsgInfo *msginfo = msginfo_list->data;
1848 if (msginfo->folder && msginfo->folder->prefs &&
1849 msginfo->folder->prefs->forward_with_format &&
1850 msginfo->folder->prefs->forward_override_from_format &&
1851 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1856 /* decode \-escape sequences in the internal representation of the quote format */
1857 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1858 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1861 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1862 compose->gtkaspell);
1864 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1866 quote_fmt_scan_string(tmp);
1869 buf = quote_fmt_get_buffer();
1871 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1873 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1874 quote_fmt_reset_vartable();
1880 textview = GTK_TEXT_VIEW(compose->text);
1881 textbuf = gtk_text_view_get_buffer(textview);
1882 compose_create_tags(textview, compose);
1884 undo_block(compose->undostruct);
1885 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1886 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1888 if (!is_file_exist(msgfile))
1889 g_warning("%s: file not exist\n", msgfile);
1891 compose_attach_append(compose, msgfile, msgfile,
1897 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1898 if (info->subject && *info->subject) {
1899 gchar *buf, *buf2, *p;
1901 buf = p = g_strdup(info->subject);
1902 p += subject_get_prefix_length(p);
1903 memmove(buf, p, strlen(p) + 1);
1905 buf2 = g_strdup_printf("Fw: %s", buf);
1906 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1912 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1913 _("Fw: multiple emails"));
1916 SIGNAL_BLOCK(textbuf);
1918 if (account->auto_sig)
1919 compose_insert_sig(compose, FALSE);
1921 compose_wrap_all(compose);
1923 SIGNAL_UNBLOCK(textbuf);
1925 gtk_text_buffer_get_start_iter(textbuf, &iter);
1926 gtk_text_buffer_place_cursor(textbuf, &iter);
1928 gtk_widget_grab_focus(compose->header_last->entry);
1929 undo_unblock(compose->undostruct);
1930 compose->modified = FALSE;
1931 compose_set_title(compose);
1933 compose->updating = FALSE;
1934 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1935 SCROLL_TO_CURSOR(compose);
1937 if (compose->deferred_destroy) {
1938 compose_destroy(compose);
1945 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1947 GtkTextIter start = *iter;
1948 GtkTextIter end_iter;
1949 int start_pos = gtk_text_iter_get_offset(&start);
1951 if (!compose->account->sig_sep)
1954 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1955 start_pos+strlen(compose->account->sig_sep));
1957 /* check sig separator */
1958 str = gtk_text_iter_get_text(&start, &end_iter);
1959 if (!strcmp(str, compose->account->sig_sep)) {
1961 /* check end of line (\n) */
1962 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1963 start_pos+strlen(compose->account->sig_sep));
1964 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1965 start_pos+strlen(compose->account->sig_sep)+1);
1966 tmp = gtk_text_iter_get_text(&start, &end_iter);
1967 if (!strcmp(tmp,"\n")) {
1979 static void compose_colorize_signature(Compose *compose)
1981 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1983 GtkTextIter end_iter;
1984 gtk_text_buffer_get_start_iter(buffer, &iter);
1985 while (gtk_text_iter_forward_line(&iter))
1986 if (compose_is_sig_separator(compose, buffer, &iter)) {
1987 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1988 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1992 #define BLOCK_WRAP() { \
1993 prev_autowrap = compose->autowrap; \
1994 buffer = gtk_text_view_get_buffer( \
1995 GTK_TEXT_VIEW(compose->text)); \
1996 compose->autowrap = FALSE; \
1998 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1999 G_CALLBACK(compose_changed_cb), \
2001 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2002 G_CALLBACK(text_inserted), \
2005 #define UNBLOCK_WRAP() { \
2006 compose->autowrap = prev_autowrap; \
2007 if (compose->autowrap) { \
2008 gint old = compose->draft_timeout_tag; \
2009 compose->draft_timeout_tag = -2; \
2010 compose_wrap_all(compose); \
2011 compose->draft_timeout_tag = old; \
2014 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2015 G_CALLBACK(compose_changed_cb), \
2017 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2018 G_CALLBACK(text_inserted), \
2022 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2024 Compose *compose = NULL;
2025 PrefsAccount *account = NULL;
2026 GtkTextView *textview;
2027 GtkTextBuffer *textbuf;
2031 gchar buf[BUFFSIZE];
2032 gboolean use_signing = FALSE;
2033 gboolean use_encryption = FALSE;
2034 gchar *privacy_system = NULL;
2035 int priority = PRIORITY_NORMAL;
2036 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2037 gboolean autowrap = prefs_common.autowrap;
2038 gboolean autoindent = prefs_common.auto_indent;
2040 cm_return_val_if_fail(msginfo != NULL, NULL);
2041 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2043 if (compose_put_existing_to_front(msginfo)) {
2047 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2048 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2049 gchar queueheader_buf[BUFFSIZE];
2052 /* Select Account from queue headers */
2053 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2054 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2055 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2056 account = account_find_from_id(id);
2058 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2059 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2060 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2061 account = account_find_from_id(id);
2063 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2064 sizeof(queueheader_buf), "NAID:")) {
2065 id = atoi(&queueheader_buf[strlen("NAID:")]);
2066 account = account_find_from_id(id);
2068 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2069 sizeof(queueheader_buf), "MAID:")) {
2070 id = atoi(&queueheader_buf[strlen("MAID:")]);
2071 account = account_find_from_id(id);
2073 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2074 sizeof(queueheader_buf), "S:")) {
2075 account = account_find_from_address(queueheader_buf, FALSE);
2077 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2078 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2079 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2080 use_signing = param;
2083 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2084 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2085 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2086 use_signing = param;
2089 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2090 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2091 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2092 use_encryption = param;
2094 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2095 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2096 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2097 use_encryption = param;
2099 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2100 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2101 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2104 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2105 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2106 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2109 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2110 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2111 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2113 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2114 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2115 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2117 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2118 sizeof(queueheader_buf), "X-Priority: ")) {
2119 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2122 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2123 sizeof(queueheader_buf), "RMID:")) {
2124 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2125 if (tokens[0] && tokens[1] && tokens[2]) {
2126 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2127 if (orig_item != NULL) {
2128 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2133 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2134 sizeof(queueheader_buf), "FMID:")) {
2135 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2136 if (tokens[0] && tokens[1] && tokens[2]) {
2137 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2138 if (orig_item != NULL) {
2139 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2145 account = msginfo->folder->folder->account;
2148 if (!account && prefs_common.reedit_account_autosel) {
2149 gchar from[BUFFSIZE];
2150 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2151 extract_address(from);
2152 account = account_find_from_address(from, FALSE);
2156 account = cur_account;
2158 cm_return_val_if_fail(account != NULL, NULL);
2160 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2162 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2163 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2164 compose->autowrap = autowrap;
2165 compose->replyinfo = replyinfo;
2166 compose->fwdinfo = fwdinfo;
2168 compose->updating = TRUE;
2169 compose->priority = priority;
2171 if (privacy_system != NULL) {
2172 compose->privacy_system = privacy_system;
2173 compose_use_signing(compose, use_signing);
2174 compose_use_encryption(compose, use_encryption);
2175 compose_update_privacy_system_menu_item(compose, FALSE);
2177 activate_privacy_system(compose, account, FALSE);
2180 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2182 compose_extract_original_charset(compose);
2184 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2185 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2186 gchar queueheader_buf[BUFFSIZE];
2188 /* Set message save folder */
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2191 compose_set_save_to(compose, &queueheader_buf[4]);
2193 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2194 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2196 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2201 if (compose_parse_header(compose, msginfo) < 0) {
2202 compose->updating = FALSE;
2203 compose_destroy(compose);
2206 compose_reedit_set_entry(compose, msginfo);
2208 textview = GTK_TEXT_VIEW(compose->text);
2209 textbuf = gtk_text_view_get_buffer(textview);
2210 compose_create_tags(textview, compose);
2212 mark = gtk_text_buffer_get_insert(textbuf);
2213 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2215 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2216 G_CALLBACK(compose_changed_cb),
2219 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2220 fp = procmime_get_first_encrypted_text_content(msginfo);
2222 compose_force_encryption(compose, account, TRUE, NULL);
2225 fp = procmime_get_first_text_content(msginfo);
2228 g_warning("Can't get text part\n");
2232 gboolean prev_autowrap = compose->autowrap;
2233 GtkTextBuffer *buffer = textbuf;
2235 while (fgets(buf, sizeof(buf), fp) != NULL) {
2237 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2243 compose_attach_parts(compose, msginfo);
2245 compose_colorize_signature(compose);
2247 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2248 G_CALLBACK(compose_changed_cb),
2251 gtk_widget_grab_focus(compose->text);
2253 if (prefs_common.auto_exteditor) {
2254 compose_exec_ext_editor(compose);
2256 compose->modified = FALSE;
2257 compose_set_title(compose);
2259 compose->updating = FALSE;
2260 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2261 SCROLL_TO_CURSOR(compose);
2263 if (compose->deferred_destroy) {
2264 compose_destroy(compose);
2268 compose->sig_str = account_get_signature_str(compose->account);
2273 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2280 cm_return_val_if_fail(msginfo != NULL, NULL);
2283 account = account_get_reply_account(msginfo,
2284 prefs_common.reply_account_autosel);
2285 cm_return_val_if_fail(account != NULL, NULL);
2287 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2289 compose->updating = TRUE;
2291 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2292 compose->replyinfo = NULL;
2293 compose->fwdinfo = NULL;
2295 compose_show_first_last_header(compose, TRUE);
2297 gtk_widget_grab_focus(compose->header_last->entry);
2299 filename = procmsg_get_message_file(msginfo);
2301 if (filename == NULL) {
2302 compose->updating = FALSE;
2303 compose_destroy(compose);
2308 compose->redirect_filename = filename;
2310 /* Set save folder */
2311 item = msginfo->folder;
2312 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2313 gchar *folderidentifier;
2315 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2316 folderidentifier = folder_item_get_identifier(item);
2317 compose_set_save_to(compose, folderidentifier);
2318 g_free(folderidentifier);
2321 compose_attach_parts(compose, msginfo);
2323 if (msginfo->subject)
2324 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2326 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2328 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2329 _("The body of the \"Redirect\" template has an error at line %d."));
2330 quote_fmt_reset_vartable();
2331 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2333 compose_colorize_signature(compose);
2336 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2337 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2338 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2340 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2341 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2342 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2344 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2345 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2346 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2347 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2349 if (compose->toolbar->draft_btn)
2350 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2351 if (compose->toolbar->insert_btn)
2352 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2353 if (compose->toolbar->attach_btn)
2354 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2355 if (compose->toolbar->sig_btn)
2356 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2357 if (compose->toolbar->exteditor_btn)
2358 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2359 if (compose->toolbar->linewrap_current_btn)
2360 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2361 if (compose->toolbar->linewrap_all_btn)
2362 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2364 compose->modified = FALSE;
2365 compose_set_title(compose);
2366 compose->updating = FALSE;
2367 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2368 SCROLL_TO_CURSOR(compose);
2370 if (compose->deferred_destroy) {
2371 compose_destroy(compose);
2378 GList *compose_get_compose_list(void)
2380 return compose_list;
2383 void compose_entry_append(Compose *compose, const gchar *address,
2384 ComposeEntryType type)
2386 const gchar *header;
2388 gboolean in_quote = FALSE;
2389 if (!address || *address == '\0') return;
2396 header = N_("Bcc:");
2398 case COMPOSE_REPLYTO:
2399 header = N_("Reply-To:");
2401 case COMPOSE_NEWSGROUPS:
2402 header = N_("Newsgroups:");
2404 case COMPOSE_FOLLOWUPTO:
2405 header = N_( "Followup-To:");
2412 header = prefs_common_translated_header_name(header);
2414 cur = begin = (gchar *)address;
2416 /* we separate the line by commas, but not if we're inside a quoted
2418 while (*cur != '\0') {
2420 in_quote = !in_quote;
2421 if (*cur == ',' && !in_quote) {
2422 gchar *tmp = g_strdup(begin);
2424 tmp[cur-begin]='\0';
2427 while (*tmp == ' ' || *tmp == '\t')
2429 compose_add_header_entry(compose, header, tmp);
2436 gchar *tmp = g_strdup(begin);
2438 tmp[cur-begin]='\0';
2441 while (*tmp == ' ' || *tmp == '\t')
2443 compose_add_header_entry(compose, header, tmp);
2448 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2450 static GdkColor yellow;
2451 static GdkColor black;
2452 static gboolean yellow_initialised = FALSE;
2456 if (!yellow_initialised) {
2457 gdk_color_parse("#f5f6be", &yellow);
2458 gdk_color_parse("#000000", &black);
2459 yellow_initialised = gdk_colormap_alloc_color(
2460 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2461 yellow_initialised &= gdk_colormap_alloc_color(
2462 gdk_colormap_get_system(), &black, FALSE, TRUE);
2465 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2466 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2467 if (gtk_entry_get_text(entry) &&
2468 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2469 if (yellow_initialised) {
2470 gtk_widget_modify_base(
2471 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2472 GTK_STATE_NORMAL, &yellow);
2473 gtk_widget_modify_text(
2474 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2475 GTK_STATE_NORMAL, &black);
2481 void compose_toolbar_cb(gint action, gpointer data)
2483 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2484 Compose *compose = (Compose*)toolbar_item->parent;
2486 cm_return_if_fail(compose != NULL);
2490 compose_send_cb(NULL, compose);
2493 compose_send_later_cb(NULL, compose);
2496 compose_draft(compose, COMPOSE_QUIT_EDITING);
2499 compose_insert_file_cb(NULL, compose);
2502 compose_attach_cb(NULL, compose);
2505 compose_insert_sig(compose, FALSE);
2508 compose_ext_editor_cb(NULL, compose);
2510 case A_LINEWRAP_CURRENT:
2511 compose_beautify_paragraph(compose, NULL, TRUE);
2513 case A_LINEWRAP_ALL:
2514 compose_wrap_all_full(compose, TRUE);
2517 compose_address_cb(NULL, compose);
2520 case A_CHECK_SPELLING:
2521 compose_check_all(NULL, compose);
2529 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2534 gchar *subject = NULL;
2538 gchar **attach = NULL;
2540 /* get mailto parts but skip from */
2541 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2544 compose_entry_append(compose, to, to_type);
2546 compose_entry_append(compose, cc, COMPOSE_CC);
2548 compose_entry_append(compose, bcc, COMPOSE_BCC);
2550 if (!g_utf8_validate (subject, -1, NULL)) {
2551 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2552 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2555 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2559 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2560 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2563 gboolean prev_autowrap = compose->autowrap;
2565 compose->autowrap = FALSE;
2567 mark = gtk_text_buffer_get_insert(buffer);
2568 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2570 if (!g_utf8_validate (body, -1, NULL)) {
2571 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2572 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2575 gtk_text_buffer_insert(buffer, &iter, body, -1);
2577 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2579 compose->autowrap = prev_autowrap;
2580 if (compose->autowrap)
2581 compose_wrap_all(compose);
2585 gint i = 0, att = 0;
2586 gchar *warn_files = NULL;
2587 while (attach[i] != NULL) {
2588 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2589 if (utf8_filename) {
2590 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2591 gchar *tmp = g_strdup_printf("%s%s\n",
2592 warn_files?warn_files:"",
2598 g_free(utf8_filename);
2600 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2605 alertpanel_notice(ngettext(
2606 "The following file has been attached: \n%s",
2607 "The following files have been attached: \n%s", att), warn_files);
2619 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2621 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2622 {"Cc:", NULL, TRUE},
2623 {"References:", NULL, FALSE},
2624 {"Bcc:", NULL, TRUE},
2625 {"Newsgroups:", NULL, TRUE},
2626 {"Followup-To:", NULL, TRUE},
2627 {"List-Post:", NULL, FALSE},
2628 {"X-Priority:", NULL, FALSE},
2629 {NULL, NULL, FALSE}};
2645 cm_return_val_if_fail(msginfo != NULL, -1);
2647 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2648 procheader_get_header_fields(fp, hentry);
2651 if (hentry[H_REPLY_TO].body != NULL) {
2652 if (hentry[H_REPLY_TO].body[0] != '\0') {
2654 conv_unmime_header(hentry[H_REPLY_TO].body,
2657 g_free(hentry[H_REPLY_TO].body);
2658 hentry[H_REPLY_TO].body = NULL;
2660 if (hentry[H_CC].body != NULL) {
2661 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2662 g_free(hentry[H_CC].body);
2663 hentry[H_CC].body = NULL;
2665 if (hentry[H_REFERENCES].body != NULL) {
2666 if (compose->mode == COMPOSE_REEDIT)
2667 compose->references = hentry[H_REFERENCES].body;
2669 compose->references = compose_parse_references
2670 (hentry[H_REFERENCES].body, msginfo->msgid);
2671 g_free(hentry[H_REFERENCES].body);
2673 hentry[H_REFERENCES].body = NULL;
2675 if (hentry[H_BCC].body != NULL) {
2676 if (compose->mode == COMPOSE_REEDIT)
2678 conv_unmime_header(hentry[H_BCC].body, NULL);
2679 g_free(hentry[H_BCC].body);
2680 hentry[H_BCC].body = NULL;
2682 if (hentry[H_NEWSGROUPS].body != NULL) {
2683 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2684 hentry[H_NEWSGROUPS].body = NULL;
2686 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2687 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2688 compose->followup_to =
2689 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2692 g_free(hentry[H_FOLLOWUP_TO].body);
2693 hentry[H_FOLLOWUP_TO].body = NULL;
2695 if (hentry[H_LIST_POST].body != NULL) {
2698 extract_address(hentry[H_LIST_POST].body);
2699 if (hentry[H_LIST_POST].body[0] != '\0') {
2700 scan_mailto_url(hentry[H_LIST_POST].body,
2701 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2703 g_free(compose->ml_post);
2704 compose->ml_post = to;
2707 g_free(hentry[H_LIST_POST].body);
2708 hentry[H_LIST_POST].body = NULL;
2711 /* CLAWS - X-Priority */
2712 if (compose->mode == COMPOSE_REEDIT)
2713 if (hentry[H_X_PRIORITY].body != NULL) {
2716 priority = atoi(hentry[H_X_PRIORITY].body);
2717 g_free(hentry[H_X_PRIORITY].body);
2719 hentry[H_X_PRIORITY].body = NULL;
2721 if (priority < PRIORITY_HIGHEST ||
2722 priority > PRIORITY_LOWEST)
2723 priority = PRIORITY_NORMAL;
2725 compose->priority = priority;
2728 if (compose->mode == COMPOSE_REEDIT) {
2729 if (msginfo->inreplyto && *msginfo->inreplyto)
2730 compose->inreplyto = g_strdup(msginfo->inreplyto);
2734 if (msginfo->msgid && *msginfo->msgid)
2735 compose->inreplyto = g_strdup(msginfo->msgid);
2737 if (!compose->references) {
2738 if (msginfo->msgid && *msginfo->msgid) {
2739 if (msginfo->inreplyto && *msginfo->inreplyto)
2740 compose->references =
2741 g_strdup_printf("<%s>\n\t<%s>",
2745 compose->references =
2746 g_strconcat("<", msginfo->msgid, ">",
2748 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2749 compose->references =
2750 g_strconcat("<", msginfo->inreplyto, ">",
2758 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2760 GSList *ref_id_list, *cur;
2764 ref_id_list = references_list_append(NULL, ref);
2765 if (!ref_id_list) return NULL;
2766 if (msgid && *msgid)
2767 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2772 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2773 /* "<" + Message-ID + ">" + CR+LF+TAB */
2774 len += strlen((gchar *)cur->data) + 5;
2776 if (len > MAX_REFERENCES_LEN) {
2777 /* remove second message-ID */
2778 if (ref_id_list && ref_id_list->next &&
2779 ref_id_list->next->next) {
2780 g_free(ref_id_list->next->data);
2781 ref_id_list = g_slist_remove
2782 (ref_id_list, ref_id_list->next->data);
2784 slist_free_strings(ref_id_list);
2785 g_slist_free(ref_id_list);
2792 new_ref = g_string_new("");
2793 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2794 if (new_ref->len > 0)
2795 g_string_append(new_ref, "\n\t");
2796 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2799 slist_free_strings(ref_id_list);
2800 g_slist_free(ref_id_list);
2802 new_ref_str = new_ref->str;
2803 g_string_free(new_ref, FALSE);
2808 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2809 const gchar *fmt, const gchar *qmark,
2810 const gchar *body, gboolean rewrap,
2811 gboolean need_unescape,
2812 const gchar *err_msg)
2814 MsgInfo* dummyinfo = NULL;
2815 gchar *quote_str = NULL;
2817 gboolean prev_autowrap;
2818 const gchar *trimmed_body = body;
2819 gint cursor_pos = -1;
2820 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2821 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2826 SIGNAL_BLOCK(buffer);
2829 dummyinfo = compose_msginfo_new_from_compose(compose);
2830 msginfo = dummyinfo;
2833 if (qmark != NULL) {
2835 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2836 compose->gtkaspell);
2838 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2840 quote_fmt_scan_string(qmark);
2843 buf = quote_fmt_get_buffer();
2845 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2847 Xstrdup_a(quote_str, buf, goto error)
2850 if (fmt && *fmt != '\0') {
2853 while (*trimmed_body == '\n')
2857 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2858 compose->gtkaspell);
2860 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2862 if (need_unescape) {
2865 /* decode \-escape sequences in the internal representation of the quote format */
2866 tmp = malloc(strlen(fmt)+1);
2867 pref_get_unescaped_pref(tmp, fmt);
2868 quote_fmt_scan_string(tmp);
2872 quote_fmt_scan_string(fmt);
2876 buf = quote_fmt_get_buffer();
2878 gint line = quote_fmt_get_line();
2879 alertpanel_error(err_msg, line);
2885 prev_autowrap = compose->autowrap;
2886 compose->autowrap = FALSE;
2888 mark = gtk_text_buffer_get_insert(buffer);
2889 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2890 if (g_utf8_validate(buf, -1, NULL)) {
2891 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2893 gchar *tmpout = NULL;
2894 tmpout = conv_codeset_strdup
2895 (buf, conv_get_locale_charset_str_no_utf8(),
2897 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2899 tmpout = g_malloc(strlen(buf)*2+1);
2900 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2902 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2906 cursor_pos = quote_fmt_get_cursor_pos();
2907 if (cursor_pos == -1)
2908 cursor_pos = gtk_text_iter_get_offset(&iter);
2909 compose->set_cursor_pos = cursor_pos;
2911 gtk_text_buffer_get_start_iter(buffer, &iter);
2912 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2913 gtk_text_buffer_place_cursor(buffer, &iter);
2915 compose->autowrap = prev_autowrap;
2916 if (compose->autowrap && rewrap)
2917 compose_wrap_all(compose);
2924 SIGNAL_UNBLOCK(buffer);
2926 procmsg_msginfo_free( dummyinfo );
2931 /* if ml_post is of type addr@host and from is of type
2932 * addr-anything@host, return TRUE
2934 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2936 gchar *left_ml = NULL;
2937 gchar *right_ml = NULL;
2938 gchar *left_from = NULL;
2939 gchar *right_from = NULL;
2940 gboolean result = FALSE;
2942 if (!ml_post || !from)
2945 left_ml = g_strdup(ml_post);
2946 if (strstr(left_ml, "@")) {
2947 right_ml = strstr(left_ml, "@")+1;
2948 *(strstr(left_ml, "@")) = '\0';
2951 left_from = g_strdup(from);
2952 if (strstr(left_from, "@")) {
2953 right_from = strstr(left_from, "@")+1;
2954 *(strstr(left_from, "@")) = '\0';
2957 if (left_ml && left_from && right_ml && right_from
2958 && !strncmp(left_from, left_ml, strlen(left_ml))
2959 && !strcmp(right_from, right_ml)) {
2968 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2970 gchar *my_addr1, *my_addr2;
2972 if (!addr1 || !addr2)
2975 Xstrdup_a(my_addr1, addr1, return FALSE);
2976 Xstrdup_a(my_addr2, addr2, return FALSE);
2978 extract_address(my_addr1);
2979 extract_address(my_addr2);
2981 return !strcasecmp(my_addr1, my_addr2);
2984 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2985 gboolean to_all, gboolean to_ml,
2987 gboolean followup_and_reply_to)
2989 GSList *cc_list = NULL;
2992 gchar *replyto = NULL;
2993 GHashTable *to_table;
2995 gboolean reply_to_ml = FALSE;
2996 gboolean default_reply_to = FALSE;
2998 cm_return_if_fail(compose->account != NULL);
2999 cm_return_if_fail(msginfo != NULL);
3001 reply_to_ml = to_ml && compose->ml_post;
3003 default_reply_to = msginfo->folder &&
3004 msginfo->folder->prefs->enable_default_reply_to;
3006 if (compose->account->protocol != A_NNTP) {
3007 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3008 if (msginfo->folder->prefs->enable_default_replyto) {
3009 compose_entry_append(compose, msginfo->folder->prefs->default_replyto, COMPOSE_REPLYTO);
3011 if (msginfo->folder->prefs->enable_default_bcc) {
3012 compose_entry_append(compose, msginfo->folder->prefs->default_bcc, COMPOSE_BCC);
3014 if (msginfo->folder->prefs->enable_default_cc) {
3015 compose_entry_append(compose, msginfo->folder->prefs->default_cc, COMPOSE_CC);
3018 if (reply_to_ml && !default_reply_to) {
3020 gboolean is_subscr = is_subscription(compose->ml_post,
3023 /* normal answer to ml post with a reply-to */
3024 compose_entry_append(compose,
3027 if (compose->replyto
3028 && !same_address(compose->ml_post, compose->replyto))
3029 compose_entry_append(compose,
3033 /* answer to subscription confirmation */
3034 if (compose->replyto)
3035 compose_entry_append(compose,
3038 else if (msginfo->from)
3039 compose_entry_append(compose,
3044 else if (!(to_all || to_sender) && default_reply_to) {
3045 compose_entry_append(compose,
3046 msginfo->folder->prefs->default_reply_to,
3048 compose_entry_mark_default_to(compose,
3049 msginfo->folder->prefs->default_reply_to);
3054 Xstrdup_a(tmp1, msginfo->from, return);
3055 extract_address(tmp1);
3056 if (to_all || to_sender ||
3057 !account_find_from_address(tmp1, FALSE))
3058 compose_entry_append(compose,
3059 (compose->replyto && !to_sender)
3060 ? compose->replyto :
3061 msginfo->from ? msginfo->from : "",
3063 else if (!to_all && !to_sender) {
3064 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3065 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3066 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3067 if (compose->replyto) {
3068 compose_entry_append(compose,
3072 compose_entry_append(compose,
3073 msginfo->from ? msginfo->from : "",
3077 /* replying to own mail, use original recp */
3078 compose_entry_append(compose,
3079 msginfo->to ? msginfo->to : "",
3081 compose_entry_append(compose,
3082 msginfo->cc ? msginfo->cc : "",
3088 if (to_sender || (compose->followup_to &&
3089 !strncmp(compose->followup_to, "poster", 6)))
3090 compose_entry_append
3092 (compose->replyto ? compose->replyto :
3093 msginfo->from ? msginfo->from : ""),
3096 else if (followup_and_reply_to || to_all) {
3097 compose_entry_append
3099 (compose->replyto ? compose->replyto :
3100 msginfo->from ? msginfo->from : ""),
3103 compose_entry_append
3105 compose->followup_to ? compose->followup_to :
3106 compose->newsgroups ? compose->newsgroups : "",
3107 COMPOSE_NEWSGROUPS);
3110 compose_entry_append
3112 compose->followup_to ? compose->followup_to :
3113 compose->newsgroups ? compose->newsgroups : "",
3114 COMPOSE_NEWSGROUPS);
3117 if (msginfo->subject && *msginfo->subject) {
3121 buf = p = g_strdup(msginfo->subject);
3122 p += subject_get_prefix_length(p);
3123 memmove(buf, p, strlen(p) + 1);
3125 buf2 = g_strdup_printf("Re: %s", buf);
3126 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3131 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3133 if (to_ml && compose->ml_post) return;
3134 if (!to_all || compose->account->protocol == A_NNTP) return;
3136 if (compose->replyto) {
3137 Xstrdup_a(replyto, compose->replyto, return);
3138 extract_address(replyto);
3140 if (msginfo->from) {
3141 Xstrdup_a(from, msginfo->from, return);
3142 extract_address(from);
3145 if (replyto && from)
3146 cc_list = address_list_append_with_comments(cc_list, from);
3147 if (to_all && msginfo->folder &&
3148 msginfo->folder->prefs->enable_default_reply_to)
3149 cc_list = address_list_append_with_comments(cc_list,
3150 msginfo->folder->prefs->default_reply_to);
3151 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3152 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3154 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3156 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3157 if (compose->account) {
3158 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3159 GINT_TO_POINTER(1));
3161 /* remove address on To: and that of current account */
3162 for (cur = cc_list; cur != NULL; ) {
3163 GSList *next = cur->next;
3166 addr = g_utf8_strdown(cur->data, -1);
3167 extract_address(addr);
3169 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3170 cc_list = g_slist_remove(cc_list, cur->data);
3172 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3176 hash_free_strings(to_table);
3177 g_hash_table_destroy(to_table);
3180 for (cur = cc_list; cur != NULL; cur = cur->next)
3181 compose_entry_append(compose, (gchar *)cur->data,
3183 slist_free_strings(cc_list);
3184 g_slist_free(cc_list);
3189 #define SET_ENTRY(entry, str) \
3192 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3195 #define SET_ADDRESS(type, str) \
3198 compose_entry_append(compose, str, type); \
3201 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3203 cm_return_if_fail(msginfo != NULL);
3205 SET_ENTRY(subject_entry, msginfo->subject);
3206 SET_ENTRY(from_name, msginfo->from);
3207 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3208 SET_ADDRESS(COMPOSE_CC, compose->cc);
3209 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3210 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3211 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3212 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3214 compose_update_priority_menu_item(compose);
3215 compose_update_privacy_system_menu_item(compose, FALSE);
3216 compose_show_first_last_header(compose, TRUE);
3222 static void compose_insert_sig(Compose *compose, gboolean replace)
3224 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3225 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3227 GtkTextIter iter, iter_end;
3228 gint cur_pos, ins_pos;
3229 gboolean prev_autowrap;
3230 gboolean found = FALSE;
3231 gboolean exists = FALSE;
3233 cm_return_if_fail(compose->account != NULL);
3237 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3238 G_CALLBACK(compose_changed_cb),
3241 mark = gtk_text_buffer_get_insert(buffer);
3242 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3243 cur_pos = gtk_text_iter_get_offset (&iter);
3246 gtk_text_buffer_get_end_iter(buffer, &iter);
3248 exists = (compose->sig_str != NULL);
3251 GtkTextIter first_iter, start_iter, end_iter;
3253 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3255 if (!exists || compose->sig_str[0] == '\0')
3258 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3259 compose->signature_tag);
3262 /* include previous \n\n */
3263 gtk_text_iter_backward_chars(&first_iter, 1);
3264 start_iter = first_iter;
3265 end_iter = first_iter;
3267 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3268 compose->signature_tag);
3269 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3270 compose->signature_tag);
3272 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3278 g_free(compose->sig_str);
3279 compose->sig_str = account_get_signature_str(compose->account);
3281 cur_pos = gtk_text_iter_get_offset(&iter);
3283 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3284 g_free(compose->sig_str);
3285 compose->sig_str = NULL;
3287 if (compose->sig_inserted == FALSE)
3288 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3289 compose->sig_inserted = TRUE;
3291 cur_pos = gtk_text_iter_get_offset(&iter);
3292 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3294 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3295 gtk_text_iter_forward_chars(&iter, 1);
3296 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3297 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3299 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3300 cur_pos = gtk_text_buffer_get_char_count (buffer);
3303 /* put the cursor where it should be
3304 * either where the quote_fmt says, either where it was */
3305 if (compose->set_cursor_pos < 0)
3306 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3308 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3309 compose->set_cursor_pos);
3311 compose->set_cursor_pos = -1;
3312 gtk_text_buffer_place_cursor(buffer, &iter);
3313 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3314 G_CALLBACK(compose_changed_cb),
3320 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3323 GtkTextBuffer *buffer;
3326 const gchar *cur_encoding;
3327 gchar buf[BUFFSIZE];
3330 gboolean prev_autowrap;
3331 gboolean badtxt = FALSE;
3333 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3335 if ((fp = g_fopen(file, "rb")) == NULL) {
3336 FILE_OP_ERROR(file, "fopen");
3337 return COMPOSE_INSERT_READ_ERROR;
3340 prev_autowrap = compose->autowrap;
3341 compose->autowrap = FALSE;
3343 text = GTK_TEXT_VIEW(compose->text);
3344 buffer = gtk_text_view_get_buffer(text);
3345 mark = gtk_text_buffer_get_insert(buffer);
3346 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3348 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3349 G_CALLBACK(text_inserted),
3352 cur_encoding = conv_get_locale_charset_str_no_utf8();
3354 while (fgets(buf, sizeof(buf), fp) != NULL) {
3357 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3358 str = g_strdup(buf);
3360 str = conv_codeset_strdup
3361 (buf, cur_encoding, CS_INTERNAL);
3364 /* strip <CR> if DOS/Windows file,
3365 replace <CR> with <LF> if Macintosh file. */
3368 if (len > 0 && str[len - 1] != '\n') {
3370 if (str[len] == '\r') str[len] = '\n';
3373 gtk_text_buffer_insert(buffer, &iter, str, -1);
3377 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3378 G_CALLBACK(text_inserted),
3380 compose->autowrap = prev_autowrap;
3381 if (compose->autowrap)
3382 compose_wrap_all(compose);
3387 return COMPOSE_INSERT_INVALID_CHARACTER;
3389 return COMPOSE_INSERT_SUCCESS;
3392 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3393 const gchar *filename,
3394 const gchar *content_type)
3402 GtkListStore *store;
3404 gboolean has_binary = FALSE;
3406 if (!is_file_exist(file)) {
3407 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3408 gboolean result = FALSE;
3409 if (file_from_uri && is_file_exist(file_from_uri)) {
3410 result = compose_attach_append(
3411 compose, file_from_uri,
3415 g_free(file_from_uri);
3418 alertpanel_error("File %s doesn't exist\n", filename);
3421 if ((size = get_file_size(file)) < 0) {
3422 alertpanel_error("Can't get file size of %s\n", filename);
3426 alertpanel_error(_("File %s is empty."), filename);
3429 if ((fp = g_fopen(file, "rb")) == NULL) {
3430 alertpanel_error(_("Can't read %s."), filename);
3435 ainfo = g_new0(AttachInfo, 1);
3436 auto_ainfo = g_auto_pointer_new_with_free
3437 (ainfo, (GFreeFunc) compose_attach_info_free);
3438 ainfo->file = g_strdup(file);
3441 ainfo->content_type = g_strdup(content_type);
3442 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3444 MsgFlags flags = {0, 0};
3446 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3447 ainfo->encoding = ENC_7BIT;
3449 ainfo->encoding = ENC_8BIT;
3451 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3452 if (msginfo && msginfo->subject)
3453 name = g_strdup(msginfo->subject);
3455 name = g_path_get_basename(filename ? filename : file);
3457 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3459 procmsg_msginfo_free(msginfo);
3461 if (!g_ascii_strncasecmp(content_type, "text", 4))
3462 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3464 ainfo->encoding = ENC_BASE64;
3465 name = g_path_get_basename(filename ? filename : file);
3466 ainfo->name = g_strdup(name);
3470 ainfo->content_type = procmime_get_mime_type(file);
3471 if (!ainfo->content_type) {
3472 ainfo->content_type =
3473 g_strdup("application/octet-stream");
3474 ainfo->encoding = ENC_BASE64;
3475 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3477 procmime_get_encoding_for_text_file(file, &has_binary);
3479 ainfo->encoding = ENC_BASE64;
3480 name = g_path_get_basename(filename ? filename : file);
3481 ainfo->name = g_strdup(name);
3485 if (ainfo->name != NULL
3486 && !strcmp(ainfo->name, ".")) {
3487 g_free(ainfo->name);
3491 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3492 g_free(ainfo->content_type);
3493 ainfo->content_type = g_strdup("application/octet-stream");
3496 ainfo->size = (goffset)size;
3497 size_text = to_human_readable((goffset)size);
3499 store = GTK_LIST_STORE(gtk_tree_view_get_model
3500 (GTK_TREE_VIEW(compose->attach_clist)));
3502 gtk_list_store_append(store, &iter);
3503 gtk_list_store_set(store, &iter,
3504 COL_MIMETYPE, ainfo->content_type,
3505 COL_SIZE, size_text,
3506 COL_NAME, ainfo->name,
3508 COL_AUTODATA, auto_ainfo,
3511 g_auto_pointer_free(auto_ainfo);
3512 compose_attach_update_label(compose);
3516 static void compose_use_signing(Compose *compose, gboolean use_signing)
3518 compose->use_signing = use_signing;
3519 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3522 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3524 compose->use_encryption = use_encryption;
3525 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3528 #define NEXT_PART_NOT_CHILD(info) \
3530 node = info->node; \
3531 while (node->children) \
3532 node = g_node_last_child(node); \
3533 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3536 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3540 MimeInfo *firsttext = NULL;
3541 MimeInfo *encrypted = NULL;
3544 const gchar *partname = NULL;
3546 mimeinfo = procmime_scan_message(msginfo);
3547 if (!mimeinfo) return;
3549 if (mimeinfo->node->children == NULL) {
3550 procmime_mimeinfo_free_all(mimeinfo);
3554 /* find first content part */
3555 child = (MimeInfo *) mimeinfo->node->children->data;
3556 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3557 child = (MimeInfo *)child->node->children->data;
3560 if (child->type == MIMETYPE_TEXT) {
3562 debug_print("First text part found\n");
3563 } else if (compose->mode == COMPOSE_REEDIT &&
3564 child->type == MIMETYPE_APPLICATION &&
3565 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3566 encrypted = (MimeInfo *)child->node->parent->data;
3569 child = (MimeInfo *) mimeinfo->node->children->data;
3570 while (child != NULL) {
3573 if (child == encrypted) {
3574 /* skip this part of tree */
3575 NEXT_PART_NOT_CHILD(child);
3579 if (child->type == MIMETYPE_MULTIPART) {
3580 /* get the actual content */
3581 child = procmime_mimeinfo_next(child);
3585 if (child == firsttext) {
3586 child = procmime_mimeinfo_next(child);
3590 outfile = procmime_get_tmp_file_name(child);
3591 if ((err = procmime_get_part(outfile, child)) < 0)
3592 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3594 gchar *content_type;
3596 content_type = procmime_get_content_type_str(child->type, child->subtype);
3598 /* if we meet a pgp signature, we don't attach it, but
3599 * we force signing. */
3600 if ((strcmp(content_type, "application/pgp-signature") &&
3601 strcmp(content_type, "application/pkcs7-signature") &&
3602 strcmp(content_type, "application/x-pkcs7-signature"))
3603 || compose->mode == COMPOSE_REDIRECT) {
3604 partname = procmime_mimeinfo_get_parameter(child, "filename");
3605 if (partname == NULL)
3606 partname = procmime_mimeinfo_get_parameter(child, "name");
3607 if (partname == NULL)
3609 compose_attach_append(compose, outfile,
3610 partname, content_type);
3612 compose_force_signing(compose, compose->account, NULL);
3614 g_free(content_type);
3617 NEXT_PART_NOT_CHILD(child);
3619 procmime_mimeinfo_free_all(mimeinfo);
3622 #undef NEXT_PART_NOT_CHILD
3627 WAIT_FOR_INDENT_CHAR,
3628 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3631 /* return indent length, we allow:
3632 indent characters followed by indent characters or spaces/tabs,
3633 alphabets and numbers immediately followed by indent characters,
3634 and the repeating sequences of the above
3635 If quote ends with multiple spaces, only the first one is included. */
3636 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3637 const GtkTextIter *start, gint *len)
3639 GtkTextIter iter = *start;
3643 IndentState state = WAIT_FOR_INDENT_CHAR;
3646 gint alnum_count = 0;
3647 gint space_count = 0;
3650 if (prefs_common.quote_chars == NULL) {
3654 while (!gtk_text_iter_ends_line(&iter)) {
3655 wc = gtk_text_iter_get_char(&iter);
3656 if (g_unichar_iswide(wc))
3658 clen = g_unichar_to_utf8(wc, ch);
3662 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3663 is_space = g_unichar_isspace(wc);
3665 if (state == WAIT_FOR_INDENT_CHAR) {
3666 if (!is_indent && !g_unichar_isalnum(wc))
3669 quote_len += alnum_count + space_count + 1;
3670 alnum_count = space_count = 0;
3671 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3674 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3675 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3679 else if (is_indent) {
3680 quote_len += alnum_count + space_count + 1;
3681 alnum_count = space_count = 0;
3684 state = WAIT_FOR_INDENT_CHAR;
3688 gtk_text_iter_forward_char(&iter);
3691 if (quote_len > 0 && space_count > 0)
3697 if (quote_len > 0) {
3699 gtk_text_iter_forward_chars(&iter, quote_len);
3700 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3706 /* return >0 if the line is itemized */
3707 static int compose_itemized_length(GtkTextBuffer *buffer,
3708 const GtkTextIter *start)
3710 GtkTextIter iter = *start;
3715 if (gtk_text_iter_ends_line(&iter))
3720 wc = gtk_text_iter_get_char(&iter);
3721 if (!g_unichar_isspace(wc))
3723 gtk_text_iter_forward_char(&iter);
3724 if (gtk_text_iter_ends_line(&iter))
3728 clen = g_unichar_to_utf8(wc, ch);
3732 if (!strchr("*-+", ch[0]))
3735 gtk_text_iter_forward_char(&iter);
3736 if (gtk_text_iter_ends_line(&iter))
3738 wc = gtk_text_iter_get_char(&iter);
3739 if (g_unichar_isspace(wc)) {
3745 /* return the string at the start of the itemization */
3746 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3747 const GtkTextIter *start)
3749 GtkTextIter iter = *start;
3752 GString *item_chars = g_string_new("");
3755 if (gtk_text_iter_ends_line(&iter))
3760 wc = gtk_text_iter_get_char(&iter);
3761 if (!g_unichar_isspace(wc))
3763 gtk_text_iter_forward_char(&iter);
3764 if (gtk_text_iter_ends_line(&iter))
3766 g_string_append_unichar(item_chars, wc);
3769 str = item_chars->str;
3770 g_string_free(item_chars, FALSE);
3774 /* return the number of spaces at a line's start */
3775 static int compose_left_offset_length(GtkTextBuffer *buffer,
3776 const GtkTextIter *start)
3778 GtkTextIter iter = *start;
3781 if (gtk_text_iter_ends_line(&iter))
3785 wc = gtk_text_iter_get_char(&iter);
3786 if (!g_unichar_isspace(wc))
3789 gtk_text_iter_forward_char(&iter);
3790 if (gtk_text_iter_ends_line(&iter))
3794 gtk_text_iter_forward_char(&iter);
3795 if (gtk_text_iter_ends_line(&iter))
3800 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3801 const GtkTextIter *start,
3802 GtkTextIter *break_pos,
3806 GtkTextIter iter = *start, line_end = *start;
3807 PangoLogAttr *attrs;
3814 gboolean can_break = FALSE;
3815 gboolean do_break = FALSE;
3816 gboolean was_white = FALSE;
3817 gboolean prev_dont_break = FALSE;
3819 gtk_text_iter_forward_to_line_end(&line_end);
3820 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3821 len = g_utf8_strlen(str, -1);
3825 g_warning("compose_get_line_break_pos: len = 0!\n");
3829 /* g_print("breaking line: %d: %s (len = %d)\n",
3830 gtk_text_iter_get_line(&iter), str, len); */
3832 attrs = g_new(PangoLogAttr, len + 1);
3834 pango_default_break(str, -1, NULL, attrs, len + 1);
3838 /* skip quote and leading spaces */
3839 for (i = 0; *p != '\0' && i < len; i++) {
3842 wc = g_utf8_get_char(p);
3843 if (i >= quote_len && !g_unichar_isspace(wc))
3845 if (g_unichar_iswide(wc))
3847 else if (*p == '\t')
3851 p = g_utf8_next_char(p);
3854 for (; *p != '\0' && i < len; i++) {
3855 PangoLogAttr *attr = attrs + i;
3859 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3862 was_white = attr->is_white;
3864 /* don't wrap URI */
3865 if ((uri_len = get_uri_len(p)) > 0) {
3867 if (pos > 0 && col > max_col) {
3877 wc = g_utf8_get_char(p);
3878 if (g_unichar_iswide(wc)) {
3880 if (prev_dont_break && can_break && attr->is_line_break)
3882 } else if (*p == '\t')
3886 if (pos > 0 && col > max_col) {
3891 if (*p == '-' || *p == '/')
3892 prev_dont_break = TRUE;
3894 prev_dont_break = FALSE;
3896 p = g_utf8_next_char(p);
3900 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3905 *break_pos = *start;
3906 gtk_text_iter_set_line_offset(break_pos, pos);
3911 static gboolean compose_join_next_line(Compose *compose,
3912 GtkTextBuffer *buffer,
3914 const gchar *quote_str)
3916 GtkTextIter iter_ = *iter, cur, prev, next, end;
3917 PangoLogAttr attrs[3];
3919 gchar *next_quote_str;
3922 gboolean keep_cursor = FALSE;
3924 if (!gtk_text_iter_forward_line(&iter_) ||
3925 gtk_text_iter_ends_line(&iter_)) {
3928 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3930 if ((quote_str || next_quote_str) &&
3931 strcmp2(quote_str, next_quote_str) != 0) {
3932 g_free(next_quote_str);
3935 g_free(next_quote_str);
3938 if (quote_len > 0) {
3939 gtk_text_iter_forward_chars(&end, quote_len);
3940 if (gtk_text_iter_ends_line(&end)) {
3945 /* don't join itemized lines */
3946 if (compose_itemized_length(buffer, &end) > 0) {
3950 /* don't join signature separator */
3951 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3954 /* delete quote str */
3956 gtk_text_buffer_delete(buffer, &iter_, &end);
3958 /* don't join line breaks put by the user */
3960 gtk_text_iter_backward_char(&cur);
3961 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3962 gtk_text_iter_forward_char(&cur);
3966 gtk_text_iter_forward_char(&cur);
3967 /* delete linebreak and extra spaces */
3968 while (gtk_text_iter_backward_char(&cur)) {
3969 wc1 = gtk_text_iter_get_char(&cur);
3970 if (!g_unichar_isspace(wc1))
3975 while (!gtk_text_iter_ends_line(&cur)) {
3976 wc1 = gtk_text_iter_get_char(&cur);
3977 if (!g_unichar_isspace(wc1))
3979 gtk_text_iter_forward_char(&cur);
3982 if (!gtk_text_iter_equal(&prev, &next)) {
3985 mark = gtk_text_buffer_get_insert(buffer);
3986 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3987 if (gtk_text_iter_equal(&prev, &cur))
3989 gtk_text_buffer_delete(buffer, &prev, &next);
3993 /* insert space if required */
3994 gtk_text_iter_backward_char(&prev);
3995 wc1 = gtk_text_iter_get_char(&prev);
3996 wc2 = gtk_text_iter_get_char(&next);
3997 gtk_text_iter_forward_char(&next);
3998 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3999 pango_default_break(str, -1, NULL, attrs, 3);
4000 if (!attrs[1].is_line_break ||
4001 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4002 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4004 gtk_text_iter_backward_char(&iter_);
4005 gtk_text_buffer_place_cursor(buffer, &iter_);
4014 #define ADD_TXT_POS(bp_, ep_, pti_) \
4015 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4016 last = last->next; \
4017 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4018 last->next = NULL; \
4020 g_warning("alloc error scanning URIs\n"); \
4023 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4025 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4026 GtkTextBuffer *buffer;
4027 GtkTextIter iter, break_pos, end_of_line;
4028 gchar *quote_str = NULL;
4030 gboolean wrap_quote = prefs_common.linewrap_quote;
4031 gboolean prev_autowrap = compose->autowrap;
4032 gint startq_offset = -1, noq_offset = -1;
4033 gint uri_start = -1, uri_stop = -1;
4034 gint nouri_start = -1, nouri_stop = -1;
4035 gint num_blocks = 0;
4036 gint quotelevel = -1;
4037 gboolean modified = force;
4038 gboolean removed = FALSE;
4039 gboolean modified_before_remove = FALSE;
4041 gboolean start = TRUE;
4042 gint itemized_len = 0, rem_item_len = 0;
4043 gchar *itemized_chars = NULL;
4044 gboolean item_continuation = FALSE;
4049 if (compose->draft_timeout_tag == -2) {
4053 compose->autowrap = FALSE;
4055 buffer = gtk_text_view_get_buffer(text);
4056 undo_wrapping(compose->undostruct, TRUE);
4061 mark = gtk_text_buffer_get_insert(buffer);
4062 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4066 if (compose->draft_timeout_tag == -2) {
4067 if (gtk_text_iter_ends_line(&iter)) {
4068 while (gtk_text_iter_ends_line(&iter) &&
4069 gtk_text_iter_forward_line(&iter))
4072 while (gtk_text_iter_backward_line(&iter)) {
4073 if (gtk_text_iter_ends_line(&iter)) {
4074 gtk_text_iter_forward_line(&iter);
4080 /* move to line start */
4081 gtk_text_iter_set_line_offset(&iter, 0);
4084 itemized_len = compose_itemized_length(buffer, &iter);
4086 if (!itemized_len) {
4087 itemized_len = compose_left_offset_length(buffer, &iter);
4088 item_continuation = TRUE;
4092 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4094 /* go until paragraph end (empty line) */
4095 while (start || !gtk_text_iter_ends_line(&iter)) {
4096 gchar *scanpos = NULL;
4097 /* parse table - in order of priority */
4099 const gchar *needle; /* token */
4101 /* token search function */
4102 gchar *(*search) (const gchar *haystack,
4103 const gchar *needle);
4104 /* part parsing function */
4105 gboolean (*parse) (const gchar *start,
4106 const gchar *scanpos,
4110 /* part to URI function */
4111 gchar *(*build_uri) (const gchar *bp,
4115 static struct table parser[] = {
4116 {"http://", strcasestr, get_uri_part, make_uri_string},
4117 {"https://", strcasestr, get_uri_part, make_uri_string},
4118 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4119 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4120 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4121 {"www.", strcasestr, get_uri_part, make_http_string},
4122 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4123 {"@", strcasestr, get_email_part, make_email_string}
4125 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4126 gint last_index = PARSE_ELEMS;
4128 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4132 if (!prev_autowrap && num_blocks == 0) {
4134 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4135 G_CALLBACK(text_inserted),
4138 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4141 uri_start = uri_stop = -1;
4143 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4146 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4147 if (startq_offset == -1)
4148 startq_offset = gtk_text_iter_get_offset(&iter);
4149 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4150 if (quotelevel > 2) {
4151 /* recycle colors */
4152 if (prefs_common.recycle_quote_colors)
4161 if (startq_offset == -1)
4162 noq_offset = gtk_text_iter_get_offset(&iter);
4166 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4169 if (gtk_text_iter_ends_line(&iter)) {
4171 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4172 prefs_common.linewrap_len,
4174 GtkTextIter prev, next, cur;
4175 if (prev_autowrap != FALSE || force) {
4176 compose->automatic_break = TRUE;
4178 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4179 compose->automatic_break = FALSE;
4180 if (itemized_len && compose->autoindent) {
4181 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4182 if (!item_continuation)
4183 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4185 } else if (quote_str && wrap_quote) {
4186 compose->automatic_break = TRUE;
4188 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4189 compose->automatic_break = FALSE;
4190 if (itemized_len && compose->autoindent) {
4191 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4192 if (!item_continuation)
4193 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4197 /* remove trailing spaces */
4199 rem_item_len = itemized_len;
4200 while (compose->autoindent && rem_item_len-- > 0)
4201 gtk_text_iter_backward_char(&cur);
4202 gtk_text_iter_backward_char(&cur);
4205 while (!gtk_text_iter_starts_line(&cur)) {
4208 gtk_text_iter_backward_char(&cur);
4209 wc = gtk_text_iter_get_char(&cur);
4210 if (!g_unichar_isspace(wc))
4214 if (!gtk_text_iter_equal(&prev, &next)) {
4215 gtk_text_buffer_delete(buffer, &prev, &next);
4217 gtk_text_iter_forward_char(&break_pos);
4221 gtk_text_buffer_insert(buffer, &break_pos,
4225 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4227 /* move iter to current line start */
4228 gtk_text_iter_set_line_offset(&iter, 0);
4235 /* move iter to next line start */
4241 if (!prev_autowrap && num_blocks > 0) {
4243 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4244 G_CALLBACK(text_inserted),
4248 while (!gtk_text_iter_ends_line(&end_of_line)) {
4249 gtk_text_iter_forward_char(&end_of_line);
4251 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4253 nouri_start = gtk_text_iter_get_offset(&iter);
4254 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4256 walk_pos = gtk_text_iter_get_offset(&iter);
4257 /* FIXME: this looks phony. scanning for anything in the parse table */
4258 for (n = 0; n < PARSE_ELEMS; n++) {
4261 tmp = parser[n].search(walk, parser[n].needle);
4263 if (scanpos == NULL || tmp < scanpos) {
4272 /* check if URI can be parsed */
4273 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4274 (const gchar **)&ep, FALSE)
4275 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4279 strlen(parser[last_index].needle);
4282 uri_start = walk_pos + (bp - o_walk);
4283 uri_stop = walk_pos + (ep - o_walk);
4287 gtk_text_iter_forward_line(&iter);
4290 if (startq_offset != -1) {
4291 GtkTextIter startquote, endquote;
4292 gtk_text_buffer_get_iter_at_offset(
4293 buffer, &startquote, startq_offset);
4296 switch (quotelevel) {
4298 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4299 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4300 gtk_text_buffer_apply_tag_by_name(
4301 buffer, "quote0", &startquote, &endquote);
4302 gtk_text_buffer_remove_tag_by_name(
4303 buffer, "quote1", &startquote, &endquote);
4304 gtk_text_buffer_remove_tag_by_name(
4305 buffer, "quote2", &startquote, &endquote);
4310 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4311 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4312 gtk_text_buffer_apply_tag_by_name(
4313 buffer, "quote1", &startquote, &endquote);
4314 gtk_text_buffer_remove_tag_by_name(
4315 buffer, "quote0", &startquote, &endquote);
4316 gtk_text_buffer_remove_tag_by_name(
4317 buffer, "quote2", &startquote, &endquote);
4322 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4323 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4324 gtk_text_buffer_apply_tag_by_name(
4325 buffer, "quote2", &startquote, &endquote);
4326 gtk_text_buffer_remove_tag_by_name(
4327 buffer, "quote0", &startquote, &endquote);
4328 gtk_text_buffer_remove_tag_by_name(
4329 buffer, "quote1", &startquote, &endquote);
4335 } else if (noq_offset != -1) {
4336 GtkTextIter startnoquote, endnoquote;
4337 gtk_text_buffer_get_iter_at_offset(
4338 buffer, &startnoquote, noq_offset);
4341 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4342 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4343 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4344 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4345 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4346 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4347 gtk_text_buffer_remove_tag_by_name(
4348 buffer, "quote0", &startnoquote, &endnoquote);
4349 gtk_text_buffer_remove_tag_by_name(
4350 buffer, "quote1", &startnoquote, &endnoquote);
4351 gtk_text_buffer_remove_tag_by_name(
4352 buffer, "quote2", &startnoquote, &endnoquote);
4358 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4359 GtkTextIter nouri_start_iter, nouri_end_iter;
4360 gtk_text_buffer_get_iter_at_offset(
4361 buffer, &nouri_start_iter, nouri_start);
4362 gtk_text_buffer_get_iter_at_offset(
4363 buffer, &nouri_end_iter, nouri_stop);
4364 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4365 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4366 gtk_text_buffer_remove_tag_by_name(
4367 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4368 modified_before_remove = modified;
4373 if (uri_start >= 0 && uri_stop > 0) {
4374 GtkTextIter uri_start_iter, uri_end_iter, back;
4375 gtk_text_buffer_get_iter_at_offset(
4376 buffer, &uri_start_iter, uri_start);
4377 gtk_text_buffer_get_iter_at_offset(
4378 buffer, &uri_end_iter, uri_stop);
4379 back = uri_end_iter;
4380 gtk_text_iter_backward_char(&back);
4381 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4382 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4383 gtk_text_buffer_apply_tag_by_name(
4384 buffer, "link", &uri_start_iter, &uri_end_iter);
4386 if (removed && !modified_before_remove) {
4392 // debug_print("not modified, out after %d lines\n", lines);
4396 // debug_print("modified, out after %d lines\n", lines);
4398 g_free(itemized_chars);
4401 undo_wrapping(compose->undostruct, FALSE);
4402 compose->autowrap = prev_autowrap;
4407 void compose_action_cb(void *data)
4409 Compose *compose = (Compose *)data;
4410 compose_wrap_all(compose);
4413 static void compose_wrap_all(Compose *compose)
4415 compose_wrap_all_full(compose, FALSE);
4418 static void compose_wrap_all_full(Compose *compose, gboolean force)
4420 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4421 GtkTextBuffer *buffer;
4423 gboolean modified = TRUE;
4425 buffer = gtk_text_view_get_buffer(text);
4427 gtk_text_buffer_get_start_iter(buffer, &iter);
4428 while (!gtk_text_iter_is_end(&iter) && modified)
4429 modified = compose_beautify_paragraph(compose, &iter, force);
4433 static void compose_set_title(Compose *compose)
4439 edited = compose->modified ? _(" [Edited]") : "";
4441 subject = gtk_editable_get_chars(
4442 GTK_EDITABLE(compose->subject_entry), 0, -1);
4444 #ifndef GENERIC_UMPC
4445 if (subject && strlen(subject))
4446 str = g_strdup_printf(_("%s - Compose message%s"),
4449 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4451 str = g_strdup(_("Compose message"));
4454 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4460 * compose_current_mail_account:
4462 * Find a current mail account (the currently selected account, or the
4463 * default account, if a news account is currently selected). If a
4464 * mail account cannot be found, display an error message.
4466 * Return value: Mail account, or NULL if not found.
4468 static PrefsAccount *
4469 compose_current_mail_account(void)
4473 if (cur_account && cur_account->protocol != A_NNTP)
4476 ac = account_get_default();
4477 if (!ac || ac->protocol == A_NNTP) {
4478 alertpanel_error(_("Account for sending mail is not specified.\n"
4479 "Please select a mail account before sending."));
4486 #define QUOTE_IF_REQUIRED(out, str) \
4488 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4492 len = strlen(str) + 3; \
4493 if ((__tmp = alloca(len)) == NULL) { \
4494 g_warning("can't allocate memory\n"); \
4495 g_string_free(header, TRUE); \
4498 g_snprintf(__tmp, len, "\"%s\"", str); \
4503 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4504 g_warning("can't allocate memory\n"); \
4505 g_string_free(header, TRUE); \
4508 strcpy(__tmp, str); \
4514 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4516 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4520 len = strlen(str) + 3; \
4521 if ((__tmp = alloca(len)) == NULL) { \
4522 g_warning("can't allocate memory\n"); \
4525 g_snprintf(__tmp, len, "\"%s\"", str); \
4530 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4531 g_warning("can't allocate memory\n"); \
4534 strcpy(__tmp, str); \
4540 static void compose_select_account(Compose *compose, PrefsAccount *account,
4543 gchar *from = NULL, *header;
4544 ComposeHeaderEntry *header_entry;
4546 cm_return_if_fail(account != NULL);
4548 compose->account = account;
4550 if (account->name && *account->name) {
4552 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4553 from = g_strdup_printf("%s <%s>",
4554 buf, account->address);
4555 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4557 from = g_strdup_printf("<%s>",
4559 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4564 compose_set_title(compose);
4566 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4567 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4569 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4570 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4571 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4573 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4575 activate_privacy_system(compose, account, FALSE);
4577 if (!init && compose->mode != COMPOSE_REDIRECT) {
4578 undo_block(compose->undostruct);
4579 compose_insert_sig(compose, TRUE);
4580 undo_unblock(compose->undostruct);
4583 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4584 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4586 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4587 if (account->protocol == A_NNTP) {
4588 if (!strcmp(header, _("To:")))
4589 combobox_select_by_text(
4590 GTK_COMBO_BOX(header_entry->combo),
4593 if (!strcmp(header, _("Newsgroups:")))
4594 combobox_select_by_text(
4595 GTK_COMBO_BOX(header_entry->combo),
4603 /* use account's dict info if set */
4604 if (compose->gtkaspell) {
4605 if (account->enable_default_dictionary)
4606 gtkaspell_change_dict(compose->gtkaspell,
4607 account->default_dictionary, FALSE);
4608 if (account->enable_default_alt_dictionary)
4609 gtkaspell_change_alt_dict(compose->gtkaspell,
4610 account->default_alt_dictionary);
4611 if (account->enable_default_dictionary
4612 || account->enable_default_alt_dictionary)
4613 compose_spell_menu_changed(compose);
4618 gboolean compose_check_for_valid_recipient(Compose *compose) {
4619 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4620 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4621 gboolean recipient_found = FALSE;
4625 /* free to and newsgroup list */
4626 slist_free_strings(compose->to_list);
4627 g_slist_free(compose->to_list);
4628 compose->to_list = NULL;
4630 slist_free_strings(compose->newsgroup_list);
4631 g_slist_free(compose->newsgroup_list);
4632 compose->newsgroup_list = NULL;
4634 /* search header entries for to and newsgroup entries */
4635 for (list = compose->header_list; list; list = list->next) {
4638 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4639 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4642 if (entry[0] != '\0') {
4643 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4644 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4645 compose->to_list = address_list_append(compose->to_list, entry);
4646 recipient_found = TRUE;
4649 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4650 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4651 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4652 recipient_found = TRUE;
4659 return recipient_found;
4662 static gboolean compose_check_for_set_recipients(Compose *compose)
4664 if (compose->account->set_autocc && compose->account->auto_cc) {
4665 gboolean found_other = FALSE;
4667 /* search header entries for to and newsgroup entries */
4668 for (list = compose->header_list; list; list = list->next) {
4671 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4672 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4675 if (strcmp(entry, compose->account->auto_cc)
4676 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4686 if (compose->batch) {
4687 gtk_widget_show_all(compose->window);
4689 aval = alertpanel(_("Send"),
4690 _("The only recipient is the default CC address. Send anyway?"),
4691 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4692 if (aval != G_ALERTALTERNATE)
4696 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4697 gboolean found_other = FALSE;
4699 /* search header entries for to and newsgroup entries */
4700 for (list = compose->header_list; list; list = list->next) {
4703 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4704 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4707 if (strcmp(entry, compose->account->auto_bcc)
4708 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4718 if (compose->batch) {
4719 gtk_widget_show_all(compose->window);
4721 aval = alertpanel(_("Send"),
4722 _("The only recipient is the default BCC address. Send anyway?"),
4723 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4724 if (aval != G_ALERTALTERNATE)
4731 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4735 if (compose_check_for_valid_recipient(compose) == FALSE) {
4736 if (compose->batch) {
4737 gtk_widget_show_all(compose->window);
4739 alertpanel_error(_("Recipient is not specified."));
4743 if (compose_check_for_set_recipients(compose) == FALSE) {
4747 if (!compose->batch) {
4748 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4749 if (*str == '\0' && check_everything == TRUE &&
4750 compose->mode != COMPOSE_REDIRECT) {
4752 gchar *button_label;
4755 if (compose->sending)
4756 button_label = _("+_Send");
4758 button_label = _("+_Queue");
4759 message = g_strdup_printf(_("Subject is empty. %s"),
4760 compose->sending?_("Send it anyway?"):
4761 _("Queue it anyway?"));
4763 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4764 GTK_STOCK_CANCEL, button_label, NULL);
4766 if (aval != G_ALERTALTERNATE)
4771 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4777 gint compose_send(Compose *compose)
4780 FolderItem *folder = NULL;
4782 gchar *msgpath = NULL;
4783 gboolean discard_window = FALSE;
4784 gchar *errstr = NULL;
4785 gchar *tmsgid = NULL;
4786 MainWindow *mainwin = mainwindow_get_mainwindow();
4787 gboolean queued_removed = FALSE;
4789 if (prefs_common.send_dialog_invisible
4790 || compose->batch == TRUE)
4791 discard_window = TRUE;
4793 compose_allow_user_actions (compose, FALSE);
4794 compose->sending = TRUE;
4796 if (compose_check_entries(compose, TRUE) == FALSE) {
4797 if (compose->batch) {
4798 gtk_widget_show_all(compose->window);
4804 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4807 if (compose->batch) {
4808 gtk_widget_show_all(compose->window);
4811 alertpanel_error(_("Could not queue message for sending:\n\n"
4812 "Charset conversion failed."));
4813 } else if (val == -5) {
4814 alertpanel_error(_("Could not queue message for sending:\n\n"
4815 "Couldn't get recipient encryption key."));
4816 } else if (val == -6) {
4818 } else if (val == -3) {
4819 if (privacy_peek_error())
4820 alertpanel_error(_("Could not queue message for sending:\n\n"
4821 "Signature failed: %s"), privacy_get_error());
4822 } else if (val == -2 && errno != 0) {
4823 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4825 alertpanel_error(_("Could not queue message for sending."));
4830 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4831 if (discard_window) {
4832 compose->sending = FALSE;
4833 compose_close(compose);
4834 /* No more compose access in the normal codepath
4835 * after this point! */
4840 alertpanel_error(_("The message was queued but could not be "
4841 "sent.\nUse \"Send queued messages\" from "
4842 "the main window to retry."));
4843 if (!discard_window) {
4850 if (msgpath == NULL) {
4851 msgpath = folder_item_fetch_msg(folder, msgnum);
4852 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4855 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4856 claws_unlink(msgpath);
4859 if (!discard_window) {
4861 if (!queued_removed)
4862 folder_item_remove_msg(folder, msgnum);
4863 folder_item_scan(folder);
4865 /* make sure we delete that */
4866 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4868 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4869 folder_item_remove_msg(folder, tmp->msgnum);
4870 procmsg_msginfo_free(tmp);
4877 if (!queued_removed)
4878 folder_item_remove_msg(folder, msgnum);
4879 folder_item_scan(folder);
4881 /* make sure we delete that */
4882 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4884 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4885 folder_item_remove_msg(folder, tmp->msgnum);
4886 procmsg_msginfo_free(tmp);
4889 if (!discard_window) {
4890 compose->sending = FALSE;
4891 compose_allow_user_actions (compose, TRUE);
4892 compose_close(compose);
4896 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4897 "the main window to retry."), errstr);
4900 alertpanel_error_log(_("The message was queued but could not be "
4901 "sent.\nUse \"Send queued messages\" from "
4902 "the main window to retry."));
4904 if (!discard_window) {
4913 toolbar_main_set_sensitive(mainwin);
4914 main_window_set_menu_sensitive(mainwin);
4920 compose_allow_user_actions (compose, TRUE);
4921 compose->sending = FALSE;
4922 compose->modified = TRUE;
4923 toolbar_main_set_sensitive(mainwin);
4924 main_window_set_menu_sensitive(mainwin);
4929 static gboolean compose_use_attach(Compose *compose)
4931 GtkTreeModel *model = gtk_tree_view_get_model
4932 (GTK_TREE_VIEW(compose->attach_clist));
4933 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4936 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4939 gchar buf[BUFFSIZE];
4941 gboolean first_to_address;
4942 gboolean first_cc_address;
4944 ComposeHeaderEntry *headerentry;
4945 const gchar *headerentryname;
4946 const gchar *cc_hdr;
4947 const gchar *to_hdr;
4948 gboolean err = FALSE;
4950 debug_print("Writing redirect header\n");
4952 cc_hdr = prefs_common_translated_header_name("Cc:");
4953 to_hdr = prefs_common_translated_header_name("To:");
4955 first_to_address = TRUE;
4956 for (list = compose->header_list; list; list = list->next) {
4957 headerentry = ((ComposeHeaderEntry *)list->data);
4958 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4960 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4961 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4962 Xstrdup_a(str, entstr, return -1);
4964 if (str[0] != '\0') {
4965 compose_convert_header
4966 (compose, buf, sizeof(buf), str,
4967 strlen("Resent-To") + 2, TRUE);
4969 if (first_to_address) {
4970 err |= (fprintf(fp, "Resent-To: ") < 0);
4971 first_to_address = FALSE;
4973 err |= (fprintf(fp, ",") < 0);
4975 err |= (fprintf(fp, "%s", buf) < 0);
4979 if (!first_to_address) {
4980 err |= (fprintf(fp, "\n") < 0);
4983 first_cc_address = TRUE;
4984 for (list = compose->header_list; list; list = list->next) {
4985 headerentry = ((ComposeHeaderEntry *)list->data);
4986 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4988 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4989 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4990 Xstrdup_a(str, strg, return -1);
4992 if (str[0] != '\0') {
4993 compose_convert_header
4994 (compose, buf, sizeof(buf), str,
4995 strlen("Resent-Cc") + 2, TRUE);
4997 if (first_cc_address) {
4998 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4999 first_cc_address = FALSE;
5001 err |= (fprintf(fp, ",") < 0);
5003 err |= (fprintf(fp, "%s", buf) < 0);
5007 if (!first_cc_address) {
5008 err |= (fprintf(fp, "\n") < 0);
5011 return (err ? -1:0);
5014 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5016 gchar buf[BUFFSIZE];
5018 const gchar *entstr;
5019 /* struct utsname utsbuf; */
5020 gboolean err = FALSE;
5022 cm_return_val_if_fail(fp != NULL, -1);
5023 cm_return_val_if_fail(compose->account != NULL, -1);
5024 cm_return_val_if_fail(compose->account->address != NULL, -1);
5027 get_rfc822_date(buf, sizeof(buf));
5028 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5031 if (compose->account->name && *compose->account->name) {
5032 compose_convert_header
5033 (compose, buf, sizeof(buf), compose->account->name,
5034 strlen("From: "), TRUE);
5035 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5036 buf, compose->account->address) < 0);
5038 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5041 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5042 if (*entstr != '\0') {
5043 Xstrdup_a(str, entstr, return -1);
5046 compose_convert_header(compose, buf, sizeof(buf), str,
5047 strlen("Subject: "), FALSE);
5048 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5052 /* Resent-Message-ID */
5053 if (compose->account->set_domain && compose->account->domain) {
5054 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5055 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5056 g_snprintf(buf, sizeof(buf), "%s",
5057 strchr(compose->account->address, '@') ?
5058 strchr(compose->account->address, '@')+1 :
5059 compose->account->address);
5061 g_snprintf(buf, sizeof(buf), "%s", "");
5064 if (compose->account->gen_msgid) {
5065 generate_msgid(buf, sizeof(buf));
5066 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5067 compose->msgid = g_strdup(buf);
5069 compose->msgid = NULL;
5072 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5075 /* separator between header and body */
5076 err |= (fputs("\n", fp) == EOF);
5078 return (err ? -1:0);
5081 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5085 gchar buf[BUFFSIZE];
5087 gboolean skip = FALSE;
5088 gboolean err = FALSE;
5089 gchar *not_included[]={
5090 "Return-Path:", "Delivered-To:", "Received:",
5091 "Subject:", "X-UIDL:", "AF:",
5092 "NF:", "PS:", "SRH:",
5093 "SFN:", "DSR:", "MID:",
5094 "CFG:", "PT:", "S:",
5095 "RQ:", "SSV:", "NSV:",
5096 "SSH:", "R:", "MAID:",
5097 "NAID:", "RMID:", "FMID:",
5098 "SCF:", "RRCPT:", "NG:",
5099 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5100 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5101 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5102 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5103 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5106 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5107 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5111 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5113 for (i = 0; not_included[i] != NULL; i++) {
5114 if (g_ascii_strncasecmp(buf, not_included[i],
5115 strlen(not_included[i])) == 0) {
5122 if (fputs(buf, fdest) == -1)
5125 if (!prefs_common.redirect_keep_from) {
5126 if (g_ascii_strncasecmp(buf, "From:",
5127 strlen("From:")) == 0) {
5128 err |= (fputs(" (by way of ", fdest) == EOF);
5129 if (compose->account->name
5130 && *compose->account->name) {
5131 compose_convert_header
5132 (compose, buf, sizeof(buf),
5133 compose->account->name,
5136 err |= (fprintf(fdest, "%s <%s>",
5138 compose->account->address) < 0);
5140 err |= (fprintf(fdest, "%s",
5141 compose->account->address) < 0);
5142 err |= (fputs(")", fdest) == EOF);
5146 if (fputs("\n", fdest) == -1)
5153 if (compose_redirect_write_headers(compose, fdest))
5156 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5157 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5170 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5172 GtkTextBuffer *buffer;
5173 GtkTextIter start, end;
5176 const gchar *out_codeset;
5177 EncodingType encoding = ENC_UNKNOWN;
5178 MimeInfo *mimemsg, *mimetext;
5180 const gchar *src_codeset = CS_INTERNAL;
5181 gchar *from_addr = NULL;
5182 gchar *from_name = NULL;
5184 if (action == COMPOSE_WRITE_FOR_SEND)
5185 attach_parts = TRUE;
5187 /* create message MimeInfo */
5188 mimemsg = procmime_mimeinfo_new();
5189 mimemsg->type = MIMETYPE_MESSAGE;
5190 mimemsg->subtype = g_strdup("rfc822");
5191 mimemsg->content = MIMECONTENT_MEM;
5192 mimemsg->tmp = TRUE; /* must free content later */
5193 mimemsg->data.mem = compose_get_header(compose);
5195 /* Create text part MimeInfo */
5196 /* get all composed text */
5197 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5198 gtk_text_buffer_get_start_iter(buffer, &start);
5199 gtk_text_buffer_get_end_iter(buffer, &end);
5200 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5202 out_codeset = conv_get_charset_str(compose->out_encoding);
5204 if (!out_codeset && is_ascii_str(chars)) {
5205 out_codeset = CS_US_ASCII;
5206 } else if (prefs_common.outgoing_fallback_to_ascii &&
5207 is_ascii_str(chars)) {
5208 out_codeset = CS_US_ASCII;
5209 encoding = ENC_7BIT;
5213 gchar *test_conv_global_out = NULL;
5214 gchar *test_conv_reply = NULL;
5216 /* automatic mode. be automatic. */
5217 codeconv_set_strict(TRUE);
5219 out_codeset = conv_get_outgoing_charset_str();
5221 debug_print("trying to convert to %s\n", out_codeset);
5222 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5225 if (!test_conv_global_out && compose->orig_charset
5226 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5227 out_codeset = compose->orig_charset;
5228 debug_print("failure; trying to convert to %s\n", out_codeset);
5229 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5232 if (!test_conv_global_out && !test_conv_reply) {
5234 out_codeset = CS_INTERNAL;
5235 debug_print("failure; finally using %s\n", out_codeset);
5237 g_free(test_conv_global_out);
5238 g_free(test_conv_reply);
5239 codeconv_set_strict(FALSE);
5242 if (encoding == ENC_UNKNOWN) {
5243 if (prefs_common.encoding_method == CTE_BASE64)
5244 encoding = ENC_BASE64;
5245 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5246 encoding = ENC_QUOTED_PRINTABLE;
5247 else if (prefs_common.encoding_method == CTE_8BIT)
5248 encoding = ENC_8BIT;
5250 encoding = procmime_get_encoding_for_charset(out_codeset);
5253 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5254 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5256 if (action == COMPOSE_WRITE_FOR_SEND) {
5257 codeconv_set_strict(TRUE);
5258 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5259 codeconv_set_strict(FALSE);
5265 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5266 "to the specified %s charset.\n"
5267 "Send it as %s?"), out_codeset, src_codeset);
5268 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5269 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5272 if (aval != G_ALERTALTERNATE) {
5277 out_codeset = src_codeset;
5283 out_codeset = src_codeset;
5288 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5289 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5290 strstr(buf, "\nFrom ") != NULL) {
5291 encoding = ENC_QUOTED_PRINTABLE;
5295 mimetext = procmime_mimeinfo_new();
5296 mimetext->content = MIMECONTENT_MEM;
5297 mimetext->tmp = TRUE; /* must free content later */
5298 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5299 * and free the data, which we need later. */
5300 mimetext->data.mem = g_strdup(buf);
5301 mimetext->type = MIMETYPE_TEXT;
5302 mimetext->subtype = g_strdup("plain");
5303 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5304 g_strdup(out_codeset));
5306 /* protect trailing spaces when signing message */
5307 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5308 privacy_system_can_sign(compose->privacy_system)) {
5309 encoding = ENC_QUOTED_PRINTABLE;
5312 debug_print("main text: %zd bytes encoded as %s in %d\n",
5313 strlen(buf), out_codeset, encoding);
5315 /* check for line length limit */
5316 if (action == COMPOSE_WRITE_FOR_SEND &&
5317 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5318 check_line_length(buf, 1000, &line) < 0) {
5322 msg = g_strdup_printf
5323 (_("Line %d exceeds the line length limit (998 bytes).\n"
5324 "The contents of the message might be broken on the way to the delivery.\n"
5326 "Send it anyway?"), line + 1);
5327 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5329 if (aval != G_ALERTALTERNATE) {
5335 if (encoding != ENC_UNKNOWN)
5336 procmime_encode_content(mimetext, encoding);
5338 /* append attachment parts */
5339 if (compose_use_attach(compose) && attach_parts) {
5340 MimeInfo *mimempart;
5341 gchar *boundary = NULL;
5342 mimempart = procmime_mimeinfo_new();
5343 mimempart->content = MIMECONTENT_EMPTY;
5344 mimempart->type = MIMETYPE_MULTIPART;
5345 mimempart->subtype = g_strdup("mixed");
5349 boundary = generate_mime_boundary(NULL);
5350 } while (strstr(buf, boundary) != NULL);
5352 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5355 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5357 g_node_append(mimempart->node, mimetext->node);
5358 g_node_append(mimemsg->node, mimempart->node);
5360 if (compose_add_attachments(compose, mimempart) < 0)
5363 g_node_append(mimemsg->node, mimetext->node);
5367 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5368 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5369 /* extract name and address */
5370 if (strstr(spec, " <") && strstr(spec, ">")) {
5371 from_addr = g_strdup(strrchr(spec, '<')+1);
5372 *(strrchr(from_addr, '>')) = '\0';
5373 from_name = g_strdup(spec);
5374 *(strrchr(from_name, '<')) = '\0';
5381 /* sign message if sending */
5382 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5383 privacy_system_can_sign(compose->privacy_system))
5384 if (!privacy_sign(compose->privacy_system, mimemsg,
5385 compose->account, from_addr)) {
5392 procmime_write_mimeinfo(mimemsg, fp);
5394 procmime_mimeinfo_free_all(mimemsg);
5399 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5401 GtkTextBuffer *buffer;
5402 GtkTextIter start, end;
5407 if ((fp = g_fopen(file, "wb")) == NULL) {
5408 FILE_OP_ERROR(file, "fopen");
5412 /* chmod for security */
5413 if (change_file_mode_rw(fp, file) < 0) {
5414 FILE_OP_ERROR(file, "chmod");
5415 g_warning("can't change file mode\n");
5418 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5419 gtk_text_buffer_get_start_iter(buffer, &start);
5420 gtk_text_buffer_get_end_iter(buffer, &end);
5421 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5423 chars = conv_codeset_strdup
5424 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5427 if (!chars) return -1;
5430 len = strlen(chars);
5431 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5432 FILE_OP_ERROR(file, "fwrite");
5441 if (fclose(fp) == EOF) {
5442 FILE_OP_ERROR(file, "fclose");
5449 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5452 MsgInfo *msginfo = compose->targetinfo;
5454 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5455 if (!msginfo) return -1;
5457 if (!force && MSG_IS_LOCKED(msginfo->flags))
5460 item = msginfo->folder;
5461 cm_return_val_if_fail(item != NULL, -1);
5463 if (procmsg_msg_exist(msginfo) &&
5464 (folder_has_parent_of_type(item, F_QUEUE) ||
5465 folder_has_parent_of_type(item, F_DRAFT)
5466 || msginfo == compose->autosaved_draft)) {
5467 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5468 g_warning("can't remove the old message\n");
5471 debug_print("removed reedit target %d\n", msginfo->msgnum);
5478 static void compose_remove_draft(Compose *compose)
5481 MsgInfo *msginfo = compose->targetinfo;
5482 drafts = account_get_special_folder(compose->account, F_DRAFT);
5484 if (procmsg_msg_exist(msginfo)) {
5485 folder_item_remove_msg(drafts, msginfo->msgnum);
5490 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5491 gboolean remove_reedit_target)
5493 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5496 static gboolean compose_warn_encryption(Compose *compose)
5498 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5499 AlertValue val = G_ALERTALTERNATE;
5501 if (warning == NULL)
5504 val = alertpanel_full(_("Encryption warning"), warning,
5505 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5506 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5507 if (val & G_ALERTDISABLE) {
5508 val &= ~G_ALERTDISABLE;
5509 if (val == G_ALERTALTERNATE)
5510 privacy_inhibit_encrypt_warning(compose->privacy_system,
5514 if (val == G_ALERTALTERNATE) {
5521 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5522 gchar **msgpath, gboolean check_subject,
5523 gboolean remove_reedit_target)
5530 static gboolean lock = FALSE;
5531 PrefsAccount *mailac = NULL, *newsac = NULL;
5532 gboolean err = FALSE;
5534 debug_print("queueing message...\n");
5535 cm_return_val_if_fail(compose->account != NULL, -1);
5539 if (compose_check_entries(compose, check_subject) == FALSE) {
5541 if (compose->batch) {
5542 gtk_widget_show_all(compose->window);
5547 if (!compose->to_list && !compose->newsgroup_list) {
5548 g_warning("can't get recipient list.");
5553 if (compose->to_list) {
5554 if (compose->account->protocol != A_NNTP)
5555 mailac = compose->account;
5556 else if (cur_account && cur_account->protocol != A_NNTP)
5557 mailac = cur_account;
5558 else if (!(mailac = compose_current_mail_account())) {
5560 alertpanel_error(_("No account for sending mails available!"));
5565 if (compose->newsgroup_list) {
5566 if (compose->account->protocol == A_NNTP)
5567 newsac = compose->account;
5570 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5575 /* write queue header */
5576 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5577 G_DIR_SEPARATOR, compose, (guint) rand());
5578 debug_print("queuing to %s\n", tmp);
5579 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5580 FILE_OP_ERROR(tmp, "fopen");
5586 if (change_file_mode_rw(fp, tmp) < 0) {
5587 FILE_OP_ERROR(tmp, "chmod");
5588 g_warning("can't change file mode\n");
5591 /* queueing variables */
5592 err |= (fprintf(fp, "AF:\n") < 0);
5593 err |= (fprintf(fp, "NF:0\n") < 0);
5594 err |= (fprintf(fp, "PS:10\n") < 0);
5595 err |= (fprintf(fp, "SRH:1\n") < 0);
5596 err |= (fprintf(fp, "SFN:\n") < 0);
5597 err |= (fprintf(fp, "DSR:\n") < 0);
5599 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5601 err |= (fprintf(fp, "MID:\n") < 0);
5602 err |= (fprintf(fp, "CFG:\n") < 0);
5603 err |= (fprintf(fp, "PT:0\n") < 0);
5604 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5605 err |= (fprintf(fp, "RQ:\n") < 0);
5607 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5609 err |= (fprintf(fp, "SSV:\n") < 0);
5611 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5613 err |= (fprintf(fp, "NSV:\n") < 0);
5614 err |= (fprintf(fp, "SSH:\n") < 0);
5615 /* write recepient list */
5616 if (compose->to_list) {
5617 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5618 for (cur = compose->to_list->next; cur != NULL;
5620 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5621 err |= (fprintf(fp, "\n") < 0);
5623 /* write newsgroup list */
5624 if (compose->newsgroup_list) {
5625 err |= (fprintf(fp, "NG:") < 0);
5626 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5627 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5628 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5629 err |= (fprintf(fp, "\n") < 0);
5631 /* Sylpheed account IDs */
5633 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5635 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5638 if (compose->privacy_system != NULL) {
5639 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5640 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5641 if (compose->use_encryption) {
5643 if (!compose_warn_encryption(compose)) {
5650 if (mailac && mailac->encrypt_to_self) {
5651 GSList *tmp_list = g_slist_copy(compose->to_list);
5652 tmp_list = g_slist_append(tmp_list, compose->account->address);
5653 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5654 g_slist_free(tmp_list);
5656 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5658 if (encdata != NULL) {
5659 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5660 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5661 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5663 } /* else we finally dont want to encrypt */
5665 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5666 /* and if encdata was null, it means there's been a problem in
5678 /* Save copy folder */
5679 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5680 gchar *savefolderid;
5682 savefolderid = compose_get_save_to(compose);
5683 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5684 g_free(savefolderid);
5686 /* Save copy folder */
5687 if (compose->return_receipt) {
5688 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5690 /* Message-ID of message replying to */
5691 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5694 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5695 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5698 /* Message-ID of message forwarding to */
5699 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5702 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5703 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5707 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5708 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5710 /* end of headers */
5711 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5713 if (compose->redirect_filename != NULL) {
5714 if (compose_redirect_write_to_file(compose, fp) < 0) {
5723 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5728 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5732 g_warning("failed to write queue message\n");
5739 if (fclose(fp) == EOF) {
5740 FILE_OP_ERROR(tmp, "fclose");
5747 if (item && *item) {
5750 queue = account_get_special_folder(compose->account, F_QUEUE);
5753 g_warning("can't find queue folder\n");
5759 folder_item_scan(queue);
5760 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5761 g_warning("can't queue the message\n");
5768 if (msgpath == NULL) {
5774 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5775 compose_remove_reedit_target(compose, FALSE);
5778 if ((msgnum != NULL) && (item != NULL)) {
5786 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5789 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5791 struct stat statbuf;
5792 gchar *type, *subtype;
5793 GtkTreeModel *model;
5796 model = gtk_tree_view_get_model(tree_view);
5798 if (!gtk_tree_model_get_iter_first(model, &iter))
5801 gtk_tree_model_get(model, &iter,
5805 if (!is_file_exist(ainfo->file)) {
5806 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5807 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5808 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5810 if (val == G_ALERTDEFAULT) {
5815 mimepart = procmime_mimeinfo_new();
5816 mimepart->content = MIMECONTENT_FILE;
5817 mimepart->data.filename = g_strdup(ainfo->file);
5818 mimepart->tmp = FALSE; /* or we destroy our attachment */
5819 mimepart->offset = 0;
5821 g_stat(ainfo->file, &statbuf);
5822 mimepart->length = statbuf.st_size;
5824 type = g_strdup(ainfo->content_type);
5826 if (!strchr(type, '/')) {
5828 type = g_strdup("application/octet-stream");
5831 subtype = strchr(type, '/') + 1;
5832 *(subtype - 1) = '\0';
5833 mimepart->type = procmime_get_media_type(type);
5834 mimepart->subtype = g_strdup(subtype);
5837 if (mimepart->type == MIMETYPE_MESSAGE &&
5838 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5839 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5842 if (mimepart->type == MIMETYPE_APPLICATION &&
5843 !strcmp2(mimepart->subtype, "octet-stream"))
5844 g_hash_table_insert(mimepart->typeparameters,
5845 g_strdup("name"), g_strdup(ainfo->name));
5846 g_hash_table_insert(mimepart->dispositionparameters,
5847 g_strdup("filename"), g_strdup(ainfo->name));
5848 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5852 if (compose->use_signing) {
5853 if (ainfo->encoding == ENC_7BIT)
5854 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5855 else if (ainfo->encoding == ENC_8BIT)
5856 ainfo->encoding = ENC_BASE64;
5859 procmime_encode_content(mimepart, ainfo->encoding);
5861 g_node_append(parent->node, mimepart->node);
5862 } while (gtk_tree_model_iter_next(model, &iter));
5867 #define IS_IN_CUSTOM_HEADER(header) \
5868 (compose->account->add_customhdr && \
5869 custom_header_find(compose->account->customhdr_list, header) != NULL)
5871 static void compose_add_headerfield_from_headerlist(Compose *compose,
5873 const gchar *fieldname,
5874 const gchar *seperator)
5876 gchar *str, *fieldname_w_colon;
5877 gboolean add_field = FALSE;
5879 ComposeHeaderEntry *headerentry;
5880 const gchar *headerentryname;
5881 const gchar *trans_fieldname;
5884 if (IS_IN_CUSTOM_HEADER(fieldname))
5887 debug_print("Adding %s-fields\n", fieldname);
5889 fieldstr = g_string_sized_new(64);
5891 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5892 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5894 for (list = compose->header_list; list; list = list->next) {
5895 headerentry = ((ComposeHeaderEntry *)list->data);
5896 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5898 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5899 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5901 if (str[0] != '\0') {
5903 g_string_append(fieldstr, seperator);
5904 g_string_append(fieldstr, str);
5913 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5914 compose_convert_header
5915 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5916 strlen(fieldname) + 2, TRUE);
5917 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5921 g_free(fieldname_w_colon);
5922 g_string_free(fieldstr, TRUE);
5927 static gchar *compose_get_header(Compose *compose)
5929 gchar buf[BUFFSIZE];
5930 const gchar *entry_str;
5934 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5936 gchar *from_name = NULL, *from_address = NULL;
5939 cm_return_val_if_fail(compose->account != NULL, NULL);
5940 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5942 header = g_string_sized_new(64);
5945 get_rfc822_date(buf, sizeof(buf));
5946 g_string_append_printf(header, "Date: %s\n", buf);
5950 if (compose->account->name && *compose->account->name) {
5952 QUOTE_IF_REQUIRED(buf, compose->account->name);
5953 tmp = g_strdup_printf("%s <%s>",
5954 buf, compose->account->address);
5956 tmp = g_strdup_printf("%s",
5957 compose->account->address);
5959 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5960 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5962 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5963 from_address = g_strdup(compose->account->address);
5965 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5966 /* extract name and address */
5967 if (strstr(spec, " <") && strstr(spec, ">")) {
5968 from_address = g_strdup(strrchr(spec, '<')+1);
5969 *(strrchr(from_address, '>')) = '\0';
5970 from_name = g_strdup(spec);
5971 *(strrchr(from_name, '<')) = '\0';
5974 from_address = g_strdup(spec);
5981 if (from_name && *from_name) {
5982 compose_convert_header
5983 (compose, buf, sizeof(buf), from_name,
5984 strlen("From: "), TRUE);
5985 QUOTE_IF_REQUIRED(name, buf);
5987 g_string_append_printf(header, "From: %s <%s>\n",
5988 name, from_address);
5990 g_string_append_printf(header, "From: %s\n", from_address);
5993 g_free(from_address);
5996 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5999 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6002 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6005 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6008 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6010 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6013 compose_convert_header(compose, buf, sizeof(buf), str,
6014 strlen("Subject: "), FALSE);
6015 g_string_append_printf(header, "Subject: %s\n", buf);
6021 if (compose->account->set_domain && compose->account->domain) {
6022 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6023 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6024 g_snprintf(buf, sizeof(buf), "%s",
6025 strchr(compose->account->address, '@') ?
6026 strchr(compose->account->address, '@')+1 :
6027 compose->account->address);
6029 g_snprintf(buf, sizeof(buf), "%s", "");
6032 if (compose->account->gen_msgid) {
6033 generate_msgid(buf, sizeof(buf));
6034 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6035 compose->msgid = g_strdup(buf);
6037 compose->msgid = NULL;
6040 if (compose->remove_references == FALSE) {
6042 if (compose->inreplyto && compose->to_list)
6043 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6046 if (compose->references)
6047 g_string_append_printf(header, "References: %s\n", compose->references);
6051 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6054 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6057 if (compose->account->organization &&
6058 strlen(compose->account->organization) &&
6059 !IS_IN_CUSTOM_HEADER("Organization")) {
6060 compose_convert_header(compose, buf, sizeof(buf),
6061 compose->account->organization,
6062 strlen("Organization: "), FALSE);
6063 g_string_append_printf(header, "Organization: %s\n", buf);
6066 /* Program version and system info */
6067 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6068 !compose->newsgroup_list) {
6069 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6071 gtk_major_version, gtk_minor_version, gtk_micro_version,
6074 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6075 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6077 gtk_major_version, gtk_minor_version, gtk_micro_version,
6081 /* custom headers */
6082 if (compose->account->add_customhdr) {
6085 for (cur = compose->account->customhdr_list; cur != NULL;
6087 CustomHeader *chdr = (CustomHeader *)cur->data;
6089 if (custom_header_is_allowed(chdr->name)) {
6090 compose_convert_header
6091 (compose, buf, sizeof(buf),
6092 chdr->value ? chdr->value : "",
6093 strlen(chdr->name) + 2, FALSE);
6094 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6099 /* Automatic Faces and X-Faces */
6100 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6101 g_string_append_printf(header, "X-Face: %s\n", buf);
6103 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6104 g_string_append_printf(header, "X-Face: %s\n", buf);
6106 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6107 g_string_append_printf(header, "Face: %s\n", buf);
6109 else if (get_default_face (buf, sizeof(buf)) == 0) {
6110 g_string_append_printf(header, "Face: %s\n", buf);
6114 switch (compose->priority) {
6115 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6116 "X-Priority: 1 (Highest)\n");
6118 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6119 "X-Priority: 2 (High)\n");
6121 case PRIORITY_NORMAL: break;
6122 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6123 "X-Priority: 4 (Low)\n");
6125 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6126 "X-Priority: 5 (Lowest)\n");
6128 default: debug_print("compose: priority unknown : %d\n",
6132 /* Request Return Receipt */
6133 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6134 if (compose->return_receipt) {
6135 if (compose->account->name
6136 && *compose->account->name) {
6137 compose_convert_header(compose, buf, sizeof(buf),
6138 compose->account->name,
6139 strlen("Disposition-Notification-To: "),
6141 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6143 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6147 /* get special headers */
6148 for (list = compose->header_list; list; list = list->next) {
6149 ComposeHeaderEntry *headerentry;
6152 gchar *headername_wcolon;
6153 const gchar *headername_trans;
6156 gboolean standard_header = FALSE;
6158 headerentry = ((ComposeHeaderEntry *)list->data);
6160 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6162 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6167 if (!strstr(tmp, ":")) {
6168 headername_wcolon = g_strconcat(tmp, ":", NULL);
6169 headername = g_strdup(tmp);
6171 headername_wcolon = g_strdup(tmp);
6172 headername = g_strdup(strtok(tmp, ":"));
6176 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6177 Xstrdup_a(headervalue, entry_str, return NULL);
6178 subst_char(headervalue, '\r', ' ');
6179 subst_char(headervalue, '\n', ' ');
6180 string = std_headers;
6181 while (*string != NULL) {
6182 headername_trans = prefs_common_translated_header_name(*string);
6183 if (!strcmp(headername_trans, headername_wcolon))
6184 standard_header = TRUE;
6187 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6188 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6191 g_free(headername_wcolon);
6195 g_string_free(header, FALSE);
6200 #undef IS_IN_CUSTOM_HEADER
6202 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6203 gint header_len, gboolean addr_field)
6205 gchar *tmpstr = NULL;
6206 const gchar *out_codeset = NULL;
6208 cm_return_if_fail(src != NULL);
6209 cm_return_if_fail(dest != NULL);
6211 if (len < 1) return;
6213 tmpstr = g_strdup(src);
6215 subst_char(tmpstr, '\n', ' ');
6216 subst_char(tmpstr, '\r', ' ');
6219 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6220 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6221 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6226 codeconv_set_strict(TRUE);
6227 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6228 conv_get_charset_str(compose->out_encoding));
6229 codeconv_set_strict(FALSE);
6231 if (!dest || *dest == '\0') {
6232 gchar *test_conv_global_out = NULL;
6233 gchar *test_conv_reply = NULL;
6235 /* automatic mode. be automatic. */
6236 codeconv_set_strict(TRUE);
6238 out_codeset = conv_get_outgoing_charset_str();
6240 debug_print("trying to convert to %s\n", out_codeset);
6241 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6244 if (!test_conv_global_out && compose->orig_charset
6245 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6246 out_codeset = compose->orig_charset;
6247 debug_print("failure; trying to convert to %s\n", out_codeset);
6248 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6251 if (!test_conv_global_out && !test_conv_reply) {
6253 out_codeset = CS_INTERNAL;
6254 debug_print("finally using %s\n", out_codeset);
6256 g_free(test_conv_global_out);
6257 g_free(test_conv_reply);
6258 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6260 codeconv_set_strict(FALSE);
6265 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6269 cm_return_if_fail(user_data != NULL);
6271 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6272 g_strstrip(address);
6273 if (*address != '\0') {
6274 gchar *name = procheader_get_fromname(address);
6275 extract_address(address);
6276 addressbook_add_contact(name, address, NULL, NULL);
6281 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6283 GtkWidget *menuitem;
6286 cm_return_if_fail(menu != NULL);
6287 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6289 menuitem = gtk_separator_menu_item_new();
6290 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6291 gtk_widget_show(menuitem);
6293 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6294 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6296 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6297 g_strstrip(address);
6298 if (*address == '\0') {
6299 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6302 g_signal_connect(G_OBJECT(menuitem), "activate",
6303 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6304 gtk_widget_show(menuitem);
6307 static void compose_create_header_entry(Compose *compose)
6309 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6316 const gchar *header = NULL;
6317 ComposeHeaderEntry *headerentry;
6318 gboolean standard_header = FALSE;
6319 #if !(GTK_CHECK_VERSION(2,12,0))
6320 GtkTooltips *tips = compose->tooltips;
6323 headerentry = g_new0(ComposeHeaderEntry, 1);
6326 combo = gtk_combo_box_entry_new_text();
6328 while(*string != NULL) {
6329 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6330 (gchar*)prefs_common_translated_header_name(*string));
6333 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6334 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6335 G_CALLBACK(compose_grab_focus_cb), compose);
6336 gtk_widget_show(combo);
6337 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6338 compose->header_nextrow, compose->header_nextrow+1,
6339 GTK_SHRINK, GTK_FILL, 0, 0);
6340 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6341 const gchar *last_header_entry = gtk_entry_get_text(
6342 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6344 while (*string != NULL) {
6345 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6346 standard_header = TRUE;
6349 if (standard_header)
6350 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6352 if (!compose->header_last || !standard_header) {
6353 switch(compose->account->protocol) {
6355 header = prefs_common_translated_header_name("Newsgroups:");
6358 header = prefs_common_translated_header_name("To:");
6363 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6365 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6366 G_CALLBACK(compose_grab_focus_cb), compose);
6368 /* Entry field with cleanup button */
6369 #if GTK_CHECK_VERSION(2, 8, 0)
6370 button = gtk_button_new();
6371 gtk_button_set_image(GTK_BUTTON(button),
6372 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6374 button = gtk_button_new_with_label(_("Clear"));
6376 gtk_widget_show(button);
6377 CLAWS_SET_TIP(button,
6378 _("Delete entry contents"));
6379 entry = gtk_entry_new();
6380 gtk_widget_show(entry);
6381 CLAWS_SET_TIP(entry,
6382 _("Use <tab> to autocomplete from addressbook"));
6383 hbox = gtk_hbox_new (FALSE, 0);
6384 gtk_widget_show(hbox);
6385 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6386 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6387 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6388 compose->header_nextrow, compose->header_nextrow+1,
6389 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6391 g_signal_connect(G_OBJECT(entry), "key-press-event",
6392 G_CALLBACK(compose_headerentry_key_press_event_cb),
6394 g_signal_connect(G_OBJECT(entry), "changed",
6395 G_CALLBACK(compose_headerentry_changed_cb),
6397 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6398 G_CALLBACK(compose_grab_focus_cb), compose);
6400 g_signal_connect(G_OBJECT(button), "clicked",
6401 G_CALLBACK(compose_headerentry_button_clicked_cb),
6405 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6406 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6407 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6408 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6409 G_CALLBACK(compose_header_drag_received_cb),
6411 g_signal_connect(G_OBJECT(entry), "drag-drop",
6412 G_CALLBACK(compose_drag_drop),
6414 g_signal_connect(G_OBJECT(entry), "populate-popup",
6415 G_CALLBACK(compose_entry_popup_extend),
6418 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6420 headerentry->compose = compose;
6421 headerentry->combo = combo;
6422 headerentry->entry = entry;
6423 headerentry->button = button;
6424 headerentry->hbox = hbox;
6425 headerentry->headernum = compose->header_nextrow;
6427 compose->header_nextrow++;
6428 compose->header_last = headerentry;
6429 compose->header_list =
6430 g_slist_append(compose->header_list,
6434 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6436 ComposeHeaderEntry *last_header;
6438 last_header = compose->header_last;
6440 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6441 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6444 static void compose_remove_header_entries(Compose *compose)
6447 for (list = compose->header_list; list; list = list->next) {
6448 ComposeHeaderEntry *headerentry =
6449 (ComposeHeaderEntry *)list->data;
6450 gtk_widget_destroy(headerentry->combo);
6451 gtk_widget_destroy(headerentry->entry);
6452 gtk_widget_destroy(headerentry->button);
6453 gtk_widget_destroy(headerentry->hbox);
6454 g_free(headerentry);
6456 compose->header_last = NULL;
6457 g_slist_free(compose->header_list);
6458 compose->header_list = NULL;
6459 compose->header_nextrow = 1;
6460 compose_create_header_entry(compose);
6463 static GtkWidget *compose_create_header(Compose *compose)
6465 GtkWidget *from_optmenu_hbox;
6466 GtkWidget *header_scrolledwin;
6467 GtkWidget *header_table;
6471 /* header labels and entries */
6472 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6473 gtk_widget_show(header_scrolledwin);
6474 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6476 header_table = gtk_table_new(2, 2, FALSE);
6477 gtk_widget_show(header_table);
6478 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6479 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6480 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6483 /* option menu for selecting accounts */
6484 from_optmenu_hbox = compose_account_option_menu_create(compose);
6485 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6486 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6489 compose->header_table = header_table;
6490 compose->header_list = NULL;
6491 compose->header_nextrow = count;
6493 compose_create_header_entry(compose);
6495 compose->table = NULL;
6497 return header_scrolledwin ;
6500 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6502 Compose *compose = (Compose *)data;
6503 GdkEventButton event;
6506 event.time = gtk_get_current_event_time();
6508 return attach_button_pressed(compose->attach_clist, &event, compose);
6511 static GtkWidget *compose_create_attach(Compose *compose)
6513 GtkWidget *attach_scrwin;
6514 GtkWidget *attach_clist;
6516 GtkListStore *store;
6517 GtkCellRenderer *renderer;
6518 GtkTreeViewColumn *column;
6519 GtkTreeSelection *selection;
6521 /* attachment list */
6522 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6523 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6524 GTK_POLICY_AUTOMATIC,
6525 GTK_POLICY_AUTOMATIC);
6526 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6528 store = gtk_list_store_new(N_ATTACH_COLS,
6533 G_TYPE_AUTO_POINTER,
6535 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6536 (GTK_TREE_MODEL(store)));
6537 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6538 g_object_unref(store);
6540 renderer = gtk_cell_renderer_text_new();
6541 column = gtk_tree_view_column_new_with_attributes
6542 (_("Mime type"), renderer, "text",
6543 COL_MIMETYPE, NULL);
6544 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6546 renderer = gtk_cell_renderer_text_new();
6547 column = gtk_tree_view_column_new_with_attributes
6548 (_("Size"), renderer, "text",
6550 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6552 renderer = gtk_cell_renderer_text_new();
6553 column = gtk_tree_view_column_new_with_attributes
6554 (_("Name"), renderer, "text",
6556 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6558 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6559 prefs_common.use_stripes_everywhere);
6560 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6561 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6563 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6564 G_CALLBACK(attach_selected), compose);
6565 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6566 G_CALLBACK(attach_button_pressed), compose);
6568 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6569 G_CALLBACK(popup_attach_button_pressed), compose);
6571 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6572 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6573 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6574 G_CALLBACK(popup_attach_button_pressed), compose);
6576 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6577 G_CALLBACK(attach_key_pressed), compose);
6580 gtk_drag_dest_set(attach_clist,
6581 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6582 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6583 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6584 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6585 G_CALLBACK(compose_attach_drag_received_cb),
6587 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6588 G_CALLBACK(compose_drag_drop),
6591 compose->attach_scrwin = attach_scrwin;
6592 compose->attach_clist = attach_clist;
6594 return attach_scrwin;
6597 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6598 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6600 static GtkWidget *compose_create_others(Compose *compose)
6603 GtkWidget *savemsg_checkbtn;
6604 GtkWidget *savemsg_combo;
6605 GtkWidget *savemsg_select;
6608 gchar *folderidentifier;
6610 /* Table for settings */
6611 table = gtk_table_new(3, 1, FALSE);
6612 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6613 gtk_widget_show(table);
6614 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6617 /* Save Message to folder */
6618 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6619 gtk_widget_show(savemsg_checkbtn);
6620 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6621 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6622 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6624 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6625 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6627 savemsg_combo = gtk_combo_box_entry_new_text();
6628 compose->savemsg_checkbtn = savemsg_checkbtn;
6629 compose->savemsg_combo = savemsg_combo;
6630 gtk_widget_show(savemsg_combo);
6632 if (prefs_common.compose_save_to_history)
6633 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6634 prefs_common.compose_save_to_history);
6636 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6637 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6638 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6639 G_CALLBACK(compose_grab_focus_cb), compose);
6640 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6641 folderidentifier = folder_item_get_identifier(account_get_special_folder
6642 (compose->account, F_OUTBOX));
6643 compose_set_save_to(compose, folderidentifier);
6644 g_free(folderidentifier);
6647 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6648 gtk_widget_show(savemsg_select);
6649 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6650 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6651 G_CALLBACK(compose_savemsg_select_cb),
6659 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6661 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6662 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6665 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6670 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6673 path = folder_item_get_identifier(dest);
6675 compose_set_save_to(compose, path);
6679 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6680 GdkAtom clip, GtkTextIter *insert_place);
6683 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6687 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6689 if (event->button == 3) {
6691 GtkTextIter sel_start, sel_end;
6692 gboolean stuff_selected;
6694 /* move the cursor to allow GtkAspell to check the word
6695 * under the mouse */
6696 if (event->x && event->y) {
6697 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6698 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6700 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6703 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6704 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6707 stuff_selected = gtk_text_buffer_get_selection_bounds(
6709 &sel_start, &sel_end);
6711 gtk_text_buffer_place_cursor (buffer, &iter);
6712 /* reselect stuff */
6714 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6715 gtk_text_buffer_select_range(buffer,
6716 &sel_start, &sel_end);
6718 return FALSE; /* pass the event so that the right-click goes through */
6721 if (event->button == 2) {
6726 /* get the middle-click position to paste at the correct place */
6727 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6728 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6730 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6733 entry_paste_clipboard(compose, text,
6734 prefs_common.linewrap_pastes,
6735 GDK_SELECTION_PRIMARY, &iter);
6743 static void compose_spell_menu_changed(void *data)
6745 Compose *compose = (Compose *)data;
6747 GtkWidget *menuitem;
6748 GtkWidget *parent_item;
6749 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6752 if (compose->gtkaspell == NULL)
6755 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6756 "/Menu/Spelling/Options");
6758 /* setting the submenu removes /Spelling/Options from the factory
6759 * so we need to save it */
6761 if (parent_item == NULL) {
6762 parent_item = compose->aspell_options_menu;
6763 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6765 compose->aspell_options_menu = parent_item;
6767 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6769 spell_menu = g_slist_reverse(spell_menu);
6770 for (items = spell_menu;
6771 items; items = items->next) {
6772 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6773 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6774 gtk_widget_show(GTK_WIDGET(menuitem));
6776 g_slist_free(spell_menu);
6778 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6779 gtk_widget_show(parent_item);
6782 static void compose_dict_changed(void *data)
6784 Compose *compose = (Compose *) data;
6786 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6789 gtkaspell_highlight_all(compose->gtkaspell);
6790 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6794 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6796 Compose *compose = (Compose *)data;
6797 GdkEventButton event;
6800 event.time = gtk_get_current_event_time();
6804 return text_clicked(compose->text, &event, compose);
6807 static gboolean compose_force_window_origin = TRUE;
6808 static Compose *compose_create(PrefsAccount *account,
6817 GtkWidget *handlebox;
6819 GtkWidget *notebook;
6821 GtkWidget *attach_hbox;
6822 GtkWidget *attach_lab1;
6823 GtkWidget *attach_lab2;
6828 GtkWidget *subject_hbox;
6829 GtkWidget *subject_frame;
6830 GtkWidget *subject_entry;
6834 GtkWidget *edit_vbox;
6835 GtkWidget *ruler_hbox;
6837 GtkWidget *scrolledwin;
6839 GtkTextBuffer *buffer;
6840 GtkClipboard *clipboard;
6843 UndoMain *undostruct;
6845 gchar *titles[N_ATTACH_COLS];
6846 GtkWidget *popupmenu;
6847 GtkWidget *tmpl_menu;
6848 GtkActionGroup *action_group = NULL;
6851 GtkAspell * gtkaspell = NULL;
6854 static GdkGeometry geometry;
6856 cm_return_val_if_fail(account != NULL, NULL);
6858 debug_print("Creating compose window...\n");
6859 compose = g_new0(Compose, 1);
6861 titles[COL_MIMETYPE] = _("MIME type");
6862 titles[COL_SIZE] = _("Size");
6863 titles[COL_NAME] = _("Name");
6865 compose->batch = batch;
6866 compose->account = account;
6867 compose->folder = folder;
6869 compose->mutex = g_mutex_new();
6870 compose->set_cursor_pos = -1;
6872 #if !(GTK_CHECK_VERSION(2,12,0))
6873 compose->tooltips = tips;
6876 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6878 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6879 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6881 if (!geometry.max_width) {
6882 geometry.max_width = gdk_screen_width();
6883 geometry.max_height = gdk_screen_height();
6886 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6887 &geometry, GDK_HINT_MAX_SIZE);
6888 if (!geometry.min_width) {
6889 geometry.min_width = 600;
6890 geometry.min_height = 440;
6892 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6893 &geometry, GDK_HINT_MIN_SIZE);
6895 #ifndef GENERIC_UMPC
6896 if (compose_force_window_origin)
6897 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6898 prefs_common.compose_y);
6900 g_signal_connect(G_OBJECT(window), "delete_event",
6901 G_CALLBACK(compose_delete_cb), compose);
6902 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6903 gtk_widget_realize(window);
6905 gtkut_widget_set_composer_icon(window);
6907 vbox = gtk_vbox_new(FALSE, 0);
6908 gtk_container_add(GTK_CONTAINER(window), vbox);
6910 compose->ui_manager = gtk_ui_manager_new();
6911 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6912 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6913 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6914 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6915 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6916 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6917 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6918 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6919 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6920 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6925 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6946 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6973 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6991 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6996 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6999 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7000 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7004 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7005 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7006 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7010 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7011 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7012 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7013 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7014 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7025 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7026 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7036 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7040 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)
7041 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)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7047 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)
7048 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)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7052 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7053 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)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7057 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)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7063 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)
7064 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7066 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7069 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)
7070 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)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7072 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7078 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7080 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7082 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)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7093 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7098 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7100 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7101 gtk_widget_show_all(menubar);
7103 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7105 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7107 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7110 if (prefs_common.toolbar_detachable) {
7111 handlebox = gtk_handle_box_new();
7113 handlebox = gtk_hbox_new(FALSE, 0);
7115 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7117 gtk_widget_realize(handlebox);
7119 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7122 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7126 vbox2 = gtk_vbox_new(FALSE, 2);
7127 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7128 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7131 notebook = gtk_notebook_new();
7132 gtk_widget_set_size_request(notebook, -1, 130);
7133 gtk_widget_show(notebook);
7135 /* header labels and entries */
7136 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7137 compose_create_header(compose),
7138 gtk_label_new_with_mnemonic(_("Hea_der")));
7139 /* attachment list */
7140 attach_hbox = gtk_hbox_new(FALSE, 0);
7141 gtk_widget_show(attach_hbox);
7143 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7144 gtk_widget_show(attach_lab1);
7145 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7147 attach_lab2 = gtk_label_new("");
7148 gtk_widget_show(attach_lab2);
7149 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7151 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7152 compose_create_attach(compose),
7155 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7156 compose_create_others(compose),
7157 gtk_label_new_with_mnemonic(_("Othe_rs")));
7160 subject_hbox = gtk_hbox_new(FALSE, 0);
7161 gtk_widget_show(subject_hbox);
7163 subject_frame = gtk_frame_new(NULL);
7164 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7165 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7166 gtk_widget_show(subject_frame);
7168 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7169 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7170 gtk_widget_show(subject);
7172 label = gtk_label_new(_("Subject:"));
7173 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7174 gtk_widget_show(label);
7177 subject_entry = claws_spell_entry_new();
7179 subject_entry = gtk_entry_new();
7181 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7182 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7183 G_CALLBACK(compose_grab_focus_cb), compose);
7184 gtk_widget_show(subject_entry);
7185 compose->subject_entry = subject_entry;
7186 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7188 edit_vbox = gtk_vbox_new(FALSE, 0);
7190 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7193 ruler_hbox = gtk_hbox_new(FALSE, 0);
7194 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7196 ruler = gtk_shruler_new();
7197 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7198 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7202 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7203 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7204 GTK_POLICY_AUTOMATIC,
7205 GTK_POLICY_AUTOMATIC);
7206 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7208 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7209 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7211 text = gtk_text_view_new();
7212 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7213 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7214 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7215 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7216 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7218 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7220 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7221 G_CALLBACK(compose_edit_size_alloc),
7223 g_signal_connect(G_OBJECT(buffer), "changed",
7224 G_CALLBACK(compose_changed_cb), compose);
7225 g_signal_connect(G_OBJECT(text), "grab_focus",
7226 G_CALLBACK(compose_grab_focus_cb), compose);
7227 g_signal_connect(G_OBJECT(buffer), "insert_text",
7228 G_CALLBACK(text_inserted), compose);
7229 g_signal_connect(G_OBJECT(text), "button_press_event",
7230 G_CALLBACK(text_clicked), compose);
7232 g_signal_connect(G_OBJECT(text), "popup-menu",
7233 G_CALLBACK(compose_popup_menu), compose);
7235 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7236 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7237 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7238 G_CALLBACK(compose_popup_menu), compose);
7240 g_signal_connect(G_OBJECT(subject_entry), "changed",
7241 G_CALLBACK(compose_changed_cb), compose);
7244 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7245 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7246 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7247 g_signal_connect(G_OBJECT(text), "drag_data_received",
7248 G_CALLBACK(compose_insert_drag_received_cb),
7250 g_signal_connect(G_OBJECT(text), "drag-drop",
7251 G_CALLBACK(compose_drag_drop),
7253 gtk_widget_show_all(vbox);
7255 /* pane between attach clist and text */
7256 paned = gtk_vpaned_new();
7257 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7259 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7260 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7262 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7264 gtk_paned_add1(GTK_PANED(paned), notebook);
7265 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7266 gtk_widget_show_all(paned);
7269 if (prefs_common.textfont) {
7270 PangoFontDescription *font_desc;
7272 font_desc = pango_font_description_from_string
7273 (prefs_common.textfont);
7275 gtk_widget_modify_font(text, font_desc);
7276 pango_font_description_free(font_desc);
7280 gtk_action_group_add_actions(action_group, compose_popup_entries,
7281 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7289 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7291 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7292 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7293 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7295 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7297 undostruct = undo_init(text);
7298 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7301 address_completion_start(window);
7303 compose->window = window;
7304 compose->vbox = vbox;
7305 compose->menubar = menubar;
7306 compose->handlebox = handlebox;
7308 compose->vbox2 = vbox2;
7310 compose->paned = paned;
7312 compose->attach_label = attach_lab2;
7314 compose->notebook = notebook;
7315 compose->edit_vbox = edit_vbox;
7316 compose->ruler_hbox = ruler_hbox;
7317 compose->ruler = ruler;
7318 compose->scrolledwin = scrolledwin;
7319 compose->text = text;
7321 compose->focused_editable = NULL;
7323 compose->popupmenu = popupmenu;
7325 compose->tmpl_menu = tmpl_menu;
7327 compose->mode = mode;
7328 compose->rmode = mode;
7330 compose->targetinfo = NULL;
7331 compose->replyinfo = NULL;
7332 compose->fwdinfo = NULL;
7334 compose->replyto = NULL;
7336 compose->bcc = NULL;
7337 compose->followup_to = NULL;
7339 compose->ml_post = NULL;
7341 compose->inreplyto = NULL;
7342 compose->references = NULL;
7343 compose->msgid = NULL;
7344 compose->boundary = NULL;
7346 compose->autowrap = prefs_common.autowrap;
7347 compose->autoindent = prefs_common.auto_indent;
7348 compose->use_signing = FALSE;
7349 compose->use_encryption = FALSE;
7350 compose->privacy_system = NULL;
7352 compose->modified = FALSE;
7354 compose->return_receipt = FALSE;
7356 compose->to_list = NULL;
7357 compose->newsgroup_list = NULL;
7359 compose->undostruct = undostruct;
7361 compose->sig_str = NULL;
7363 compose->exteditor_file = NULL;
7364 compose->exteditor_pid = -1;
7365 compose->exteditor_tag = -1;
7366 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7369 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7370 if (mode != COMPOSE_REDIRECT) {
7371 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7372 strcmp(prefs_common.dictionary, "")) {
7373 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7374 prefs_common.alt_dictionary,
7375 conv_get_locale_charset_str(),
7376 prefs_common.misspelled_col,
7377 prefs_common.check_while_typing,
7378 prefs_common.recheck_when_changing_dict,
7379 prefs_common.use_alternate,
7380 prefs_common.use_both_dicts,
7381 GTK_TEXT_VIEW(text),
7382 GTK_WINDOW(compose->window),
7383 compose_dict_changed,
7384 compose_spell_menu_changed,
7387 alertpanel_error(_("Spell checker could not "
7389 gtkaspell_checkers_strerror());
7390 gtkaspell_checkers_reset_error();
7392 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7396 compose->gtkaspell = gtkaspell;
7397 compose_spell_menu_changed(compose);
7398 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7401 compose_select_account(compose, account, TRUE);
7403 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7406 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7407 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7409 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7410 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7412 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7413 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7416 if (account->protocol != A_NNTP)
7417 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7418 prefs_common_translated_header_name("To:"));
7420 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7421 prefs_common_translated_header_name("Newsgroups:"));
7423 addressbook_set_target_compose(compose);
7425 if (mode != COMPOSE_REDIRECT)
7426 compose_set_template_menu(compose);
7428 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7431 compose_list = g_list_append(compose_list, compose);
7433 if (!prefs_common.show_ruler)
7434 gtk_widget_hide(ruler_hbox);
7436 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7439 compose->priority = PRIORITY_NORMAL;
7440 compose_update_priority_menu_item(compose);
7442 compose_set_out_encoding(compose);
7445 compose_update_actions_menu(compose);
7447 /* Privacy Systems menu */
7448 compose_update_privacy_systems_menu(compose);
7450 activate_privacy_system(compose, account, TRUE);
7451 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7453 gtk_widget_realize(window);
7455 gtk_widget_show(window);
7457 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7458 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7465 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7470 GtkWidget *optmenubox;
7473 GtkWidget *from_name = NULL;
7474 #if !(GTK_CHECK_VERSION(2,12,0))
7475 GtkTooltips *tips = compose->tooltips;
7478 gint num = 0, def_menu = 0;
7480 accounts = account_get_list();
7481 cm_return_val_if_fail(accounts != NULL, NULL);
7483 optmenubox = gtk_event_box_new();
7484 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7485 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7487 hbox = gtk_hbox_new(FALSE, 6);
7488 from_name = gtk_entry_new();
7490 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7491 G_CALLBACK(compose_grab_focus_cb), compose);
7493 for (; accounts != NULL; accounts = accounts->next, num++) {
7494 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7495 gchar *name, *from = NULL;
7497 if (ac == compose->account) def_menu = num;
7499 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7502 if (ac == compose->account) {
7503 if (ac->name && *ac->name) {
7505 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7506 from = g_strdup_printf("%s <%s>",
7508 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7510 from = g_strdup_printf("%s",
7512 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7515 COMBOBOX_ADD(menu, name, ac->account_id);
7520 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7522 g_signal_connect(G_OBJECT(optmenu), "changed",
7523 G_CALLBACK(account_activated),
7525 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7526 G_CALLBACK(compose_entry_popup_extend),
7529 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7530 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7532 CLAWS_SET_TIP(optmenubox,
7533 _("Account to use for this email"));
7534 CLAWS_SET_TIP(from_name,
7535 _("Sender address to be used"));
7537 compose->from_name = from_name;
7542 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7544 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7545 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7546 Compose *compose = (Compose *) data;
7548 compose->priority = value;
7552 static void compose_reply_change_mode(Compose *compose,
7555 gboolean was_modified = compose->modified;
7557 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7559 cm_return_if_fail(compose->replyinfo != NULL);
7561 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7563 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7565 if (action == COMPOSE_REPLY_TO_ALL)
7567 if (action == COMPOSE_REPLY_TO_SENDER)
7569 if (action == COMPOSE_REPLY_TO_LIST)
7572 compose_remove_header_entries(compose);
7573 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7574 if (compose->account->set_autocc && compose->account->auto_cc)
7575 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7577 if (compose->account->set_autobcc && compose->account->auto_bcc)
7578 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7580 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7581 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7582 compose_show_first_last_header(compose, TRUE);
7583 compose->modified = was_modified;
7584 compose_set_title(compose);
7587 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7589 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7590 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7591 Compose *compose = (Compose *) data;
7594 compose_reply_change_mode(compose, value);
7597 static void compose_update_priority_menu_item(Compose * compose)
7599 GtkWidget *menuitem = NULL;
7600 switch (compose->priority) {
7601 case PRIORITY_HIGHEST:
7602 menuitem = gtk_ui_manager_get_widget
7603 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7606 menuitem = gtk_ui_manager_get_widget
7607 (compose->ui_manager, "/Menu/Options/Priority/High");
7609 case PRIORITY_NORMAL:
7610 menuitem = gtk_ui_manager_get_widget
7611 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7614 menuitem = gtk_ui_manager_get_widget
7615 (compose->ui_manager, "/Menu/Options/Priority/Low");
7617 case PRIORITY_LOWEST:
7618 menuitem = gtk_ui_manager_get_widget
7619 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7622 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7625 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7627 Compose *compose = (Compose *) data;
7629 gboolean can_sign = FALSE, can_encrypt = FALSE;
7631 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7633 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7636 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7637 g_free(compose->privacy_system);
7638 compose->privacy_system = NULL;
7639 if (systemid != NULL) {
7640 compose->privacy_system = g_strdup(systemid);
7642 can_sign = privacy_system_can_sign(systemid);
7643 can_encrypt = privacy_system_can_encrypt(systemid);
7646 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7648 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7649 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7652 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7654 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7655 GtkWidget *menuitem = NULL;
7657 gboolean can_sign = FALSE, can_encrypt = FALSE;
7658 gboolean found = FALSE;
7660 if (compose->privacy_system != NULL) {
7662 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7663 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7664 cm_return_if_fail(menuitem != NULL);
7666 amenu = GTK_MENU_SHELL(menuitem)->children;
7668 while (amenu != NULL) {
7669 GList *alist = amenu->next;
7671 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7672 if (systemid != NULL) {
7673 if (strcmp(systemid, compose->privacy_system) == 0 &&
7674 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7675 menuitem = GTK_WIDGET(amenu->data);
7677 can_sign = privacy_system_can_sign(systemid);
7678 can_encrypt = privacy_system_can_encrypt(systemid);
7682 } else if (strlen(compose->privacy_system) == 0 &&
7683 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7684 menuitem = GTK_WIDGET(amenu->data);
7687 can_encrypt = FALSE;
7694 if (menuitem != NULL)
7695 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7697 if (warn && !found && strlen(compose->privacy_system)) {
7698 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7699 "will not be able to sign or encrypt this message."),
7700 compose->privacy_system);
7704 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7705 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7708 static void compose_set_out_encoding(Compose *compose)
7710 CharSet out_encoding;
7711 const gchar *branch = NULL;
7712 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7714 switch(out_encoding) {
7715 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7716 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7717 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7718 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7719 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7720 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7721 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7722 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7723 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7724 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7725 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7726 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7727 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7728 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7729 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7730 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7731 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7732 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7733 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7734 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7735 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7736 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7737 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7738 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7739 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7740 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7741 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7742 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7743 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7744 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7745 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7746 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7748 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7751 static void compose_set_template_menu(Compose *compose)
7753 GSList *tmpl_list, *cur;
7757 tmpl_list = template_get_config();
7759 menu = gtk_menu_new();
7761 gtk_menu_set_accel_group (GTK_MENU (menu),
7762 gtk_ui_manager_get_accel_group(compose->ui_manager));
7763 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7764 Template *tmpl = (Template *)cur->data;
7765 gchar *accel_path = NULL;
7766 item = gtk_menu_item_new_with_label(tmpl->name);
7767 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7768 g_signal_connect(G_OBJECT(item), "activate",
7769 G_CALLBACK(compose_template_activate_cb),
7771 g_object_set_data(G_OBJECT(item), "template", tmpl);
7772 gtk_widget_show(item);
7773 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7774 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7778 gtk_widget_show(menu);
7779 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7782 void compose_update_actions_menu(Compose *compose)
7784 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7787 static void compose_update_privacy_systems_menu(Compose *compose)
7789 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7790 GSList *systems, *cur;
7792 GtkWidget *system_none;
7794 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7795 GtkWidget *privacy_menu = gtk_menu_new();
7797 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7798 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7800 g_signal_connect(G_OBJECT(system_none), "activate",
7801 G_CALLBACK(compose_set_privacy_system_cb), compose);
7803 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7804 gtk_widget_show(system_none);
7806 systems = privacy_get_system_ids();
7807 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7808 gchar *systemid = cur->data;
7810 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7811 widget = gtk_radio_menu_item_new_with_label(group,
7812 privacy_system_get_name(systemid));
7813 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7814 g_strdup(systemid), g_free);
7815 g_signal_connect(G_OBJECT(widget), "activate",
7816 G_CALLBACK(compose_set_privacy_system_cb), compose);
7818 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7819 gtk_widget_show(widget);
7822 g_slist_free(systems);
7823 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7824 gtk_widget_show_all(privacy_menu);
7825 gtk_widget_show_all(privacy_menuitem);
7828 void compose_reflect_prefs_all(void)
7833 for (cur = compose_list; cur != NULL; cur = cur->next) {
7834 compose = (Compose *)cur->data;
7835 compose_set_template_menu(compose);
7839 void compose_reflect_prefs_pixmap_theme(void)
7844 for (cur = compose_list; cur != NULL; cur = cur->next) {
7845 compose = (Compose *)cur->data;
7846 toolbar_update(TOOLBAR_COMPOSE, compose);
7850 static const gchar *compose_quote_char_from_context(Compose *compose)
7852 const gchar *qmark = NULL;
7854 cm_return_val_if_fail(compose != NULL, NULL);
7856 switch (compose->mode) {
7857 /* use forward-specific quote char */
7858 case COMPOSE_FORWARD:
7859 case COMPOSE_FORWARD_AS_ATTACH:
7860 case COMPOSE_FORWARD_INLINE:
7861 if (compose->folder && compose->folder->prefs &&
7862 compose->folder->prefs->forward_with_format)
7863 qmark = compose->folder->prefs->forward_quotemark;
7864 else if (compose->account->forward_with_format)
7865 qmark = compose->account->forward_quotemark;
7867 qmark = prefs_common.fw_quotemark;
7870 /* use reply-specific quote char in all other modes */
7872 if (compose->folder && compose->folder->prefs &&
7873 compose->folder->prefs->reply_with_format)
7874 qmark = compose->folder->prefs->reply_quotemark;
7875 else if (compose->account->reply_with_format)
7876 qmark = compose->account->reply_quotemark;
7878 qmark = prefs_common.quotemark;
7882 if (qmark == NULL || *qmark == '\0')
7888 void compose_template_apply(Compose *compose, Template *tmpl,
7892 GtkTextBuffer *buffer;
7896 gchar *parsed_str = NULL;
7897 gint cursor_pos = 0;
7898 const gchar *err_msg = _("The body of the template has an error at line %d.");
7901 /* process the body */
7903 text = GTK_TEXT_VIEW(compose->text);
7904 buffer = gtk_text_view_get_buffer(text);
7907 qmark = compose_quote_char_from_context(compose);
7909 if (compose->replyinfo != NULL) {
7912 gtk_text_buffer_set_text(buffer, "", -1);
7913 mark = gtk_text_buffer_get_insert(buffer);
7914 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7916 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7917 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7919 } else if (compose->fwdinfo != NULL) {
7922 gtk_text_buffer_set_text(buffer, "", -1);
7923 mark = gtk_text_buffer_get_insert(buffer);
7924 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7926 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7927 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7930 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7932 GtkTextIter start, end;
7935 gtk_text_buffer_get_start_iter(buffer, &start);
7936 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7937 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7939 /* clear the buffer now */
7941 gtk_text_buffer_set_text(buffer, "", -1);
7943 parsed_str = compose_quote_fmt(compose, dummyinfo,
7944 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7945 procmsg_msginfo_free( dummyinfo );
7951 gtk_text_buffer_set_text(buffer, "", -1);
7952 mark = gtk_text_buffer_get_insert(buffer);
7953 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7956 if (replace && parsed_str && compose->account->auto_sig)
7957 compose_insert_sig(compose, FALSE);
7959 if (replace && parsed_str) {
7960 gtk_text_buffer_get_start_iter(buffer, &iter);
7961 gtk_text_buffer_place_cursor(buffer, &iter);
7965 cursor_pos = quote_fmt_get_cursor_pos();
7966 compose->set_cursor_pos = cursor_pos;
7967 if (cursor_pos == -1)
7969 gtk_text_buffer_get_start_iter(buffer, &iter);
7970 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7971 gtk_text_buffer_place_cursor(buffer, &iter);
7974 /* process the other fields */
7976 compose_template_apply_fields(compose, tmpl);
7977 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7978 quote_fmt_reset_vartable();
7979 compose_changed_cb(NULL, compose);
7982 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7984 MsgInfo* dummyinfo = NULL;
7985 MsgInfo *msginfo = NULL;
7988 if (compose->replyinfo != NULL)
7989 msginfo = compose->replyinfo;
7990 else if (compose->fwdinfo != NULL)
7991 msginfo = compose->fwdinfo;
7993 dummyinfo = compose_msginfo_new_from_compose(compose);
7994 msginfo = dummyinfo;
7997 if (tmpl->from && *tmpl->from != '\0') {
7999 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8000 compose->gtkaspell);
8002 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8004 quote_fmt_scan_string(tmpl->from);
8007 buf = quote_fmt_get_buffer();
8009 alertpanel_error(_("Template From format error."));
8011 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8015 if (tmpl->to && *tmpl->to != '\0') {
8017 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8018 compose->gtkaspell);
8020 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8022 quote_fmt_scan_string(tmpl->to);
8025 buf = quote_fmt_get_buffer();
8027 alertpanel_error(_("Template To format error."));
8029 compose_entry_append(compose, buf, COMPOSE_TO);
8033 if (tmpl->cc && *tmpl->cc != '\0') {
8035 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8036 compose->gtkaspell);
8038 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8040 quote_fmt_scan_string(tmpl->cc);
8043 buf = quote_fmt_get_buffer();
8045 alertpanel_error(_("Template Cc format error."));
8047 compose_entry_append(compose, buf, COMPOSE_CC);
8051 if (tmpl->bcc && *tmpl->bcc != '\0') {
8053 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8054 compose->gtkaspell);
8056 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8058 quote_fmt_scan_string(tmpl->bcc);
8061 buf = quote_fmt_get_buffer();
8063 alertpanel_error(_("Template Bcc format error."));
8065 compose_entry_append(compose, buf, COMPOSE_BCC);
8069 /* process the subject */
8070 if (tmpl->subject && *tmpl->subject != '\0') {
8072 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8073 compose->gtkaspell);
8075 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8077 quote_fmt_scan_string(tmpl->subject);
8080 buf = quote_fmt_get_buffer();
8082 alertpanel_error(_("Template subject format error."));
8084 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8088 procmsg_msginfo_free( dummyinfo );
8091 static void compose_destroy(Compose *compose)
8093 GtkTextBuffer *buffer;
8094 GtkClipboard *clipboard;
8096 compose_list = g_list_remove(compose_list, compose);
8098 if (compose->updating) {
8099 debug_print("danger, not destroying anything now\n");
8100 compose->deferred_destroy = TRUE;
8103 /* NOTE: address_completion_end() does nothing with the window
8104 * however this may change. */
8105 address_completion_end(compose->window);
8107 slist_free_strings(compose->to_list);
8108 g_slist_free(compose->to_list);
8109 slist_free_strings(compose->newsgroup_list);
8110 g_slist_free(compose->newsgroup_list);
8111 slist_free_strings(compose->header_list);
8112 g_slist_free(compose->header_list);
8114 procmsg_msginfo_free(compose->targetinfo);
8115 procmsg_msginfo_free(compose->replyinfo);
8116 procmsg_msginfo_free(compose->fwdinfo);
8118 g_free(compose->replyto);
8119 g_free(compose->cc);
8120 g_free(compose->bcc);
8121 g_free(compose->newsgroups);
8122 g_free(compose->followup_to);
8124 g_free(compose->ml_post);
8126 g_free(compose->inreplyto);
8127 g_free(compose->references);
8128 g_free(compose->msgid);
8129 g_free(compose->boundary);
8131 g_free(compose->redirect_filename);
8132 if (compose->undostruct)
8133 undo_destroy(compose->undostruct);
8135 g_free(compose->sig_str);
8137 g_free(compose->exteditor_file);
8139 g_free(compose->orig_charset);
8141 g_free(compose->privacy_system);
8143 if (addressbook_get_target_compose() == compose)
8144 addressbook_set_target_compose(NULL);
8147 if (compose->gtkaspell) {
8148 gtkaspell_delete(compose->gtkaspell);
8149 compose->gtkaspell = NULL;
8153 if (!compose->batch) {
8154 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8155 prefs_common.compose_height = compose->window->allocation.height;
8158 if (!gtk_widget_get_parent(compose->paned))
8159 gtk_widget_destroy(compose->paned);
8160 gtk_widget_destroy(compose->popupmenu);
8162 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8163 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8164 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8166 gtk_widget_destroy(compose->window);
8167 toolbar_destroy(compose->toolbar);
8168 g_free(compose->toolbar);
8169 g_mutex_free(compose->mutex);
8173 static void compose_attach_info_free(AttachInfo *ainfo)
8175 g_free(ainfo->file);
8176 g_free(ainfo->content_type);
8177 g_free(ainfo->name);
8181 static void compose_attach_update_label(Compose *compose)
8186 GtkTreeModel *model;
8191 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8192 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8193 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8197 while(gtk_tree_model_iter_next(model, &iter))
8200 text = g_strdup_printf("(%d)", i);
8201 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8205 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8207 Compose *compose = (Compose *)data;
8208 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8209 GtkTreeSelection *selection;
8211 GtkTreeModel *model;
8213 selection = gtk_tree_view_get_selection(tree_view);
8214 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8219 for (cur = sel; cur != NULL; cur = cur->next) {
8220 GtkTreePath *path = cur->data;
8221 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8224 gtk_tree_path_free(path);
8227 for (cur = sel; cur != NULL; cur = cur->next) {
8228 GtkTreeRowReference *ref = cur->data;
8229 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8232 if (gtk_tree_model_get_iter(model, &iter, path))
8233 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8235 gtk_tree_path_free(path);
8236 gtk_tree_row_reference_free(ref);
8240 compose_attach_update_label(compose);
8243 static struct _AttachProperty
8246 GtkWidget *mimetype_entry;
8247 GtkWidget *encoding_optmenu;
8248 GtkWidget *path_entry;
8249 GtkWidget *filename_entry;
8251 GtkWidget *cancel_btn;
8254 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8256 gtk_tree_path_free((GtkTreePath *)ptr);
8259 static void compose_attach_property(GtkAction *action, gpointer data)
8261 Compose *compose = (Compose *)data;
8262 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8264 GtkComboBox *optmenu;
8265 GtkTreeSelection *selection;
8267 GtkTreeModel *model;
8270 static gboolean cancelled;
8272 /* only if one selected */
8273 selection = gtk_tree_view_get_selection(tree_view);
8274 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8277 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8281 path = (GtkTreePath *) sel->data;
8282 gtk_tree_model_get_iter(model, &iter, path);
8283 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8286 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8292 if (!attach_prop.window)
8293 compose_attach_property_create(&cancelled);
8294 gtk_widget_grab_focus(attach_prop.ok_btn);
8295 gtk_widget_show(attach_prop.window);
8296 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8298 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8299 if (ainfo->encoding == ENC_UNKNOWN)
8300 combobox_select_by_data(optmenu, ENC_BASE64);
8302 combobox_select_by_data(optmenu, ainfo->encoding);
8304 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8305 ainfo->content_type ? ainfo->content_type : "");
8306 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8307 ainfo->file ? ainfo->file : "");
8308 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8309 ainfo->name ? ainfo->name : "");
8312 const gchar *entry_text;
8314 gchar *cnttype = NULL;
8321 gtk_widget_hide(attach_prop.window);
8326 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8327 if (*entry_text != '\0') {
8330 text = g_strstrip(g_strdup(entry_text));
8331 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8332 cnttype = g_strdup(text);
8335 alertpanel_error(_("Invalid MIME type."));
8341 ainfo->encoding = combobox_get_active_data(optmenu);
8343 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8344 if (*entry_text != '\0') {
8345 if (is_file_exist(entry_text) &&
8346 (size = get_file_size(entry_text)) > 0)
8347 file = g_strdup(entry_text);
8350 (_("File doesn't exist or is empty."));
8356 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8357 if (*entry_text != '\0') {
8358 g_free(ainfo->name);
8359 ainfo->name = g_strdup(entry_text);
8363 g_free(ainfo->content_type);
8364 ainfo->content_type = cnttype;
8367 g_free(ainfo->file);
8371 ainfo->size = (goffset)size;
8373 /* update tree store */
8374 text = to_human_readable(ainfo->size);
8375 gtk_tree_model_get_iter(model, &iter, path);
8376 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8377 COL_MIMETYPE, ainfo->content_type,
8379 COL_NAME, ainfo->name,
8385 gtk_tree_path_free(path);
8388 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8390 label = gtk_label_new(str); \
8391 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8392 GTK_FILL, 0, 0, 0); \
8393 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8395 entry = gtk_entry_new(); \
8396 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8397 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8400 static void compose_attach_property_create(gboolean *cancelled)
8406 GtkWidget *mimetype_entry;
8409 GtkListStore *optmenu_menu;
8410 GtkWidget *path_entry;
8411 GtkWidget *filename_entry;
8414 GtkWidget *cancel_btn;
8415 GList *mime_type_list, *strlist;
8418 debug_print("Creating attach_property window...\n");
8420 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8421 gtk_widget_set_size_request(window, 480, -1);
8422 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8423 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8424 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8425 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8426 g_signal_connect(G_OBJECT(window), "delete_event",
8427 G_CALLBACK(attach_property_delete_event),
8429 g_signal_connect(G_OBJECT(window), "key_press_event",
8430 G_CALLBACK(attach_property_key_pressed),
8433 vbox = gtk_vbox_new(FALSE, 8);
8434 gtk_container_add(GTK_CONTAINER(window), vbox);
8436 table = gtk_table_new(4, 2, FALSE);
8437 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8438 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8439 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8441 label = gtk_label_new(_("MIME type"));
8442 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8444 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8445 mimetype_entry = gtk_combo_box_entry_new_text();
8446 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8447 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8449 /* stuff with list */
8450 mime_type_list = procmime_get_mime_type_list();
8452 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8453 MimeType *type = (MimeType *) mime_type_list->data;
8456 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8458 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8461 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8462 (GCompareFunc)strcmp2);
8465 for (mime_type_list = strlist; mime_type_list != NULL;
8466 mime_type_list = mime_type_list->next) {
8467 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8468 g_free(mime_type_list->data);
8470 g_list_free(strlist);
8471 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8472 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8474 label = gtk_label_new(_("Encoding"));
8475 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8477 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8479 hbox = gtk_hbox_new(FALSE, 0);
8480 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8481 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8483 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8484 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8486 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8487 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8488 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8489 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8490 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8492 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8494 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8495 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8497 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8498 &ok_btn, GTK_STOCK_OK,
8500 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8501 gtk_widget_grab_default(ok_btn);
8503 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8504 G_CALLBACK(attach_property_ok),
8506 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8507 G_CALLBACK(attach_property_cancel),
8510 gtk_widget_show_all(vbox);
8512 attach_prop.window = window;
8513 attach_prop.mimetype_entry = mimetype_entry;
8514 attach_prop.encoding_optmenu = optmenu;
8515 attach_prop.path_entry = path_entry;
8516 attach_prop.filename_entry = filename_entry;
8517 attach_prop.ok_btn = ok_btn;
8518 attach_prop.cancel_btn = cancel_btn;
8521 #undef SET_LABEL_AND_ENTRY
8523 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8529 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8535 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8536 gboolean *cancelled)
8544 static gboolean attach_property_key_pressed(GtkWidget *widget,
8546 gboolean *cancelled)
8548 if (event && event->keyval == GDK_Escape) {
8552 if (event && event->keyval == GDK_Return) {
8560 static void compose_exec_ext_editor(Compose *compose)
8567 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8568 G_DIR_SEPARATOR, compose);
8570 if (pipe(pipe_fds) < 0) {
8576 if ((pid = fork()) < 0) {
8583 /* close the write side of the pipe */
8586 compose->exteditor_file = g_strdup(tmp);
8587 compose->exteditor_pid = pid;
8589 compose_set_ext_editor_sensitive(compose, FALSE);
8592 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8594 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8596 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8600 } else { /* process-monitoring process */
8606 /* close the read side of the pipe */
8609 if (compose_write_body_to_file(compose, tmp) < 0) {
8610 fd_write_all(pipe_fds[1], "2\n", 2);
8614 pid_ed = compose_exec_ext_editor_real(tmp);
8616 fd_write_all(pipe_fds[1], "1\n", 2);
8620 /* wait until editor is terminated */
8621 waitpid(pid_ed, NULL, 0);
8623 fd_write_all(pipe_fds[1], "0\n", 2);
8630 #endif /* G_OS_UNIX */
8634 static gint compose_exec_ext_editor_real(const gchar *file)
8641 cm_return_val_if_fail(file != NULL, -1);
8643 if ((pid = fork()) < 0) {
8648 if (pid != 0) return pid;
8650 /* grandchild process */
8652 if (setpgid(0, getppid()))
8655 if (prefs_common_get_ext_editor_cmd() &&
8656 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8657 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8658 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8660 if (prefs_common_get_ext_editor_cmd())
8661 g_warning("External editor command-line is invalid: '%s'\n",
8662 prefs_common_get_ext_editor_cmd());
8663 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8666 cmdline = strsplit_with_quote(buf, " ", 1024);
8667 execvp(cmdline[0], cmdline);
8670 g_strfreev(cmdline);
8675 static gboolean compose_ext_editor_kill(Compose *compose)
8677 pid_t pgid = compose->exteditor_pid * -1;
8680 ret = kill(pgid, 0);
8682 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8686 msg = g_strdup_printf
8687 (_("The external editor is still working.\n"
8688 "Force terminating the process?\n"
8689 "process group id: %d"), -pgid);
8690 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8691 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8695 if (val == G_ALERTALTERNATE) {
8696 g_source_remove(compose->exteditor_tag);
8697 g_io_channel_shutdown(compose->exteditor_ch,
8699 g_io_channel_unref(compose->exteditor_ch);
8701 if (kill(pgid, SIGTERM) < 0) perror("kill");
8702 waitpid(compose->exteditor_pid, NULL, 0);
8704 g_warning("Terminated process group id: %d", -pgid);
8705 g_warning("Temporary file: %s",
8706 compose->exteditor_file);
8708 compose_set_ext_editor_sensitive(compose, TRUE);
8710 g_free(compose->exteditor_file);
8711 compose->exteditor_file = NULL;
8712 compose->exteditor_pid = -1;
8713 compose->exteditor_ch = NULL;
8714 compose->exteditor_tag = -1;
8722 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8726 Compose *compose = (Compose *)data;
8729 debug_print(_("Compose: input from monitoring process\n"));
8731 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8733 g_io_channel_shutdown(source, FALSE, NULL);
8734 g_io_channel_unref(source);
8736 waitpid(compose->exteditor_pid, NULL, 0);
8738 if (buf[0] == '0') { /* success */
8739 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8740 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8742 gtk_text_buffer_set_text(buffer, "", -1);
8743 compose_insert_file(compose, compose->exteditor_file);
8744 compose_changed_cb(NULL, compose);
8745 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8747 if (claws_unlink(compose->exteditor_file) < 0)
8748 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8749 } else if (buf[0] == '1') { /* failed */
8750 g_warning("Couldn't exec external editor\n");
8751 if (claws_unlink(compose->exteditor_file) < 0)
8752 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8753 } else if (buf[0] == '2') {
8754 g_warning("Couldn't write to file\n");
8755 } else if (buf[0] == '3') {
8756 g_warning("Pipe read failed\n");
8759 compose_set_ext_editor_sensitive(compose, TRUE);
8761 g_free(compose->exteditor_file);
8762 compose->exteditor_file = NULL;
8763 compose->exteditor_pid = -1;
8764 compose->exteditor_ch = NULL;
8765 compose->exteditor_tag = -1;
8770 static void compose_set_ext_editor_sensitive(Compose *compose,
8773 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8774 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8775 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8776 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8777 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8778 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8779 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8781 gtk_widget_set_sensitive(compose->text, sensitive);
8782 if (compose->toolbar->send_btn)
8783 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8784 if (compose->toolbar->sendl_btn)
8785 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8786 if (compose->toolbar->draft_btn)
8787 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8788 if (compose->toolbar->insert_btn)
8789 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8790 if (compose->toolbar->sig_btn)
8791 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8792 if (compose->toolbar->exteditor_btn)
8793 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8794 if (compose->toolbar->linewrap_current_btn)
8795 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8796 if (compose->toolbar->linewrap_all_btn)
8797 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8799 #endif /* G_OS_UNIX */
8802 * compose_undo_state_changed:
8804 * Change the sensivity of the menuentries undo and redo
8806 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8807 gint redo_state, gpointer data)
8809 Compose *compose = (Compose *)data;
8811 switch (undo_state) {
8812 case UNDO_STATE_TRUE:
8813 if (!undostruct->undo_state) {
8814 undostruct->undo_state = TRUE;
8815 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8818 case UNDO_STATE_FALSE:
8819 if (undostruct->undo_state) {
8820 undostruct->undo_state = FALSE;
8821 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8824 case UNDO_STATE_UNCHANGED:
8826 case UNDO_STATE_REFRESH:
8827 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8830 g_warning("Undo state not recognized");
8834 switch (redo_state) {
8835 case UNDO_STATE_TRUE:
8836 if (!undostruct->redo_state) {
8837 undostruct->redo_state = TRUE;
8838 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8841 case UNDO_STATE_FALSE:
8842 if (undostruct->redo_state) {
8843 undostruct->redo_state = FALSE;
8844 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8847 case UNDO_STATE_UNCHANGED:
8849 case UNDO_STATE_REFRESH:
8850 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8853 g_warning("Redo state not recognized");
8858 /* callback functions */
8860 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8861 * includes "non-client" (windows-izm) in calculation, so this calculation
8862 * may not be accurate.
8864 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8865 GtkAllocation *allocation,
8866 GtkSHRuler *shruler)
8868 if (prefs_common.show_ruler) {
8869 gint char_width = 0, char_height = 0;
8870 gint line_width_in_chars;
8872 gtkut_get_font_size(GTK_WIDGET(widget),
8873 &char_width, &char_height);
8874 line_width_in_chars =
8875 (allocation->width - allocation->x) / char_width;
8877 /* got the maximum */
8878 gtk_ruler_set_range(GTK_RULER(shruler),
8879 0.0, line_width_in_chars, 0,
8880 /*line_width_in_chars*/ char_width);
8886 static void account_activated(GtkComboBox *optmenu, gpointer data)
8888 Compose *compose = (Compose *)data;
8891 gchar *folderidentifier;
8892 gint account_id = 0;
8896 /* Get ID of active account in the combo box */
8897 menu = gtk_combo_box_get_model(optmenu);
8898 gtk_combo_box_get_active_iter(optmenu, &iter);
8899 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8901 ac = account_find_from_id(account_id);
8902 cm_return_if_fail(ac != NULL);
8904 if (ac != compose->account)
8905 compose_select_account(compose, ac, FALSE);
8907 /* Set message save folder */
8908 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8909 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8911 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8912 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8914 compose_set_save_to(compose, NULL);
8915 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8916 folderidentifier = folder_item_get_identifier(account_get_special_folder
8917 (compose->account, F_OUTBOX));
8918 compose_set_save_to(compose, folderidentifier);
8919 g_free(folderidentifier);
8923 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8924 GtkTreeViewColumn *column, Compose *compose)
8926 compose_attach_property(NULL, compose);
8929 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8932 Compose *compose = (Compose *)data;
8933 GtkTreeSelection *attach_selection;
8934 gint attach_nr_selected;
8936 if (!event) return FALSE;
8938 if (event->button == 3) {
8939 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8940 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8942 if (attach_nr_selected > 0)
8944 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8945 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8947 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8948 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8951 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8952 NULL, NULL, event->button, event->time);
8959 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8962 Compose *compose = (Compose *)data;
8964 if (!event) return FALSE;
8966 switch (event->keyval) {
8968 compose_attach_remove_selected(NULL, compose);
8974 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8976 toolbar_comp_set_sensitive(compose, allow);
8977 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8978 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8980 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8982 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8983 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8984 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8986 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8990 static void compose_send_cb(GtkAction *action, gpointer data)
8992 Compose *compose = (Compose *)data;
8994 if (prefs_common.work_offline &&
8995 !inc_offline_should_override(TRUE,
8996 _("Claws Mail needs network access in order "
8997 "to send this email.")))
9000 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9001 g_source_remove(compose->draft_timeout_tag);
9002 compose->draft_timeout_tag = -1;
9005 compose_send(compose);
9008 static void compose_send_later_cb(GtkAction *action, gpointer data)
9010 Compose *compose = (Compose *)data;
9014 compose_allow_user_actions(compose, FALSE);
9015 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9016 compose_allow_user_actions(compose, TRUE);
9020 compose_close(compose);
9021 } else if (val == -1) {
9022 alertpanel_error(_("Could not queue message."));
9023 } else if (val == -2) {
9024 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9025 } else if (val == -3) {
9026 if (privacy_peek_error())
9027 alertpanel_error(_("Could not queue message for sending:\n\n"
9028 "Signature failed: %s"), privacy_get_error());
9029 } else if (val == -4) {
9030 alertpanel_error(_("Could not queue message for sending:\n\n"
9031 "Charset conversion failed."));
9032 } else if (val == -5) {
9033 alertpanel_error(_("Could not queue message for sending:\n\n"
9034 "Couldn't get recipient encryption key."));
9035 } else if (val == -6) {
9038 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9041 #define DRAFTED_AT_EXIT "drafted_at_exit"
9042 static void compose_register_draft(MsgInfo *info)
9044 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9045 DRAFTED_AT_EXIT, NULL);
9046 FILE *fp = g_fopen(filepath, "ab");
9049 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9057 gboolean compose_draft (gpointer data, guint action)
9059 Compose *compose = (Compose *)data;
9063 MsgFlags flag = {0, 0};
9064 static gboolean lock = FALSE;
9065 MsgInfo *newmsginfo;
9067 gboolean target_locked = FALSE;
9068 gboolean err = FALSE;
9070 if (lock) return FALSE;
9072 if (compose->sending)
9075 draft = account_get_special_folder(compose->account, F_DRAFT);
9076 cm_return_val_if_fail(draft != NULL, FALSE);
9078 if (!g_mutex_trylock(compose->mutex)) {
9079 /* we don't want to lock the mutex once it's available,
9080 * because as the only other part of compose.c locking
9081 * it is compose_close - which means once unlocked,
9082 * the compose struct will be freed */
9083 debug_print("couldn't lock mutex, probably sending\n");
9089 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9090 G_DIR_SEPARATOR, compose);
9091 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9092 FILE_OP_ERROR(tmp, "fopen");
9096 /* chmod for security */
9097 if (change_file_mode_rw(fp, tmp) < 0) {
9098 FILE_OP_ERROR(tmp, "chmod");
9099 g_warning("can't change file mode\n");
9102 /* Save draft infos */
9103 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9104 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9106 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9107 gchar *savefolderid;
9109 savefolderid = compose_get_save_to(compose);
9110 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9111 g_free(savefolderid);
9113 if (compose->return_receipt) {
9114 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9116 if (compose->privacy_system) {
9117 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9118 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9119 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9122 /* Message-ID of message replying to */
9123 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9126 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9127 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9130 /* Message-ID of message forwarding to */
9131 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9134 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9135 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9139 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9140 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9142 /* end of headers */
9143 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9150 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9154 if (fclose(fp) == EOF) {
9158 if (compose->targetinfo) {
9159 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9160 flag.perm_flags = target_locked?MSG_LOCKED:0;
9162 flag.tmp_flags = MSG_DRAFT;
9164 folder_item_scan(draft);
9165 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9166 MsgInfo *tmpinfo = NULL;
9167 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9168 if (compose->msgid) {
9169 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9172 msgnum = tmpinfo->msgnum;
9173 procmsg_msginfo_free(tmpinfo);
9174 debug_print("got draft msgnum %d from scanning\n", msgnum);
9176 debug_print("didn't get draft msgnum after scanning\n");
9179 debug_print("got draft msgnum %d from adding\n", msgnum);
9185 if (action != COMPOSE_AUTO_SAVE) {
9186 if (action != COMPOSE_DRAFT_FOR_EXIT)
9187 alertpanel_error(_("Could not save draft."));
9190 gtkut_window_popup(compose->window);
9191 val = alertpanel_full(_("Could not save draft"),
9192 _("Could not save draft.\n"
9193 "Do you want to cancel exit or discard this email?"),
9194 _("_Cancel exit"), _("_Discard email"), NULL,
9195 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9196 if (val == G_ALERTALTERNATE) {
9198 g_mutex_unlock(compose->mutex); /* must be done before closing */
9199 compose_close(compose);
9203 g_mutex_unlock(compose->mutex); /* must be done before closing */
9212 if (compose->mode == COMPOSE_REEDIT) {
9213 compose_remove_reedit_target(compose, TRUE);
9216 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9219 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9221 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9223 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9224 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9225 procmsg_msginfo_set_flags(newmsginfo, 0,
9226 MSG_HAS_ATTACHMENT);
9228 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9229 compose_register_draft(newmsginfo);
9231 procmsg_msginfo_free(newmsginfo);
9234 folder_item_scan(draft);
9236 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9238 g_mutex_unlock(compose->mutex); /* must be done before closing */
9239 compose_close(compose);
9245 path = folder_item_fetch_msg(draft, msgnum);
9247 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9250 if (g_stat(path, &s) < 0) {
9251 FILE_OP_ERROR(path, "stat");
9257 procmsg_msginfo_free(compose->targetinfo);
9258 compose->targetinfo = procmsg_msginfo_new();
9259 compose->targetinfo->msgnum = msgnum;
9260 compose->targetinfo->size = (goffset)s.st_size;
9261 compose->targetinfo->mtime = s.st_mtime;
9262 compose->targetinfo->folder = draft;
9264 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9265 compose->mode = COMPOSE_REEDIT;
9267 if (action == COMPOSE_AUTO_SAVE) {
9268 compose->autosaved_draft = compose->targetinfo;
9270 compose->modified = FALSE;
9271 compose_set_title(compose);
9275 g_mutex_unlock(compose->mutex);
9279 void compose_clear_exit_drafts(void)
9281 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9282 DRAFTED_AT_EXIT, NULL);
9283 if (is_file_exist(filepath))
9284 claws_unlink(filepath);
9289 void compose_reopen_exit_drafts(void)
9291 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9292 DRAFTED_AT_EXIT, NULL);
9293 FILE *fp = g_fopen(filepath, "rb");
9297 while (fgets(buf, sizeof(buf), fp)) {
9298 gchar **parts = g_strsplit(buf, "\t", 2);
9299 const gchar *folder = parts[0];
9300 int msgnum = parts[1] ? atoi(parts[1]):-1;
9302 if (folder && *folder && msgnum > -1) {
9303 FolderItem *item = folder_find_item_from_identifier(folder);
9304 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9306 compose_reedit(info, FALSE);
9313 compose_clear_exit_drafts();
9316 static void compose_save_cb(GtkAction *action, gpointer data)
9318 Compose *compose = (Compose *)data;
9319 compose_draft(compose, COMPOSE_KEEP_EDITING);
9320 compose->rmode = COMPOSE_REEDIT;
9323 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9325 if (compose && file_list) {
9328 for ( tmp = file_list; tmp; tmp = tmp->next) {
9329 gchar *file = (gchar *) tmp->data;
9330 gchar *utf8_filename = conv_filename_to_utf8(file);
9331 compose_attach_append(compose, file, utf8_filename, NULL);
9332 compose_changed_cb(NULL, compose);
9337 g_free(utf8_filename);
9342 static void compose_attach_cb(GtkAction *action, gpointer data)
9344 Compose *compose = (Compose *)data;
9347 if (compose->redirect_filename != NULL)
9350 file_list = filesel_select_multiple_files_open(_("Select file"));
9353 compose_attach_from_list(compose, file_list, TRUE);
9354 g_list_free(file_list);
9358 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9360 Compose *compose = (Compose *)data;
9363 file_list = filesel_select_multiple_files_open(_("Select file"));
9368 for ( tmp = file_list; tmp; tmp = tmp->next) {
9369 gchar *file = (gchar *) tmp->data;
9370 gchar *filedup = g_strdup(file);
9371 gchar *shortfile = g_path_get_basename(filedup);
9372 ComposeInsertResult res;
9374 res = compose_insert_file(compose, file);
9375 if (res == COMPOSE_INSERT_READ_ERROR) {
9376 alertpanel_error(_("File '%s' could not be read."), shortfile);
9377 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9378 alertpanel_error(_("File '%s' contained invalid characters\n"
9379 "for the current encoding, insertion may be incorrect."), shortfile);
9385 g_list_free(file_list);
9389 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9391 Compose *compose = (Compose *)data;
9393 compose_insert_sig(compose, FALSE);
9396 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9400 Compose *compose = (Compose *)data;
9402 gtkut_widget_get_uposition(widget, &x, &y);
9403 if (!compose->batch) {
9404 prefs_common.compose_x = x;
9405 prefs_common.compose_y = y;
9407 if (compose->sending || compose->updating)
9409 compose_close_cb(NULL, compose);
9413 void compose_close_toolbar(Compose *compose)
9415 compose_close_cb(NULL, compose);
9418 static void compose_close_cb(GtkAction *action, gpointer data)
9420 Compose *compose = (Compose *)data;
9424 if (compose->exteditor_tag != -1) {
9425 if (!compose_ext_editor_kill(compose))
9430 if (compose->modified) {
9431 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9432 if (!g_mutex_trylock(compose->mutex)) {
9433 /* we don't want to lock the mutex once it's available,
9434 * because as the only other part of compose.c locking
9435 * it is compose_close - which means once unlocked,
9436 * the compose struct will be freed */
9437 debug_print("couldn't lock mutex, probably sending\n");
9441 val = alertpanel(_("Discard message"),
9442 _("This message has been modified. Discard it?"),
9443 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9445 val = alertpanel(_("Save changes"),
9446 _("This message has been modified. Save the latest changes?"),
9447 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9449 g_mutex_unlock(compose->mutex);
9451 case G_ALERTDEFAULT:
9452 if (prefs_common.autosave && !reedit)
9453 compose_remove_draft(compose);
9455 case G_ALERTALTERNATE:
9456 compose_draft(data, COMPOSE_QUIT_EDITING);
9463 compose_close(compose);
9466 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9468 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9469 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9470 Compose *compose = (Compose *) data;
9473 compose->out_encoding = (CharSet)value;
9476 static void compose_address_cb(GtkAction *action, gpointer data)
9478 Compose *compose = (Compose *)data;
9480 addressbook_open(compose);
9483 static void about_show_cb(GtkAction *action, gpointer data)
9488 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9490 Compose *compose = (Compose *)data;
9495 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9496 cm_return_if_fail(tmpl != NULL);
9498 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9500 val = alertpanel(_("Apply template"), msg,
9501 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9504 if (val == G_ALERTDEFAULT)
9505 compose_template_apply(compose, tmpl, TRUE);
9506 else if (val == G_ALERTALTERNATE)
9507 compose_template_apply(compose, tmpl, FALSE);
9510 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9512 Compose *compose = (Compose *)data;
9514 compose_exec_ext_editor(compose);
9517 static void compose_undo_cb(GtkAction *action, gpointer data)
9519 Compose *compose = (Compose *)data;
9520 gboolean prev_autowrap = compose->autowrap;
9522 compose->autowrap = FALSE;
9523 undo_undo(compose->undostruct);
9524 compose->autowrap = prev_autowrap;
9527 static void compose_redo_cb(GtkAction *action, gpointer data)
9529 Compose *compose = (Compose *)data;
9530 gboolean prev_autowrap = compose->autowrap;
9532 compose->autowrap = FALSE;
9533 undo_redo(compose->undostruct);
9534 compose->autowrap = prev_autowrap;
9537 static void entry_cut_clipboard(GtkWidget *entry)
9539 if (GTK_IS_EDITABLE(entry))
9540 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9541 else if (GTK_IS_TEXT_VIEW(entry))
9542 gtk_text_buffer_cut_clipboard(
9543 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9544 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9548 static void entry_copy_clipboard(GtkWidget *entry)
9550 if (GTK_IS_EDITABLE(entry))
9551 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9552 else if (GTK_IS_TEXT_VIEW(entry))
9553 gtk_text_buffer_copy_clipboard(
9554 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9555 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9558 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9559 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9561 if (GTK_IS_TEXT_VIEW(entry)) {
9562 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9563 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9564 GtkTextIter start_iter, end_iter;
9566 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9568 if (contents == NULL)
9571 /* we shouldn't delete the selection when middle-click-pasting, or we
9572 * can't mid-click-paste our own selection */
9573 if (clip != GDK_SELECTION_PRIMARY) {
9574 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9575 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9578 if (insert_place == NULL) {
9579 /* if insert_place isn't specified, insert at the cursor.
9580 * used for Ctrl-V pasting */
9581 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9582 start = gtk_text_iter_get_offset(&start_iter);
9583 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9585 /* if insert_place is specified, paste here.
9586 * used for mid-click-pasting */
9587 start = gtk_text_iter_get_offset(insert_place);
9588 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9589 if (prefs_common.primary_paste_unselects)
9590 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9594 /* paste unwrapped: mark the paste so it's not wrapped later */
9595 end = start + strlen(contents);
9596 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9597 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9598 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9599 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9600 /* rewrap paragraph now (after a mid-click-paste) */
9601 mark_start = gtk_text_buffer_get_insert(buffer);
9602 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9603 gtk_text_iter_backward_char(&start_iter);
9604 compose_beautify_paragraph(compose, &start_iter, TRUE);
9606 } else if (GTK_IS_EDITABLE(entry))
9607 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9609 compose->modified = TRUE;
9612 static void entry_allsel(GtkWidget *entry)
9614 if (GTK_IS_EDITABLE(entry))
9615 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9616 else if (GTK_IS_TEXT_VIEW(entry)) {
9617 GtkTextIter startiter, enditer;
9618 GtkTextBuffer *textbuf;
9620 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9621 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9622 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9624 gtk_text_buffer_move_mark_by_name(textbuf,
9625 "selection_bound", &startiter);
9626 gtk_text_buffer_move_mark_by_name(textbuf,
9627 "insert", &enditer);
9631 static void compose_cut_cb(GtkAction *action, gpointer data)
9633 Compose *compose = (Compose *)data;
9634 if (compose->focused_editable
9635 #ifndef GENERIC_UMPC
9636 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9639 entry_cut_clipboard(compose->focused_editable);
9642 static void compose_copy_cb(GtkAction *action, gpointer data)
9644 Compose *compose = (Compose *)data;
9645 if (compose->focused_editable
9646 #ifndef GENERIC_UMPC
9647 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9650 entry_copy_clipboard(compose->focused_editable);
9653 static void compose_paste_cb(GtkAction *action, gpointer data)
9655 Compose *compose = (Compose *)data;
9657 GtkTextBuffer *buffer;
9659 if (compose->focused_editable &&
9660 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9661 entry_paste_clipboard(compose, compose->focused_editable,
9662 prefs_common.linewrap_pastes,
9663 GDK_SELECTION_CLIPBOARD, NULL);
9667 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9669 Compose *compose = (Compose *)data;
9670 gint wrap_quote = prefs_common.linewrap_quote;
9671 if (compose->focused_editable
9672 #ifndef GENERIC_UMPC
9673 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9676 /* let text_insert() (called directly or at a later time
9677 * after the gtk_editable_paste_clipboard) know that
9678 * text is to be inserted as a quotation. implemented
9679 * by using a simple refcount... */
9680 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9681 G_OBJECT(compose->focused_editable),
9682 "paste_as_quotation"));
9683 g_object_set_data(G_OBJECT(compose->focused_editable),
9684 "paste_as_quotation",
9685 GINT_TO_POINTER(paste_as_quotation + 1));
9686 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9687 entry_paste_clipboard(compose, compose->focused_editable,
9688 prefs_common.linewrap_pastes,
9689 GDK_SELECTION_CLIPBOARD, NULL);
9690 prefs_common.linewrap_quote = wrap_quote;
9694 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9696 Compose *compose = (Compose *)data;
9698 GtkTextBuffer *buffer;
9700 if (compose->focused_editable
9701 #ifndef GENERIC_UMPC
9702 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9705 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9706 GDK_SELECTION_CLIPBOARD, NULL);
9710 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9712 Compose *compose = (Compose *)data;
9714 GtkTextBuffer *buffer;
9716 if (compose->focused_editable
9717 #ifndef GENERIC_UMPC
9718 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9721 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9722 GDK_SELECTION_CLIPBOARD, NULL);
9726 static void compose_allsel_cb(GtkAction *action, gpointer data)
9728 Compose *compose = (Compose *)data;
9729 if (compose->focused_editable
9730 #ifndef GENERIC_UMPC
9731 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9734 entry_allsel(compose->focused_editable);
9737 static void textview_move_beginning_of_line (GtkTextView *text)
9739 GtkTextBuffer *buffer;
9743 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9745 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9746 mark = gtk_text_buffer_get_insert(buffer);
9747 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9748 gtk_text_iter_set_line_offset(&ins, 0);
9749 gtk_text_buffer_place_cursor(buffer, &ins);
9752 static void textview_move_forward_character (GtkTextView *text)
9754 GtkTextBuffer *buffer;
9758 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9760 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9761 mark = gtk_text_buffer_get_insert(buffer);
9762 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9763 if (gtk_text_iter_forward_cursor_position(&ins))
9764 gtk_text_buffer_place_cursor(buffer, &ins);
9767 static void textview_move_backward_character (GtkTextView *text)
9769 GtkTextBuffer *buffer;
9773 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9775 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9776 mark = gtk_text_buffer_get_insert(buffer);
9777 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9778 if (gtk_text_iter_backward_cursor_position(&ins))
9779 gtk_text_buffer_place_cursor(buffer, &ins);
9782 static void textview_move_forward_word (GtkTextView *text)
9784 GtkTextBuffer *buffer;
9789 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9791 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9792 mark = gtk_text_buffer_get_insert(buffer);
9793 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9794 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9795 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9796 gtk_text_iter_backward_word_start(&ins);
9797 gtk_text_buffer_place_cursor(buffer, &ins);
9801 static void textview_move_backward_word (GtkTextView *text)
9803 GtkTextBuffer *buffer;
9808 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9810 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9811 mark = gtk_text_buffer_get_insert(buffer);
9812 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9813 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9814 if (gtk_text_iter_backward_word_starts(&ins, 1))
9815 gtk_text_buffer_place_cursor(buffer, &ins);
9818 static void textview_move_end_of_line (GtkTextView *text)
9820 GtkTextBuffer *buffer;
9824 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9826 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9827 mark = gtk_text_buffer_get_insert(buffer);
9828 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9829 if (gtk_text_iter_forward_to_line_end(&ins))
9830 gtk_text_buffer_place_cursor(buffer, &ins);
9833 static void textview_move_next_line (GtkTextView *text)
9835 GtkTextBuffer *buffer;
9840 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9842 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9843 mark = gtk_text_buffer_get_insert(buffer);
9844 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9845 offset = gtk_text_iter_get_line_offset(&ins);
9846 if (gtk_text_iter_forward_line(&ins)) {
9847 gtk_text_iter_set_line_offset(&ins, offset);
9848 gtk_text_buffer_place_cursor(buffer, &ins);
9852 static void textview_move_previous_line (GtkTextView *text)
9854 GtkTextBuffer *buffer;
9859 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9861 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9862 mark = gtk_text_buffer_get_insert(buffer);
9863 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9864 offset = gtk_text_iter_get_line_offset(&ins);
9865 if (gtk_text_iter_backward_line(&ins)) {
9866 gtk_text_iter_set_line_offset(&ins, offset);
9867 gtk_text_buffer_place_cursor(buffer, &ins);
9871 static void textview_delete_forward_character (GtkTextView *text)
9873 GtkTextBuffer *buffer;
9875 GtkTextIter ins, end_iter;
9877 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9879 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9880 mark = gtk_text_buffer_get_insert(buffer);
9881 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9883 if (gtk_text_iter_forward_char(&end_iter)) {
9884 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9888 static void textview_delete_backward_character (GtkTextView *text)
9890 GtkTextBuffer *buffer;
9892 GtkTextIter ins, end_iter;
9894 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9896 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9897 mark = gtk_text_buffer_get_insert(buffer);
9898 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9900 if (gtk_text_iter_backward_char(&end_iter)) {
9901 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9905 static void textview_delete_forward_word (GtkTextView *text)
9907 GtkTextBuffer *buffer;
9909 GtkTextIter ins, end_iter;
9911 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9913 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9914 mark = gtk_text_buffer_get_insert(buffer);
9915 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9917 if (gtk_text_iter_forward_word_end(&end_iter)) {
9918 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9922 static void textview_delete_backward_word (GtkTextView *text)
9924 GtkTextBuffer *buffer;
9926 GtkTextIter ins, end_iter;
9928 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9930 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9931 mark = gtk_text_buffer_get_insert(buffer);
9932 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9934 if (gtk_text_iter_backward_word_start(&end_iter)) {
9935 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9939 static void textview_delete_line (GtkTextView *text)
9941 GtkTextBuffer *buffer;
9943 GtkTextIter ins, start_iter, end_iter;
9945 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9947 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9948 mark = gtk_text_buffer_get_insert(buffer);
9949 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9952 gtk_text_iter_set_line_offset(&start_iter, 0);
9955 if (gtk_text_iter_ends_line(&end_iter)){
9956 if (!gtk_text_iter_forward_char(&end_iter))
9957 gtk_text_iter_backward_char(&start_iter);
9960 gtk_text_iter_forward_to_line_end(&end_iter);
9961 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9964 static void textview_delete_to_line_end (GtkTextView *text)
9966 GtkTextBuffer *buffer;
9968 GtkTextIter ins, end_iter;
9970 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9972 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9973 mark = gtk_text_buffer_get_insert(buffer);
9974 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9976 if (gtk_text_iter_ends_line(&end_iter))
9977 gtk_text_iter_forward_char(&end_iter);
9979 gtk_text_iter_forward_to_line_end(&end_iter);
9980 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9983 #define DO_ACTION(name, act) { \
9984 if(!strcmp(name, a_name)) { \
9988 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9990 const gchar *a_name = gtk_action_get_name(action);
9991 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9992 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9993 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9994 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9995 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9996 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9997 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9998 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9999 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10000 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10001 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10002 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10003 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10004 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10008 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10010 Compose *compose = (Compose *)data;
10011 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10012 ComposeCallAdvancedAction action = -1;
10014 action = compose_call_advanced_action_from_path(gaction);
10017 void (*do_action) (GtkTextView *text);
10018 } action_table[] = {
10019 {textview_move_beginning_of_line},
10020 {textview_move_forward_character},
10021 {textview_move_backward_character},
10022 {textview_move_forward_word},
10023 {textview_move_backward_word},
10024 {textview_move_end_of_line},
10025 {textview_move_next_line},
10026 {textview_move_previous_line},
10027 {textview_delete_forward_character},
10028 {textview_delete_backward_character},
10029 {textview_delete_forward_word},
10030 {textview_delete_backward_word},
10031 {textview_delete_line},
10032 {textview_delete_to_line_end}
10035 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10037 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10038 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10039 if (action_table[action].do_action)
10040 action_table[action].do_action(text);
10042 g_warning("Not implemented yet.");
10046 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10050 if (GTK_IS_EDITABLE(widget)) {
10051 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10052 gtk_editable_set_position(GTK_EDITABLE(widget),
10055 if (widget->parent && widget->parent->parent
10056 && widget->parent->parent->parent) {
10057 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10058 gint y = widget->allocation.y;
10059 gint height = widget->allocation.height;
10060 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10061 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10063 if (y < (int)shown->value) {
10064 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10066 if (y + height > (int)shown->value + (int)shown->page_size) {
10067 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10068 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10069 y + height - (int)shown->page_size - 1);
10071 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10072 (int)shown->upper - (int)shown->page_size - 1);
10079 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10080 compose->focused_editable = widget;
10082 #ifdef GENERIC_UMPC
10083 if (GTK_IS_TEXT_VIEW(widget)
10084 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10085 g_object_ref(compose->notebook);
10086 g_object_ref(compose->edit_vbox);
10087 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10088 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10089 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10090 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10091 g_object_unref(compose->notebook);
10092 g_object_unref(compose->edit_vbox);
10093 g_signal_handlers_block_by_func(G_OBJECT(widget),
10094 G_CALLBACK(compose_grab_focus_cb),
10096 gtk_widget_grab_focus(widget);
10097 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10098 G_CALLBACK(compose_grab_focus_cb),
10100 } else if (!GTK_IS_TEXT_VIEW(widget)
10101 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10102 g_object_ref(compose->notebook);
10103 g_object_ref(compose->edit_vbox);
10104 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10105 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10106 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10107 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10108 g_object_unref(compose->notebook);
10109 g_object_unref(compose->edit_vbox);
10110 g_signal_handlers_block_by_func(G_OBJECT(widget),
10111 G_CALLBACK(compose_grab_focus_cb),
10113 gtk_widget_grab_focus(widget);
10114 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10115 G_CALLBACK(compose_grab_focus_cb),
10121 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10123 compose->modified = TRUE;
10124 // compose_beautify_paragraph(compose, NULL, TRUE);
10125 #ifndef GENERIC_UMPC
10126 compose_set_title(compose);
10130 static void compose_wrap_cb(GtkAction *action, gpointer data)
10132 Compose *compose = (Compose *)data;
10133 compose_beautify_paragraph(compose, NULL, TRUE);
10136 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10138 Compose *compose = (Compose *)data;
10139 compose_wrap_all_full(compose, TRUE);
10142 static void compose_find_cb(GtkAction *action, gpointer data)
10144 Compose *compose = (Compose *)data;
10146 message_search_compose(compose);
10149 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10152 Compose *compose = (Compose *)data;
10153 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10154 if (compose->autowrap)
10155 compose_wrap_all_full(compose, TRUE);
10156 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10159 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10162 Compose *compose = (Compose *)data;
10163 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10166 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10168 Compose *compose = (Compose *)data;
10170 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10173 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10175 Compose *compose = (Compose *)data;
10177 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10180 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10182 g_free(compose->privacy_system);
10184 compose->privacy_system = g_strdup(account->default_privacy_system);
10185 compose_update_privacy_system_menu_item(compose, warn);
10188 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10190 Compose *compose = (Compose *)data;
10192 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10193 gtk_widget_show(compose->ruler_hbox);
10194 prefs_common.show_ruler = TRUE;
10196 gtk_widget_hide(compose->ruler_hbox);
10197 gtk_widget_queue_resize(compose->edit_vbox);
10198 prefs_common.show_ruler = FALSE;
10202 static void compose_attach_drag_received_cb (GtkWidget *widget,
10203 GdkDragContext *context,
10206 GtkSelectionData *data,
10209 gpointer user_data)
10211 Compose *compose = (Compose *)user_data;
10214 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10216 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10218 ) && gtk_drag_get_source_widget(context) !=
10219 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10220 list = uri_list_extract_filenames((const gchar *)data->data);
10221 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10222 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10223 compose_attach_append
10224 (compose, (const gchar *)tmp->data,
10225 utf8_filename, NULL);
10226 g_free(utf8_filename);
10228 if (list) compose_changed_cb(NULL, compose);
10229 list_free_strings(list);
10231 } else if (gtk_drag_get_source_widget(context)
10232 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10233 /* comes from our summaryview */
10234 SummaryView * summaryview = NULL;
10235 GSList * list = NULL, *cur = NULL;
10237 if (mainwindow_get_mainwindow())
10238 summaryview = mainwindow_get_mainwindow()->summaryview;
10241 list = summary_get_selected_msg_list(summaryview);
10243 for (cur = list; cur; cur = cur->next) {
10244 MsgInfo *msginfo = (MsgInfo *)cur->data;
10245 gchar *file = NULL;
10247 file = procmsg_get_message_file_full(msginfo,
10250 compose_attach_append(compose, (const gchar *)file,
10251 (const gchar *)file, "message/rfc822");
10255 g_slist_free(list);
10259 static gboolean compose_drag_drop(GtkWidget *widget,
10260 GdkDragContext *drag_context,
10262 guint time, gpointer user_data)
10264 /* not handling this signal makes compose_insert_drag_received_cb
10269 static void compose_insert_drag_received_cb (GtkWidget *widget,
10270 GdkDragContext *drag_context,
10273 GtkSelectionData *data,
10276 gpointer user_data)
10278 Compose *compose = (Compose *)user_data;
10281 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10284 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10286 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10288 AlertValue val = G_ALERTDEFAULT;
10290 list = uri_list_extract_filenames((const gchar *)data->data);
10291 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10292 /* Assume a list of no files, and data has ://, is a remote link */
10293 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10294 gchar *tmpfile = get_tmp_file();
10295 str_write_to_file(tmpdata, tmpfile);
10297 compose_insert_file(compose, tmpfile);
10298 claws_unlink(tmpfile);
10300 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10301 compose_beautify_paragraph(compose, NULL, TRUE);
10304 switch (prefs_common.compose_dnd_mode) {
10305 case COMPOSE_DND_ASK:
10306 val = alertpanel_full(_("Insert or attach?"),
10307 _("Do you want to insert the contents of the file(s) "
10308 "into the message body, or attach it to the email?"),
10309 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10310 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10312 case COMPOSE_DND_INSERT:
10313 val = G_ALERTALTERNATE;
10315 case COMPOSE_DND_ATTACH:
10316 val = G_ALERTOTHER;
10319 /* unexpected case */
10320 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10323 if (val & G_ALERTDISABLE) {
10324 val &= ~G_ALERTDISABLE;
10325 /* remember what action to perform by default, only if we don't click Cancel */
10326 if (val == G_ALERTALTERNATE)
10327 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10328 else if (val == G_ALERTOTHER)
10329 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10332 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10333 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10334 list_free_strings(list);
10337 } else if (val == G_ALERTOTHER) {
10338 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10339 list_free_strings(list);
10344 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10345 compose_insert_file(compose, (const gchar *)tmp->data);
10347 list_free_strings(list);
10349 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10352 #if GTK_CHECK_VERSION(2, 8, 0)
10353 /* do nothing, handled by GTK */
10355 gchar *tmpfile = get_tmp_file();
10356 str_write_to_file((const gchar *)data->data, tmpfile);
10357 compose_insert_file(compose, tmpfile);
10358 claws_unlink(tmpfile);
10360 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10364 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10367 static void compose_header_drag_received_cb (GtkWidget *widget,
10368 GdkDragContext *drag_context,
10371 GtkSelectionData *data,
10374 gpointer user_data)
10376 GtkEditable *entry = (GtkEditable *)user_data;
10377 gchar *email = (gchar *)data->data;
10379 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10382 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10383 gchar *decoded=g_new(gchar, strlen(email));
10386 email += strlen("mailto:");
10387 decode_uri(decoded, email); /* will fit */
10388 gtk_editable_delete_text(entry, 0, -1);
10389 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10390 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10394 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10397 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10399 Compose *compose = (Compose *)data;
10401 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10402 compose->return_receipt = TRUE;
10404 compose->return_receipt = FALSE;
10407 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10409 Compose *compose = (Compose *)data;
10411 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10412 compose->remove_references = TRUE;
10414 compose->remove_references = FALSE;
10417 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10418 ComposeHeaderEntry *headerentry)
10420 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10424 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10425 GdkEventKey *event,
10426 ComposeHeaderEntry *headerentry)
10428 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10429 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10430 !(event->state & GDK_MODIFIER_MASK) &&
10431 (event->keyval == GDK_BackSpace) &&
10432 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10433 gtk_container_remove
10434 (GTK_CONTAINER(headerentry->compose->header_table),
10435 headerentry->combo);
10436 gtk_container_remove
10437 (GTK_CONTAINER(headerentry->compose->header_table),
10438 headerentry->entry);
10439 headerentry->compose->header_list =
10440 g_slist_remove(headerentry->compose->header_list,
10442 g_free(headerentry);
10443 } else if (event->keyval == GDK_Tab) {
10444 if (headerentry->compose->header_last == headerentry) {
10445 /* Override default next focus, and give it to subject_entry
10446 * instead of notebook tabs
10448 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10449 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10456 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10457 ComposeHeaderEntry *headerentry)
10459 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10460 compose_create_header_entry(headerentry->compose);
10461 g_signal_handlers_disconnect_matched
10462 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10463 0, 0, NULL, NULL, headerentry);
10465 /* Automatically scroll down */
10466 compose_show_first_last_header(headerentry->compose, FALSE);
10472 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10474 GtkAdjustment *vadj;
10476 cm_return_if_fail(compose);
10477 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10478 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10480 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10481 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10482 gtk_adjustment_changed(vadj);
10485 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10486 const gchar *text, gint len, Compose *compose)
10488 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10489 (G_OBJECT(compose->text), "paste_as_quotation"));
10492 cm_return_if_fail(text != NULL);
10494 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10495 G_CALLBACK(text_inserted),
10497 if (paste_as_quotation) {
10499 const gchar *qmark;
10501 GtkTextIter start_iter;
10504 len = strlen(text);
10506 new_text = g_strndup(text, len);
10508 qmark = compose_quote_char_from_context(compose);
10510 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10511 gtk_text_buffer_place_cursor(buffer, iter);
10513 pos = gtk_text_iter_get_offset(iter);
10515 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10516 _("Quote format error at line %d."));
10517 quote_fmt_reset_vartable();
10519 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10520 GINT_TO_POINTER(paste_as_quotation - 1));
10522 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10523 gtk_text_buffer_place_cursor(buffer, iter);
10524 gtk_text_buffer_delete_mark(buffer, mark);
10526 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10527 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10528 compose_beautify_paragraph(compose, &start_iter, FALSE);
10529 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10530 gtk_text_buffer_delete_mark(buffer, mark);
10532 if (strcmp(text, "\n") || compose->automatic_break
10533 || gtk_text_iter_starts_line(iter)) {
10534 GtkTextIter before_ins;
10535 gtk_text_buffer_insert(buffer, iter, text, len);
10536 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10537 before_ins = *iter;
10538 gtk_text_iter_backward_chars(&before_ins, len);
10539 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10542 /* check if the preceding is just whitespace or quote */
10543 GtkTextIter start_line;
10544 gchar *tmp = NULL, *quote = NULL;
10545 gint quote_len = 0, is_normal = 0;
10546 start_line = *iter;
10547 gtk_text_iter_set_line_offset(&start_line, 0);
10548 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10551 if (*tmp == '\0') {
10554 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10562 gtk_text_buffer_insert(buffer, iter, text, len);
10564 gtk_text_buffer_insert_with_tags_by_name(buffer,
10565 iter, text, len, "no_join", NULL);
10570 if (!paste_as_quotation) {
10571 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10572 compose_beautify_paragraph(compose, iter, FALSE);
10573 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10574 gtk_text_buffer_delete_mark(buffer, mark);
10577 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10578 G_CALLBACK(text_inserted),
10580 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10582 if (prefs_common.autosave &&
10583 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10584 compose->draft_timeout_tag != -2 /* disabled while loading */)
10585 compose->draft_timeout_tag = g_timeout_add
10586 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10588 static gint compose_defer_auto_save_draft(Compose *compose)
10590 compose->draft_timeout_tag = -1;
10591 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10596 static void compose_check_all(GtkAction *action, gpointer data)
10598 Compose *compose = (Compose *)data;
10599 if (!compose->gtkaspell)
10602 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10603 claws_spell_entry_check_all(
10604 CLAWS_SPELL_ENTRY(compose->subject_entry));
10606 gtkaspell_check_all(compose->gtkaspell);
10609 static void compose_highlight_all(GtkAction *action, gpointer data)
10611 Compose *compose = (Compose *)data;
10612 if (compose->gtkaspell) {
10613 claws_spell_entry_recheck_all(
10614 CLAWS_SPELL_ENTRY(compose->subject_entry));
10615 gtkaspell_highlight_all(compose->gtkaspell);
10619 static void compose_check_backwards(GtkAction *action, gpointer data)
10621 Compose *compose = (Compose *)data;
10622 if (!compose->gtkaspell) {
10623 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10627 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10628 claws_spell_entry_check_backwards(
10629 CLAWS_SPELL_ENTRY(compose->subject_entry));
10631 gtkaspell_check_backwards(compose->gtkaspell);
10634 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10636 Compose *compose = (Compose *)data;
10637 if (!compose->gtkaspell) {
10638 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10642 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10643 claws_spell_entry_check_forwards_go(
10644 CLAWS_SPELL_ENTRY(compose->subject_entry));
10646 gtkaspell_check_forwards_go(compose->gtkaspell);
10651 *\brief Guess originating forward account from MsgInfo and several
10652 * "common preference" settings. Return NULL if no guess.
10654 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10656 PrefsAccount *account = NULL;
10658 cm_return_val_if_fail(msginfo, NULL);
10659 cm_return_val_if_fail(msginfo->folder, NULL);
10660 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10662 if (msginfo->folder->prefs->enable_default_account)
10663 account = account_find_from_id(msginfo->folder->prefs->default_account);
10666 account = msginfo->folder->folder->account;
10668 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10670 Xstrdup_a(to, msginfo->to, return NULL);
10671 extract_address(to);
10672 account = account_find_from_address(to, FALSE);
10675 if (!account && prefs_common.forward_account_autosel) {
10676 gchar cc[BUFFSIZE];
10677 if (!procheader_get_header_from_msginfo
10678 (msginfo, cc,sizeof cc , "Cc:")) {
10679 gchar *buf = cc + strlen("Cc:");
10680 extract_address(buf);
10681 account = account_find_from_address(buf, FALSE);
10685 if (!account && prefs_common.forward_account_autosel) {
10686 gchar deliveredto[BUFFSIZE];
10687 if (!procheader_get_header_from_msginfo
10688 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10689 gchar *buf = deliveredto + strlen("Delivered-To:");
10690 extract_address(buf);
10691 account = account_find_from_address(buf, FALSE);
10698 gboolean compose_close(Compose *compose)
10702 if (!g_mutex_trylock(compose->mutex)) {
10703 /* we have to wait for the (possibly deferred by auto-save)
10704 * drafting to be done, before destroying the compose under
10706 debug_print("waiting for drafting to finish...\n");
10707 compose_allow_user_actions(compose, FALSE);
10708 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10711 cm_return_val_if_fail(compose, FALSE);
10712 gtkut_widget_get_uposition(compose->window, &x, &y);
10713 if (!compose->batch) {
10714 prefs_common.compose_x = x;
10715 prefs_common.compose_y = y;
10717 g_mutex_unlock(compose->mutex);
10718 compose_destroy(compose);
10723 * Add entry field for each address in list.
10724 * \param compose E-Mail composition object.
10725 * \param listAddress List of (formatted) E-Mail addresses.
10727 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10730 node = listAddress;
10732 addr = ( gchar * ) node->data;
10733 compose_entry_append( compose, addr, COMPOSE_TO );
10734 node = g_list_next( node );
10738 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10739 guint action, gboolean opening_multiple)
10741 gchar *body = NULL;
10742 GSList *new_msglist = NULL;
10743 MsgInfo *tmp_msginfo = NULL;
10744 gboolean originally_enc = FALSE;
10745 gboolean originally_sig = FALSE;
10746 Compose *compose = NULL;
10747 gchar *s_system = NULL;
10749 cm_return_if_fail(msgview != NULL);
10751 cm_return_if_fail(msginfo_list != NULL);
10753 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10754 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10755 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10757 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10758 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10759 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10760 orig_msginfo, mimeinfo);
10761 if (tmp_msginfo != NULL) {
10762 new_msglist = g_slist_append(NULL, tmp_msginfo);
10764 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10765 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10766 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10768 tmp_msginfo->folder = orig_msginfo->folder;
10769 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10770 if (orig_msginfo->tags) {
10771 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10772 tmp_msginfo->folder->tags_dirty = TRUE;
10778 if (!opening_multiple)
10779 body = messageview_get_selection(msgview);
10782 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10783 procmsg_msginfo_free(tmp_msginfo);
10784 g_slist_free(new_msglist);
10786 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10788 if (compose && originally_enc) {
10789 compose_force_encryption(compose, compose->account, FALSE, s_system);
10792 if (compose && originally_sig && compose->account->default_sign_reply) {
10793 compose_force_signing(compose, compose->account, s_system);
10799 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10802 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10803 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10804 GSList *cur = msginfo_list;
10805 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10806 "messages. Opening the windows "
10807 "could take some time. Do you "
10808 "want to continue?"),
10809 g_slist_length(msginfo_list));
10810 if (g_slist_length(msginfo_list) > 9
10811 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10812 != G_ALERTALTERNATE) {
10817 /* We'll open multiple compose windows */
10818 /* let the WM place the next windows */
10819 compose_force_window_origin = FALSE;
10820 for (; cur; cur = cur->next) {
10822 tmplist.data = cur->data;
10823 tmplist.next = NULL;
10824 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10826 compose_force_window_origin = TRUE;
10828 /* forwarding multiple mails as attachments is done via a
10829 * single compose window */
10830 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10834 void compose_set_position(Compose *compose, gint pos)
10836 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10838 gtkut_text_view_set_position(text, pos);
10841 gboolean compose_search_string(Compose *compose,
10842 const gchar *str, gboolean case_sens)
10844 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10846 return gtkut_text_view_search_string(text, str, case_sens);
10849 gboolean compose_search_string_backward(Compose *compose,
10850 const gchar *str, gboolean case_sens)
10852 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10854 return gtkut_text_view_search_string_backward(text, str, case_sens);
10857 /* allocate a msginfo structure and populate its data from a compose data structure */
10858 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10860 MsgInfo *newmsginfo;
10862 gchar buf[BUFFSIZE];
10864 cm_return_val_if_fail( compose != NULL, NULL );
10866 newmsginfo = procmsg_msginfo_new();
10869 get_rfc822_date(buf, sizeof(buf));
10870 newmsginfo->date = g_strdup(buf);
10873 if (compose->from_name) {
10874 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10875 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10879 if (compose->subject_entry)
10880 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10882 /* to, cc, reply-to, newsgroups */
10883 for (list = compose->header_list; list; list = list->next) {
10884 gchar *header = gtk_editable_get_chars(
10886 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10887 gchar *entry = gtk_editable_get_chars(
10888 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10890 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10891 if ( newmsginfo->to == NULL ) {
10892 newmsginfo->to = g_strdup(entry);
10893 } else if (entry && *entry) {
10894 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10895 g_free(newmsginfo->to);
10896 newmsginfo->to = tmp;
10899 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10900 if ( newmsginfo->cc == NULL ) {
10901 newmsginfo->cc = g_strdup(entry);
10902 } else if (entry && *entry) {
10903 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10904 g_free(newmsginfo->cc);
10905 newmsginfo->cc = tmp;
10908 if ( strcasecmp(header,
10909 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10910 if ( newmsginfo->newsgroups == NULL ) {
10911 newmsginfo->newsgroups = g_strdup(entry);
10912 } else if (entry && *entry) {
10913 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10914 g_free(newmsginfo->newsgroups);
10915 newmsginfo->newsgroups = tmp;
10923 /* other data is unset */
10929 /* update compose's dictionaries from folder dict settings */
10930 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10931 FolderItem *folder_item)
10933 cm_return_if_fail(compose != NULL);
10935 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10936 FolderItemPrefs *prefs = folder_item->prefs;
10938 if (prefs->enable_default_dictionary)
10939 gtkaspell_change_dict(compose->gtkaspell,
10940 prefs->default_dictionary, FALSE);
10941 if (folder_item->prefs->enable_default_alt_dictionary)
10942 gtkaspell_change_alt_dict(compose->gtkaspell,
10943 prefs->default_alt_dictionary);
10944 if (prefs->enable_default_dictionary
10945 || prefs->enable_default_alt_dictionary)
10946 compose_spell_menu_changed(compose);