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;
3334 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3336 if ((fp = g_fopen(file, "rb")) == NULL) {
3337 FILE_OP_ERROR(file, "fopen");
3338 return COMPOSE_INSERT_READ_ERROR;
3341 prev_autowrap = compose->autowrap;
3342 compose->autowrap = FALSE;
3344 text = GTK_TEXT_VIEW(compose->text);
3345 buffer = gtk_text_view_get_buffer(text);
3346 mark = gtk_text_buffer_get_insert(buffer);
3347 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3349 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3350 G_CALLBACK(text_inserted),
3353 cur_encoding = conv_get_locale_charset_str_no_utf8();
3355 while (fgets(buf, sizeof(buf), fp) != NULL) {
3358 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3359 str = g_strdup(buf);
3361 str = conv_codeset_strdup
3362 (buf, cur_encoding, CS_INTERNAL);
3365 /* strip <CR> if DOS/Windows file,
3366 replace <CR> with <LF> if Macintosh file. */
3369 if (len > 0 && str[len - 1] != '\n') {
3371 if (str[len] == '\r') str[len] = '\n';
3374 gtk_text_buffer_insert(buffer, &iter, str, -1);
3378 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3379 G_CALLBACK(text_inserted),
3381 compose->autowrap = prev_autowrap;
3382 if (compose->autowrap)
3383 compose_wrap_all(compose);
3388 return COMPOSE_INSERT_INVALID_CHARACTER;
3390 return COMPOSE_INSERT_SUCCESS;
3393 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3394 const gchar *filename,
3395 const gchar *content_type)
3403 GtkListStore *store;
3405 gboolean has_binary = FALSE;
3407 if (!is_file_exist(file)) {
3408 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3409 gboolean result = FALSE;
3410 if (file_from_uri && is_file_exist(file_from_uri)) {
3411 result = compose_attach_append(
3412 compose, file_from_uri,
3416 g_free(file_from_uri);
3419 alertpanel_error("File %s doesn't exist\n", filename);
3422 if ((size = get_file_size(file)) < 0) {
3423 alertpanel_error("Can't get file size of %s\n", filename);
3427 alertpanel_error(_("File %s is empty."), filename);
3430 if ((fp = g_fopen(file, "rb")) == NULL) {
3431 alertpanel_error(_("Can't read %s."), filename);
3436 ainfo = g_new0(AttachInfo, 1);
3437 auto_ainfo = g_auto_pointer_new_with_free
3438 (ainfo, (GFreeFunc) compose_attach_info_free);
3439 ainfo->file = g_strdup(file);
3442 ainfo->content_type = g_strdup(content_type);
3443 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3445 MsgFlags flags = {0, 0};
3447 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3448 ainfo->encoding = ENC_7BIT;
3450 ainfo->encoding = ENC_8BIT;
3452 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3453 if (msginfo && msginfo->subject)
3454 name = g_strdup(msginfo->subject);
3456 name = g_path_get_basename(filename ? filename : file);
3458 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3460 procmsg_msginfo_free(msginfo);
3462 if (!g_ascii_strncasecmp(content_type, "text", 4))
3463 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3465 ainfo->encoding = ENC_BASE64;
3466 name = g_path_get_basename(filename ? filename : file);
3467 ainfo->name = g_strdup(name);
3471 ainfo->content_type = procmime_get_mime_type(file);
3472 if (!ainfo->content_type) {
3473 ainfo->content_type =
3474 g_strdup("application/octet-stream");
3475 ainfo->encoding = ENC_BASE64;
3476 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3478 procmime_get_encoding_for_text_file(file, &has_binary);
3480 ainfo->encoding = ENC_BASE64;
3481 name = g_path_get_basename(filename ? filename : file);
3482 ainfo->name = g_strdup(name);
3486 if (ainfo->name != NULL
3487 && !strcmp(ainfo->name, ".")) {
3488 g_free(ainfo->name);
3492 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3493 g_free(ainfo->content_type);
3494 ainfo->content_type = g_strdup("application/octet-stream");
3497 ainfo->size = (goffset)size;
3498 size_text = to_human_readable((goffset)size);
3500 store = GTK_LIST_STORE(gtk_tree_view_get_model
3501 (GTK_TREE_VIEW(compose->attach_clist)));
3503 gtk_list_store_append(store, &iter);
3504 gtk_list_store_set(store, &iter,
3505 COL_MIMETYPE, ainfo->content_type,
3506 COL_SIZE, size_text,
3507 COL_NAME, ainfo->name,
3509 COL_AUTODATA, auto_ainfo,
3512 g_auto_pointer_free(auto_ainfo);
3513 compose_attach_update_label(compose);
3517 static void compose_use_signing(Compose *compose, gboolean use_signing)
3519 compose->use_signing = use_signing;
3520 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3523 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3525 compose->use_encryption = use_encryption;
3526 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3529 #define NEXT_PART_NOT_CHILD(info) \
3531 node = info->node; \
3532 while (node->children) \
3533 node = g_node_last_child(node); \
3534 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3537 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3541 MimeInfo *firsttext = NULL;
3542 MimeInfo *encrypted = NULL;
3545 const gchar *partname = NULL;
3547 mimeinfo = procmime_scan_message(msginfo);
3548 if (!mimeinfo) return;
3550 if (mimeinfo->node->children == NULL) {
3551 procmime_mimeinfo_free_all(mimeinfo);
3555 /* find first content part */
3556 child = (MimeInfo *) mimeinfo->node->children->data;
3557 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3558 child = (MimeInfo *)child->node->children->data;
3561 if (child->type == MIMETYPE_TEXT) {
3563 debug_print("First text part found\n");
3564 } else if (compose->mode == COMPOSE_REEDIT &&
3565 child->type == MIMETYPE_APPLICATION &&
3566 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3567 encrypted = (MimeInfo *)child->node->parent->data;
3570 child = (MimeInfo *) mimeinfo->node->children->data;
3571 while (child != NULL) {
3574 if (child == encrypted) {
3575 /* skip this part of tree */
3576 NEXT_PART_NOT_CHILD(child);
3580 if (child->type == MIMETYPE_MULTIPART) {
3581 /* get the actual content */
3582 child = procmime_mimeinfo_next(child);
3586 if (child == firsttext) {
3587 child = procmime_mimeinfo_next(child);
3591 outfile = procmime_get_tmp_file_name(child);
3592 if ((err = procmime_get_part(outfile, child)) < 0)
3593 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3595 gchar *content_type;
3597 content_type = procmime_get_content_type_str(child->type, child->subtype);
3599 /* if we meet a pgp signature, we don't attach it, but
3600 * we force signing. */
3601 if ((strcmp(content_type, "application/pgp-signature") &&
3602 strcmp(content_type, "application/pkcs7-signature") &&
3603 strcmp(content_type, "application/x-pkcs7-signature"))
3604 || compose->mode == COMPOSE_REDIRECT) {
3605 partname = procmime_mimeinfo_get_parameter(child, "filename");
3606 if (partname == NULL)
3607 partname = procmime_mimeinfo_get_parameter(child, "name");
3608 if (partname == NULL)
3610 compose_attach_append(compose, outfile,
3611 partname, content_type);
3613 compose_force_signing(compose, compose->account, NULL);
3615 g_free(content_type);
3618 NEXT_PART_NOT_CHILD(child);
3620 procmime_mimeinfo_free_all(mimeinfo);
3623 #undef NEXT_PART_NOT_CHILD
3628 WAIT_FOR_INDENT_CHAR,
3629 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3632 /* return indent length, we allow:
3633 indent characters followed by indent characters or spaces/tabs,
3634 alphabets and numbers immediately followed by indent characters,
3635 and the repeating sequences of the above
3636 If quote ends with multiple spaces, only the first one is included. */
3637 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3638 const GtkTextIter *start, gint *len)
3640 GtkTextIter iter = *start;
3644 IndentState state = WAIT_FOR_INDENT_CHAR;
3647 gint alnum_count = 0;
3648 gint space_count = 0;
3651 if (prefs_common.quote_chars == NULL) {
3655 while (!gtk_text_iter_ends_line(&iter)) {
3656 wc = gtk_text_iter_get_char(&iter);
3657 if (g_unichar_iswide(wc))
3659 clen = g_unichar_to_utf8(wc, ch);
3663 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3664 is_space = g_unichar_isspace(wc);
3666 if (state == WAIT_FOR_INDENT_CHAR) {
3667 if (!is_indent && !g_unichar_isalnum(wc))
3670 quote_len += alnum_count + space_count + 1;
3671 alnum_count = space_count = 0;
3672 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3675 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3676 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3680 else if (is_indent) {
3681 quote_len += alnum_count + space_count + 1;
3682 alnum_count = space_count = 0;
3685 state = WAIT_FOR_INDENT_CHAR;
3689 gtk_text_iter_forward_char(&iter);
3692 if (quote_len > 0 && space_count > 0)
3698 if (quote_len > 0) {
3700 gtk_text_iter_forward_chars(&iter, quote_len);
3701 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3707 /* return >0 if the line is itemized */
3708 static int compose_itemized_length(GtkTextBuffer *buffer,
3709 const GtkTextIter *start)
3711 GtkTextIter iter = *start;
3716 if (gtk_text_iter_ends_line(&iter))
3721 wc = gtk_text_iter_get_char(&iter);
3722 if (!g_unichar_isspace(wc))
3724 gtk_text_iter_forward_char(&iter);
3725 if (gtk_text_iter_ends_line(&iter))
3729 clen = g_unichar_to_utf8(wc, ch);
3733 if (!strchr("*-+", ch[0]))
3736 gtk_text_iter_forward_char(&iter);
3737 if (gtk_text_iter_ends_line(&iter))
3739 wc = gtk_text_iter_get_char(&iter);
3740 if (g_unichar_isspace(wc)) {
3746 /* return the string at the start of the itemization */
3747 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3748 const GtkTextIter *start)
3750 GtkTextIter iter = *start;
3753 GString *item_chars = g_string_new("");
3756 if (gtk_text_iter_ends_line(&iter))
3761 wc = gtk_text_iter_get_char(&iter);
3762 if (!g_unichar_isspace(wc))
3764 gtk_text_iter_forward_char(&iter);
3765 if (gtk_text_iter_ends_line(&iter))
3767 g_string_append_unichar(item_chars, wc);
3770 str = item_chars->str;
3771 g_string_free(item_chars, FALSE);
3775 /* return the number of spaces at a line's start */
3776 static int compose_left_offset_length(GtkTextBuffer *buffer,
3777 const GtkTextIter *start)
3779 GtkTextIter iter = *start;
3782 if (gtk_text_iter_ends_line(&iter))
3786 wc = gtk_text_iter_get_char(&iter);
3787 if (!g_unichar_isspace(wc))
3790 gtk_text_iter_forward_char(&iter);
3791 if (gtk_text_iter_ends_line(&iter))
3795 gtk_text_iter_forward_char(&iter);
3796 if (gtk_text_iter_ends_line(&iter))
3801 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3802 const GtkTextIter *start,
3803 GtkTextIter *break_pos,
3807 GtkTextIter iter = *start, line_end = *start;
3808 PangoLogAttr *attrs;
3815 gboolean can_break = FALSE;
3816 gboolean do_break = FALSE;
3817 gboolean was_white = FALSE;
3818 gboolean prev_dont_break = FALSE;
3820 gtk_text_iter_forward_to_line_end(&line_end);
3821 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3822 len = g_utf8_strlen(str, -1);
3826 g_warning("compose_get_line_break_pos: len = 0!\n");
3830 /* g_print("breaking line: %d: %s (len = %d)\n",
3831 gtk_text_iter_get_line(&iter), str, len); */
3833 attrs = g_new(PangoLogAttr, len + 1);
3835 pango_default_break(str, -1, NULL, attrs, len + 1);
3839 /* skip quote and leading spaces */
3840 for (i = 0; *p != '\0' && i < len; i++) {
3843 wc = g_utf8_get_char(p);
3844 if (i >= quote_len && !g_unichar_isspace(wc))
3846 if (g_unichar_iswide(wc))
3848 else if (*p == '\t')
3852 p = g_utf8_next_char(p);
3855 for (; *p != '\0' && i < len; i++) {
3856 PangoLogAttr *attr = attrs + i;
3860 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3863 was_white = attr->is_white;
3865 /* don't wrap URI */
3866 if ((uri_len = get_uri_len(p)) > 0) {
3868 if (pos > 0 && col > max_col) {
3878 wc = g_utf8_get_char(p);
3879 if (g_unichar_iswide(wc)) {
3881 if (prev_dont_break && can_break && attr->is_line_break)
3883 } else if (*p == '\t')
3887 if (pos > 0 && col > max_col) {
3892 if (*p == '-' || *p == '/')
3893 prev_dont_break = TRUE;
3895 prev_dont_break = FALSE;
3897 p = g_utf8_next_char(p);
3901 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3906 *break_pos = *start;
3907 gtk_text_iter_set_line_offset(break_pos, pos);
3912 static gboolean compose_join_next_line(Compose *compose,
3913 GtkTextBuffer *buffer,
3915 const gchar *quote_str)
3917 GtkTextIter iter_ = *iter, cur, prev, next, end;
3918 PangoLogAttr attrs[3];
3920 gchar *next_quote_str;
3923 gboolean keep_cursor = FALSE;
3925 if (!gtk_text_iter_forward_line(&iter_) ||
3926 gtk_text_iter_ends_line(&iter_)) {
3929 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3931 if ((quote_str || next_quote_str) &&
3932 strcmp2(quote_str, next_quote_str) != 0) {
3933 g_free(next_quote_str);
3936 g_free(next_quote_str);
3939 if (quote_len > 0) {
3940 gtk_text_iter_forward_chars(&end, quote_len);
3941 if (gtk_text_iter_ends_line(&end)) {
3946 /* don't join itemized lines */
3947 if (compose_itemized_length(buffer, &end) > 0) {
3951 /* don't join signature separator */
3952 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3955 /* delete quote str */
3957 gtk_text_buffer_delete(buffer, &iter_, &end);
3959 /* don't join line breaks put by the user */
3961 gtk_text_iter_backward_char(&cur);
3962 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3963 gtk_text_iter_forward_char(&cur);
3967 gtk_text_iter_forward_char(&cur);
3968 /* delete linebreak and extra spaces */
3969 while (gtk_text_iter_backward_char(&cur)) {
3970 wc1 = gtk_text_iter_get_char(&cur);
3971 if (!g_unichar_isspace(wc1))
3976 while (!gtk_text_iter_ends_line(&cur)) {
3977 wc1 = gtk_text_iter_get_char(&cur);
3978 if (!g_unichar_isspace(wc1))
3980 gtk_text_iter_forward_char(&cur);
3983 if (!gtk_text_iter_equal(&prev, &next)) {
3986 mark = gtk_text_buffer_get_insert(buffer);
3987 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3988 if (gtk_text_iter_equal(&prev, &cur))
3990 gtk_text_buffer_delete(buffer, &prev, &next);
3994 /* insert space if required */
3995 gtk_text_iter_backward_char(&prev);
3996 wc1 = gtk_text_iter_get_char(&prev);
3997 wc2 = gtk_text_iter_get_char(&next);
3998 gtk_text_iter_forward_char(&next);
3999 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4000 pango_default_break(str, -1, NULL, attrs, 3);
4001 if (!attrs[1].is_line_break ||
4002 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4003 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4005 gtk_text_iter_backward_char(&iter_);
4006 gtk_text_buffer_place_cursor(buffer, &iter_);
4015 #define ADD_TXT_POS(bp_, ep_, pti_) \
4016 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4017 last = last->next; \
4018 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4019 last->next = NULL; \
4021 g_warning("alloc error scanning URIs\n"); \
4024 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4026 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4027 GtkTextBuffer *buffer;
4028 GtkTextIter iter, break_pos, end_of_line;
4029 gchar *quote_str = NULL;
4031 gboolean wrap_quote = prefs_common.linewrap_quote;
4032 gboolean prev_autowrap = compose->autowrap;
4033 gint startq_offset = -1, noq_offset = -1;
4034 gint uri_start = -1, uri_stop = -1;
4035 gint nouri_start = -1, nouri_stop = -1;
4036 gint num_blocks = 0;
4037 gint quotelevel = -1;
4038 gboolean modified = force;
4039 gboolean removed = FALSE;
4040 gboolean modified_before_remove = FALSE;
4042 gboolean start = TRUE;
4043 gint itemized_len = 0, rem_item_len = 0;
4044 gchar *itemized_chars = NULL;
4045 gboolean item_continuation = FALSE;
4050 if (compose->draft_timeout_tag == -2) {
4054 compose->autowrap = FALSE;
4056 buffer = gtk_text_view_get_buffer(text);
4057 undo_wrapping(compose->undostruct, TRUE);
4062 mark = gtk_text_buffer_get_insert(buffer);
4063 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4067 if (compose->draft_timeout_tag == -2) {
4068 if (gtk_text_iter_ends_line(&iter)) {
4069 while (gtk_text_iter_ends_line(&iter) &&
4070 gtk_text_iter_forward_line(&iter))
4073 while (gtk_text_iter_backward_line(&iter)) {
4074 if (gtk_text_iter_ends_line(&iter)) {
4075 gtk_text_iter_forward_line(&iter);
4081 /* move to line start */
4082 gtk_text_iter_set_line_offset(&iter, 0);
4085 itemized_len = compose_itemized_length(buffer, &iter);
4087 if (!itemized_len) {
4088 itemized_len = compose_left_offset_length(buffer, &iter);
4089 item_continuation = TRUE;
4093 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4095 /* go until paragraph end (empty line) */
4096 while (start || !gtk_text_iter_ends_line(&iter)) {
4097 gchar *scanpos = NULL;
4098 /* parse table - in order of priority */
4100 const gchar *needle; /* token */
4102 /* token search function */
4103 gchar *(*search) (const gchar *haystack,
4104 const gchar *needle);
4105 /* part parsing function */
4106 gboolean (*parse) (const gchar *start,
4107 const gchar *scanpos,
4111 /* part to URI function */
4112 gchar *(*build_uri) (const gchar *bp,
4116 static struct table parser[] = {
4117 {"http://", strcasestr, get_uri_part, make_uri_string},
4118 {"https://", strcasestr, get_uri_part, make_uri_string},
4119 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4120 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4121 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4122 {"www.", strcasestr, get_uri_part, make_http_string},
4123 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4124 {"@", strcasestr, get_email_part, make_email_string}
4126 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4127 gint last_index = PARSE_ELEMS;
4129 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4133 if (!prev_autowrap && num_blocks == 0) {
4135 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4136 G_CALLBACK(text_inserted),
4139 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4142 uri_start = uri_stop = -1;
4144 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4147 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4148 if (startq_offset == -1)
4149 startq_offset = gtk_text_iter_get_offset(&iter);
4150 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4151 if (quotelevel > 2) {
4152 /* recycle colors */
4153 if (prefs_common.recycle_quote_colors)
4162 if (startq_offset == -1)
4163 noq_offset = gtk_text_iter_get_offset(&iter);
4167 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4170 if (gtk_text_iter_ends_line(&iter)) {
4172 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4173 prefs_common.linewrap_len,
4175 GtkTextIter prev, next, cur;
4176 if (prev_autowrap != FALSE || force) {
4177 compose->automatic_break = TRUE;
4179 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4180 compose->automatic_break = FALSE;
4181 if (itemized_len && compose->autoindent) {
4182 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4183 if (!item_continuation)
4184 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4186 } else if (quote_str && wrap_quote) {
4187 compose->automatic_break = TRUE;
4189 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4190 compose->automatic_break = FALSE;
4191 if (itemized_len && compose->autoindent) {
4192 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4193 if (!item_continuation)
4194 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4198 /* remove trailing spaces */
4200 rem_item_len = itemized_len;
4201 while (compose->autoindent && rem_item_len-- > 0)
4202 gtk_text_iter_backward_char(&cur);
4203 gtk_text_iter_backward_char(&cur);
4206 while (!gtk_text_iter_starts_line(&cur)) {
4209 gtk_text_iter_backward_char(&cur);
4210 wc = gtk_text_iter_get_char(&cur);
4211 if (!g_unichar_isspace(wc))
4215 if (!gtk_text_iter_equal(&prev, &next)) {
4216 gtk_text_buffer_delete(buffer, &prev, &next);
4218 gtk_text_iter_forward_char(&break_pos);
4222 gtk_text_buffer_insert(buffer, &break_pos,
4226 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4228 /* move iter to current line start */
4229 gtk_text_iter_set_line_offset(&iter, 0);
4236 /* move iter to next line start */
4242 if (!prev_autowrap && num_blocks > 0) {
4244 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4245 G_CALLBACK(text_inserted),
4249 while (!gtk_text_iter_ends_line(&end_of_line)) {
4250 gtk_text_iter_forward_char(&end_of_line);
4252 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4254 nouri_start = gtk_text_iter_get_offset(&iter);
4255 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4257 walk_pos = gtk_text_iter_get_offset(&iter);
4258 /* FIXME: this looks phony. scanning for anything in the parse table */
4259 for (n = 0; n < PARSE_ELEMS; n++) {
4262 tmp = parser[n].search(walk, parser[n].needle);
4264 if (scanpos == NULL || tmp < scanpos) {
4273 /* check if URI can be parsed */
4274 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4275 (const gchar **)&ep, FALSE)
4276 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4280 strlen(parser[last_index].needle);
4283 uri_start = walk_pos + (bp - o_walk);
4284 uri_stop = walk_pos + (ep - o_walk);
4288 gtk_text_iter_forward_line(&iter);
4291 if (startq_offset != -1) {
4292 GtkTextIter startquote, endquote;
4293 gtk_text_buffer_get_iter_at_offset(
4294 buffer, &startquote, startq_offset);
4297 switch (quotelevel) {
4299 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4300 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4301 gtk_text_buffer_apply_tag_by_name(
4302 buffer, "quote0", &startquote, &endquote);
4303 gtk_text_buffer_remove_tag_by_name(
4304 buffer, "quote1", &startquote, &endquote);
4305 gtk_text_buffer_remove_tag_by_name(
4306 buffer, "quote2", &startquote, &endquote);
4311 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4312 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4313 gtk_text_buffer_apply_tag_by_name(
4314 buffer, "quote1", &startquote, &endquote);
4315 gtk_text_buffer_remove_tag_by_name(
4316 buffer, "quote0", &startquote, &endquote);
4317 gtk_text_buffer_remove_tag_by_name(
4318 buffer, "quote2", &startquote, &endquote);
4323 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4324 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4325 gtk_text_buffer_apply_tag_by_name(
4326 buffer, "quote2", &startquote, &endquote);
4327 gtk_text_buffer_remove_tag_by_name(
4328 buffer, "quote0", &startquote, &endquote);
4329 gtk_text_buffer_remove_tag_by_name(
4330 buffer, "quote1", &startquote, &endquote);
4336 } else if (noq_offset != -1) {
4337 GtkTextIter startnoquote, endnoquote;
4338 gtk_text_buffer_get_iter_at_offset(
4339 buffer, &startnoquote, noq_offset);
4342 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4343 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4344 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4345 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4346 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4347 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4348 gtk_text_buffer_remove_tag_by_name(
4349 buffer, "quote0", &startnoquote, &endnoquote);
4350 gtk_text_buffer_remove_tag_by_name(
4351 buffer, "quote1", &startnoquote, &endnoquote);
4352 gtk_text_buffer_remove_tag_by_name(
4353 buffer, "quote2", &startnoquote, &endnoquote);
4359 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4360 GtkTextIter nouri_start_iter, nouri_end_iter;
4361 gtk_text_buffer_get_iter_at_offset(
4362 buffer, &nouri_start_iter, nouri_start);
4363 gtk_text_buffer_get_iter_at_offset(
4364 buffer, &nouri_end_iter, nouri_stop);
4365 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4366 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4367 gtk_text_buffer_remove_tag_by_name(
4368 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4369 modified_before_remove = modified;
4374 if (uri_start >= 0 && uri_stop > 0) {
4375 GtkTextIter uri_start_iter, uri_end_iter, back;
4376 gtk_text_buffer_get_iter_at_offset(
4377 buffer, &uri_start_iter, uri_start);
4378 gtk_text_buffer_get_iter_at_offset(
4379 buffer, &uri_end_iter, uri_stop);
4380 back = uri_end_iter;
4381 gtk_text_iter_backward_char(&back);
4382 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4383 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4384 gtk_text_buffer_apply_tag_by_name(
4385 buffer, "link", &uri_start_iter, &uri_end_iter);
4387 if (removed && !modified_before_remove) {
4393 // debug_print("not modified, out after %d lines\n", lines);
4397 // debug_print("modified, out after %d lines\n", lines);
4399 g_free(itemized_chars);
4402 undo_wrapping(compose->undostruct, FALSE);
4403 compose->autowrap = prev_autowrap;
4408 void compose_action_cb(void *data)
4410 Compose *compose = (Compose *)data;
4411 compose_wrap_all(compose);
4414 static void compose_wrap_all(Compose *compose)
4416 compose_wrap_all_full(compose, FALSE);
4419 static void compose_wrap_all_full(Compose *compose, gboolean force)
4421 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4422 GtkTextBuffer *buffer;
4424 gboolean modified = TRUE;
4426 buffer = gtk_text_view_get_buffer(text);
4428 gtk_text_buffer_get_start_iter(buffer, &iter);
4429 while (!gtk_text_iter_is_end(&iter) && modified)
4430 modified = compose_beautify_paragraph(compose, &iter, force);
4434 static void compose_set_title(Compose *compose)
4440 edited = compose->modified ? _(" [Edited]") : "";
4442 subject = gtk_editable_get_chars(
4443 GTK_EDITABLE(compose->subject_entry), 0, -1);
4445 #ifndef GENERIC_UMPC
4446 if (subject && strlen(subject))
4447 str = g_strdup_printf(_("%s - Compose message%s"),
4450 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4452 str = g_strdup(_("Compose message"));
4455 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4461 * compose_current_mail_account:
4463 * Find a current mail account (the currently selected account, or the
4464 * default account, if a news account is currently selected). If a
4465 * mail account cannot be found, display an error message.
4467 * Return value: Mail account, or NULL if not found.
4469 static PrefsAccount *
4470 compose_current_mail_account(void)
4474 if (cur_account && cur_account->protocol != A_NNTP)
4477 ac = account_get_default();
4478 if (!ac || ac->protocol == A_NNTP) {
4479 alertpanel_error(_("Account for sending mail is not specified.\n"
4480 "Please select a mail account before sending."));
4487 #define QUOTE_IF_REQUIRED(out, str) \
4489 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4493 len = strlen(str) + 3; \
4494 if ((__tmp = alloca(len)) == NULL) { \
4495 g_warning("can't allocate memory\n"); \
4496 g_string_free(header, TRUE); \
4499 g_snprintf(__tmp, len, "\"%s\"", str); \
4504 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4505 g_warning("can't allocate memory\n"); \
4506 g_string_free(header, TRUE); \
4509 strcpy(__tmp, str); \
4515 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4517 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4521 len = strlen(str) + 3; \
4522 if ((__tmp = alloca(len)) == NULL) { \
4523 g_warning("can't allocate memory\n"); \
4526 g_snprintf(__tmp, len, "\"%s\"", str); \
4531 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4532 g_warning("can't allocate memory\n"); \
4535 strcpy(__tmp, str); \
4541 static void compose_select_account(Compose *compose, PrefsAccount *account,
4544 gchar *from = NULL, *header;
4545 ComposeHeaderEntry *header_entry;
4547 cm_return_if_fail(account != NULL);
4549 compose->account = account;
4551 if (account->name && *account->name) {
4553 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4554 from = g_strdup_printf("%s <%s>",
4555 buf, account->address);
4556 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4558 from = g_strdup_printf("<%s>",
4560 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4565 compose_set_title(compose);
4567 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4568 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4571 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4572 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4574 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4576 activate_privacy_system(compose, account, FALSE);
4578 if (!init && compose->mode != COMPOSE_REDIRECT) {
4579 undo_block(compose->undostruct);
4580 compose_insert_sig(compose, TRUE);
4581 undo_unblock(compose->undostruct);
4584 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4585 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4587 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4588 if (account->protocol == A_NNTP) {
4589 if (!strcmp(header, _("To:")))
4590 combobox_select_by_text(
4591 GTK_COMBO_BOX(header_entry->combo),
4594 if (!strcmp(header, _("Newsgroups:")))
4595 combobox_select_by_text(
4596 GTK_COMBO_BOX(header_entry->combo),
4604 /* use account's dict info if set */
4605 if (compose->gtkaspell) {
4606 if (account->enable_default_dictionary)
4607 gtkaspell_change_dict(compose->gtkaspell,
4608 account->default_dictionary, FALSE);
4609 if (account->enable_default_alt_dictionary)
4610 gtkaspell_change_alt_dict(compose->gtkaspell,
4611 account->default_alt_dictionary);
4612 if (account->enable_default_dictionary
4613 || account->enable_default_alt_dictionary)
4614 compose_spell_menu_changed(compose);
4619 gboolean compose_check_for_valid_recipient(Compose *compose) {
4620 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4621 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4622 gboolean recipient_found = FALSE;
4626 /* free to and newsgroup list */
4627 slist_free_strings(compose->to_list);
4628 g_slist_free(compose->to_list);
4629 compose->to_list = NULL;
4631 slist_free_strings(compose->newsgroup_list);
4632 g_slist_free(compose->newsgroup_list);
4633 compose->newsgroup_list = NULL;
4635 /* search header entries for to and newsgroup entries */
4636 for (list = compose->header_list; list; list = list->next) {
4639 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4640 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4643 if (entry[0] != '\0') {
4644 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4645 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4646 compose->to_list = address_list_append(compose->to_list, entry);
4647 recipient_found = TRUE;
4650 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4651 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4652 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4653 recipient_found = TRUE;
4660 return recipient_found;
4663 static gboolean compose_check_for_set_recipients(Compose *compose)
4665 if (compose->account->set_autocc && compose->account->auto_cc) {
4666 gboolean found_other = FALSE;
4668 /* search header entries for to and newsgroup entries */
4669 for (list = compose->header_list; list; list = list->next) {
4672 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4673 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4676 if (strcmp(entry, compose->account->auto_cc)
4677 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4687 if (compose->batch) {
4688 gtk_widget_show_all(compose->window);
4690 aval = alertpanel(_("Send"),
4691 _("The only recipient is the default CC address. Send anyway?"),
4692 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4693 if (aval != G_ALERTALTERNATE)
4697 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4698 gboolean found_other = FALSE;
4700 /* search header entries for to and newsgroup entries */
4701 for (list = compose->header_list; list; list = list->next) {
4704 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4705 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4708 if (strcmp(entry, compose->account->auto_bcc)
4709 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4719 if (compose->batch) {
4720 gtk_widget_show_all(compose->window);
4722 aval = alertpanel(_("Send"),
4723 _("The only recipient is the default BCC address. Send anyway?"),
4724 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4725 if (aval != G_ALERTALTERNATE)
4732 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4736 if (compose_check_for_valid_recipient(compose) == FALSE) {
4737 if (compose->batch) {
4738 gtk_widget_show_all(compose->window);
4740 alertpanel_error(_("Recipient is not specified."));
4744 if (compose_check_for_set_recipients(compose) == FALSE) {
4748 if (!compose->batch) {
4749 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4750 if (*str == '\0' && check_everything == TRUE &&
4751 compose->mode != COMPOSE_REDIRECT) {
4753 gchar *button_label;
4756 if (compose->sending)
4757 button_label = _("+_Send");
4759 button_label = _("+_Queue");
4760 message = g_strdup_printf(_("Subject is empty. %s"),
4761 compose->sending?_("Send it anyway?"):
4762 _("Queue it anyway?"));
4764 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4765 GTK_STOCK_CANCEL, button_label, NULL);
4767 if (aval != G_ALERTALTERNATE)
4772 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4778 gint compose_send(Compose *compose)
4781 FolderItem *folder = NULL;
4783 gchar *msgpath = NULL;
4784 gboolean discard_window = FALSE;
4785 gchar *errstr = NULL;
4786 gchar *tmsgid = NULL;
4787 MainWindow *mainwin = mainwindow_get_mainwindow();
4788 gboolean queued_removed = FALSE;
4790 if (prefs_common.send_dialog_invisible
4791 || compose->batch == TRUE)
4792 discard_window = TRUE;
4794 compose_allow_user_actions (compose, FALSE);
4795 compose->sending = TRUE;
4797 if (compose_check_entries(compose, TRUE) == FALSE) {
4798 if (compose->batch) {
4799 gtk_widget_show_all(compose->window);
4805 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4808 if (compose->batch) {
4809 gtk_widget_show_all(compose->window);
4812 alertpanel_error(_("Could not queue message for sending:\n\n"
4813 "Charset conversion failed."));
4814 } else if (val == -5) {
4815 alertpanel_error(_("Could not queue message for sending:\n\n"
4816 "Couldn't get recipient encryption key."));
4817 } else if (val == -6) {
4819 } else if (val == -3) {
4820 if (privacy_peek_error())
4821 alertpanel_error(_("Could not queue message for sending:\n\n"
4822 "Signature failed: %s"), privacy_get_error());
4823 } else if (val == -2 && errno != 0) {
4824 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4826 alertpanel_error(_("Could not queue message for sending."));
4831 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4832 if (discard_window) {
4833 compose->sending = FALSE;
4834 compose_close(compose);
4835 /* No more compose access in the normal codepath
4836 * after this point! */
4841 alertpanel_error(_("The message was queued but could not be "
4842 "sent.\nUse \"Send queued messages\" from "
4843 "the main window to retry."));
4844 if (!discard_window) {
4851 if (msgpath == NULL) {
4852 msgpath = folder_item_fetch_msg(folder, msgnum);
4853 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4856 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4857 claws_unlink(msgpath);
4860 if (!discard_window) {
4862 if (!queued_removed)
4863 folder_item_remove_msg(folder, msgnum);
4864 folder_item_scan(folder);
4866 /* make sure we delete that */
4867 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4869 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4870 folder_item_remove_msg(folder, tmp->msgnum);
4871 procmsg_msginfo_free(tmp);
4878 if (!queued_removed)
4879 folder_item_remove_msg(folder, msgnum);
4880 folder_item_scan(folder);
4882 /* make sure we delete that */
4883 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4885 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4886 folder_item_remove_msg(folder, tmp->msgnum);
4887 procmsg_msginfo_free(tmp);
4890 if (!discard_window) {
4891 compose->sending = FALSE;
4892 compose_allow_user_actions (compose, TRUE);
4893 compose_close(compose);
4897 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4898 "the main window to retry."), errstr);
4901 alertpanel_error_log(_("The message was queued but could not be "
4902 "sent.\nUse \"Send queued messages\" from "
4903 "the main window to retry."));
4905 if (!discard_window) {
4914 toolbar_main_set_sensitive(mainwin);
4915 main_window_set_menu_sensitive(mainwin);
4921 compose_allow_user_actions (compose, TRUE);
4922 compose->sending = FALSE;
4923 compose->modified = TRUE;
4924 toolbar_main_set_sensitive(mainwin);
4925 main_window_set_menu_sensitive(mainwin);
4930 static gboolean compose_use_attach(Compose *compose)
4932 GtkTreeModel *model = gtk_tree_view_get_model
4933 (GTK_TREE_VIEW(compose->attach_clist));
4934 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4937 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4940 gchar buf[BUFFSIZE];
4942 gboolean first_to_address;
4943 gboolean first_cc_address;
4945 ComposeHeaderEntry *headerentry;
4946 const gchar *headerentryname;
4947 const gchar *cc_hdr;
4948 const gchar *to_hdr;
4949 gboolean err = FALSE;
4951 debug_print("Writing redirect header\n");
4953 cc_hdr = prefs_common_translated_header_name("Cc:");
4954 to_hdr = prefs_common_translated_header_name("To:");
4956 first_to_address = TRUE;
4957 for (list = compose->header_list; list; list = list->next) {
4958 headerentry = ((ComposeHeaderEntry *)list->data);
4959 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4961 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4962 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4963 Xstrdup_a(str, entstr, return -1);
4965 if (str[0] != '\0') {
4966 compose_convert_header
4967 (compose, buf, sizeof(buf), str,
4968 strlen("Resent-To") + 2, TRUE);
4970 if (first_to_address) {
4971 err |= (fprintf(fp, "Resent-To: ") < 0);
4972 first_to_address = FALSE;
4974 err |= (fprintf(fp, ",") < 0);
4976 err |= (fprintf(fp, "%s", buf) < 0);
4980 if (!first_to_address) {
4981 err |= (fprintf(fp, "\n") < 0);
4984 first_cc_address = TRUE;
4985 for (list = compose->header_list; list; list = list->next) {
4986 headerentry = ((ComposeHeaderEntry *)list->data);
4987 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4989 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4990 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4991 Xstrdup_a(str, strg, return -1);
4993 if (str[0] != '\0') {
4994 compose_convert_header
4995 (compose, buf, sizeof(buf), str,
4996 strlen("Resent-Cc") + 2, TRUE);
4998 if (first_cc_address) {
4999 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5000 first_cc_address = FALSE;
5002 err |= (fprintf(fp, ",") < 0);
5004 err |= (fprintf(fp, "%s", buf) < 0);
5008 if (!first_cc_address) {
5009 err |= (fprintf(fp, "\n") < 0);
5012 return (err ? -1:0);
5015 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5017 gchar buf[BUFFSIZE];
5019 const gchar *entstr;
5020 /* struct utsname utsbuf; */
5021 gboolean err = FALSE;
5023 cm_return_val_if_fail(fp != NULL, -1);
5024 cm_return_val_if_fail(compose->account != NULL, -1);
5025 cm_return_val_if_fail(compose->account->address != NULL, -1);
5028 get_rfc822_date(buf, sizeof(buf));
5029 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5032 if (compose->account->name && *compose->account->name) {
5033 compose_convert_header
5034 (compose, buf, sizeof(buf), compose->account->name,
5035 strlen("From: "), TRUE);
5036 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5037 buf, compose->account->address) < 0);
5039 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5042 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5043 if (*entstr != '\0') {
5044 Xstrdup_a(str, entstr, return -1);
5047 compose_convert_header(compose, buf, sizeof(buf), str,
5048 strlen("Subject: "), FALSE);
5049 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5053 /* Resent-Message-ID */
5054 if (compose->account->set_domain && compose->account->domain) {
5055 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5056 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5057 g_snprintf(buf, sizeof(buf), "%s",
5058 strchr(compose->account->address, '@') ?
5059 strchr(compose->account->address, '@')+1 :
5060 compose->account->address);
5062 g_snprintf(buf, sizeof(buf), "%s", "");
5065 if (compose->account->gen_msgid) {
5067 if (compose->account->msgid_with_addr) {
5068 addr = compose->account->address;
5070 generate_msgid(buf, sizeof(buf), addr);
5071 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5072 compose->msgid = g_strdup(buf);
5074 compose->msgid = NULL;
5077 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5080 /* separator between header and body */
5081 err |= (fputs("\n", fp) == EOF);
5083 return (err ? -1:0);
5086 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5090 gchar buf[BUFFSIZE];
5092 gboolean skip = FALSE;
5093 gboolean err = FALSE;
5094 gchar *not_included[]={
5095 "Return-Path:", "Delivered-To:", "Received:",
5096 "Subject:", "X-UIDL:", "AF:",
5097 "NF:", "PS:", "SRH:",
5098 "SFN:", "DSR:", "MID:",
5099 "CFG:", "PT:", "S:",
5100 "RQ:", "SSV:", "NSV:",
5101 "SSH:", "R:", "MAID:",
5102 "NAID:", "RMID:", "FMID:",
5103 "SCF:", "RRCPT:", "NG:",
5104 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5105 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5106 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5107 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5108 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5111 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5112 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5116 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5118 for (i = 0; not_included[i] != NULL; i++) {
5119 if (g_ascii_strncasecmp(buf, not_included[i],
5120 strlen(not_included[i])) == 0) {
5127 if (fputs(buf, fdest) == -1)
5130 if (!prefs_common.redirect_keep_from) {
5131 if (g_ascii_strncasecmp(buf, "From:",
5132 strlen("From:")) == 0) {
5133 err |= (fputs(" (by way of ", fdest) == EOF);
5134 if (compose->account->name
5135 && *compose->account->name) {
5136 compose_convert_header
5137 (compose, buf, sizeof(buf),
5138 compose->account->name,
5141 err |= (fprintf(fdest, "%s <%s>",
5143 compose->account->address) < 0);
5145 err |= (fprintf(fdest, "%s",
5146 compose->account->address) < 0);
5147 err |= (fputs(")", fdest) == EOF);
5151 if (fputs("\n", fdest) == -1)
5158 if (compose_redirect_write_headers(compose, fdest))
5161 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5162 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5175 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5177 GtkTextBuffer *buffer;
5178 GtkTextIter start, end;
5181 const gchar *out_codeset;
5182 EncodingType encoding = ENC_UNKNOWN;
5183 MimeInfo *mimemsg, *mimetext;
5185 const gchar *src_codeset = CS_INTERNAL;
5186 gchar *from_addr = NULL;
5187 gchar *from_name = NULL;
5189 if (action == COMPOSE_WRITE_FOR_SEND)
5190 attach_parts = TRUE;
5192 /* create message MimeInfo */
5193 mimemsg = procmime_mimeinfo_new();
5194 mimemsg->type = MIMETYPE_MESSAGE;
5195 mimemsg->subtype = g_strdup("rfc822");
5196 mimemsg->content = MIMECONTENT_MEM;
5197 mimemsg->tmp = TRUE; /* must free content later */
5198 mimemsg->data.mem = compose_get_header(compose);
5200 /* Create text part MimeInfo */
5201 /* get all composed text */
5202 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5203 gtk_text_buffer_get_start_iter(buffer, &start);
5204 gtk_text_buffer_get_end_iter(buffer, &end);
5205 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5207 out_codeset = conv_get_charset_str(compose->out_encoding);
5209 if (!out_codeset && is_ascii_str(chars)) {
5210 out_codeset = CS_US_ASCII;
5211 } else if (prefs_common.outgoing_fallback_to_ascii &&
5212 is_ascii_str(chars)) {
5213 out_codeset = CS_US_ASCII;
5214 encoding = ENC_7BIT;
5218 gchar *test_conv_global_out = NULL;
5219 gchar *test_conv_reply = NULL;
5221 /* automatic mode. be automatic. */
5222 codeconv_set_strict(TRUE);
5224 out_codeset = conv_get_outgoing_charset_str();
5226 debug_print("trying to convert to %s\n", out_codeset);
5227 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5230 if (!test_conv_global_out && compose->orig_charset
5231 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5232 out_codeset = compose->orig_charset;
5233 debug_print("failure; trying to convert to %s\n", out_codeset);
5234 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5237 if (!test_conv_global_out && !test_conv_reply) {
5239 out_codeset = CS_INTERNAL;
5240 debug_print("failure; finally using %s\n", out_codeset);
5242 g_free(test_conv_global_out);
5243 g_free(test_conv_reply);
5244 codeconv_set_strict(FALSE);
5247 if (encoding == ENC_UNKNOWN) {
5248 if (prefs_common.encoding_method == CTE_BASE64)
5249 encoding = ENC_BASE64;
5250 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5251 encoding = ENC_QUOTED_PRINTABLE;
5252 else if (prefs_common.encoding_method == CTE_8BIT)
5253 encoding = ENC_8BIT;
5255 encoding = procmime_get_encoding_for_charset(out_codeset);
5258 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5259 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5261 if (action == COMPOSE_WRITE_FOR_SEND) {
5262 codeconv_set_strict(TRUE);
5263 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5264 codeconv_set_strict(FALSE);
5270 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5271 "to the specified %s charset.\n"
5272 "Send it as %s?"), out_codeset, src_codeset);
5273 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5274 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5277 if (aval != G_ALERTALTERNATE) {
5282 out_codeset = src_codeset;
5288 out_codeset = src_codeset;
5293 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5294 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5295 strstr(buf, "\nFrom ") != NULL) {
5296 encoding = ENC_QUOTED_PRINTABLE;
5300 mimetext = procmime_mimeinfo_new();
5301 mimetext->content = MIMECONTENT_MEM;
5302 mimetext->tmp = TRUE; /* must free content later */
5303 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5304 * and free the data, which we need later. */
5305 mimetext->data.mem = g_strdup(buf);
5306 mimetext->type = MIMETYPE_TEXT;
5307 mimetext->subtype = g_strdup("plain");
5308 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5309 g_strdup(out_codeset));
5311 /* protect trailing spaces when signing message */
5312 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5313 privacy_system_can_sign(compose->privacy_system)) {
5314 encoding = ENC_QUOTED_PRINTABLE;
5317 debug_print("main text: %zd bytes encoded as %s in %d\n",
5318 strlen(buf), out_codeset, encoding);
5320 /* check for line length limit */
5321 if (action == COMPOSE_WRITE_FOR_SEND &&
5322 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5323 check_line_length(buf, 1000, &line) < 0) {
5327 msg = g_strdup_printf
5328 (_("Line %d exceeds the line length limit (998 bytes).\n"
5329 "The contents of the message might be broken on the way to the delivery.\n"
5331 "Send it anyway?"), line + 1);
5332 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5334 if (aval != G_ALERTALTERNATE) {
5340 if (encoding != ENC_UNKNOWN)
5341 procmime_encode_content(mimetext, encoding);
5343 /* append attachment parts */
5344 if (compose_use_attach(compose) && attach_parts) {
5345 MimeInfo *mimempart;
5346 gchar *boundary = NULL;
5347 mimempart = procmime_mimeinfo_new();
5348 mimempart->content = MIMECONTENT_EMPTY;
5349 mimempart->type = MIMETYPE_MULTIPART;
5350 mimempart->subtype = g_strdup("mixed");
5354 boundary = generate_mime_boundary(NULL);
5355 } while (strstr(buf, boundary) != NULL);
5357 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5360 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5362 g_node_append(mimempart->node, mimetext->node);
5363 g_node_append(mimemsg->node, mimempart->node);
5365 if (compose_add_attachments(compose, mimempart) < 0)
5368 g_node_append(mimemsg->node, mimetext->node);
5372 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5373 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5374 /* extract name and address */
5375 if (strstr(spec, " <") && strstr(spec, ">")) {
5376 from_addr = g_strdup(strrchr(spec, '<')+1);
5377 *(strrchr(from_addr, '>')) = '\0';
5378 from_name = g_strdup(spec);
5379 *(strrchr(from_name, '<')) = '\0';
5386 /* sign message if sending */
5387 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5388 privacy_system_can_sign(compose->privacy_system))
5389 if (!privacy_sign(compose->privacy_system, mimemsg,
5390 compose->account, from_addr)) {
5397 procmime_write_mimeinfo(mimemsg, fp);
5399 procmime_mimeinfo_free_all(mimemsg);
5404 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5406 GtkTextBuffer *buffer;
5407 GtkTextIter start, end;
5412 if ((fp = g_fopen(file, "wb")) == NULL) {
5413 FILE_OP_ERROR(file, "fopen");
5417 /* chmod for security */
5418 if (change_file_mode_rw(fp, file) < 0) {
5419 FILE_OP_ERROR(file, "chmod");
5420 g_warning("can't change file mode\n");
5423 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5424 gtk_text_buffer_get_start_iter(buffer, &start);
5425 gtk_text_buffer_get_end_iter(buffer, &end);
5426 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5428 chars = conv_codeset_strdup
5429 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5432 if (!chars) return -1;
5435 len = strlen(chars);
5436 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5437 FILE_OP_ERROR(file, "fwrite");
5446 if (fclose(fp) == EOF) {
5447 FILE_OP_ERROR(file, "fclose");
5454 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5457 MsgInfo *msginfo = compose->targetinfo;
5459 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5460 if (!msginfo) return -1;
5462 if (!force && MSG_IS_LOCKED(msginfo->flags))
5465 item = msginfo->folder;
5466 cm_return_val_if_fail(item != NULL, -1);
5468 if (procmsg_msg_exist(msginfo) &&
5469 (folder_has_parent_of_type(item, F_QUEUE) ||
5470 folder_has_parent_of_type(item, F_DRAFT)
5471 || msginfo == compose->autosaved_draft)) {
5472 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5473 g_warning("can't remove the old message\n");
5476 debug_print("removed reedit target %d\n", msginfo->msgnum);
5483 static void compose_remove_draft(Compose *compose)
5486 MsgInfo *msginfo = compose->targetinfo;
5487 drafts = account_get_special_folder(compose->account, F_DRAFT);
5489 if (procmsg_msg_exist(msginfo)) {
5490 folder_item_remove_msg(drafts, msginfo->msgnum);
5495 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5496 gboolean remove_reedit_target)
5498 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5501 static gboolean compose_warn_encryption(Compose *compose)
5503 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5504 AlertValue val = G_ALERTALTERNATE;
5506 if (warning == NULL)
5509 val = alertpanel_full(_("Encryption warning"), warning,
5510 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5511 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5512 if (val & G_ALERTDISABLE) {
5513 val &= ~G_ALERTDISABLE;
5514 if (val == G_ALERTALTERNATE)
5515 privacy_inhibit_encrypt_warning(compose->privacy_system,
5519 if (val == G_ALERTALTERNATE) {
5526 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5527 gchar **msgpath, gboolean check_subject,
5528 gboolean remove_reedit_target)
5535 static gboolean lock = FALSE;
5536 PrefsAccount *mailac = NULL, *newsac = NULL;
5537 gboolean err = FALSE;
5539 debug_print("queueing message...\n");
5540 cm_return_val_if_fail(compose->account != NULL, -1);
5544 if (compose_check_entries(compose, check_subject) == FALSE) {
5546 if (compose->batch) {
5547 gtk_widget_show_all(compose->window);
5552 if (!compose->to_list && !compose->newsgroup_list) {
5553 g_warning("can't get recipient list.");
5558 if (compose->to_list) {
5559 if (compose->account->protocol != A_NNTP)
5560 mailac = compose->account;
5561 else if (cur_account && cur_account->protocol != A_NNTP)
5562 mailac = cur_account;
5563 else if (!(mailac = compose_current_mail_account())) {
5565 alertpanel_error(_("No account for sending mails available!"));
5570 if (compose->newsgroup_list) {
5571 if (compose->account->protocol == A_NNTP)
5572 newsac = compose->account;
5575 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5580 /* write queue header */
5581 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5582 G_DIR_SEPARATOR, compose, (guint) rand());
5583 debug_print("queuing to %s\n", tmp);
5584 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5585 FILE_OP_ERROR(tmp, "fopen");
5591 if (change_file_mode_rw(fp, tmp) < 0) {
5592 FILE_OP_ERROR(tmp, "chmod");
5593 g_warning("can't change file mode\n");
5596 /* queueing variables */
5597 err |= (fprintf(fp, "AF:\n") < 0);
5598 err |= (fprintf(fp, "NF:0\n") < 0);
5599 err |= (fprintf(fp, "PS:10\n") < 0);
5600 err |= (fprintf(fp, "SRH:1\n") < 0);
5601 err |= (fprintf(fp, "SFN:\n") < 0);
5602 err |= (fprintf(fp, "DSR:\n") < 0);
5604 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5606 err |= (fprintf(fp, "MID:\n") < 0);
5607 err |= (fprintf(fp, "CFG:\n") < 0);
5608 err |= (fprintf(fp, "PT:0\n") < 0);
5609 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5610 err |= (fprintf(fp, "RQ:\n") < 0);
5612 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5614 err |= (fprintf(fp, "SSV:\n") < 0);
5616 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5618 err |= (fprintf(fp, "NSV:\n") < 0);
5619 err |= (fprintf(fp, "SSH:\n") < 0);
5620 /* write recepient list */
5621 if (compose->to_list) {
5622 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5623 for (cur = compose->to_list->next; cur != NULL;
5625 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5626 err |= (fprintf(fp, "\n") < 0);
5628 /* write newsgroup list */
5629 if (compose->newsgroup_list) {
5630 err |= (fprintf(fp, "NG:") < 0);
5631 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5632 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5633 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5634 err |= (fprintf(fp, "\n") < 0);
5636 /* Sylpheed account IDs */
5638 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5640 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5643 if (compose->privacy_system != NULL) {
5644 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5645 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5646 if (compose->use_encryption) {
5648 if (!compose_warn_encryption(compose)) {
5655 if (mailac && mailac->encrypt_to_self) {
5656 GSList *tmp_list = g_slist_copy(compose->to_list);
5657 tmp_list = g_slist_append(tmp_list, compose->account->address);
5658 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5659 g_slist_free(tmp_list);
5661 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5663 if (encdata != NULL) {
5664 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5665 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5666 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5668 } /* else we finally dont want to encrypt */
5670 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5671 /* and if encdata was null, it means there's been a problem in
5683 /* Save copy folder */
5684 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5685 gchar *savefolderid;
5687 savefolderid = compose_get_save_to(compose);
5688 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5689 g_free(savefolderid);
5691 /* Save copy folder */
5692 if (compose->return_receipt) {
5693 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5695 /* Message-ID of message replying to */
5696 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5699 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5700 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5703 /* Message-ID of message forwarding to */
5704 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5707 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5708 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5712 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5713 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5715 /* end of headers */
5716 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5718 if (compose->redirect_filename != NULL) {
5719 if (compose_redirect_write_to_file(compose, fp) < 0) {
5728 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5733 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5737 g_warning("failed to write queue message\n");
5744 if (fclose(fp) == EOF) {
5745 FILE_OP_ERROR(tmp, "fclose");
5752 if (item && *item) {
5755 queue = account_get_special_folder(compose->account, F_QUEUE);
5758 g_warning("can't find queue folder\n");
5764 folder_item_scan(queue);
5765 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5766 g_warning("can't queue the message\n");
5773 if (msgpath == NULL) {
5779 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5780 compose_remove_reedit_target(compose, FALSE);
5783 if ((msgnum != NULL) && (item != NULL)) {
5791 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5794 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5796 struct stat statbuf;
5797 gchar *type, *subtype;
5798 GtkTreeModel *model;
5801 model = gtk_tree_view_get_model(tree_view);
5803 if (!gtk_tree_model_get_iter_first(model, &iter))
5806 gtk_tree_model_get(model, &iter,
5810 if (!is_file_exist(ainfo->file)) {
5811 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5812 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5813 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5815 if (val == G_ALERTDEFAULT) {
5820 mimepart = procmime_mimeinfo_new();
5821 mimepart->content = MIMECONTENT_FILE;
5822 mimepart->data.filename = g_strdup(ainfo->file);
5823 mimepart->tmp = FALSE; /* or we destroy our attachment */
5824 mimepart->offset = 0;
5826 g_stat(ainfo->file, &statbuf);
5827 mimepart->length = statbuf.st_size;
5829 type = g_strdup(ainfo->content_type);
5831 if (!strchr(type, '/')) {
5833 type = g_strdup("application/octet-stream");
5836 subtype = strchr(type, '/') + 1;
5837 *(subtype - 1) = '\0';
5838 mimepart->type = procmime_get_media_type(type);
5839 mimepart->subtype = g_strdup(subtype);
5842 if (mimepart->type == MIMETYPE_MESSAGE &&
5843 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5844 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5847 if (mimepart->type == MIMETYPE_APPLICATION &&
5848 !strcmp2(mimepart->subtype, "octet-stream"))
5849 g_hash_table_insert(mimepart->typeparameters,
5850 g_strdup("name"), g_strdup(ainfo->name));
5851 g_hash_table_insert(mimepart->dispositionparameters,
5852 g_strdup("filename"), g_strdup(ainfo->name));
5853 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5857 if (compose->use_signing) {
5858 if (ainfo->encoding == ENC_7BIT)
5859 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5860 else if (ainfo->encoding == ENC_8BIT)
5861 ainfo->encoding = ENC_BASE64;
5864 procmime_encode_content(mimepart, ainfo->encoding);
5866 g_node_append(parent->node, mimepart->node);
5867 } while (gtk_tree_model_iter_next(model, &iter));
5872 #define IS_IN_CUSTOM_HEADER(header) \
5873 (compose->account->add_customhdr && \
5874 custom_header_find(compose->account->customhdr_list, header) != NULL)
5876 static void compose_add_headerfield_from_headerlist(Compose *compose,
5878 const gchar *fieldname,
5879 const gchar *seperator)
5881 gchar *str, *fieldname_w_colon;
5882 gboolean add_field = FALSE;
5884 ComposeHeaderEntry *headerentry;
5885 const gchar *headerentryname;
5886 const gchar *trans_fieldname;
5889 if (IS_IN_CUSTOM_HEADER(fieldname))
5892 debug_print("Adding %s-fields\n", fieldname);
5894 fieldstr = g_string_sized_new(64);
5896 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5897 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5899 for (list = compose->header_list; list; list = list->next) {
5900 headerentry = ((ComposeHeaderEntry *)list->data);
5901 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5903 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5904 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5906 if (str[0] != '\0') {
5908 g_string_append(fieldstr, seperator);
5909 g_string_append(fieldstr, str);
5918 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5919 compose_convert_header
5920 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5921 strlen(fieldname) + 2, TRUE);
5922 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5926 g_free(fieldname_w_colon);
5927 g_string_free(fieldstr, TRUE);
5932 static gchar *compose_get_header(Compose *compose)
5934 gchar buf[BUFFSIZE];
5935 const gchar *entry_str;
5939 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5941 gchar *from_name = NULL, *from_address = NULL;
5944 cm_return_val_if_fail(compose->account != NULL, NULL);
5945 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5947 header = g_string_sized_new(64);
5950 get_rfc822_date(buf, sizeof(buf));
5951 g_string_append_printf(header, "Date: %s\n", buf);
5955 if (compose->account->name && *compose->account->name) {
5957 QUOTE_IF_REQUIRED(buf, compose->account->name);
5958 tmp = g_strdup_printf("%s <%s>",
5959 buf, compose->account->address);
5961 tmp = g_strdup_printf("%s",
5962 compose->account->address);
5964 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5965 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5967 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5968 from_address = g_strdup(compose->account->address);
5970 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5971 /* extract name and address */
5972 if (strstr(spec, " <") && strstr(spec, ">")) {
5973 from_address = g_strdup(strrchr(spec, '<')+1);
5974 *(strrchr(from_address, '>')) = '\0';
5975 from_name = g_strdup(spec);
5976 *(strrchr(from_name, '<')) = '\0';
5979 from_address = g_strdup(spec);
5986 if (from_name && *from_name) {
5987 compose_convert_header
5988 (compose, buf, sizeof(buf), from_name,
5989 strlen("From: "), TRUE);
5990 QUOTE_IF_REQUIRED(name, buf);
5992 g_string_append_printf(header, "From: %s <%s>\n",
5993 name, from_address);
5995 g_string_append_printf(header, "From: %s\n", from_address);
5998 g_free(from_address);
6001 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6004 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6007 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6010 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6013 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6015 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6018 compose_convert_header(compose, buf, sizeof(buf), str,
6019 strlen("Subject: "), FALSE);
6020 g_string_append_printf(header, "Subject: %s\n", buf);
6026 if (compose->account->set_domain && compose->account->domain) {
6027 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6028 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6029 g_snprintf(buf, sizeof(buf), "%s",
6030 strchr(compose->account->address, '@') ?
6031 strchr(compose->account->address, '@')+1 :
6032 compose->account->address);
6034 g_snprintf(buf, sizeof(buf), "%s", "");
6037 if (compose->account->gen_msgid) {
6039 if (compose->account->msgid_with_addr) {
6040 addr = compose->account->address;
6042 generate_msgid(buf, sizeof(buf), addr);
6043 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6044 compose->msgid = g_strdup(buf);
6046 compose->msgid = NULL;
6049 if (compose->remove_references == FALSE) {
6051 if (compose->inreplyto && compose->to_list)
6052 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6055 if (compose->references)
6056 g_string_append_printf(header, "References: %s\n", compose->references);
6060 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6063 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6066 if (compose->account->organization &&
6067 strlen(compose->account->organization) &&
6068 !IS_IN_CUSTOM_HEADER("Organization")) {
6069 compose_convert_header(compose, buf, sizeof(buf),
6070 compose->account->organization,
6071 strlen("Organization: "), FALSE);
6072 g_string_append_printf(header, "Organization: %s\n", buf);
6075 /* Program version and system info */
6076 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6077 !compose->newsgroup_list) {
6078 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6080 gtk_major_version, gtk_minor_version, gtk_micro_version,
6083 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6084 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6086 gtk_major_version, gtk_minor_version, gtk_micro_version,
6090 /* custom headers */
6091 if (compose->account->add_customhdr) {
6094 for (cur = compose->account->customhdr_list; cur != NULL;
6096 CustomHeader *chdr = (CustomHeader *)cur->data;
6098 if (custom_header_is_allowed(chdr->name)) {
6099 compose_convert_header
6100 (compose, buf, sizeof(buf),
6101 chdr->value ? chdr->value : "",
6102 strlen(chdr->name) + 2, FALSE);
6103 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6108 /* Automatic Faces and X-Faces */
6109 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6110 g_string_append_printf(header, "X-Face: %s\n", buf);
6112 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6113 g_string_append_printf(header, "X-Face: %s\n", buf);
6115 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6116 g_string_append_printf(header, "Face: %s\n", buf);
6118 else if (get_default_face (buf, sizeof(buf)) == 0) {
6119 g_string_append_printf(header, "Face: %s\n", buf);
6123 switch (compose->priority) {
6124 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6125 "X-Priority: 1 (Highest)\n");
6127 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6128 "X-Priority: 2 (High)\n");
6130 case PRIORITY_NORMAL: break;
6131 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6132 "X-Priority: 4 (Low)\n");
6134 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6135 "X-Priority: 5 (Lowest)\n");
6137 default: debug_print("compose: priority unknown : %d\n",
6141 /* Request Return Receipt */
6142 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6143 if (compose->return_receipt) {
6144 if (compose->account->name
6145 && *compose->account->name) {
6146 compose_convert_header(compose, buf, sizeof(buf),
6147 compose->account->name,
6148 strlen("Disposition-Notification-To: "),
6150 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6152 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6156 /* get special headers */
6157 for (list = compose->header_list; list; list = list->next) {
6158 ComposeHeaderEntry *headerentry;
6161 gchar *headername_wcolon;
6162 const gchar *headername_trans;
6165 gboolean standard_header = FALSE;
6167 headerentry = ((ComposeHeaderEntry *)list->data);
6169 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6171 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6176 if (!strstr(tmp, ":")) {
6177 headername_wcolon = g_strconcat(tmp, ":", NULL);
6178 headername = g_strdup(tmp);
6180 headername_wcolon = g_strdup(tmp);
6181 headername = g_strdup(strtok(tmp, ":"));
6185 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6186 Xstrdup_a(headervalue, entry_str, return NULL);
6187 subst_char(headervalue, '\r', ' ');
6188 subst_char(headervalue, '\n', ' ');
6189 string = std_headers;
6190 while (*string != NULL) {
6191 headername_trans = prefs_common_translated_header_name(*string);
6192 if (!strcmp(headername_trans, headername_wcolon))
6193 standard_header = TRUE;
6196 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6197 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6200 g_free(headername_wcolon);
6204 g_string_free(header, FALSE);
6209 #undef IS_IN_CUSTOM_HEADER
6211 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6212 gint header_len, gboolean addr_field)
6214 gchar *tmpstr = NULL;
6215 const gchar *out_codeset = NULL;
6217 cm_return_if_fail(src != NULL);
6218 cm_return_if_fail(dest != NULL);
6220 if (len < 1) return;
6222 tmpstr = g_strdup(src);
6224 subst_char(tmpstr, '\n', ' ');
6225 subst_char(tmpstr, '\r', ' ');
6228 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6229 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6230 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6235 codeconv_set_strict(TRUE);
6236 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6237 conv_get_charset_str(compose->out_encoding));
6238 codeconv_set_strict(FALSE);
6240 if (!dest || *dest == '\0') {
6241 gchar *test_conv_global_out = NULL;
6242 gchar *test_conv_reply = NULL;
6244 /* automatic mode. be automatic. */
6245 codeconv_set_strict(TRUE);
6247 out_codeset = conv_get_outgoing_charset_str();
6249 debug_print("trying to convert to %s\n", out_codeset);
6250 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6253 if (!test_conv_global_out && compose->orig_charset
6254 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6255 out_codeset = compose->orig_charset;
6256 debug_print("failure; trying to convert to %s\n", out_codeset);
6257 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6260 if (!test_conv_global_out && !test_conv_reply) {
6262 out_codeset = CS_INTERNAL;
6263 debug_print("finally using %s\n", out_codeset);
6265 g_free(test_conv_global_out);
6266 g_free(test_conv_reply);
6267 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6269 codeconv_set_strict(FALSE);
6274 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6278 cm_return_if_fail(user_data != NULL);
6280 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6281 g_strstrip(address);
6282 if (*address != '\0') {
6283 gchar *name = procheader_get_fromname(address);
6284 extract_address(address);
6285 addressbook_add_contact(name, address, NULL, NULL);
6290 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6292 GtkWidget *menuitem;
6295 cm_return_if_fail(menu != NULL);
6296 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6298 menuitem = gtk_separator_menu_item_new();
6299 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6300 gtk_widget_show(menuitem);
6302 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6303 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6305 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6306 g_strstrip(address);
6307 if (*address == '\0') {
6308 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6311 g_signal_connect(G_OBJECT(menuitem), "activate",
6312 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6313 gtk_widget_show(menuitem);
6316 static void compose_create_header_entry(Compose *compose)
6318 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6325 const gchar *header = NULL;
6326 ComposeHeaderEntry *headerentry;
6327 gboolean standard_header = FALSE;
6328 #if !(GTK_CHECK_VERSION(2,12,0))
6329 GtkTooltips *tips = compose->tooltips;
6332 headerentry = g_new0(ComposeHeaderEntry, 1);
6335 combo = gtk_combo_box_entry_new_text();
6337 while(*string != NULL) {
6338 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6339 (gchar*)prefs_common_translated_header_name(*string));
6342 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6343 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6344 G_CALLBACK(compose_grab_focus_cb), compose);
6345 gtk_widget_show(combo);
6346 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6347 compose->header_nextrow, compose->header_nextrow+1,
6348 GTK_SHRINK, GTK_FILL, 0, 0);
6349 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6350 const gchar *last_header_entry = gtk_entry_get_text(
6351 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6353 while (*string != NULL) {
6354 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6355 standard_header = TRUE;
6358 if (standard_header)
6359 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6361 if (!compose->header_last || !standard_header) {
6362 switch(compose->account->protocol) {
6364 header = prefs_common_translated_header_name("Newsgroups:");
6367 header = prefs_common_translated_header_name("To:");
6372 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6374 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6375 G_CALLBACK(compose_grab_focus_cb), compose);
6377 /* Entry field with cleanup button */
6378 #if GTK_CHECK_VERSION(2, 8, 0)
6379 button = gtk_button_new();
6380 gtk_button_set_image(GTK_BUTTON(button),
6381 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6383 button = gtk_button_new_with_label(_("Clear"));
6385 gtk_widget_show(button);
6386 CLAWS_SET_TIP(button,
6387 _("Delete entry contents"));
6388 entry = gtk_entry_new();
6389 gtk_widget_show(entry);
6390 CLAWS_SET_TIP(entry,
6391 _("Use <tab> to autocomplete from addressbook"));
6392 hbox = gtk_hbox_new (FALSE, 0);
6393 gtk_widget_show(hbox);
6394 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6395 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6396 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6397 compose->header_nextrow, compose->header_nextrow+1,
6398 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6400 g_signal_connect(G_OBJECT(entry), "key-press-event",
6401 G_CALLBACK(compose_headerentry_key_press_event_cb),
6403 g_signal_connect(G_OBJECT(entry), "changed",
6404 G_CALLBACK(compose_headerentry_changed_cb),
6406 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6407 G_CALLBACK(compose_grab_focus_cb), compose);
6409 g_signal_connect(G_OBJECT(button), "clicked",
6410 G_CALLBACK(compose_headerentry_button_clicked_cb),
6414 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6415 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6416 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6417 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6418 G_CALLBACK(compose_header_drag_received_cb),
6420 g_signal_connect(G_OBJECT(entry), "drag-drop",
6421 G_CALLBACK(compose_drag_drop),
6423 g_signal_connect(G_OBJECT(entry), "populate-popup",
6424 G_CALLBACK(compose_entry_popup_extend),
6427 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6429 headerentry->compose = compose;
6430 headerentry->combo = combo;
6431 headerentry->entry = entry;
6432 headerentry->button = button;
6433 headerentry->hbox = hbox;
6434 headerentry->headernum = compose->header_nextrow;
6436 compose->header_nextrow++;
6437 compose->header_last = headerentry;
6438 compose->header_list =
6439 g_slist_append(compose->header_list,
6443 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6445 ComposeHeaderEntry *last_header;
6447 last_header = compose->header_last;
6449 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6450 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6453 static void compose_remove_header_entries(Compose *compose)
6456 for (list = compose->header_list; list; list = list->next) {
6457 ComposeHeaderEntry *headerentry =
6458 (ComposeHeaderEntry *)list->data;
6459 gtk_widget_destroy(headerentry->combo);
6460 gtk_widget_destroy(headerentry->entry);
6461 gtk_widget_destroy(headerentry->button);
6462 gtk_widget_destroy(headerentry->hbox);
6463 g_free(headerentry);
6465 compose->header_last = NULL;
6466 g_slist_free(compose->header_list);
6467 compose->header_list = NULL;
6468 compose->header_nextrow = 1;
6469 compose_create_header_entry(compose);
6472 static GtkWidget *compose_create_header(Compose *compose)
6474 GtkWidget *from_optmenu_hbox;
6475 GtkWidget *header_scrolledwin;
6476 GtkWidget *header_table;
6480 /* header labels and entries */
6481 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6482 gtk_widget_show(header_scrolledwin);
6483 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6485 header_table = gtk_table_new(2, 2, FALSE);
6486 gtk_widget_show(header_table);
6487 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6488 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6489 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6492 /* option menu for selecting accounts */
6493 from_optmenu_hbox = compose_account_option_menu_create(compose);
6494 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6495 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6498 compose->header_table = header_table;
6499 compose->header_list = NULL;
6500 compose->header_nextrow = count;
6502 compose_create_header_entry(compose);
6504 compose->table = NULL;
6506 return header_scrolledwin ;
6509 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6511 Compose *compose = (Compose *)data;
6512 GdkEventButton event;
6515 event.time = gtk_get_current_event_time();
6517 return attach_button_pressed(compose->attach_clist, &event, compose);
6520 static GtkWidget *compose_create_attach(Compose *compose)
6522 GtkWidget *attach_scrwin;
6523 GtkWidget *attach_clist;
6525 GtkListStore *store;
6526 GtkCellRenderer *renderer;
6527 GtkTreeViewColumn *column;
6528 GtkTreeSelection *selection;
6530 /* attachment list */
6531 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6532 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6533 GTK_POLICY_AUTOMATIC,
6534 GTK_POLICY_AUTOMATIC);
6535 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6537 store = gtk_list_store_new(N_ATTACH_COLS,
6542 G_TYPE_AUTO_POINTER,
6544 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6545 (GTK_TREE_MODEL(store)));
6546 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6547 g_object_unref(store);
6549 renderer = gtk_cell_renderer_text_new();
6550 column = gtk_tree_view_column_new_with_attributes
6551 (_("Mime type"), renderer, "text",
6552 COL_MIMETYPE, NULL);
6553 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6555 renderer = gtk_cell_renderer_text_new();
6556 column = gtk_tree_view_column_new_with_attributes
6557 (_("Size"), renderer, "text",
6559 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6561 renderer = gtk_cell_renderer_text_new();
6562 column = gtk_tree_view_column_new_with_attributes
6563 (_("Name"), renderer, "text",
6565 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6567 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6568 prefs_common.use_stripes_everywhere);
6569 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6570 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6572 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6573 G_CALLBACK(attach_selected), compose);
6574 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6575 G_CALLBACK(attach_button_pressed), compose);
6577 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6578 G_CALLBACK(popup_attach_button_pressed), compose);
6580 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6581 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6582 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6583 G_CALLBACK(popup_attach_button_pressed), compose);
6585 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6586 G_CALLBACK(attach_key_pressed), compose);
6589 gtk_drag_dest_set(attach_clist,
6590 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6591 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6592 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6593 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6594 G_CALLBACK(compose_attach_drag_received_cb),
6596 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6597 G_CALLBACK(compose_drag_drop),
6600 compose->attach_scrwin = attach_scrwin;
6601 compose->attach_clist = attach_clist;
6603 return attach_scrwin;
6606 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6607 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6609 static GtkWidget *compose_create_others(Compose *compose)
6612 GtkWidget *savemsg_checkbtn;
6613 GtkWidget *savemsg_combo;
6614 GtkWidget *savemsg_select;
6617 gchar *folderidentifier;
6619 /* Table for settings */
6620 table = gtk_table_new(3, 1, FALSE);
6621 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6622 gtk_widget_show(table);
6623 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6626 /* Save Message to folder */
6627 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6628 gtk_widget_show(savemsg_checkbtn);
6629 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6630 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6631 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6633 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6634 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6636 savemsg_combo = gtk_combo_box_entry_new_text();
6637 compose->savemsg_checkbtn = savemsg_checkbtn;
6638 compose->savemsg_combo = savemsg_combo;
6639 gtk_widget_show(savemsg_combo);
6641 if (prefs_common.compose_save_to_history)
6642 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6643 prefs_common.compose_save_to_history);
6645 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6646 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6647 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6648 G_CALLBACK(compose_grab_focus_cb), compose);
6649 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6650 folderidentifier = folder_item_get_identifier(account_get_special_folder
6651 (compose->account, F_OUTBOX));
6652 compose_set_save_to(compose, folderidentifier);
6653 g_free(folderidentifier);
6656 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6657 gtk_widget_show(savemsg_select);
6658 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6659 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6660 G_CALLBACK(compose_savemsg_select_cb),
6668 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6670 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6671 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6674 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6679 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6682 path = folder_item_get_identifier(dest);
6684 compose_set_save_to(compose, path);
6688 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6689 GdkAtom clip, GtkTextIter *insert_place);
6692 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6696 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6698 if (event->button == 3) {
6700 GtkTextIter sel_start, sel_end;
6701 gboolean stuff_selected;
6703 /* move the cursor to allow GtkAspell to check the word
6704 * under the mouse */
6705 if (event->x && event->y) {
6706 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6707 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6709 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6712 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6713 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6716 stuff_selected = gtk_text_buffer_get_selection_bounds(
6718 &sel_start, &sel_end);
6720 gtk_text_buffer_place_cursor (buffer, &iter);
6721 /* reselect stuff */
6723 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6724 gtk_text_buffer_select_range(buffer,
6725 &sel_start, &sel_end);
6727 return FALSE; /* pass the event so that the right-click goes through */
6730 if (event->button == 2) {
6735 /* get the middle-click position to paste at the correct place */
6736 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6737 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6739 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6742 entry_paste_clipboard(compose, text,
6743 prefs_common.linewrap_pastes,
6744 GDK_SELECTION_PRIMARY, &iter);
6752 static void compose_spell_menu_changed(void *data)
6754 Compose *compose = (Compose *)data;
6756 GtkWidget *menuitem;
6757 GtkWidget *parent_item;
6758 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6761 if (compose->gtkaspell == NULL)
6764 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6765 "/Menu/Spelling/Options");
6767 /* setting the submenu removes /Spelling/Options from the factory
6768 * so we need to save it */
6770 if (parent_item == NULL) {
6771 parent_item = compose->aspell_options_menu;
6772 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6774 compose->aspell_options_menu = parent_item;
6776 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6778 spell_menu = g_slist_reverse(spell_menu);
6779 for (items = spell_menu;
6780 items; items = items->next) {
6781 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6782 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6783 gtk_widget_show(GTK_WIDGET(menuitem));
6785 g_slist_free(spell_menu);
6787 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6788 gtk_widget_show(parent_item);
6791 static void compose_dict_changed(void *data)
6793 Compose *compose = (Compose *) data;
6795 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6798 gtkaspell_highlight_all(compose->gtkaspell);
6799 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6803 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6805 Compose *compose = (Compose *)data;
6806 GdkEventButton event;
6809 event.time = gtk_get_current_event_time();
6813 return text_clicked(compose->text, &event, compose);
6816 static gboolean compose_force_window_origin = TRUE;
6817 static Compose *compose_create(PrefsAccount *account,
6826 GtkWidget *handlebox;
6828 GtkWidget *notebook;
6830 GtkWidget *attach_hbox;
6831 GtkWidget *attach_lab1;
6832 GtkWidget *attach_lab2;
6837 GtkWidget *subject_hbox;
6838 GtkWidget *subject_frame;
6839 GtkWidget *subject_entry;
6843 GtkWidget *edit_vbox;
6844 GtkWidget *ruler_hbox;
6846 GtkWidget *scrolledwin;
6848 GtkTextBuffer *buffer;
6849 GtkClipboard *clipboard;
6852 UndoMain *undostruct;
6854 gchar *titles[N_ATTACH_COLS];
6855 GtkWidget *popupmenu;
6856 GtkWidget *tmpl_menu;
6857 GtkActionGroup *action_group = NULL;
6860 GtkAspell * gtkaspell = NULL;
6863 static GdkGeometry geometry;
6865 cm_return_val_if_fail(account != NULL, NULL);
6867 debug_print("Creating compose window...\n");
6868 compose = g_new0(Compose, 1);
6870 titles[COL_MIMETYPE] = _("MIME type");
6871 titles[COL_SIZE] = _("Size");
6872 titles[COL_NAME] = _("Name");
6874 compose->batch = batch;
6875 compose->account = account;
6876 compose->folder = folder;
6878 compose->mutex = g_mutex_new();
6879 compose->set_cursor_pos = -1;
6881 #if !(GTK_CHECK_VERSION(2,12,0))
6882 compose->tooltips = tips;
6885 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6887 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6888 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6890 if (!geometry.max_width) {
6891 geometry.max_width = gdk_screen_width();
6892 geometry.max_height = gdk_screen_height();
6895 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6896 &geometry, GDK_HINT_MAX_SIZE);
6897 if (!geometry.min_width) {
6898 geometry.min_width = 600;
6899 geometry.min_height = 440;
6901 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6902 &geometry, GDK_HINT_MIN_SIZE);
6904 #ifndef GENERIC_UMPC
6905 if (compose_force_window_origin)
6906 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6907 prefs_common.compose_y);
6909 g_signal_connect(G_OBJECT(window), "delete_event",
6910 G_CALLBACK(compose_delete_cb), compose);
6911 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6912 gtk_widget_realize(window);
6914 gtkut_widget_set_composer_icon(window);
6916 vbox = gtk_vbox_new(FALSE, 0);
6917 gtk_container_add(GTK_CONTAINER(window), vbox);
6919 compose->ui_manager = gtk_ui_manager_new();
6920 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6921 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6922 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6923 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6924 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6925 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6926 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6927 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6928 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6929 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6988 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6990 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6996 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7000 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7004 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7005 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7006 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7013 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7014 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7016 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7026 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7030 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7036 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7043 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7049 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)
7050 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)
7051 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7056 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)
7057 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)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7062 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)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7066 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)
7067 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7072 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)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7078 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)
7079 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)
7080 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7084 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7085 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7086 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7091 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)
7093 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7094 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7099 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7100 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7101 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7102 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7109 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7110 gtk_widget_show_all(menubar);
7112 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7114 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7116 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7119 if (prefs_common.toolbar_detachable) {
7120 handlebox = gtk_handle_box_new();
7122 handlebox = gtk_hbox_new(FALSE, 0);
7124 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7126 gtk_widget_realize(handlebox);
7128 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7131 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7135 vbox2 = gtk_vbox_new(FALSE, 2);
7136 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7137 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7140 notebook = gtk_notebook_new();
7141 gtk_widget_set_size_request(notebook, -1, 130);
7142 gtk_widget_show(notebook);
7144 /* header labels and entries */
7145 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7146 compose_create_header(compose),
7147 gtk_label_new_with_mnemonic(_("Hea_der")));
7148 /* attachment list */
7149 attach_hbox = gtk_hbox_new(FALSE, 0);
7150 gtk_widget_show(attach_hbox);
7152 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7153 gtk_widget_show(attach_lab1);
7154 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7156 attach_lab2 = gtk_label_new("");
7157 gtk_widget_show(attach_lab2);
7158 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7160 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7161 compose_create_attach(compose),
7164 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7165 compose_create_others(compose),
7166 gtk_label_new_with_mnemonic(_("Othe_rs")));
7169 subject_hbox = gtk_hbox_new(FALSE, 0);
7170 gtk_widget_show(subject_hbox);
7172 subject_frame = gtk_frame_new(NULL);
7173 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7174 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7175 gtk_widget_show(subject_frame);
7177 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7178 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7179 gtk_widget_show(subject);
7181 label = gtk_label_new(_("Subject:"));
7182 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7183 gtk_widget_show(label);
7186 subject_entry = claws_spell_entry_new();
7188 subject_entry = gtk_entry_new();
7190 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7191 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7192 G_CALLBACK(compose_grab_focus_cb), compose);
7193 gtk_widget_show(subject_entry);
7194 compose->subject_entry = subject_entry;
7195 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7197 edit_vbox = gtk_vbox_new(FALSE, 0);
7199 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7202 ruler_hbox = gtk_hbox_new(FALSE, 0);
7203 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7205 ruler = gtk_shruler_new();
7206 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7207 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7211 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7212 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7213 GTK_POLICY_AUTOMATIC,
7214 GTK_POLICY_AUTOMATIC);
7215 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7217 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7218 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7220 text = gtk_text_view_new();
7221 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7222 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7223 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7224 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7225 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7227 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7229 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7230 G_CALLBACK(compose_edit_size_alloc),
7232 g_signal_connect(G_OBJECT(buffer), "changed",
7233 G_CALLBACK(compose_changed_cb), compose);
7234 g_signal_connect(G_OBJECT(text), "grab_focus",
7235 G_CALLBACK(compose_grab_focus_cb), compose);
7236 g_signal_connect(G_OBJECT(buffer), "insert_text",
7237 G_CALLBACK(text_inserted), compose);
7238 g_signal_connect(G_OBJECT(text), "button_press_event",
7239 G_CALLBACK(text_clicked), compose);
7241 g_signal_connect(G_OBJECT(text), "popup-menu",
7242 G_CALLBACK(compose_popup_menu), compose);
7244 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7245 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7246 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7247 G_CALLBACK(compose_popup_menu), compose);
7249 g_signal_connect(G_OBJECT(subject_entry), "changed",
7250 G_CALLBACK(compose_changed_cb), compose);
7253 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7254 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7255 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7256 g_signal_connect(G_OBJECT(text), "drag_data_received",
7257 G_CALLBACK(compose_insert_drag_received_cb),
7259 g_signal_connect(G_OBJECT(text), "drag-drop",
7260 G_CALLBACK(compose_drag_drop),
7262 gtk_widget_show_all(vbox);
7264 /* pane between attach clist and text */
7265 paned = gtk_vpaned_new();
7266 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7268 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7269 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7271 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7273 gtk_paned_add1(GTK_PANED(paned), notebook);
7274 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7275 gtk_widget_show_all(paned);
7278 if (prefs_common.textfont) {
7279 PangoFontDescription *font_desc;
7281 font_desc = pango_font_description_from_string
7282 (prefs_common.textfont);
7284 gtk_widget_modify_font(text, font_desc);
7285 pango_font_description_free(font_desc);
7289 gtk_action_group_add_actions(action_group, compose_popup_entries,
7290 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7298 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7300 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7301 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7302 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7304 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7306 undostruct = undo_init(text);
7307 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7310 address_completion_start(window);
7312 compose->window = window;
7313 compose->vbox = vbox;
7314 compose->menubar = menubar;
7315 compose->handlebox = handlebox;
7317 compose->vbox2 = vbox2;
7319 compose->paned = paned;
7321 compose->attach_label = attach_lab2;
7323 compose->notebook = notebook;
7324 compose->edit_vbox = edit_vbox;
7325 compose->ruler_hbox = ruler_hbox;
7326 compose->ruler = ruler;
7327 compose->scrolledwin = scrolledwin;
7328 compose->text = text;
7330 compose->focused_editable = NULL;
7332 compose->popupmenu = popupmenu;
7334 compose->tmpl_menu = tmpl_menu;
7336 compose->mode = mode;
7337 compose->rmode = mode;
7339 compose->targetinfo = NULL;
7340 compose->replyinfo = NULL;
7341 compose->fwdinfo = NULL;
7343 compose->replyto = NULL;
7345 compose->bcc = NULL;
7346 compose->followup_to = NULL;
7348 compose->ml_post = NULL;
7350 compose->inreplyto = NULL;
7351 compose->references = NULL;
7352 compose->msgid = NULL;
7353 compose->boundary = NULL;
7355 compose->autowrap = prefs_common.autowrap;
7356 compose->autoindent = prefs_common.auto_indent;
7357 compose->use_signing = FALSE;
7358 compose->use_encryption = FALSE;
7359 compose->privacy_system = NULL;
7361 compose->modified = FALSE;
7363 compose->return_receipt = FALSE;
7365 compose->to_list = NULL;
7366 compose->newsgroup_list = NULL;
7368 compose->undostruct = undostruct;
7370 compose->sig_str = NULL;
7372 compose->exteditor_file = NULL;
7373 compose->exteditor_pid = -1;
7374 compose->exteditor_tag = -1;
7375 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7378 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7379 if (mode != COMPOSE_REDIRECT) {
7380 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7381 strcmp(prefs_common.dictionary, "")) {
7382 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7383 prefs_common.alt_dictionary,
7384 conv_get_locale_charset_str(),
7385 prefs_common.misspelled_col,
7386 prefs_common.check_while_typing,
7387 prefs_common.recheck_when_changing_dict,
7388 prefs_common.use_alternate,
7389 prefs_common.use_both_dicts,
7390 GTK_TEXT_VIEW(text),
7391 GTK_WINDOW(compose->window),
7392 compose_dict_changed,
7393 compose_spell_menu_changed,
7396 alertpanel_error(_("Spell checker could not "
7398 gtkaspell_checkers_strerror());
7399 gtkaspell_checkers_reset_error();
7401 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7405 compose->gtkaspell = gtkaspell;
7406 compose_spell_menu_changed(compose);
7407 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7410 compose_select_account(compose, account, TRUE);
7412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7413 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7415 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7416 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7418 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7419 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7421 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7422 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7424 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7425 if (account->protocol != A_NNTP)
7426 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7427 prefs_common_translated_header_name("To:"));
7429 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7430 prefs_common_translated_header_name("Newsgroups:"));
7432 addressbook_set_target_compose(compose);
7434 if (mode != COMPOSE_REDIRECT)
7435 compose_set_template_menu(compose);
7437 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7440 compose_list = g_list_append(compose_list, compose);
7442 if (!prefs_common.show_ruler)
7443 gtk_widget_hide(ruler_hbox);
7445 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7448 compose->priority = PRIORITY_NORMAL;
7449 compose_update_priority_menu_item(compose);
7451 compose_set_out_encoding(compose);
7454 compose_update_actions_menu(compose);
7456 /* Privacy Systems menu */
7457 compose_update_privacy_systems_menu(compose);
7459 activate_privacy_system(compose, account, TRUE);
7460 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7462 gtk_widget_realize(window);
7464 gtk_widget_show(window);
7466 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7467 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7474 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7479 GtkWidget *optmenubox;
7482 GtkWidget *from_name = NULL;
7483 #if !(GTK_CHECK_VERSION(2,12,0))
7484 GtkTooltips *tips = compose->tooltips;
7487 gint num = 0, def_menu = 0;
7489 accounts = account_get_list();
7490 cm_return_val_if_fail(accounts != NULL, NULL);
7492 optmenubox = gtk_event_box_new();
7493 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7494 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7496 hbox = gtk_hbox_new(FALSE, 6);
7497 from_name = gtk_entry_new();
7499 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7500 G_CALLBACK(compose_grab_focus_cb), compose);
7502 for (; accounts != NULL; accounts = accounts->next, num++) {
7503 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7504 gchar *name, *from = NULL;
7506 if (ac == compose->account) def_menu = num;
7508 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7511 if (ac == compose->account) {
7512 if (ac->name && *ac->name) {
7514 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7515 from = g_strdup_printf("%s <%s>",
7517 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7519 from = g_strdup_printf("%s",
7521 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7524 COMBOBOX_ADD(menu, name, ac->account_id);
7529 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7531 g_signal_connect(G_OBJECT(optmenu), "changed",
7532 G_CALLBACK(account_activated),
7534 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7535 G_CALLBACK(compose_entry_popup_extend),
7538 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7539 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7541 CLAWS_SET_TIP(optmenubox,
7542 _("Account to use for this email"));
7543 CLAWS_SET_TIP(from_name,
7544 _("Sender address to be used"));
7546 compose->from_name = from_name;
7551 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7553 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7554 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7555 Compose *compose = (Compose *) data;
7557 compose->priority = value;
7561 static void compose_reply_change_mode(Compose *compose,
7564 gboolean was_modified = compose->modified;
7566 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7568 cm_return_if_fail(compose->replyinfo != NULL);
7570 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7572 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7574 if (action == COMPOSE_REPLY_TO_ALL)
7576 if (action == COMPOSE_REPLY_TO_SENDER)
7578 if (action == COMPOSE_REPLY_TO_LIST)
7581 compose_remove_header_entries(compose);
7582 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7583 if (compose->account->set_autocc && compose->account->auto_cc)
7584 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7586 if (compose->account->set_autobcc && compose->account->auto_bcc)
7587 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7589 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7590 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7591 compose_show_first_last_header(compose, TRUE);
7592 compose->modified = was_modified;
7593 compose_set_title(compose);
7596 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7598 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7599 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7600 Compose *compose = (Compose *) data;
7603 compose_reply_change_mode(compose, value);
7606 static void compose_update_priority_menu_item(Compose * compose)
7608 GtkWidget *menuitem = NULL;
7609 switch (compose->priority) {
7610 case PRIORITY_HIGHEST:
7611 menuitem = gtk_ui_manager_get_widget
7612 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7615 menuitem = gtk_ui_manager_get_widget
7616 (compose->ui_manager, "/Menu/Options/Priority/High");
7618 case PRIORITY_NORMAL:
7619 menuitem = gtk_ui_manager_get_widget
7620 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7623 menuitem = gtk_ui_manager_get_widget
7624 (compose->ui_manager, "/Menu/Options/Priority/Low");
7626 case PRIORITY_LOWEST:
7627 menuitem = gtk_ui_manager_get_widget
7628 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7631 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7634 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7636 Compose *compose = (Compose *) data;
7638 gboolean can_sign = FALSE, can_encrypt = FALSE;
7640 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7642 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7645 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7646 g_free(compose->privacy_system);
7647 compose->privacy_system = NULL;
7648 if (systemid != NULL) {
7649 compose->privacy_system = g_strdup(systemid);
7651 can_sign = privacy_system_can_sign(systemid);
7652 can_encrypt = privacy_system_can_encrypt(systemid);
7655 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7657 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7658 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7661 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7663 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7664 GtkWidget *menuitem = NULL;
7666 gboolean can_sign = FALSE, can_encrypt = FALSE;
7667 gboolean found = FALSE;
7669 if (compose->privacy_system != NULL) {
7671 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7672 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7673 cm_return_if_fail(menuitem != NULL);
7675 amenu = GTK_MENU_SHELL(menuitem)->children;
7677 while (amenu != NULL) {
7678 GList *alist = amenu->next;
7680 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7681 if (systemid != NULL) {
7682 if (strcmp(systemid, compose->privacy_system) == 0 &&
7683 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7684 menuitem = GTK_WIDGET(amenu->data);
7686 can_sign = privacy_system_can_sign(systemid);
7687 can_encrypt = privacy_system_can_encrypt(systemid);
7691 } else if (strlen(compose->privacy_system) == 0 &&
7692 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7693 menuitem = GTK_WIDGET(amenu->data);
7696 can_encrypt = FALSE;
7703 if (menuitem != NULL)
7704 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7706 if (warn && !found && strlen(compose->privacy_system)) {
7707 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7708 "will not be able to sign or encrypt this message."),
7709 compose->privacy_system);
7713 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7714 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7717 static void compose_set_out_encoding(Compose *compose)
7719 CharSet out_encoding;
7720 const gchar *branch = NULL;
7721 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7723 switch(out_encoding) {
7724 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7725 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7726 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7727 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7728 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7729 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7730 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7731 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7732 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7733 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7734 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7735 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7736 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7737 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7738 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7739 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7740 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7741 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7742 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7743 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7744 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7745 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7746 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7747 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7748 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7749 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7750 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7751 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7752 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7753 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7754 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7755 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7757 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7760 static void compose_set_template_menu(Compose *compose)
7762 GSList *tmpl_list, *cur;
7766 tmpl_list = template_get_config();
7768 menu = gtk_menu_new();
7770 gtk_menu_set_accel_group (GTK_MENU (menu),
7771 gtk_ui_manager_get_accel_group(compose->ui_manager));
7772 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7773 Template *tmpl = (Template *)cur->data;
7774 gchar *accel_path = NULL;
7775 item = gtk_menu_item_new_with_label(tmpl->name);
7776 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7777 g_signal_connect(G_OBJECT(item), "activate",
7778 G_CALLBACK(compose_template_activate_cb),
7780 g_object_set_data(G_OBJECT(item), "template", tmpl);
7781 gtk_widget_show(item);
7782 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7783 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7787 gtk_widget_show(menu);
7788 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7791 void compose_update_actions_menu(Compose *compose)
7793 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7796 static void compose_update_privacy_systems_menu(Compose *compose)
7798 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7799 GSList *systems, *cur;
7801 GtkWidget *system_none;
7803 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7804 GtkWidget *privacy_menu = gtk_menu_new();
7806 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7807 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7809 g_signal_connect(G_OBJECT(system_none), "activate",
7810 G_CALLBACK(compose_set_privacy_system_cb), compose);
7812 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7813 gtk_widget_show(system_none);
7815 systems = privacy_get_system_ids();
7816 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7817 gchar *systemid = cur->data;
7819 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7820 widget = gtk_radio_menu_item_new_with_label(group,
7821 privacy_system_get_name(systemid));
7822 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7823 g_strdup(systemid), g_free);
7824 g_signal_connect(G_OBJECT(widget), "activate",
7825 G_CALLBACK(compose_set_privacy_system_cb), compose);
7827 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7828 gtk_widget_show(widget);
7831 g_slist_free(systems);
7832 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7833 gtk_widget_show_all(privacy_menu);
7834 gtk_widget_show_all(privacy_menuitem);
7837 void compose_reflect_prefs_all(void)
7842 for (cur = compose_list; cur != NULL; cur = cur->next) {
7843 compose = (Compose *)cur->data;
7844 compose_set_template_menu(compose);
7848 void compose_reflect_prefs_pixmap_theme(void)
7853 for (cur = compose_list; cur != NULL; cur = cur->next) {
7854 compose = (Compose *)cur->data;
7855 toolbar_update(TOOLBAR_COMPOSE, compose);
7859 static const gchar *compose_quote_char_from_context(Compose *compose)
7861 const gchar *qmark = NULL;
7863 cm_return_val_if_fail(compose != NULL, NULL);
7865 switch (compose->mode) {
7866 /* use forward-specific quote char */
7867 case COMPOSE_FORWARD:
7868 case COMPOSE_FORWARD_AS_ATTACH:
7869 case COMPOSE_FORWARD_INLINE:
7870 if (compose->folder && compose->folder->prefs &&
7871 compose->folder->prefs->forward_with_format)
7872 qmark = compose->folder->prefs->forward_quotemark;
7873 else if (compose->account->forward_with_format)
7874 qmark = compose->account->forward_quotemark;
7876 qmark = prefs_common.fw_quotemark;
7879 /* use reply-specific quote char in all other modes */
7881 if (compose->folder && compose->folder->prefs &&
7882 compose->folder->prefs->reply_with_format)
7883 qmark = compose->folder->prefs->reply_quotemark;
7884 else if (compose->account->reply_with_format)
7885 qmark = compose->account->reply_quotemark;
7887 qmark = prefs_common.quotemark;
7891 if (qmark == NULL || *qmark == '\0')
7897 static void compose_template_apply(Compose *compose, Template *tmpl,
7901 GtkTextBuffer *buffer;
7905 gchar *parsed_str = NULL;
7906 gint cursor_pos = 0;
7907 const gchar *err_msg = _("The body of the template has an error at line %d.");
7910 /* process the body */
7912 text = GTK_TEXT_VIEW(compose->text);
7913 buffer = gtk_text_view_get_buffer(text);
7916 qmark = compose_quote_char_from_context(compose);
7918 if (compose->replyinfo != NULL) {
7921 gtk_text_buffer_set_text(buffer, "", -1);
7922 mark = gtk_text_buffer_get_insert(buffer);
7923 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7925 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7926 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7928 } else if (compose->fwdinfo != NULL) {
7931 gtk_text_buffer_set_text(buffer, "", -1);
7932 mark = gtk_text_buffer_get_insert(buffer);
7933 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7935 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7936 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7939 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7941 GtkTextIter start, end;
7944 gtk_text_buffer_get_start_iter(buffer, &start);
7945 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7946 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7948 /* clear the buffer now */
7950 gtk_text_buffer_set_text(buffer, "", -1);
7952 parsed_str = compose_quote_fmt(compose, dummyinfo,
7953 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7954 procmsg_msginfo_free( dummyinfo );
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);
7965 if (replace && parsed_str && compose->account->auto_sig)
7966 compose_insert_sig(compose, FALSE);
7968 if (replace && parsed_str) {
7969 gtk_text_buffer_get_start_iter(buffer, &iter);
7970 gtk_text_buffer_place_cursor(buffer, &iter);
7974 cursor_pos = quote_fmt_get_cursor_pos();
7975 compose->set_cursor_pos = cursor_pos;
7976 if (cursor_pos == -1)
7978 gtk_text_buffer_get_start_iter(buffer, &iter);
7979 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7980 gtk_text_buffer_place_cursor(buffer, &iter);
7983 /* process the other fields */
7985 compose_template_apply_fields(compose, tmpl);
7986 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7987 quote_fmt_reset_vartable();
7988 compose_changed_cb(NULL, compose);
7991 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7993 MsgInfo* dummyinfo = NULL;
7994 MsgInfo *msginfo = NULL;
7997 if (compose->replyinfo != NULL)
7998 msginfo = compose->replyinfo;
7999 else if (compose->fwdinfo != NULL)
8000 msginfo = compose->fwdinfo;
8002 dummyinfo = compose_msginfo_new_from_compose(compose);
8003 msginfo = dummyinfo;
8006 if (tmpl->from && *tmpl->from != '\0') {
8008 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8009 compose->gtkaspell);
8011 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8013 quote_fmt_scan_string(tmpl->from);
8016 buf = quote_fmt_get_buffer();
8018 alertpanel_error(_("Template From format error."));
8020 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8024 if (tmpl->to && *tmpl->to != '\0') {
8026 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8027 compose->gtkaspell);
8029 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8031 quote_fmt_scan_string(tmpl->to);
8034 buf = quote_fmt_get_buffer();
8036 alertpanel_error(_("Template To format error."));
8038 compose_entry_append(compose, buf, COMPOSE_TO);
8042 if (tmpl->cc && *tmpl->cc != '\0') {
8044 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8045 compose->gtkaspell);
8047 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8049 quote_fmt_scan_string(tmpl->cc);
8052 buf = quote_fmt_get_buffer();
8054 alertpanel_error(_("Template Cc format error."));
8056 compose_entry_append(compose, buf, COMPOSE_CC);
8060 if (tmpl->bcc && *tmpl->bcc != '\0') {
8062 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8063 compose->gtkaspell);
8065 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8067 quote_fmt_scan_string(tmpl->bcc);
8070 buf = quote_fmt_get_buffer();
8072 alertpanel_error(_("Template Bcc format error."));
8074 compose_entry_append(compose, buf, COMPOSE_BCC);
8078 /* process the subject */
8079 if (tmpl->subject && *tmpl->subject != '\0') {
8081 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8082 compose->gtkaspell);
8084 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8086 quote_fmt_scan_string(tmpl->subject);
8089 buf = quote_fmt_get_buffer();
8091 alertpanel_error(_("Template subject format error."));
8093 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8097 procmsg_msginfo_free( dummyinfo );
8100 static void compose_destroy(Compose *compose)
8102 GtkTextBuffer *buffer;
8103 GtkClipboard *clipboard;
8105 compose_list = g_list_remove(compose_list, compose);
8107 if (compose->updating) {
8108 debug_print("danger, not destroying anything now\n");
8109 compose->deferred_destroy = TRUE;
8112 /* NOTE: address_completion_end() does nothing with the window
8113 * however this may change. */
8114 address_completion_end(compose->window);
8116 slist_free_strings(compose->to_list);
8117 g_slist_free(compose->to_list);
8118 slist_free_strings(compose->newsgroup_list);
8119 g_slist_free(compose->newsgroup_list);
8120 slist_free_strings(compose->header_list);
8121 g_slist_free(compose->header_list);
8123 procmsg_msginfo_free(compose->targetinfo);
8124 procmsg_msginfo_free(compose->replyinfo);
8125 procmsg_msginfo_free(compose->fwdinfo);
8127 g_free(compose->replyto);
8128 g_free(compose->cc);
8129 g_free(compose->bcc);
8130 g_free(compose->newsgroups);
8131 g_free(compose->followup_to);
8133 g_free(compose->ml_post);
8135 g_free(compose->inreplyto);
8136 g_free(compose->references);
8137 g_free(compose->msgid);
8138 g_free(compose->boundary);
8140 g_free(compose->redirect_filename);
8141 if (compose->undostruct)
8142 undo_destroy(compose->undostruct);
8144 g_free(compose->sig_str);
8146 g_free(compose->exteditor_file);
8148 g_free(compose->orig_charset);
8150 g_free(compose->privacy_system);
8152 if (addressbook_get_target_compose() == compose)
8153 addressbook_set_target_compose(NULL);
8156 if (compose->gtkaspell) {
8157 gtkaspell_delete(compose->gtkaspell);
8158 compose->gtkaspell = NULL;
8162 if (!compose->batch) {
8163 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8164 prefs_common.compose_height = compose->window->allocation.height;
8167 if (!gtk_widget_get_parent(compose->paned))
8168 gtk_widget_destroy(compose->paned);
8169 gtk_widget_destroy(compose->popupmenu);
8171 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8172 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8173 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8175 gtk_widget_destroy(compose->window);
8176 toolbar_destroy(compose->toolbar);
8177 g_free(compose->toolbar);
8178 g_mutex_free(compose->mutex);
8182 static void compose_attach_info_free(AttachInfo *ainfo)
8184 g_free(ainfo->file);
8185 g_free(ainfo->content_type);
8186 g_free(ainfo->name);
8190 static void compose_attach_update_label(Compose *compose)
8195 GtkTreeModel *model;
8200 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8201 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8202 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8206 while(gtk_tree_model_iter_next(model, &iter))
8209 text = g_strdup_printf("(%d)", i);
8210 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8214 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8216 Compose *compose = (Compose *)data;
8217 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8218 GtkTreeSelection *selection;
8220 GtkTreeModel *model;
8222 selection = gtk_tree_view_get_selection(tree_view);
8223 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8228 for (cur = sel; cur != NULL; cur = cur->next) {
8229 GtkTreePath *path = cur->data;
8230 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8233 gtk_tree_path_free(path);
8236 for (cur = sel; cur != NULL; cur = cur->next) {
8237 GtkTreeRowReference *ref = cur->data;
8238 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8241 if (gtk_tree_model_get_iter(model, &iter, path))
8242 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8244 gtk_tree_path_free(path);
8245 gtk_tree_row_reference_free(ref);
8249 compose_attach_update_label(compose);
8252 static struct _AttachProperty
8255 GtkWidget *mimetype_entry;
8256 GtkWidget *encoding_optmenu;
8257 GtkWidget *path_entry;
8258 GtkWidget *filename_entry;
8260 GtkWidget *cancel_btn;
8263 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8265 gtk_tree_path_free((GtkTreePath *)ptr);
8268 static void compose_attach_property(GtkAction *action, gpointer data)
8270 Compose *compose = (Compose *)data;
8271 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8273 GtkComboBox *optmenu;
8274 GtkTreeSelection *selection;
8276 GtkTreeModel *model;
8279 static gboolean cancelled;
8281 /* only if one selected */
8282 selection = gtk_tree_view_get_selection(tree_view);
8283 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8286 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8290 path = (GtkTreePath *) sel->data;
8291 gtk_tree_model_get_iter(model, &iter, path);
8292 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8295 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8301 if (!attach_prop.window)
8302 compose_attach_property_create(&cancelled);
8303 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8304 gtk_widget_grab_focus(attach_prop.ok_btn);
8305 gtk_widget_show(attach_prop.window);
8306 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8308 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8309 if (ainfo->encoding == ENC_UNKNOWN)
8310 combobox_select_by_data(optmenu, ENC_BASE64);
8312 combobox_select_by_data(optmenu, ainfo->encoding);
8314 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8315 ainfo->content_type ? ainfo->content_type : "");
8316 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8317 ainfo->file ? ainfo->file : "");
8318 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8319 ainfo->name ? ainfo->name : "");
8322 const gchar *entry_text;
8324 gchar *cnttype = NULL;
8331 gtk_widget_hide(attach_prop.window);
8332 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8337 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8338 if (*entry_text != '\0') {
8341 text = g_strstrip(g_strdup(entry_text));
8342 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8343 cnttype = g_strdup(text);
8346 alertpanel_error(_("Invalid MIME type."));
8352 ainfo->encoding = combobox_get_active_data(optmenu);
8354 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8355 if (*entry_text != '\0') {
8356 if (is_file_exist(entry_text) &&
8357 (size = get_file_size(entry_text)) > 0)
8358 file = g_strdup(entry_text);
8361 (_("File doesn't exist or is empty."));
8367 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8368 if (*entry_text != '\0') {
8369 g_free(ainfo->name);
8370 ainfo->name = g_strdup(entry_text);
8374 g_free(ainfo->content_type);
8375 ainfo->content_type = cnttype;
8378 g_free(ainfo->file);
8382 ainfo->size = (goffset)size;
8384 /* update tree store */
8385 text = to_human_readable(ainfo->size);
8386 gtk_tree_model_get_iter(model, &iter, path);
8387 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8388 COL_MIMETYPE, ainfo->content_type,
8390 COL_NAME, ainfo->name,
8396 gtk_tree_path_free(path);
8399 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8401 label = gtk_label_new(str); \
8402 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8403 GTK_FILL, 0, 0, 0); \
8404 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8406 entry = gtk_entry_new(); \
8407 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8408 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8411 static void compose_attach_property_create(gboolean *cancelled)
8417 GtkWidget *mimetype_entry;
8420 GtkListStore *optmenu_menu;
8421 GtkWidget *path_entry;
8422 GtkWidget *filename_entry;
8425 GtkWidget *cancel_btn;
8426 GList *mime_type_list, *strlist;
8429 debug_print("Creating attach_property window...\n");
8431 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8432 gtk_widget_set_size_request(window, 480, -1);
8433 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8434 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8435 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8436 g_signal_connect(G_OBJECT(window), "delete_event",
8437 G_CALLBACK(attach_property_delete_event),
8439 g_signal_connect(G_OBJECT(window), "key_press_event",
8440 G_CALLBACK(attach_property_key_pressed),
8443 vbox = gtk_vbox_new(FALSE, 8);
8444 gtk_container_add(GTK_CONTAINER(window), vbox);
8446 table = gtk_table_new(4, 2, FALSE);
8447 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8448 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8449 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8451 label = gtk_label_new(_("MIME type"));
8452 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8454 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8455 mimetype_entry = gtk_combo_box_entry_new_text();
8456 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8457 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8459 /* stuff with list */
8460 mime_type_list = procmime_get_mime_type_list();
8462 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8463 MimeType *type = (MimeType *) mime_type_list->data;
8466 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8468 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8471 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8472 (GCompareFunc)strcmp2);
8475 for (mime_type_list = strlist; mime_type_list != NULL;
8476 mime_type_list = mime_type_list->next) {
8477 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8478 g_free(mime_type_list->data);
8480 g_list_free(strlist);
8481 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8482 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8484 label = gtk_label_new(_("Encoding"));
8485 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8487 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8489 hbox = gtk_hbox_new(FALSE, 0);
8490 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8491 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8493 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8494 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8496 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8497 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8498 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8499 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8500 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8502 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8504 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8505 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8507 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8508 &ok_btn, GTK_STOCK_OK,
8510 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8511 gtk_widget_grab_default(ok_btn);
8513 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8514 G_CALLBACK(attach_property_ok),
8516 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8517 G_CALLBACK(attach_property_cancel),
8520 gtk_widget_show_all(vbox);
8522 attach_prop.window = window;
8523 attach_prop.mimetype_entry = mimetype_entry;
8524 attach_prop.encoding_optmenu = optmenu;
8525 attach_prop.path_entry = path_entry;
8526 attach_prop.filename_entry = filename_entry;
8527 attach_prop.ok_btn = ok_btn;
8528 attach_prop.cancel_btn = cancel_btn;
8531 #undef SET_LABEL_AND_ENTRY
8533 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8539 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8545 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8546 gboolean *cancelled)
8554 static gboolean attach_property_key_pressed(GtkWidget *widget,
8556 gboolean *cancelled)
8558 if (event && event->keyval == GDK_Escape) {
8562 if (event && event->keyval == GDK_Return) {
8570 static void compose_exec_ext_editor(Compose *compose)
8577 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8578 G_DIR_SEPARATOR, compose);
8580 if (pipe(pipe_fds) < 0) {
8586 if ((pid = fork()) < 0) {
8593 /* close the write side of the pipe */
8596 compose->exteditor_file = g_strdup(tmp);
8597 compose->exteditor_pid = pid;
8599 compose_set_ext_editor_sensitive(compose, FALSE);
8602 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8604 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8606 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8610 } else { /* process-monitoring process */
8616 /* close the read side of the pipe */
8619 if (compose_write_body_to_file(compose, tmp) < 0) {
8620 fd_write_all(pipe_fds[1], "2\n", 2);
8624 pid_ed = compose_exec_ext_editor_real(tmp);
8626 fd_write_all(pipe_fds[1], "1\n", 2);
8630 /* wait until editor is terminated */
8631 waitpid(pid_ed, NULL, 0);
8633 fd_write_all(pipe_fds[1], "0\n", 2);
8640 #endif /* G_OS_UNIX */
8644 static gint compose_exec_ext_editor_real(const gchar *file)
8651 cm_return_val_if_fail(file != NULL, -1);
8653 if ((pid = fork()) < 0) {
8658 if (pid != 0) return pid;
8660 /* grandchild process */
8662 if (setpgid(0, getppid()))
8665 if (prefs_common_get_ext_editor_cmd() &&
8666 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8667 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8668 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8670 if (prefs_common_get_ext_editor_cmd())
8671 g_warning("External editor command-line is invalid: '%s'\n",
8672 prefs_common_get_ext_editor_cmd());
8673 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8676 cmdline = strsplit_with_quote(buf, " ", 1024);
8677 execvp(cmdline[0], cmdline);
8680 g_strfreev(cmdline);
8685 static gboolean compose_ext_editor_kill(Compose *compose)
8687 pid_t pgid = compose->exteditor_pid * -1;
8690 ret = kill(pgid, 0);
8692 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8696 msg = g_strdup_printf
8697 (_("The external editor is still working.\n"
8698 "Force terminating the process?\n"
8699 "process group id: %d"), -pgid);
8700 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8701 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8705 if (val == G_ALERTALTERNATE) {
8706 g_source_remove(compose->exteditor_tag);
8707 g_io_channel_shutdown(compose->exteditor_ch,
8709 g_io_channel_unref(compose->exteditor_ch);
8711 if (kill(pgid, SIGTERM) < 0) perror("kill");
8712 waitpid(compose->exteditor_pid, NULL, 0);
8714 g_warning("Terminated process group id: %d", -pgid);
8715 g_warning("Temporary file: %s",
8716 compose->exteditor_file);
8718 compose_set_ext_editor_sensitive(compose, TRUE);
8720 g_free(compose->exteditor_file);
8721 compose->exteditor_file = NULL;
8722 compose->exteditor_pid = -1;
8723 compose->exteditor_ch = NULL;
8724 compose->exteditor_tag = -1;
8732 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8736 Compose *compose = (Compose *)data;
8739 debug_print(_("Compose: input from monitoring process\n"));
8741 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8743 g_io_channel_shutdown(source, FALSE, NULL);
8744 g_io_channel_unref(source);
8746 waitpid(compose->exteditor_pid, NULL, 0);
8748 if (buf[0] == '0') { /* success */
8749 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8750 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8752 gtk_text_buffer_set_text(buffer, "", -1);
8753 compose_insert_file(compose, compose->exteditor_file);
8754 compose_changed_cb(NULL, compose);
8755 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8757 if (claws_unlink(compose->exteditor_file) < 0)
8758 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8759 } else if (buf[0] == '1') { /* failed */
8760 g_warning("Couldn't exec external editor\n");
8761 if (claws_unlink(compose->exteditor_file) < 0)
8762 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8763 } else if (buf[0] == '2') {
8764 g_warning("Couldn't write to file\n");
8765 } else if (buf[0] == '3') {
8766 g_warning("Pipe read failed\n");
8769 compose_set_ext_editor_sensitive(compose, TRUE);
8771 g_free(compose->exteditor_file);
8772 compose->exteditor_file = NULL;
8773 compose->exteditor_pid = -1;
8774 compose->exteditor_ch = NULL;
8775 compose->exteditor_tag = -1;
8780 static void compose_set_ext_editor_sensitive(Compose *compose,
8783 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8784 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8785 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8786 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8787 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8788 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8789 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8791 gtk_widget_set_sensitive(compose->text, sensitive);
8792 if (compose->toolbar->send_btn)
8793 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8794 if (compose->toolbar->sendl_btn)
8795 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8796 if (compose->toolbar->draft_btn)
8797 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8798 if (compose->toolbar->insert_btn)
8799 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8800 if (compose->toolbar->sig_btn)
8801 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8802 if (compose->toolbar->exteditor_btn)
8803 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8804 if (compose->toolbar->linewrap_current_btn)
8805 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8806 if (compose->toolbar->linewrap_all_btn)
8807 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8809 #endif /* G_OS_UNIX */
8812 * compose_undo_state_changed:
8814 * Change the sensivity of the menuentries undo and redo
8816 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8817 gint redo_state, gpointer data)
8819 Compose *compose = (Compose *)data;
8821 switch (undo_state) {
8822 case UNDO_STATE_TRUE:
8823 if (!undostruct->undo_state) {
8824 undostruct->undo_state = TRUE;
8825 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8828 case UNDO_STATE_FALSE:
8829 if (undostruct->undo_state) {
8830 undostruct->undo_state = FALSE;
8831 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8834 case UNDO_STATE_UNCHANGED:
8836 case UNDO_STATE_REFRESH:
8837 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8840 g_warning("Undo state not recognized");
8844 switch (redo_state) {
8845 case UNDO_STATE_TRUE:
8846 if (!undostruct->redo_state) {
8847 undostruct->redo_state = TRUE;
8848 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8851 case UNDO_STATE_FALSE:
8852 if (undostruct->redo_state) {
8853 undostruct->redo_state = FALSE;
8854 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8857 case UNDO_STATE_UNCHANGED:
8859 case UNDO_STATE_REFRESH:
8860 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8863 g_warning("Redo state not recognized");
8868 /* callback functions */
8870 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8871 * includes "non-client" (windows-izm) in calculation, so this calculation
8872 * may not be accurate.
8874 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8875 GtkAllocation *allocation,
8876 GtkSHRuler *shruler)
8878 if (prefs_common.show_ruler) {
8879 gint char_width = 0, char_height = 0;
8880 gint line_width_in_chars;
8882 gtkut_get_font_size(GTK_WIDGET(widget),
8883 &char_width, &char_height);
8884 line_width_in_chars =
8885 (allocation->width - allocation->x) / char_width;
8887 /* got the maximum */
8888 gtk_ruler_set_range(GTK_RULER(shruler),
8889 0.0, line_width_in_chars, 0,
8890 /*line_width_in_chars*/ char_width);
8896 static void account_activated(GtkComboBox *optmenu, gpointer data)
8898 Compose *compose = (Compose *)data;
8901 gchar *folderidentifier;
8902 gint account_id = 0;
8906 /* Get ID of active account in the combo box */
8907 menu = gtk_combo_box_get_model(optmenu);
8908 gtk_combo_box_get_active_iter(optmenu, &iter);
8909 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8911 ac = account_find_from_id(account_id);
8912 cm_return_if_fail(ac != NULL);
8914 if (ac != compose->account)
8915 compose_select_account(compose, ac, FALSE);
8917 /* Set message save folder */
8918 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8919 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8921 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8922 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8924 compose_set_save_to(compose, NULL);
8925 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8926 folderidentifier = folder_item_get_identifier(account_get_special_folder
8927 (compose->account, F_OUTBOX));
8928 compose_set_save_to(compose, folderidentifier);
8929 g_free(folderidentifier);
8933 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8934 GtkTreeViewColumn *column, Compose *compose)
8936 compose_attach_property(NULL, compose);
8939 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8942 Compose *compose = (Compose *)data;
8943 GtkTreeSelection *attach_selection;
8944 gint attach_nr_selected;
8946 if (!event) return FALSE;
8948 if (event->button == 3) {
8949 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8950 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8952 if (attach_nr_selected > 0)
8954 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8955 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8957 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8958 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8961 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8962 NULL, NULL, event->button, event->time);
8969 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8972 Compose *compose = (Compose *)data;
8974 if (!event) return FALSE;
8976 switch (event->keyval) {
8978 compose_attach_remove_selected(NULL, compose);
8984 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8986 toolbar_comp_set_sensitive(compose, allow);
8987 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8988 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8992 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8993 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8994 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8996 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9000 static void compose_send_cb(GtkAction *action, gpointer data)
9002 Compose *compose = (Compose *)data;
9004 if (prefs_common.work_offline &&
9005 !inc_offline_should_override(TRUE,
9006 _("Claws Mail needs network access in order "
9007 "to send this email.")))
9010 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9011 g_source_remove(compose->draft_timeout_tag);
9012 compose->draft_timeout_tag = -1;
9015 compose_send(compose);
9018 static void compose_send_later_cb(GtkAction *action, gpointer data)
9020 Compose *compose = (Compose *)data;
9024 compose_allow_user_actions(compose, FALSE);
9025 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9026 compose_allow_user_actions(compose, TRUE);
9030 compose_close(compose);
9031 } else if (val == -1) {
9032 alertpanel_error(_("Could not queue message."));
9033 } else if (val == -2) {
9034 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9035 } else if (val == -3) {
9036 if (privacy_peek_error())
9037 alertpanel_error(_("Could not queue message for sending:\n\n"
9038 "Signature failed: %s"), privacy_get_error());
9039 } else if (val == -4) {
9040 alertpanel_error(_("Could not queue message for sending:\n\n"
9041 "Charset conversion failed."));
9042 } else if (val == -5) {
9043 alertpanel_error(_("Could not queue message for sending:\n\n"
9044 "Couldn't get recipient encryption key."));
9045 } else if (val == -6) {
9048 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9051 #define DRAFTED_AT_EXIT "drafted_at_exit"
9052 static void compose_register_draft(MsgInfo *info)
9054 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9055 DRAFTED_AT_EXIT, NULL);
9056 FILE *fp = g_fopen(filepath, "ab");
9059 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9067 gboolean compose_draft (gpointer data, guint action)
9069 Compose *compose = (Compose *)data;
9073 MsgFlags flag = {0, 0};
9074 static gboolean lock = FALSE;
9075 MsgInfo *newmsginfo;
9077 gboolean target_locked = FALSE;
9078 gboolean err = FALSE;
9080 if (lock) return FALSE;
9082 if (compose->sending)
9085 draft = account_get_special_folder(compose->account, F_DRAFT);
9086 cm_return_val_if_fail(draft != NULL, FALSE);
9088 if (!g_mutex_trylock(compose->mutex)) {
9089 /* we don't want to lock the mutex once it's available,
9090 * because as the only other part of compose.c locking
9091 * it is compose_close - which means once unlocked,
9092 * the compose struct will be freed */
9093 debug_print("couldn't lock mutex, probably sending\n");
9099 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9100 G_DIR_SEPARATOR, compose);
9101 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9102 FILE_OP_ERROR(tmp, "fopen");
9106 /* chmod for security */
9107 if (change_file_mode_rw(fp, tmp) < 0) {
9108 FILE_OP_ERROR(tmp, "chmod");
9109 g_warning("can't change file mode\n");
9112 /* Save draft infos */
9113 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9114 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9116 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9117 gchar *savefolderid;
9119 savefolderid = compose_get_save_to(compose);
9120 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9121 g_free(savefolderid);
9123 if (compose->return_receipt) {
9124 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9126 if (compose->privacy_system) {
9127 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9128 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9129 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9132 /* Message-ID of message replying to */
9133 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9136 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9137 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9140 /* Message-ID of message forwarding to */
9141 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9144 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9145 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9149 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9150 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9152 /* end of headers */
9153 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9160 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9164 if (fclose(fp) == EOF) {
9168 if (compose->targetinfo) {
9169 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9170 flag.perm_flags = target_locked?MSG_LOCKED:0;
9172 flag.tmp_flags = MSG_DRAFT;
9174 folder_item_scan(draft);
9175 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9176 MsgInfo *tmpinfo = NULL;
9177 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9178 if (compose->msgid) {
9179 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9182 msgnum = tmpinfo->msgnum;
9183 procmsg_msginfo_free(tmpinfo);
9184 debug_print("got draft msgnum %d from scanning\n", msgnum);
9186 debug_print("didn't get draft msgnum after scanning\n");
9189 debug_print("got draft msgnum %d from adding\n", msgnum);
9195 if (action != COMPOSE_AUTO_SAVE) {
9196 if (action != COMPOSE_DRAFT_FOR_EXIT)
9197 alertpanel_error(_("Could not save draft."));
9200 gtkut_window_popup(compose->window);
9201 val = alertpanel_full(_("Could not save draft"),
9202 _("Could not save draft.\n"
9203 "Do you want to cancel exit or discard this email?"),
9204 _("_Cancel exit"), _("_Discard email"), NULL,
9205 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9206 if (val == G_ALERTALTERNATE) {
9208 g_mutex_unlock(compose->mutex); /* must be done before closing */
9209 compose_close(compose);
9213 g_mutex_unlock(compose->mutex); /* must be done before closing */
9222 if (compose->mode == COMPOSE_REEDIT) {
9223 compose_remove_reedit_target(compose, TRUE);
9226 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9229 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9231 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9233 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9234 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9235 procmsg_msginfo_set_flags(newmsginfo, 0,
9236 MSG_HAS_ATTACHMENT);
9238 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9239 compose_register_draft(newmsginfo);
9241 procmsg_msginfo_free(newmsginfo);
9244 folder_item_scan(draft);
9246 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9248 g_mutex_unlock(compose->mutex); /* must be done before closing */
9249 compose_close(compose);
9255 path = folder_item_fetch_msg(draft, msgnum);
9257 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9260 if (g_stat(path, &s) < 0) {
9261 FILE_OP_ERROR(path, "stat");
9267 procmsg_msginfo_free(compose->targetinfo);
9268 compose->targetinfo = procmsg_msginfo_new();
9269 compose->targetinfo->msgnum = msgnum;
9270 compose->targetinfo->size = (goffset)s.st_size;
9271 compose->targetinfo->mtime = s.st_mtime;
9272 compose->targetinfo->folder = draft;
9274 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9275 compose->mode = COMPOSE_REEDIT;
9277 if (action == COMPOSE_AUTO_SAVE) {
9278 compose->autosaved_draft = compose->targetinfo;
9280 compose->modified = FALSE;
9281 compose_set_title(compose);
9285 g_mutex_unlock(compose->mutex);
9289 void compose_clear_exit_drafts(void)
9291 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9292 DRAFTED_AT_EXIT, NULL);
9293 if (is_file_exist(filepath))
9294 claws_unlink(filepath);
9299 void compose_reopen_exit_drafts(void)
9301 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9302 DRAFTED_AT_EXIT, NULL);
9303 FILE *fp = g_fopen(filepath, "rb");
9307 while (fgets(buf, sizeof(buf), fp)) {
9308 gchar **parts = g_strsplit(buf, "\t", 2);
9309 const gchar *folder = parts[0];
9310 int msgnum = parts[1] ? atoi(parts[1]):-1;
9312 if (folder && *folder && msgnum > -1) {
9313 FolderItem *item = folder_find_item_from_identifier(folder);
9314 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9316 compose_reedit(info, FALSE);
9323 compose_clear_exit_drafts();
9326 static void compose_save_cb(GtkAction *action, gpointer data)
9328 Compose *compose = (Compose *)data;
9329 compose_draft(compose, COMPOSE_KEEP_EDITING);
9330 compose->rmode = COMPOSE_REEDIT;
9333 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9335 if (compose && file_list) {
9338 for ( tmp = file_list; tmp; tmp = tmp->next) {
9339 gchar *file = (gchar *) tmp->data;
9340 gchar *utf8_filename = conv_filename_to_utf8(file);
9341 compose_attach_append(compose, file, utf8_filename, NULL);
9342 compose_changed_cb(NULL, compose);
9347 g_free(utf8_filename);
9352 static void compose_attach_cb(GtkAction *action, gpointer data)
9354 Compose *compose = (Compose *)data;
9357 if (compose->redirect_filename != NULL)
9360 file_list = filesel_select_multiple_files_open(_("Select file"));
9363 compose_attach_from_list(compose, file_list, TRUE);
9364 g_list_free(file_list);
9368 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9370 Compose *compose = (Compose *)data;
9373 file_list = filesel_select_multiple_files_open(_("Select file"));
9378 for ( tmp = file_list; tmp; tmp = tmp->next) {
9379 gchar *file = (gchar *) tmp->data;
9380 gchar *filedup = g_strdup(file);
9381 gchar *shortfile = g_path_get_basename(filedup);
9382 struct stat file_stat;
9384 ComposeInsertResult res;
9389 /* get the size of the file we are about to insert */
9390 ret = g_stat(file, &file_stat);
9392 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
9394 } else if (prefs_common.warn_large_insert == TRUE) {
9396 /* ask user for confirmation if the file is large */
9397 if (prefs_common.warn_large_insert_size < 0 ||
9398 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
9402 msg = g_strdup_printf(_("You are about to insert a file of %s "
9403 "in the message body. Are your sure to do that?"),
9404 to_human_readable(file_stat.st_size));
9405 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
9406 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9409 /* do we ask for confirmation next time? */
9410 if (aval & G_ALERTDISABLE) {
9411 /* no confirmation next time, disable feature in preferences */
9412 aval &= ~G_ALERTDISABLE;
9413 prefs_common.warn_large_insert = FALSE;
9416 /* abort file insertion if user canceled action */
9417 if (aval != G_ALERTALTERNATE) {
9423 /* insert the file if the file is short or if the user confirmed that
9424 he/she wants to insert the large file */
9426 res = compose_insert_file(compose, file);
9427 if (res == COMPOSE_INSERT_READ_ERROR) {
9428 alertpanel_error(_("File '%s' could not be read."), shortfile);
9429 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9430 alertpanel_error(_("File '%s' contained invalid characters\n"
9431 "for the current encoding, insertion may be incorrect."),
9440 g_list_free(file_list);
9444 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9446 Compose *compose = (Compose *)data;
9448 compose_insert_sig(compose, FALSE);
9451 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9455 Compose *compose = (Compose *)data;
9457 gtkut_widget_get_uposition(widget, &x, &y);
9458 if (!compose->batch) {
9459 prefs_common.compose_x = x;
9460 prefs_common.compose_y = y;
9462 if (compose->sending || compose->updating)
9464 compose_close_cb(NULL, compose);
9468 void compose_close_toolbar(Compose *compose)
9470 compose_close_cb(NULL, compose);
9473 static void compose_close_cb(GtkAction *action, gpointer data)
9475 Compose *compose = (Compose *)data;
9479 if (compose->exteditor_tag != -1) {
9480 if (!compose_ext_editor_kill(compose))
9485 if (compose->modified) {
9486 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9487 if (!g_mutex_trylock(compose->mutex)) {
9488 /* we don't want to lock the mutex once it's available,
9489 * because as the only other part of compose.c locking
9490 * it is compose_close - which means once unlocked,
9491 * the compose struct will be freed */
9492 debug_print("couldn't lock mutex, probably sending\n");
9496 val = alertpanel(_("Discard message"),
9497 _("This message has been modified. Discard it?"),
9498 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9500 val = alertpanel(_("Save changes"),
9501 _("This message has been modified. Save the latest changes?"),
9502 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9504 g_mutex_unlock(compose->mutex);
9506 case G_ALERTDEFAULT:
9507 if (prefs_common.autosave && !reedit)
9508 compose_remove_draft(compose);
9510 case G_ALERTALTERNATE:
9511 compose_draft(data, COMPOSE_QUIT_EDITING);
9518 compose_close(compose);
9521 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9523 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9524 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9525 Compose *compose = (Compose *) data;
9528 compose->out_encoding = (CharSet)value;
9531 static void compose_address_cb(GtkAction *action, gpointer data)
9533 Compose *compose = (Compose *)data;
9535 addressbook_open(compose);
9538 static void about_show_cb(GtkAction *action, gpointer data)
9543 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9545 Compose *compose = (Compose *)data;
9550 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9551 cm_return_if_fail(tmpl != NULL);
9553 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9555 val = alertpanel(_("Apply template"), msg,
9556 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9559 if (val == G_ALERTDEFAULT)
9560 compose_template_apply(compose, tmpl, TRUE);
9561 else if (val == G_ALERTALTERNATE)
9562 compose_template_apply(compose, tmpl, FALSE);
9565 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9567 Compose *compose = (Compose *)data;
9569 compose_exec_ext_editor(compose);
9572 static void compose_undo_cb(GtkAction *action, gpointer data)
9574 Compose *compose = (Compose *)data;
9575 gboolean prev_autowrap = compose->autowrap;
9577 compose->autowrap = FALSE;
9578 undo_undo(compose->undostruct);
9579 compose->autowrap = prev_autowrap;
9582 static void compose_redo_cb(GtkAction *action, gpointer data)
9584 Compose *compose = (Compose *)data;
9585 gboolean prev_autowrap = compose->autowrap;
9587 compose->autowrap = FALSE;
9588 undo_redo(compose->undostruct);
9589 compose->autowrap = prev_autowrap;
9592 static void entry_cut_clipboard(GtkWidget *entry)
9594 if (GTK_IS_EDITABLE(entry))
9595 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9596 else if (GTK_IS_TEXT_VIEW(entry))
9597 gtk_text_buffer_cut_clipboard(
9598 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9599 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9603 static void entry_copy_clipboard(GtkWidget *entry)
9605 if (GTK_IS_EDITABLE(entry))
9606 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9607 else if (GTK_IS_TEXT_VIEW(entry))
9608 gtk_text_buffer_copy_clipboard(
9609 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9610 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9613 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9614 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9616 if (GTK_IS_TEXT_VIEW(entry)) {
9617 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9618 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9619 GtkTextIter start_iter, end_iter;
9621 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9623 if (contents == NULL)
9626 /* we shouldn't delete the selection when middle-click-pasting, or we
9627 * can't mid-click-paste our own selection */
9628 if (clip != GDK_SELECTION_PRIMARY) {
9629 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9630 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9633 if (insert_place == NULL) {
9634 /* if insert_place isn't specified, insert at the cursor.
9635 * used for Ctrl-V pasting */
9636 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9637 start = gtk_text_iter_get_offset(&start_iter);
9638 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9640 /* if insert_place is specified, paste here.
9641 * used for mid-click-pasting */
9642 start = gtk_text_iter_get_offset(insert_place);
9643 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9644 if (prefs_common.primary_paste_unselects)
9645 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9649 /* paste unwrapped: mark the paste so it's not wrapped later */
9650 end = start + strlen(contents);
9651 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9652 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9653 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9654 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9655 /* rewrap paragraph now (after a mid-click-paste) */
9656 mark_start = gtk_text_buffer_get_insert(buffer);
9657 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9658 gtk_text_iter_backward_char(&start_iter);
9659 compose_beautify_paragraph(compose, &start_iter, TRUE);
9661 } else if (GTK_IS_EDITABLE(entry))
9662 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9664 compose->modified = TRUE;
9667 static void entry_allsel(GtkWidget *entry)
9669 if (GTK_IS_EDITABLE(entry))
9670 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9671 else if (GTK_IS_TEXT_VIEW(entry)) {
9672 GtkTextIter startiter, enditer;
9673 GtkTextBuffer *textbuf;
9675 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9676 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9677 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9679 gtk_text_buffer_move_mark_by_name(textbuf,
9680 "selection_bound", &startiter);
9681 gtk_text_buffer_move_mark_by_name(textbuf,
9682 "insert", &enditer);
9686 static void compose_cut_cb(GtkAction *action, gpointer data)
9688 Compose *compose = (Compose *)data;
9689 if (compose->focused_editable
9690 #ifndef GENERIC_UMPC
9691 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9694 entry_cut_clipboard(compose->focused_editable);
9697 static void compose_copy_cb(GtkAction *action, gpointer data)
9699 Compose *compose = (Compose *)data;
9700 if (compose->focused_editable
9701 #ifndef GENERIC_UMPC
9702 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9705 entry_copy_clipboard(compose->focused_editable);
9708 static void compose_paste_cb(GtkAction *action, gpointer data)
9710 Compose *compose = (Compose *)data;
9712 GtkTextBuffer *buffer;
9714 if (compose->focused_editable &&
9715 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9716 entry_paste_clipboard(compose, compose->focused_editable,
9717 prefs_common.linewrap_pastes,
9718 GDK_SELECTION_CLIPBOARD, NULL);
9722 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9724 Compose *compose = (Compose *)data;
9725 gint wrap_quote = prefs_common.linewrap_quote;
9726 if (compose->focused_editable
9727 #ifndef GENERIC_UMPC
9728 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9731 /* let text_insert() (called directly or at a later time
9732 * after the gtk_editable_paste_clipboard) know that
9733 * text is to be inserted as a quotation. implemented
9734 * by using a simple refcount... */
9735 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9736 G_OBJECT(compose->focused_editable),
9737 "paste_as_quotation"));
9738 g_object_set_data(G_OBJECT(compose->focused_editable),
9739 "paste_as_quotation",
9740 GINT_TO_POINTER(paste_as_quotation + 1));
9741 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9742 entry_paste_clipboard(compose, compose->focused_editable,
9743 prefs_common.linewrap_pastes,
9744 GDK_SELECTION_CLIPBOARD, NULL);
9745 prefs_common.linewrap_quote = wrap_quote;
9749 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9751 Compose *compose = (Compose *)data;
9753 GtkTextBuffer *buffer;
9755 if (compose->focused_editable
9756 #ifndef GENERIC_UMPC
9757 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9760 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9761 GDK_SELECTION_CLIPBOARD, NULL);
9765 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9767 Compose *compose = (Compose *)data;
9769 GtkTextBuffer *buffer;
9771 if (compose->focused_editable
9772 #ifndef GENERIC_UMPC
9773 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9776 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9777 GDK_SELECTION_CLIPBOARD, NULL);
9781 static void compose_allsel_cb(GtkAction *action, gpointer data)
9783 Compose *compose = (Compose *)data;
9784 if (compose->focused_editable
9785 #ifndef GENERIC_UMPC
9786 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9789 entry_allsel(compose->focused_editable);
9792 static void textview_move_beginning_of_line (GtkTextView *text)
9794 GtkTextBuffer *buffer;
9798 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9800 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9801 mark = gtk_text_buffer_get_insert(buffer);
9802 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9803 gtk_text_iter_set_line_offset(&ins, 0);
9804 gtk_text_buffer_place_cursor(buffer, &ins);
9807 static void textview_move_forward_character (GtkTextView *text)
9809 GtkTextBuffer *buffer;
9813 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9815 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9816 mark = gtk_text_buffer_get_insert(buffer);
9817 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9818 if (gtk_text_iter_forward_cursor_position(&ins))
9819 gtk_text_buffer_place_cursor(buffer, &ins);
9822 static void textview_move_backward_character (GtkTextView *text)
9824 GtkTextBuffer *buffer;
9828 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9830 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9831 mark = gtk_text_buffer_get_insert(buffer);
9832 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9833 if (gtk_text_iter_backward_cursor_position(&ins))
9834 gtk_text_buffer_place_cursor(buffer, &ins);
9837 static void textview_move_forward_word (GtkTextView *text)
9839 GtkTextBuffer *buffer;
9844 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9846 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9847 mark = gtk_text_buffer_get_insert(buffer);
9848 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9849 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9850 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9851 gtk_text_iter_backward_word_start(&ins);
9852 gtk_text_buffer_place_cursor(buffer, &ins);
9856 static void textview_move_backward_word (GtkTextView *text)
9858 GtkTextBuffer *buffer;
9863 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9865 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9866 mark = gtk_text_buffer_get_insert(buffer);
9867 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9868 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9869 if (gtk_text_iter_backward_word_starts(&ins, 1))
9870 gtk_text_buffer_place_cursor(buffer, &ins);
9873 static void textview_move_end_of_line (GtkTextView *text)
9875 GtkTextBuffer *buffer;
9879 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9881 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9882 mark = gtk_text_buffer_get_insert(buffer);
9883 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9884 if (gtk_text_iter_forward_to_line_end(&ins))
9885 gtk_text_buffer_place_cursor(buffer, &ins);
9888 static void textview_move_next_line (GtkTextView *text)
9890 GtkTextBuffer *buffer;
9895 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9897 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9898 mark = gtk_text_buffer_get_insert(buffer);
9899 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9900 offset = gtk_text_iter_get_line_offset(&ins);
9901 if (gtk_text_iter_forward_line(&ins)) {
9902 gtk_text_iter_set_line_offset(&ins, offset);
9903 gtk_text_buffer_place_cursor(buffer, &ins);
9907 static void textview_move_previous_line (GtkTextView *text)
9909 GtkTextBuffer *buffer;
9914 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9916 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9917 mark = gtk_text_buffer_get_insert(buffer);
9918 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9919 offset = gtk_text_iter_get_line_offset(&ins);
9920 if (gtk_text_iter_backward_line(&ins)) {
9921 gtk_text_iter_set_line_offset(&ins, offset);
9922 gtk_text_buffer_place_cursor(buffer, &ins);
9926 static void textview_delete_forward_character (GtkTextView *text)
9928 GtkTextBuffer *buffer;
9930 GtkTextIter ins, end_iter;
9932 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9934 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9935 mark = gtk_text_buffer_get_insert(buffer);
9936 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9938 if (gtk_text_iter_forward_char(&end_iter)) {
9939 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9943 static void textview_delete_backward_character (GtkTextView *text)
9945 GtkTextBuffer *buffer;
9947 GtkTextIter ins, end_iter;
9949 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9951 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9952 mark = gtk_text_buffer_get_insert(buffer);
9953 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9955 if (gtk_text_iter_backward_char(&end_iter)) {
9956 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9960 static void textview_delete_forward_word (GtkTextView *text)
9962 GtkTextBuffer *buffer;
9964 GtkTextIter ins, end_iter;
9966 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9968 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9969 mark = gtk_text_buffer_get_insert(buffer);
9970 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9972 if (gtk_text_iter_forward_word_end(&end_iter)) {
9973 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9977 static void textview_delete_backward_word (GtkTextView *text)
9979 GtkTextBuffer *buffer;
9981 GtkTextIter ins, end_iter;
9983 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9985 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9986 mark = gtk_text_buffer_get_insert(buffer);
9987 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9989 if (gtk_text_iter_backward_word_start(&end_iter)) {
9990 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9994 static void textview_delete_line (GtkTextView *text)
9996 GtkTextBuffer *buffer;
9998 GtkTextIter ins, start_iter, end_iter;
10000 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10002 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10003 mark = gtk_text_buffer_get_insert(buffer);
10004 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10007 gtk_text_iter_set_line_offset(&start_iter, 0);
10010 if (gtk_text_iter_ends_line(&end_iter)){
10011 if (!gtk_text_iter_forward_char(&end_iter))
10012 gtk_text_iter_backward_char(&start_iter);
10015 gtk_text_iter_forward_to_line_end(&end_iter);
10016 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10019 static void textview_delete_to_line_end (GtkTextView *text)
10021 GtkTextBuffer *buffer;
10023 GtkTextIter ins, end_iter;
10025 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10027 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10028 mark = gtk_text_buffer_get_insert(buffer);
10029 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10031 if (gtk_text_iter_ends_line(&end_iter))
10032 gtk_text_iter_forward_char(&end_iter);
10034 gtk_text_iter_forward_to_line_end(&end_iter);
10035 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10038 #define DO_ACTION(name, act) { \
10039 if(!strcmp(name, a_name)) { \
10043 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10045 const gchar *a_name = gtk_action_get_name(action);
10046 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10047 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10048 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10049 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10050 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10051 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10052 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10053 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10054 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10055 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10056 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10057 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10058 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10059 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10063 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10065 Compose *compose = (Compose *)data;
10066 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10067 ComposeCallAdvancedAction action = -1;
10069 action = compose_call_advanced_action_from_path(gaction);
10072 void (*do_action) (GtkTextView *text);
10073 } action_table[] = {
10074 {textview_move_beginning_of_line},
10075 {textview_move_forward_character},
10076 {textview_move_backward_character},
10077 {textview_move_forward_word},
10078 {textview_move_backward_word},
10079 {textview_move_end_of_line},
10080 {textview_move_next_line},
10081 {textview_move_previous_line},
10082 {textview_delete_forward_character},
10083 {textview_delete_backward_character},
10084 {textview_delete_forward_word},
10085 {textview_delete_backward_word},
10086 {textview_delete_line},
10087 {textview_delete_to_line_end}
10090 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10092 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10093 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10094 if (action_table[action].do_action)
10095 action_table[action].do_action(text);
10097 g_warning("Not implemented yet.");
10101 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10105 if (GTK_IS_EDITABLE(widget)) {
10106 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10107 gtk_editable_set_position(GTK_EDITABLE(widget),
10110 if (widget->parent && widget->parent->parent
10111 && widget->parent->parent->parent) {
10112 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10113 gint y = widget->allocation.y;
10114 gint height = widget->allocation.height;
10115 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10116 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10118 if (y < (int)shown->value) {
10119 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10121 if (y + height > (int)shown->value + (int)shown->page_size) {
10122 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10123 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10124 y + height - (int)shown->page_size - 1);
10126 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10127 (int)shown->upper - (int)shown->page_size - 1);
10134 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10135 compose->focused_editable = widget;
10137 #ifdef GENERIC_UMPC
10138 if (GTK_IS_TEXT_VIEW(widget)
10139 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10140 g_object_ref(compose->notebook);
10141 g_object_ref(compose->edit_vbox);
10142 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10143 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10144 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10145 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10146 g_object_unref(compose->notebook);
10147 g_object_unref(compose->edit_vbox);
10148 g_signal_handlers_block_by_func(G_OBJECT(widget),
10149 G_CALLBACK(compose_grab_focus_cb),
10151 gtk_widget_grab_focus(widget);
10152 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10153 G_CALLBACK(compose_grab_focus_cb),
10155 } else if (!GTK_IS_TEXT_VIEW(widget)
10156 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10157 g_object_ref(compose->notebook);
10158 g_object_ref(compose->edit_vbox);
10159 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10160 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10161 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10162 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10163 g_object_unref(compose->notebook);
10164 g_object_unref(compose->edit_vbox);
10165 g_signal_handlers_block_by_func(G_OBJECT(widget),
10166 G_CALLBACK(compose_grab_focus_cb),
10168 gtk_widget_grab_focus(widget);
10169 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10170 G_CALLBACK(compose_grab_focus_cb),
10176 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10178 compose->modified = TRUE;
10179 // compose_beautify_paragraph(compose, NULL, TRUE);
10180 #ifndef GENERIC_UMPC
10181 compose_set_title(compose);
10185 static void compose_wrap_cb(GtkAction *action, gpointer data)
10187 Compose *compose = (Compose *)data;
10188 compose_beautify_paragraph(compose, NULL, TRUE);
10191 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10193 Compose *compose = (Compose *)data;
10194 compose_wrap_all_full(compose, TRUE);
10197 static void compose_find_cb(GtkAction *action, gpointer data)
10199 Compose *compose = (Compose *)data;
10201 message_search_compose(compose);
10204 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10207 Compose *compose = (Compose *)data;
10208 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10209 if (compose->autowrap)
10210 compose_wrap_all_full(compose, TRUE);
10211 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10214 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10217 Compose *compose = (Compose *)data;
10218 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10221 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10223 Compose *compose = (Compose *)data;
10225 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10228 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10230 Compose *compose = (Compose *)data;
10232 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10235 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10237 g_free(compose->privacy_system);
10239 compose->privacy_system = g_strdup(account->default_privacy_system);
10240 compose_update_privacy_system_menu_item(compose, warn);
10243 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10245 Compose *compose = (Compose *)data;
10247 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10248 gtk_widget_show(compose->ruler_hbox);
10249 prefs_common.show_ruler = TRUE;
10251 gtk_widget_hide(compose->ruler_hbox);
10252 gtk_widget_queue_resize(compose->edit_vbox);
10253 prefs_common.show_ruler = FALSE;
10257 static void compose_attach_drag_received_cb (GtkWidget *widget,
10258 GdkDragContext *context,
10261 GtkSelectionData *data,
10264 gpointer user_data)
10266 Compose *compose = (Compose *)user_data;
10269 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10271 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10273 ) && gtk_drag_get_source_widget(context) !=
10274 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10275 list = uri_list_extract_filenames((const gchar *)data->data);
10276 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10277 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10278 compose_attach_append
10279 (compose, (const gchar *)tmp->data,
10280 utf8_filename, NULL);
10281 g_free(utf8_filename);
10283 if (list) compose_changed_cb(NULL, compose);
10284 list_free_strings(list);
10286 } else if (gtk_drag_get_source_widget(context)
10287 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10288 /* comes from our summaryview */
10289 SummaryView * summaryview = NULL;
10290 GSList * list = NULL, *cur = NULL;
10292 if (mainwindow_get_mainwindow())
10293 summaryview = mainwindow_get_mainwindow()->summaryview;
10296 list = summary_get_selected_msg_list(summaryview);
10298 for (cur = list; cur; cur = cur->next) {
10299 MsgInfo *msginfo = (MsgInfo *)cur->data;
10300 gchar *file = NULL;
10302 file = procmsg_get_message_file_full(msginfo,
10305 compose_attach_append(compose, (const gchar *)file,
10306 (const gchar *)file, "message/rfc822");
10310 g_slist_free(list);
10314 static gboolean compose_drag_drop(GtkWidget *widget,
10315 GdkDragContext *drag_context,
10317 guint time, gpointer user_data)
10319 /* not handling this signal makes compose_insert_drag_received_cb
10324 static void compose_insert_drag_received_cb (GtkWidget *widget,
10325 GdkDragContext *drag_context,
10328 GtkSelectionData *data,
10331 gpointer user_data)
10333 Compose *compose = (Compose *)user_data;
10336 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10339 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10341 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10343 AlertValue val = G_ALERTDEFAULT;
10345 list = uri_list_extract_filenames((const gchar *)data->data);
10346 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10347 /* Assume a list of no files, and data has ://, is a remote link */
10348 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10349 gchar *tmpfile = get_tmp_file();
10350 str_write_to_file(tmpdata, tmpfile);
10352 compose_insert_file(compose, tmpfile);
10353 claws_unlink(tmpfile);
10355 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10356 compose_beautify_paragraph(compose, NULL, TRUE);
10359 switch (prefs_common.compose_dnd_mode) {
10360 case COMPOSE_DND_ASK:
10361 val = alertpanel_full(_("Insert or attach?"),
10362 _("Do you want to insert the contents of the file(s) "
10363 "into the message body, or attach it to the email?"),
10364 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10365 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10367 case COMPOSE_DND_INSERT:
10368 val = G_ALERTALTERNATE;
10370 case COMPOSE_DND_ATTACH:
10371 val = G_ALERTOTHER;
10374 /* unexpected case */
10375 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10378 if (val & G_ALERTDISABLE) {
10379 val &= ~G_ALERTDISABLE;
10380 /* remember what action to perform by default, only if we don't click Cancel */
10381 if (val == G_ALERTALTERNATE)
10382 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10383 else if (val == G_ALERTOTHER)
10384 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10387 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10388 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10389 list_free_strings(list);
10392 } else if (val == G_ALERTOTHER) {
10393 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10394 list_free_strings(list);
10399 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10400 compose_insert_file(compose, (const gchar *)tmp->data);
10402 list_free_strings(list);
10404 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10407 #if GTK_CHECK_VERSION(2, 8, 0)
10408 /* do nothing, handled by GTK */
10410 gchar *tmpfile = get_tmp_file();
10411 str_write_to_file((const gchar *)data->data, tmpfile);
10412 compose_insert_file(compose, tmpfile);
10413 claws_unlink(tmpfile);
10415 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10419 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10422 static void compose_header_drag_received_cb (GtkWidget *widget,
10423 GdkDragContext *drag_context,
10426 GtkSelectionData *data,
10429 gpointer user_data)
10431 GtkEditable *entry = (GtkEditable *)user_data;
10432 gchar *email = (gchar *)data->data;
10434 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10437 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10438 gchar *decoded=g_new(gchar, strlen(email));
10441 email += strlen("mailto:");
10442 decode_uri(decoded, email); /* will fit */
10443 gtk_editable_delete_text(entry, 0, -1);
10444 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10445 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10449 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10452 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10454 Compose *compose = (Compose *)data;
10456 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10457 compose->return_receipt = TRUE;
10459 compose->return_receipt = FALSE;
10462 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10464 Compose *compose = (Compose *)data;
10466 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10467 compose->remove_references = TRUE;
10469 compose->remove_references = FALSE;
10472 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10473 ComposeHeaderEntry *headerentry)
10475 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10479 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10480 GdkEventKey *event,
10481 ComposeHeaderEntry *headerentry)
10483 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10484 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10485 !(event->state & GDK_MODIFIER_MASK) &&
10486 (event->keyval == GDK_BackSpace) &&
10487 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10488 gtk_container_remove
10489 (GTK_CONTAINER(headerentry->compose->header_table),
10490 headerentry->combo);
10491 gtk_container_remove
10492 (GTK_CONTAINER(headerentry->compose->header_table),
10493 headerentry->entry);
10494 headerentry->compose->header_list =
10495 g_slist_remove(headerentry->compose->header_list,
10497 g_free(headerentry);
10498 } else if (event->keyval == GDK_Tab) {
10499 if (headerentry->compose->header_last == headerentry) {
10500 /* Override default next focus, and give it to subject_entry
10501 * instead of notebook tabs
10503 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10504 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10511 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10512 ComposeHeaderEntry *headerentry)
10514 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10515 compose_create_header_entry(headerentry->compose);
10516 g_signal_handlers_disconnect_matched
10517 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10518 0, 0, NULL, NULL, headerentry);
10520 /* Automatically scroll down */
10521 compose_show_first_last_header(headerentry->compose, FALSE);
10527 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10529 GtkAdjustment *vadj;
10531 cm_return_if_fail(compose);
10532 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10533 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10535 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10536 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10537 gtk_adjustment_changed(vadj);
10540 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10541 const gchar *text, gint len, Compose *compose)
10543 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10544 (G_OBJECT(compose->text), "paste_as_quotation"));
10547 cm_return_if_fail(text != NULL);
10549 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10550 G_CALLBACK(text_inserted),
10552 if (paste_as_quotation) {
10554 const gchar *qmark;
10556 GtkTextIter start_iter;
10559 len = strlen(text);
10561 new_text = g_strndup(text, len);
10563 qmark = compose_quote_char_from_context(compose);
10565 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10566 gtk_text_buffer_place_cursor(buffer, iter);
10568 pos = gtk_text_iter_get_offset(iter);
10570 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10571 _("Quote format error at line %d."));
10572 quote_fmt_reset_vartable();
10574 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10575 GINT_TO_POINTER(paste_as_quotation - 1));
10577 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10578 gtk_text_buffer_place_cursor(buffer, iter);
10579 gtk_text_buffer_delete_mark(buffer, mark);
10581 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10582 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10583 compose_beautify_paragraph(compose, &start_iter, FALSE);
10584 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10585 gtk_text_buffer_delete_mark(buffer, mark);
10587 if (strcmp(text, "\n") || compose->automatic_break
10588 || gtk_text_iter_starts_line(iter)) {
10589 GtkTextIter before_ins;
10590 gtk_text_buffer_insert(buffer, iter, text, len);
10591 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10592 before_ins = *iter;
10593 gtk_text_iter_backward_chars(&before_ins, len);
10594 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10597 /* check if the preceding is just whitespace or quote */
10598 GtkTextIter start_line;
10599 gchar *tmp = NULL, *quote = NULL;
10600 gint quote_len = 0, is_normal = 0;
10601 start_line = *iter;
10602 gtk_text_iter_set_line_offset(&start_line, 0);
10603 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10606 if (*tmp == '\0') {
10609 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10617 gtk_text_buffer_insert(buffer, iter, text, len);
10619 gtk_text_buffer_insert_with_tags_by_name(buffer,
10620 iter, text, len, "no_join", NULL);
10625 if (!paste_as_quotation) {
10626 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10627 compose_beautify_paragraph(compose, iter, FALSE);
10628 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10629 gtk_text_buffer_delete_mark(buffer, mark);
10632 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10633 G_CALLBACK(text_inserted),
10635 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10637 if (prefs_common.autosave &&
10638 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10639 compose->draft_timeout_tag != -2 /* disabled while loading */)
10640 compose->draft_timeout_tag = g_timeout_add
10641 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10643 static gint compose_defer_auto_save_draft(Compose *compose)
10645 compose->draft_timeout_tag = -1;
10646 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10651 static void compose_check_all(GtkAction *action, gpointer data)
10653 Compose *compose = (Compose *)data;
10654 if (!compose->gtkaspell)
10657 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10658 claws_spell_entry_check_all(
10659 CLAWS_SPELL_ENTRY(compose->subject_entry));
10661 gtkaspell_check_all(compose->gtkaspell);
10664 static void compose_highlight_all(GtkAction *action, gpointer data)
10666 Compose *compose = (Compose *)data;
10667 if (compose->gtkaspell) {
10668 claws_spell_entry_recheck_all(
10669 CLAWS_SPELL_ENTRY(compose->subject_entry));
10670 gtkaspell_highlight_all(compose->gtkaspell);
10674 static void compose_check_backwards(GtkAction *action, gpointer data)
10676 Compose *compose = (Compose *)data;
10677 if (!compose->gtkaspell) {
10678 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10682 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10683 claws_spell_entry_check_backwards(
10684 CLAWS_SPELL_ENTRY(compose->subject_entry));
10686 gtkaspell_check_backwards(compose->gtkaspell);
10689 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10691 Compose *compose = (Compose *)data;
10692 if (!compose->gtkaspell) {
10693 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10697 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10698 claws_spell_entry_check_forwards_go(
10699 CLAWS_SPELL_ENTRY(compose->subject_entry));
10701 gtkaspell_check_forwards_go(compose->gtkaspell);
10706 *\brief Guess originating forward account from MsgInfo and several
10707 * "common preference" settings. Return NULL if no guess.
10709 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10711 PrefsAccount *account = NULL;
10713 cm_return_val_if_fail(msginfo, NULL);
10714 cm_return_val_if_fail(msginfo->folder, NULL);
10715 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10717 if (msginfo->folder->prefs->enable_default_account)
10718 account = account_find_from_id(msginfo->folder->prefs->default_account);
10721 account = msginfo->folder->folder->account;
10723 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10725 Xstrdup_a(to, msginfo->to, return NULL);
10726 extract_address(to);
10727 account = account_find_from_address(to, FALSE);
10730 if (!account && prefs_common.forward_account_autosel) {
10731 gchar cc[BUFFSIZE];
10732 if (!procheader_get_header_from_msginfo
10733 (msginfo, cc,sizeof cc , "Cc:")) {
10734 gchar *buf = cc + strlen("Cc:");
10735 extract_address(buf);
10736 account = account_find_from_address(buf, FALSE);
10740 if (!account && prefs_common.forward_account_autosel) {
10741 gchar deliveredto[BUFFSIZE];
10742 if (!procheader_get_header_from_msginfo
10743 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10744 gchar *buf = deliveredto + strlen("Delivered-To:");
10745 extract_address(buf);
10746 account = account_find_from_address(buf, FALSE);
10753 gboolean compose_close(Compose *compose)
10757 if (!g_mutex_trylock(compose->mutex)) {
10758 /* we have to wait for the (possibly deferred by auto-save)
10759 * drafting to be done, before destroying the compose under
10761 debug_print("waiting for drafting to finish...\n");
10762 compose_allow_user_actions(compose, FALSE);
10763 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10766 cm_return_val_if_fail(compose, FALSE);
10767 gtkut_widget_get_uposition(compose->window, &x, &y);
10768 if (!compose->batch) {
10769 prefs_common.compose_x = x;
10770 prefs_common.compose_y = y;
10772 g_mutex_unlock(compose->mutex);
10773 compose_destroy(compose);
10778 * Add entry field for each address in list.
10779 * \param compose E-Mail composition object.
10780 * \param listAddress List of (formatted) E-Mail addresses.
10782 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10785 node = listAddress;
10787 addr = ( gchar * ) node->data;
10788 compose_entry_append( compose, addr, COMPOSE_TO );
10789 node = g_list_next( node );
10793 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10794 guint action, gboolean opening_multiple)
10796 gchar *body = NULL;
10797 GSList *new_msglist = NULL;
10798 MsgInfo *tmp_msginfo = NULL;
10799 gboolean originally_enc = FALSE;
10800 gboolean originally_sig = FALSE;
10801 Compose *compose = NULL;
10802 gchar *s_system = NULL;
10804 cm_return_if_fail(msgview != NULL);
10806 cm_return_if_fail(msginfo_list != NULL);
10808 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10809 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10810 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10812 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10813 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10814 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10815 orig_msginfo, mimeinfo);
10816 if (tmp_msginfo != NULL) {
10817 new_msglist = g_slist_append(NULL, tmp_msginfo);
10819 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10820 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10821 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10823 tmp_msginfo->folder = orig_msginfo->folder;
10824 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10825 if (orig_msginfo->tags) {
10826 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10827 tmp_msginfo->folder->tags_dirty = TRUE;
10833 if (!opening_multiple)
10834 body = messageview_get_selection(msgview);
10837 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10838 procmsg_msginfo_free(tmp_msginfo);
10839 g_slist_free(new_msglist);
10841 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10843 if (compose && originally_enc) {
10844 compose_force_encryption(compose, compose->account, FALSE, s_system);
10847 if (compose && originally_sig && compose->account->default_sign_reply) {
10848 compose_force_signing(compose, compose->account, s_system);
10854 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10857 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10858 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10859 GSList *cur = msginfo_list;
10860 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10861 "messages. Opening the windows "
10862 "could take some time. Do you "
10863 "want to continue?"),
10864 g_slist_length(msginfo_list));
10865 if (g_slist_length(msginfo_list) > 9
10866 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10867 != G_ALERTALTERNATE) {
10872 /* We'll open multiple compose windows */
10873 /* let the WM place the next windows */
10874 compose_force_window_origin = FALSE;
10875 for (; cur; cur = cur->next) {
10877 tmplist.data = cur->data;
10878 tmplist.next = NULL;
10879 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10881 compose_force_window_origin = TRUE;
10883 /* forwarding multiple mails as attachments is done via a
10884 * single compose window */
10885 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10889 void compose_set_position(Compose *compose, gint pos)
10891 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10893 gtkut_text_view_set_position(text, pos);
10896 gboolean compose_search_string(Compose *compose,
10897 const gchar *str, gboolean case_sens)
10899 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10901 return gtkut_text_view_search_string(text, str, case_sens);
10904 gboolean compose_search_string_backward(Compose *compose,
10905 const gchar *str, gboolean case_sens)
10907 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10909 return gtkut_text_view_search_string_backward(text, str, case_sens);
10912 /* allocate a msginfo structure and populate its data from a compose data structure */
10913 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10915 MsgInfo *newmsginfo;
10917 gchar buf[BUFFSIZE];
10919 cm_return_val_if_fail( compose != NULL, NULL );
10921 newmsginfo = procmsg_msginfo_new();
10924 get_rfc822_date(buf, sizeof(buf));
10925 newmsginfo->date = g_strdup(buf);
10928 if (compose->from_name) {
10929 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10930 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10934 if (compose->subject_entry)
10935 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10937 /* to, cc, reply-to, newsgroups */
10938 for (list = compose->header_list; list; list = list->next) {
10939 gchar *header = gtk_editable_get_chars(
10941 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10942 gchar *entry = gtk_editable_get_chars(
10943 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10945 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10946 if ( newmsginfo->to == NULL ) {
10947 newmsginfo->to = g_strdup(entry);
10948 } else if (entry && *entry) {
10949 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10950 g_free(newmsginfo->to);
10951 newmsginfo->to = tmp;
10954 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10955 if ( newmsginfo->cc == NULL ) {
10956 newmsginfo->cc = g_strdup(entry);
10957 } else if (entry && *entry) {
10958 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10959 g_free(newmsginfo->cc);
10960 newmsginfo->cc = tmp;
10963 if ( strcasecmp(header,
10964 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10965 if ( newmsginfo->newsgroups == NULL ) {
10966 newmsginfo->newsgroups = g_strdup(entry);
10967 } else if (entry && *entry) {
10968 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10969 g_free(newmsginfo->newsgroups);
10970 newmsginfo->newsgroups = tmp;
10978 /* other data is unset */
10984 /* update compose's dictionaries from folder dict settings */
10985 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10986 FolderItem *folder_item)
10988 cm_return_if_fail(compose != NULL);
10990 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10991 FolderItemPrefs *prefs = folder_item->prefs;
10993 if (prefs->enable_default_dictionary)
10994 gtkaspell_change_dict(compose->gtkaspell,
10995 prefs->default_dictionary, FALSE);
10996 if (folder_item->prefs->enable_default_alt_dictionary)
10997 gtkaspell_change_alt_dict(compose->gtkaspell,
10998 prefs->default_alt_dictionary);
10999 if (prefs->enable_default_dictionary
11000 || prefs->enable_default_alt_dictionary)
11001 compose_spell_menu_changed(compose);