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_template_apply (Compose *compose,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
337 static void compose_dict_changed (void *data);
339 static void compose_add_field_list ( Compose *compose,
340 GList *listAddress );
342 /* callback functions */
344 static gboolean compose_edit_size_alloc (GtkEditable *widget,
345 GtkAllocation *allocation,
346 GtkSHRuler *shruler);
347 static void account_activated (GtkComboBox *optmenu,
349 static void attach_selected (GtkTreeView *tree_view,
350 GtkTreePath *tree_path,
351 GtkTreeViewColumn *column,
353 static gboolean attach_button_pressed (GtkWidget *widget,
354 GdkEventButton *event,
356 static gboolean attach_key_pressed (GtkWidget *widget,
359 static void compose_send_cb (GtkAction *action, gpointer data);
360 static void compose_send_later_cb (GtkAction *action, gpointer data);
362 static void compose_save_cb (GtkAction *action,
365 static void compose_attach_cb (GtkAction *action,
367 static void compose_insert_file_cb (GtkAction *action,
369 static void compose_insert_sig_cb (GtkAction *action,
372 static void compose_close_cb (GtkAction *action,
375 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
377 static void compose_address_cb (GtkAction *action,
379 static void about_show_cb (GtkAction *action,
381 static void compose_template_activate_cb(GtkWidget *widget,
384 static void compose_ext_editor_cb (GtkAction *action,
387 static gint compose_delete_cb (GtkWidget *widget,
391 static void compose_undo_cb (GtkAction *action,
393 static void compose_redo_cb (GtkAction *action,
395 static void compose_cut_cb (GtkAction *action,
397 static void compose_copy_cb (GtkAction *action,
399 static void compose_paste_cb (GtkAction *action,
401 static void compose_paste_as_quote_cb (GtkAction *action,
403 static void compose_paste_no_wrap_cb (GtkAction *action,
405 static void compose_paste_wrap_cb (GtkAction *action,
407 static void compose_allsel_cb (GtkAction *action,
410 static void compose_advanced_action_cb (GtkAction *action,
413 static void compose_grab_focus_cb (GtkWidget *widget,
416 static void compose_changed_cb (GtkTextBuffer *textbuf,
419 static void compose_wrap_cb (GtkAction *action,
421 static void compose_wrap_all_cb (GtkAction *action,
423 static void compose_find_cb (GtkAction *action,
425 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
430 static void compose_toggle_ruler_cb (GtkToggleAction *action,
432 static void compose_toggle_sign_cb (GtkToggleAction *action,
434 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
436 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
437 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
438 static void activate_privacy_system (Compose *compose,
439 PrefsAccount *account,
441 static void compose_use_signing(Compose *compose, gboolean use_signing);
442 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
443 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
445 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
447 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
449 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
451 static void compose_attach_drag_received_cb (GtkWidget *widget,
452 GdkDragContext *drag_context,
455 GtkSelectionData *data,
459 static void compose_insert_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_header_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
476 static gboolean compose_drag_drop (GtkWidget *widget,
477 GdkDragContext *drag_context,
479 guint time, gpointer user_data);
481 static void text_inserted (GtkTextBuffer *buffer,
486 static Compose *compose_generic_reply(MsgInfo *msginfo,
487 ComposeQuoteMode quote_mode,
491 gboolean followup_and_reply_to,
494 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
496 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
498 ComposeHeaderEntry *headerentry);
499 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
500 ComposeHeaderEntry *headerentry);
502 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
504 static void compose_allow_user_actions (Compose *compose, gboolean allow);
506 static void compose_nothing_cb (GtkAction *action, gpointer data)
512 static void compose_check_all (GtkAction *action, gpointer data);
513 static void compose_highlight_all (GtkAction *action, gpointer data);
514 static void compose_check_backwards (GtkAction *action, gpointer data);
515 static void compose_check_forwards_go (GtkAction *action, gpointer data);
518 static gint compose_defer_auto_save_draft (Compose *compose);
519 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
521 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
524 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
525 FolderItem *folder_item);
527 static void compose_attach_update_label(Compose *compose);
529 static GtkActionEntry compose_popup_entries[] =
531 {"Compose", NULL, "Compose" },
532 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
533 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
534 {"Compose/---", NULL, "---", NULL, NULL, NULL },
535 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
538 static GtkActionEntry compose_entries[] =
540 {"Menu", NULL, "Menu" },
542 {"Message", NULL, N_("_Message") },
543 {"Edit", NULL, N_("_Edit") },
545 {"Spelling", NULL, N_("_Spelling") },
547 {"Options", NULL, N_("_Options") },
548 {"Tools", NULL, N_("_Tools") },
549 {"Help", NULL, N_("_Help") },
551 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
552 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
553 {"Message/---", NULL, "---" },
555 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
556 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
557 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
560 /* {"Message/---", NULL, "---" }, */
561 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
564 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
565 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
566 {"Edit/---", NULL, "---" },
568 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
569 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
570 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
572 {"Edit/SpecialPaste", NULL, N_("Special paste") },
573 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
574 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
575 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
577 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
579 {"Edit/Advanced", NULL, N_("A_dvanced") },
580 {"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*/
581 {"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*/
582 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
583 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"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*/
590 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
591 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
592 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
593 {"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*/
595 /* {"Edit/---", NULL, "---" }, */
596 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
598 /* {"Edit/---", NULL, "---" }, */
599 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
600 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
601 /* {"Edit/---", NULL, "---" }, */
602 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
605 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
606 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
607 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
608 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
610 {"Spelling/---", NULL, "---" },
611 {"Spelling/Options", NULL, N_("_Options") },
616 {"Options/ReplyMode", NULL, N_("Reply _mode") },
617 {"Options/---", NULL, "---" },
618 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
619 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
621 /* {"Options/---", NULL, "---" }, */
623 {"Options/Priority", NULL, N_("_Priority") },
625 {"Options/Encoding", NULL, N_("Character _encoding") },
626 {"Options/Encoding/---", NULL, "---" },
627 #define ENC_ACTION(cs_char,c_char,string) \
628 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
630 {"Options/Encoding/Western", NULL, N_("Western European") },
631 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
632 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
633 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
634 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
635 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
636 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
637 {"Options/Encoding/Korean", NULL, N_("Korean") },
638 {"Options/Encoding/Thai", NULL, N_("Thai") },
641 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
643 {"Tools/Template", NULL, N_("_Template") },
644 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
645 {"Tools/Actions", NULL, N_("Actio_ns") },
646 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
649 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
652 static GtkToggleActionEntry compose_toggle_entries[] =
654 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
655 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
656 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
657 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
658 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
659 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
660 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
663 static GtkRadioActionEntry compose_radio_rm_entries[] =
665 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
667 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
668 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
671 static GtkRadioActionEntry compose_radio_prio_entries[] =
673 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
677 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
680 static GtkRadioActionEntry compose_radio_enc_entries[] =
682 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
715 static GtkTargetEntry compose_mime_types[] =
717 {"text/uri-list", 0, 0},
718 {"UTF8_STRING", 0, 0},
722 static gboolean compose_put_existing_to_front(MsgInfo *info)
724 GList *compose_list = compose_get_compose_list();
728 for (elem = compose_list; elem != NULL && elem->data != NULL;
730 Compose *c = (Compose*)elem->data;
732 if (!c->targetinfo || !c->targetinfo->msgid ||
736 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
737 gtkut_window_popup(c->window);
745 static GdkColor quote_color1 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color2 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
749 static GdkColor quote_color3 =
750 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor1 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor2 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_bgcolor3 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor signature_color = {
766 static GdkColor uri_color = {
773 static void compose_create_tags(GtkTextView *text, Compose *compose)
775 GtkTextBuffer *buffer;
776 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 buffer = gtk_text_view_get_buffer(text);
784 if (prefs_common.enable_color) {
785 /* grab the quote colors, converting from an int to a GdkColor */
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
798 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
800 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
803 signature_color = quote_color1 = quote_color2 = quote_color3 =
804 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
807 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
808 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
809 "foreground-gdk", "e_color1,
810 "paragraph-background-gdk", "e_bgcolor1,
812 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
813 "foreground-gdk", "e_color2,
814 "paragraph-background-gdk", "e_bgcolor2,
816 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
817 "foreground-gdk", "e_color3,
818 "paragraph-background-gdk", "e_bgcolor3,
821 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
822 "foreground-gdk", "e_color1,
824 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
825 "foreground-gdk", "e_color2,
827 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
828 "foreground-gdk", "e_color3,
832 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
833 "foreground-gdk", &signature_color,
836 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
837 "foreground-gdk", &uri_color,
839 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
840 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
842 color[0] = quote_color1;
843 color[1] = quote_color2;
844 color[2] = quote_color3;
845 color[3] = quote_bgcolor1;
846 color[4] = quote_bgcolor2;
847 color[5] = quote_bgcolor3;
848 color[6] = signature_color;
849 color[7] = uri_color;
850 cmap = gdk_drawable_get_colormap(compose->window->window);
851 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
853 for (i = 0; i < 8; i++) {
854 if (success[i] == FALSE) {
857 g_warning("Compose: color allocation failed.\n");
858 style = gtk_widget_get_style(GTK_WIDGET(text));
859 quote_color1 = quote_color2 = quote_color3 =
860 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
861 signature_color = uri_color = black;
866 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
867 GPtrArray *attach_files)
869 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
872 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
874 return compose_generic_new(account, mailto, item, NULL, NULL);
877 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
879 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
882 #define SCROLL_TO_CURSOR(compose) { \
883 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
884 gtk_text_view_get_buffer( \
885 GTK_TEXT_VIEW(compose->text))); \
886 gtk_text_view_scroll_mark_onscreen( \
887 GTK_TEXT_VIEW(compose->text), \
891 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
894 if (folderidentifier) {
895 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
896 prefs_common.compose_save_to_history = add_history(
897 prefs_common.compose_save_to_history, folderidentifier);
898 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
899 prefs_common.compose_save_to_history);
902 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
903 if (folderidentifier)
904 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
906 gtk_entry_set_text(GTK_ENTRY(entry), "");
909 static gchar *compose_get_save_to(Compose *compose)
912 gchar *result = NULL;
913 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
914 result = gtk_editable_get_chars(entry, 0, -1);
917 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
918 prefs_common.compose_save_to_history = add_history(
919 prefs_common.compose_save_to_history, result);
920 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
921 prefs_common.compose_save_to_history);
926 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
927 GPtrArray *attach_files, GList *listAddress )
930 GtkTextView *textview;
931 GtkTextBuffer *textbuf;
933 const gchar *subject_format = NULL;
934 const gchar *body_format = NULL;
935 gchar *mailto_from = NULL;
936 PrefsAccount *mailto_account = NULL;
937 MsgInfo* dummyinfo = NULL;
939 /* check if mailto defines a from */
940 if (mailto && *mailto != '\0') {
941 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
942 /* mailto defines a from, check if we can get account prefs from it,
943 if not, the account prefs will be guessed using other ways, but we'll keep
946 mailto_account = account_find_from_address(mailto_from, TRUE);
948 account = mailto_account;
951 /* if no account prefs set from mailto, set if from folder prefs (if any) */
952 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
953 account = account_find_from_id(item->prefs->default_account);
955 /* if no account prefs set, fallback to the current one */
956 if (!account) account = cur_account;
957 cm_return_val_if_fail(account != NULL, NULL);
959 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
961 /* override from name if mailto asked for it */
963 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
966 /* override from name according to folder properties */
967 if (item && item->prefs &&
968 item->prefs->compose_with_format &&
969 item->prefs->compose_override_from_format &&
970 *item->prefs->compose_override_from_format != '\0') {
975 dummyinfo = compose_msginfo_new_from_compose(compose);
977 /* decode \-escape sequences in the internal representation of the quote format */
978 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
979 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
982 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
985 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
987 quote_fmt_scan_string(tmp);
990 buf = quote_fmt_get_buffer();
992 alertpanel_error(_("New message From format error."));
994 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
995 quote_fmt_reset_vartable();
1000 compose->replyinfo = NULL;
1001 compose->fwdinfo = NULL;
1003 textview = GTK_TEXT_VIEW(compose->text);
1004 textbuf = gtk_text_view_get_buffer(textview);
1005 compose_create_tags(textview, compose);
1007 undo_block(compose->undostruct);
1009 compose_set_dictionaries_from_folder_prefs(compose, item);
1012 if (account->auto_sig)
1013 compose_insert_sig(compose, FALSE);
1014 gtk_text_buffer_get_start_iter(textbuf, &iter);
1015 gtk_text_buffer_place_cursor(textbuf, &iter);
1017 if (account->protocol != A_NNTP) {
1018 if (mailto && *mailto != '\0') {
1019 compose_entries_set(compose, mailto, COMPOSE_TO);
1021 } else if (item && item->prefs) {
1022 if (item->prefs->enable_default_bcc) {
1023 compose_entry_append(compose, item->prefs->default_bcc, COMPOSE_BCC);
1025 if (item->prefs->enable_default_cc) {
1026 compose_entry_append(compose, item->prefs->default_cc, COMPOSE_CC);
1028 if (item->prefs->enable_default_replyto) {
1029 compose_entry_append(compose, item->prefs->default_replyto, COMPOSE_REPLYTO);
1031 if (item->prefs->enable_default_to) {
1032 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1033 compose_entry_mark_default_to(compose, item->prefs->default_to);
1036 if (item && item->ret_rcpt) {
1037 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1040 if (mailto && *mailto != '\0') {
1041 if (!strchr(mailto, '@'))
1042 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1044 compose_entries_set(compose, mailto, COMPOSE_TO);
1045 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1046 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1049 * CLAWS: just don't allow return receipt request, even if the user
1050 * may want to send an email. simple but foolproof.
1052 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1054 compose_add_field_list( compose, listAddress );
1056 if (item && item->prefs && item->prefs->compose_with_format) {
1057 subject_format = item->prefs->compose_subject_format;
1058 body_format = item->prefs->compose_body_format;
1059 } else if (account->compose_with_format) {
1060 subject_format = account->compose_subject_format;
1061 body_format = account->compose_body_format;
1062 } else if (prefs_common.compose_with_format) {
1063 subject_format = prefs_common.compose_subject_format;
1064 body_format = prefs_common.compose_body_format;
1067 if (subject_format || body_format) {
1070 && *subject_format != '\0' )
1072 gchar *subject = NULL;
1077 dummyinfo = compose_msginfo_new_from_compose(compose);
1079 /* decode \-escape sequences in the internal representation of the quote format */
1080 tmp = malloc(strlen(subject_format)+1);
1081 pref_get_unescaped_pref(tmp, subject_format);
1083 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1085 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1086 compose->gtkaspell);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1090 quote_fmt_scan_string(tmp);
1093 buf = quote_fmt_get_buffer();
1095 alertpanel_error(_("New message subject format error."));
1097 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1098 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1099 quote_fmt_reset_vartable();
1106 && *body_format != '\0' )
1109 GtkTextBuffer *buffer;
1110 GtkTextIter start, end;
1114 dummyinfo = compose_msginfo_new_from_compose(compose);
1116 text = GTK_TEXT_VIEW(compose->text);
1117 buffer = gtk_text_view_get_buffer(text);
1118 gtk_text_buffer_get_start_iter(buffer, &start);
1119 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1120 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1122 compose_quote_fmt(compose, dummyinfo,
1124 NULL, tmp, FALSE, TRUE,
1125 _("The body of the \"New message\" template has an error at line %d."));
1126 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1127 quote_fmt_reset_vartable();
1133 procmsg_msginfo_free( dummyinfo );
1139 for (i = 0; i < attach_files->len; i++) {
1140 file = g_ptr_array_index(attach_files, i);
1141 compose_attach_append(compose, file, file, NULL);
1145 compose_show_first_last_header(compose, TRUE);
1147 /* Set save folder */
1148 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1149 gchar *folderidentifier;
1151 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1152 folderidentifier = folder_item_get_identifier(item);
1153 compose_set_save_to(compose, folderidentifier);
1154 g_free(folderidentifier);
1157 gtk_widget_grab_focus(compose->header_last->entry);
1159 undo_unblock(compose->undostruct);
1161 if (prefs_common.auto_exteditor)
1162 compose_exec_ext_editor(compose);
1164 compose->draft_timeout_tag = -1;
1165 SCROLL_TO_CURSOR(compose);
1167 compose->modified = FALSE;
1168 compose_set_title(compose);
1172 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1173 gboolean override_pref, const gchar *system)
1175 const gchar *privacy = NULL;
1177 cm_return_if_fail(compose != NULL);
1178 cm_return_if_fail(account != NULL);
1180 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1185 else if (account->default_privacy_system
1186 && strlen(account->default_privacy_system)) {
1187 privacy = account->default_privacy_system;
1189 GSList *privacy_avail = privacy_get_system_ids();
1190 if (privacy_avail && g_slist_length(privacy_avail)) {
1191 privacy = (gchar *)(privacy_avail->data);
1194 if (privacy != NULL) {
1196 g_free(compose->privacy_system);
1197 compose->privacy_system = NULL;
1199 if (compose->privacy_system == NULL)
1200 compose->privacy_system = g_strdup(privacy);
1201 else if (*(compose->privacy_system) == '\0') {
1202 g_free(compose->privacy_system);
1203 compose->privacy_system = g_strdup(privacy);
1205 compose_update_privacy_system_menu_item(compose, FALSE);
1206 compose_use_encryption(compose, TRUE);
1210 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1212 const gchar *privacy = NULL;
1216 else if (account->default_privacy_system
1217 && strlen(account->default_privacy_system)) {
1218 privacy = account->default_privacy_system;
1220 GSList *privacy_avail = privacy_get_system_ids();
1221 if (privacy_avail && g_slist_length(privacy_avail)) {
1222 privacy = (gchar *)(privacy_avail->data);
1226 if (privacy != NULL) {
1228 g_free(compose->privacy_system);
1229 compose->privacy_system = NULL;
1231 if (compose->privacy_system == NULL)
1232 compose->privacy_system = g_strdup(privacy);
1233 compose_update_privacy_system_menu_item(compose, FALSE);
1234 compose_use_signing(compose, TRUE);
1238 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1242 Compose *compose = NULL;
1244 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1246 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1247 cm_return_val_if_fail(msginfo != NULL, NULL);
1249 list_len = g_slist_length(msginfo_list);
1253 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1254 FALSE, prefs_common.default_reply_list, FALSE, body);
1256 case COMPOSE_REPLY_WITH_QUOTE:
1257 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1258 FALSE, prefs_common.default_reply_list, FALSE, body);
1260 case COMPOSE_REPLY_WITHOUT_QUOTE:
1261 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1262 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1264 case COMPOSE_REPLY_TO_SENDER:
1265 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1266 FALSE, FALSE, TRUE, body);
1268 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1269 compose = compose_followup_and_reply_to(msginfo,
1270 COMPOSE_QUOTE_CHECK,
1271 FALSE, FALSE, body);
1273 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1274 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1275 FALSE, FALSE, TRUE, body);
1277 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1278 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1279 FALSE, FALSE, TRUE, NULL);
1281 case COMPOSE_REPLY_TO_ALL:
1282 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1283 TRUE, FALSE, FALSE, body);
1285 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1286 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1287 TRUE, FALSE, FALSE, body);
1289 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1290 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1291 TRUE, FALSE, FALSE, NULL);
1293 case COMPOSE_REPLY_TO_LIST:
1294 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1295 FALSE, TRUE, FALSE, body);
1297 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1298 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1299 FALSE, TRUE, FALSE, body);
1301 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1302 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1303 FALSE, TRUE, FALSE, NULL);
1305 case COMPOSE_FORWARD:
1306 if (prefs_common.forward_as_attachment) {
1307 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1310 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1314 case COMPOSE_FORWARD_INLINE:
1315 /* check if we reply to more than one Message */
1316 if (list_len == 1) {
1317 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1320 /* more messages FALL THROUGH */
1321 case COMPOSE_FORWARD_AS_ATTACH:
1322 compose = compose_forward_multiple(NULL, msginfo_list);
1324 case COMPOSE_REDIRECT:
1325 compose = compose_redirect(NULL, msginfo, FALSE);
1328 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1331 if (compose == NULL) {
1332 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1336 compose->rmode = mode;
1337 switch (compose->rmode) {
1339 case COMPOSE_REPLY_WITH_QUOTE:
1340 case COMPOSE_REPLY_WITHOUT_QUOTE:
1341 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1342 debug_print("reply mode Normal\n");
1343 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1344 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1346 case COMPOSE_REPLY_TO_SENDER:
1347 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1348 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1349 debug_print("reply mode Sender\n");
1350 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1352 case COMPOSE_REPLY_TO_ALL:
1353 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1354 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1355 debug_print("reply mode All\n");
1356 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1358 case COMPOSE_REPLY_TO_LIST:
1359 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1360 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1361 debug_print("reply mode List\n");
1362 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1370 static Compose *compose_reply(MsgInfo *msginfo,
1371 ComposeQuoteMode quote_mode,
1377 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1378 to_sender, FALSE, body);
1381 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1382 ComposeQuoteMode quote_mode,
1387 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1388 to_sender, TRUE, body);
1391 static void compose_extract_original_charset(Compose *compose)
1393 MsgInfo *info = NULL;
1394 if (compose->replyinfo) {
1395 info = compose->replyinfo;
1396 } else if (compose->fwdinfo) {
1397 info = compose->fwdinfo;
1398 } else if (compose->targetinfo) {
1399 info = compose->targetinfo;
1402 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1403 MimeInfo *partinfo = mimeinfo;
1404 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1405 partinfo = procmime_mimeinfo_next(partinfo);
1407 compose->orig_charset =
1408 g_strdup(procmime_mimeinfo_get_parameter(
1409 partinfo, "charset"));
1411 procmime_mimeinfo_free_all(mimeinfo);
1415 #define SIGNAL_BLOCK(buffer) { \
1416 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1417 G_CALLBACK(compose_changed_cb), \
1419 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1420 G_CALLBACK(text_inserted), \
1424 #define SIGNAL_UNBLOCK(buffer) { \
1425 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1426 G_CALLBACK(compose_changed_cb), \
1428 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1429 G_CALLBACK(text_inserted), \
1433 static Compose *compose_generic_reply(MsgInfo *msginfo,
1434 ComposeQuoteMode quote_mode,
1435 gboolean to_all, gboolean to_ml,
1437 gboolean followup_and_reply_to,
1441 PrefsAccount *account = NULL;
1442 GtkTextView *textview;
1443 GtkTextBuffer *textbuf;
1444 gboolean quote = FALSE;
1445 const gchar *qmark = NULL;
1446 const gchar *body_fmt = NULL;
1447 gchar *s_system = NULL;
1449 cm_return_val_if_fail(msginfo != NULL, NULL);
1450 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1452 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1454 cm_return_val_if_fail(account != NULL, NULL);
1456 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1458 compose->updating = TRUE;
1460 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1461 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1463 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1464 if (!compose->replyinfo)
1465 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1467 compose_extract_original_charset(compose);
1469 if (msginfo->folder && msginfo->folder->ret_rcpt)
1470 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1472 /* Set save folder */
1473 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1474 gchar *folderidentifier;
1476 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1477 folderidentifier = folder_item_get_identifier(msginfo->folder);
1478 compose_set_save_to(compose, folderidentifier);
1479 g_free(folderidentifier);
1482 if (compose_parse_header(compose, msginfo) < 0) {
1483 compose->updating = FALSE;
1484 compose_destroy(compose);
1488 /* override from name according to folder properties */
1489 if (msginfo->folder && msginfo->folder->prefs &&
1490 msginfo->folder->prefs->reply_with_format &&
1491 msginfo->folder->prefs->reply_override_from_format &&
1492 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1497 /* decode \-escape sequences in the internal representation of the quote format */
1498 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1499 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1502 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1503 compose->gtkaspell);
1505 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1507 quote_fmt_scan_string(tmp);
1510 buf = quote_fmt_get_buffer();
1512 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1514 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1515 quote_fmt_reset_vartable();
1520 textview = (GTK_TEXT_VIEW(compose->text));
1521 textbuf = gtk_text_view_get_buffer(textview);
1522 compose_create_tags(textview, compose);
1524 undo_block(compose->undostruct);
1526 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1529 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1530 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1531 /* use the reply format of folder (if enabled), or the account's one
1532 (if enabled) or fallback to the global reply format, which is always
1533 enabled (even if empty), and use the relevant quotemark */
1535 if (msginfo->folder && msginfo->folder->prefs &&
1536 msginfo->folder->prefs->reply_with_format) {
1537 qmark = msginfo->folder->prefs->reply_quotemark;
1538 body_fmt = msginfo->folder->prefs->reply_body_format;
1540 } else if (account->reply_with_format) {
1541 qmark = account->reply_quotemark;
1542 body_fmt = account->reply_body_format;
1545 qmark = prefs_common.quotemark;
1546 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1547 body_fmt = gettext(prefs_common.quotefmt);
1554 /* empty quotemark is not allowed */
1555 if (qmark == NULL || *qmark == '\0')
1557 compose_quote_fmt(compose, compose->replyinfo,
1558 body_fmt, qmark, body, FALSE, TRUE,
1559 _("The body of the \"Reply\" template has an error at line %d."));
1560 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1561 quote_fmt_reset_vartable();
1564 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1565 compose_force_encryption(compose, account, FALSE, s_system);
1568 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1569 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1570 compose_force_signing(compose, account, s_system);
1574 SIGNAL_BLOCK(textbuf);
1576 if (account->auto_sig)
1577 compose_insert_sig(compose, FALSE);
1579 compose_wrap_all(compose);
1581 SIGNAL_UNBLOCK(textbuf);
1583 gtk_widget_grab_focus(compose->text);
1585 undo_unblock(compose->undostruct);
1587 if (prefs_common.auto_exteditor)
1588 compose_exec_ext_editor(compose);
1590 compose->modified = FALSE;
1591 compose_set_title(compose);
1593 compose->updating = FALSE;
1594 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1595 SCROLL_TO_CURSOR(compose);
1597 if (compose->deferred_destroy) {
1598 compose_destroy(compose);
1605 #define INSERT_FW_HEADER(var, hdr) \
1606 if (msginfo->var && *msginfo->var) { \
1607 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1608 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1609 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1612 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1613 gboolean as_attach, const gchar *body,
1614 gboolean no_extedit,
1618 GtkTextView *textview;
1619 GtkTextBuffer *textbuf;
1622 cm_return_val_if_fail(msginfo != NULL, NULL);
1623 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1626 !(account = compose_guess_forward_account_from_msginfo
1628 account = cur_account;
1630 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1632 compose->updating = TRUE;
1633 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1634 if (!compose->fwdinfo)
1635 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1637 compose_extract_original_charset(compose);
1639 if (msginfo->subject && *msginfo->subject) {
1640 gchar *buf, *buf2, *p;
1642 buf = p = g_strdup(msginfo->subject);
1643 p += subject_get_prefix_length(p);
1644 memmove(buf, p, strlen(p) + 1);
1646 buf2 = g_strdup_printf("Fw: %s", buf);
1647 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1653 /* override from name according to folder properties */
1654 if (msginfo->folder && msginfo->folder->prefs &&
1655 msginfo->folder->prefs->forward_with_format &&
1656 msginfo->folder->prefs->forward_override_from_format &&
1657 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1661 MsgInfo *full_msginfo = NULL;
1664 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1666 full_msginfo = procmsg_msginfo_copy(msginfo);
1668 /* decode \-escape sequences in the internal representation of the quote format */
1669 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1670 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1673 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1674 compose->gtkaspell);
1676 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1678 quote_fmt_scan_string(tmp);
1681 buf = quote_fmt_get_buffer();
1683 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1685 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1686 quote_fmt_reset_vartable();
1689 procmsg_msginfo_free(full_msginfo);
1692 textview = GTK_TEXT_VIEW(compose->text);
1693 textbuf = gtk_text_view_get_buffer(textview);
1694 compose_create_tags(textview, compose);
1696 undo_block(compose->undostruct);
1700 msgfile = procmsg_get_message_file(msginfo);
1701 if (!is_file_exist(msgfile))
1702 g_warning("%s: file not exist\n", msgfile);
1704 compose_attach_append(compose, msgfile, msgfile,
1709 const gchar *qmark = NULL;
1710 const gchar *body_fmt = NULL;
1711 MsgInfo *full_msginfo;
1713 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1714 body_fmt = gettext(prefs_common.fw_quotefmt);
1718 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1720 full_msginfo = procmsg_msginfo_copy(msginfo);
1722 /* use the forward format of folder (if enabled), or the account's one
1723 (if enabled) or fallback to the global forward format, which is always
1724 enabled (even if empty), and use the relevant quotemark */
1725 if (msginfo->folder && msginfo->folder->prefs &&
1726 msginfo->folder->prefs->forward_with_format) {
1727 qmark = msginfo->folder->prefs->forward_quotemark;
1728 body_fmt = msginfo->folder->prefs->forward_body_format;
1730 } else if (account->forward_with_format) {
1731 qmark = account->forward_quotemark;
1732 body_fmt = account->forward_body_format;
1735 qmark = prefs_common.fw_quotemark;
1736 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1737 body_fmt = gettext(prefs_common.fw_quotefmt);
1742 /* empty quotemark is not allowed */
1743 if (qmark == NULL || *qmark == '\0')
1746 compose_quote_fmt(compose, full_msginfo,
1747 body_fmt, qmark, body, FALSE, TRUE,
1748 _("The body of the \"Forward\" template has an error at line %d."));
1749 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1750 quote_fmt_reset_vartable();
1751 compose_attach_parts(compose, msginfo);
1753 procmsg_msginfo_free(full_msginfo);
1756 SIGNAL_BLOCK(textbuf);
1758 if (account->auto_sig)
1759 compose_insert_sig(compose, FALSE);
1761 compose_wrap_all(compose);
1763 SIGNAL_UNBLOCK(textbuf);
1765 gtk_text_buffer_get_start_iter(textbuf, &iter);
1766 gtk_text_buffer_place_cursor(textbuf, &iter);
1768 gtk_widget_grab_focus(compose->header_last->entry);
1770 if (!no_extedit && prefs_common.auto_exteditor)
1771 compose_exec_ext_editor(compose);
1774 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1775 gchar *folderidentifier;
1777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1778 folderidentifier = folder_item_get_identifier(msginfo->folder);
1779 compose_set_save_to(compose, folderidentifier);
1780 g_free(folderidentifier);
1783 undo_unblock(compose->undostruct);
1785 compose->modified = FALSE;
1786 compose_set_title(compose);
1788 compose->updating = FALSE;
1789 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1790 SCROLL_TO_CURSOR(compose);
1792 if (compose->deferred_destroy) {
1793 compose_destroy(compose);
1800 #undef INSERT_FW_HEADER
1802 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1805 GtkTextView *textview;
1806 GtkTextBuffer *textbuf;
1810 gboolean single_mail = TRUE;
1812 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1814 if (g_slist_length(msginfo_list) > 1)
1815 single_mail = FALSE;
1817 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1818 if (((MsgInfo *)msginfo->data)->folder == NULL)
1821 /* guess account from first selected message */
1823 !(account = compose_guess_forward_account_from_msginfo
1824 (msginfo_list->data)))
1825 account = cur_account;
1827 cm_return_val_if_fail(account != NULL, NULL);
1829 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1830 if (msginfo->data) {
1831 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1832 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1836 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1837 g_warning("no msginfo_list");
1841 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1843 compose->updating = TRUE;
1845 /* override from name according to folder properties */
1846 if (msginfo_list->data) {
1847 MsgInfo *msginfo = msginfo_list->data;
1849 if (msginfo->folder && msginfo->folder->prefs &&
1850 msginfo->folder->prefs->forward_with_format &&
1851 msginfo->folder->prefs->forward_override_from_format &&
1852 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1857 /* decode \-escape sequences in the internal representation of the quote format */
1858 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1859 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1862 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1863 compose->gtkaspell);
1865 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1867 quote_fmt_scan_string(tmp);
1870 buf = quote_fmt_get_buffer();
1872 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1874 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1875 quote_fmt_reset_vartable();
1881 textview = GTK_TEXT_VIEW(compose->text);
1882 textbuf = gtk_text_view_get_buffer(textview);
1883 compose_create_tags(textview, compose);
1885 undo_block(compose->undostruct);
1886 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1887 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1889 if (!is_file_exist(msgfile))
1890 g_warning("%s: file not exist\n", msgfile);
1892 compose_attach_append(compose, msgfile, msgfile,
1898 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1899 if (info->subject && *info->subject) {
1900 gchar *buf, *buf2, *p;
1902 buf = p = g_strdup(info->subject);
1903 p += subject_get_prefix_length(p);
1904 memmove(buf, p, strlen(p) + 1);
1906 buf2 = g_strdup_printf("Fw: %s", buf);
1907 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1913 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1914 _("Fw: multiple emails"));
1917 SIGNAL_BLOCK(textbuf);
1919 if (account->auto_sig)
1920 compose_insert_sig(compose, FALSE);
1922 compose_wrap_all(compose);
1924 SIGNAL_UNBLOCK(textbuf);
1926 gtk_text_buffer_get_start_iter(textbuf, &iter);
1927 gtk_text_buffer_place_cursor(textbuf, &iter);
1929 gtk_widget_grab_focus(compose->header_last->entry);
1930 undo_unblock(compose->undostruct);
1931 compose->modified = FALSE;
1932 compose_set_title(compose);
1934 compose->updating = FALSE;
1935 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1936 SCROLL_TO_CURSOR(compose);
1938 if (compose->deferred_destroy) {
1939 compose_destroy(compose);
1946 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1948 GtkTextIter start = *iter;
1949 GtkTextIter end_iter;
1950 int start_pos = gtk_text_iter_get_offset(&start);
1952 if (!compose->account->sig_sep)
1955 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1956 start_pos+strlen(compose->account->sig_sep));
1958 /* check sig separator */
1959 str = gtk_text_iter_get_text(&start, &end_iter);
1960 if (!strcmp(str, compose->account->sig_sep)) {
1962 /* check end of line (\n) */
1963 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1964 start_pos+strlen(compose->account->sig_sep));
1965 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1966 start_pos+strlen(compose->account->sig_sep)+1);
1967 tmp = gtk_text_iter_get_text(&start, &end_iter);
1968 if (!strcmp(tmp,"\n")) {
1980 static void compose_colorize_signature(Compose *compose)
1982 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1984 GtkTextIter end_iter;
1985 gtk_text_buffer_get_start_iter(buffer, &iter);
1986 while (gtk_text_iter_forward_line(&iter))
1987 if (compose_is_sig_separator(compose, buffer, &iter)) {
1988 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1989 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1993 #define BLOCK_WRAP() { \
1994 prev_autowrap = compose->autowrap; \
1995 buffer = gtk_text_view_get_buffer( \
1996 GTK_TEXT_VIEW(compose->text)); \
1997 compose->autowrap = FALSE; \
1999 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2000 G_CALLBACK(compose_changed_cb), \
2002 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2003 G_CALLBACK(text_inserted), \
2006 #define UNBLOCK_WRAP() { \
2007 compose->autowrap = prev_autowrap; \
2008 if (compose->autowrap) { \
2009 gint old = compose->draft_timeout_tag; \
2010 compose->draft_timeout_tag = -2; \
2011 compose_wrap_all(compose); \
2012 compose->draft_timeout_tag = old; \
2015 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2016 G_CALLBACK(compose_changed_cb), \
2018 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2019 G_CALLBACK(text_inserted), \
2023 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2025 Compose *compose = NULL;
2026 PrefsAccount *account = NULL;
2027 GtkTextView *textview;
2028 GtkTextBuffer *textbuf;
2032 gchar buf[BUFFSIZE];
2033 gboolean use_signing = FALSE;
2034 gboolean use_encryption = FALSE;
2035 gchar *privacy_system = NULL;
2036 int priority = PRIORITY_NORMAL;
2037 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2038 gboolean autowrap = prefs_common.autowrap;
2039 gboolean autoindent = prefs_common.auto_indent;
2041 cm_return_val_if_fail(msginfo != NULL, NULL);
2042 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2044 if (compose_put_existing_to_front(msginfo)) {
2048 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2049 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2050 gchar queueheader_buf[BUFFSIZE];
2053 /* Select Account from queue headers */
2054 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2055 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2056 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2057 account = account_find_from_id(id);
2059 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2060 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2061 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2062 account = account_find_from_id(id);
2064 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2065 sizeof(queueheader_buf), "NAID:")) {
2066 id = atoi(&queueheader_buf[strlen("NAID:")]);
2067 account = account_find_from_id(id);
2069 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2070 sizeof(queueheader_buf), "MAID:")) {
2071 id = atoi(&queueheader_buf[strlen("MAID:")]);
2072 account = account_find_from_id(id);
2074 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2075 sizeof(queueheader_buf), "S:")) {
2076 account = account_find_from_address(queueheader_buf, FALSE);
2078 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2079 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2080 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2081 use_signing = param;
2084 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2085 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2086 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2087 use_signing = param;
2090 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2091 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2092 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2093 use_encryption = param;
2095 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2096 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2097 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2098 use_encryption = param;
2100 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2101 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2102 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2105 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2106 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2107 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2110 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2111 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2112 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2114 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2115 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2116 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2118 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2119 sizeof(queueheader_buf), "X-Priority: ")) {
2120 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2123 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2124 sizeof(queueheader_buf), "RMID:")) {
2125 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2126 if (tokens[0] && tokens[1] && tokens[2]) {
2127 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2128 if (orig_item != NULL) {
2129 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2134 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2135 sizeof(queueheader_buf), "FMID:")) {
2136 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2137 if (tokens[0] && tokens[1] && tokens[2]) {
2138 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2139 if (orig_item != NULL) {
2140 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2146 account = msginfo->folder->folder->account;
2149 if (!account && prefs_common.reedit_account_autosel) {
2150 gchar from[BUFFSIZE];
2151 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2152 extract_address(from);
2153 account = account_find_from_address(from, FALSE);
2157 account = cur_account;
2159 cm_return_val_if_fail(account != NULL, NULL);
2161 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2163 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2164 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2165 compose->autowrap = autowrap;
2166 compose->replyinfo = replyinfo;
2167 compose->fwdinfo = fwdinfo;
2169 compose->updating = TRUE;
2170 compose->priority = priority;
2172 if (privacy_system != NULL) {
2173 compose->privacy_system = privacy_system;
2174 compose_use_signing(compose, use_signing);
2175 compose_use_encryption(compose, use_encryption);
2176 compose_update_privacy_system_menu_item(compose, FALSE);
2178 activate_privacy_system(compose, account, FALSE);
2181 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2183 compose_extract_original_charset(compose);
2185 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2186 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2187 gchar queueheader_buf[BUFFSIZE];
2189 /* Set message save folder */
2190 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2191 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2192 compose_set_save_to(compose, &queueheader_buf[4]);
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2195 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2197 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2202 if (compose_parse_header(compose, msginfo) < 0) {
2203 compose->updating = FALSE;
2204 compose_destroy(compose);
2207 compose_reedit_set_entry(compose, msginfo);
2209 textview = GTK_TEXT_VIEW(compose->text);
2210 textbuf = gtk_text_view_get_buffer(textview);
2211 compose_create_tags(textview, compose);
2213 mark = gtk_text_buffer_get_insert(textbuf);
2214 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2216 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2217 G_CALLBACK(compose_changed_cb),
2220 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2221 fp = procmime_get_first_encrypted_text_content(msginfo);
2223 compose_force_encryption(compose, account, TRUE, NULL);
2226 fp = procmime_get_first_text_content(msginfo);
2229 g_warning("Can't get text part\n");
2233 gboolean prev_autowrap = compose->autowrap;
2234 GtkTextBuffer *buffer = textbuf;
2236 while (fgets(buf, sizeof(buf), fp) != NULL) {
2238 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2244 compose_attach_parts(compose, msginfo);
2246 compose_colorize_signature(compose);
2248 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2249 G_CALLBACK(compose_changed_cb),
2252 gtk_widget_grab_focus(compose->text);
2254 if (prefs_common.auto_exteditor) {
2255 compose_exec_ext_editor(compose);
2257 compose->modified = FALSE;
2258 compose_set_title(compose);
2260 compose->updating = FALSE;
2261 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2262 SCROLL_TO_CURSOR(compose);
2264 if (compose->deferred_destroy) {
2265 compose_destroy(compose);
2269 compose->sig_str = account_get_signature_str(compose->account);
2274 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2281 cm_return_val_if_fail(msginfo != NULL, NULL);
2284 account = account_get_reply_account(msginfo,
2285 prefs_common.reply_account_autosel);
2286 cm_return_val_if_fail(account != NULL, NULL);
2288 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2290 compose->updating = TRUE;
2292 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2293 compose->replyinfo = NULL;
2294 compose->fwdinfo = NULL;
2296 compose_show_first_last_header(compose, TRUE);
2298 gtk_widget_grab_focus(compose->header_last->entry);
2300 filename = procmsg_get_message_file(msginfo);
2302 if (filename == NULL) {
2303 compose->updating = FALSE;
2304 compose_destroy(compose);
2309 compose->redirect_filename = filename;
2311 /* Set save folder */
2312 item = msginfo->folder;
2313 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2314 gchar *folderidentifier;
2316 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2317 folderidentifier = folder_item_get_identifier(item);
2318 compose_set_save_to(compose, folderidentifier);
2319 g_free(folderidentifier);
2322 compose_attach_parts(compose, msginfo);
2324 if (msginfo->subject)
2325 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2327 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2329 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2330 _("The body of the \"Redirect\" template has an error at line %d."));
2331 quote_fmt_reset_vartable();
2332 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2334 compose_colorize_signature(compose);
2337 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2338 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2339 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2341 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2342 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2344 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2345 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2346 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2347 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2348 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2350 if (compose->toolbar->draft_btn)
2351 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2352 if (compose->toolbar->insert_btn)
2353 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2354 if (compose->toolbar->attach_btn)
2355 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2356 if (compose->toolbar->sig_btn)
2357 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2358 if (compose->toolbar->exteditor_btn)
2359 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2360 if (compose->toolbar->linewrap_current_btn)
2361 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2362 if (compose->toolbar->linewrap_all_btn)
2363 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2365 compose->modified = FALSE;
2366 compose_set_title(compose);
2367 compose->updating = FALSE;
2368 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2369 SCROLL_TO_CURSOR(compose);
2371 if (compose->deferred_destroy) {
2372 compose_destroy(compose);
2379 GList *compose_get_compose_list(void)
2381 return compose_list;
2384 void compose_entry_append(Compose *compose, const gchar *address,
2385 ComposeEntryType type)
2387 const gchar *header;
2389 gboolean in_quote = FALSE;
2390 if (!address || *address == '\0') return;
2397 header = N_("Bcc:");
2399 case COMPOSE_REPLYTO:
2400 header = N_("Reply-To:");
2402 case COMPOSE_NEWSGROUPS:
2403 header = N_("Newsgroups:");
2405 case COMPOSE_FOLLOWUPTO:
2406 header = N_( "Followup-To:");
2413 header = prefs_common_translated_header_name(header);
2415 cur = begin = (gchar *)address;
2417 /* we separate the line by commas, but not if we're inside a quoted
2419 while (*cur != '\0') {
2421 in_quote = !in_quote;
2422 if (*cur == ',' && !in_quote) {
2423 gchar *tmp = g_strdup(begin);
2425 tmp[cur-begin]='\0';
2428 while (*tmp == ' ' || *tmp == '\t')
2430 compose_add_header_entry(compose, header, tmp);
2437 gchar *tmp = g_strdup(begin);
2439 tmp[cur-begin]='\0';
2442 while (*tmp == ' ' || *tmp == '\t')
2444 compose_add_header_entry(compose, header, tmp);
2449 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2451 static GdkColor yellow;
2452 static GdkColor black;
2453 static gboolean yellow_initialised = FALSE;
2457 if (!yellow_initialised) {
2458 gdk_color_parse("#f5f6be", &yellow);
2459 gdk_color_parse("#000000", &black);
2460 yellow_initialised = gdk_colormap_alloc_color(
2461 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2462 yellow_initialised &= gdk_colormap_alloc_color(
2463 gdk_colormap_get_system(), &black, FALSE, TRUE);
2466 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2467 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2468 if (gtk_entry_get_text(entry) &&
2469 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2470 if (yellow_initialised) {
2471 gtk_widget_modify_base(
2472 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2473 GTK_STATE_NORMAL, &yellow);
2474 gtk_widget_modify_text(
2475 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2476 GTK_STATE_NORMAL, &black);
2482 void compose_toolbar_cb(gint action, gpointer data)
2484 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2485 Compose *compose = (Compose*)toolbar_item->parent;
2487 cm_return_if_fail(compose != NULL);
2491 compose_send_cb(NULL, compose);
2494 compose_send_later_cb(NULL, compose);
2497 compose_draft(compose, COMPOSE_QUIT_EDITING);
2500 compose_insert_file_cb(NULL, compose);
2503 compose_attach_cb(NULL, compose);
2506 compose_insert_sig(compose, FALSE);
2509 compose_ext_editor_cb(NULL, compose);
2511 case A_LINEWRAP_CURRENT:
2512 compose_beautify_paragraph(compose, NULL, TRUE);
2514 case A_LINEWRAP_ALL:
2515 compose_wrap_all_full(compose, TRUE);
2518 compose_address_cb(NULL, compose);
2521 case A_CHECK_SPELLING:
2522 compose_check_all(NULL, compose);
2530 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2535 gchar *subject = NULL;
2539 gchar **attach = NULL;
2541 /* get mailto parts but skip from */
2542 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2545 compose_entry_append(compose, to, to_type);
2547 compose_entry_append(compose, cc, COMPOSE_CC);
2549 compose_entry_append(compose, bcc, COMPOSE_BCC);
2551 if (!g_utf8_validate (subject, -1, NULL)) {
2552 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2553 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2556 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2560 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2561 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2564 gboolean prev_autowrap = compose->autowrap;
2566 compose->autowrap = FALSE;
2568 mark = gtk_text_buffer_get_insert(buffer);
2569 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2571 if (!g_utf8_validate (body, -1, NULL)) {
2572 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2573 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2576 gtk_text_buffer_insert(buffer, &iter, body, -1);
2578 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2580 compose->autowrap = prev_autowrap;
2581 if (compose->autowrap)
2582 compose_wrap_all(compose);
2586 gint i = 0, att = 0;
2587 gchar *warn_files = NULL;
2588 while (attach[i] != NULL) {
2589 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2590 if (utf8_filename) {
2591 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2592 gchar *tmp = g_strdup_printf("%s%s\n",
2593 warn_files?warn_files:"",
2599 g_free(utf8_filename);
2601 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2606 alertpanel_notice(ngettext(
2607 "The following file has been attached: \n%s",
2608 "The following files have been attached: \n%s", att), warn_files);
2620 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2622 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2623 {"Cc:", NULL, TRUE},
2624 {"References:", NULL, FALSE},
2625 {"Bcc:", NULL, TRUE},
2626 {"Newsgroups:", NULL, TRUE},
2627 {"Followup-To:", NULL, TRUE},
2628 {"List-Post:", NULL, FALSE},
2629 {"X-Priority:", NULL, FALSE},
2630 {NULL, NULL, FALSE}};
2646 cm_return_val_if_fail(msginfo != NULL, -1);
2648 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2649 procheader_get_header_fields(fp, hentry);
2652 if (hentry[H_REPLY_TO].body != NULL) {
2653 if (hentry[H_REPLY_TO].body[0] != '\0') {
2655 conv_unmime_header(hentry[H_REPLY_TO].body,
2658 g_free(hentry[H_REPLY_TO].body);
2659 hentry[H_REPLY_TO].body = NULL;
2661 if (hentry[H_CC].body != NULL) {
2662 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2663 g_free(hentry[H_CC].body);
2664 hentry[H_CC].body = NULL;
2666 if (hentry[H_REFERENCES].body != NULL) {
2667 if (compose->mode == COMPOSE_REEDIT)
2668 compose->references = hentry[H_REFERENCES].body;
2670 compose->references = compose_parse_references
2671 (hentry[H_REFERENCES].body, msginfo->msgid);
2672 g_free(hentry[H_REFERENCES].body);
2674 hentry[H_REFERENCES].body = NULL;
2676 if (hentry[H_BCC].body != NULL) {
2677 if (compose->mode == COMPOSE_REEDIT)
2679 conv_unmime_header(hentry[H_BCC].body, NULL);
2680 g_free(hentry[H_BCC].body);
2681 hentry[H_BCC].body = NULL;
2683 if (hentry[H_NEWSGROUPS].body != NULL) {
2684 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2685 hentry[H_NEWSGROUPS].body = NULL;
2687 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2688 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2689 compose->followup_to =
2690 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2693 g_free(hentry[H_FOLLOWUP_TO].body);
2694 hentry[H_FOLLOWUP_TO].body = NULL;
2696 if (hentry[H_LIST_POST].body != NULL) {
2699 extract_address(hentry[H_LIST_POST].body);
2700 if (hentry[H_LIST_POST].body[0] != '\0') {
2701 scan_mailto_url(hentry[H_LIST_POST].body,
2702 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2704 g_free(compose->ml_post);
2705 compose->ml_post = to;
2708 g_free(hentry[H_LIST_POST].body);
2709 hentry[H_LIST_POST].body = NULL;
2712 /* CLAWS - X-Priority */
2713 if (compose->mode == COMPOSE_REEDIT)
2714 if (hentry[H_X_PRIORITY].body != NULL) {
2717 priority = atoi(hentry[H_X_PRIORITY].body);
2718 g_free(hentry[H_X_PRIORITY].body);
2720 hentry[H_X_PRIORITY].body = NULL;
2722 if (priority < PRIORITY_HIGHEST ||
2723 priority > PRIORITY_LOWEST)
2724 priority = PRIORITY_NORMAL;
2726 compose->priority = priority;
2729 if (compose->mode == COMPOSE_REEDIT) {
2730 if (msginfo->inreplyto && *msginfo->inreplyto)
2731 compose->inreplyto = g_strdup(msginfo->inreplyto);
2735 if (msginfo->msgid && *msginfo->msgid)
2736 compose->inreplyto = g_strdup(msginfo->msgid);
2738 if (!compose->references) {
2739 if (msginfo->msgid && *msginfo->msgid) {
2740 if (msginfo->inreplyto && *msginfo->inreplyto)
2741 compose->references =
2742 g_strdup_printf("<%s>\n\t<%s>",
2746 compose->references =
2747 g_strconcat("<", msginfo->msgid, ">",
2749 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2750 compose->references =
2751 g_strconcat("<", msginfo->inreplyto, ">",
2759 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2761 GSList *ref_id_list, *cur;
2765 ref_id_list = references_list_append(NULL, ref);
2766 if (!ref_id_list) return NULL;
2767 if (msgid && *msgid)
2768 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2773 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2774 /* "<" + Message-ID + ">" + CR+LF+TAB */
2775 len += strlen((gchar *)cur->data) + 5;
2777 if (len > MAX_REFERENCES_LEN) {
2778 /* remove second message-ID */
2779 if (ref_id_list && ref_id_list->next &&
2780 ref_id_list->next->next) {
2781 g_free(ref_id_list->next->data);
2782 ref_id_list = g_slist_remove
2783 (ref_id_list, ref_id_list->next->data);
2785 slist_free_strings(ref_id_list);
2786 g_slist_free(ref_id_list);
2793 new_ref = g_string_new("");
2794 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2795 if (new_ref->len > 0)
2796 g_string_append(new_ref, "\n\t");
2797 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2800 slist_free_strings(ref_id_list);
2801 g_slist_free(ref_id_list);
2803 new_ref_str = new_ref->str;
2804 g_string_free(new_ref, FALSE);
2809 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2810 const gchar *fmt, const gchar *qmark,
2811 const gchar *body, gboolean rewrap,
2812 gboolean need_unescape,
2813 const gchar *err_msg)
2815 MsgInfo* dummyinfo = NULL;
2816 gchar *quote_str = NULL;
2818 gboolean prev_autowrap;
2819 const gchar *trimmed_body = body;
2820 gint cursor_pos = -1;
2821 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2822 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2827 SIGNAL_BLOCK(buffer);
2830 dummyinfo = compose_msginfo_new_from_compose(compose);
2831 msginfo = dummyinfo;
2834 if (qmark != NULL) {
2836 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2837 compose->gtkaspell);
2839 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2841 quote_fmt_scan_string(qmark);
2844 buf = quote_fmt_get_buffer();
2846 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2848 Xstrdup_a(quote_str, buf, goto error)
2851 if (fmt && *fmt != '\0') {
2854 while (*trimmed_body == '\n')
2858 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2859 compose->gtkaspell);
2861 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2863 if (need_unescape) {
2866 /* decode \-escape sequences in the internal representation of the quote format */
2867 tmp = malloc(strlen(fmt)+1);
2868 pref_get_unescaped_pref(tmp, fmt);
2869 quote_fmt_scan_string(tmp);
2873 quote_fmt_scan_string(fmt);
2877 buf = quote_fmt_get_buffer();
2879 gint line = quote_fmt_get_line();
2880 alertpanel_error(err_msg, line);
2886 prev_autowrap = compose->autowrap;
2887 compose->autowrap = FALSE;
2889 mark = gtk_text_buffer_get_insert(buffer);
2890 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2891 if (g_utf8_validate(buf, -1, NULL)) {
2892 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2894 gchar *tmpout = NULL;
2895 tmpout = conv_codeset_strdup
2896 (buf, conv_get_locale_charset_str_no_utf8(),
2898 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2900 tmpout = g_malloc(strlen(buf)*2+1);
2901 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2903 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2907 cursor_pos = quote_fmt_get_cursor_pos();
2908 if (cursor_pos == -1)
2909 cursor_pos = gtk_text_iter_get_offset(&iter);
2910 compose->set_cursor_pos = cursor_pos;
2912 gtk_text_buffer_get_start_iter(buffer, &iter);
2913 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2914 gtk_text_buffer_place_cursor(buffer, &iter);
2916 compose->autowrap = prev_autowrap;
2917 if (compose->autowrap && rewrap)
2918 compose_wrap_all(compose);
2925 SIGNAL_UNBLOCK(buffer);
2927 procmsg_msginfo_free( dummyinfo );
2932 /* if ml_post is of type addr@host and from is of type
2933 * addr-anything@host, return TRUE
2935 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2937 gchar *left_ml = NULL;
2938 gchar *right_ml = NULL;
2939 gchar *left_from = NULL;
2940 gchar *right_from = NULL;
2941 gboolean result = FALSE;
2943 if (!ml_post || !from)
2946 left_ml = g_strdup(ml_post);
2947 if (strstr(left_ml, "@")) {
2948 right_ml = strstr(left_ml, "@")+1;
2949 *(strstr(left_ml, "@")) = '\0';
2952 left_from = g_strdup(from);
2953 if (strstr(left_from, "@")) {
2954 right_from = strstr(left_from, "@")+1;
2955 *(strstr(left_from, "@")) = '\0';
2958 if (left_ml && left_from && right_ml && right_from
2959 && !strncmp(left_from, left_ml, strlen(left_ml))
2960 && !strcmp(right_from, right_ml)) {
2969 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2971 gchar *my_addr1, *my_addr2;
2973 if (!addr1 || !addr2)
2976 Xstrdup_a(my_addr1, addr1, return FALSE);
2977 Xstrdup_a(my_addr2, addr2, return FALSE);
2979 extract_address(my_addr1);
2980 extract_address(my_addr2);
2982 return !strcasecmp(my_addr1, my_addr2);
2985 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2986 gboolean to_all, gboolean to_ml,
2988 gboolean followup_and_reply_to)
2990 GSList *cc_list = NULL;
2993 gchar *replyto = NULL;
2994 GHashTable *to_table;
2996 gboolean reply_to_ml = FALSE;
2997 gboolean default_reply_to = FALSE;
2999 cm_return_if_fail(compose->account != NULL);
3000 cm_return_if_fail(msginfo != NULL);
3002 reply_to_ml = to_ml && compose->ml_post;
3004 default_reply_to = msginfo->folder &&
3005 msginfo->folder->prefs->enable_default_reply_to;
3007 if (compose->account->protocol != A_NNTP) {
3008 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3009 if (msginfo->folder->prefs->enable_default_replyto) {
3010 compose_entry_append(compose, msginfo->folder->prefs->default_replyto, COMPOSE_REPLYTO);
3012 if (msginfo->folder->prefs->enable_default_bcc) {
3013 compose_entry_append(compose, msginfo->folder->prefs->default_bcc, COMPOSE_BCC);
3015 if (msginfo->folder->prefs->enable_default_cc) {
3016 compose_entry_append(compose, msginfo->folder->prefs->default_cc, COMPOSE_CC);
3019 if (reply_to_ml && !default_reply_to) {
3021 gboolean is_subscr = is_subscription(compose->ml_post,
3024 /* normal answer to ml post with a reply-to */
3025 compose_entry_append(compose,
3028 if (compose->replyto
3029 && !same_address(compose->ml_post, compose->replyto))
3030 compose_entry_append(compose,
3034 /* answer to subscription confirmation */
3035 if (compose->replyto)
3036 compose_entry_append(compose,
3039 else if (msginfo->from)
3040 compose_entry_append(compose,
3045 else if (!(to_all || to_sender) && default_reply_to) {
3046 compose_entry_append(compose,
3047 msginfo->folder->prefs->default_reply_to,
3049 compose_entry_mark_default_to(compose,
3050 msginfo->folder->prefs->default_reply_to);
3055 Xstrdup_a(tmp1, msginfo->from, return);
3056 extract_address(tmp1);
3057 if (to_all || to_sender ||
3058 !account_find_from_address(tmp1, FALSE))
3059 compose_entry_append(compose,
3060 (compose->replyto && !to_sender)
3061 ? compose->replyto :
3062 msginfo->from ? msginfo->from : "",
3064 else if (!to_all && !to_sender) {
3065 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3066 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3067 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3068 if (compose->replyto) {
3069 compose_entry_append(compose,
3073 compose_entry_append(compose,
3074 msginfo->from ? msginfo->from : "",
3078 /* replying to own mail, use original recp */
3079 compose_entry_append(compose,
3080 msginfo->to ? msginfo->to : "",
3082 compose_entry_append(compose,
3083 msginfo->cc ? msginfo->cc : "",
3089 if (to_sender || (compose->followup_to &&
3090 !strncmp(compose->followup_to, "poster", 6)))
3091 compose_entry_append
3093 (compose->replyto ? compose->replyto :
3094 msginfo->from ? msginfo->from : ""),
3097 else if (followup_and_reply_to || to_all) {
3098 compose_entry_append
3100 (compose->replyto ? compose->replyto :
3101 msginfo->from ? msginfo->from : ""),
3104 compose_entry_append
3106 compose->followup_to ? compose->followup_to :
3107 compose->newsgroups ? compose->newsgroups : "",
3108 COMPOSE_NEWSGROUPS);
3111 compose_entry_append
3113 compose->followup_to ? compose->followup_to :
3114 compose->newsgroups ? compose->newsgroups : "",
3115 COMPOSE_NEWSGROUPS);
3118 if (msginfo->subject && *msginfo->subject) {
3122 buf = p = g_strdup(msginfo->subject);
3123 p += subject_get_prefix_length(p);
3124 memmove(buf, p, strlen(p) + 1);
3126 buf2 = g_strdup_printf("Re: %s", buf);
3127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3132 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3134 if (to_ml && compose->ml_post) return;
3135 if (!to_all || compose->account->protocol == A_NNTP) return;
3137 if (compose->replyto) {
3138 Xstrdup_a(replyto, compose->replyto, return);
3139 extract_address(replyto);
3141 if (msginfo->from) {
3142 Xstrdup_a(from, msginfo->from, return);
3143 extract_address(from);
3146 if (replyto && from)
3147 cc_list = address_list_append_with_comments(cc_list, from);
3148 if (to_all && msginfo->folder &&
3149 msginfo->folder->prefs->enable_default_reply_to)
3150 cc_list = address_list_append_with_comments(cc_list,
3151 msginfo->folder->prefs->default_reply_to);
3152 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3153 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3155 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3157 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3158 if (compose->account) {
3159 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3160 GINT_TO_POINTER(1));
3162 /* remove address on To: and that of current account */
3163 for (cur = cc_list; cur != NULL; ) {
3164 GSList *next = cur->next;
3167 addr = g_utf8_strdown(cur->data, -1);
3168 extract_address(addr);
3170 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3171 cc_list = g_slist_remove(cc_list, cur->data);
3173 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3177 hash_free_strings(to_table);
3178 g_hash_table_destroy(to_table);
3181 for (cur = cc_list; cur != NULL; cur = cur->next)
3182 compose_entry_append(compose, (gchar *)cur->data,
3184 slist_free_strings(cc_list);
3185 g_slist_free(cc_list);
3190 #define SET_ENTRY(entry, str) \
3193 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3196 #define SET_ADDRESS(type, str) \
3199 compose_entry_append(compose, str, type); \
3202 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3204 cm_return_if_fail(msginfo != NULL);
3206 SET_ENTRY(subject_entry, msginfo->subject);
3207 SET_ENTRY(from_name, msginfo->from);
3208 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3209 SET_ADDRESS(COMPOSE_CC, compose->cc);
3210 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3211 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3212 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3213 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3215 compose_update_priority_menu_item(compose);
3216 compose_update_privacy_system_menu_item(compose, FALSE);
3217 compose_show_first_last_header(compose, TRUE);
3223 static void compose_insert_sig(Compose *compose, gboolean replace)
3225 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3226 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3228 GtkTextIter iter, iter_end;
3229 gint cur_pos, ins_pos;
3230 gboolean prev_autowrap;
3231 gboolean found = FALSE;
3232 gboolean exists = FALSE;
3234 cm_return_if_fail(compose->account != NULL);
3238 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3239 G_CALLBACK(compose_changed_cb),
3242 mark = gtk_text_buffer_get_insert(buffer);
3243 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3244 cur_pos = gtk_text_iter_get_offset (&iter);
3247 gtk_text_buffer_get_end_iter(buffer, &iter);
3249 exists = (compose->sig_str != NULL);
3252 GtkTextIter first_iter, start_iter, end_iter;
3254 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3256 if (!exists || compose->sig_str[0] == '\0')
3259 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3260 compose->signature_tag);
3263 /* include previous \n\n */
3264 gtk_text_iter_backward_chars(&first_iter, 1);
3265 start_iter = first_iter;
3266 end_iter = first_iter;
3268 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3269 compose->signature_tag);
3270 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3271 compose->signature_tag);
3273 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3279 g_free(compose->sig_str);
3280 compose->sig_str = account_get_signature_str(compose->account);
3282 cur_pos = gtk_text_iter_get_offset(&iter);
3284 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3285 g_free(compose->sig_str);
3286 compose->sig_str = NULL;
3288 if (compose->sig_inserted == FALSE)
3289 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3290 compose->sig_inserted = TRUE;
3292 cur_pos = gtk_text_iter_get_offset(&iter);
3293 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3295 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3296 gtk_text_iter_forward_chars(&iter, 1);
3297 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3298 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3300 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3301 cur_pos = gtk_text_buffer_get_char_count (buffer);
3304 /* put the cursor where it should be
3305 * either where the quote_fmt says, either where it was */
3306 if (compose->set_cursor_pos < 0)
3307 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3309 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3310 compose->set_cursor_pos);
3312 compose->set_cursor_pos = -1;
3313 gtk_text_buffer_place_cursor(buffer, &iter);
3314 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3315 G_CALLBACK(compose_changed_cb),
3321 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3324 GtkTextBuffer *buffer;
3327 const gchar *cur_encoding;
3328 gchar buf[BUFFSIZE];
3331 gboolean prev_autowrap;
3332 gboolean badtxt = FALSE;
3333 struct stat file_stat;
3336 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3338 /* get the size of the file we are about to insert */
3339 ret = g_stat(file, &file_stat);
3341 gchar *shortfile = g_path_get_basename(file);
3342 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3344 return COMPOSE_INSERT_NO_FILE;
3345 } else if (prefs_common.warn_large_insert == TRUE) {
3347 /* ask user for confirmation if the file is large */
3348 if (prefs_common.warn_large_insert_size < 0 ||
3349 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3353 msg = g_strdup_printf(_("You are about to insert a file of %s "
3354 "in the message body. Are you sure you want to do that?"),
3355 to_human_readable(file_stat.st_size));
3356 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3357 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3360 /* do we ask for confirmation next time? */
3361 if (aval & G_ALERTDISABLE) {
3362 /* no confirmation next time, disable feature in preferences */
3363 aval &= ~G_ALERTDISABLE;
3364 prefs_common.warn_large_insert = FALSE;
3367 /* abort file insertion if user canceled action */
3368 if (aval != G_ALERTALTERNATE) {
3369 return COMPOSE_INSERT_NO_FILE;
3375 if ((fp = g_fopen(file, "rb")) == NULL) {
3376 FILE_OP_ERROR(file, "fopen");
3377 return COMPOSE_INSERT_READ_ERROR;
3380 prev_autowrap = compose->autowrap;
3381 compose->autowrap = FALSE;
3383 text = GTK_TEXT_VIEW(compose->text);
3384 buffer = gtk_text_view_get_buffer(text);
3385 mark = gtk_text_buffer_get_insert(buffer);
3386 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3388 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3389 G_CALLBACK(text_inserted),
3392 cur_encoding = conv_get_locale_charset_str_no_utf8();
3394 while (fgets(buf, sizeof(buf), fp) != NULL) {
3397 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3398 str = g_strdup(buf);
3400 str = conv_codeset_strdup
3401 (buf, cur_encoding, CS_INTERNAL);
3404 /* strip <CR> if DOS/Windows file,
3405 replace <CR> with <LF> if Macintosh file. */
3408 if (len > 0 && str[len - 1] != '\n') {
3410 if (str[len] == '\r') str[len] = '\n';
3413 gtk_text_buffer_insert(buffer, &iter, str, -1);
3417 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3418 G_CALLBACK(text_inserted),
3420 compose->autowrap = prev_autowrap;
3421 if (compose->autowrap)
3422 compose_wrap_all(compose);
3427 return COMPOSE_INSERT_INVALID_CHARACTER;
3429 return COMPOSE_INSERT_SUCCESS;
3432 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3433 const gchar *filename,
3434 const gchar *content_type)
3442 GtkListStore *store;
3444 gboolean has_binary = FALSE;
3446 if (!is_file_exist(file)) {
3447 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3448 gboolean result = FALSE;
3449 if (file_from_uri && is_file_exist(file_from_uri)) {
3450 result = compose_attach_append(
3451 compose, file_from_uri,
3455 g_free(file_from_uri);
3458 alertpanel_error("File %s doesn't exist\n", filename);
3461 if ((size = get_file_size(file)) < 0) {
3462 alertpanel_error("Can't get file size of %s\n", filename);
3466 alertpanel_error(_("File %s is empty."), filename);
3469 if ((fp = g_fopen(file, "rb")) == NULL) {
3470 alertpanel_error(_("Can't read %s."), filename);
3475 ainfo = g_new0(AttachInfo, 1);
3476 auto_ainfo = g_auto_pointer_new_with_free
3477 (ainfo, (GFreeFunc) compose_attach_info_free);
3478 ainfo->file = g_strdup(file);
3481 ainfo->content_type = g_strdup(content_type);
3482 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3484 MsgFlags flags = {0, 0};
3486 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3487 ainfo->encoding = ENC_7BIT;
3489 ainfo->encoding = ENC_8BIT;
3491 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3492 if (msginfo && msginfo->subject)
3493 name = g_strdup(msginfo->subject);
3495 name = g_path_get_basename(filename ? filename : file);
3497 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3499 procmsg_msginfo_free(msginfo);
3501 if (!g_ascii_strncasecmp(content_type, "text", 4))
3502 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3504 ainfo->encoding = ENC_BASE64;
3505 name = g_path_get_basename(filename ? filename : file);
3506 ainfo->name = g_strdup(name);
3510 ainfo->content_type = procmime_get_mime_type(file);
3511 if (!ainfo->content_type) {
3512 ainfo->content_type =
3513 g_strdup("application/octet-stream");
3514 ainfo->encoding = ENC_BASE64;
3515 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3517 procmime_get_encoding_for_text_file(file, &has_binary);
3519 ainfo->encoding = ENC_BASE64;
3520 name = g_path_get_basename(filename ? filename : file);
3521 ainfo->name = g_strdup(name);
3525 if (ainfo->name != NULL
3526 && !strcmp(ainfo->name, ".")) {
3527 g_free(ainfo->name);
3531 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3532 g_free(ainfo->content_type);
3533 ainfo->content_type = g_strdup("application/octet-stream");
3536 ainfo->size = (goffset)size;
3537 size_text = to_human_readable((goffset)size);
3539 store = GTK_LIST_STORE(gtk_tree_view_get_model
3540 (GTK_TREE_VIEW(compose->attach_clist)));
3542 gtk_list_store_append(store, &iter);
3543 gtk_list_store_set(store, &iter,
3544 COL_MIMETYPE, ainfo->content_type,
3545 COL_SIZE, size_text,
3546 COL_NAME, ainfo->name,
3548 COL_AUTODATA, auto_ainfo,
3551 g_auto_pointer_free(auto_ainfo);
3552 compose_attach_update_label(compose);
3556 static void compose_use_signing(Compose *compose, gboolean use_signing)
3558 compose->use_signing = use_signing;
3559 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3562 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3564 compose->use_encryption = use_encryption;
3565 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3568 #define NEXT_PART_NOT_CHILD(info) \
3570 node = info->node; \
3571 while (node->children) \
3572 node = g_node_last_child(node); \
3573 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3576 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3580 MimeInfo *firsttext = NULL;
3581 MimeInfo *encrypted = NULL;
3584 const gchar *partname = NULL;
3586 mimeinfo = procmime_scan_message(msginfo);
3587 if (!mimeinfo) return;
3589 if (mimeinfo->node->children == NULL) {
3590 procmime_mimeinfo_free_all(mimeinfo);
3594 /* find first content part */
3595 child = (MimeInfo *) mimeinfo->node->children->data;
3596 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3597 child = (MimeInfo *)child->node->children->data;
3600 if (child->type == MIMETYPE_TEXT) {
3602 debug_print("First text part found\n");
3603 } else if (compose->mode == COMPOSE_REEDIT &&
3604 child->type == MIMETYPE_APPLICATION &&
3605 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3606 encrypted = (MimeInfo *)child->node->parent->data;
3609 child = (MimeInfo *) mimeinfo->node->children->data;
3610 while (child != NULL) {
3613 if (child == encrypted) {
3614 /* skip this part of tree */
3615 NEXT_PART_NOT_CHILD(child);
3619 if (child->type == MIMETYPE_MULTIPART) {
3620 /* get the actual content */
3621 child = procmime_mimeinfo_next(child);
3625 if (child == firsttext) {
3626 child = procmime_mimeinfo_next(child);
3630 outfile = procmime_get_tmp_file_name(child);
3631 if ((err = procmime_get_part(outfile, child)) < 0)
3632 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3634 gchar *content_type;
3636 content_type = procmime_get_content_type_str(child->type, child->subtype);
3638 /* if we meet a pgp signature, we don't attach it, but
3639 * we force signing. */
3640 if ((strcmp(content_type, "application/pgp-signature") &&
3641 strcmp(content_type, "application/pkcs7-signature") &&
3642 strcmp(content_type, "application/x-pkcs7-signature"))
3643 || compose->mode == COMPOSE_REDIRECT) {
3644 partname = procmime_mimeinfo_get_parameter(child, "filename");
3645 if (partname == NULL)
3646 partname = procmime_mimeinfo_get_parameter(child, "name");
3647 if (partname == NULL)
3649 compose_attach_append(compose, outfile,
3650 partname, content_type);
3652 compose_force_signing(compose, compose->account, NULL);
3654 g_free(content_type);
3657 NEXT_PART_NOT_CHILD(child);
3659 procmime_mimeinfo_free_all(mimeinfo);
3662 #undef NEXT_PART_NOT_CHILD
3667 WAIT_FOR_INDENT_CHAR,
3668 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3671 /* return indent length, we allow:
3672 indent characters followed by indent characters or spaces/tabs,
3673 alphabets and numbers immediately followed by indent characters,
3674 and the repeating sequences of the above
3675 If quote ends with multiple spaces, only the first one is included. */
3676 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3677 const GtkTextIter *start, gint *len)
3679 GtkTextIter iter = *start;
3683 IndentState state = WAIT_FOR_INDENT_CHAR;
3686 gint alnum_count = 0;
3687 gint space_count = 0;
3690 if (prefs_common.quote_chars == NULL) {
3694 while (!gtk_text_iter_ends_line(&iter)) {
3695 wc = gtk_text_iter_get_char(&iter);
3696 if (g_unichar_iswide(wc))
3698 clen = g_unichar_to_utf8(wc, ch);
3702 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3703 is_space = g_unichar_isspace(wc);
3705 if (state == WAIT_FOR_INDENT_CHAR) {
3706 if (!is_indent && !g_unichar_isalnum(wc))
3709 quote_len += alnum_count + space_count + 1;
3710 alnum_count = space_count = 0;
3711 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3714 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3715 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3719 else if (is_indent) {
3720 quote_len += alnum_count + space_count + 1;
3721 alnum_count = space_count = 0;
3724 state = WAIT_FOR_INDENT_CHAR;
3728 gtk_text_iter_forward_char(&iter);
3731 if (quote_len > 0 && space_count > 0)
3737 if (quote_len > 0) {
3739 gtk_text_iter_forward_chars(&iter, quote_len);
3740 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3746 /* return >0 if the line is itemized */
3747 static int compose_itemized_length(GtkTextBuffer *buffer,
3748 const GtkTextIter *start)
3750 GtkTextIter iter = *start;
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))
3768 clen = g_unichar_to_utf8(wc, ch);
3772 if (!strchr("*-+", ch[0]))
3775 gtk_text_iter_forward_char(&iter);
3776 if (gtk_text_iter_ends_line(&iter))
3778 wc = gtk_text_iter_get_char(&iter);
3779 if (g_unichar_isspace(wc)) {
3785 /* return the string at the start of the itemization */
3786 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3787 const GtkTextIter *start)
3789 GtkTextIter iter = *start;
3792 GString *item_chars = g_string_new("");
3795 if (gtk_text_iter_ends_line(&iter))
3800 wc = gtk_text_iter_get_char(&iter);
3801 if (!g_unichar_isspace(wc))
3803 gtk_text_iter_forward_char(&iter);
3804 if (gtk_text_iter_ends_line(&iter))
3806 g_string_append_unichar(item_chars, wc);
3809 str = item_chars->str;
3810 g_string_free(item_chars, FALSE);
3814 /* return the number of spaces at a line's start */
3815 static int compose_left_offset_length(GtkTextBuffer *buffer,
3816 const GtkTextIter *start)
3818 GtkTextIter iter = *start;
3821 if (gtk_text_iter_ends_line(&iter))
3825 wc = gtk_text_iter_get_char(&iter);
3826 if (!g_unichar_isspace(wc))
3829 gtk_text_iter_forward_char(&iter);
3830 if (gtk_text_iter_ends_line(&iter))
3834 gtk_text_iter_forward_char(&iter);
3835 if (gtk_text_iter_ends_line(&iter))
3840 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3841 const GtkTextIter *start,
3842 GtkTextIter *break_pos,
3846 GtkTextIter iter = *start, line_end = *start;
3847 PangoLogAttr *attrs;
3854 gboolean can_break = FALSE;
3855 gboolean do_break = FALSE;
3856 gboolean was_white = FALSE;
3857 gboolean prev_dont_break = FALSE;
3859 gtk_text_iter_forward_to_line_end(&line_end);
3860 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3861 len = g_utf8_strlen(str, -1);
3865 g_warning("compose_get_line_break_pos: len = 0!\n");
3869 /* g_print("breaking line: %d: %s (len = %d)\n",
3870 gtk_text_iter_get_line(&iter), str, len); */
3872 attrs = g_new(PangoLogAttr, len + 1);
3874 pango_default_break(str, -1, NULL, attrs, len + 1);
3878 /* skip quote and leading spaces */
3879 for (i = 0; *p != '\0' && i < len; i++) {
3882 wc = g_utf8_get_char(p);
3883 if (i >= quote_len && !g_unichar_isspace(wc))
3885 if (g_unichar_iswide(wc))
3887 else if (*p == '\t')
3891 p = g_utf8_next_char(p);
3894 for (; *p != '\0' && i < len; i++) {
3895 PangoLogAttr *attr = attrs + i;
3899 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3902 was_white = attr->is_white;
3904 /* don't wrap URI */
3905 if ((uri_len = get_uri_len(p)) > 0) {
3907 if (pos > 0 && col > max_col) {
3917 wc = g_utf8_get_char(p);
3918 if (g_unichar_iswide(wc)) {
3920 if (prev_dont_break && can_break && attr->is_line_break)
3922 } else if (*p == '\t')
3926 if (pos > 0 && col > max_col) {
3931 if (*p == '-' || *p == '/')
3932 prev_dont_break = TRUE;
3934 prev_dont_break = FALSE;
3936 p = g_utf8_next_char(p);
3940 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3945 *break_pos = *start;
3946 gtk_text_iter_set_line_offset(break_pos, pos);
3951 static gboolean compose_join_next_line(Compose *compose,
3952 GtkTextBuffer *buffer,
3954 const gchar *quote_str)
3956 GtkTextIter iter_ = *iter, cur, prev, next, end;
3957 PangoLogAttr attrs[3];
3959 gchar *next_quote_str;
3962 gboolean keep_cursor = FALSE;
3964 if (!gtk_text_iter_forward_line(&iter_) ||
3965 gtk_text_iter_ends_line(&iter_)) {
3968 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3970 if ((quote_str || next_quote_str) &&
3971 strcmp2(quote_str, next_quote_str) != 0) {
3972 g_free(next_quote_str);
3975 g_free(next_quote_str);
3978 if (quote_len > 0) {
3979 gtk_text_iter_forward_chars(&end, quote_len);
3980 if (gtk_text_iter_ends_line(&end)) {
3985 /* don't join itemized lines */
3986 if (compose_itemized_length(buffer, &end) > 0) {
3990 /* don't join signature separator */
3991 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3994 /* delete quote str */
3996 gtk_text_buffer_delete(buffer, &iter_, &end);
3998 /* don't join line breaks put by the user */
4000 gtk_text_iter_backward_char(&cur);
4001 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4002 gtk_text_iter_forward_char(&cur);
4006 gtk_text_iter_forward_char(&cur);
4007 /* delete linebreak and extra spaces */
4008 while (gtk_text_iter_backward_char(&cur)) {
4009 wc1 = gtk_text_iter_get_char(&cur);
4010 if (!g_unichar_isspace(wc1))
4015 while (!gtk_text_iter_ends_line(&cur)) {
4016 wc1 = gtk_text_iter_get_char(&cur);
4017 if (!g_unichar_isspace(wc1))
4019 gtk_text_iter_forward_char(&cur);
4022 if (!gtk_text_iter_equal(&prev, &next)) {
4025 mark = gtk_text_buffer_get_insert(buffer);
4026 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4027 if (gtk_text_iter_equal(&prev, &cur))
4029 gtk_text_buffer_delete(buffer, &prev, &next);
4033 /* insert space if required */
4034 gtk_text_iter_backward_char(&prev);
4035 wc1 = gtk_text_iter_get_char(&prev);
4036 wc2 = gtk_text_iter_get_char(&next);
4037 gtk_text_iter_forward_char(&next);
4038 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4039 pango_default_break(str, -1, NULL, attrs, 3);
4040 if (!attrs[1].is_line_break ||
4041 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4042 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4044 gtk_text_iter_backward_char(&iter_);
4045 gtk_text_buffer_place_cursor(buffer, &iter_);
4054 #define ADD_TXT_POS(bp_, ep_, pti_) \
4055 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4056 last = last->next; \
4057 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4058 last->next = NULL; \
4060 g_warning("alloc error scanning URIs\n"); \
4063 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4065 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4066 GtkTextBuffer *buffer;
4067 GtkTextIter iter, break_pos, end_of_line;
4068 gchar *quote_str = NULL;
4070 gboolean wrap_quote = prefs_common.linewrap_quote;
4071 gboolean prev_autowrap = compose->autowrap;
4072 gint startq_offset = -1, noq_offset = -1;
4073 gint uri_start = -1, uri_stop = -1;
4074 gint nouri_start = -1, nouri_stop = -1;
4075 gint num_blocks = 0;
4076 gint quotelevel = -1;
4077 gboolean modified = force;
4078 gboolean removed = FALSE;
4079 gboolean modified_before_remove = FALSE;
4081 gboolean start = TRUE;
4082 gint itemized_len = 0, rem_item_len = 0;
4083 gchar *itemized_chars = NULL;
4084 gboolean item_continuation = FALSE;
4089 if (compose->draft_timeout_tag == -2) {
4093 compose->autowrap = FALSE;
4095 buffer = gtk_text_view_get_buffer(text);
4096 undo_wrapping(compose->undostruct, TRUE);
4101 mark = gtk_text_buffer_get_insert(buffer);
4102 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4106 if (compose->draft_timeout_tag == -2) {
4107 if (gtk_text_iter_ends_line(&iter)) {
4108 while (gtk_text_iter_ends_line(&iter) &&
4109 gtk_text_iter_forward_line(&iter))
4112 while (gtk_text_iter_backward_line(&iter)) {
4113 if (gtk_text_iter_ends_line(&iter)) {
4114 gtk_text_iter_forward_line(&iter);
4120 /* move to line start */
4121 gtk_text_iter_set_line_offset(&iter, 0);
4124 itemized_len = compose_itemized_length(buffer, &iter);
4126 if (!itemized_len) {
4127 itemized_len = compose_left_offset_length(buffer, &iter);
4128 item_continuation = TRUE;
4132 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4134 /* go until paragraph end (empty line) */
4135 while (start || !gtk_text_iter_ends_line(&iter)) {
4136 gchar *scanpos = NULL;
4137 /* parse table - in order of priority */
4139 const gchar *needle; /* token */
4141 /* token search function */
4142 gchar *(*search) (const gchar *haystack,
4143 const gchar *needle);
4144 /* part parsing function */
4145 gboolean (*parse) (const gchar *start,
4146 const gchar *scanpos,
4150 /* part to URI function */
4151 gchar *(*build_uri) (const gchar *bp,
4155 static struct table parser[] = {
4156 {"http://", strcasestr, get_uri_part, make_uri_string},
4157 {"https://", strcasestr, get_uri_part, make_uri_string},
4158 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4159 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4160 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4161 {"www.", strcasestr, get_uri_part, make_http_string},
4162 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4163 {"@", strcasestr, get_email_part, make_email_string}
4165 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4166 gint last_index = PARSE_ELEMS;
4168 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4172 if (!prev_autowrap && num_blocks == 0) {
4174 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4175 G_CALLBACK(text_inserted),
4178 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4181 uri_start = uri_stop = -1;
4183 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4186 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4187 if (startq_offset == -1)
4188 startq_offset = gtk_text_iter_get_offset(&iter);
4189 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4190 if (quotelevel > 2) {
4191 /* recycle colors */
4192 if (prefs_common.recycle_quote_colors)
4201 if (startq_offset == -1)
4202 noq_offset = gtk_text_iter_get_offset(&iter);
4206 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4209 if (gtk_text_iter_ends_line(&iter)) {
4211 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4212 prefs_common.linewrap_len,
4214 GtkTextIter prev, next, cur;
4215 if (prev_autowrap != FALSE || force) {
4216 compose->automatic_break = TRUE;
4218 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4219 compose->automatic_break = FALSE;
4220 if (itemized_len && compose->autoindent) {
4221 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4222 if (!item_continuation)
4223 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4225 } else if (quote_str && wrap_quote) {
4226 compose->automatic_break = TRUE;
4228 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4229 compose->automatic_break = FALSE;
4230 if (itemized_len && compose->autoindent) {
4231 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4232 if (!item_continuation)
4233 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4237 /* remove trailing spaces */
4239 rem_item_len = itemized_len;
4240 while (compose->autoindent && rem_item_len-- > 0)
4241 gtk_text_iter_backward_char(&cur);
4242 gtk_text_iter_backward_char(&cur);
4245 while (!gtk_text_iter_starts_line(&cur)) {
4248 gtk_text_iter_backward_char(&cur);
4249 wc = gtk_text_iter_get_char(&cur);
4250 if (!g_unichar_isspace(wc))
4254 if (!gtk_text_iter_equal(&prev, &next)) {
4255 gtk_text_buffer_delete(buffer, &prev, &next);
4257 gtk_text_iter_forward_char(&break_pos);
4261 gtk_text_buffer_insert(buffer, &break_pos,
4265 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4267 /* move iter to current line start */
4268 gtk_text_iter_set_line_offset(&iter, 0);
4275 /* move iter to next line start */
4281 if (!prev_autowrap && num_blocks > 0) {
4283 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4284 G_CALLBACK(text_inserted),
4288 while (!gtk_text_iter_ends_line(&end_of_line)) {
4289 gtk_text_iter_forward_char(&end_of_line);
4291 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4293 nouri_start = gtk_text_iter_get_offset(&iter);
4294 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4296 walk_pos = gtk_text_iter_get_offset(&iter);
4297 /* FIXME: this looks phony. scanning for anything in the parse table */
4298 for (n = 0; n < PARSE_ELEMS; n++) {
4301 tmp = parser[n].search(walk, parser[n].needle);
4303 if (scanpos == NULL || tmp < scanpos) {
4312 /* check if URI can be parsed */
4313 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4314 (const gchar **)&ep, FALSE)
4315 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4319 strlen(parser[last_index].needle);
4322 uri_start = walk_pos + (bp - o_walk);
4323 uri_stop = walk_pos + (ep - o_walk);
4327 gtk_text_iter_forward_line(&iter);
4330 if (startq_offset != -1) {
4331 GtkTextIter startquote, endquote;
4332 gtk_text_buffer_get_iter_at_offset(
4333 buffer, &startquote, startq_offset);
4336 switch (quotelevel) {
4338 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4339 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4340 gtk_text_buffer_apply_tag_by_name(
4341 buffer, "quote0", &startquote, &endquote);
4342 gtk_text_buffer_remove_tag_by_name(
4343 buffer, "quote1", &startquote, &endquote);
4344 gtk_text_buffer_remove_tag_by_name(
4345 buffer, "quote2", &startquote, &endquote);
4350 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4351 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4352 gtk_text_buffer_apply_tag_by_name(
4353 buffer, "quote1", &startquote, &endquote);
4354 gtk_text_buffer_remove_tag_by_name(
4355 buffer, "quote0", &startquote, &endquote);
4356 gtk_text_buffer_remove_tag_by_name(
4357 buffer, "quote2", &startquote, &endquote);
4362 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4363 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4364 gtk_text_buffer_apply_tag_by_name(
4365 buffer, "quote2", &startquote, &endquote);
4366 gtk_text_buffer_remove_tag_by_name(
4367 buffer, "quote0", &startquote, &endquote);
4368 gtk_text_buffer_remove_tag_by_name(
4369 buffer, "quote1", &startquote, &endquote);
4375 } else if (noq_offset != -1) {
4376 GtkTextIter startnoquote, endnoquote;
4377 gtk_text_buffer_get_iter_at_offset(
4378 buffer, &startnoquote, noq_offset);
4381 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4382 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4383 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4384 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4385 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4386 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4387 gtk_text_buffer_remove_tag_by_name(
4388 buffer, "quote0", &startnoquote, &endnoquote);
4389 gtk_text_buffer_remove_tag_by_name(
4390 buffer, "quote1", &startnoquote, &endnoquote);
4391 gtk_text_buffer_remove_tag_by_name(
4392 buffer, "quote2", &startnoquote, &endnoquote);
4398 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4399 GtkTextIter nouri_start_iter, nouri_end_iter;
4400 gtk_text_buffer_get_iter_at_offset(
4401 buffer, &nouri_start_iter, nouri_start);
4402 gtk_text_buffer_get_iter_at_offset(
4403 buffer, &nouri_end_iter, nouri_stop);
4404 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4405 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4406 gtk_text_buffer_remove_tag_by_name(
4407 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4408 modified_before_remove = modified;
4413 if (uri_start >= 0 && uri_stop > 0) {
4414 GtkTextIter uri_start_iter, uri_end_iter, back;
4415 gtk_text_buffer_get_iter_at_offset(
4416 buffer, &uri_start_iter, uri_start);
4417 gtk_text_buffer_get_iter_at_offset(
4418 buffer, &uri_end_iter, uri_stop);
4419 back = uri_end_iter;
4420 gtk_text_iter_backward_char(&back);
4421 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4422 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4423 gtk_text_buffer_apply_tag_by_name(
4424 buffer, "link", &uri_start_iter, &uri_end_iter);
4426 if (removed && !modified_before_remove) {
4432 // debug_print("not modified, out after %d lines\n", lines);
4436 // debug_print("modified, out after %d lines\n", lines);
4438 g_free(itemized_chars);
4441 undo_wrapping(compose->undostruct, FALSE);
4442 compose->autowrap = prev_autowrap;
4447 void compose_action_cb(void *data)
4449 Compose *compose = (Compose *)data;
4450 compose_wrap_all(compose);
4453 static void compose_wrap_all(Compose *compose)
4455 compose_wrap_all_full(compose, FALSE);
4458 static void compose_wrap_all_full(Compose *compose, gboolean force)
4460 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4461 GtkTextBuffer *buffer;
4463 gboolean modified = TRUE;
4465 buffer = gtk_text_view_get_buffer(text);
4467 gtk_text_buffer_get_start_iter(buffer, &iter);
4468 while (!gtk_text_iter_is_end(&iter) && modified)
4469 modified = compose_beautify_paragraph(compose, &iter, force);
4473 static void compose_set_title(Compose *compose)
4479 edited = compose->modified ? _(" [Edited]") : "";
4481 subject = gtk_editable_get_chars(
4482 GTK_EDITABLE(compose->subject_entry), 0, -1);
4484 #ifndef GENERIC_UMPC
4485 if (subject && strlen(subject))
4486 str = g_strdup_printf(_("%s - Compose message%s"),
4489 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4491 str = g_strdup(_("Compose message"));
4494 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4500 * compose_current_mail_account:
4502 * Find a current mail account (the currently selected account, or the
4503 * default account, if a news account is currently selected). If a
4504 * mail account cannot be found, display an error message.
4506 * Return value: Mail account, or NULL if not found.
4508 static PrefsAccount *
4509 compose_current_mail_account(void)
4513 if (cur_account && cur_account->protocol != A_NNTP)
4516 ac = account_get_default();
4517 if (!ac || ac->protocol == A_NNTP) {
4518 alertpanel_error(_("Account for sending mail is not specified.\n"
4519 "Please select a mail account before sending."));
4526 #define QUOTE_IF_REQUIRED(out, str) \
4528 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4532 len = strlen(str) + 3; \
4533 if ((__tmp = alloca(len)) == NULL) { \
4534 g_warning("can't allocate memory\n"); \
4535 g_string_free(header, TRUE); \
4538 g_snprintf(__tmp, len, "\"%s\"", str); \
4543 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4544 g_warning("can't allocate memory\n"); \
4545 g_string_free(header, TRUE); \
4548 strcpy(__tmp, str); \
4554 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4556 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4560 len = strlen(str) + 3; \
4561 if ((__tmp = alloca(len)) == NULL) { \
4562 g_warning("can't allocate memory\n"); \
4565 g_snprintf(__tmp, len, "\"%s\"", str); \
4570 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4571 g_warning("can't allocate memory\n"); \
4574 strcpy(__tmp, str); \
4580 static void compose_select_account(Compose *compose, PrefsAccount *account,
4583 gchar *from = NULL, *header;
4584 ComposeHeaderEntry *header_entry;
4586 cm_return_if_fail(account != NULL);
4588 compose->account = account;
4590 if (account->name && *account->name) {
4592 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4593 from = g_strdup_printf("%s <%s>",
4594 buf, account->address);
4595 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4597 from = g_strdup_printf("<%s>",
4599 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4604 compose_set_title(compose);
4606 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4607 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4609 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4610 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4611 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4613 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4615 activate_privacy_system(compose, account, FALSE);
4617 if (!init && compose->mode != COMPOSE_REDIRECT) {
4618 undo_block(compose->undostruct);
4619 compose_insert_sig(compose, TRUE);
4620 undo_unblock(compose->undostruct);
4623 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4624 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4626 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4627 if (account->protocol == A_NNTP) {
4628 if (!strcmp(header, _("To:")))
4629 combobox_select_by_text(
4630 GTK_COMBO_BOX(header_entry->combo),
4633 if (!strcmp(header, _("Newsgroups:")))
4634 combobox_select_by_text(
4635 GTK_COMBO_BOX(header_entry->combo),
4643 /* use account's dict info if set */
4644 if (compose->gtkaspell) {
4645 if (account->enable_default_dictionary)
4646 gtkaspell_change_dict(compose->gtkaspell,
4647 account->default_dictionary, FALSE);
4648 if (account->enable_default_alt_dictionary)
4649 gtkaspell_change_alt_dict(compose->gtkaspell,
4650 account->default_alt_dictionary);
4651 if (account->enable_default_dictionary
4652 || account->enable_default_alt_dictionary)
4653 compose_spell_menu_changed(compose);
4658 gboolean compose_check_for_valid_recipient(Compose *compose) {
4659 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4660 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4661 gboolean recipient_found = FALSE;
4665 /* free to and newsgroup list */
4666 slist_free_strings(compose->to_list);
4667 g_slist_free(compose->to_list);
4668 compose->to_list = NULL;
4670 slist_free_strings(compose->newsgroup_list);
4671 g_slist_free(compose->newsgroup_list);
4672 compose->newsgroup_list = NULL;
4674 /* search header entries for to and newsgroup entries */
4675 for (list = compose->header_list; list; list = list->next) {
4678 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4679 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4682 if (entry[0] != '\0') {
4683 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4684 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4685 compose->to_list = address_list_append(compose->to_list, entry);
4686 recipient_found = TRUE;
4689 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4690 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4691 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4692 recipient_found = TRUE;
4699 return recipient_found;
4702 static gboolean compose_check_for_set_recipients(Compose *compose)
4704 if (compose->account->set_autocc && compose->account->auto_cc) {
4705 gboolean found_other = FALSE;
4707 /* search header entries for to and newsgroup entries */
4708 for (list = compose->header_list; list; list = list->next) {
4711 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4712 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4715 if (strcmp(entry, compose->account->auto_cc)
4716 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4726 if (compose->batch) {
4727 gtk_widget_show_all(compose->window);
4729 aval = alertpanel(_("Send"),
4730 _("The only recipient is the default CC address. Send anyway?"),
4731 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4732 if (aval != G_ALERTALTERNATE)
4736 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4737 gboolean found_other = FALSE;
4739 /* search header entries for to and newsgroup entries */
4740 for (list = compose->header_list; list; list = list->next) {
4743 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4744 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4747 if (strcmp(entry, compose->account->auto_bcc)
4748 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4758 if (compose->batch) {
4759 gtk_widget_show_all(compose->window);
4761 aval = alertpanel(_("Send"),
4762 _("The only recipient is the default BCC address. Send anyway?"),
4763 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4764 if (aval != G_ALERTALTERNATE)
4771 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4775 if (compose_check_for_valid_recipient(compose) == FALSE) {
4776 if (compose->batch) {
4777 gtk_widget_show_all(compose->window);
4779 alertpanel_error(_("Recipient is not specified."));
4783 if (compose_check_for_set_recipients(compose) == FALSE) {
4787 if (!compose->batch) {
4788 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4789 if (*str == '\0' && check_everything == TRUE &&
4790 compose->mode != COMPOSE_REDIRECT) {
4792 gchar *button_label;
4795 if (compose->sending)
4796 button_label = _("+_Send");
4798 button_label = _("+_Queue");
4799 message = g_strdup_printf(_("Subject is empty. %s"),
4800 compose->sending?_("Send it anyway?"):
4801 _("Queue it anyway?"));
4803 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4804 GTK_STOCK_CANCEL, button_label, NULL);
4806 if (aval != G_ALERTALTERNATE)
4811 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4817 gint compose_send(Compose *compose)
4820 FolderItem *folder = NULL;
4822 gchar *msgpath = NULL;
4823 gboolean discard_window = FALSE;
4824 gchar *errstr = NULL;
4825 gchar *tmsgid = NULL;
4826 MainWindow *mainwin = mainwindow_get_mainwindow();
4827 gboolean queued_removed = FALSE;
4829 if (prefs_common.send_dialog_invisible
4830 || compose->batch == TRUE)
4831 discard_window = TRUE;
4833 compose_allow_user_actions (compose, FALSE);
4834 compose->sending = TRUE;
4836 if (compose_check_entries(compose, TRUE) == FALSE) {
4837 if (compose->batch) {
4838 gtk_widget_show_all(compose->window);
4844 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4847 if (compose->batch) {
4848 gtk_widget_show_all(compose->window);
4851 alertpanel_error(_("Could not queue message for sending:\n\n"
4852 "Charset conversion failed."));
4853 } else if (val == -5) {
4854 alertpanel_error(_("Could not queue message for sending:\n\n"
4855 "Couldn't get recipient encryption key."));
4856 } else if (val == -6) {
4858 } else if (val == -3) {
4859 if (privacy_peek_error())
4860 alertpanel_error(_("Could not queue message for sending:\n\n"
4861 "Signature failed: %s"), privacy_get_error());
4862 } else if (val == -2 && errno != 0) {
4863 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4865 alertpanel_error(_("Could not queue message for sending."));
4870 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4871 if (discard_window) {
4872 compose->sending = FALSE;
4873 compose_close(compose);
4874 /* No more compose access in the normal codepath
4875 * after this point! */
4880 alertpanel_error(_("The message was queued but could not be "
4881 "sent.\nUse \"Send queued messages\" from "
4882 "the main window to retry."));
4883 if (!discard_window) {
4890 if (msgpath == NULL) {
4891 msgpath = folder_item_fetch_msg(folder, msgnum);
4892 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4895 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4896 claws_unlink(msgpath);
4899 if (!discard_window) {
4901 if (!queued_removed)
4902 folder_item_remove_msg(folder, msgnum);
4903 folder_item_scan(folder);
4905 /* make sure we delete that */
4906 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4908 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4909 folder_item_remove_msg(folder, tmp->msgnum);
4910 procmsg_msginfo_free(tmp);
4917 if (!queued_removed)
4918 folder_item_remove_msg(folder, msgnum);
4919 folder_item_scan(folder);
4921 /* make sure we delete that */
4922 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4924 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4925 folder_item_remove_msg(folder, tmp->msgnum);
4926 procmsg_msginfo_free(tmp);
4929 if (!discard_window) {
4930 compose->sending = FALSE;
4931 compose_allow_user_actions (compose, TRUE);
4932 compose_close(compose);
4936 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4937 "the main window to retry."), errstr);
4940 alertpanel_error_log(_("The message was queued but could not be "
4941 "sent.\nUse \"Send queued messages\" from "
4942 "the main window to retry."));
4944 if (!discard_window) {
4953 toolbar_main_set_sensitive(mainwin);
4954 main_window_set_menu_sensitive(mainwin);
4960 compose_allow_user_actions (compose, TRUE);
4961 compose->sending = FALSE;
4962 compose->modified = TRUE;
4963 toolbar_main_set_sensitive(mainwin);
4964 main_window_set_menu_sensitive(mainwin);
4969 static gboolean compose_use_attach(Compose *compose)
4971 GtkTreeModel *model = gtk_tree_view_get_model
4972 (GTK_TREE_VIEW(compose->attach_clist));
4973 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4976 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4979 gchar buf[BUFFSIZE];
4981 gboolean first_to_address;
4982 gboolean first_cc_address;
4984 ComposeHeaderEntry *headerentry;
4985 const gchar *headerentryname;
4986 const gchar *cc_hdr;
4987 const gchar *to_hdr;
4988 gboolean err = FALSE;
4990 debug_print("Writing redirect header\n");
4992 cc_hdr = prefs_common_translated_header_name("Cc:");
4993 to_hdr = prefs_common_translated_header_name("To:");
4995 first_to_address = TRUE;
4996 for (list = compose->header_list; list; list = list->next) {
4997 headerentry = ((ComposeHeaderEntry *)list->data);
4998 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5000 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5001 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5002 Xstrdup_a(str, entstr, return -1);
5004 if (str[0] != '\0') {
5005 compose_convert_header
5006 (compose, buf, sizeof(buf), str,
5007 strlen("Resent-To") + 2, TRUE);
5009 if (first_to_address) {
5010 err |= (fprintf(fp, "Resent-To: ") < 0);
5011 first_to_address = FALSE;
5013 err |= (fprintf(fp, ",") < 0);
5015 err |= (fprintf(fp, "%s", buf) < 0);
5019 if (!first_to_address) {
5020 err |= (fprintf(fp, "\n") < 0);
5023 first_cc_address = TRUE;
5024 for (list = compose->header_list; list; list = list->next) {
5025 headerentry = ((ComposeHeaderEntry *)list->data);
5026 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5028 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5029 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5030 Xstrdup_a(str, strg, return -1);
5032 if (str[0] != '\0') {
5033 compose_convert_header
5034 (compose, buf, sizeof(buf), str,
5035 strlen("Resent-Cc") + 2, TRUE);
5037 if (first_cc_address) {
5038 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5039 first_cc_address = FALSE;
5041 err |= (fprintf(fp, ",") < 0);
5043 err |= (fprintf(fp, "%s", buf) < 0);
5047 if (!first_cc_address) {
5048 err |= (fprintf(fp, "\n") < 0);
5051 return (err ? -1:0);
5054 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5056 gchar buf[BUFFSIZE];
5058 const gchar *entstr;
5059 /* struct utsname utsbuf; */
5060 gboolean err = FALSE;
5062 cm_return_val_if_fail(fp != NULL, -1);
5063 cm_return_val_if_fail(compose->account != NULL, -1);
5064 cm_return_val_if_fail(compose->account->address != NULL, -1);
5067 get_rfc822_date(buf, sizeof(buf));
5068 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5071 if (compose->account->name && *compose->account->name) {
5072 compose_convert_header
5073 (compose, buf, sizeof(buf), compose->account->name,
5074 strlen("From: "), TRUE);
5075 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5076 buf, compose->account->address) < 0);
5078 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5081 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5082 if (*entstr != '\0') {
5083 Xstrdup_a(str, entstr, return -1);
5086 compose_convert_header(compose, buf, sizeof(buf), str,
5087 strlen("Subject: "), FALSE);
5088 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5092 /* Resent-Message-ID */
5093 if (compose->account->set_domain && compose->account->domain) {
5094 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5095 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5096 g_snprintf(buf, sizeof(buf), "%s",
5097 strchr(compose->account->address, '@') ?
5098 strchr(compose->account->address, '@')+1 :
5099 compose->account->address);
5101 g_snprintf(buf, sizeof(buf), "%s", "");
5104 if (compose->account->gen_msgid) {
5106 if (compose->account->msgid_with_addr) {
5107 addr = compose->account->address;
5109 generate_msgid(buf, sizeof(buf), addr);
5110 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5111 compose->msgid = g_strdup(buf);
5113 compose->msgid = NULL;
5116 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5119 /* separator between header and body */
5120 err |= (fputs("\n", fp) == EOF);
5122 return (err ? -1:0);
5125 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5129 gchar buf[BUFFSIZE];
5131 gboolean skip = FALSE;
5132 gboolean err = FALSE;
5133 gchar *not_included[]={
5134 "Return-Path:", "Delivered-To:", "Received:",
5135 "Subject:", "X-UIDL:", "AF:",
5136 "NF:", "PS:", "SRH:",
5137 "SFN:", "DSR:", "MID:",
5138 "CFG:", "PT:", "S:",
5139 "RQ:", "SSV:", "NSV:",
5140 "SSH:", "R:", "MAID:",
5141 "NAID:", "RMID:", "FMID:",
5142 "SCF:", "RRCPT:", "NG:",
5143 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5144 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5145 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5146 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5147 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5150 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5151 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5155 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5157 for (i = 0; not_included[i] != NULL; i++) {
5158 if (g_ascii_strncasecmp(buf, not_included[i],
5159 strlen(not_included[i])) == 0) {
5166 if (fputs(buf, fdest) == -1)
5169 if (!prefs_common.redirect_keep_from) {
5170 if (g_ascii_strncasecmp(buf, "From:",
5171 strlen("From:")) == 0) {
5172 err |= (fputs(" (by way of ", fdest) == EOF);
5173 if (compose->account->name
5174 && *compose->account->name) {
5175 compose_convert_header
5176 (compose, buf, sizeof(buf),
5177 compose->account->name,
5180 err |= (fprintf(fdest, "%s <%s>",
5182 compose->account->address) < 0);
5184 err |= (fprintf(fdest, "%s",
5185 compose->account->address) < 0);
5186 err |= (fputs(")", fdest) == EOF);
5190 if (fputs("\n", fdest) == -1)
5197 if (compose_redirect_write_headers(compose, fdest))
5200 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5201 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5214 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5216 GtkTextBuffer *buffer;
5217 GtkTextIter start, end;
5220 const gchar *out_codeset;
5221 EncodingType encoding = ENC_UNKNOWN;
5222 MimeInfo *mimemsg, *mimetext;
5224 const gchar *src_codeset = CS_INTERNAL;
5225 gchar *from_addr = NULL;
5226 gchar *from_name = NULL;
5228 if (action == COMPOSE_WRITE_FOR_SEND)
5229 attach_parts = TRUE;
5231 /* create message MimeInfo */
5232 mimemsg = procmime_mimeinfo_new();
5233 mimemsg->type = MIMETYPE_MESSAGE;
5234 mimemsg->subtype = g_strdup("rfc822");
5235 mimemsg->content = MIMECONTENT_MEM;
5236 mimemsg->tmp = TRUE; /* must free content later */
5237 mimemsg->data.mem = compose_get_header(compose);
5239 /* Create text part MimeInfo */
5240 /* get all composed text */
5241 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5242 gtk_text_buffer_get_start_iter(buffer, &start);
5243 gtk_text_buffer_get_end_iter(buffer, &end);
5244 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5246 out_codeset = conv_get_charset_str(compose->out_encoding);
5248 if (!out_codeset && is_ascii_str(chars)) {
5249 out_codeset = CS_US_ASCII;
5250 } else if (prefs_common.outgoing_fallback_to_ascii &&
5251 is_ascii_str(chars)) {
5252 out_codeset = CS_US_ASCII;
5253 encoding = ENC_7BIT;
5257 gchar *test_conv_global_out = NULL;
5258 gchar *test_conv_reply = NULL;
5260 /* automatic mode. be automatic. */
5261 codeconv_set_strict(TRUE);
5263 out_codeset = conv_get_outgoing_charset_str();
5265 debug_print("trying to convert to %s\n", out_codeset);
5266 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5269 if (!test_conv_global_out && compose->orig_charset
5270 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5271 out_codeset = compose->orig_charset;
5272 debug_print("failure; trying to convert to %s\n", out_codeset);
5273 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5276 if (!test_conv_global_out && !test_conv_reply) {
5278 out_codeset = CS_INTERNAL;
5279 debug_print("failure; finally using %s\n", out_codeset);
5281 g_free(test_conv_global_out);
5282 g_free(test_conv_reply);
5283 codeconv_set_strict(FALSE);
5286 if (encoding == ENC_UNKNOWN) {
5287 if (prefs_common.encoding_method == CTE_BASE64)
5288 encoding = ENC_BASE64;
5289 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5290 encoding = ENC_QUOTED_PRINTABLE;
5291 else if (prefs_common.encoding_method == CTE_8BIT)
5292 encoding = ENC_8BIT;
5294 encoding = procmime_get_encoding_for_charset(out_codeset);
5297 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5298 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5300 if (action == COMPOSE_WRITE_FOR_SEND) {
5301 codeconv_set_strict(TRUE);
5302 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5303 codeconv_set_strict(FALSE);
5309 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5310 "to the specified %s charset.\n"
5311 "Send it as %s?"), out_codeset, src_codeset);
5312 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5313 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5316 if (aval != G_ALERTALTERNATE) {
5321 out_codeset = src_codeset;
5327 out_codeset = src_codeset;
5332 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5333 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5334 strstr(buf, "\nFrom ") != NULL) {
5335 encoding = ENC_QUOTED_PRINTABLE;
5339 mimetext = procmime_mimeinfo_new();
5340 mimetext->content = MIMECONTENT_MEM;
5341 mimetext->tmp = TRUE; /* must free content later */
5342 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5343 * and free the data, which we need later. */
5344 mimetext->data.mem = g_strdup(buf);
5345 mimetext->type = MIMETYPE_TEXT;
5346 mimetext->subtype = g_strdup("plain");
5347 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5348 g_strdup(out_codeset));
5350 /* protect trailing spaces when signing message */
5351 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5352 privacy_system_can_sign(compose->privacy_system)) {
5353 encoding = ENC_QUOTED_PRINTABLE;
5356 debug_print("main text: %zd bytes encoded as %s in %d\n",
5357 strlen(buf), out_codeset, encoding);
5359 /* check for line length limit */
5360 if (action == COMPOSE_WRITE_FOR_SEND &&
5361 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5362 check_line_length(buf, 1000, &line) < 0) {
5366 msg = g_strdup_printf
5367 (_("Line %d exceeds the line length limit (998 bytes).\n"
5368 "The contents of the message might be broken on the way to the delivery.\n"
5370 "Send it anyway?"), line + 1);
5371 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5373 if (aval != G_ALERTALTERNATE) {
5379 if (encoding != ENC_UNKNOWN)
5380 procmime_encode_content(mimetext, encoding);
5382 /* append attachment parts */
5383 if (compose_use_attach(compose) && attach_parts) {
5384 MimeInfo *mimempart;
5385 gchar *boundary = NULL;
5386 mimempart = procmime_mimeinfo_new();
5387 mimempart->content = MIMECONTENT_EMPTY;
5388 mimempart->type = MIMETYPE_MULTIPART;
5389 mimempart->subtype = g_strdup("mixed");
5393 boundary = generate_mime_boundary(NULL);
5394 } while (strstr(buf, boundary) != NULL);
5396 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5399 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5401 g_node_append(mimempart->node, mimetext->node);
5402 g_node_append(mimemsg->node, mimempart->node);
5404 if (compose_add_attachments(compose, mimempart) < 0)
5407 g_node_append(mimemsg->node, mimetext->node);
5411 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5412 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5413 /* extract name and address */
5414 if (strstr(spec, " <") && strstr(spec, ">")) {
5415 from_addr = g_strdup(strrchr(spec, '<')+1);
5416 *(strrchr(from_addr, '>')) = '\0';
5417 from_name = g_strdup(spec);
5418 *(strrchr(from_name, '<')) = '\0';
5425 /* sign message if sending */
5426 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5427 privacy_system_can_sign(compose->privacy_system))
5428 if (!privacy_sign(compose->privacy_system, mimemsg,
5429 compose->account, from_addr)) {
5436 procmime_write_mimeinfo(mimemsg, fp);
5438 procmime_mimeinfo_free_all(mimemsg);
5443 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5445 GtkTextBuffer *buffer;
5446 GtkTextIter start, end;
5451 if ((fp = g_fopen(file, "wb")) == NULL) {
5452 FILE_OP_ERROR(file, "fopen");
5456 /* chmod for security */
5457 if (change_file_mode_rw(fp, file) < 0) {
5458 FILE_OP_ERROR(file, "chmod");
5459 g_warning("can't change file mode\n");
5462 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5463 gtk_text_buffer_get_start_iter(buffer, &start);
5464 gtk_text_buffer_get_end_iter(buffer, &end);
5465 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5467 chars = conv_codeset_strdup
5468 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5471 if (!chars) return -1;
5474 len = strlen(chars);
5475 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5476 FILE_OP_ERROR(file, "fwrite");
5485 if (fclose(fp) == EOF) {
5486 FILE_OP_ERROR(file, "fclose");
5493 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5496 MsgInfo *msginfo = compose->targetinfo;
5498 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5499 if (!msginfo) return -1;
5501 if (!force && MSG_IS_LOCKED(msginfo->flags))
5504 item = msginfo->folder;
5505 cm_return_val_if_fail(item != NULL, -1);
5507 if (procmsg_msg_exist(msginfo) &&
5508 (folder_has_parent_of_type(item, F_QUEUE) ||
5509 folder_has_parent_of_type(item, F_DRAFT)
5510 || msginfo == compose->autosaved_draft)) {
5511 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5512 g_warning("can't remove the old message\n");
5515 debug_print("removed reedit target %d\n", msginfo->msgnum);
5522 static void compose_remove_draft(Compose *compose)
5525 MsgInfo *msginfo = compose->targetinfo;
5526 drafts = account_get_special_folder(compose->account, F_DRAFT);
5528 if (procmsg_msg_exist(msginfo)) {
5529 folder_item_remove_msg(drafts, msginfo->msgnum);
5534 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5535 gboolean remove_reedit_target)
5537 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5540 static gboolean compose_warn_encryption(Compose *compose)
5542 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5543 AlertValue val = G_ALERTALTERNATE;
5545 if (warning == NULL)
5548 val = alertpanel_full(_("Encryption warning"), warning,
5549 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5550 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5551 if (val & G_ALERTDISABLE) {
5552 val &= ~G_ALERTDISABLE;
5553 if (val == G_ALERTALTERNATE)
5554 privacy_inhibit_encrypt_warning(compose->privacy_system,
5558 if (val == G_ALERTALTERNATE) {
5565 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5566 gchar **msgpath, gboolean check_subject,
5567 gboolean remove_reedit_target)
5574 static gboolean lock = FALSE;
5575 PrefsAccount *mailac = NULL, *newsac = NULL;
5576 gboolean err = FALSE;
5578 debug_print("queueing message...\n");
5579 cm_return_val_if_fail(compose->account != NULL, -1);
5583 if (compose_check_entries(compose, check_subject) == FALSE) {
5585 if (compose->batch) {
5586 gtk_widget_show_all(compose->window);
5591 if (!compose->to_list && !compose->newsgroup_list) {
5592 g_warning("can't get recipient list.");
5597 if (compose->to_list) {
5598 if (compose->account->protocol != A_NNTP)
5599 mailac = compose->account;
5600 else if (cur_account && cur_account->protocol != A_NNTP)
5601 mailac = cur_account;
5602 else if (!(mailac = compose_current_mail_account())) {
5604 alertpanel_error(_("No account for sending mails available!"));
5609 if (compose->newsgroup_list) {
5610 if (compose->account->protocol == A_NNTP)
5611 newsac = compose->account;
5614 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5619 /* write queue header */
5620 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5621 G_DIR_SEPARATOR, compose, (guint) rand());
5622 debug_print("queuing to %s\n", tmp);
5623 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5624 FILE_OP_ERROR(tmp, "fopen");
5630 if (change_file_mode_rw(fp, tmp) < 0) {
5631 FILE_OP_ERROR(tmp, "chmod");
5632 g_warning("can't change file mode\n");
5635 /* queueing variables */
5636 err |= (fprintf(fp, "AF:\n") < 0);
5637 err |= (fprintf(fp, "NF:0\n") < 0);
5638 err |= (fprintf(fp, "PS:10\n") < 0);
5639 err |= (fprintf(fp, "SRH:1\n") < 0);
5640 err |= (fprintf(fp, "SFN:\n") < 0);
5641 err |= (fprintf(fp, "DSR:\n") < 0);
5643 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5645 err |= (fprintf(fp, "MID:\n") < 0);
5646 err |= (fprintf(fp, "CFG:\n") < 0);
5647 err |= (fprintf(fp, "PT:0\n") < 0);
5648 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5649 err |= (fprintf(fp, "RQ:\n") < 0);
5651 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5653 err |= (fprintf(fp, "SSV:\n") < 0);
5655 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5657 err |= (fprintf(fp, "NSV:\n") < 0);
5658 err |= (fprintf(fp, "SSH:\n") < 0);
5659 /* write recepient list */
5660 if (compose->to_list) {
5661 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5662 for (cur = compose->to_list->next; cur != NULL;
5664 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5665 err |= (fprintf(fp, "\n") < 0);
5667 /* write newsgroup list */
5668 if (compose->newsgroup_list) {
5669 err |= (fprintf(fp, "NG:") < 0);
5670 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5671 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5672 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5673 err |= (fprintf(fp, "\n") < 0);
5675 /* Sylpheed account IDs */
5677 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5679 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5682 if (compose->privacy_system != NULL) {
5683 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5684 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5685 if (compose->use_encryption) {
5687 if (!compose_warn_encryption(compose)) {
5694 if (mailac && mailac->encrypt_to_self) {
5695 GSList *tmp_list = g_slist_copy(compose->to_list);
5696 tmp_list = g_slist_append(tmp_list, compose->account->address);
5697 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5698 g_slist_free(tmp_list);
5700 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5702 if (encdata != NULL) {
5703 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5704 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5705 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5707 } /* else we finally dont want to encrypt */
5709 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5710 /* and if encdata was null, it means there's been a problem in
5722 /* Save copy folder */
5723 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5724 gchar *savefolderid;
5726 savefolderid = compose_get_save_to(compose);
5727 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5728 g_free(savefolderid);
5730 /* Save copy folder */
5731 if (compose->return_receipt) {
5732 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5734 /* Message-ID of message replying to */
5735 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5738 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5739 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5742 /* Message-ID of message forwarding to */
5743 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5746 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5747 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5751 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5752 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5754 /* end of headers */
5755 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5757 if (compose->redirect_filename != NULL) {
5758 if (compose_redirect_write_to_file(compose, fp) < 0) {
5767 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5772 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5776 g_warning("failed to write queue message\n");
5783 if (fclose(fp) == EOF) {
5784 FILE_OP_ERROR(tmp, "fclose");
5791 if (item && *item) {
5794 queue = account_get_special_folder(compose->account, F_QUEUE);
5797 g_warning("can't find queue folder\n");
5803 folder_item_scan(queue);
5804 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5805 g_warning("can't queue the message\n");
5812 if (msgpath == NULL) {
5818 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5819 compose_remove_reedit_target(compose, FALSE);
5822 if ((msgnum != NULL) && (item != NULL)) {
5830 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5833 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5835 struct stat statbuf;
5836 gchar *type, *subtype;
5837 GtkTreeModel *model;
5840 model = gtk_tree_view_get_model(tree_view);
5842 if (!gtk_tree_model_get_iter_first(model, &iter))
5845 gtk_tree_model_get(model, &iter,
5849 if (!is_file_exist(ainfo->file)) {
5850 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5851 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5852 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5854 if (val == G_ALERTDEFAULT) {
5859 mimepart = procmime_mimeinfo_new();
5860 mimepart->content = MIMECONTENT_FILE;
5861 mimepart->data.filename = g_strdup(ainfo->file);
5862 mimepart->tmp = FALSE; /* or we destroy our attachment */
5863 mimepart->offset = 0;
5865 g_stat(ainfo->file, &statbuf);
5866 mimepart->length = statbuf.st_size;
5868 type = g_strdup(ainfo->content_type);
5870 if (!strchr(type, '/')) {
5872 type = g_strdup("application/octet-stream");
5875 subtype = strchr(type, '/') + 1;
5876 *(subtype - 1) = '\0';
5877 mimepart->type = procmime_get_media_type(type);
5878 mimepart->subtype = g_strdup(subtype);
5881 if (mimepart->type == MIMETYPE_MESSAGE &&
5882 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5883 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5886 if (mimepart->type == MIMETYPE_APPLICATION &&
5887 !strcmp2(mimepart->subtype, "octet-stream"))
5888 g_hash_table_insert(mimepart->typeparameters,
5889 g_strdup("name"), g_strdup(ainfo->name));
5890 g_hash_table_insert(mimepart->dispositionparameters,
5891 g_strdup("filename"), g_strdup(ainfo->name));
5892 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5896 if (compose->use_signing) {
5897 if (ainfo->encoding == ENC_7BIT)
5898 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5899 else if (ainfo->encoding == ENC_8BIT)
5900 ainfo->encoding = ENC_BASE64;
5903 procmime_encode_content(mimepart, ainfo->encoding);
5905 g_node_append(parent->node, mimepart->node);
5906 } while (gtk_tree_model_iter_next(model, &iter));
5911 #define IS_IN_CUSTOM_HEADER(header) \
5912 (compose->account->add_customhdr && \
5913 custom_header_find(compose->account->customhdr_list, header) != NULL)
5915 static void compose_add_headerfield_from_headerlist(Compose *compose,
5917 const gchar *fieldname,
5918 const gchar *seperator)
5920 gchar *str, *fieldname_w_colon;
5921 gboolean add_field = FALSE;
5923 ComposeHeaderEntry *headerentry;
5924 const gchar *headerentryname;
5925 const gchar *trans_fieldname;
5928 if (IS_IN_CUSTOM_HEADER(fieldname))
5931 debug_print("Adding %s-fields\n", fieldname);
5933 fieldstr = g_string_sized_new(64);
5935 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5936 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5938 for (list = compose->header_list; list; list = list->next) {
5939 headerentry = ((ComposeHeaderEntry *)list->data);
5940 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5942 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5943 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5945 if (str[0] != '\0') {
5947 g_string_append(fieldstr, seperator);
5948 g_string_append(fieldstr, str);
5957 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5958 compose_convert_header
5959 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5960 strlen(fieldname) + 2, TRUE);
5961 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5965 g_free(fieldname_w_colon);
5966 g_string_free(fieldstr, TRUE);
5971 static gchar *compose_get_header(Compose *compose)
5973 gchar buf[BUFFSIZE];
5974 const gchar *entry_str;
5978 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5980 gchar *from_name = NULL, *from_address = NULL;
5983 cm_return_val_if_fail(compose->account != NULL, NULL);
5984 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5986 header = g_string_sized_new(64);
5989 get_rfc822_date(buf, sizeof(buf));
5990 g_string_append_printf(header, "Date: %s\n", buf);
5994 if (compose->account->name && *compose->account->name) {
5996 QUOTE_IF_REQUIRED(buf, compose->account->name);
5997 tmp = g_strdup_printf("%s <%s>",
5998 buf, compose->account->address);
6000 tmp = g_strdup_printf("%s",
6001 compose->account->address);
6003 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6004 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6006 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6007 from_address = g_strdup(compose->account->address);
6009 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6010 /* extract name and address */
6011 if (strstr(spec, " <") && strstr(spec, ">")) {
6012 from_address = g_strdup(strrchr(spec, '<')+1);
6013 *(strrchr(from_address, '>')) = '\0';
6014 from_name = g_strdup(spec);
6015 *(strrchr(from_name, '<')) = '\0';
6018 from_address = g_strdup(spec);
6025 if (from_name && *from_name) {
6026 compose_convert_header
6027 (compose, buf, sizeof(buf), from_name,
6028 strlen("From: "), TRUE);
6029 QUOTE_IF_REQUIRED(name, buf);
6031 g_string_append_printf(header, "From: %s <%s>\n",
6032 name, from_address);
6034 g_string_append_printf(header, "From: %s\n", from_address);
6037 g_free(from_address);
6040 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6043 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6046 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6049 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6052 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6054 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6057 compose_convert_header(compose, buf, sizeof(buf), str,
6058 strlen("Subject: "), FALSE);
6059 g_string_append_printf(header, "Subject: %s\n", buf);
6065 if (compose->account->set_domain && compose->account->domain) {
6066 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6067 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6068 g_snprintf(buf, sizeof(buf), "%s",
6069 strchr(compose->account->address, '@') ?
6070 strchr(compose->account->address, '@')+1 :
6071 compose->account->address);
6073 g_snprintf(buf, sizeof(buf), "%s", "");
6076 if (compose->account->gen_msgid) {
6078 if (compose->account->msgid_with_addr) {
6079 addr = compose->account->address;
6081 generate_msgid(buf, sizeof(buf), addr);
6082 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6083 compose->msgid = g_strdup(buf);
6085 compose->msgid = NULL;
6088 if (compose->remove_references == FALSE) {
6090 if (compose->inreplyto && compose->to_list)
6091 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6094 if (compose->references)
6095 g_string_append_printf(header, "References: %s\n", compose->references);
6099 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6102 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6105 if (compose->account->organization &&
6106 strlen(compose->account->organization) &&
6107 !IS_IN_CUSTOM_HEADER("Organization")) {
6108 compose_convert_header(compose, buf, sizeof(buf),
6109 compose->account->organization,
6110 strlen("Organization: "), FALSE);
6111 g_string_append_printf(header, "Organization: %s\n", buf);
6114 /* Program version and system info */
6115 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6116 !compose->newsgroup_list) {
6117 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6119 gtk_major_version, gtk_minor_version, gtk_micro_version,
6122 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6123 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6125 gtk_major_version, gtk_minor_version, gtk_micro_version,
6129 /* custom headers */
6130 if (compose->account->add_customhdr) {
6133 for (cur = compose->account->customhdr_list; cur != NULL;
6135 CustomHeader *chdr = (CustomHeader *)cur->data;
6137 if (custom_header_is_allowed(chdr->name)) {
6138 compose_convert_header
6139 (compose, buf, sizeof(buf),
6140 chdr->value ? chdr->value : "",
6141 strlen(chdr->name) + 2, FALSE);
6142 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6147 /* Automatic Faces and X-Faces */
6148 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6149 g_string_append_printf(header, "X-Face: %s\n", buf);
6151 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6152 g_string_append_printf(header, "X-Face: %s\n", buf);
6154 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6155 g_string_append_printf(header, "Face: %s\n", buf);
6157 else if (get_default_face (buf, sizeof(buf)) == 0) {
6158 g_string_append_printf(header, "Face: %s\n", buf);
6162 switch (compose->priority) {
6163 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6164 "X-Priority: 1 (Highest)\n");
6166 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6167 "X-Priority: 2 (High)\n");
6169 case PRIORITY_NORMAL: break;
6170 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6171 "X-Priority: 4 (Low)\n");
6173 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6174 "X-Priority: 5 (Lowest)\n");
6176 default: debug_print("compose: priority unknown : %d\n",
6180 /* Request Return Receipt */
6181 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6182 if (compose->return_receipt) {
6183 if (compose->account->name
6184 && *compose->account->name) {
6185 compose_convert_header(compose, buf, sizeof(buf),
6186 compose->account->name,
6187 strlen("Disposition-Notification-To: "),
6189 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6191 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6195 /* get special headers */
6196 for (list = compose->header_list; list; list = list->next) {
6197 ComposeHeaderEntry *headerentry;
6200 gchar *headername_wcolon;
6201 const gchar *headername_trans;
6204 gboolean standard_header = FALSE;
6206 headerentry = ((ComposeHeaderEntry *)list->data);
6208 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6210 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6215 if (!strstr(tmp, ":")) {
6216 headername_wcolon = g_strconcat(tmp, ":", NULL);
6217 headername = g_strdup(tmp);
6219 headername_wcolon = g_strdup(tmp);
6220 headername = g_strdup(strtok(tmp, ":"));
6224 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6225 Xstrdup_a(headervalue, entry_str, return NULL);
6226 subst_char(headervalue, '\r', ' ');
6227 subst_char(headervalue, '\n', ' ');
6228 string = std_headers;
6229 while (*string != NULL) {
6230 headername_trans = prefs_common_translated_header_name(*string);
6231 if (!strcmp(headername_trans, headername_wcolon))
6232 standard_header = TRUE;
6235 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6236 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6239 g_free(headername_wcolon);
6243 g_string_free(header, FALSE);
6248 #undef IS_IN_CUSTOM_HEADER
6250 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6251 gint header_len, gboolean addr_field)
6253 gchar *tmpstr = NULL;
6254 const gchar *out_codeset = NULL;
6256 cm_return_if_fail(src != NULL);
6257 cm_return_if_fail(dest != NULL);
6259 if (len < 1) return;
6261 tmpstr = g_strdup(src);
6263 subst_char(tmpstr, '\n', ' ');
6264 subst_char(tmpstr, '\r', ' ');
6267 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6268 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6269 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6274 codeconv_set_strict(TRUE);
6275 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6276 conv_get_charset_str(compose->out_encoding));
6277 codeconv_set_strict(FALSE);
6279 if (!dest || *dest == '\0') {
6280 gchar *test_conv_global_out = NULL;
6281 gchar *test_conv_reply = NULL;
6283 /* automatic mode. be automatic. */
6284 codeconv_set_strict(TRUE);
6286 out_codeset = conv_get_outgoing_charset_str();
6288 debug_print("trying to convert to %s\n", out_codeset);
6289 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6292 if (!test_conv_global_out && compose->orig_charset
6293 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6294 out_codeset = compose->orig_charset;
6295 debug_print("failure; trying to convert to %s\n", out_codeset);
6296 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6299 if (!test_conv_global_out && !test_conv_reply) {
6301 out_codeset = CS_INTERNAL;
6302 debug_print("finally using %s\n", out_codeset);
6304 g_free(test_conv_global_out);
6305 g_free(test_conv_reply);
6306 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6308 codeconv_set_strict(FALSE);
6313 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6317 cm_return_if_fail(user_data != NULL);
6319 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6320 g_strstrip(address);
6321 if (*address != '\0') {
6322 gchar *name = procheader_get_fromname(address);
6323 extract_address(address);
6324 addressbook_add_contact(name, address, NULL, NULL);
6329 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6331 GtkWidget *menuitem;
6334 cm_return_if_fail(menu != NULL);
6335 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6337 menuitem = gtk_separator_menu_item_new();
6338 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6339 gtk_widget_show(menuitem);
6341 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6342 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6344 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6345 g_strstrip(address);
6346 if (*address == '\0') {
6347 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6350 g_signal_connect(G_OBJECT(menuitem), "activate",
6351 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6352 gtk_widget_show(menuitem);
6355 static void compose_create_header_entry(Compose *compose)
6357 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6364 const gchar *header = NULL;
6365 ComposeHeaderEntry *headerentry;
6366 gboolean standard_header = FALSE;
6367 #if !(GTK_CHECK_VERSION(2,12,0))
6368 GtkTooltips *tips = compose->tooltips;
6371 headerentry = g_new0(ComposeHeaderEntry, 1);
6374 combo = gtk_combo_box_entry_new_text();
6376 while(*string != NULL) {
6377 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6378 (gchar*)prefs_common_translated_header_name(*string));
6381 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6382 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6383 G_CALLBACK(compose_grab_focus_cb), compose);
6384 gtk_widget_show(combo);
6385 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6386 compose->header_nextrow, compose->header_nextrow+1,
6387 GTK_SHRINK, GTK_FILL, 0, 0);
6388 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6389 const gchar *last_header_entry = gtk_entry_get_text(
6390 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6392 while (*string != NULL) {
6393 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6394 standard_header = TRUE;
6397 if (standard_header)
6398 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6400 if (!compose->header_last || !standard_header) {
6401 switch(compose->account->protocol) {
6403 header = prefs_common_translated_header_name("Newsgroups:");
6406 header = prefs_common_translated_header_name("To:");
6411 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6413 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6414 G_CALLBACK(compose_grab_focus_cb), compose);
6416 /* Entry field with cleanup button */
6417 #if GTK_CHECK_VERSION(2, 8, 0)
6418 button = gtk_button_new();
6419 gtk_button_set_image(GTK_BUTTON(button),
6420 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6422 button = gtk_button_new_with_label(_("Clear"));
6424 gtk_widget_show(button);
6425 CLAWS_SET_TIP(button,
6426 _("Delete entry contents"));
6427 entry = gtk_entry_new();
6428 gtk_widget_show(entry);
6429 CLAWS_SET_TIP(entry,
6430 _("Use <tab> to autocomplete from addressbook"));
6431 hbox = gtk_hbox_new (FALSE, 0);
6432 gtk_widget_show(hbox);
6433 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6434 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6435 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6436 compose->header_nextrow, compose->header_nextrow+1,
6437 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6439 g_signal_connect(G_OBJECT(entry), "key-press-event",
6440 G_CALLBACK(compose_headerentry_key_press_event_cb),
6442 g_signal_connect(G_OBJECT(entry), "changed",
6443 G_CALLBACK(compose_headerentry_changed_cb),
6445 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6446 G_CALLBACK(compose_grab_focus_cb), compose);
6448 g_signal_connect(G_OBJECT(button), "clicked",
6449 G_CALLBACK(compose_headerentry_button_clicked_cb),
6453 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6454 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6455 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6456 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6457 G_CALLBACK(compose_header_drag_received_cb),
6459 g_signal_connect(G_OBJECT(entry), "drag-drop",
6460 G_CALLBACK(compose_drag_drop),
6462 g_signal_connect(G_OBJECT(entry), "populate-popup",
6463 G_CALLBACK(compose_entry_popup_extend),
6466 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6468 headerentry->compose = compose;
6469 headerentry->combo = combo;
6470 headerentry->entry = entry;
6471 headerentry->button = button;
6472 headerentry->hbox = hbox;
6473 headerentry->headernum = compose->header_nextrow;
6475 compose->header_nextrow++;
6476 compose->header_last = headerentry;
6477 compose->header_list =
6478 g_slist_append(compose->header_list,
6482 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6484 ComposeHeaderEntry *last_header;
6486 last_header = compose->header_last;
6488 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6489 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6492 static void compose_remove_header_entries(Compose *compose)
6495 for (list = compose->header_list; list; list = list->next) {
6496 ComposeHeaderEntry *headerentry =
6497 (ComposeHeaderEntry *)list->data;
6498 gtk_widget_destroy(headerentry->combo);
6499 gtk_widget_destroy(headerentry->entry);
6500 gtk_widget_destroy(headerentry->button);
6501 gtk_widget_destroy(headerentry->hbox);
6502 g_free(headerentry);
6504 compose->header_last = NULL;
6505 g_slist_free(compose->header_list);
6506 compose->header_list = NULL;
6507 compose->header_nextrow = 1;
6508 compose_create_header_entry(compose);
6511 static GtkWidget *compose_create_header(Compose *compose)
6513 GtkWidget *from_optmenu_hbox;
6514 GtkWidget *header_scrolledwin;
6515 GtkWidget *header_table;
6519 /* header labels and entries */
6520 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6521 gtk_widget_show(header_scrolledwin);
6522 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6524 header_table = gtk_table_new(2, 2, FALSE);
6525 gtk_widget_show(header_table);
6526 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6527 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6528 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6531 /* option menu for selecting accounts */
6532 from_optmenu_hbox = compose_account_option_menu_create(compose);
6533 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6534 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6537 compose->header_table = header_table;
6538 compose->header_list = NULL;
6539 compose->header_nextrow = count;
6541 compose_create_header_entry(compose);
6543 compose->table = NULL;
6545 return header_scrolledwin ;
6548 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6550 Compose *compose = (Compose *)data;
6551 GdkEventButton event;
6554 event.time = gtk_get_current_event_time();
6556 return attach_button_pressed(compose->attach_clist, &event, compose);
6559 static GtkWidget *compose_create_attach(Compose *compose)
6561 GtkWidget *attach_scrwin;
6562 GtkWidget *attach_clist;
6564 GtkListStore *store;
6565 GtkCellRenderer *renderer;
6566 GtkTreeViewColumn *column;
6567 GtkTreeSelection *selection;
6569 /* attachment list */
6570 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6571 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6572 GTK_POLICY_AUTOMATIC,
6573 GTK_POLICY_AUTOMATIC);
6574 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6576 store = gtk_list_store_new(N_ATTACH_COLS,
6581 G_TYPE_AUTO_POINTER,
6583 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6584 (GTK_TREE_MODEL(store)));
6585 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6586 g_object_unref(store);
6588 renderer = gtk_cell_renderer_text_new();
6589 column = gtk_tree_view_column_new_with_attributes
6590 (_("Mime type"), renderer, "text",
6591 COL_MIMETYPE, NULL);
6592 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6594 renderer = gtk_cell_renderer_text_new();
6595 column = gtk_tree_view_column_new_with_attributes
6596 (_("Size"), renderer, "text",
6598 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6600 renderer = gtk_cell_renderer_text_new();
6601 column = gtk_tree_view_column_new_with_attributes
6602 (_("Name"), renderer, "text",
6604 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6606 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6607 prefs_common.use_stripes_everywhere);
6608 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6609 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6611 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6612 G_CALLBACK(attach_selected), compose);
6613 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6614 G_CALLBACK(attach_button_pressed), compose);
6616 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6617 G_CALLBACK(popup_attach_button_pressed), compose);
6619 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6620 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6621 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6622 G_CALLBACK(popup_attach_button_pressed), compose);
6624 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6625 G_CALLBACK(attach_key_pressed), compose);
6628 gtk_drag_dest_set(attach_clist,
6629 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6630 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6631 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6632 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6633 G_CALLBACK(compose_attach_drag_received_cb),
6635 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6636 G_CALLBACK(compose_drag_drop),
6639 compose->attach_scrwin = attach_scrwin;
6640 compose->attach_clist = attach_clist;
6642 return attach_scrwin;
6645 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6646 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6648 static GtkWidget *compose_create_others(Compose *compose)
6651 GtkWidget *savemsg_checkbtn;
6652 GtkWidget *savemsg_combo;
6653 GtkWidget *savemsg_select;
6656 gchar *folderidentifier;
6658 /* Table for settings */
6659 table = gtk_table_new(3, 1, FALSE);
6660 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6661 gtk_widget_show(table);
6662 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6665 /* Save Message to folder */
6666 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6667 gtk_widget_show(savemsg_checkbtn);
6668 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6669 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6670 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6672 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6673 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6675 savemsg_combo = gtk_combo_box_entry_new_text();
6676 compose->savemsg_checkbtn = savemsg_checkbtn;
6677 compose->savemsg_combo = savemsg_combo;
6678 gtk_widget_show(savemsg_combo);
6680 if (prefs_common.compose_save_to_history)
6681 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6682 prefs_common.compose_save_to_history);
6684 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6685 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6686 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6687 G_CALLBACK(compose_grab_focus_cb), compose);
6688 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6689 folderidentifier = folder_item_get_identifier(account_get_special_folder
6690 (compose->account, F_OUTBOX));
6691 compose_set_save_to(compose, folderidentifier);
6692 g_free(folderidentifier);
6695 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6696 gtk_widget_show(savemsg_select);
6697 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6698 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6699 G_CALLBACK(compose_savemsg_select_cb),
6707 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6709 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6710 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6713 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6718 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6721 path = folder_item_get_identifier(dest);
6723 compose_set_save_to(compose, path);
6727 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6728 GdkAtom clip, GtkTextIter *insert_place);
6731 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6735 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6737 if (event->button == 3) {
6739 GtkTextIter sel_start, sel_end;
6740 gboolean stuff_selected;
6742 /* move the cursor to allow GtkAspell to check the word
6743 * under the mouse */
6744 if (event->x && event->y) {
6745 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6746 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6748 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6751 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6752 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6755 stuff_selected = gtk_text_buffer_get_selection_bounds(
6757 &sel_start, &sel_end);
6759 gtk_text_buffer_place_cursor (buffer, &iter);
6760 /* reselect stuff */
6762 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6763 gtk_text_buffer_select_range(buffer,
6764 &sel_start, &sel_end);
6766 return FALSE; /* pass the event so that the right-click goes through */
6769 if (event->button == 2) {
6774 /* get the middle-click position to paste at the correct place */
6775 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6776 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6778 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6781 entry_paste_clipboard(compose, text,
6782 prefs_common.linewrap_pastes,
6783 GDK_SELECTION_PRIMARY, &iter);
6791 static void compose_spell_menu_changed(void *data)
6793 Compose *compose = (Compose *)data;
6795 GtkWidget *menuitem;
6796 GtkWidget *parent_item;
6797 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6800 if (compose->gtkaspell == NULL)
6803 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6804 "/Menu/Spelling/Options");
6806 /* setting the submenu removes /Spelling/Options from the factory
6807 * so we need to save it */
6809 if (parent_item == NULL) {
6810 parent_item = compose->aspell_options_menu;
6811 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6813 compose->aspell_options_menu = parent_item;
6815 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6817 spell_menu = g_slist_reverse(spell_menu);
6818 for (items = spell_menu;
6819 items; items = items->next) {
6820 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6821 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6822 gtk_widget_show(GTK_WIDGET(menuitem));
6824 g_slist_free(spell_menu);
6826 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6827 gtk_widget_show(parent_item);
6830 static void compose_dict_changed(void *data)
6832 Compose *compose = (Compose *) data;
6834 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6837 gtkaspell_highlight_all(compose->gtkaspell);
6838 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6842 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6844 Compose *compose = (Compose *)data;
6845 GdkEventButton event;
6848 event.time = gtk_get_current_event_time();
6852 return text_clicked(compose->text, &event, compose);
6855 static gboolean compose_force_window_origin = TRUE;
6856 static Compose *compose_create(PrefsAccount *account,
6865 GtkWidget *handlebox;
6867 GtkWidget *notebook;
6869 GtkWidget *attach_hbox;
6870 GtkWidget *attach_lab1;
6871 GtkWidget *attach_lab2;
6876 GtkWidget *subject_hbox;
6877 GtkWidget *subject_frame;
6878 GtkWidget *subject_entry;
6882 GtkWidget *edit_vbox;
6883 GtkWidget *ruler_hbox;
6885 GtkWidget *scrolledwin;
6887 GtkTextBuffer *buffer;
6888 GtkClipboard *clipboard;
6891 UndoMain *undostruct;
6893 gchar *titles[N_ATTACH_COLS];
6894 GtkWidget *popupmenu;
6895 GtkWidget *tmpl_menu;
6896 GtkActionGroup *action_group = NULL;
6899 GtkAspell * gtkaspell = NULL;
6902 static GdkGeometry geometry;
6904 cm_return_val_if_fail(account != NULL, NULL);
6906 debug_print("Creating compose window...\n");
6907 compose = g_new0(Compose, 1);
6909 titles[COL_MIMETYPE] = _("MIME type");
6910 titles[COL_SIZE] = _("Size");
6911 titles[COL_NAME] = _("Name");
6913 compose->batch = batch;
6914 compose->account = account;
6915 compose->folder = folder;
6917 compose->mutex = g_mutex_new();
6918 compose->set_cursor_pos = -1;
6920 #if !(GTK_CHECK_VERSION(2,12,0))
6921 compose->tooltips = tips;
6924 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6926 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6927 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6929 if (!geometry.max_width) {
6930 geometry.max_width = gdk_screen_width();
6931 geometry.max_height = gdk_screen_height();
6934 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6935 &geometry, GDK_HINT_MAX_SIZE);
6936 if (!geometry.min_width) {
6937 geometry.min_width = 600;
6938 geometry.min_height = 440;
6940 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6941 &geometry, GDK_HINT_MIN_SIZE);
6943 #ifndef GENERIC_UMPC
6944 if (compose_force_window_origin)
6945 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6946 prefs_common.compose_y);
6948 g_signal_connect(G_OBJECT(window), "delete_event",
6949 G_CALLBACK(compose_delete_cb), compose);
6950 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6951 gtk_widget_realize(window);
6953 gtkut_widget_set_composer_icon(window);
6955 vbox = gtk_vbox_new(FALSE, 0);
6956 gtk_container_add(GTK_CONTAINER(window), vbox);
6958 compose->ui_manager = gtk_ui_manager_new();
6959 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6960 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6961 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6962 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6963 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6964 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6965 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6966 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6967 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6968 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6973 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6988 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6990 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6991 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6999 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7000 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7002 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7003 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7004 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7006 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7011 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7013 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7014 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7016 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7025 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7026 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7043 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7052 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7066 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7067 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7070 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7076 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7079 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7088 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)
7089 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)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7095 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)
7096 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)
7098 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7101 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)
7102 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7105 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)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7111 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)
7112 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7113 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7117 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)
7118 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)
7119 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7120 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7123 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7129 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7130 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)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7134 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7148 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7149 gtk_widget_show_all(menubar);
7151 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7153 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7155 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7158 if (prefs_common.toolbar_detachable) {
7159 handlebox = gtk_handle_box_new();
7161 handlebox = gtk_hbox_new(FALSE, 0);
7163 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7165 gtk_widget_realize(handlebox);
7167 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7170 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7174 vbox2 = gtk_vbox_new(FALSE, 2);
7175 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7176 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7179 notebook = gtk_notebook_new();
7180 gtk_widget_set_size_request(notebook, -1, 130);
7181 gtk_widget_show(notebook);
7183 /* header labels and entries */
7184 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7185 compose_create_header(compose),
7186 gtk_label_new_with_mnemonic(_("Hea_der")));
7187 /* attachment list */
7188 attach_hbox = gtk_hbox_new(FALSE, 0);
7189 gtk_widget_show(attach_hbox);
7191 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7192 gtk_widget_show(attach_lab1);
7193 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7195 attach_lab2 = gtk_label_new("");
7196 gtk_widget_show(attach_lab2);
7197 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7199 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7200 compose_create_attach(compose),
7203 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7204 compose_create_others(compose),
7205 gtk_label_new_with_mnemonic(_("Othe_rs")));
7208 subject_hbox = gtk_hbox_new(FALSE, 0);
7209 gtk_widget_show(subject_hbox);
7211 subject_frame = gtk_frame_new(NULL);
7212 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7213 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7214 gtk_widget_show(subject_frame);
7216 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7217 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7218 gtk_widget_show(subject);
7220 label = gtk_label_new(_("Subject:"));
7221 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7222 gtk_widget_show(label);
7225 subject_entry = claws_spell_entry_new();
7227 subject_entry = gtk_entry_new();
7229 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7230 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7231 G_CALLBACK(compose_grab_focus_cb), compose);
7232 gtk_widget_show(subject_entry);
7233 compose->subject_entry = subject_entry;
7234 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7236 edit_vbox = gtk_vbox_new(FALSE, 0);
7238 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7241 ruler_hbox = gtk_hbox_new(FALSE, 0);
7242 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7244 ruler = gtk_shruler_new();
7245 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7246 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7250 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7251 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7252 GTK_POLICY_AUTOMATIC,
7253 GTK_POLICY_AUTOMATIC);
7254 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7256 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7257 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7259 text = gtk_text_view_new();
7260 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7261 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7262 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7263 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7264 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7266 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7268 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7269 G_CALLBACK(compose_edit_size_alloc),
7271 g_signal_connect(G_OBJECT(buffer), "changed",
7272 G_CALLBACK(compose_changed_cb), compose);
7273 g_signal_connect(G_OBJECT(text), "grab_focus",
7274 G_CALLBACK(compose_grab_focus_cb), compose);
7275 g_signal_connect(G_OBJECT(buffer), "insert_text",
7276 G_CALLBACK(text_inserted), compose);
7277 g_signal_connect(G_OBJECT(text), "button_press_event",
7278 G_CALLBACK(text_clicked), compose);
7280 g_signal_connect(G_OBJECT(text), "popup-menu",
7281 G_CALLBACK(compose_popup_menu), compose);
7283 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7284 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7285 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7286 G_CALLBACK(compose_popup_menu), compose);
7288 g_signal_connect(G_OBJECT(subject_entry), "changed",
7289 G_CALLBACK(compose_changed_cb), compose);
7292 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7293 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7294 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7295 g_signal_connect(G_OBJECT(text), "drag_data_received",
7296 G_CALLBACK(compose_insert_drag_received_cb),
7298 g_signal_connect(G_OBJECT(text), "drag-drop",
7299 G_CALLBACK(compose_drag_drop),
7301 gtk_widget_show_all(vbox);
7303 /* pane between attach clist and text */
7304 paned = gtk_vpaned_new();
7305 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7307 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7308 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7310 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7312 gtk_paned_add1(GTK_PANED(paned), notebook);
7313 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7314 gtk_widget_show_all(paned);
7317 if (prefs_common.textfont) {
7318 PangoFontDescription *font_desc;
7320 font_desc = pango_font_description_from_string
7321 (prefs_common.textfont);
7323 gtk_widget_modify_font(text, font_desc);
7324 pango_font_description_free(font_desc);
7328 gtk_action_group_add_actions(action_group, compose_popup_entries,
7329 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7337 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7339 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7340 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7341 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7343 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7345 undostruct = undo_init(text);
7346 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7349 address_completion_start(window);
7351 compose->window = window;
7352 compose->vbox = vbox;
7353 compose->menubar = menubar;
7354 compose->handlebox = handlebox;
7356 compose->vbox2 = vbox2;
7358 compose->paned = paned;
7360 compose->attach_label = attach_lab2;
7362 compose->notebook = notebook;
7363 compose->edit_vbox = edit_vbox;
7364 compose->ruler_hbox = ruler_hbox;
7365 compose->ruler = ruler;
7366 compose->scrolledwin = scrolledwin;
7367 compose->text = text;
7369 compose->focused_editable = NULL;
7371 compose->popupmenu = popupmenu;
7373 compose->tmpl_menu = tmpl_menu;
7375 compose->mode = mode;
7376 compose->rmode = mode;
7378 compose->targetinfo = NULL;
7379 compose->replyinfo = NULL;
7380 compose->fwdinfo = NULL;
7382 compose->replyto = NULL;
7384 compose->bcc = NULL;
7385 compose->followup_to = NULL;
7387 compose->ml_post = NULL;
7389 compose->inreplyto = NULL;
7390 compose->references = NULL;
7391 compose->msgid = NULL;
7392 compose->boundary = NULL;
7394 compose->autowrap = prefs_common.autowrap;
7395 compose->autoindent = prefs_common.auto_indent;
7396 compose->use_signing = FALSE;
7397 compose->use_encryption = FALSE;
7398 compose->privacy_system = NULL;
7400 compose->modified = FALSE;
7402 compose->return_receipt = FALSE;
7404 compose->to_list = NULL;
7405 compose->newsgroup_list = NULL;
7407 compose->undostruct = undostruct;
7409 compose->sig_str = NULL;
7411 compose->exteditor_file = NULL;
7412 compose->exteditor_pid = -1;
7413 compose->exteditor_tag = -1;
7414 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7417 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7418 if (mode != COMPOSE_REDIRECT) {
7419 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7420 strcmp(prefs_common.dictionary, "")) {
7421 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7422 prefs_common.alt_dictionary,
7423 conv_get_locale_charset_str(),
7424 prefs_common.misspelled_col,
7425 prefs_common.check_while_typing,
7426 prefs_common.recheck_when_changing_dict,
7427 prefs_common.use_alternate,
7428 prefs_common.use_both_dicts,
7429 GTK_TEXT_VIEW(text),
7430 GTK_WINDOW(compose->window),
7431 compose_dict_changed,
7432 compose_spell_menu_changed,
7435 alertpanel_error(_("Spell checker could not "
7437 gtkaspell_checkers_strerror());
7438 gtkaspell_checkers_reset_error();
7440 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7444 compose->gtkaspell = gtkaspell;
7445 compose_spell_menu_changed(compose);
7446 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7449 compose_select_account(compose, account, TRUE);
7451 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7452 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7454 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7455 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7457 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7458 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7460 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7461 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7464 if (account->protocol != A_NNTP)
7465 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7466 prefs_common_translated_header_name("To:"));
7468 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7469 prefs_common_translated_header_name("Newsgroups:"));
7471 addressbook_set_target_compose(compose);
7473 if (mode != COMPOSE_REDIRECT)
7474 compose_set_template_menu(compose);
7476 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7479 compose_list = g_list_append(compose_list, compose);
7481 if (!prefs_common.show_ruler)
7482 gtk_widget_hide(ruler_hbox);
7484 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7487 compose->priority = PRIORITY_NORMAL;
7488 compose_update_priority_menu_item(compose);
7490 compose_set_out_encoding(compose);
7493 compose_update_actions_menu(compose);
7495 /* Privacy Systems menu */
7496 compose_update_privacy_systems_menu(compose);
7498 activate_privacy_system(compose, account, TRUE);
7499 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7501 gtk_widget_realize(window);
7503 gtk_widget_show(window);
7505 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7506 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7513 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7518 GtkWidget *optmenubox;
7521 GtkWidget *from_name = NULL;
7522 #if !(GTK_CHECK_VERSION(2,12,0))
7523 GtkTooltips *tips = compose->tooltips;
7526 gint num = 0, def_menu = 0;
7528 accounts = account_get_list();
7529 cm_return_val_if_fail(accounts != NULL, NULL);
7531 optmenubox = gtk_event_box_new();
7532 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7533 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7535 hbox = gtk_hbox_new(FALSE, 6);
7536 from_name = gtk_entry_new();
7538 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7539 G_CALLBACK(compose_grab_focus_cb), compose);
7541 for (; accounts != NULL; accounts = accounts->next, num++) {
7542 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7543 gchar *name, *from = NULL;
7545 if (ac == compose->account) def_menu = num;
7547 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7550 if (ac == compose->account) {
7551 if (ac->name && *ac->name) {
7553 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7554 from = g_strdup_printf("%s <%s>",
7556 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7558 from = g_strdup_printf("%s",
7560 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7563 COMBOBOX_ADD(menu, name, ac->account_id);
7568 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7570 g_signal_connect(G_OBJECT(optmenu), "changed",
7571 G_CALLBACK(account_activated),
7573 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7574 G_CALLBACK(compose_entry_popup_extend),
7577 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7578 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7580 CLAWS_SET_TIP(optmenubox,
7581 _("Account to use for this email"));
7582 CLAWS_SET_TIP(from_name,
7583 _("Sender address to be used"));
7585 compose->from_name = from_name;
7590 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7592 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7593 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7594 Compose *compose = (Compose *) data;
7596 compose->priority = value;
7600 static void compose_reply_change_mode(Compose *compose,
7603 gboolean was_modified = compose->modified;
7605 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7607 cm_return_if_fail(compose->replyinfo != NULL);
7609 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7611 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7613 if (action == COMPOSE_REPLY_TO_ALL)
7615 if (action == COMPOSE_REPLY_TO_SENDER)
7617 if (action == COMPOSE_REPLY_TO_LIST)
7620 compose_remove_header_entries(compose);
7621 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7622 if (compose->account->set_autocc && compose->account->auto_cc)
7623 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7625 if (compose->account->set_autobcc && compose->account->auto_bcc)
7626 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7628 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7629 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7630 compose_show_first_last_header(compose, TRUE);
7631 compose->modified = was_modified;
7632 compose_set_title(compose);
7635 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7637 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7638 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7639 Compose *compose = (Compose *) data;
7642 compose_reply_change_mode(compose, value);
7645 static void compose_update_priority_menu_item(Compose * compose)
7647 GtkWidget *menuitem = NULL;
7648 switch (compose->priority) {
7649 case PRIORITY_HIGHEST:
7650 menuitem = gtk_ui_manager_get_widget
7651 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7654 menuitem = gtk_ui_manager_get_widget
7655 (compose->ui_manager, "/Menu/Options/Priority/High");
7657 case PRIORITY_NORMAL:
7658 menuitem = gtk_ui_manager_get_widget
7659 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7662 menuitem = gtk_ui_manager_get_widget
7663 (compose->ui_manager, "/Menu/Options/Priority/Low");
7665 case PRIORITY_LOWEST:
7666 menuitem = gtk_ui_manager_get_widget
7667 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7670 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7673 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7675 Compose *compose = (Compose *) data;
7677 gboolean can_sign = FALSE, can_encrypt = FALSE;
7679 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7681 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7684 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7685 g_free(compose->privacy_system);
7686 compose->privacy_system = NULL;
7687 if (systemid != NULL) {
7688 compose->privacy_system = g_strdup(systemid);
7690 can_sign = privacy_system_can_sign(systemid);
7691 can_encrypt = privacy_system_can_encrypt(systemid);
7694 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7696 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7697 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7700 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7702 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7703 GtkWidget *menuitem = NULL;
7705 gboolean can_sign = FALSE, can_encrypt = FALSE;
7706 gboolean found = FALSE;
7708 if (compose->privacy_system != NULL) {
7710 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7711 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7712 cm_return_if_fail(menuitem != NULL);
7714 amenu = GTK_MENU_SHELL(menuitem)->children;
7716 while (amenu != NULL) {
7717 GList *alist = amenu->next;
7719 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7720 if (systemid != NULL) {
7721 if (strcmp(systemid, compose->privacy_system) == 0 &&
7722 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7723 menuitem = GTK_WIDGET(amenu->data);
7725 can_sign = privacy_system_can_sign(systemid);
7726 can_encrypt = privacy_system_can_encrypt(systemid);
7730 } else if (strlen(compose->privacy_system) == 0 &&
7731 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7732 menuitem = GTK_WIDGET(amenu->data);
7735 can_encrypt = FALSE;
7742 if (menuitem != NULL)
7743 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7745 if (warn && !found && strlen(compose->privacy_system)) {
7746 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7747 "will not be able to sign or encrypt this message."),
7748 compose->privacy_system);
7752 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7753 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7756 static void compose_set_out_encoding(Compose *compose)
7758 CharSet out_encoding;
7759 const gchar *branch = NULL;
7760 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7762 switch(out_encoding) {
7763 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7764 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7765 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7766 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7767 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7768 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7769 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7770 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7771 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7772 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7773 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7774 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7775 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7776 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7777 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7778 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7779 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7780 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7781 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7782 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7783 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7784 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7785 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7786 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7787 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7788 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7789 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7790 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7791 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7792 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7793 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7794 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7796 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7799 static void compose_set_template_menu(Compose *compose)
7801 GSList *tmpl_list, *cur;
7805 tmpl_list = template_get_config();
7807 menu = gtk_menu_new();
7809 gtk_menu_set_accel_group (GTK_MENU (menu),
7810 gtk_ui_manager_get_accel_group(compose->ui_manager));
7811 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7812 Template *tmpl = (Template *)cur->data;
7813 gchar *accel_path = NULL;
7814 item = gtk_menu_item_new_with_label(tmpl->name);
7815 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7816 g_signal_connect(G_OBJECT(item), "activate",
7817 G_CALLBACK(compose_template_activate_cb),
7819 g_object_set_data(G_OBJECT(item), "template", tmpl);
7820 gtk_widget_show(item);
7821 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7822 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7826 gtk_widget_show(menu);
7827 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7830 void compose_update_actions_menu(Compose *compose)
7832 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7835 static void compose_update_privacy_systems_menu(Compose *compose)
7837 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7838 GSList *systems, *cur;
7840 GtkWidget *system_none;
7842 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7843 GtkWidget *privacy_menu = gtk_menu_new();
7845 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7846 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7848 g_signal_connect(G_OBJECT(system_none), "activate",
7849 G_CALLBACK(compose_set_privacy_system_cb), compose);
7851 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7852 gtk_widget_show(system_none);
7854 systems = privacy_get_system_ids();
7855 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7856 gchar *systemid = cur->data;
7858 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7859 widget = gtk_radio_menu_item_new_with_label(group,
7860 privacy_system_get_name(systemid));
7861 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7862 g_strdup(systemid), g_free);
7863 g_signal_connect(G_OBJECT(widget), "activate",
7864 G_CALLBACK(compose_set_privacy_system_cb), compose);
7866 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7867 gtk_widget_show(widget);
7870 g_slist_free(systems);
7871 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7872 gtk_widget_show_all(privacy_menu);
7873 gtk_widget_show_all(privacy_menuitem);
7876 void compose_reflect_prefs_all(void)
7881 for (cur = compose_list; cur != NULL; cur = cur->next) {
7882 compose = (Compose *)cur->data;
7883 compose_set_template_menu(compose);
7887 void compose_reflect_prefs_pixmap_theme(void)
7892 for (cur = compose_list; cur != NULL; cur = cur->next) {
7893 compose = (Compose *)cur->data;
7894 toolbar_update(TOOLBAR_COMPOSE, compose);
7898 static const gchar *compose_quote_char_from_context(Compose *compose)
7900 const gchar *qmark = NULL;
7902 cm_return_val_if_fail(compose != NULL, NULL);
7904 switch (compose->mode) {
7905 /* use forward-specific quote char */
7906 case COMPOSE_FORWARD:
7907 case COMPOSE_FORWARD_AS_ATTACH:
7908 case COMPOSE_FORWARD_INLINE:
7909 if (compose->folder && compose->folder->prefs &&
7910 compose->folder->prefs->forward_with_format)
7911 qmark = compose->folder->prefs->forward_quotemark;
7912 else if (compose->account->forward_with_format)
7913 qmark = compose->account->forward_quotemark;
7915 qmark = prefs_common.fw_quotemark;
7918 /* use reply-specific quote char in all other modes */
7920 if (compose->folder && compose->folder->prefs &&
7921 compose->folder->prefs->reply_with_format)
7922 qmark = compose->folder->prefs->reply_quotemark;
7923 else if (compose->account->reply_with_format)
7924 qmark = compose->account->reply_quotemark;
7926 qmark = prefs_common.quotemark;
7930 if (qmark == NULL || *qmark == '\0')
7936 static void compose_template_apply(Compose *compose, Template *tmpl,
7940 GtkTextBuffer *buffer;
7944 gchar *parsed_str = NULL;
7945 gint cursor_pos = 0;
7946 const gchar *err_msg = _("The body of the template has an error at line %d.");
7949 /* process the body */
7951 text = GTK_TEXT_VIEW(compose->text);
7952 buffer = gtk_text_view_get_buffer(text);
7955 qmark = compose_quote_char_from_context(compose);
7957 if (compose->replyinfo != NULL) {
7960 gtk_text_buffer_set_text(buffer, "", -1);
7961 mark = gtk_text_buffer_get_insert(buffer);
7962 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7964 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7965 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7967 } else if (compose->fwdinfo != NULL) {
7970 gtk_text_buffer_set_text(buffer, "", -1);
7971 mark = gtk_text_buffer_get_insert(buffer);
7972 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7974 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7975 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7978 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7980 GtkTextIter start, end;
7983 gtk_text_buffer_get_start_iter(buffer, &start);
7984 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7985 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7987 /* clear the buffer now */
7989 gtk_text_buffer_set_text(buffer, "", -1);
7991 parsed_str = compose_quote_fmt(compose, dummyinfo,
7992 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7993 procmsg_msginfo_free( dummyinfo );
7999 gtk_text_buffer_set_text(buffer, "", -1);
8000 mark = gtk_text_buffer_get_insert(buffer);
8001 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8004 if (replace && parsed_str && compose->account->auto_sig)
8005 compose_insert_sig(compose, FALSE);
8007 if (replace && parsed_str) {
8008 gtk_text_buffer_get_start_iter(buffer, &iter);
8009 gtk_text_buffer_place_cursor(buffer, &iter);
8013 cursor_pos = quote_fmt_get_cursor_pos();
8014 compose->set_cursor_pos = cursor_pos;
8015 if (cursor_pos == -1)
8017 gtk_text_buffer_get_start_iter(buffer, &iter);
8018 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8019 gtk_text_buffer_place_cursor(buffer, &iter);
8022 /* process the other fields */
8024 compose_template_apply_fields(compose, tmpl);
8025 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8026 quote_fmt_reset_vartable();
8027 compose_changed_cb(NULL, compose);
8030 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8032 MsgInfo* dummyinfo = NULL;
8033 MsgInfo *msginfo = NULL;
8036 if (compose->replyinfo != NULL)
8037 msginfo = compose->replyinfo;
8038 else if (compose->fwdinfo != NULL)
8039 msginfo = compose->fwdinfo;
8041 dummyinfo = compose_msginfo_new_from_compose(compose);
8042 msginfo = dummyinfo;
8045 if (tmpl->from && *tmpl->from != '\0') {
8047 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8048 compose->gtkaspell);
8050 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8052 quote_fmt_scan_string(tmpl->from);
8055 buf = quote_fmt_get_buffer();
8057 alertpanel_error(_("Template From format error."));
8059 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8063 if (tmpl->to && *tmpl->to != '\0') {
8065 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8066 compose->gtkaspell);
8068 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8070 quote_fmt_scan_string(tmpl->to);
8073 buf = quote_fmt_get_buffer();
8075 alertpanel_error(_("Template To format error."));
8077 compose_entry_append(compose, buf, COMPOSE_TO);
8081 if (tmpl->cc && *tmpl->cc != '\0') {
8083 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8084 compose->gtkaspell);
8086 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8088 quote_fmt_scan_string(tmpl->cc);
8091 buf = quote_fmt_get_buffer();
8093 alertpanel_error(_("Template Cc format error."));
8095 compose_entry_append(compose, buf, COMPOSE_CC);
8099 if (tmpl->bcc && *tmpl->bcc != '\0') {
8101 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8102 compose->gtkaspell);
8104 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8106 quote_fmt_scan_string(tmpl->bcc);
8109 buf = quote_fmt_get_buffer();
8111 alertpanel_error(_("Template Bcc format error."));
8113 compose_entry_append(compose, buf, COMPOSE_BCC);
8117 /* process the subject */
8118 if (tmpl->subject && *tmpl->subject != '\0') {
8120 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8121 compose->gtkaspell);
8123 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8125 quote_fmt_scan_string(tmpl->subject);
8128 buf = quote_fmt_get_buffer();
8130 alertpanel_error(_("Template subject format error."));
8132 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8136 procmsg_msginfo_free( dummyinfo );
8139 static void compose_destroy(Compose *compose)
8141 GtkTextBuffer *buffer;
8142 GtkClipboard *clipboard;
8144 compose_list = g_list_remove(compose_list, compose);
8146 if (compose->updating) {
8147 debug_print("danger, not destroying anything now\n");
8148 compose->deferred_destroy = TRUE;
8151 /* NOTE: address_completion_end() does nothing with the window
8152 * however this may change. */
8153 address_completion_end(compose->window);
8155 slist_free_strings(compose->to_list);
8156 g_slist_free(compose->to_list);
8157 slist_free_strings(compose->newsgroup_list);
8158 g_slist_free(compose->newsgroup_list);
8159 slist_free_strings(compose->header_list);
8160 g_slist_free(compose->header_list);
8162 procmsg_msginfo_free(compose->targetinfo);
8163 procmsg_msginfo_free(compose->replyinfo);
8164 procmsg_msginfo_free(compose->fwdinfo);
8166 g_free(compose->replyto);
8167 g_free(compose->cc);
8168 g_free(compose->bcc);
8169 g_free(compose->newsgroups);
8170 g_free(compose->followup_to);
8172 g_free(compose->ml_post);
8174 g_free(compose->inreplyto);
8175 g_free(compose->references);
8176 g_free(compose->msgid);
8177 g_free(compose->boundary);
8179 g_free(compose->redirect_filename);
8180 if (compose->undostruct)
8181 undo_destroy(compose->undostruct);
8183 g_free(compose->sig_str);
8185 g_free(compose->exteditor_file);
8187 g_free(compose->orig_charset);
8189 g_free(compose->privacy_system);
8191 if (addressbook_get_target_compose() == compose)
8192 addressbook_set_target_compose(NULL);
8195 if (compose->gtkaspell) {
8196 gtkaspell_delete(compose->gtkaspell);
8197 compose->gtkaspell = NULL;
8201 if (!compose->batch) {
8202 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8203 prefs_common.compose_height = compose->window->allocation.height;
8206 if (!gtk_widget_get_parent(compose->paned))
8207 gtk_widget_destroy(compose->paned);
8208 gtk_widget_destroy(compose->popupmenu);
8210 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8211 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8212 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8214 gtk_widget_destroy(compose->window);
8215 toolbar_destroy(compose->toolbar);
8216 g_free(compose->toolbar);
8217 g_mutex_free(compose->mutex);
8221 static void compose_attach_info_free(AttachInfo *ainfo)
8223 g_free(ainfo->file);
8224 g_free(ainfo->content_type);
8225 g_free(ainfo->name);
8229 static void compose_attach_update_label(Compose *compose)
8234 GtkTreeModel *model;
8239 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8240 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8241 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8245 while(gtk_tree_model_iter_next(model, &iter))
8248 text = g_strdup_printf("(%d)", i);
8249 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8253 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8255 Compose *compose = (Compose *)data;
8256 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8257 GtkTreeSelection *selection;
8259 GtkTreeModel *model;
8261 selection = gtk_tree_view_get_selection(tree_view);
8262 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8267 for (cur = sel; cur != NULL; cur = cur->next) {
8268 GtkTreePath *path = cur->data;
8269 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8272 gtk_tree_path_free(path);
8275 for (cur = sel; cur != NULL; cur = cur->next) {
8276 GtkTreeRowReference *ref = cur->data;
8277 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8280 if (gtk_tree_model_get_iter(model, &iter, path))
8281 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8283 gtk_tree_path_free(path);
8284 gtk_tree_row_reference_free(ref);
8288 compose_attach_update_label(compose);
8291 static struct _AttachProperty
8294 GtkWidget *mimetype_entry;
8295 GtkWidget *encoding_optmenu;
8296 GtkWidget *path_entry;
8297 GtkWidget *filename_entry;
8299 GtkWidget *cancel_btn;
8302 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8304 gtk_tree_path_free((GtkTreePath *)ptr);
8307 static void compose_attach_property(GtkAction *action, gpointer data)
8309 Compose *compose = (Compose *)data;
8310 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8312 GtkComboBox *optmenu;
8313 GtkTreeSelection *selection;
8315 GtkTreeModel *model;
8318 static gboolean cancelled;
8320 /* only if one selected */
8321 selection = gtk_tree_view_get_selection(tree_view);
8322 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8325 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8329 path = (GtkTreePath *) sel->data;
8330 gtk_tree_model_get_iter(model, &iter, path);
8331 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8334 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8340 if (!attach_prop.window)
8341 compose_attach_property_create(&cancelled);
8342 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8343 gtk_widget_grab_focus(attach_prop.ok_btn);
8344 gtk_widget_show(attach_prop.window);
8345 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8347 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8348 if (ainfo->encoding == ENC_UNKNOWN)
8349 combobox_select_by_data(optmenu, ENC_BASE64);
8351 combobox_select_by_data(optmenu, ainfo->encoding);
8353 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8354 ainfo->content_type ? ainfo->content_type : "");
8355 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8356 ainfo->file ? ainfo->file : "");
8357 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8358 ainfo->name ? ainfo->name : "");
8361 const gchar *entry_text;
8363 gchar *cnttype = NULL;
8370 gtk_widget_hide(attach_prop.window);
8371 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8376 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8377 if (*entry_text != '\0') {
8380 text = g_strstrip(g_strdup(entry_text));
8381 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8382 cnttype = g_strdup(text);
8385 alertpanel_error(_("Invalid MIME type."));
8391 ainfo->encoding = combobox_get_active_data(optmenu);
8393 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8394 if (*entry_text != '\0') {
8395 if (is_file_exist(entry_text) &&
8396 (size = get_file_size(entry_text)) > 0)
8397 file = g_strdup(entry_text);
8400 (_("File doesn't exist or is empty."));
8406 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8407 if (*entry_text != '\0') {
8408 g_free(ainfo->name);
8409 ainfo->name = g_strdup(entry_text);
8413 g_free(ainfo->content_type);
8414 ainfo->content_type = cnttype;
8417 g_free(ainfo->file);
8421 ainfo->size = (goffset)size;
8423 /* update tree store */
8424 text = to_human_readable(ainfo->size);
8425 gtk_tree_model_get_iter(model, &iter, path);
8426 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8427 COL_MIMETYPE, ainfo->content_type,
8429 COL_NAME, ainfo->name,
8435 gtk_tree_path_free(path);
8438 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8440 label = gtk_label_new(str); \
8441 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8442 GTK_FILL, 0, 0, 0); \
8443 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8445 entry = gtk_entry_new(); \
8446 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8447 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8450 static void compose_attach_property_create(gboolean *cancelled)
8456 GtkWidget *mimetype_entry;
8459 GtkListStore *optmenu_menu;
8460 GtkWidget *path_entry;
8461 GtkWidget *filename_entry;
8464 GtkWidget *cancel_btn;
8465 GList *mime_type_list, *strlist;
8468 debug_print("Creating attach_property window...\n");
8470 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8471 gtk_widget_set_size_request(window, 480, -1);
8472 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8473 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8474 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8475 g_signal_connect(G_OBJECT(window), "delete_event",
8476 G_CALLBACK(attach_property_delete_event),
8478 g_signal_connect(G_OBJECT(window), "key_press_event",
8479 G_CALLBACK(attach_property_key_pressed),
8482 vbox = gtk_vbox_new(FALSE, 8);
8483 gtk_container_add(GTK_CONTAINER(window), vbox);
8485 table = gtk_table_new(4, 2, FALSE);
8486 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8487 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8488 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8490 label = gtk_label_new(_("MIME type"));
8491 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8493 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8494 mimetype_entry = gtk_combo_box_entry_new_text();
8495 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8496 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8498 /* stuff with list */
8499 mime_type_list = procmime_get_mime_type_list();
8501 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8502 MimeType *type = (MimeType *) mime_type_list->data;
8505 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8507 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8510 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8511 (GCompareFunc)strcmp2);
8514 for (mime_type_list = strlist; mime_type_list != NULL;
8515 mime_type_list = mime_type_list->next) {
8516 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8517 g_free(mime_type_list->data);
8519 g_list_free(strlist);
8520 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8521 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8523 label = gtk_label_new(_("Encoding"));
8524 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8526 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8528 hbox = gtk_hbox_new(FALSE, 0);
8529 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8530 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8532 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8533 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8535 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8536 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8537 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8538 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8539 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8541 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8543 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8544 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8546 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8547 &ok_btn, GTK_STOCK_OK,
8549 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8550 gtk_widget_grab_default(ok_btn);
8552 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8553 G_CALLBACK(attach_property_ok),
8555 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8556 G_CALLBACK(attach_property_cancel),
8559 gtk_widget_show_all(vbox);
8561 attach_prop.window = window;
8562 attach_prop.mimetype_entry = mimetype_entry;
8563 attach_prop.encoding_optmenu = optmenu;
8564 attach_prop.path_entry = path_entry;
8565 attach_prop.filename_entry = filename_entry;
8566 attach_prop.ok_btn = ok_btn;
8567 attach_prop.cancel_btn = cancel_btn;
8570 #undef SET_LABEL_AND_ENTRY
8572 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8578 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8584 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8585 gboolean *cancelled)
8593 static gboolean attach_property_key_pressed(GtkWidget *widget,
8595 gboolean *cancelled)
8597 if (event && event->keyval == GDK_Escape) {
8601 if (event && event->keyval == GDK_Return) {
8609 static void compose_exec_ext_editor(Compose *compose)
8616 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8617 G_DIR_SEPARATOR, compose);
8619 if (pipe(pipe_fds) < 0) {
8625 if ((pid = fork()) < 0) {
8632 /* close the write side of the pipe */
8635 compose->exteditor_file = g_strdup(tmp);
8636 compose->exteditor_pid = pid;
8638 compose_set_ext_editor_sensitive(compose, FALSE);
8641 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8643 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8645 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8649 } else { /* process-monitoring process */
8655 /* close the read side of the pipe */
8658 if (compose_write_body_to_file(compose, tmp) < 0) {
8659 fd_write_all(pipe_fds[1], "2\n", 2);
8663 pid_ed = compose_exec_ext_editor_real(tmp);
8665 fd_write_all(pipe_fds[1], "1\n", 2);
8669 /* wait until editor is terminated */
8670 waitpid(pid_ed, NULL, 0);
8672 fd_write_all(pipe_fds[1], "0\n", 2);
8679 #endif /* G_OS_UNIX */
8683 static gint compose_exec_ext_editor_real(const gchar *file)
8690 cm_return_val_if_fail(file != NULL, -1);
8692 if ((pid = fork()) < 0) {
8697 if (pid != 0) return pid;
8699 /* grandchild process */
8701 if (setpgid(0, getppid()))
8704 if (prefs_common_get_ext_editor_cmd() &&
8705 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8706 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8707 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8709 if (prefs_common_get_ext_editor_cmd())
8710 g_warning("External editor command-line is invalid: '%s'\n",
8711 prefs_common_get_ext_editor_cmd());
8712 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8715 cmdline = strsplit_with_quote(buf, " ", 1024);
8716 execvp(cmdline[0], cmdline);
8719 g_strfreev(cmdline);
8724 static gboolean compose_ext_editor_kill(Compose *compose)
8726 pid_t pgid = compose->exteditor_pid * -1;
8729 ret = kill(pgid, 0);
8731 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8735 msg = g_strdup_printf
8736 (_("The external editor is still working.\n"
8737 "Force terminating the process?\n"
8738 "process group id: %d"), -pgid);
8739 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8740 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8744 if (val == G_ALERTALTERNATE) {
8745 g_source_remove(compose->exteditor_tag);
8746 g_io_channel_shutdown(compose->exteditor_ch,
8748 g_io_channel_unref(compose->exteditor_ch);
8750 if (kill(pgid, SIGTERM) < 0) perror("kill");
8751 waitpid(compose->exteditor_pid, NULL, 0);
8753 g_warning("Terminated process group id: %d", -pgid);
8754 g_warning("Temporary file: %s",
8755 compose->exteditor_file);
8757 compose_set_ext_editor_sensitive(compose, TRUE);
8759 g_free(compose->exteditor_file);
8760 compose->exteditor_file = NULL;
8761 compose->exteditor_pid = -1;
8762 compose->exteditor_ch = NULL;
8763 compose->exteditor_tag = -1;
8771 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8775 Compose *compose = (Compose *)data;
8778 debug_print(_("Compose: input from monitoring process\n"));
8780 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8782 g_io_channel_shutdown(source, FALSE, NULL);
8783 g_io_channel_unref(source);
8785 waitpid(compose->exteditor_pid, NULL, 0);
8787 if (buf[0] == '0') { /* success */
8788 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8789 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8791 gtk_text_buffer_set_text(buffer, "", -1);
8792 compose_insert_file(compose, compose->exteditor_file);
8793 compose_changed_cb(NULL, compose);
8794 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8796 if (claws_unlink(compose->exteditor_file) < 0)
8797 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8798 } else if (buf[0] == '1') { /* failed */
8799 g_warning("Couldn't exec external editor\n");
8800 if (claws_unlink(compose->exteditor_file) < 0)
8801 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8802 } else if (buf[0] == '2') {
8803 g_warning("Couldn't write to file\n");
8804 } else if (buf[0] == '3') {
8805 g_warning("Pipe read failed\n");
8808 compose_set_ext_editor_sensitive(compose, TRUE);
8810 g_free(compose->exteditor_file);
8811 compose->exteditor_file = NULL;
8812 compose->exteditor_pid = -1;
8813 compose->exteditor_ch = NULL;
8814 compose->exteditor_tag = -1;
8819 static void compose_set_ext_editor_sensitive(Compose *compose,
8822 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8823 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8824 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8825 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8826 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8827 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8828 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8830 gtk_widget_set_sensitive(compose->text, sensitive);
8831 if (compose->toolbar->send_btn)
8832 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8833 if (compose->toolbar->sendl_btn)
8834 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8835 if (compose->toolbar->draft_btn)
8836 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8837 if (compose->toolbar->insert_btn)
8838 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8839 if (compose->toolbar->sig_btn)
8840 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8841 if (compose->toolbar->exteditor_btn)
8842 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8843 if (compose->toolbar->linewrap_current_btn)
8844 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8845 if (compose->toolbar->linewrap_all_btn)
8846 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8848 #endif /* G_OS_UNIX */
8851 * compose_undo_state_changed:
8853 * Change the sensivity of the menuentries undo and redo
8855 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8856 gint redo_state, gpointer data)
8858 Compose *compose = (Compose *)data;
8860 switch (undo_state) {
8861 case UNDO_STATE_TRUE:
8862 if (!undostruct->undo_state) {
8863 undostruct->undo_state = TRUE;
8864 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8867 case UNDO_STATE_FALSE:
8868 if (undostruct->undo_state) {
8869 undostruct->undo_state = FALSE;
8870 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8873 case UNDO_STATE_UNCHANGED:
8875 case UNDO_STATE_REFRESH:
8876 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8879 g_warning("Undo state not recognized");
8883 switch (redo_state) {
8884 case UNDO_STATE_TRUE:
8885 if (!undostruct->redo_state) {
8886 undostruct->redo_state = TRUE;
8887 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8890 case UNDO_STATE_FALSE:
8891 if (undostruct->redo_state) {
8892 undostruct->redo_state = FALSE;
8893 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8896 case UNDO_STATE_UNCHANGED:
8898 case UNDO_STATE_REFRESH:
8899 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8902 g_warning("Redo state not recognized");
8907 /* callback functions */
8909 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8910 * includes "non-client" (windows-izm) in calculation, so this calculation
8911 * may not be accurate.
8913 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8914 GtkAllocation *allocation,
8915 GtkSHRuler *shruler)
8917 if (prefs_common.show_ruler) {
8918 gint char_width = 0, char_height = 0;
8919 gint line_width_in_chars;
8921 gtkut_get_font_size(GTK_WIDGET(widget),
8922 &char_width, &char_height);
8923 line_width_in_chars =
8924 (allocation->width - allocation->x) / char_width;
8926 /* got the maximum */
8927 gtk_ruler_set_range(GTK_RULER(shruler),
8928 0.0, line_width_in_chars, 0,
8929 /*line_width_in_chars*/ char_width);
8935 static void account_activated(GtkComboBox *optmenu, gpointer data)
8937 Compose *compose = (Compose *)data;
8940 gchar *folderidentifier;
8941 gint account_id = 0;
8945 /* Get ID of active account in the combo box */
8946 menu = gtk_combo_box_get_model(optmenu);
8947 gtk_combo_box_get_active_iter(optmenu, &iter);
8948 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8950 ac = account_find_from_id(account_id);
8951 cm_return_if_fail(ac != NULL);
8953 if (ac != compose->account)
8954 compose_select_account(compose, ac, FALSE);
8956 /* Set message save folder */
8957 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8958 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8960 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8961 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8963 compose_set_save_to(compose, NULL);
8964 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8965 folderidentifier = folder_item_get_identifier(account_get_special_folder
8966 (compose->account, F_OUTBOX));
8967 compose_set_save_to(compose, folderidentifier);
8968 g_free(folderidentifier);
8972 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8973 GtkTreeViewColumn *column, Compose *compose)
8975 compose_attach_property(NULL, compose);
8978 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8981 Compose *compose = (Compose *)data;
8982 GtkTreeSelection *attach_selection;
8983 gint attach_nr_selected;
8985 if (!event) return FALSE;
8987 if (event->button == 3) {
8988 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8989 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8991 if (attach_nr_selected > 0)
8993 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8994 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8996 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8997 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9000 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9001 NULL, NULL, event->button, event->time);
9008 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9011 Compose *compose = (Compose *)data;
9013 if (!event) return FALSE;
9015 switch (event->keyval) {
9017 compose_attach_remove_selected(NULL, compose);
9023 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9025 toolbar_comp_set_sensitive(compose, allow);
9026 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9027 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9029 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9031 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9032 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9033 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9035 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9039 static void compose_send_cb(GtkAction *action, gpointer data)
9041 Compose *compose = (Compose *)data;
9043 if (prefs_common.work_offline &&
9044 !inc_offline_should_override(TRUE,
9045 _("Claws Mail needs network access in order "
9046 "to send this email.")))
9049 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9050 g_source_remove(compose->draft_timeout_tag);
9051 compose->draft_timeout_tag = -1;
9054 compose_send(compose);
9057 static void compose_send_later_cb(GtkAction *action, gpointer data)
9059 Compose *compose = (Compose *)data;
9063 compose_allow_user_actions(compose, FALSE);
9064 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9065 compose_allow_user_actions(compose, TRUE);
9069 compose_close(compose);
9070 } else if (val == -1) {
9071 alertpanel_error(_("Could not queue message."));
9072 } else if (val == -2) {
9073 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9074 } else if (val == -3) {
9075 if (privacy_peek_error())
9076 alertpanel_error(_("Could not queue message for sending:\n\n"
9077 "Signature failed: %s"), privacy_get_error());
9078 } else if (val == -4) {
9079 alertpanel_error(_("Could not queue message for sending:\n\n"
9080 "Charset conversion failed."));
9081 } else if (val == -5) {
9082 alertpanel_error(_("Could not queue message for sending:\n\n"
9083 "Couldn't get recipient encryption key."));
9084 } else if (val == -6) {
9087 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9090 #define DRAFTED_AT_EXIT "drafted_at_exit"
9091 static void compose_register_draft(MsgInfo *info)
9093 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9094 DRAFTED_AT_EXIT, NULL);
9095 FILE *fp = g_fopen(filepath, "ab");
9098 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9106 gboolean compose_draft (gpointer data, guint action)
9108 Compose *compose = (Compose *)data;
9112 MsgFlags flag = {0, 0};
9113 static gboolean lock = FALSE;
9114 MsgInfo *newmsginfo;
9116 gboolean target_locked = FALSE;
9117 gboolean err = FALSE;
9119 if (lock) return FALSE;
9121 if (compose->sending)
9124 draft = account_get_special_folder(compose->account, F_DRAFT);
9125 cm_return_val_if_fail(draft != NULL, FALSE);
9127 if (!g_mutex_trylock(compose->mutex)) {
9128 /* we don't want to lock the mutex once it's available,
9129 * because as the only other part of compose.c locking
9130 * it is compose_close - which means once unlocked,
9131 * the compose struct will be freed */
9132 debug_print("couldn't lock mutex, probably sending\n");
9138 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9139 G_DIR_SEPARATOR, compose);
9140 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9141 FILE_OP_ERROR(tmp, "fopen");
9145 /* chmod for security */
9146 if (change_file_mode_rw(fp, tmp) < 0) {
9147 FILE_OP_ERROR(tmp, "chmod");
9148 g_warning("can't change file mode\n");
9151 /* Save draft infos */
9152 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9153 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9155 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9156 gchar *savefolderid;
9158 savefolderid = compose_get_save_to(compose);
9159 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9160 g_free(savefolderid);
9162 if (compose->return_receipt) {
9163 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9165 if (compose->privacy_system) {
9166 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9167 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9168 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9171 /* Message-ID of message replying to */
9172 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9175 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9176 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9179 /* Message-ID of message forwarding to */
9180 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9183 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9184 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9188 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9189 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9191 /* end of headers */
9192 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9199 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9203 if (fclose(fp) == EOF) {
9207 if (compose->targetinfo) {
9208 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9209 flag.perm_flags = target_locked?MSG_LOCKED:0;
9211 flag.tmp_flags = MSG_DRAFT;
9213 folder_item_scan(draft);
9214 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9215 MsgInfo *tmpinfo = NULL;
9216 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9217 if (compose->msgid) {
9218 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9221 msgnum = tmpinfo->msgnum;
9222 procmsg_msginfo_free(tmpinfo);
9223 debug_print("got draft msgnum %d from scanning\n", msgnum);
9225 debug_print("didn't get draft msgnum after scanning\n");
9228 debug_print("got draft msgnum %d from adding\n", msgnum);
9234 if (action != COMPOSE_AUTO_SAVE) {
9235 if (action != COMPOSE_DRAFT_FOR_EXIT)
9236 alertpanel_error(_("Could not save draft."));
9239 gtkut_window_popup(compose->window);
9240 val = alertpanel_full(_("Could not save draft"),
9241 _("Could not save draft.\n"
9242 "Do you want to cancel exit or discard this email?"),
9243 _("_Cancel exit"), _("_Discard email"), NULL,
9244 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9245 if (val == G_ALERTALTERNATE) {
9247 g_mutex_unlock(compose->mutex); /* must be done before closing */
9248 compose_close(compose);
9252 g_mutex_unlock(compose->mutex); /* must be done before closing */
9261 if (compose->mode == COMPOSE_REEDIT) {
9262 compose_remove_reedit_target(compose, TRUE);
9265 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9268 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9270 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9272 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9273 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9274 procmsg_msginfo_set_flags(newmsginfo, 0,
9275 MSG_HAS_ATTACHMENT);
9277 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9278 compose_register_draft(newmsginfo);
9280 procmsg_msginfo_free(newmsginfo);
9283 folder_item_scan(draft);
9285 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9287 g_mutex_unlock(compose->mutex); /* must be done before closing */
9288 compose_close(compose);
9294 path = folder_item_fetch_msg(draft, msgnum);
9296 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9299 if (g_stat(path, &s) < 0) {
9300 FILE_OP_ERROR(path, "stat");
9306 procmsg_msginfo_free(compose->targetinfo);
9307 compose->targetinfo = procmsg_msginfo_new();
9308 compose->targetinfo->msgnum = msgnum;
9309 compose->targetinfo->size = (goffset)s.st_size;
9310 compose->targetinfo->mtime = s.st_mtime;
9311 compose->targetinfo->folder = draft;
9313 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9314 compose->mode = COMPOSE_REEDIT;
9316 if (action == COMPOSE_AUTO_SAVE) {
9317 compose->autosaved_draft = compose->targetinfo;
9319 compose->modified = FALSE;
9320 compose_set_title(compose);
9324 g_mutex_unlock(compose->mutex);
9328 void compose_clear_exit_drafts(void)
9330 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9331 DRAFTED_AT_EXIT, NULL);
9332 if (is_file_exist(filepath))
9333 claws_unlink(filepath);
9338 void compose_reopen_exit_drafts(void)
9340 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9341 DRAFTED_AT_EXIT, NULL);
9342 FILE *fp = g_fopen(filepath, "rb");
9346 while (fgets(buf, sizeof(buf), fp)) {
9347 gchar **parts = g_strsplit(buf, "\t", 2);
9348 const gchar *folder = parts[0];
9349 int msgnum = parts[1] ? atoi(parts[1]):-1;
9351 if (folder && *folder && msgnum > -1) {
9352 FolderItem *item = folder_find_item_from_identifier(folder);
9353 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9355 compose_reedit(info, FALSE);
9362 compose_clear_exit_drafts();
9365 static void compose_save_cb(GtkAction *action, gpointer data)
9367 Compose *compose = (Compose *)data;
9368 compose_draft(compose, COMPOSE_KEEP_EDITING);
9369 compose->rmode = COMPOSE_REEDIT;
9372 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9374 if (compose && file_list) {
9377 for ( tmp = file_list; tmp; tmp = tmp->next) {
9378 gchar *file = (gchar *) tmp->data;
9379 gchar *utf8_filename = conv_filename_to_utf8(file);
9380 compose_attach_append(compose, file, utf8_filename, NULL);
9381 compose_changed_cb(NULL, compose);
9386 g_free(utf8_filename);
9391 static void compose_attach_cb(GtkAction *action, gpointer data)
9393 Compose *compose = (Compose *)data;
9396 if (compose->redirect_filename != NULL)
9399 file_list = filesel_select_multiple_files_open(_("Select file"));
9402 compose_attach_from_list(compose, file_list, TRUE);
9403 g_list_free(file_list);
9407 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9409 Compose *compose = (Compose *)data;
9412 file_list = filesel_select_multiple_files_open(_("Select file"));
9417 for ( tmp = file_list; tmp; tmp = tmp->next) {
9418 gchar *file = (gchar *) tmp->data;
9419 gchar *filedup = g_strdup(file);
9420 gchar *shortfile = g_path_get_basename(filedup);
9421 ComposeInsertResult res;
9422 /* insert the file if the file is short or if the user confirmed that
9423 he/she wants to insert the large file */
9424 res = compose_insert_file(compose, file);
9425 if (res == COMPOSE_INSERT_READ_ERROR) {
9426 alertpanel_error(_("File '%s' could not be read."), shortfile);
9427 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9428 alertpanel_error(_("File '%s' contained invalid characters\n"
9429 "for the current encoding, insertion may be incorrect."),
9437 g_list_free(file_list);
9441 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9443 Compose *compose = (Compose *)data;
9445 compose_insert_sig(compose, FALSE);
9448 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9452 Compose *compose = (Compose *)data;
9454 gtkut_widget_get_uposition(widget, &x, &y);
9455 if (!compose->batch) {
9456 prefs_common.compose_x = x;
9457 prefs_common.compose_y = y;
9459 if (compose->sending || compose->updating)
9461 compose_close_cb(NULL, compose);
9465 void compose_close_toolbar(Compose *compose)
9467 compose_close_cb(NULL, compose);
9470 static void compose_close_cb(GtkAction *action, gpointer data)
9472 Compose *compose = (Compose *)data;
9476 if (compose->exteditor_tag != -1) {
9477 if (!compose_ext_editor_kill(compose))
9482 if (compose->modified) {
9483 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9484 if (!g_mutex_trylock(compose->mutex)) {
9485 /* we don't want to lock the mutex once it's available,
9486 * because as the only other part of compose.c locking
9487 * it is compose_close - which means once unlocked,
9488 * the compose struct will be freed */
9489 debug_print("couldn't lock mutex, probably sending\n");
9493 val = alertpanel(_("Discard message"),
9494 _("This message has been modified. Discard it?"),
9495 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9497 val = alertpanel(_("Save changes"),
9498 _("This message has been modified. Save the latest changes?"),
9499 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9501 g_mutex_unlock(compose->mutex);
9503 case G_ALERTDEFAULT:
9504 if (prefs_common.autosave && !reedit)
9505 compose_remove_draft(compose);
9507 case G_ALERTALTERNATE:
9508 compose_draft(data, COMPOSE_QUIT_EDITING);
9515 compose_close(compose);
9518 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9520 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9521 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9522 Compose *compose = (Compose *) data;
9525 compose->out_encoding = (CharSet)value;
9528 static void compose_address_cb(GtkAction *action, gpointer data)
9530 Compose *compose = (Compose *)data;
9532 addressbook_open(compose);
9535 static void about_show_cb(GtkAction *action, gpointer data)
9540 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9542 Compose *compose = (Compose *)data;
9547 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9548 cm_return_if_fail(tmpl != NULL);
9550 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9552 val = alertpanel(_("Apply template"), msg,
9553 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9556 if (val == G_ALERTDEFAULT)
9557 compose_template_apply(compose, tmpl, TRUE);
9558 else if (val == G_ALERTALTERNATE)
9559 compose_template_apply(compose, tmpl, FALSE);
9562 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9564 Compose *compose = (Compose *)data;
9566 compose_exec_ext_editor(compose);
9569 static void compose_undo_cb(GtkAction *action, gpointer data)
9571 Compose *compose = (Compose *)data;
9572 gboolean prev_autowrap = compose->autowrap;
9574 compose->autowrap = FALSE;
9575 undo_undo(compose->undostruct);
9576 compose->autowrap = prev_autowrap;
9579 static void compose_redo_cb(GtkAction *action, gpointer data)
9581 Compose *compose = (Compose *)data;
9582 gboolean prev_autowrap = compose->autowrap;
9584 compose->autowrap = FALSE;
9585 undo_redo(compose->undostruct);
9586 compose->autowrap = prev_autowrap;
9589 static void entry_cut_clipboard(GtkWidget *entry)
9591 if (GTK_IS_EDITABLE(entry))
9592 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9593 else if (GTK_IS_TEXT_VIEW(entry))
9594 gtk_text_buffer_cut_clipboard(
9595 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9596 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9600 static void entry_copy_clipboard(GtkWidget *entry)
9602 if (GTK_IS_EDITABLE(entry))
9603 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9604 else if (GTK_IS_TEXT_VIEW(entry))
9605 gtk_text_buffer_copy_clipboard(
9606 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9607 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9610 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9611 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9613 if (GTK_IS_TEXT_VIEW(entry)) {
9614 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9615 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9616 GtkTextIter start_iter, end_iter;
9618 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9620 if (contents == NULL)
9623 /* we shouldn't delete the selection when middle-click-pasting, or we
9624 * can't mid-click-paste our own selection */
9625 if (clip != GDK_SELECTION_PRIMARY) {
9626 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9627 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9630 if (insert_place == NULL) {
9631 /* if insert_place isn't specified, insert at the cursor.
9632 * used for Ctrl-V pasting */
9633 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9634 start = gtk_text_iter_get_offset(&start_iter);
9635 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9637 /* if insert_place is specified, paste here.
9638 * used for mid-click-pasting */
9639 start = gtk_text_iter_get_offset(insert_place);
9640 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9641 if (prefs_common.primary_paste_unselects)
9642 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9646 /* paste unwrapped: mark the paste so it's not wrapped later */
9647 end = start + strlen(contents);
9648 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9649 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9650 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9651 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9652 /* rewrap paragraph now (after a mid-click-paste) */
9653 mark_start = gtk_text_buffer_get_insert(buffer);
9654 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9655 gtk_text_iter_backward_char(&start_iter);
9656 compose_beautify_paragraph(compose, &start_iter, TRUE);
9658 } else if (GTK_IS_EDITABLE(entry))
9659 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9661 compose->modified = TRUE;
9664 static void entry_allsel(GtkWidget *entry)
9666 if (GTK_IS_EDITABLE(entry))
9667 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9668 else if (GTK_IS_TEXT_VIEW(entry)) {
9669 GtkTextIter startiter, enditer;
9670 GtkTextBuffer *textbuf;
9672 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9673 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9674 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9676 gtk_text_buffer_move_mark_by_name(textbuf,
9677 "selection_bound", &startiter);
9678 gtk_text_buffer_move_mark_by_name(textbuf,
9679 "insert", &enditer);
9683 static void compose_cut_cb(GtkAction *action, gpointer data)
9685 Compose *compose = (Compose *)data;
9686 if (compose->focused_editable
9687 #ifndef GENERIC_UMPC
9688 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9691 entry_cut_clipboard(compose->focused_editable);
9694 static void compose_copy_cb(GtkAction *action, gpointer data)
9696 Compose *compose = (Compose *)data;
9697 if (compose->focused_editable
9698 #ifndef GENERIC_UMPC
9699 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9702 entry_copy_clipboard(compose->focused_editable);
9705 static void compose_paste_cb(GtkAction *action, gpointer data)
9707 Compose *compose = (Compose *)data;
9709 GtkTextBuffer *buffer;
9711 if (compose->focused_editable &&
9712 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9713 entry_paste_clipboard(compose, compose->focused_editable,
9714 prefs_common.linewrap_pastes,
9715 GDK_SELECTION_CLIPBOARD, NULL);
9719 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9721 Compose *compose = (Compose *)data;
9722 gint wrap_quote = prefs_common.linewrap_quote;
9723 if (compose->focused_editable
9724 #ifndef GENERIC_UMPC
9725 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9728 /* let text_insert() (called directly or at a later time
9729 * after the gtk_editable_paste_clipboard) know that
9730 * text is to be inserted as a quotation. implemented
9731 * by using a simple refcount... */
9732 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9733 G_OBJECT(compose->focused_editable),
9734 "paste_as_quotation"));
9735 g_object_set_data(G_OBJECT(compose->focused_editable),
9736 "paste_as_quotation",
9737 GINT_TO_POINTER(paste_as_quotation + 1));
9738 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9739 entry_paste_clipboard(compose, compose->focused_editable,
9740 prefs_common.linewrap_pastes,
9741 GDK_SELECTION_CLIPBOARD, NULL);
9742 prefs_common.linewrap_quote = wrap_quote;
9746 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9748 Compose *compose = (Compose *)data;
9750 GtkTextBuffer *buffer;
9752 if (compose->focused_editable
9753 #ifndef GENERIC_UMPC
9754 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9757 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9758 GDK_SELECTION_CLIPBOARD, NULL);
9762 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9764 Compose *compose = (Compose *)data;
9766 GtkTextBuffer *buffer;
9768 if (compose->focused_editable
9769 #ifndef GENERIC_UMPC
9770 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9773 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9774 GDK_SELECTION_CLIPBOARD, NULL);
9778 static void compose_allsel_cb(GtkAction *action, gpointer data)
9780 Compose *compose = (Compose *)data;
9781 if (compose->focused_editable
9782 #ifndef GENERIC_UMPC
9783 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9786 entry_allsel(compose->focused_editable);
9789 static void textview_move_beginning_of_line (GtkTextView *text)
9791 GtkTextBuffer *buffer;
9795 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9797 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9798 mark = gtk_text_buffer_get_insert(buffer);
9799 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9800 gtk_text_iter_set_line_offset(&ins, 0);
9801 gtk_text_buffer_place_cursor(buffer, &ins);
9804 static void textview_move_forward_character (GtkTextView *text)
9806 GtkTextBuffer *buffer;
9810 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9812 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9813 mark = gtk_text_buffer_get_insert(buffer);
9814 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9815 if (gtk_text_iter_forward_cursor_position(&ins))
9816 gtk_text_buffer_place_cursor(buffer, &ins);
9819 static void textview_move_backward_character (GtkTextView *text)
9821 GtkTextBuffer *buffer;
9825 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9827 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9828 mark = gtk_text_buffer_get_insert(buffer);
9829 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9830 if (gtk_text_iter_backward_cursor_position(&ins))
9831 gtk_text_buffer_place_cursor(buffer, &ins);
9834 static void textview_move_forward_word (GtkTextView *text)
9836 GtkTextBuffer *buffer;
9841 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9843 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9844 mark = gtk_text_buffer_get_insert(buffer);
9845 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9846 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9847 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9848 gtk_text_iter_backward_word_start(&ins);
9849 gtk_text_buffer_place_cursor(buffer, &ins);
9853 static void textview_move_backward_word (GtkTextView *text)
9855 GtkTextBuffer *buffer;
9860 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9862 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9863 mark = gtk_text_buffer_get_insert(buffer);
9864 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9865 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9866 if (gtk_text_iter_backward_word_starts(&ins, 1))
9867 gtk_text_buffer_place_cursor(buffer, &ins);
9870 static void textview_move_end_of_line (GtkTextView *text)
9872 GtkTextBuffer *buffer;
9876 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9878 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9879 mark = gtk_text_buffer_get_insert(buffer);
9880 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9881 if (gtk_text_iter_forward_to_line_end(&ins))
9882 gtk_text_buffer_place_cursor(buffer, &ins);
9885 static void textview_move_next_line (GtkTextView *text)
9887 GtkTextBuffer *buffer;
9892 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9894 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9895 mark = gtk_text_buffer_get_insert(buffer);
9896 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9897 offset = gtk_text_iter_get_line_offset(&ins);
9898 if (gtk_text_iter_forward_line(&ins)) {
9899 gtk_text_iter_set_line_offset(&ins, offset);
9900 gtk_text_buffer_place_cursor(buffer, &ins);
9904 static void textview_move_previous_line (GtkTextView *text)
9906 GtkTextBuffer *buffer;
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);
9916 offset = gtk_text_iter_get_line_offset(&ins);
9917 if (gtk_text_iter_backward_line(&ins)) {
9918 gtk_text_iter_set_line_offset(&ins, offset);
9919 gtk_text_buffer_place_cursor(buffer, &ins);
9923 static void textview_delete_forward_character (GtkTextView *text)
9925 GtkTextBuffer *buffer;
9927 GtkTextIter ins, end_iter;
9929 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9931 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9932 mark = gtk_text_buffer_get_insert(buffer);
9933 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9935 if (gtk_text_iter_forward_char(&end_iter)) {
9936 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9940 static void textview_delete_backward_character (GtkTextView *text)
9942 GtkTextBuffer *buffer;
9944 GtkTextIter ins, end_iter;
9946 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9948 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9949 mark = gtk_text_buffer_get_insert(buffer);
9950 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9952 if (gtk_text_iter_backward_char(&end_iter)) {
9953 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9957 static void textview_delete_forward_word (GtkTextView *text)
9959 GtkTextBuffer *buffer;
9961 GtkTextIter ins, end_iter;
9963 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9965 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9966 mark = gtk_text_buffer_get_insert(buffer);
9967 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9969 if (gtk_text_iter_forward_word_end(&end_iter)) {
9970 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9974 static void textview_delete_backward_word (GtkTextView *text)
9976 GtkTextBuffer *buffer;
9978 GtkTextIter ins, end_iter;
9980 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9982 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9983 mark = gtk_text_buffer_get_insert(buffer);
9984 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9986 if (gtk_text_iter_backward_word_start(&end_iter)) {
9987 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9991 static void textview_delete_line (GtkTextView *text)
9993 GtkTextBuffer *buffer;
9995 GtkTextIter ins, start_iter, end_iter;
9997 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9999 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10000 mark = gtk_text_buffer_get_insert(buffer);
10001 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10004 gtk_text_iter_set_line_offset(&start_iter, 0);
10007 if (gtk_text_iter_ends_line(&end_iter)){
10008 if (!gtk_text_iter_forward_char(&end_iter))
10009 gtk_text_iter_backward_char(&start_iter);
10012 gtk_text_iter_forward_to_line_end(&end_iter);
10013 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10016 static void textview_delete_to_line_end (GtkTextView *text)
10018 GtkTextBuffer *buffer;
10020 GtkTextIter ins, end_iter;
10022 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10024 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10025 mark = gtk_text_buffer_get_insert(buffer);
10026 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10028 if (gtk_text_iter_ends_line(&end_iter))
10029 gtk_text_iter_forward_char(&end_iter);
10031 gtk_text_iter_forward_to_line_end(&end_iter);
10032 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10035 #define DO_ACTION(name, act) { \
10036 if(!strcmp(name, a_name)) { \
10040 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10042 const gchar *a_name = gtk_action_get_name(action);
10043 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10044 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10045 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10046 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10047 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10048 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10049 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10050 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10051 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10052 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10053 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10054 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10055 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10056 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10060 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10062 Compose *compose = (Compose *)data;
10063 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10064 ComposeCallAdvancedAction action = -1;
10066 action = compose_call_advanced_action_from_path(gaction);
10069 void (*do_action) (GtkTextView *text);
10070 } action_table[] = {
10071 {textview_move_beginning_of_line},
10072 {textview_move_forward_character},
10073 {textview_move_backward_character},
10074 {textview_move_forward_word},
10075 {textview_move_backward_word},
10076 {textview_move_end_of_line},
10077 {textview_move_next_line},
10078 {textview_move_previous_line},
10079 {textview_delete_forward_character},
10080 {textview_delete_backward_character},
10081 {textview_delete_forward_word},
10082 {textview_delete_backward_word},
10083 {textview_delete_line},
10084 {textview_delete_to_line_end}
10087 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10089 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10090 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10091 if (action_table[action].do_action)
10092 action_table[action].do_action(text);
10094 g_warning("Not implemented yet.");
10098 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10102 if (GTK_IS_EDITABLE(widget)) {
10103 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10104 gtk_editable_set_position(GTK_EDITABLE(widget),
10107 if (widget->parent && widget->parent->parent
10108 && widget->parent->parent->parent) {
10109 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10110 gint y = widget->allocation.y;
10111 gint height = widget->allocation.height;
10112 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10113 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10115 if (y < (int)shown->value) {
10116 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10118 if (y + height > (int)shown->value + (int)shown->page_size) {
10119 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10120 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10121 y + height - (int)shown->page_size - 1);
10123 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10124 (int)shown->upper - (int)shown->page_size - 1);
10131 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10132 compose->focused_editable = widget;
10134 #ifdef GENERIC_UMPC
10135 if (GTK_IS_TEXT_VIEW(widget)
10136 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10137 g_object_ref(compose->notebook);
10138 g_object_ref(compose->edit_vbox);
10139 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10140 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10141 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10142 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10143 g_object_unref(compose->notebook);
10144 g_object_unref(compose->edit_vbox);
10145 g_signal_handlers_block_by_func(G_OBJECT(widget),
10146 G_CALLBACK(compose_grab_focus_cb),
10148 gtk_widget_grab_focus(widget);
10149 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10150 G_CALLBACK(compose_grab_focus_cb),
10152 } else if (!GTK_IS_TEXT_VIEW(widget)
10153 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10154 g_object_ref(compose->notebook);
10155 g_object_ref(compose->edit_vbox);
10156 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10157 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10158 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10159 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10160 g_object_unref(compose->notebook);
10161 g_object_unref(compose->edit_vbox);
10162 g_signal_handlers_block_by_func(G_OBJECT(widget),
10163 G_CALLBACK(compose_grab_focus_cb),
10165 gtk_widget_grab_focus(widget);
10166 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10167 G_CALLBACK(compose_grab_focus_cb),
10173 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10175 compose->modified = TRUE;
10176 // compose_beautify_paragraph(compose, NULL, TRUE);
10177 #ifndef GENERIC_UMPC
10178 compose_set_title(compose);
10182 static void compose_wrap_cb(GtkAction *action, gpointer data)
10184 Compose *compose = (Compose *)data;
10185 compose_beautify_paragraph(compose, NULL, TRUE);
10188 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10190 Compose *compose = (Compose *)data;
10191 compose_wrap_all_full(compose, TRUE);
10194 static void compose_find_cb(GtkAction *action, gpointer data)
10196 Compose *compose = (Compose *)data;
10198 message_search_compose(compose);
10201 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10204 Compose *compose = (Compose *)data;
10205 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10206 if (compose->autowrap)
10207 compose_wrap_all_full(compose, TRUE);
10208 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10211 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10214 Compose *compose = (Compose *)data;
10215 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10218 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10220 Compose *compose = (Compose *)data;
10222 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10225 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10227 Compose *compose = (Compose *)data;
10229 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10232 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10234 g_free(compose->privacy_system);
10236 compose->privacy_system = g_strdup(account->default_privacy_system);
10237 compose_update_privacy_system_menu_item(compose, warn);
10240 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10242 Compose *compose = (Compose *)data;
10244 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10245 gtk_widget_show(compose->ruler_hbox);
10246 prefs_common.show_ruler = TRUE;
10248 gtk_widget_hide(compose->ruler_hbox);
10249 gtk_widget_queue_resize(compose->edit_vbox);
10250 prefs_common.show_ruler = FALSE;
10254 static void compose_attach_drag_received_cb (GtkWidget *widget,
10255 GdkDragContext *context,
10258 GtkSelectionData *data,
10261 gpointer user_data)
10263 Compose *compose = (Compose *)user_data;
10266 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10268 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10270 ) && gtk_drag_get_source_widget(context) !=
10271 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10272 list = uri_list_extract_filenames((const gchar *)data->data);
10273 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10274 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10275 compose_attach_append
10276 (compose, (const gchar *)tmp->data,
10277 utf8_filename, NULL);
10278 g_free(utf8_filename);
10280 if (list) compose_changed_cb(NULL, compose);
10281 list_free_strings(list);
10283 } else if (gtk_drag_get_source_widget(context)
10284 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10285 /* comes from our summaryview */
10286 SummaryView * summaryview = NULL;
10287 GSList * list = NULL, *cur = NULL;
10289 if (mainwindow_get_mainwindow())
10290 summaryview = mainwindow_get_mainwindow()->summaryview;
10293 list = summary_get_selected_msg_list(summaryview);
10295 for (cur = list; cur; cur = cur->next) {
10296 MsgInfo *msginfo = (MsgInfo *)cur->data;
10297 gchar *file = NULL;
10299 file = procmsg_get_message_file_full(msginfo,
10302 compose_attach_append(compose, (const gchar *)file,
10303 (const gchar *)file, "message/rfc822");
10307 g_slist_free(list);
10311 static gboolean compose_drag_drop(GtkWidget *widget,
10312 GdkDragContext *drag_context,
10314 guint time, gpointer user_data)
10316 /* not handling this signal makes compose_insert_drag_received_cb
10321 static void compose_insert_drag_received_cb (GtkWidget *widget,
10322 GdkDragContext *drag_context,
10325 GtkSelectionData *data,
10328 gpointer user_data)
10330 Compose *compose = (Compose *)user_data;
10333 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10336 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10338 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10340 AlertValue val = G_ALERTDEFAULT;
10342 list = uri_list_extract_filenames((const gchar *)data->data);
10343 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10344 /* Assume a list of no files, and data has ://, is a remote link */
10345 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10346 gchar *tmpfile = get_tmp_file();
10347 str_write_to_file(tmpdata, tmpfile);
10349 compose_insert_file(compose, tmpfile);
10350 claws_unlink(tmpfile);
10352 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10353 compose_beautify_paragraph(compose, NULL, TRUE);
10356 switch (prefs_common.compose_dnd_mode) {
10357 case COMPOSE_DND_ASK:
10358 val = alertpanel_full(_("Insert or attach?"),
10359 _("Do you want to insert the contents of the file(s) "
10360 "into the message body, or attach it to the email?"),
10361 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10362 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10364 case COMPOSE_DND_INSERT:
10365 val = G_ALERTALTERNATE;
10367 case COMPOSE_DND_ATTACH:
10368 val = G_ALERTOTHER;
10371 /* unexpected case */
10372 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10375 if (val & G_ALERTDISABLE) {
10376 val &= ~G_ALERTDISABLE;
10377 /* remember what action to perform by default, only if we don't click Cancel */
10378 if (val == G_ALERTALTERNATE)
10379 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10380 else if (val == G_ALERTOTHER)
10381 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10384 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10385 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10386 list_free_strings(list);
10389 } else if (val == G_ALERTOTHER) {
10390 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10391 list_free_strings(list);
10396 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10397 compose_insert_file(compose, (const gchar *)tmp->data);
10399 list_free_strings(list);
10401 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10404 #if GTK_CHECK_VERSION(2, 8, 0)
10405 /* do nothing, handled by GTK */
10407 gchar *tmpfile = get_tmp_file();
10408 str_write_to_file((const gchar *)data->data, tmpfile);
10409 compose_insert_file(compose, tmpfile);
10410 claws_unlink(tmpfile);
10412 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10416 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10419 static void compose_header_drag_received_cb (GtkWidget *widget,
10420 GdkDragContext *drag_context,
10423 GtkSelectionData *data,
10426 gpointer user_data)
10428 GtkEditable *entry = (GtkEditable *)user_data;
10429 gchar *email = (gchar *)data->data;
10431 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10434 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10435 gchar *decoded=g_new(gchar, strlen(email));
10438 email += strlen("mailto:");
10439 decode_uri(decoded, email); /* will fit */
10440 gtk_editable_delete_text(entry, 0, -1);
10441 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10442 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10446 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10449 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10451 Compose *compose = (Compose *)data;
10453 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10454 compose->return_receipt = TRUE;
10456 compose->return_receipt = FALSE;
10459 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10461 Compose *compose = (Compose *)data;
10463 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10464 compose->remove_references = TRUE;
10466 compose->remove_references = FALSE;
10469 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10470 ComposeHeaderEntry *headerentry)
10472 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10476 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10477 GdkEventKey *event,
10478 ComposeHeaderEntry *headerentry)
10480 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10481 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10482 !(event->state & GDK_MODIFIER_MASK) &&
10483 (event->keyval == GDK_BackSpace) &&
10484 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10485 gtk_container_remove
10486 (GTK_CONTAINER(headerentry->compose->header_table),
10487 headerentry->combo);
10488 gtk_container_remove
10489 (GTK_CONTAINER(headerentry->compose->header_table),
10490 headerentry->entry);
10491 headerentry->compose->header_list =
10492 g_slist_remove(headerentry->compose->header_list,
10494 g_free(headerentry);
10495 } else if (event->keyval == GDK_Tab) {
10496 if (headerentry->compose->header_last == headerentry) {
10497 /* Override default next focus, and give it to subject_entry
10498 * instead of notebook tabs
10500 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10501 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10508 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10509 ComposeHeaderEntry *headerentry)
10511 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10512 compose_create_header_entry(headerentry->compose);
10513 g_signal_handlers_disconnect_matched
10514 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10515 0, 0, NULL, NULL, headerentry);
10517 /* Automatically scroll down */
10518 GTK_EVENTS_FLUSH();
10519 compose_show_first_last_header(headerentry->compose, FALSE);
10525 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10527 GtkAdjustment *vadj;
10529 cm_return_if_fail(compose);
10530 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10531 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10532 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10533 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10534 gtk_adjustment_changed(vadj);
10537 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10538 const gchar *text, gint len, Compose *compose)
10540 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10541 (G_OBJECT(compose->text), "paste_as_quotation"));
10544 cm_return_if_fail(text != NULL);
10546 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10547 G_CALLBACK(text_inserted),
10549 if (paste_as_quotation) {
10551 const gchar *qmark;
10553 GtkTextIter start_iter;
10556 len = strlen(text);
10558 new_text = g_strndup(text, len);
10560 qmark = compose_quote_char_from_context(compose);
10562 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10563 gtk_text_buffer_place_cursor(buffer, iter);
10565 pos = gtk_text_iter_get_offset(iter);
10567 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10568 _("Quote format error at line %d."));
10569 quote_fmt_reset_vartable();
10571 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10572 GINT_TO_POINTER(paste_as_quotation - 1));
10574 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10575 gtk_text_buffer_place_cursor(buffer, iter);
10576 gtk_text_buffer_delete_mark(buffer, mark);
10578 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10579 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10580 compose_beautify_paragraph(compose, &start_iter, FALSE);
10581 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10582 gtk_text_buffer_delete_mark(buffer, mark);
10584 if (strcmp(text, "\n") || compose->automatic_break
10585 || gtk_text_iter_starts_line(iter)) {
10586 GtkTextIter before_ins;
10587 gtk_text_buffer_insert(buffer, iter, text, len);
10588 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10589 before_ins = *iter;
10590 gtk_text_iter_backward_chars(&before_ins, len);
10591 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10594 /* check if the preceding is just whitespace or quote */
10595 GtkTextIter start_line;
10596 gchar *tmp = NULL, *quote = NULL;
10597 gint quote_len = 0, is_normal = 0;
10598 start_line = *iter;
10599 gtk_text_iter_set_line_offset(&start_line, 0);
10600 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10603 if (*tmp == '\0') {
10606 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10614 gtk_text_buffer_insert(buffer, iter, text, len);
10616 gtk_text_buffer_insert_with_tags_by_name(buffer,
10617 iter, text, len, "no_join", NULL);
10622 if (!paste_as_quotation) {
10623 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10624 compose_beautify_paragraph(compose, iter, FALSE);
10625 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10626 gtk_text_buffer_delete_mark(buffer, mark);
10629 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10630 G_CALLBACK(text_inserted),
10632 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10634 if (prefs_common.autosave &&
10635 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10636 compose->draft_timeout_tag != -2 /* disabled while loading */)
10637 compose->draft_timeout_tag = g_timeout_add
10638 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10640 static gint compose_defer_auto_save_draft(Compose *compose)
10642 compose->draft_timeout_tag = -1;
10643 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10648 static void compose_check_all(GtkAction *action, gpointer data)
10650 Compose *compose = (Compose *)data;
10651 if (!compose->gtkaspell)
10654 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10655 claws_spell_entry_check_all(
10656 CLAWS_SPELL_ENTRY(compose->subject_entry));
10658 gtkaspell_check_all(compose->gtkaspell);
10661 static void compose_highlight_all(GtkAction *action, gpointer data)
10663 Compose *compose = (Compose *)data;
10664 if (compose->gtkaspell) {
10665 claws_spell_entry_recheck_all(
10666 CLAWS_SPELL_ENTRY(compose->subject_entry));
10667 gtkaspell_highlight_all(compose->gtkaspell);
10671 static void compose_check_backwards(GtkAction *action, gpointer data)
10673 Compose *compose = (Compose *)data;
10674 if (!compose->gtkaspell) {
10675 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10679 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10680 claws_spell_entry_check_backwards(
10681 CLAWS_SPELL_ENTRY(compose->subject_entry));
10683 gtkaspell_check_backwards(compose->gtkaspell);
10686 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10688 Compose *compose = (Compose *)data;
10689 if (!compose->gtkaspell) {
10690 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10694 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10695 claws_spell_entry_check_forwards_go(
10696 CLAWS_SPELL_ENTRY(compose->subject_entry));
10698 gtkaspell_check_forwards_go(compose->gtkaspell);
10703 *\brief Guess originating forward account from MsgInfo and several
10704 * "common preference" settings. Return NULL if no guess.
10706 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10708 PrefsAccount *account = NULL;
10710 cm_return_val_if_fail(msginfo, NULL);
10711 cm_return_val_if_fail(msginfo->folder, NULL);
10712 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10714 if (msginfo->folder->prefs->enable_default_account)
10715 account = account_find_from_id(msginfo->folder->prefs->default_account);
10718 account = msginfo->folder->folder->account;
10720 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10722 Xstrdup_a(to, msginfo->to, return NULL);
10723 extract_address(to);
10724 account = account_find_from_address(to, FALSE);
10727 if (!account && prefs_common.forward_account_autosel) {
10728 gchar cc[BUFFSIZE];
10729 if (!procheader_get_header_from_msginfo
10730 (msginfo, cc,sizeof cc , "Cc:")) {
10731 gchar *buf = cc + strlen("Cc:");
10732 extract_address(buf);
10733 account = account_find_from_address(buf, FALSE);
10737 if (!account && prefs_common.forward_account_autosel) {
10738 gchar deliveredto[BUFFSIZE];
10739 if (!procheader_get_header_from_msginfo
10740 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10741 gchar *buf = deliveredto + strlen("Delivered-To:");
10742 extract_address(buf);
10743 account = account_find_from_address(buf, FALSE);
10750 gboolean compose_close(Compose *compose)
10754 if (!g_mutex_trylock(compose->mutex)) {
10755 /* we have to wait for the (possibly deferred by auto-save)
10756 * drafting to be done, before destroying the compose under
10758 debug_print("waiting for drafting to finish...\n");
10759 compose_allow_user_actions(compose, FALSE);
10760 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10763 cm_return_val_if_fail(compose, FALSE);
10764 gtkut_widget_get_uposition(compose->window, &x, &y);
10765 if (!compose->batch) {
10766 prefs_common.compose_x = x;
10767 prefs_common.compose_y = y;
10769 g_mutex_unlock(compose->mutex);
10770 compose_destroy(compose);
10775 * Add entry field for each address in list.
10776 * \param compose E-Mail composition object.
10777 * \param listAddress List of (formatted) E-Mail addresses.
10779 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10782 node = listAddress;
10784 addr = ( gchar * ) node->data;
10785 compose_entry_append( compose, addr, COMPOSE_TO );
10786 node = g_list_next( node );
10790 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10791 guint action, gboolean opening_multiple)
10793 gchar *body = NULL;
10794 GSList *new_msglist = NULL;
10795 MsgInfo *tmp_msginfo = NULL;
10796 gboolean originally_enc = FALSE;
10797 gboolean originally_sig = FALSE;
10798 Compose *compose = NULL;
10799 gchar *s_system = NULL;
10801 cm_return_if_fail(msgview != NULL);
10803 cm_return_if_fail(msginfo_list != NULL);
10805 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10806 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10807 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10809 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10810 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10811 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10812 orig_msginfo, mimeinfo);
10813 if (tmp_msginfo != NULL) {
10814 new_msglist = g_slist_append(NULL, tmp_msginfo);
10816 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10817 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10818 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10820 tmp_msginfo->folder = orig_msginfo->folder;
10821 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10822 if (orig_msginfo->tags) {
10823 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10824 tmp_msginfo->folder->tags_dirty = TRUE;
10830 if (!opening_multiple)
10831 body = messageview_get_selection(msgview);
10834 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10835 procmsg_msginfo_free(tmp_msginfo);
10836 g_slist_free(new_msglist);
10838 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10840 if (compose && originally_enc) {
10841 compose_force_encryption(compose, compose->account, FALSE, s_system);
10844 if (compose && originally_sig && compose->account->default_sign_reply) {
10845 compose_force_signing(compose, compose->account, s_system);
10851 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10854 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10855 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10856 GSList *cur = msginfo_list;
10857 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10858 "messages. Opening the windows "
10859 "could take some time. Do you "
10860 "want to continue?"),
10861 g_slist_length(msginfo_list));
10862 if (g_slist_length(msginfo_list) > 9
10863 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10864 != G_ALERTALTERNATE) {
10869 /* We'll open multiple compose windows */
10870 /* let the WM place the next windows */
10871 compose_force_window_origin = FALSE;
10872 for (; cur; cur = cur->next) {
10874 tmplist.data = cur->data;
10875 tmplist.next = NULL;
10876 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10878 compose_force_window_origin = TRUE;
10880 /* forwarding multiple mails as attachments is done via a
10881 * single compose window */
10882 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10886 void compose_set_position(Compose *compose, gint pos)
10888 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10890 gtkut_text_view_set_position(text, pos);
10893 gboolean compose_search_string(Compose *compose,
10894 const gchar *str, gboolean case_sens)
10896 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10898 return gtkut_text_view_search_string(text, str, case_sens);
10901 gboolean compose_search_string_backward(Compose *compose,
10902 const gchar *str, gboolean case_sens)
10904 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10906 return gtkut_text_view_search_string_backward(text, str, case_sens);
10909 /* allocate a msginfo structure and populate its data from a compose data structure */
10910 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10912 MsgInfo *newmsginfo;
10914 gchar buf[BUFFSIZE];
10916 cm_return_val_if_fail( compose != NULL, NULL );
10918 newmsginfo = procmsg_msginfo_new();
10921 get_rfc822_date(buf, sizeof(buf));
10922 newmsginfo->date = g_strdup(buf);
10925 if (compose->from_name) {
10926 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10927 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10931 if (compose->subject_entry)
10932 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10934 /* to, cc, reply-to, newsgroups */
10935 for (list = compose->header_list; list; list = list->next) {
10936 gchar *header = gtk_editable_get_chars(
10938 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10939 gchar *entry = gtk_editable_get_chars(
10940 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10942 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10943 if ( newmsginfo->to == NULL ) {
10944 newmsginfo->to = g_strdup(entry);
10945 } else if (entry && *entry) {
10946 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10947 g_free(newmsginfo->to);
10948 newmsginfo->to = tmp;
10951 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10952 if ( newmsginfo->cc == NULL ) {
10953 newmsginfo->cc = g_strdup(entry);
10954 } else if (entry && *entry) {
10955 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10956 g_free(newmsginfo->cc);
10957 newmsginfo->cc = tmp;
10960 if ( strcasecmp(header,
10961 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10962 if ( newmsginfo->newsgroups == NULL ) {
10963 newmsginfo->newsgroups = g_strdup(entry);
10964 } else if (entry && *entry) {
10965 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10966 g_free(newmsginfo->newsgroups);
10967 newmsginfo->newsgroups = tmp;
10975 /* other data is unset */
10981 /* update compose's dictionaries from folder dict settings */
10982 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10983 FolderItem *folder_item)
10985 cm_return_if_fail(compose != NULL);
10987 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10988 FolderItemPrefs *prefs = folder_item->prefs;
10990 if (prefs->enable_default_dictionary)
10991 gtkaspell_change_dict(compose->gtkaspell,
10992 prefs->default_dictionary, FALSE);
10993 if (folder_item->prefs->enable_default_alt_dictionary)
10994 gtkaspell_change_alt_dict(compose->gtkaspell,
10995 prefs->default_alt_dictionary);
10996 if (prefs->enable_default_dictionary
10997 || prefs->enable_default_alt_dictionary)
10998 compose_spell_menu_changed(compose);