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_template_apply (Compose *compose,
211 static void compose_destroy (Compose *compose);
213 static void compose_entries_set (Compose *compose,
215 ComposeEntryType to_type);
216 static gint compose_parse_header (Compose *compose,
218 static gchar *compose_parse_references (const gchar *ref,
221 static gchar *compose_quote_fmt (Compose *compose,
227 gboolean need_unescape,
228 const gchar *err_msg);
230 static void compose_reply_set_entry (Compose *compose,
236 followup_and_reply_to);
237 static void compose_reedit_set_entry (Compose *compose,
240 static void compose_insert_sig (Compose *compose,
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static void compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
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);
500 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
502 static void compose_allow_user_actions (Compose *compose, gboolean allow);
504 static void compose_nothing_cb (GtkAction *action, gpointer data)
510 static void compose_check_all (GtkAction *action, gpointer data);
511 static void compose_highlight_all (GtkAction *action, gpointer data);
512 static void compose_check_backwards (GtkAction *action, gpointer data);
513 static void compose_check_forwards_go (GtkAction *action, gpointer data);
516 static gint compose_defer_auto_save_draft (Compose *compose);
517 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
519 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
522 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
523 FolderItem *folder_item);
525 static void compose_attach_update_label(Compose *compose);
527 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
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 _("New message body format 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(_("Message reply From format error."));
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 _("Message reply format 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(_("Message forward From format error."));
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 _("Message forward format 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(_("Message forward From format error."));
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 _("Message redirect format 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(_("Quote mark format error."));
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 {"www.", strcasestr, get_uri_part, make_http_string},
4122 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4123 {"@", strcasestr, get_email_part, make_email_string}
4125 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4126 gint last_index = PARSE_ELEMS;
4128 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4132 if (!prev_autowrap && num_blocks == 0) {
4134 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4135 G_CALLBACK(text_inserted),
4138 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4141 uri_start = uri_stop = -1;
4143 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4146 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4147 if (startq_offset == -1)
4148 startq_offset = gtk_text_iter_get_offset(&iter);
4149 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4150 if (quotelevel > 2) {
4151 /* recycle colors */
4152 if (prefs_common.recycle_quote_colors)
4161 if (startq_offset == -1)
4162 noq_offset = gtk_text_iter_get_offset(&iter);
4166 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4169 if (gtk_text_iter_ends_line(&iter)) {
4171 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4172 prefs_common.linewrap_len,
4174 GtkTextIter prev, next, cur;
4175 if (prev_autowrap != FALSE || force) {
4176 compose->automatic_break = TRUE;
4178 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4179 compose->automatic_break = FALSE;
4180 if (itemized_len && compose->autoindent) {
4181 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4182 if (!item_continuation)
4183 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4185 } else if (quote_str && wrap_quote) {
4186 compose->automatic_break = TRUE;
4188 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4189 compose->automatic_break = FALSE;
4190 if (itemized_len && compose->autoindent) {
4191 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4192 if (!item_continuation)
4193 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4197 /* remove trailing spaces */
4199 rem_item_len = itemized_len;
4200 while (compose->autoindent && rem_item_len-- > 0)
4201 gtk_text_iter_backward_char(&cur);
4202 gtk_text_iter_backward_char(&cur);
4205 while (!gtk_text_iter_starts_line(&cur)) {
4208 gtk_text_iter_backward_char(&cur);
4209 wc = gtk_text_iter_get_char(&cur);
4210 if (!g_unichar_isspace(wc))
4214 if (!gtk_text_iter_equal(&prev, &next)) {
4215 gtk_text_buffer_delete(buffer, &prev, &next);
4217 gtk_text_iter_forward_char(&break_pos);
4221 gtk_text_buffer_insert(buffer, &break_pos,
4225 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4227 /* move iter to current line start */
4228 gtk_text_iter_set_line_offset(&iter, 0);
4235 /* move iter to next line start */
4241 if (!prev_autowrap && num_blocks > 0) {
4243 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4244 G_CALLBACK(text_inserted),
4248 while (!gtk_text_iter_ends_line(&end_of_line)) {
4249 gtk_text_iter_forward_char(&end_of_line);
4251 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4253 nouri_start = gtk_text_iter_get_offset(&iter);
4254 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4256 walk_pos = gtk_text_iter_get_offset(&iter);
4257 /* FIXME: this looks phony. scanning for anything in the parse table */
4258 for (n = 0; n < PARSE_ELEMS; n++) {
4261 tmp = parser[n].search(walk, parser[n].needle);
4263 if (scanpos == NULL || tmp < scanpos) {
4272 /* check if URI can be parsed */
4273 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4274 (const gchar **)&ep, FALSE)
4275 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4279 strlen(parser[last_index].needle);
4282 uri_start = walk_pos + (bp - o_walk);
4283 uri_stop = walk_pos + (ep - o_walk);
4287 gtk_text_iter_forward_line(&iter);
4290 if (startq_offset != -1) {
4291 GtkTextIter startquote, endquote;
4292 gtk_text_buffer_get_iter_at_offset(
4293 buffer, &startquote, startq_offset);
4296 switch (quotelevel) {
4298 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4299 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4300 gtk_text_buffer_apply_tag_by_name(
4301 buffer, "quote0", &startquote, &endquote);
4302 gtk_text_buffer_remove_tag_by_name(
4303 buffer, "quote1", &startquote, &endquote);
4304 gtk_text_buffer_remove_tag_by_name(
4305 buffer, "quote2", &startquote, &endquote);
4310 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4311 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4312 gtk_text_buffer_apply_tag_by_name(
4313 buffer, "quote1", &startquote, &endquote);
4314 gtk_text_buffer_remove_tag_by_name(
4315 buffer, "quote0", &startquote, &endquote);
4316 gtk_text_buffer_remove_tag_by_name(
4317 buffer, "quote2", &startquote, &endquote);
4322 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4323 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4324 gtk_text_buffer_apply_tag_by_name(
4325 buffer, "quote2", &startquote, &endquote);
4326 gtk_text_buffer_remove_tag_by_name(
4327 buffer, "quote0", &startquote, &endquote);
4328 gtk_text_buffer_remove_tag_by_name(
4329 buffer, "quote1", &startquote, &endquote);
4335 } else if (noq_offset != -1) {
4336 GtkTextIter startnoquote, endnoquote;
4337 gtk_text_buffer_get_iter_at_offset(
4338 buffer, &startnoquote, noq_offset);
4341 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4342 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4343 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4344 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4345 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4346 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4347 gtk_text_buffer_remove_tag_by_name(
4348 buffer, "quote0", &startnoquote, &endnoquote);
4349 gtk_text_buffer_remove_tag_by_name(
4350 buffer, "quote1", &startnoquote, &endnoquote);
4351 gtk_text_buffer_remove_tag_by_name(
4352 buffer, "quote2", &startnoquote, &endnoquote);
4358 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4359 GtkTextIter nouri_start_iter, nouri_end_iter;
4360 gtk_text_buffer_get_iter_at_offset(
4361 buffer, &nouri_start_iter, nouri_start);
4362 gtk_text_buffer_get_iter_at_offset(
4363 buffer, &nouri_end_iter, nouri_stop);
4364 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4365 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4366 gtk_text_buffer_remove_tag_by_name(
4367 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4368 modified_before_remove = modified;
4373 if (uri_start >= 0 && uri_stop > 0) {
4374 GtkTextIter uri_start_iter, uri_end_iter, back;
4375 gtk_text_buffer_get_iter_at_offset(
4376 buffer, &uri_start_iter, uri_start);
4377 gtk_text_buffer_get_iter_at_offset(
4378 buffer, &uri_end_iter, uri_stop);
4379 back = uri_end_iter;
4380 gtk_text_iter_backward_char(&back);
4381 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4382 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4383 gtk_text_buffer_apply_tag_by_name(
4384 buffer, "link", &uri_start_iter, &uri_end_iter);
4386 if (removed && !modified_before_remove) {
4392 // debug_print("not modified, out after %d lines\n", lines);
4396 // debug_print("modified, out after %d lines\n", lines);
4398 g_free(itemized_chars);
4401 undo_wrapping(compose->undostruct, FALSE);
4402 compose->autowrap = prev_autowrap;
4407 void compose_action_cb(void *data)
4409 Compose *compose = (Compose *)data;
4410 compose_wrap_all(compose);
4413 static void compose_wrap_all(Compose *compose)
4415 compose_wrap_all_full(compose, FALSE);
4418 static void compose_wrap_all_full(Compose *compose, gboolean force)
4420 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4421 GtkTextBuffer *buffer;
4423 gboolean modified = TRUE;
4425 buffer = gtk_text_view_get_buffer(text);
4427 gtk_text_buffer_get_start_iter(buffer, &iter);
4428 while (!gtk_text_iter_is_end(&iter) && modified)
4429 modified = compose_beautify_paragraph(compose, &iter, force);
4433 static void compose_set_title(Compose *compose)
4439 edited = compose->modified ? _(" [Edited]") : "";
4441 subject = gtk_editable_get_chars(
4442 GTK_EDITABLE(compose->subject_entry), 0, -1);
4444 #ifndef GENERIC_UMPC
4445 if (subject && strlen(subject))
4446 str = g_strdup_printf(_("%s - Compose message%s"),
4449 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4451 str = g_strdup(_("Compose message"));
4454 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4460 * compose_current_mail_account:
4462 * Find a current mail account (the currently selected account, or the
4463 * default account, if a news account is currently selected). If a
4464 * mail account cannot be found, display an error message.
4466 * Return value: Mail account, or NULL if not found.
4468 static PrefsAccount *
4469 compose_current_mail_account(void)
4473 if (cur_account && cur_account->protocol != A_NNTP)
4476 ac = account_get_default();
4477 if (!ac || ac->protocol == A_NNTP) {
4478 alertpanel_error(_("Account for sending mail is not specified.\n"
4479 "Please select a mail account before sending."));
4486 #define QUOTE_IF_REQUIRED(out, str) \
4488 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4492 len = strlen(str) + 3; \
4493 if ((__tmp = alloca(len)) == NULL) { \
4494 g_warning("can't allocate memory\n"); \
4495 g_string_free(header, TRUE); \
4498 g_snprintf(__tmp, len, "\"%s\"", str); \
4503 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4504 g_warning("can't allocate memory\n"); \
4505 g_string_free(header, TRUE); \
4508 strcpy(__tmp, str); \
4514 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4516 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4520 len = strlen(str) + 3; \
4521 if ((__tmp = alloca(len)) == NULL) { \
4522 g_warning("can't allocate memory\n"); \
4525 g_snprintf(__tmp, len, "\"%s\"", str); \
4530 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4531 g_warning("can't allocate memory\n"); \
4534 strcpy(__tmp, str); \
4540 static void compose_select_account(Compose *compose, PrefsAccount *account,
4545 cm_return_if_fail(account != NULL);
4547 compose->account = account;
4549 if (account->name && *account->name) {
4551 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4552 from = g_strdup_printf("%s <%s>",
4553 buf, account->address);
4554 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4556 from = g_strdup_printf("<%s>",
4558 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4563 compose_set_title(compose);
4565 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4566 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4568 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4569 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4572 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4574 activate_privacy_system(compose, account, FALSE);
4576 if (!init && compose->mode != COMPOSE_REDIRECT) {
4577 undo_block(compose->undostruct);
4578 compose_insert_sig(compose, TRUE);
4579 undo_unblock(compose->undostruct);
4583 /* use account's dict info if set */
4584 if (compose->gtkaspell) {
4585 if (account->enable_default_dictionary)
4586 gtkaspell_change_dict(compose->gtkaspell,
4587 account->default_dictionary, FALSE);
4588 if (account->enable_default_alt_dictionary)
4589 gtkaspell_change_alt_dict(compose->gtkaspell,
4590 account->default_alt_dictionary);
4591 if (account->enable_default_dictionary
4592 || account->enable_default_alt_dictionary)
4593 compose_spell_menu_changed(compose);
4598 gboolean compose_check_for_valid_recipient(Compose *compose) {
4599 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4600 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4601 gboolean recipient_found = FALSE;
4605 /* free to and newsgroup list */
4606 slist_free_strings(compose->to_list);
4607 g_slist_free(compose->to_list);
4608 compose->to_list = NULL;
4610 slist_free_strings(compose->newsgroup_list);
4611 g_slist_free(compose->newsgroup_list);
4612 compose->newsgroup_list = NULL;
4614 /* search header entries for to and newsgroup entries */
4615 for (list = compose->header_list; list; list = list->next) {
4618 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4619 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4622 if (entry[0] != '\0') {
4623 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4624 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4625 compose->to_list = address_list_append(compose->to_list, entry);
4626 recipient_found = TRUE;
4629 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4630 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4631 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4632 recipient_found = TRUE;
4639 return recipient_found;
4642 static gboolean compose_check_for_set_recipients(Compose *compose)
4644 if (compose->account->set_autocc && compose->account->auto_cc) {
4645 gboolean found_other = FALSE;
4647 /* search header entries for to and newsgroup entries */
4648 for (list = compose->header_list; list; list = list->next) {
4651 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4652 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4655 if (strcmp(entry, compose->account->auto_cc)
4656 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4666 if (compose->batch) {
4667 gtk_widget_show_all(compose->window);
4669 aval = alertpanel(_("Send"),
4670 _("The only recipient is the default CC address. Send anyway?"),
4671 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4672 if (aval != G_ALERTALTERNATE)
4676 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4677 gboolean found_other = FALSE;
4679 /* search header entries for to and newsgroup entries */
4680 for (list = compose->header_list; list; list = list->next) {
4683 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4684 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4687 if (strcmp(entry, compose->account->auto_bcc)
4688 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4698 if (compose->batch) {
4699 gtk_widget_show_all(compose->window);
4701 aval = alertpanel(_("Send"),
4702 _("The only recipient is the default BCC address. Send anyway?"),
4703 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4704 if (aval != G_ALERTALTERNATE)
4711 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4715 if (compose_check_for_valid_recipient(compose) == FALSE) {
4716 if (compose->batch) {
4717 gtk_widget_show_all(compose->window);
4719 alertpanel_error(_("Recipient is not specified."));
4723 if (compose_check_for_set_recipients(compose) == FALSE) {
4727 if (!compose->batch) {
4728 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4729 if (*str == '\0' && check_everything == TRUE &&
4730 compose->mode != COMPOSE_REDIRECT) {
4732 gchar *button_label;
4735 if (compose->sending)
4736 button_label = _("+_Send");
4738 button_label = _("+_Queue");
4739 message = g_strdup_printf(_("Subject is empty. %s"),
4740 compose->sending?_("Send it anyway?"):
4741 _("Queue it anyway?"));
4743 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4744 GTK_STOCK_CANCEL, button_label, NULL);
4746 if (aval != G_ALERTALTERNATE)
4751 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4757 gint compose_send(Compose *compose)
4760 FolderItem *folder = NULL;
4762 gchar *msgpath = NULL;
4763 gboolean discard_window = FALSE;
4764 gchar *errstr = NULL;
4765 gchar *tmsgid = NULL;
4766 MainWindow *mainwin = mainwindow_get_mainwindow();
4767 gboolean queued_removed = FALSE;
4769 if (prefs_common.send_dialog_invisible
4770 || compose->batch == TRUE)
4771 discard_window = TRUE;
4773 compose_allow_user_actions (compose, FALSE);
4774 compose->sending = TRUE;
4776 if (compose_check_entries(compose, TRUE) == FALSE) {
4777 if (compose->batch) {
4778 gtk_widget_show_all(compose->window);
4784 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4787 if (compose->batch) {
4788 gtk_widget_show_all(compose->window);
4791 alertpanel_error(_("Could not queue message for sending:\n\n"
4792 "Charset conversion failed."));
4793 } else if (val == -5) {
4794 alertpanel_error(_("Could not queue message for sending:\n\n"
4795 "Couldn't get recipient encryption key."));
4796 } else if (val == -6) {
4798 } else if (val == -3) {
4799 if (privacy_peek_error())
4800 alertpanel_error(_("Could not queue message for sending:\n\n"
4801 "Signature failed: %s"), privacy_get_error());
4802 } else if (val == -2 && errno != 0) {
4803 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4805 alertpanel_error(_("Could not queue message for sending."));
4810 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4811 if (discard_window) {
4812 compose->sending = FALSE;
4813 compose_close(compose);
4814 /* No more compose access in the normal codepath
4815 * after this point! */
4820 alertpanel_error(_("The message was queued but could not be "
4821 "sent.\nUse \"Send queued messages\" from "
4822 "the main window to retry."));
4823 if (!discard_window) {
4830 if (msgpath == NULL) {
4831 msgpath = folder_item_fetch_msg(folder, msgnum);
4832 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4835 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4836 claws_unlink(msgpath);
4839 if (!discard_window) {
4841 if (!queued_removed)
4842 folder_item_remove_msg(folder, msgnum);
4843 folder_item_scan(folder);
4845 /* make sure we delete that */
4846 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4848 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4849 folder_item_remove_msg(folder, tmp->msgnum);
4850 procmsg_msginfo_free(tmp);
4857 if (!queued_removed)
4858 folder_item_remove_msg(folder, msgnum);
4859 folder_item_scan(folder);
4861 /* make sure we delete that */
4862 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4864 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4865 folder_item_remove_msg(folder, tmp->msgnum);
4866 procmsg_msginfo_free(tmp);
4869 if (!discard_window) {
4870 compose->sending = FALSE;
4871 compose_allow_user_actions (compose, TRUE);
4872 compose_close(compose);
4876 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4877 "the main window to retry."), errstr);
4880 alertpanel_error_log(_("The message was queued but could not be "
4881 "sent.\nUse \"Send queued messages\" from "
4882 "the main window to retry."));
4884 if (!discard_window) {
4893 toolbar_main_set_sensitive(mainwin);
4894 main_window_set_menu_sensitive(mainwin);
4900 compose_allow_user_actions (compose, TRUE);
4901 compose->sending = FALSE;
4902 compose->modified = TRUE;
4903 toolbar_main_set_sensitive(mainwin);
4904 main_window_set_menu_sensitive(mainwin);
4909 static gboolean compose_use_attach(Compose *compose)
4911 GtkTreeModel *model = gtk_tree_view_get_model
4912 (GTK_TREE_VIEW(compose->attach_clist));
4913 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4916 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4919 gchar buf[BUFFSIZE];
4921 gboolean first_to_address;
4922 gboolean first_cc_address;
4924 ComposeHeaderEntry *headerentry;
4925 const gchar *headerentryname;
4926 const gchar *cc_hdr;
4927 const gchar *to_hdr;
4928 gboolean err = FALSE;
4930 debug_print("Writing redirect header\n");
4932 cc_hdr = prefs_common_translated_header_name("Cc:");
4933 to_hdr = prefs_common_translated_header_name("To:");
4935 first_to_address = TRUE;
4936 for (list = compose->header_list; list; list = list->next) {
4937 headerentry = ((ComposeHeaderEntry *)list->data);
4938 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4940 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4941 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4942 Xstrdup_a(str, entstr, return -1);
4944 if (str[0] != '\0') {
4945 compose_convert_header
4946 (compose, buf, sizeof(buf), str,
4947 strlen("Resent-To") + 2, TRUE);
4949 if (first_to_address) {
4950 err |= (fprintf(fp, "Resent-To: ") < 0);
4951 first_to_address = FALSE;
4953 err |= (fprintf(fp, ",") < 0);
4955 err |= (fprintf(fp, "%s", buf) < 0);
4959 if (!first_to_address) {
4960 err |= (fprintf(fp, "\n") < 0);
4963 first_cc_address = TRUE;
4964 for (list = compose->header_list; list; list = list->next) {
4965 headerentry = ((ComposeHeaderEntry *)list->data);
4966 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4968 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4969 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4970 Xstrdup_a(str, strg, return -1);
4972 if (str[0] != '\0') {
4973 compose_convert_header
4974 (compose, buf, sizeof(buf), str,
4975 strlen("Resent-Cc") + 2, TRUE);
4977 if (first_cc_address) {
4978 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4979 first_cc_address = FALSE;
4981 err |= (fprintf(fp, ",") < 0);
4983 err |= (fprintf(fp, "%s", buf) < 0);
4987 if (!first_cc_address) {
4988 err |= (fprintf(fp, "\n") < 0);
4991 return (err ? -1:0);
4994 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4996 gchar buf[BUFFSIZE];
4998 const gchar *entstr;
4999 /* struct utsname utsbuf; */
5000 gboolean err = FALSE;
5002 cm_return_val_if_fail(fp != NULL, -1);
5003 cm_return_val_if_fail(compose->account != NULL, -1);
5004 cm_return_val_if_fail(compose->account->address != NULL, -1);
5007 get_rfc822_date(buf, sizeof(buf));
5008 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5011 if (compose->account->name && *compose->account->name) {
5012 compose_convert_header
5013 (compose, buf, sizeof(buf), compose->account->name,
5014 strlen("From: "), TRUE);
5015 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5016 buf, compose->account->address) < 0);
5018 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5021 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5022 if (*entstr != '\0') {
5023 Xstrdup_a(str, entstr, return -1);
5026 compose_convert_header(compose, buf, sizeof(buf), str,
5027 strlen("Subject: "), FALSE);
5028 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5032 /* Resent-Message-ID */
5033 if (compose->account->set_domain && compose->account->domain) {
5034 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5035 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5036 g_snprintf(buf, sizeof(buf), "%s",
5037 strchr(compose->account->address, '@') ?
5038 strchr(compose->account->address, '@')+1 :
5039 compose->account->address);
5041 g_snprintf(buf, sizeof(buf), "%s", "");
5044 if (compose->account->gen_msgid) {
5045 generate_msgid(buf, sizeof(buf));
5046 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5047 compose->msgid = g_strdup(buf);
5049 compose->msgid = NULL;
5052 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5055 /* separator between header and body */
5056 err |= (fputs("\n", fp) == EOF);
5058 return (err ? -1:0);
5061 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5065 gchar buf[BUFFSIZE];
5067 gboolean skip = FALSE;
5068 gboolean err = FALSE;
5069 gchar *not_included[]={
5070 "Return-Path:", "Delivered-To:", "Received:",
5071 "Subject:", "X-UIDL:", "AF:",
5072 "NF:", "PS:", "SRH:",
5073 "SFN:", "DSR:", "MID:",
5074 "CFG:", "PT:", "S:",
5075 "RQ:", "SSV:", "NSV:",
5076 "SSH:", "R:", "MAID:",
5077 "NAID:", "RMID:", "FMID:",
5078 "SCF:", "RRCPT:", "NG:",
5079 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5080 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5081 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5082 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5083 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5086 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5087 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5091 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5093 for (i = 0; not_included[i] != NULL; i++) {
5094 if (g_ascii_strncasecmp(buf, not_included[i],
5095 strlen(not_included[i])) == 0) {
5102 if (fputs(buf, fdest) == -1)
5105 if (!prefs_common.redirect_keep_from) {
5106 if (g_ascii_strncasecmp(buf, "From:",
5107 strlen("From:")) == 0) {
5108 err |= (fputs(" (by way of ", fdest) == EOF);
5109 if (compose->account->name
5110 && *compose->account->name) {
5111 compose_convert_header
5112 (compose, buf, sizeof(buf),
5113 compose->account->name,
5116 err |= (fprintf(fdest, "%s <%s>",
5118 compose->account->address) < 0);
5120 err |= (fprintf(fdest, "%s",
5121 compose->account->address) < 0);
5122 err |= (fputs(")", fdest) == EOF);
5126 if (fputs("\n", fdest) == -1)
5133 if (compose_redirect_write_headers(compose, fdest))
5136 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5137 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5150 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5152 GtkTextBuffer *buffer;
5153 GtkTextIter start, end;
5156 const gchar *out_codeset;
5157 EncodingType encoding = ENC_UNKNOWN;
5158 MimeInfo *mimemsg, *mimetext;
5160 const gchar *src_codeset = CS_INTERNAL;
5161 gchar *from_addr = NULL;
5162 gchar *from_name = NULL;
5164 if (action == COMPOSE_WRITE_FOR_SEND)
5165 attach_parts = TRUE;
5167 /* create message MimeInfo */
5168 mimemsg = procmime_mimeinfo_new();
5169 mimemsg->type = MIMETYPE_MESSAGE;
5170 mimemsg->subtype = g_strdup("rfc822");
5171 mimemsg->content = MIMECONTENT_MEM;
5172 mimemsg->tmp = TRUE; /* must free content later */
5173 mimemsg->data.mem = compose_get_header(compose);
5175 /* Create text part MimeInfo */
5176 /* get all composed text */
5177 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5178 gtk_text_buffer_get_start_iter(buffer, &start);
5179 gtk_text_buffer_get_end_iter(buffer, &end);
5180 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5182 out_codeset = conv_get_charset_str(compose->out_encoding);
5184 if (!out_codeset && is_ascii_str(chars)) {
5185 out_codeset = CS_US_ASCII;
5186 } else if (prefs_common.outgoing_fallback_to_ascii &&
5187 is_ascii_str(chars)) {
5188 out_codeset = CS_US_ASCII;
5189 encoding = ENC_7BIT;
5193 gchar *test_conv_global_out = NULL;
5194 gchar *test_conv_reply = NULL;
5196 /* automatic mode. be automatic. */
5197 codeconv_set_strict(TRUE);
5199 out_codeset = conv_get_outgoing_charset_str();
5201 debug_print("trying to convert to %s\n", out_codeset);
5202 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5205 if (!test_conv_global_out && compose->orig_charset
5206 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5207 out_codeset = compose->orig_charset;
5208 debug_print("failure; trying to convert to %s\n", out_codeset);
5209 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5212 if (!test_conv_global_out && !test_conv_reply) {
5214 out_codeset = CS_INTERNAL;
5215 debug_print("failure; finally using %s\n", out_codeset);
5217 g_free(test_conv_global_out);
5218 g_free(test_conv_reply);
5219 codeconv_set_strict(FALSE);
5222 if (encoding == ENC_UNKNOWN) {
5223 if (prefs_common.encoding_method == CTE_BASE64)
5224 encoding = ENC_BASE64;
5225 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5226 encoding = ENC_QUOTED_PRINTABLE;
5227 else if (prefs_common.encoding_method == CTE_8BIT)
5228 encoding = ENC_8BIT;
5230 encoding = procmime_get_encoding_for_charset(out_codeset);
5233 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5234 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5236 if (action == COMPOSE_WRITE_FOR_SEND) {
5237 codeconv_set_strict(TRUE);
5238 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5239 codeconv_set_strict(FALSE);
5245 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5246 "to the specified %s charset.\n"
5247 "Send it as %s?"), out_codeset, src_codeset);
5248 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5249 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5252 if (aval != G_ALERTALTERNATE) {
5257 out_codeset = src_codeset;
5263 out_codeset = src_codeset;
5268 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5269 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5270 strstr(buf, "\nFrom ") != NULL) {
5271 encoding = ENC_QUOTED_PRINTABLE;
5275 mimetext = procmime_mimeinfo_new();
5276 mimetext->content = MIMECONTENT_MEM;
5277 mimetext->tmp = TRUE; /* must free content later */
5278 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5279 * and free the data, which we need later. */
5280 mimetext->data.mem = g_strdup(buf);
5281 mimetext->type = MIMETYPE_TEXT;
5282 mimetext->subtype = g_strdup("plain");
5283 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5284 g_strdup(out_codeset));
5286 /* protect trailing spaces when signing message */
5287 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5288 privacy_system_can_sign(compose->privacy_system)) {
5289 encoding = ENC_QUOTED_PRINTABLE;
5292 debug_print("main text: %zd bytes encoded as %s in %d\n",
5293 strlen(buf), out_codeset, encoding);
5295 /* check for line length limit */
5296 if (action == COMPOSE_WRITE_FOR_SEND &&
5297 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5298 check_line_length(buf, 1000, &line) < 0) {
5302 msg = g_strdup_printf
5303 (_("Line %d exceeds the line length limit (998 bytes).\n"
5304 "The contents of the message might be broken on the way to the delivery.\n"
5306 "Send it anyway?"), line + 1);
5307 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5309 if (aval != G_ALERTALTERNATE) {
5315 if (encoding != ENC_UNKNOWN)
5316 procmime_encode_content(mimetext, encoding);
5318 /* append attachment parts */
5319 if (compose_use_attach(compose) && attach_parts) {
5320 MimeInfo *mimempart;
5321 gchar *boundary = NULL;
5322 mimempart = procmime_mimeinfo_new();
5323 mimempart->content = MIMECONTENT_EMPTY;
5324 mimempart->type = MIMETYPE_MULTIPART;
5325 mimempart->subtype = g_strdup("mixed");
5329 boundary = generate_mime_boundary(NULL);
5330 } while (strstr(buf, boundary) != NULL);
5332 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5335 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5337 g_node_append(mimempart->node, mimetext->node);
5338 g_node_append(mimemsg->node, mimempart->node);
5340 compose_add_attachments(compose, mimempart);
5342 g_node_append(mimemsg->node, mimetext->node);
5346 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5347 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5348 /* extract name and address */
5349 if (strstr(spec, " <") && strstr(spec, ">")) {
5350 from_addr = g_strdup(strrchr(spec, '<')+1);
5351 *(strrchr(from_addr, '>')) = '\0';
5352 from_name = g_strdup(spec);
5353 *(strrchr(from_name, '<')) = '\0';
5360 /* sign message if sending */
5361 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5362 privacy_system_can_sign(compose->privacy_system))
5363 if (!privacy_sign(compose->privacy_system, mimemsg,
5364 compose->account, from_addr)) {
5371 procmime_write_mimeinfo(mimemsg, fp);
5373 procmime_mimeinfo_free_all(mimemsg);
5378 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5380 GtkTextBuffer *buffer;
5381 GtkTextIter start, end;
5386 if ((fp = g_fopen(file, "wb")) == NULL) {
5387 FILE_OP_ERROR(file, "fopen");
5391 /* chmod for security */
5392 if (change_file_mode_rw(fp, file) < 0) {
5393 FILE_OP_ERROR(file, "chmod");
5394 g_warning("can't change file mode\n");
5397 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5398 gtk_text_buffer_get_start_iter(buffer, &start);
5399 gtk_text_buffer_get_end_iter(buffer, &end);
5400 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5402 chars = conv_codeset_strdup
5403 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5406 if (!chars) return -1;
5409 len = strlen(chars);
5410 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5411 FILE_OP_ERROR(file, "fwrite");
5420 if (fclose(fp) == EOF) {
5421 FILE_OP_ERROR(file, "fclose");
5428 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5431 MsgInfo *msginfo = compose->targetinfo;
5433 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5434 if (!msginfo) return -1;
5436 if (!force && MSG_IS_LOCKED(msginfo->flags))
5439 item = msginfo->folder;
5440 cm_return_val_if_fail(item != NULL, -1);
5442 if (procmsg_msg_exist(msginfo) &&
5443 (folder_has_parent_of_type(item, F_QUEUE) ||
5444 folder_has_parent_of_type(item, F_DRAFT)
5445 || msginfo == compose->autosaved_draft)) {
5446 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5447 g_warning("can't remove the old message\n");
5450 debug_print("removed reedit target %d\n", msginfo->msgnum);
5457 static void compose_remove_draft(Compose *compose)
5460 MsgInfo *msginfo = compose->targetinfo;
5461 drafts = account_get_special_folder(compose->account, F_DRAFT);
5463 if (procmsg_msg_exist(msginfo)) {
5464 folder_item_remove_msg(drafts, msginfo->msgnum);
5469 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5470 gboolean remove_reedit_target)
5472 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5475 static gboolean compose_warn_encryption(Compose *compose)
5477 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5478 AlertValue val = G_ALERTALTERNATE;
5480 if (warning == NULL)
5483 val = alertpanel_full(_("Encryption warning"), warning,
5484 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5485 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5486 if (val & G_ALERTDISABLE) {
5487 val &= ~G_ALERTDISABLE;
5488 if (val == G_ALERTALTERNATE)
5489 privacy_inhibit_encrypt_warning(compose->privacy_system,
5493 if (val == G_ALERTALTERNATE) {
5500 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5501 gchar **msgpath, gboolean check_subject,
5502 gboolean remove_reedit_target)
5509 static gboolean lock = FALSE;
5510 PrefsAccount *mailac = NULL, *newsac = NULL;
5511 gboolean err = FALSE;
5513 debug_print("queueing message...\n");
5514 cm_return_val_if_fail(compose->account != NULL, -1);
5518 if (compose_check_entries(compose, check_subject) == FALSE) {
5520 if (compose->batch) {
5521 gtk_widget_show_all(compose->window);
5526 if (!compose->to_list && !compose->newsgroup_list) {
5527 g_warning("can't get recipient list.");
5532 if (compose->to_list) {
5533 if (compose->account->protocol != A_NNTP)
5534 mailac = compose->account;
5535 else if (cur_account && cur_account->protocol != A_NNTP)
5536 mailac = cur_account;
5537 else if (!(mailac = compose_current_mail_account())) {
5539 alertpanel_error(_("No account for sending mails available!"));
5544 if (compose->newsgroup_list) {
5545 if (compose->account->protocol == A_NNTP)
5546 newsac = compose->account;
5549 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5554 /* write queue header */
5555 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5556 G_DIR_SEPARATOR, compose, (guint) rand());
5557 debug_print("queuing to %s\n", tmp);
5558 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5559 FILE_OP_ERROR(tmp, "fopen");
5565 if (change_file_mode_rw(fp, tmp) < 0) {
5566 FILE_OP_ERROR(tmp, "chmod");
5567 g_warning("can't change file mode\n");
5570 /* queueing variables */
5571 err |= (fprintf(fp, "AF:\n") < 0);
5572 err |= (fprintf(fp, "NF:0\n") < 0);
5573 err |= (fprintf(fp, "PS:10\n") < 0);
5574 err |= (fprintf(fp, "SRH:1\n") < 0);
5575 err |= (fprintf(fp, "SFN:\n") < 0);
5576 err |= (fprintf(fp, "DSR:\n") < 0);
5578 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5580 err |= (fprintf(fp, "MID:\n") < 0);
5581 err |= (fprintf(fp, "CFG:\n") < 0);
5582 err |= (fprintf(fp, "PT:0\n") < 0);
5583 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5584 err |= (fprintf(fp, "RQ:\n") < 0);
5586 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5588 err |= (fprintf(fp, "SSV:\n") < 0);
5590 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5592 err |= (fprintf(fp, "NSV:\n") < 0);
5593 err |= (fprintf(fp, "SSH:\n") < 0);
5594 /* write recepient list */
5595 if (compose->to_list) {
5596 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5597 for (cur = compose->to_list->next; cur != NULL;
5599 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5600 err |= (fprintf(fp, "\n") < 0);
5602 /* write newsgroup list */
5603 if (compose->newsgroup_list) {
5604 err |= (fprintf(fp, "NG:") < 0);
5605 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5606 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5607 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5608 err |= (fprintf(fp, "\n") < 0);
5610 /* Sylpheed account IDs */
5612 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5614 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5617 if (compose->privacy_system != NULL) {
5618 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5619 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5620 if (compose->use_encryption) {
5622 if (!compose_warn_encryption(compose)) {
5629 if (mailac && mailac->encrypt_to_self) {
5630 GSList *tmp_list = g_slist_copy(compose->to_list);
5631 tmp_list = g_slist_append(tmp_list, compose->account->address);
5632 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5633 g_slist_free(tmp_list);
5635 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5637 if (encdata != NULL) {
5638 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5639 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5640 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5642 } /* else we finally dont want to encrypt */
5644 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5645 /* and if encdata was null, it means there's been a problem in
5657 /* Save copy folder */
5658 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5659 gchar *savefolderid;
5661 savefolderid = compose_get_save_to(compose);
5662 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5663 g_free(savefolderid);
5665 /* Save copy folder */
5666 if (compose->return_receipt) {
5667 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5669 /* Message-ID of message replying to */
5670 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5673 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5674 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5677 /* Message-ID of message forwarding to */
5678 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5681 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5682 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5686 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5687 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5689 /* end of headers */
5690 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5692 if (compose->redirect_filename != NULL) {
5693 if (compose_redirect_write_to_file(compose, fp) < 0) {
5702 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5707 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5711 g_warning("failed to write queue message\n");
5718 if (fclose(fp) == EOF) {
5719 FILE_OP_ERROR(tmp, "fclose");
5726 if (item && *item) {
5729 queue = account_get_special_folder(compose->account, F_QUEUE);
5732 g_warning("can't find queue folder\n");
5738 folder_item_scan(queue);
5739 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5740 g_warning("can't queue the message\n");
5747 if (msgpath == NULL) {
5753 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5754 compose_remove_reedit_target(compose, FALSE);
5757 if ((msgnum != NULL) && (item != NULL)) {
5765 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5768 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5770 struct stat statbuf;
5771 gchar *type, *subtype;
5772 GtkTreeModel *model;
5775 model = gtk_tree_view_get_model(tree_view);
5777 if (!gtk_tree_model_get_iter_first(model, &iter))
5780 gtk_tree_model_get(model, &iter,
5784 mimepart = procmime_mimeinfo_new();
5785 mimepart->content = MIMECONTENT_FILE;
5786 mimepart->data.filename = g_strdup(ainfo->file);
5787 mimepart->tmp = FALSE; /* or we destroy our attachment */
5788 mimepart->offset = 0;
5790 g_stat(ainfo->file, &statbuf);
5791 mimepart->length = statbuf.st_size;
5793 type = g_strdup(ainfo->content_type);
5795 if (!strchr(type, '/')) {
5797 type = g_strdup("application/octet-stream");
5800 subtype = strchr(type, '/') + 1;
5801 *(subtype - 1) = '\0';
5802 mimepart->type = procmime_get_media_type(type);
5803 mimepart->subtype = g_strdup(subtype);
5806 if (mimepart->type == MIMETYPE_MESSAGE &&
5807 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5808 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5811 if (mimepart->type == MIMETYPE_APPLICATION &&
5812 !strcmp2(mimepart->subtype, "octet-stream"))
5813 g_hash_table_insert(mimepart->typeparameters,
5814 g_strdup("name"), g_strdup(ainfo->name));
5815 g_hash_table_insert(mimepart->dispositionparameters,
5816 g_strdup("filename"), g_strdup(ainfo->name));
5817 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5821 if (compose->use_signing) {
5822 if (ainfo->encoding == ENC_7BIT)
5823 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5824 else if (ainfo->encoding == ENC_8BIT)
5825 ainfo->encoding = ENC_BASE64;
5828 procmime_encode_content(mimepart, ainfo->encoding);
5830 g_node_append(parent->node, mimepart->node);
5831 } while (gtk_tree_model_iter_next(model, &iter));
5834 #define IS_IN_CUSTOM_HEADER(header) \
5835 (compose->account->add_customhdr && \
5836 custom_header_find(compose->account->customhdr_list, header) != NULL)
5838 static void compose_add_headerfield_from_headerlist(Compose *compose,
5840 const gchar *fieldname,
5841 const gchar *seperator)
5843 gchar *str, *fieldname_w_colon;
5844 gboolean add_field = FALSE;
5846 ComposeHeaderEntry *headerentry;
5847 const gchar *headerentryname;
5848 const gchar *trans_fieldname;
5851 if (IS_IN_CUSTOM_HEADER(fieldname))
5854 debug_print("Adding %s-fields\n", fieldname);
5856 fieldstr = g_string_sized_new(64);
5858 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5859 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5861 for (list = compose->header_list; list; list = list->next) {
5862 headerentry = ((ComposeHeaderEntry *)list->data);
5863 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5865 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5866 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5868 if (str[0] != '\0') {
5870 g_string_append(fieldstr, seperator);
5871 g_string_append(fieldstr, str);
5880 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5881 compose_convert_header
5882 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5883 strlen(fieldname) + 2, TRUE);
5884 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5888 g_free(fieldname_w_colon);
5889 g_string_free(fieldstr, TRUE);
5894 static gchar *compose_get_header(Compose *compose)
5896 gchar buf[BUFFSIZE];
5897 const gchar *entry_str;
5901 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5903 gchar *from_name = NULL, *from_address = NULL;
5906 cm_return_val_if_fail(compose->account != NULL, NULL);
5907 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5909 header = g_string_sized_new(64);
5912 get_rfc822_date(buf, sizeof(buf));
5913 g_string_append_printf(header, "Date: %s\n", buf);
5917 if (compose->account->name && *compose->account->name) {
5919 QUOTE_IF_REQUIRED(buf, compose->account->name);
5920 tmp = g_strdup_printf("%s <%s>",
5921 buf, compose->account->address);
5923 tmp = g_strdup_printf("%s",
5924 compose->account->address);
5926 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5927 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5929 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5930 from_address = g_strdup(compose->account->address);
5932 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5933 /* extract name and address */
5934 if (strstr(spec, " <") && strstr(spec, ">")) {
5935 from_address = g_strdup(strrchr(spec, '<')+1);
5936 *(strrchr(from_address, '>')) = '\0';
5937 from_name = g_strdup(spec);
5938 *(strrchr(from_name, '<')) = '\0';
5941 from_address = g_strdup(spec);
5948 if (from_name && *from_name) {
5949 compose_convert_header
5950 (compose, buf, sizeof(buf), from_name,
5951 strlen("From: "), TRUE);
5952 QUOTE_IF_REQUIRED(name, buf);
5954 g_string_append_printf(header, "From: %s <%s>\n",
5955 name, from_address);
5957 g_string_append_printf(header, "From: %s\n", from_address);
5960 g_free(from_address);
5963 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5966 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5969 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5972 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5975 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5977 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5980 compose_convert_header(compose, buf, sizeof(buf), str,
5981 strlen("Subject: "), FALSE);
5982 g_string_append_printf(header, "Subject: %s\n", buf);
5988 if (compose->account->set_domain && compose->account->domain) {
5989 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5990 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5991 g_snprintf(buf, sizeof(buf), "%s",
5992 strchr(compose->account->address, '@') ?
5993 strchr(compose->account->address, '@')+1 :
5994 compose->account->address);
5996 g_snprintf(buf, sizeof(buf), "%s", "");
5999 if (compose->account->gen_msgid) {
6000 generate_msgid(buf, sizeof(buf));
6001 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6002 compose->msgid = g_strdup(buf);
6004 compose->msgid = NULL;
6007 if (compose->remove_references == FALSE) {
6009 if (compose->inreplyto && compose->to_list)
6010 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6013 if (compose->references)
6014 g_string_append_printf(header, "References: %s\n", compose->references);
6018 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6021 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6024 if (compose->account->organization &&
6025 strlen(compose->account->organization) &&
6026 !IS_IN_CUSTOM_HEADER("Organization")) {
6027 compose_convert_header(compose, buf, sizeof(buf),
6028 compose->account->organization,
6029 strlen("Organization: "), FALSE);
6030 g_string_append_printf(header, "Organization: %s\n", buf);
6033 /* Program version and system info */
6034 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6035 !compose->newsgroup_list) {
6036 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6038 gtk_major_version, gtk_minor_version, gtk_micro_version,
6041 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6042 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6044 gtk_major_version, gtk_minor_version, gtk_micro_version,
6048 /* custom headers */
6049 if (compose->account->add_customhdr) {
6052 for (cur = compose->account->customhdr_list; cur != NULL;
6054 CustomHeader *chdr = (CustomHeader *)cur->data;
6056 if (custom_header_is_allowed(chdr->name)) {
6057 compose_convert_header
6058 (compose, buf, sizeof(buf),
6059 chdr->value ? chdr->value : "",
6060 strlen(chdr->name) + 2, FALSE);
6061 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6066 /* Automatic Faces and X-Faces */
6067 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6068 g_string_append_printf(header, "X-Face: %s\n", buf);
6070 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6071 g_string_append_printf(header, "X-Face: %s\n", buf);
6073 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6074 g_string_append_printf(header, "Face: %s\n", buf);
6076 else if (get_default_face (buf, sizeof(buf)) == 0) {
6077 g_string_append_printf(header, "Face: %s\n", buf);
6081 switch (compose->priority) {
6082 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6083 "X-Priority: 1 (Highest)\n");
6085 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6086 "X-Priority: 2 (High)\n");
6088 case PRIORITY_NORMAL: break;
6089 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6090 "X-Priority: 4 (Low)\n");
6092 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6093 "X-Priority: 5 (Lowest)\n");
6095 default: debug_print("compose: priority unknown : %d\n",
6099 /* Request Return Receipt */
6100 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6101 if (compose->return_receipt) {
6102 if (compose->account->name
6103 && *compose->account->name) {
6104 compose_convert_header(compose, buf, sizeof(buf),
6105 compose->account->name,
6106 strlen("Disposition-Notification-To: "),
6108 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6110 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6114 /* get special headers */
6115 for (list = compose->header_list; list; list = list->next) {
6116 ComposeHeaderEntry *headerentry;
6119 gchar *headername_wcolon;
6120 const gchar *headername_trans;
6123 gboolean standard_header = FALSE;
6125 headerentry = ((ComposeHeaderEntry *)list->data);
6127 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6129 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6134 if (!strstr(tmp, ":")) {
6135 headername_wcolon = g_strconcat(tmp, ":", NULL);
6136 headername = g_strdup(tmp);
6138 headername_wcolon = g_strdup(tmp);
6139 headername = g_strdup(strtok(tmp, ":"));
6143 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6144 Xstrdup_a(headervalue, entry_str, return NULL);
6145 subst_char(headervalue, '\r', ' ');
6146 subst_char(headervalue, '\n', ' ');
6147 string = std_headers;
6148 while (*string != NULL) {
6149 headername_trans = prefs_common_translated_header_name(*string);
6150 if (!strcmp(headername_trans, headername_wcolon))
6151 standard_header = TRUE;
6154 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6155 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6158 g_free(headername_wcolon);
6162 g_string_free(header, FALSE);
6167 #undef IS_IN_CUSTOM_HEADER
6169 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6170 gint header_len, gboolean addr_field)
6172 gchar *tmpstr = NULL;
6173 const gchar *out_codeset = NULL;
6175 cm_return_if_fail(src != NULL);
6176 cm_return_if_fail(dest != NULL);
6178 if (len < 1) return;
6180 tmpstr = g_strdup(src);
6182 subst_char(tmpstr, '\n', ' ');
6183 subst_char(tmpstr, '\r', ' ');
6186 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6187 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6188 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6193 codeconv_set_strict(TRUE);
6194 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6195 conv_get_charset_str(compose->out_encoding));
6196 codeconv_set_strict(FALSE);
6198 if (!dest || *dest == '\0') {
6199 gchar *test_conv_global_out = NULL;
6200 gchar *test_conv_reply = NULL;
6202 /* automatic mode. be automatic. */
6203 codeconv_set_strict(TRUE);
6205 out_codeset = conv_get_outgoing_charset_str();
6207 debug_print("trying to convert to %s\n", out_codeset);
6208 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6211 if (!test_conv_global_out && compose->orig_charset
6212 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6213 out_codeset = compose->orig_charset;
6214 debug_print("failure; trying to convert to %s\n", out_codeset);
6215 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6218 if (!test_conv_global_out && !test_conv_reply) {
6220 out_codeset = CS_INTERNAL;
6221 debug_print("finally using %s\n", out_codeset);
6223 g_free(test_conv_global_out);
6224 g_free(test_conv_reply);
6225 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6227 codeconv_set_strict(FALSE);
6232 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6236 cm_return_if_fail(user_data != NULL);
6238 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6239 g_strstrip(address);
6240 if (*address != '\0') {
6241 gchar *name = procheader_get_fromname(address);
6242 extract_address(address);
6243 addressbook_add_contact(name, address, NULL, NULL);
6248 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6250 GtkWidget *menuitem;
6253 cm_return_if_fail(menu != NULL);
6254 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6256 menuitem = gtk_separator_menu_item_new();
6257 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6258 gtk_widget_show(menuitem);
6260 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6261 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6263 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6264 g_strstrip(address);
6265 if (*address == '\0') {
6266 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6269 g_signal_connect(G_OBJECT(menuitem), "activate",
6270 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6271 gtk_widget_show(menuitem);
6274 static void compose_create_header_entry(Compose *compose)
6276 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6281 const gchar *header = NULL;
6282 ComposeHeaderEntry *headerentry;
6283 gboolean standard_header = FALSE;
6284 #if !(GTK_CHECK_VERSION(2,12,0))
6285 GtkTooltips *tips = compose->tooltips;
6288 headerentry = g_new0(ComposeHeaderEntry, 1);
6291 combo = gtk_combo_box_entry_new_text();
6293 while(*string != NULL) {
6294 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6295 (gchar*)prefs_common_translated_header_name(*string));
6298 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6299 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6300 G_CALLBACK(compose_grab_focus_cb), compose);
6301 gtk_widget_show(combo);
6302 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6303 compose->header_nextrow, compose->header_nextrow+1,
6304 GTK_SHRINK, GTK_FILL, 0, 0);
6305 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6306 const gchar *last_header_entry = gtk_entry_get_text(
6307 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6309 while (*string != NULL) {
6310 if (!strcmp(*string, last_header_entry))
6311 standard_header = TRUE;
6314 if (standard_header)
6315 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6317 if (!compose->header_last || !standard_header) {
6318 switch(compose->account->protocol) {
6320 header = prefs_common_translated_header_name("Newsgroups:");
6323 header = prefs_common_translated_header_name("To:");
6328 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6330 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6331 G_CALLBACK(compose_grab_focus_cb), compose);
6334 entry = gtk_entry_new();
6335 gtk_widget_show(entry);
6336 CLAWS_SET_TIP(entry,
6337 _("Use <tab> to autocomplete from addressbook"));
6338 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6339 compose->header_nextrow, compose->header_nextrow+1,
6340 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6342 g_signal_connect(G_OBJECT(entry), "key-press-event",
6343 G_CALLBACK(compose_headerentry_key_press_event_cb),
6345 g_signal_connect(G_OBJECT(entry), "changed",
6346 G_CALLBACK(compose_headerentry_changed_cb),
6348 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6349 G_CALLBACK(compose_grab_focus_cb), compose);
6352 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6353 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6354 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6355 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6356 G_CALLBACK(compose_header_drag_received_cb),
6358 g_signal_connect(G_OBJECT(entry), "drag-drop",
6359 G_CALLBACK(compose_drag_drop),
6361 g_signal_connect(G_OBJECT(entry), "populate-popup",
6362 G_CALLBACK(compose_entry_popup_extend),
6365 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6367 headerentry->compose = compose;
6368 headerentry->combo = combo;
6369 headerentry->entry = entry;
6370 headerentry->headernum = compose->header_nextrow;
6372 compose->header_nextrow++;
6373 compose->header_last = headerentry;
6374 compose->header_list =
6375 g_slist_append(compose->header_list,
6379 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6381 ComposeHeaderEntry *last_header;
6383 last_header = compose->header_last;
6385 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6386 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6389 static void compose_remove_header_entries(Compose *compose)
6392 for (list = compose->header_list; list; list = list->next) {
6393 ComposeHeaderEntry *headerentry =
6394 (ComposeHeaderEntry *)list->data;
6395 gtk_widget_destroy(headerentry->combo);
6396 gtk_widget_destroy(headerentry->entry);
6397 g_free(headerentry);
6399 compose->header_last = NULL;
6400 g_slist_free(compose->header_list);
6401 compose->header_list = NULL;
6402 compose->header_nextrow = 1;
6403 compose_create_header_entry(compose);
6406 static GtkWidget *compose_create_header(Compose *compose)
6408 GtkWidget *from_optmenu_hbox;
6409 GtkWidget *header_scrolledwin;
6410 GtkWidget *header_table;
6414 /* header labels and entries */
6415 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6416 gtk_widget_show(header_scrolledwin);
6417 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6419 header_table = gtk_table_new(2, 2, FALSE);
6420 gtk_widget_show(header_table);
6421 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6422 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6423 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6426 /* option menu for selecting accounts */
6427 from_optmenu_hbox = compose_account_option_menu_create(compose);
6428 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6429 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6432 compose->header_table = header_table;
6433 compose->header_list = NULL;
6434 compose->header_nextrow = count;
6436 compose_create_header_entry(compose);
6438 compose->table = NULL;
6440 return header_scrolledwin ;
6443 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6445 Compose *compose = (Compose *)data;
6446 GdkEventButton event;
6449 event.time = gtk_get_current_event_time();
6451 return attach_button_pressed(compose->attach_clist, &event, compose);
6454 static GtkWidget *compose_create_attach(Compose *compose)
6456 GtkWidget *attach_scrwin;
6457 GtkWidget *attach_clist;
6459 GtkListStore *store;
6460 GtkCellRenderer *renderer;
6461 GtkTreeViewColumn *column;
6462 GtkTreeSelection *selection;
6464 /* attachment list */
6465 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6466 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6467 GTK_POLICY_AUTOMATIC,
6468 GTK_POLICY_AUTOMATIC);
6469 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6471 store = gtk_list_store_new(N_ATTACH_COLS,
6476 G_TYPE_AUTO_POINTER,
6478 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6479 (GTK_TREE_MODEL(store)));
6480 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6481 g_object_unref(store);
6483 renderer = gtk_cell_renderer_text_new();
6484 column = gtk_tree_view_column_new_with_attributes
6485 (_("Mime type"), renderer, "text",
6486 COL_MIMETYPE, NULL);
6487 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6489 renderer = gtk_cell_renderer_text_new();
6490 column = gtk_tree_view_column_new_with_attributes
6491 (_("Size"), renderer, "text",
6493 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6495 renderer = gtk_cell_renderer_text_new();
6496 column = gtk_tree_view_column_new_with_attributes
6497 (_("Name"), renderer, "text",
6499 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6501 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6502 prefs_common.use_stripes_everywhere);
6503 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6504 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6506 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6507 G_CALLBACK(attach_selected), compose);
6508 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6509 G_CALLBACK(attach_button_pressed), compose);
6511 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6512 G_CALLBACK(popup_attach_button_pressed), compose);
6514 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6515 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6516 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6517 G_CALLBACK(popup_attach_button_pressed), compose);
6519 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6520 G_CALLBACK(attach_key_pressed), compose);
6523 gtk_drag_dest_set(attach_clist,
6524 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6525 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6526 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6527 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6528 G_CALLBACK(compose_attach_drag_received_cb),
6530 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6531 G_CALLBACK(compose_drag_drop),
6534 compose->attach_scrwin = attach_scrwin;
6535 compose->attach_clist = attach_clist;
6537 return attach_scrwin;
6540 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6541 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6543 static GtkWidget *compose_create_others(Compose *compose)
6546 GtkWidget *savemsg_checkbtn;
6547 GtkWidget *savemsg_combo;
6548 GtkWidget *savemsg_select;
6551 gchar *folderidentifier;
6553 /* Table for settings */
6554 table = gtk_table_new(3, 1, FALSE);
6555 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6556 gtk_widget_show(table);
6557 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6560 /* Save Message to folder */
6561 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6562 gtk_widget_show(savemsg_checkbtn);
6563 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6564 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6565 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6567 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6568 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6570 savemsg_combo = gtk_combo_box_entry_new_text();
6571 compose->savemsg_checkbtn = savemsg_checkbtn;
6572 compose->savemsg_combo = savemsg_combo;
6573 gtk_widget_show(savemsg_combo);
6575 if (prefs_common.compose_save_to_history)
6576 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6577 prefs_common.compose_save_to_history);
6579 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6580 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6581 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6582 G_CALLBACK(compose_grab_focus_cb), compose);
6583 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6584 folderidentifier = folder_item_get_identifier(account_get_special_folder
6585 (compose->account, F_OUTBOX));
6586 compose_set_save_to(compose, folderidentifier);
6587 g_free(folderidentifier);
6590 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6591 gtk_widget_show(savemsg_select);
6592 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6593 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6594 G_CALLBACK(compose_savemsg_select_cb),
6602 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6604 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6605 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6608 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6613 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6616 path = folder_item_get_identifier(dest);
6618 compose_set_save_to(compose, path);
6622 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6623 GdkAtom clip, GtkTextIter *insert_place);
6626 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6630 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6632 if (event->button == 3) {
6634 GtkTextIter sel_start, sel_end;
6635 gboolean stuff_selected;
6637 /* move the cursor to allow GtkAspell to check the word
6638 * under the mouse */
6639 if (event->x && event->y) {
6640 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6641 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6643 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6646 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6647 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6650 stuff_selected = gtk_text_buffer_get_selection_bounds(
6652 &sel_start, &sel_end);
6654 gtk_text_buffer_place_cursor (buffer, &iter);
6655 /* reselect stuff */
6657 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6658 gtk_text_buffer_select_range(buffer,
6659 &sel_start, &sel_end);
6661 return FALSE; /* pass the event so that the right-click goes through */
6664 if (event->button == 2) {
6669 /* get the middle-click position to paste at the correct place */
6670 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6671 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6673 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6676 entry_paste_clipboard(compose, text,
6677 prefs_common.linewrap_pastes,
6678 GDK_SELECTION_PRIMARY, &iter);
6686 static void compose_spell_menu_changed(void *data)
6688 Compose *compose = (Compose *)data;
6690 GtkWidget *menuitem;
6691 GtkWidget *parent_item;
6692 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6695 if (compose->gtkaspell == NULL)
6698 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6699 "/Menu/Spelling/Options");
6701 /* setting the submenu removes /Spelling/Options from the factory
6702 * so we need to save it */
6704 if (parent_item == NULL) {
6705 parent_item = compose->aspell_options_menu;
6706 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6708 compose->aspell_options_menu = parent_item;
6710 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6712 spell_menu = g_slist_reverse(spell_menu);
6713 for (items = spell_menu;
6714 items; items = items->next) {
6715 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6716 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6717 gtk_widget_show(GTK_WIDGET(menuitem));
6719 g_slist_free(spell_menu);
6721 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6722 gtk_widget_show(parent_item);
6725 static void compose_dict_changed(void *data)
6727 Compose *compose = (Compose *) data;
6729 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6732 gtkaspell_highlight_all(compose->gtkaspell);
6733 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6737 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6739 Compose *compose = (Compose *)data;
6740 GdkEventButton event;
6743 event.time = gtk_get_current_event_time();
6747 return text_clicked(compose->text, &event, compose);
6750 static gboolean compose_force_window_origin = TRUE;
6751 static Compose *compose_create(PrefsAccount *account,
6760 GtkWidget *handlebox;
6762 GtkWidget *notebook;
6764 GtkWidget *attach_hbox;
6765 GtkWidget *attach_lab1;
6766 GtkWidget *attach_lab2;
6771 GtkWidget *subject_hbox;
6772 GtkWidget *subject_frame;
6773 GtkWidget *subject_entry;
6777 GtkWidget *edit_vbox;
6778 GtkWidget *ruler_hbox;
6780 GtkWidget *scrolledwin;
6782 GtkTextBuffer *buffer;
6783 GtkClipboard *clipboard;
6786 UndoMain *undostruct;
6788 gchar *titles[N_ATTACH_COLS];
6789 GtkWidget *popupmenu;
6790 GtkWidget *tmpl_menu;
6791 GtkActionGroup *action_group = NULL;
6794 GtkAspell * gtkaspell = NULL;
6797 static GdkGeometry geometry;
6799 cm_return_val_if_fail(account != NULL, NULL);
6801 debug_print("Creating compose window...\n");
6802 compose = g_new0(Compose, 1);
6804 titles[COL_MIMETYPE] = _("MIME type");
6805 titles[COL_SIZE] = _("Size");
6806 titles[COL_NAME] = _("Name");
6808 compose->batch = batch;
6809 compose->account = account;
6810 compose->folder = folder;
6812 compose->mutex = g_mutex_new();
6813 compose->set_cursor_pos = -1;
6815 #if !(GTK_CHECK_VERSION(2,12,0))
6816 compose->tooltips = tips;
6819 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6821 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6822 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6824 if (!geometry.max_width) {
6825 geometry.max_width = gdk_screen_width();
6826 geometry.max_height = gdk_screen_height();
6829 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6830 &geometry, GDK_HINT_MAX_SIZE);
6831 if (!geometry.min_width) {
6832 geometry.min_width = 600;
6833 geometry.min_height = 440;
6835 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6836 &geometry, GDK_HINT_MIN_SIZE);
6838 #ifndef GENERIC_UMPC
6839 if (compose_force_window_origin)
6840 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6841 prefs_common.compose_y);
6843 g_signal_connect(G_OBJECT(window), "delete_event",
6844 G_CALLBACK(compose_delete_cb), compose);
6845 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6846 gtk_widget_realize(window);
6848 gtkut_widget_set_composer_icon(window);
6850 vbox = gtk_vbox_new(FALSE, 0);
6851 gtk_container_add(GTK_CONTAINER(window), vbox);
6853 compose->ui_manager = gtk_ui_manager_new();
6854 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6855 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6856 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6857 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6858 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6859 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6860 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6861 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6862 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6863 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6866 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6874 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6876 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6877 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6881 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6882 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6885 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6887 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6888 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6893 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6894 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6901 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6916 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6924 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6930 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6957 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6983 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)
6984 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)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6990 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)
6991 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)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6996 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)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6999 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7000 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)
7001 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7003 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7005 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7006 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)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7011 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7012 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)
7013 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)
7014 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7025 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)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7036 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7043 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7044 gtk_widget_show_all(menubar);
7046 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7048 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7050 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7053 if (prefs_common.toolbar_detachable) {
7054 handlebox = gtk_handle_box_new();
7056 handlebox = gtk_hbox_new(FALSE, 0);
7058 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7060 gtk_widget_realize(handlebox);
7062 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7065 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7069 vbox2 = gtk_vbox_new(FALSE, 2);
7070 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7071 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7074 notebook = gtk_notebook_new();
7075 gtk_widget_set_size_request(notebook, -1, 130);
7076 gtk_widget_show(notebook);
7078 /* header labels and entries */
7079 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7080 compose_create_header(compose),
7081 gtk_label_new_with_mnemonic(_("Hea_der")));
7082 /* attachment list */
7083 attach_hbox = gtk_hbox_new(FALSE, 0);
7084 gtk_widget_show(attach_hbox);
7086 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7087 gtk_widget_show(attach_lab1);
7088 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7090 attach_lab2 = gtk_label_new("");
7091 gtk_widget_show(attach_lab2);
7092 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7094 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7095 compose_create_attach(compose),
7098 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7099 compose_create_others(compose),
7100 gtk_label_new_with_mnemonic(_("Othe_rs")));
7103 subject_hbox = gtk_hbox_new(FALSE, 0);
7104 gtk_widget_show(subject_hbox);
7106 subject_frame = gtk_frame_new(NULL);
7107 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7108 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7109 gtk_widget_show(subject_frame);
7111 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7112 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7113 gtk_widget_show(subject);
7115 label = gtk_label_new(_("Subject:"));
7116 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7117 gtk_widget_show(label);
7120 subject_entry = claws_spell_entry_new();
7122 subject_entry = gtk_entry_new();
7124 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7125 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7126 G_CALLBACK(compose_grab_focus_cb), compose);
7127 gtk_widget_show(subject_entry);
7128 compose->subject_entry = subject_entry;
7129 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7131 edit_vbox = gtk_vbox_new(FALSE, 0);
7133 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7136 ruler_hbox = gtk_hbox_new(FALSE, 0);
7137 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7139 ruler = gtk_shruler_new();
7140 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7141 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7145 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7146 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7147 GTK_POLICY_AUTOMATIC,
7148 GTK_POLICY_AUTOMATIC);
7149 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7151 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7152 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7154 text = gtk_text_view_new();
7155 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7156 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7157 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7158 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7159 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7161 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7163 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7164 G_CALLBACK(compose_edit_size_alloc),
7166 g_signal_connect(G_OBJECT(buffer), "changed",
7167 G_CALLBACK(compose_changed_cb), compose);
7168 g_signal_connect(G_OBJECT(text), "grab_focus",
7169 G_CALLBACK(compose_grab_focus_cb), compose);
7170 g_signal_connect(G_OBJECT(buffer), "insert_text",
7171 G_CALLBACK(text_inserted), compose);
7172 g_signal_connect(G_OBJECT(text), "button_press_event",
7173 G_CALLBACK(text_clicked), compose);
7175 g_signal_connect(G_OBJECT(text), "popup-menu",
7176 G_CALLBACK(compose_popup_menu), compose);
7178 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7179 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7180 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7181 G_CALLBACK(compose_popup_menu), compose);
7183 g_signal_connect(G_OBJECT(subject_entry), "changed",
7184 G_CALLBACK(compose_changed_cb), compose);
7187 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7188 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7189 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7190 g_signal_connect(G_OBJECT(text), "drag_data_received",
7191 G_CALLBACK(compose_insert_drag_received_cb),
7193 g_signal_connect(G_OBJECT(text), "drag-drop",
7194 G_CALLBACK(compose_drag_drop),
7196 gtk_widget_show_all(vbox);
7198 /* pane between attach clist and text */
7199 paned = gtk_vpaned_new();
7200 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7202 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7203 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7205 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7207 gtk_paned_add1(GTK_PANED(paned), notebook);
7208 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7209 gtk_widget_show_all(paned);
7212 if (prefs_common.textfont) {
7213 PangoFontDescription *font_desc;
7215 font_desc = pango_font_description_from_string
7216 (prefs_common.textfont);
7218 gtk_widget_modify_font(text, font_desc);
7219 pango_font_description_free(font_desc);
7223 gtk_action_group_add_actions(action_group, compose_popup_entries,
7224 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7232 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7234 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7235 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7236 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7238 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7240 undostruct = undo_init(text);
7241 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7244 address_completion_start(window);
7246 compose->window = window;
7247 compose->vbox = vbox;
7248 compose->menubar = menubar;
7249 compose->handlebox = handlebox;
7251 compose->vbox2 = vbox2;
7253 compose->paned = paned;
7255 compose->attach_label = attach_lab2;
7257 compose->notebook = notebook;
7258 compose->edit_vbox = edit_vbox;
7259 compose->ruler_hbox = ruler_hbox;
7260 compose->ruler = ruler;
7261 compose->scrolledwin = scrolledwin;
7262 compose->text = text;
7264 compose->focused_editable = NULL;
7266 compose->popupmenu = popupmenu;
7268 compose->tmpl_menu = tmpl_menu;
7270 compose->mode = mode;
7271 compose->rmode = mode;
7273 compose->targetinfo = NULL;
7274 compose->replyinfo = NULL;
7275 compose->fwdinfo = NULL;
7277 compose->replyto = NULL;
7279 compose->bcc = NULL;
7280 compose->followup_to = NULL;
7282 compose->ml_post = NULL;
7284 compose->inreplyto = NULL;
7285 compose->references = NULL;
7286 compose->msgid = NULL;
7287 compose->boundary = NULL;
7289 compose->autowrap = prefs_common.autowrap;
7290 compose->autoindent = prefs_common.auto_indent;
7291 compose->use_signing = FALSE;
7292 compose->use_encryption = FALSE;
7293 compose->privacy_system = NULL;
7295 compose->modified = FALSE;
7297 compose->return_receipt = FALSE;
7299 compose->to_list = NULL;
7300 compose->newsgroup_list = NULL;
7302 compose->undostruct = undostruct;
7304 compose->sig_str = NULL;
7306 compose->exteditor_file = NULL;
7307 compose->exteditor_pid = -1;
7308 compose->exteditor_tag = -1;
7309 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7312 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7313 if (mode != COMPOSE_REDIRECT) {
7314 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7315 strcmp(prefs_common.dictionary, "")) {
7316 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7317 prefs_common.alt_dictionary,
7318 conv_get_locale_charset_str(),
7319 prefs_common.misspelled_col,
7320 prefs_common.check_while_typing,
7321 prefs_common.recheck_when_changing_dict,
7322 prefs_common.use_alternate,
7323 prefs_common.use_both_dicts,
7324 GTK_TEXT_VIEW(text),
7325 GTK_WINDOW(compose->window),
7326 compose_dict_changed,
7327 compose_spell_menu_changed,
7330 alertpanel_error(_("Spell checker could not "
7332 gtkaspell_checkers_strerror());
7333 gtkaspell_checkers_reset_error();
7335 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7339 compose->gtkaspell = gtkaspell;
7340 compose_spell_menu_changed(compose);
7341 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7344 compose_select_account(compose, account, TRUE);
7346 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7347 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7349 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7350 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7352 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7353 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7355 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7356 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7358 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7359 if (account->protocol != A_NNTP)
7360 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7361 prefs_common_translated_header_name("To:"));
7363 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7364 prefs_common_translated_header_name("Newsgroups:"));
7366 addressbook_set_target_compose(compose);
7368 if (mode != COMPOSE_REDIRECT)
7369 compose_set_template_menu(compose);
7371 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7374 compose_list = g_list_append(compose_list, compose);
7376 if (!prefs_common.show_ruler)
7377 gtk_widget_hide(ruler_hbox);
7379 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7382 compose->priority = PRIORITY_NORMAL;
7383 compose_update_priority_menu_item(compose);
7385 compose_set_out_encoding(compose);
7388 compose_update_actions_menu(compose);
7390 /* Privacy Systems menu */
7391 compose_update_privacy_systems_menu(compose);
7393 activate_privacy_system(compose, account, TRUE);
7394 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7396 gtk_widget_realize(window);
7398 gtk_widget_show(window);
7400 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7401 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7408 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7413 GtkWidget *optmenubox;
7416 GtkWidget *from_name = NULL;
7417 #if !(GTK_CHECK_VERSION(2,12,0))
7418 GtkTooltips *tips = compose->tooltips;
7421 gint num = 0, def_menu = 0;
7423 accounts = account_get_list();
7424 cm_return_val_if_fail(accounts != NULL, NULL);
7426 optmenubox = gtk_event_box_new();
7427 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7428 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7430 hbox = gtk_hbox_new(FALSE, 6);
7431 from_name = gtk_entry_new();
7433 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7434 G_CALLBACK(compose_grab_focus_cb), compose);
7436 for (; accounts != NULL; accounts = accounts->next, num++) {
7437 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7438 gchar *name, *from = NULL;
7440 if (ac == compose->account) def_menu = num;
7442 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7445 if (ac == compose->account) {
7446 if (ac->name && *ac->name) {
7448 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7449 from = g_strdup_printf("%s <%s>",
7451 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7453 from = g_strdup_printf("%s",
7455 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7458 COMBOBOX_ADD(menu, name, ac->account_id);
7463 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7465 g_signal_connect(G_OBJECT(optmenu), "changed",
7466 G_CALLBACK(account_activated),
7468 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7469 G_CALLBACK(compose_entry_popup_extend),
7472 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7473 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7475 CLAWS_SET_TIP(optmenubox,
7476 _("Account to use for this email"));
7477 CLAWS_SET_TIP(from_name,
7478 _("Sender address to be used"));
7480 compose->from_name = from_name;
7485 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7487 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7488 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7489 Compose *compose = (Compose *) data;
7491 compose->priority = value;
7495 static void compose_reply_change_mode(Compose *compose,
7498 gboolean was_modified = compose->modified;
7500 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7502 cm_return_if_fail(compose->replyinfo != NULL);
7504 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7506 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7508 if (action == COMPOSE_REPLY_TO_ALL)
7510 if (action == COMPOSE_REPLY_TO_SENDER)
7512 if (action == COMPOSE_REPLY_TO_LIST)
7515 compose_remove_header_entries(compose);
7516 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7517 if (compose->account->set_autocc && compose->account->auto_cc)
7518 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7520 if (compose->account->set_autobcc && compose->account->auto_bcc)
7521 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7523 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7524 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7525 compose_show_first_last_header(compose, TRUE);
7526 compose->modified = was_modified;
7527 compose_set_title(compose);
7530 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7532 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7533 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7534 Compose *compose = (Compose *) data;
7537 compose_reply_change_mode(compose, value);
7540 static void compose_update_priority_menu_item(Compose * compose)
7542 GtkWidget *menuitem = NULL;
7543 switch (compose->priority) {
7544 case PRIORITY_HIGHEST:
7545 menuitem = gtk_ui_manager_get_widget
7546 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7549 menuitem = gtk_ui_manager_get_widget
7550 (compose->ui_manager, "/Menu/Options/Priority/High");
7552 case PRIORITY_NORMAL:
7553 menuitem = gtk_ui_manager_get_widget
7554 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7557 menuitem = gtk_ui_manager_get_widget
7558 (compose->ui_manager, "/Menu/Options/Priority/Low");
7560 case PRIORITY_LOWEST:
7561 menuitem = gtk_ui_manager_get_widget
7562 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7565 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7568 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7570 Compose *compose = (Compose *) data;
7572 gboolean can_sign = FALSE, can_encrypt = FALSE;
7574 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7576 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7579 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7580 g_free(compose->privacy_system);
7581 compose->privacy_system = NULL;
7582 if (systemid != NULL) {
7583 compose->privacy_system = g_strdup(systemid);
7585 can_sign = privacy_system_can_sign(systemid);
7586 can_encrypt = privacy_system_can_encrypt(systemid);
7589 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7592 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7595 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7597 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7598 GtkWidget *menuitem = NULL;
7600 gboolean can_sign = FALSE, can_encrypt = FALSE;
7601 gboolean found = FALSE;
7603 if (compose->privacy_system != NULL) {
7605 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7606 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7607 cm_return_if_fail(menuitem != NULL);
7609 amenu = GTK_MENU_SHELL(menuitem)->children;
7611 while (amenu != NULL) {
7612 GList *alist = amenu->next;
7614 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7615 if (systemid != NULL) {
7616 if (strcmp(systemid, compose->privacy_system) == 0 &&
7617 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7618 menuitem = GTK_WIDGET(amenu->data);
7620 can_sign = privacy_system_can_sign(systemid);
7621 can_encrypt = privacy_system_can_encrypt(systemid);
7625 } else if (strlen(compose->privacy_system) == 0 &&
7626 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7627 menuitem = GTK_WIDGET(amenu->data);
7630 can_encrypt = FALSE;
7637 if (menuitem != NULL)
7638 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7640 if (warn && !found && strlen(compose->privacy_system)) {
7641 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7642 "will not be able to sign or encrypt this message."),
7643 compose->privacy_system);
7647 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7648 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7651 static void compose_set_out_encoding(Compose *compose)
7653 CharSet out_encoding;
7654 const gchar *branch = NULL;
7655 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7657 switch(out_encoding) {
7658 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7659 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7660 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7661 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7662 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7663 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7664 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7665 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7666 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7667 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7668 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7669 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7670 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7671 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7672 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7673 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7674 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7675 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7676 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7677 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7678 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7679 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7680 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7681 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7682 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7683 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7684 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7685 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7686 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7687 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7688 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7689 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7691 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7694 static void compose_set_template_menu(Compose *compose)
7696 GSList *tmpl_list, *cur;
7700 tmpl_list = template_get_config();
7702 menu = gtk_menu_new();
7704 gtk_menu_set_accel_group (GTK_MENU (menu),
7705 gtk_ui_manager_get_accel_group(compose->ui_manager));
7706 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7707 Template *tmpl = (Template *)cur->data;
7708 gchar *accel_path = NULL;
7709 item = gtk_menu_item_new_with_label(tmpl->name);
7710 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7711 g_signal_connect(G_OBJECT(item), "activate",
7712 G_CALLBACK(compose_template_activate_cb),
7714 g_object_set_data(G_OBJECT(item), "template", tmpl);
7715 gtk_widget_show(item);
7716 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7717 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7721 gtk_widget_show(menu);
7722 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7725 void compose_update_actions_menu(Compose *compose)
7727 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7730 static void compose_update_privacy_systems_menu(Compose *compose)
7732 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7733 GSList *systems, *cur;
7735 GtkWidget *system_none;
7737 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7738 GtkWidget *privacy_menu = gtk_menu_new();
7740 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7741 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7743 g_signal_connect(G_OBJECT(system_none), "activate",
7744 G_CALLBACK(compose_set_privacy_system_cb), compose);
7746 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7747 gtk_widget_show(system_none);
7749 systems = privacy_get_system_ids();
7750 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7751 gchar *systemid = cur->data;
7753 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7754 widget = gtk_radio_menu_item_new_with_label(group,
7755 privacy_system_get_name(systemid));
7756 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7757 g_strdup(systemid), g_free);
7758 g_signal_connect(G_OBJECT(widget), "activate",
7759 G_CALLBACK(compose_set_privacy_system_cb), compose);
7761 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7762 gtk_widget_show(widget);
7765 g_slist_free(systems);
7766 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7767 gtk_widget_show_all(privacy_menu);
7768 gtk_widget_show_all(privacy_menuitem);
7771 void compose_reflect_prefs_all(void)
7776 for (cur = compose_list; cur != NULL; cur = cur->next) {
7777 compose = (Compose *)cur->data;
7778 compose_set_template_menu(compose);
7782 void compose_reflect_prefs_pixmap_theme(void)
7787 for (cur = compose_list; cur != NULL; cur = cur->next) {
7788 compose = (Compose *)cur->data;
7789 toolbar_update(TOOLBAR_COMPOSE, compose);
7793 static const gchar *compose_quote_char_from_context(Compose *compose)
7795 const gchar *qmark = NULL;
7797 cm_return_val_if_fail(compose != NULL, NULL);
7799 switch (compose->mode) {
7800 /* use forward-specific quote char */
7801 case COMPOSE_FORWARD:
7802 case COMPOSE_FORWARD_AS_ATTACH:
7803 case COMPOSE_FORWARD_INLINE:
7804 if (compose->folder && compose->folder->prefs &&
7805 compose->folder->prefs->forward_with_format)
7806 qmark = compose->folder->prefs->forward_quotemark;
7807 else if (compose->account->forward_with_format)
7808 qmark = compose->account->forward_quotemark;
7810 qmark = prefs_common.fw_quotemark;
7813 /* use reply-specific quote char in all other modes */
7815 if (compose->folder && compose->folder->prefs &&
7816 compose->folder->prefs->reply_with_format)
7817 qmark = compose->folder->prefs->reply_quotemark;
7818 else if (compose->account->reply_with_format)
7819 qmark = compose->account->reply_quotemark;
7821 qmark = prefs_common.quotemark;
7825 if (qmark == NULL || *qmark == '\0')
7831 static void compose_template_apply(Compose *compose, Template *tmpl,
7835 GtkTextBuffer *buffer;
7839 gchar *parsed_str = NULL;
7840 gint cursor_pos = 0;
7841 const gchar *err_msg = _("Template body format error at line %d.");
7844 /* process the body */
7846 text = GTK_TEXT_VIEW(compose->text);
7847 buffer = gtk_text_view_get_buffer(text);
7850 qmark = compose_quote_char_from_context(compose);
7852 if (compose->replyinfo != NULL) {
7855 gtk_text_buffer_set_text(buffer, "", -1);
7856 mark = gtk_text_buffer_get_insert(buffer);
7857 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7859 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7860 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7862 } else if (compose->fwdinfo != NULL) {
7865 gtk_text_buffer_set_text(buffer, "", -1);
7866 mark = gtk_text_buffer_get_insert(buffer);
7867 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7869 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7870 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7873 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7875 GtkTextIter start, end;
7878 gtk_text_buffer_get_start_iter(buffer, &start);
7879 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7880 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7882 /* clear the buffer now */
7884 gtk_text_buffer_set_text(buffer, "", -1);
7886 parsed_str = compose_quote_fmt(compose, dummyinfo,
7887 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7888 procmsg_msginfo_free( dummyinfo );
7894 gtk_text_buffer_set_text(buffer, "", -1);
7895 mark = gtk_text_buffer_get_insert(buffer);
7896 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7899 if (replace && parsed_str && compose->account->auto_sig)
7900 compose_insert_sig(compose, FALSE);
7902 if (replace && parsed_str) {
7903 gtk_text_buffer_get_start_iter(buffer, &iter);
7904 gtk_text_buffer_place_cursor(buffer, &iter);
7908 cursor_pos = quote_fmt_get_cursor_pos();
7909 compose->set_cursor_pos = cursor_pos;
7910 if (cursor_pos == -1)
7912 gtk_text_buffer_get_start_iter(buffer, &iter);
7913 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7914 gtk_text_buffer_place_cursor(buffer, &iter);
7917 /* process the other fields */
7919 compose_template_apply_fields(compose, tmpl);
7920 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7921 quote_fmt_reset_vartable();
7922 compose_changed_cb(NULL, compose);
7925 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7927 MsgInfo* dummyinfo = NULL;
7928 MsgInfo *msginfo = NULL;
7931 if (compose->replyinfo != NULL)
7932 msginfo = compose->replyinfo;
7933 else if (compose->fwdinfo != NULL)
7934 msginfo = compose->fwdinfo;
7936 dummyinfo = compose_msginfo_new_from_compose(compose);
7937 msginfo = dummyinfo;
7940 if (tmpl->from && *tmpl->from != '\0') {
7942 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7943 compose->gtkaspell);
7945 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7947 quote_fmt_scan_string(tmpl->from);
7950 buf = quote_fmt_get_buffer();
7952 alertpanel_error(_("Template From format error."));
7954 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7958 if (tmpl->to && *tmpl->to != '\0') {
7960 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7961 compose->gtkaspell);
7963 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7965 quote_fmt_scan_string(tmpl->to);
7968 buf = quote_fmt_get_buffer();
7970 alertpanel_error(_("Template To format error."));
7972 compose_entry_append(compose, buf, COMPOSE_TO);
7976 if (tmpl->cc && *tmpl->cc != '\0') {
7978 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7979 compose->gtkaspell);
7981 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7983 quote_fmt_scan_string(tmpl->cc);
7986 buf = quote_fmt_get_buffer();
7988 alertpanel_error(_("Template Cc format error."));
7990 compose_entry_append(compose, buf, COMPOSE_CC);
7994 if (tmpl->bcc && *tmpl->bcc != '\0') {
7996 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7997 compose->gtkaspell);
7999 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8001 quote_fmt_scan_string(tmpl->bcc);
8004 buf = quote_fmt_get_buffer();
8006 alertpanel_error(_("Template Bcc format error."));
8008 compose_entry_append(compose, buf, COMPOSE_BCC);
8012 /* process the subject */
8013 if (tmpl->subject && *tmpl->subject != '\0') {
8015 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8016 compose->gtkaspell);
8018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8020 quote_fmt_scan_string(tmpl->subject);
8023 buf = quote_fmt_get_buffer();
8025 alertpanel_error(_("Template subject format error."));
8027 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8031 procmsg_msginfo_free( dummyinfo );
8034 static void compose_destroy(Compose *compose)
8036 GtkTextBuffer *buffer;
8037 GtkClipboard *clipboard;
8039 compose_list = g_list_remove(compose_list, compose);
8041 if (compose->updating) {
8042 debug_print("danger, not destroying anything now\n");
8043 compose->deferred_destroy = TRUE;
8046 /* NOTE: address_completion_end() does nothing with the window
8047 * however this may change. */
8048 address_completion_end(compose->window);
8050 slist_free_strings(compose->to_list);
8051 g_slist_free(compose->to_list);
8052 slist_free_strings(compose->newsgroup_list);
8053 g_slist_free(compose->newsgroup_list);
8054 slist_free_strings(compose->header_list);
8055 g_slist_free(compose->header_list);
8057 procmsg_msginfo_free(compose->targetinfo);
8058 procmsg_msginfo_free(compose->replyinfo);
8059 procmsg_msginfo_free(compose->fwdinfo);
8061 g_free(compose->replyto);
8062 g_free(compose->cc);
8063 g_free(compose->bcc);
8064 g_free(compose->newsgroups);
8065 g_free(compose->followup_to);
8067 g_free(compose->ml_post);
8069 g_free(compose->inreplyto);
8070 g_free(compose->references);
8071 g_free(compose->msgid);
8072 g_free(compose->boundary);
8074 g_free(compose->redirect_filename);
8075 if (compose->undostruct)
8076 undo_destroy(compose->undostruct);
8078 g_free(compose->sig_str);
8080 g_free(compose->exteditor_file);
8082 g_free(compose->orig_charset);
8084 g_free(compose->privacy_system);
8086 if (addressbook_get_target_compose() == compose)
8087 addressbook_set_target_compose(NULL);
8090 if (compose->gtkaspell) {
8091 gtkaspell_delete(compose->gtkaspell);
8092 compose->gtkaspell = NULL;
8096 if (!compose->batch) {
8097 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8098 prefs_common.compose_height = compose->window->allocation.height;
8101 if (!gtk_widget_get_parent(compose->paned))
8102 gtk_widget_destroy(compose->paned);
8103 gtk_widget_destroy(compose->popupmenu);
8105 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8106 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8107 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8109 gtk_widget_destroy(compose->window);
8110 toolbar_destroy(compose->toolbar);
8111 g_free(compose->toolbar);
8112 g_mutex_free(compose->mutex);
8116 static void compose_attach_info_free(AttachInfo *ainfo)
8118 g_free(ainfo->file);
8119 g_free(ainfo->content_type);
8120 g_free(ainfo->name);
8124 static void compose_attach_update_label(Compose *compose)
8129 GtkTreeModel *model;
8134 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8135 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8136 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8140 while(gtk_tree_model_iter_next(model, &iter))
8143 text = g_strdup_printf("(%d)", i);
8144 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8148 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8150 Compose *compose = (Compose *)data;
8151 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8152 GtkTreeSelection *selection;
8154 GtkTreeModel *model;
8156 selection = gtk_tree_view_get_selection(tree_view);
8157 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8162 for (cur = sel; cur != NULL; cur = cur->next) {
8163 GtkTreePath *path = cur->data;
8164 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8167 gtk_tree_path_free(path);
8170 for (cur = sel; cur != NULL; cur = cur->next) {
8171 GtkTreeRowReference *ref = cur->data;
8172 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8175 if (gtk_tree_model_get_iter(model, &iter, path))
8176 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8178 gtk_tree_path_free(path);
8179 gtk_tree_row_reference_free(ref);
8183 compose_attach_update_label(compose);
8186 static struct _AttachProperty
8189 GtkWidget *mimetype_entry;
8190 GtkWidget *encoding_optmenu;
8191 GtkWidget *path_entry;
8192 GtkWidget *filename_entry;
8194 GtkWidget *cancel_btn;
8197 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8199 gtk_tree_path_free((GtkTreePath *)ptr);
8202 static void compose_attach_property(GtkAction *action, gpointer data)
8204 Compose *compose = (Compose *)data;
8205 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8207 GtkComboBox *optmenu;
8208 GtkTreeSelection *selection;
8210 GtkTreeModel *model;
8213 static gboolean cancelled;
8215 /* only if one selected */
8216 selection = gtk_tree_view_get_selection(tree_view);
8217 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8220 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8224 path = (GtkTreePath *) sel->data;
8225 gtk_tree_model_get_iter(model, &iter, path);
8226 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8229 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8235 if (!attach_prop.window)
8236 compose_attach_property_create(&cancelled);
8237 gtk_widget_grab_focus(attach_prop.ok_btn);
8238 gtk_widget_show(attach_prop.window);
8239 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8241 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8242 if (ainfo->encoding == ENC_UNKNOWN)
8243 combobox_select_by_data(optmenu, ENC_BASE64);
8245 combobox_select_by_data(optmenu, ainfo->encoding);
8247 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8248 ainfo->content_type ? ainfo->content_type : "");
8249 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8250 ainfo->file ? ainfo->file : "");
8251 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8252 ainfo->name ? ainfo->name : "");
8255 const gchar *entry_text;
8257 gchar *cnttype = NULL;
8264 gtk_widget_hide(attach_prop.window);
8269 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8270 if (*entry_text != '\0') {
8273 text = g_strstrip(g_strdup(entry_text));
8274 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8275 cnttype = g_strdup(text);
8278 alertpanel_error(_("Invalid MIME type."));
8284 ainfo->encoding = combobox_get_active_data(optmenu);
8286 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8287 if (*entry_text != '\0') {
8288 if (is_file_exist(entry_text) &&
8289 (size = get_file_size(entry_text)) > 0)
8290 file = g_strdup(entry_text);
8293 (_("File doesn't exist or is empty."));
8299 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8300 if (*entry_text != '\0') {
8301 g_free(ainfo->name);
8302 ainfo->name = g_strdup(entry_text);
8306 g_free(ainfo->content_type);
8307 ainfo->content_type = cnttype;
8310 g_free(ainfo->file);
8314 ainfo->size = (goffset)size;
8316 /* update tree store */
8317 text = to_human_readable(ainfo->size);
8318 gtk_tree_model_get_iter(model, &iter, path);
8319 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8320 COL_MIMETYPE, ainfo->content_type,
8322 COL_NAME, ainfo->name,
8328 gtk_tree_path_free(path);
8331 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8333 label = gtk_label_new(str); \
8334 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8335 GTK_FILL, 0, 0, 0); \
8336 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8338 entry = gtk_entry_new(); \
8339 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8340 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8343 static void compose_attach_property_create(gboolean *cancelled)
8349 GtkWidget *mimetype_entry;
8352 GtkListStore *optmenu_menu;
8353 GtkWidget *path_entry;
8354 GtkWidget *filename_entry;
8357 GtkWidget *cancel_btn;
8358 GList *mime_type_list, *strlist;
8361 debug_print("Creating attach_property window...\n");
8363 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8364 gtk_widget_set_size_request(window, 480, -1);
8365 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8366 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8367 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8368 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8369 g_signal_connect(G_OBJECT(window), "delete_event",
8370 G_CALLBACK(attach_property_delete_event),
8372 g_signal_connect(G_OBJECT(window), "key_press_event",
8373 G_CALLBACK(attach_property_key_pressed),
8376 vbox = gtk_vbox_new(FALSE, 8);
8377 gtk_container_add(GTK_CONTAINER(window), vbox);
8379 table = gtk_table_new(4, 2, FALSE);
8380 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8381 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8382 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8384 label = gtk_label_new(_("MIME type"));
8385 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8387 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8388 mimetype_entry = gtk_combo_box_entry_new_text();
8389 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8390 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8392 /* stuff with list */
8393 mime_type_list = procmime_get_mime_type_list();
8395 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8396 MimeType *type = (MimeType *) mime_type_list->data;
8399 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8401 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8404 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8405 (GCompareFunc)strcmp2);
8408 for (mime_type_list = strlist; mime_type_list != NULL;
8409 mime_type_list = mime_type_list->next) {
8410 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8411 g_free(mime_type_list->data);
8413 g_list_free(strlist);
8414 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8415 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8417 label = gtk_label_new(_("Encoding"));
8418 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8420 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8422 hbox = gtk_hbox_new(FALSE, 0);
8423 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8424 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8426 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8427 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8429 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8430 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8431 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8432 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8433 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8435 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8437 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8438 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8440 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8441 &ok_btn, GTK_STOCK_OK,
8443 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8444 gtk_widget_grab_default(ok_btn);
8446 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8447 G_CALLBACK(attach_property_ok),
8449 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8450 G_CALLBACK(attach_property_cancel),
8453 gtk_widget_show_all(vbox);
8455 attach_prop.window = window;
8456 attach_prop.mimetype_entry = mimetype_entry;
8457 attach_prop.encoding_optmenu = optmenu;
8458 attach_prop.path_entry = path_entry;
8459 attach_prop.filename_entry = filename_entry;
8460 attach_prop.ok_btn = ok_btn;
8461 attach_prop.cancel_btn = cancel_btn;
8464 #undef SET_LABEL_AND_ENTRY
8466 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8472 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8478 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8479 gboolean *cancelled)
8487 static gboolean attach_property_key_pressed(GtkWidget *widget,
8489 gboolean *cancelled)
8491 if (event && event->keyval == GDK_Escape) {
8495 if (event && event->keyval == GDK_Return) {
8502 static void compose_exec_ext_editor(Compose *compose)
8509 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8510 G_DIR_SEPARATOR, compose);
8512 if (pipe(pipe_fds) < 0) {
8518 if ((pid = fork()) < 0) {
8525 /* close the write side of the pipe */
8528 compose->exteditor_file = g_strdup(tmp);
8529 compose->exteditor_pid = pid;
8531 compose_set_ext_editor_sensitive(compose, FALSE);
8534 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8536 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8538 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8542 } else { /* process-monitoring process */
8548 /* close the read side of the pipe */
8551 if (compose_write_body_to_file(compose, tmp) < 0) {
8552 fd_write_all(pipe_fds[1], "2\n", 2);
8556 pid_ed = compose_exec_ext_editor_real(tmp);
8558 fd_write_all(pipe_fds[1], "1\n", 2);
8562 /* wait until editor is terminated */
8563 waitpid(pid_ed, NULL, 0);
8565 fd_write_all(pipe_fds[1], "0\n", 2);
8572 #endif /* G_OS_UNIX */
8576 static gint compose_exec_ext_editor_real(const gchar *file)
8583 cm_return_val_if_fail(file != NULL, -1);
8585 if ((pid = fork()) < 0) {
8590 if (pid != 0) return pid;
8592 /* grandchild process */
8594 if (setpgid(0, getppid()))
8597 if (prefs_common_get_ext_editor_cmd() &&
8598 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8599 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8600 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8602 if (prefs_common_get_ext_editor_cmd())
8603 g_warning("External editor command-line is invalid: '%s'\n",
8604 prefs_common_get_ext_editor_cmd());
8605 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8608 cmdline = strsplit_with_quote(buf, " ", 1024);
8609 execvp(cmdline[0], cmdline);
8612 g_strfreev(cmdline);
8617 static gboolean compose_ext_editor_kill(Compose *compose)
8619 pid_t pgid = compose->exteditor_pid * -1;
8622 ret = kill(pgid, 0);
8624 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8628 msg = g_strdup_printf
8629 (_("The external editor is still working.\n"
8630 "Force terminating the process?\n"
8631 "process group id: %d"), -pgid);
8632 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8633 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8637 if (val == G_ALERTALTERNATE) {
8638 g_source_remove(compose->exteditor_tag);
8639 g_io_channel_shutdown(compose->exteditor_ch,
8641 g_io_channel_unref(compose->exteditor_ch);
8643 if (kill(pgid, SIGTERM) < 0) perror("kill");
8644 waitpid(compose->exteditor_pid, NULL, 0);
8646 g_warning("Terminated process group id: %d", -pgid);
8647 g_warning("Temporary file: %s",
8648 compose->exteditor_file);
8650 compose_set_ext_editor_sensitive(compose, TRUE);
8652 g_free(compose->exteditor_file);
8653 compose->exteditor_file = NULL;
8654 compose->exteditor_pid = -1;
8655 compose->exteditor_ch = NULL;
8656 compose->exteditor_tag = -1;
8664 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8668 Compose *compose = (Compose *)data;
8671 debug_print(_("Compose: input from monitoring process\n"));
8673 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8675 g_io_channel_shutdown(source, FALSE, NULL);
8676 g_io_channel_unref(source);
8678 waitpid(compose->exteditor_pid, NULL, 0);
8680 if (buf[0] == '0') { /* success */
8681 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8682 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8684 gtk_text_buffer_set_text(buffer, "", -1);
8685 compose_insert_file(compose, compose->exteditor_file);
8686 compose_changed_cb(NULL, compose);
8687 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8689 if (claws_unlink(compose->exteditor_file) < 0)
8690 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8691 } else if (buf[0] == '1') { /* failed */
8692 g_warning("Couldn't exec external editor\n");
8693 if (claws_unlink(compose->exteditor_file) < 0)
8694 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8695 } else if (buf[0] == '2') {
8696 g_warning("Couldn't write to file\n");
8697 } else if (buf[0] == '3') {
8698 g_warning("Pipe read failed\n");
8701 compose_set_ext_editor_sensitive(compose, TRUE);
8703 g_free(compose->exteditor_file);
8704 compose->exteditor_file = NULL;
8705 compose->exteditor_pid = -1;
8706 compose->exteditor_ch = NULL;
8707 compose->exteditor_tag = -1;
8712 static void compose_set_ext_editor_sensitive(Compose *compose,
8715 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8716 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8717 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8718 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8719 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8720 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8721 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8723 gtk_widget_set_sensitive(compose->text, sensitive);
8724 if (compose->toolbar->send_btn)
8725 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8726 if (compose->toolbar->sendl_btn)
8727 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8728 if (compose->toolbar->draft_btn)
8729 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8730 if (compose->toolbar->insert_btn)
8731 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8732 if (compose->toolbar->sig_btn)
8733 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8734 if (compose->toolbar->exteditor_btn)
8735 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8736 if (compose->toolbar->linewrap_current_btn)
8737 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8738 if (compose->toolbar->linewrap_all_btn)
8739 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8741 #endif /* G_OS_UNIX */
8744 * compose_undo_state_changed:
8746 * Change the sensivity of the menuentries undo and redo
8748 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8749 gint redo_state, gpointer data)
8751 Compose *compose = (Compose *)data;
8753 switch (undo_state) {
8754 case UNDO_STATE_TRUE:
8755 if (!undostruct->undo_state) {
8756 undostruct->undo_state = TRUE;
8757 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8760 case UNDO_STATE_FALSE:
8761 if (undostruct->undo_state) {
8762 undostruct->undo_state = FALSE;
8763 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8766 case UNDO_STATE_UNCHANGED:
8768 case UNDO_STATE_REFRESH:
8769 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8772 g_warning("Undo state not recognized");
8776 switch (redo_state) {
8777 case UNDO_STATE_TRUE:
8778 if (!undostruct->redo_state) {
8779 undostruct->redo_state = TRUE;
8780 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8783 case UNDO_STATE_FALSE:
8784 if (undostruct->redo_state) {
8785 undostruct->redo_state = FALSE;
8786 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8789 case UNDO_STATE_UNCHANGED:
8791 case UNDO_STATE_REFRESH:
8792 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8795 g_warning("Redo state not recognized");
8800 /* callback functions */
8802 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8803 * includes "non-client" (windows-izm) in calculation, so this calculation
8804 * may not be accurate.
8806 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8807 GtkAllocation *allocation,
8808 GtkSHRuler *shruler)
8810 if (prefs_common.show_ruler) {
8811 gint char_width = 0, char_height = 0;
8812 gint line_width_in_chars;
8814 gtkut_get_font_size(GTK_WIDGET(widget),
8815 &char_width, &char_height);
8816 line_width_in_chars =
8817 (allocation->width - allocation->x) / char_width;
8819 /* got the maximum */
8820 gtk_ruler_set_range(GTK_RULER(shruler),
8821 0.0, line_width_in_chars, 0,
8822 /*line_width_in_chars*/ char_width);
8828 static void account_activated(GtkComboBox *optmenu, gpointer data)
8830 Compose *compose = (Compose *)data;
8833 gchar *folderidentifier;
8834 gint account_id = 0;
8838 /* Get ID of active account in the combo box */
8839 menu = gtk_combo_box_get_model(optmenu);
8840 gtk_combo_box_get_active_iter(optmenu, &iter);
8841 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8843 ac = account_find_from_id(account_id);
8844 cm_return_if_fail(ac != NULL);
8846 if (ac != compose->account)
8847 compose_select_account(compose, ac, FALSE);
8849 /* Set message save folder */
8850 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8851 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8853 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8854 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8856 compose_set_save_to(compose, NULL);
8857 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8858 folderidentifier = folder_item_get_identifier(account_get_special_folder
8859 (compose->account, F_OUTBOX));
8860 compose_set_save_to(compose, folderidentifier);
8861 g_free(folderidentifier);
8865 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8866 GtkTreeViewColumn *column, Compose *compose)
8868 compose_attach_property(NULL, compose);
8871 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8874 Compose *compose = (Compose *)data;
8875 GtkTreeSelection *attach_selection;
8876 gint attach_nr_selected;
8878 if (!event) return FALSE;
8880 if (event->button == 3) {
8881 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8882 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8884 if (attach_nr_selected > 0)
8886 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8887 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8889 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8890 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8893 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8894 NULL, NULL, event->button, event->time);
8901 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8904 Compose *compose = (Compose *)data;
8906 if (!event) return FALSE;
8908 switch (event->keyval) {
8910 compose_attach_remove_selected(NULL, compose);
8916 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8918 toolbar_comp_set_sensitive(compose, allow);
8919 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8920 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8922 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8924 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8925 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8926 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8928 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8932 static void compose_send_cb(GtkAction *action, gpointer data)
8934 Compose *compose = (Compose *)data;
8936 if (prefs_common.work_offline &&
8937 !inc_offline_should_override(TRUE,
8938 _("Claws Mail needs network access in order "
8939 "to send this email.")))
8942 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8943 g_source_remove(compose->draft_timeout_tag);
8944 compose->draft_timeout_tag = -1;
8947 compose_send(compose);
8950 static void compose_send_later_cb(GtkAction *action, gpointer data)
8952 Compose *compose = (Compose *)data;
8956 compose_allow_user_actions(compose, FALSE);
8957 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8958 compose_allow_user_actions(compose, TRUE);
8962 compose_close(compose);
8963 } else if (val == -1) {
8964 alertpanel_error(_("Could not queue message."));
8965 } else if (val == -2) {
8966 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8967 } else if (val == -3) {
8968 if (privacy_peek_error())
8969 alertpanel_error(_("Could not queue message for sending:\n\n"
8970 "Signature failed: %s"), privacy_get_error());
8971 } else if (val == -4) {
8972 alertpanel_error(_("Could not queue message for sending:\n\n"
8973 "Charset conversion failed."));
8974 } else if (val == -5) {
8975 alertpanel_error(_("Could not queue message for sending:\n\n"
8976 "Couldn't get recipient encryption key."));
8977 } else if (val == -6) {
8980 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8983 #define DRAFTED_AT_EXIT "drafted_at_exit"
8984 static void compose_register_draft(MsgInfo *info)
8986 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8987 DRAFTED_AT_EXIT, NULL);
8988 FILE *fp = g_fopen(filepath, "ab");
8991 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8999 gboolean compose_draft (gpointer data, guint action)
9001 Compose *compose = (Compose *)data;
9005 MsgFlags flag = {0, 0};
9006 static gboolean lock = FALSE;
9007 MsgInfo *newmsginfo;
9009 gboolean target_locked = FALSE;
9010 gboolean err = FALSE;
9012 if (lock) return FALSE;
9014 if (compose->sending)
9017 draft = account_get_special_folder(compose->account, F_DRAFT);
9018 cm_return_val_if_fail(draft != NULL, FALSE);
9020 if (!g_mutex_trylock(compose->mutex)) {
9021 /* we don't want to lock the mutex once it's available,
9022 * because as the only other part of compose.c locking
9023 * it is compose_close - which means once unlocked,
9024 * the compose struct will be freed */
9025 debug_print("couldn't lock mutex, probably sending\n");
9031 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9032 G_DIR_SEPARATOR, compose);
9033 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9034 FILE_OP_ERROR(tmp, "fopen");
9038 /* chmod for security */
9039 if (change_file_mode_rw(fp, tmp) < 0) {
9040 FILE_OP_ERROR(tmp, "chmod");
9041 g_warning("can't change file mode\n");
9044 /* Save draft infos */
9045 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9046 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9048 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9049 gchar *savefolderid;
9051 savefolderid = compose_get_save_to(compose);
9052 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9053 g_free(savefolderid);
9055 if (compose->return_receipt) {
9056 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9058 if (compose->privacy_system) {
9059 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9060 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9061 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9064 /* Message-ID of message replying to */
9065 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9068 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9069 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9072 /* Message-ID of message forwarding to */
9073 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9076 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9077 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9081 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9082 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9084 /* end of headers */
9085 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9092 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9096 if (fclose(fp) == EOF) {
9100 if (compose->targetinfo) {
9101 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9102 flag.perm_flags = target_locked?MSG_LOCKED:0;
9104 flag.tmp_flags = MSG_DRAFT;
9106 folder_item_scan(draft);
9107 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9108 MsgInfo *tmpinfo = NULL;
9109 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9110 if (compose->msgid) {
9111 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9114 msgnum = tmpinfo->msgnum;
9115 procmsg_msginfo_free(tmpinfo);
9116 debug_print("got draft msgnum %d from scanning\n", msgnum);
9118 debug_print("didn't get draft msgnum after scanning\n");
9121 debug_print("got draft msgnum %d from adding\n", msgnum);
9127 if (action != COMPOSE_AUTO_SAVE) {
9128 if (action != COMPOSE_DRAFT_FOR_EXIT)
9129 alertpanel_error(_("Could not save draft."));
9132 gtkut_window_popup(compose->window);
9133 val = alertpanel_full(_("Could not save draft"),
9134 _("Could not save draft.\n"
9135 "Do you want to cancel exit or discard this email?"),
9136 _("_Cancel exit"), _("_Discard email"), NULL,
9137 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9138 if (val == G_ALERTALTERNATE) {
9140 g_mutex_unlock(compose->mutex); /* must be done before closing */
9141 compose_close(compose);
9145 g_mutex_unlock(compose->mutex); /* must be done before closing */
9154 if (compose->mode == COMPOSE_REEDIT) {
9155 compose_remove_reedit_target(compose, TRUE);
9158 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9161 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9163 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9165 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9166 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9167 procmsg_msginfo_set_flags(newmsginfo, 0,
9168 MSG_HAS_ATTACHMENT);
9170 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9171 compose_register_draft(newmsginfo);
9173 procmsg_msginfo_free(newmsginfo);
9176 folder_item_scan(draft);
9178 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9180 g_mutex_unlock(compose->mutex); /* must be done before closing */
9181 compose_close(compose);
9187 path = folder_item_fetch_msg(draft, msgnum);
9189 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9192 if (g_stat(path, &s) < 0) {
9193 FILE_OP_ERROR(path, "stat");
9199 procmsg_msginfo_free(compose->targetinfo);
9200 compose->targetinfo = procmsg_msginfo_new();
9201 compose->targetinfo->msgnum = msgnum;
9202 compose->targetinfo->size = (goffset)s.st_size;
9203 compose->targetinfo->mtime = s.st_mtime;
9204 compose->targetinfo->folder = draft;
9206 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9207 compose->mode = COMPOSE_REEDIT;
9209 if (action == COMPOSE_AUTO_SAVE) {
9210 compose->autosaved_draft = compose->targetinfo;
9212 compose->modified = FALSE;
9213 compose_set_title(compose);
9217 g_mutex_unlock(compose->mutex);
9221 void compose_clear_exit_drafts(void)
9223 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9224 DRAFTED_AT_EXIT, NULL);
9225 if (is_file_exist(filepath))
9226 claws_unlink(filepath);
9231 void compose_reopen_exit_drafts(void)
9233 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9234 DRAFTED_AT_EXIT, NULL);
9235 FILE *fp = g_fopen(filepath, "rb");
9239 while (fgets(buf, sizeof(buf), fp)) {
9240 gchar **parts = g_strsplit(buf, "\t", 2);
9241 const gchar *folder = parts[0];
9242 int msgnum = parts[1] ? atoi(parts[1]):-1;
9244 if (folder && *folder && msgnum > -1) {
9245 FolderItem *item = folder_find_item_from_identifier(folder);
9246 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9248 compose_reedit(info, FALSE);
9255 compose_clear_exit_drafts();
9258 static void compose_save_cb(GtkAction *action, gpointer data)
9260 Compose *compose = (Compose *)data;
9261 compose_draft(compose, COMPOSE_KEEP_EDITING);
9262 compose->rmode = COMPOSE_REEDIT;
9265 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9267 if (compose && file_list) {
9270 for ( tmp = file_list; tmp; tmp = tmp->next) {
9271 gchar *file = (gchar *) tmp->data;
9272 gchar *utf8_filename = conv_filename_to_utf8(file);
9273 compose_attach_append(compose, file, utf8_filename, NULL);
9274 compose_changed_cb(NULL, compose);
9279 g_free(utf8_filename);
9284 static void compose_attach_cb(GtkAction *action, gpointer data)
9286 Compose *compose = (Compose *)data;
9289 if (compose->redirect_filename != NULL)
9292 file_list = filesel_select_multiple_files_open(_("Select file"));
9295 compose_attach_from_list(compose, file_list, TRUE);
9296 g_list_free(file_list);
9300 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9302 Compose *compose = (Compose *)data;
9305 file_list = filesel_select_multiple_files_open(_("Select file"));
9310 for ( tmp = file_list; tmp; tmp = tmp->next) {
9311 gchar *file = (gchar *) tmp->data;
9312 gchar *filedup = g_strdup(file);
9313 gchar *shortfile = g_path_get_basename(filedup);
9314 ComposeInsertResult res;
9316 res = compose_insert_file(compose, file);
9317 if (res == COMPOSE_INSERT_READ_ERROR) {
9318 alertpanel_error(_("File '%s' could not be read."), shortfile);
9319 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9320 alertpanel_error(_("File '%s' contained invalid characters\n"
9321 "for the current encoding, insertion may be incorrect."), shortfile);
9327 g_list_free(file_list);
9331 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9333 Compose *compose = (Compose *)data;
9335 compose_insert_sig(compose, FALSE);
9338 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9342 Compose *compose = (Compose *)data;
9344 gtkut_widget_get_uposition(widget, &x, &y);
9345 if (!compose->batch) {
9346 prefs_common.compose_x = x;
9347 prefs_common.compose_y = y;
9349 if (compose->sending || compose->updating)
9351 compose_close_cb(NULL, compose);
9355 void compose_close_toolbar(Compose *compose)
9357 compose_close_cb(NULL, compose);
9360 static void compose_close_cb(GtkAction *action, gpointer data)
9362 Compose *compose = (Compose *)data;
9366 if (compose->exteditor_tag != -1) {
9367 if (!compose_ext_editor_kill(compose))
9372 if (compose->modified) {
9373 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9374 if (!g_mutex_trylock(compose->mutex)) {
9375 /* we don't want to lock the mutex once it's available,
9376 * because as the only other part of compose.c locking
9377 * it is compose_close - which means once unlocked,
9378 * the compose struct will be freed */
9379 debug_print("couldn't lock mutex, probably sending\n");
9383 val = alertpanel(_("Discard message"),
9384 _("This message has been modified. Discard it?"),
9385 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9387 val = alertpanel(_("Save changes"),
9388 _("This message has been modified. Save the latest changes?"),
9389 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9391 g_mutex_unlock(compose->mutex);
9393 case G_ALERTDEFAULT:
9394 if (prefs_common.autosave && !reedit)
9395 compose_remove_draft(compose);
9397 case G_ALERTALTERNATE:
9398 compose_draft(data, COMPOSE_QUIT_EDITING);
9405 compose_close(compose);
9408 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9410 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9411 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9412 Compose *compose = (Compose *) data;
9415 compose->out_encoding = (CharSet)value;
9418 static void compose_address_cb(GtkAction *action, gpointer data)
9420 Compose *compose = (Compose *)data;
9422 addressbook_open(compose);
9425 static void about_show_cb(GtkAction *action, gpointer data)
9430 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9432 Compose *compose = (Compose *)data;
9437 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9438 cm_return_if_fail(tmpl != NULL);
9440 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9442 val = alertpanel(_("Apply template"), msg,
9443 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9446 if (val == G_ALERTDEFAULT)
9447 compose_template_apply(compose, tmpl, TRUE);
9448 else if (val == G_ALERTALTERNATE)
9449 compose_template_apply(compose, tmpl, FALSE);
9452 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9454 Compose *compose = (Compose *)data;
9456 compose_exec_ext_editor(compose);
9459 static void compose_undo_cb(GtkAction *action, gpointer data)
9461 Compose *compose = (Compose *)data;
9462 gboolean prev_autowrap = compose->autowrap;
9464 compose->autowrap = FALSE;
9465 undo_undo(compose->undostruct);
9466 compose->autowrap = prev_autowrap;
9469 static void compose_redo_cb(GtkAction *action, gpointer data)
9471 Compose *compose = (Compose *)data;
9472 gboolean prev_autowrap = compose->autowrap;
9474 compose->autowrap = FALSE;
9475 undo_redo(compose->undostruct);
9476 compose->autowrap = prev_autowrap;
9479 static void entry_cut_clipboard(GtkWidget *entry)
9481 if (GTK_IS_EDITABLE(entry))
9482 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9483 else if (GTK_IS_TEXT_VIEW(entry))
9484 gtk_text_buffer_cut_clipboard(
9485 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9486 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9490 static void entry_copy_clipboard(GtkWidget *entry)
9492 if (GTK_IS_EDITABLE(entry))
9493 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9494 else if (GTK_IS_TEXT_VIEW(entry))
9495 gtk_text_buffer_copy_clipboard(
9496 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9497 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9500 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9501 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9503 if (GTK_IS_TEXT_VIEW(entry)) {
9504 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9505 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9506 GtkTextIter start_iter, end_iter;
9508 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9510 if (contents == NULL)
9513 /* we shouldn't delete the selection when middle-click-pasting, or we
9514 * can't mid-click-paste our own selection */
9515 if (clip != GDK_SELECTION_PRIMARY) {
9516 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9517 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9520 if (insert_place == NULL) {
9521 /* if insert_place isn't specified, insert at the cursor.
9522 * used for Ctrl-V pasting */
9523 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9524 start = gtk_text_iter_get_offset(&start_iter);
9525 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9527 /* if insert_place is specified, paste here.
9528 * used for mid-click-pasting */
9529 start = gtk_text_iter_get_offset(insert_place);
9530 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9531 if (prefs_common.primary_paste_unselects)
9532 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9536 /* paste unwrapped: mark the paste so it's not wrapped later */
9537 end = start + strlen(contents);
9538 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9539 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9540 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9541 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9542 /* rewrap paragraph now (after a mid-click-paste) */
9543 mark_start = gtk_text_buffer_get_insert(buffer);
9544 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9545 gtk_text_iter_backward_char(&start_iter);
9546 compose_beautify_paragraph(compose, &start_iter, TRUE);
9548 } else if (GTK_IS_EDITABLE(entry))
9549 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9551 compose->modified = TRUE;
9554 static void entry_allsel(GtkWidget *entry)
9556 if (GTK_IS_EDITABLE(entry))
9557 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9558 else if (GTK_IS_TEXT_VIEW(entry)) {
9559 GtkTextIter startiter, enditer;
9560 GtkTextBuffer *textbuf;
9562 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9563 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9564 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9566 gtk_text_buffer_move_mark_by_name(textbuf,
9567 "selection_bound", &startiter);
9568 gtk_text_buffer_move_mark_by_name(textbuf,
9569 "insert", &enditer);
9573 static void compose_cut_cb(GtkAction *action, gpointer data)
9575 Compose *compose = (Compose *)data;
9576 if (compose->focused_editable
9577 #ifndef GENERIC_UMPC
9578 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9581 entry_cut_clipboard(compose->focused_editable);
9584 static void compose_copy_cb(GtkAction *action, gpointer data)
9586 Compose *compose = (Compose *)data;
9587 if (compose->focused_editable
9588 #ifndef GENERIC_UMPC
9589 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9592 entry_copy_clipboard(compose->focused_editable);
9595 static void compose_paste_cb(GtkAction *action, gpointer data)
9597 Compose *compose = (Compose *)data;
9599 GtkTextBuffer *buffer;
9601 if (compose->focused_editable &&
9602 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9603 entry_paste_clipboard(compose, compose->focused_editable,
9604 prefs_common.linewrap_pastes,
9605 GDK_SELECTION_CLIPBOARD, NULL);
9609 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9611 Compose *compose = (Compose *)data;
9612 gint wrap_quote = prefs_common.linewrap_quote;
9613 if (compose->focused_editable
9614 #ifndef GENERIC_UMPC
9615 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9618 /* let text_insert() (called directly or at a later time
9619 * after the gtk_editable_paste_clipboard) know that
9620 * text is to be inserted as a quotation. implemented
9621 * by using a simple refcount... */
9622 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9623 G_OBJECT(compose->focused_editable),
9624 "paste_as_quotation"));
9625 g_object_set_data(G_OBJECT(compose->focused_editable),
9626 "paste_as_quotation",
9627 GINT_TO_POINTER(paste_as_quotation + 1));
9628 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9629 entry_paste_clipboard(compose, compose->focused_editable,
9630 prefs_common.linewrap_pastes,
9631 GDK_SELECTION_CLIPBOARD, NULL);
9632 prefs_common.linewrap_quote = wrap_quote;
9636 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9638 Compose *compose = (Compose *)data;
9640 GtkTextBuffer *buffer;
9642 if (compose->focused_editable
9643 #ifndef GENERIC_UMPC
9644 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9647 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9648 GDK_SELECTION_CLIPBOARD, NULL);
9652 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9654 Compose *compose = (Compose *)data;
9656 GtkTextBuffer *buffer;
9658 if (compose->focused_editable
9659 #ifndef GENERIC_UMPC
9660 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9663 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9664 GDK_SELECTION_CLIPBOARD, NULL);
9668 static void compose_allsel_cb(GtkAction *action, gpointer data)
9670 Compose *compose = (Compose *)data;
9671 if (compose->focused_editable
9672 #ifndef GENERIC_UMPC
9673 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9676 entry_allsel(compose->focused_editable);
9679 static void textview_move_beginning_of_line (GtkTextView *text)
9681 GtkTextBuffer *buffer;
9685 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9687 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9688 mark = gtk_text_buffer_get_insert(buffer);
9689 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9690 gtk_text_iter_set_line_offset(&ins, 0);
9691 gtk_text_buffer_place_cursor(buffer, &ins);
9694 static void textview_move_forward_character (GtkTextView *text)
9696 GtkTextBuffer *buffer;
9700 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9702 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9703 mark = gtk_text_buffer_get_insert(buffer);
9704 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9705 if (gtk_text_iter_forward_cursor_position(&ins))
9706 gtk_text_buffer_place_cursor(buffer, &ins);
9709 static void textview_move_backward_character (GtkTextView *text)
9711 GtkTextBuffer *buffer;
9715 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9717 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9718 mark = gtk_text_buffer_get_insert(buffer);
9719 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9720 if (gtk_text_iter_backward_cursor_position(&ins))
9721 gtk_text_buffer_place_cursor(buffer, &ins);
9724 static void textview_move_forward_word (GtkTextView *text)
9726 GtkTextBuffer *buffer;
9731 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9733 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9734 mark = gtk_text_buffer_get_insert(buffer);
9735 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9736 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9737 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9738 gtk_text_iter_backward_word_start(&ins);
9739 gtk_text_buffer_place_cursor(buffer, &ins);
9743 static void textview_move_backward_word (GtkTextView *text)
9745 GtkTextBuffer *buffer;
9750 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9752 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9753 mark = gtk_text_buffer_get_insert(buffer);
9754 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9755 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9756 if (gtk_text_iter_backward_word_starts(&ins, 1))
9757 gtk_text_buffer_place_cursor(buffer, &ins);
9760 static void textview_move_end_of_line (GtkTextView *text)
9762 GtkTextBuffer *buffer;
9766 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9768 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9769 mark = gtk_text_buffer_get_insert(buffer);
9770 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9771 if (gtk_text_iter_forward_to_line_end(&ins))
9772 gtk_text_buffer_place_cursor(buffer, &ins);
9775 static void textview_move_next_line (GtkTextView *text)
9777 GtkTextBuffer *buffer;
9782 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9784 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9785 mark = gtk_text_buffer_get_insert(buffer);
9786 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9787 offset = gtk_text_iter_get_line_offset(&ins);
9788 if (gtk_text_iter_forward_line(&ins)) {
9789 gtk_text_iter_set_line_offset(&ins, offset);
9790 gtk_text_buffer_place_cursor(buffer, &ins);
9794 static void textview_move_previous_line (GtkTextView *text)
9796 GtkTextBuffer *buffer;
9801 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9803 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9804 mark = gtk_text_buffer_get_insert(buffer);
9805 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9806 offset = gtk_text_iter_get_line_offset(&ins);
9807 if (gtk_text_iter_backward_line(&ins)) {
9808 gtk_text_iter_set_line_offset(&ins, offset);
9809 gtk_text_buffer_place_cursor(buffer, &ins);
9813 static void textview_delete_forward_character (GtkTextView *text)
9815 GtkTextBuffer *buffer;
9817 GtkTextIter ins, end_iter;
9819 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9821 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9822 mark = gtk_text_buffer_get_insert(buffer);
9823 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9825 if (gtk_text_iter_forward_char(&end_iter)) {
9826 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9830 static void textview_delete_backward_character (GtkTextView *text)
9832 GtkTextBuffer *buffer;
9834 GtkTextIter ins, end_iter;
9836 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9838 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9839 mark = gtk_text_buffer_get_insert(buffer);
9840 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9842 if (gtk_text_iter_backward_char(&end_iter)) {
9843 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9847 static void textview_delete_forward_word (GtkTextView *text)
9849 GtkTextBuffer *buffer;
9851 GtkTextIter ins, end_iter;
9853 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9855 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9856 mark = gtk_text_buffer_get_insert(buffer);
9857 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9859 if (gtk_text_iter_forward_word_end(&end_iter)) {
9860 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9864 static void textview_delete_backward_word (GtkTextView *text)
9866 GtkTextBuffer *buffer;
9868 GtkTextIter ins, end_iter;
9870 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9872 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9873 mark = gtk_text_buffer_get_insert(buffer);
9874 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9876 if (gtk_text_iter_backward_word_start(&end_iter)) {
9877 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9881 static void textview_delete_line (GtkTextView *text)
9883 GtkTextBuffer *buffer;
9885 GtkTextIter ins, start_iter, end_iter;
9887 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9889 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9890 mark = gtk_text_buffer_get_insert(buffer);
9891 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9894 gtk_text_iter_set_line_offset(&start_iter, 0);
9897 if (gtk_text_iter_ends_line(&end_iter)){
9898 if (!gtk_text_iter_forward_char(&end_iter))
9899 gtk_text_iter_backward_char(&start_iter);
9902 gtk_text_iter_forward_to_line_end(&end_iter);
9903 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9906 static void textview_delete_to_line_end (GtkTextView *text)
9908 GtkTextBuffer *buffer;
9910 GtkTextIter ins, end_iter;
9912 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9914 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9915 mark = gtk_text_buffer_get_insert(buffer);
9916 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9918 if (gtk_text_iter_ends_line(&end_iter))
9919 gtk_text_iter_forward_char(&end_iter);
9921 gtk_text_iter_forward_to_line_end(&end_iter);
9922 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9925 #define DO_ACTION(name, act) { \
9926 if(!strcmp(name, a_name)) { \
9930 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9932 const gchar *a_name = gtk_action_get_name(action);
9933 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9934 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9935 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9936 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9937 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9938 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9939 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9940 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9941 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9942 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9943 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9944 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9945 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9946 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9950 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9952 Compose *compose = (Compose *)data;
9953 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9954 ComposeCallAdvancedAction action = -1;
9956 action = compose_call_advanced_action_from_path(gaction);
9959 void (*do_action) (GtkTextView *text);
9960 } action_table[] = {
9961 {textview_move_beginning_of_line},
9962 {textview_move_forward_character},
9963 {textview_move_backward_character},
9964 {textview_move_forward_word},
9965 {textview_move_backward_word},
9966 {textview_move_end_of_line},
9967 {textview_move_next_line},
9968 {textview_move_previous_line},
9969 {textview_delete_forward_character},
9970 {textview_delete_backward_character},
9971 {textview_delete_forward_word},
9972 {textview_delete_backward_word},
9973 {textview_delete_line},
9974 {textview_delete_to_line_end}
9977 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9979 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9980 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9981 if (action_table[action].do_action)
9982 action_table[action].do_action(text);
9984 g_warning("Not implemented yet.");
9988 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9992 if (GTK_IS_EDITABLE(widget)) {
9993 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9994 gtk_editable_set_position(GTK_EDITABLE(widget),
9997 if (widget->parent && widget->parent->parent
9998 && widget->parent->parent->parent) {
9999 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10000 gint y = widget->allocation.y;
10001 gint height = widget->allocation.height;
10002 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10003 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10005 if (y < (int)shown->value) {
10006 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10008 if (y + height > (int)shown->value + (int)shown->page_size) {
10009 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10010 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10011 y + height - (int)shown->page_size - 1);
10013 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10014 (int)shown->upper - (int)shown->page_size - 1);
10021 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10022 compose->focused_editable = widget;
10024 #ifdef GENERIC_UMPC
10025 if (GTK_IS_TEXT_VIEW(widget)
10026 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10027 g_object_ref(compose->notebook);
10028 g_object_ref(compose->edit_vbox);
10029 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10030 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10031 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10032 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10033 g_object_unref(compose->notebook);
10034 g_object_unref(compose->edit_vbox);
10035 g_signal_handlers_block_by_func(G_OBJECT(widget),
10036 G_CALLBACK(compose_grab_focus_cb),
10038 gtk_widget_grab_focus(widget);
10039 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10040 G_CALLBACK(compose_grab_focus_cb),
10042 } else if (!GTK_IS_TEXT_VIEW(widget)
10043 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10044 g_object_ref(compose->notebook);
10045 g_object_ref(compose->edit_vbox);
10046 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10047 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10048 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10049 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10050 g_object_unref(compose->notebook);
10051 g_object_unref(compose->edit_vbox);
10052 g_signal_handlers_block_by_func(G_OBJECT(widget),
10053 G_CALLBACK(compose_grab_focus_cb),
10055 gtk_widget_grab_focus(widget);
10056 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10057 G_CALLBACK(compose_grab_focus_cb),
10063 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10065 compose->modified = TRUE;
10066 // compose_beautify_paragraph(compose, NULL, TRUE);
10067 #ifndef GENERIC_UMPC
10068 compose_set_title(compose);
10072 static void compose_wrap_cb(GtkAction *action, gpointer data)
10074 Compose *compose = (Compose *)data;
10075 compose_beautify_paragraph(compose, NULL, TRUE);
10078 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10080 Compose *compose = (Compose *)data;
10081 compose_wrap_all_full(compose, TRUE);
10084 static void compose_find_cb(GtkAction *action, gpointer data)
10086 Compose *compose = (Compose *)data;
10088 message_search_compose(compose);
10091 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10094 Compose *compose = (Compose *)data;
10095 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10096 if (compose->autowrap)
10097 compose_wrap_all_full(compose, TRUE);
10098 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10101 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10104 Compose *compose = (Compose *)data;
10105 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10108 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10110 Compose *compose = (Compose *)data;
10112 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10115 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10117 Compose *compose = (Compose *)data;
10119 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10122 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10124 g_free(compose->privacy_system);
10126 compose->privacy_system = g_strdup(account->default_privacy_system);
10127 compose_update_privacy_system_menu_item(compose, warn);
10130 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10132 Compose *compose = (Compose *)data;
10134 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10135 gtk_widget_show(compose->ruler_hbox);
10136 prefs_common.show_ruler = TRUE;
10138 gtk_widget_hide(compose->ruler_hbox);
10139 gtk_widget_queue_resize(compose->edit_vbox);
10140 prefs_common.show_ruler = FALSE;
10144 static void compose_attach_drag_received_cb (GtkWidget *widget,
10145 GdkDragContext *context,
10148 GtkSelectionData *data,
10151 gpointer user_data)
10153 Compose *compose = (Compose *)user_data;
10156 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10158 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10160 ) && gtk_drag_get_source_widget(context) !=
10161 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10162 list = uri_list_extract_filenames((const gchar *)data->data);
10163 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10164 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10165 compose_attach_append
10166 (compose, (const gchar *)tmp->data,
10167 utf8_filename, NULL);
10168 g_free(utf8_filename);
10170 if (list) compose_changed_cb(NULL, compose);
10171 list_free_strings(list);
10173 } else if (gtk_drag_get_source_widget(context)
10174 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10175 /* comes from our summaryview */
10176 SummaryView * summaryview = NULL;
10177 GSList * list = NULL, *cur = NULL;
10179 if (mainwindow_get_mainwindow())
10180 summaryview = mainwindow_get_mainwindow()->summaryview;
10183 list = summary_get_selected_msg_list(summaryview);
10185 for (cur = list; cur; cur = cur->next) {
10186 MsgInfo *msginfo = (MsgInfo *)cur->data;
10187 gchar *file = NULL;
10189 file = procmsg_get_message_file_full(msginfo,
10192 compose_attach_append(compose, (const gchar *)file,
10193 (const gchar *)file, "message/rfc822");
10197 g_slist_free(list);
10201 static gboolean compose_drag_drop(GtkWidget *widget,
10202 GdkDragContext *drag_context,
10204 guint time, gpointer user_data)
10206 /* not handling this signal makes compose_insert_drag_received_cb
10211 static void compose_insert_drag_received_cb (GtkWidget *widget,
10212 GdkDragContext *drag_context,
10215 GtkSelectionData *data,
10218 gpointer user_data)
10220 Compose *compose = (Compose *)user_data;
10223 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10226 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10228 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10230 AlertValue val = G_ALERTDEFAULT;
10232 list = uri_list_extract_filenames((const gchar *)data->data);
10233 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10234 /* Assume a list of no files, and data has ://, is a remote link */
10235 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10236 gchar *tmpfile = get_tmp_file();
10237 str_write_to_file(tmpdata, tmpfile);
10239 compose_insert_file(compose, tmpfile);
10240 claws_unlink(tmpfile);
10242 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10243 compose_beautify_paragraph(compose, NULL, TRUE);
10246 switch (prefs_common.compose_dnd_mode) {
10247 case COMPOSE_DND_ASK:
10248 val = alertpanel_full(_("Insert or attach?"),
10249 _("Do you want to insert the contents of the file(s) "
10250 "into the message body, or attach it to the email?"),
10251 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10252 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10254 case COMPOSE_DND_INSERT:
10255 val = G_ALERTALTERNATE;
10257 case COMPOSE_DND_ATTACH:
10258 val = G_ALERTOTHER;
10261 /* unexpected case */
10262 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10265 if (val & G_ALERTDISABLE) {
10266 val &= ~G_ALERTDISABLE;
10267 /* remember what action to perform by default, only if we don't click Cancel */
10268 if (val == G_ALERTALTERNATE)
10269 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10270 else if (val == G_ALERTOTHER)
10271 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10274 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10275 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10276 list_free_strings(list);
10279 } else if (val == G_ALERTOTHER) {
10280 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10281 list_free_strings(list);
10286 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10287 compose_insert_file(compose, (const gchar *)tmp->data);
10289 list_free_strings(list);
10291 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10294 #if GTK_CHECK_VERSION(2, 8, 0)
10295 /* do nothing, handled by GTK */
10297 gchar *tmpfile = get_tmp_file();
10298 str_write_to_file((const gchar *)data->data, tmpfile);
10299 compose_insert_file(compose, tmpfile);
10300 claws_unlink(tmpfile);
10302 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10306 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10309 static void compose_header_drag_received_cb (GtkWidget *widget,
10310 GdkDragContext *drag_context,
10313 GtkSelectionData *data,
10316 gpointer user_data)
10318 GtkEditable *entry = (GtkEditable *)user_data;
10319 gchar *email = (gchar *)data->data;
10321 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10324 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10325 gchar *decoded=g_new(gchar, strlen(email));
10328 email += strlen("mailto:");
10329 decode_uri(decoded, email); /* will fit */
10330 gtk_editable_delete_text(entry, 0, -1);
10331 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10332 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10336 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10339 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10341 Compose *compose = (Compose *)data;
10343 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10344 compose->return_receipt = TRUE;
10346 compose->return_receipt = FALSE;
10349 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10351 Compose *compose = (Compose *)data;
10353 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10354 compose->remove_references = TRUE;
10356 compose->remove_references = FALSE;
10359 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10360 GdkEventKey *event,
10361 ComposeHeaderEntry *headerentry)
10363 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10364 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10365 !(event->state & GDK_MODIFIER_MASK) &&
10366 (event->keyval == GDK_BackSpace) &&
10367 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10368 gtk_container_remove
10369 (GTK_CONTAINER(headerentry->compose->header_table),
10370 headerentry->combo);
10371 gtk_container_remove
10372 (GTK_CONTAINER(headerentry->compose->header_table),
10373 headerentry->entry);
10374 headerentry->compose->header_list =
10375 g_slist_remove(headerentry->compose->header_list,
10377 g_free(headerentry);
10378 } else if (event->keyval == GDK_Tab) {
10379 if (headerentry->compose->header_last == headerentry) {
10380 /* Override default next focus, and give it to subject_entry
10381 * instead of notebook tabs
10383 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10384 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10391 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10392 ComposeHeaderEntry *headerentry)
10394 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10395 compose_create_header_entry(headerentry->compose);
10396 g_signal_handlers_disconnect_matched
10397 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10398 0, 0, NULL, NULL, headerentry);
10400 /* Automatically scroll down */
10401 compose_show_first_last_header(headerentry->compose, FALSE);
10407 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10409 GtkAdjustment *vadj;
10411 cm_return_if_fail(compose);
10412 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10413 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10415 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10416 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10417 gtk_adjustment_changed(vadj);
10420 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10421 const gchar *text, gint len, Compose *compose)
10423 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10424 (G_OBJECT(compose->text), "paste_as_quotation"));
10427 cm_return_if_fail(text != NULL);
10429 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10430 G_CALLBACK(text_inserted),
10432 if (paste_as_quotation) {
10434 const gchar *qmark;
10436 GtkTextIter start_iter;
10439 len = strlen(text);
10441 new_text = g_strndup(text, len);
10443 qmark = compose_quote_char_from_context(compose);
10445 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10446 gtk_text_buffer_place_cursor(buffer, iter);
10448 pos = gtk_text_iter_get_offset(iter);
10450 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10451 _("Quote format error at line %d."));
10452 quote_fmt_reset_vartable();
10454 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10455 GINT_TO_POINTER(paste_as_quotation - 1));
10457 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10458 gtk_text_buffer_place_cursor(buffer, iter);
10459 gtk_text_buffer_delete_mark(buffer, mark);
10461 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10462 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10463 compose_beautify_paragraph(compose, &start_iter, FALSE);
10464 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10465 gtk_text_buffer_delete_mark(buffer, mark);
10467 if (strcmp(text, "\n") || compose->automatic_break
10468 || gtk_text_iter_starts_line(iter)) {
10469 GtkTextIter before_ins;
10470 gtk_text_buffer_insert(buffer, iter, text, len);
10471 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10472 before_ins = *iter;
10473 gtk_text_iter_backward_chars(&before_ins, len);
10474 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10477 /* check if the preceding is just whitespace or quote */
10478 GtkTextIter start_line;
10479 gchar *tmp = NULL, *quote = NULL;
10480 gint quote_len = 0, is_normal = 0;
10481 start_line = *iter;
10482 gtk_text_iter_set_line_offset(&start_line, 0);
10483 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10486 if (*tmp == '\0') {
10489 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10497 gtk_text_buffer_insert(buffer, iter, text, len);
10499 gtk_text_buffer_insert_with_tags_by_name(buffer,
10500 iter, text, len, "no_join", NULL);
10505 if (!paste_as_quotation) {
10506 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10507 compose_beautify_paragraph(compose, iter, FALSE);
10508 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10509 gtk_text_buffer_delete_mark(buffer, mark);
10512 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10513 G_CALLBACK(text_inserted),
10515 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10517 if (prefs_common.autosave &&
10518 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10519 compose->draft_timeout_tag != -2 /* disabled while loading */)
10520 compose->draft_timeout_tag = g_timeout_add
10521 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10523 static gint compose_defer_auto_save_draft(Compose *compose)
10525 compose->draft_timeout_tag = -1;
10526 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10531 static void compose_check_all(GtkAction *action, gpointer data)
10533 Compose *compose = (Compose *)data;
10534 if (!compose->gtkaspell)
10537 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10538 claws_spell_entry_check_all(
10539 CLAWS_SPELL_ENTRY(compose->subject_entry));
10541 gtkaspell_check_all(compose->gtkaspell);
10544 static void compose_highlight_all(GtkAction *action, gpointer data)
10546 Compose *compose = (Compose *)data;
10547 if (compose->gtkaspell) {
10548 claws_spell_entry_recheck_all(
10549 CLAWS_SPELL_ENTRY(compose->subject_entry));
10550 gtkaspell_highlight_all(compose->gtkaspell);
10554 static void compose_check_backwards(GtkAction *action, gpointer data)
10556 Compose *compose = (Compose *)data;
10557 if (!compose->gtkaspell) {
10558 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10562 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10563 claws_spell_entry_check_backwards(
10564 CLAWS_SPELL_ENTRY(compose->subject_entry));
10566 gtkaspell_check_backwards(compose->gtkaspell);
10569 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10571 Compose *compose = (Compose *)data;
10572 if (!compose->gtkaspell) {
10573 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10577 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10578 claws_spell_entry_check_forwards_go(
10579 CLAWS_SPELL_ENTRY(compose->subject_entry));
10581 gtkaspell_check_forwards_go(compose->gtkaspell);
10586 *\brief Guess originating forward account from MsgInfo and several
10587 * "common preference" settings. Return NULL if no guess.
10589 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10591 PrefsAccount *account = NULL;
10593 cm_return_val_if_fail(msginfo, NULL);
10594 cm_return_val_if_fail(msginfo->folder, NULL);
10595 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10597 if (msginfo->folder->prefs->enable_default_account)
10598 account = account_find_from_id(msginfo->folder->prefs->default_account);
10601 account = msginfo->folder->folder->account;
10603 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10605 Xstrdup_a(to, msginfo->to, return NULL);
10606 extract_address(to);
10607 account = account_find_from_address(to, FALSE);
10610 if (!account && prefs_common.forward_account_autosel) {
10611 gchar cc[BUFFSIZE];
10612 if (!procheader_get_header_from_msginfo
10613 (msginfo, cc,sizeof cc , "Cc:")) {
10614 gchar *buf = cc + strlen("Cc:");
10615 extract_address(buf);
10616 account = account_find_from_address(buf, FALSE);
10620 if (!account && prefs_common.forward_account_autosel) {
10621 gchar deliveredto[BUFFSIZE];
10622 if (!procheader_get_header_from_msginfo
10623 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10624 gchar *buf = deliveredto + strlen("Delivered-To:");
10625 extract_address(buf);
10626 account = account_find_from_address(buf, FALSE);
10633 gboolean compose_close(Compose *compose)
10637 if (!g_mutex_trylock(compose->mutex)) {
10638 /* we have to wait for the (possibly deferred by auto-save)
10639 * drafting to be done, before destroying the compose under
10641 debug_print("waiting for drafting to finish...\n");
10642 compose_allow_user_actions(compose, FALSE);
10643 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10646 cm_return_val_if_fail(compose, FALSE);
10647 gtkut_widget_get_uposition(compose->window, &x, &y);
10648 if (!compose->batch) {
10649 prefs_common.compose_x = x;
10650 prefs_common.compose_y = y;
10652 g_mutex_unlock(compose->mutex);
10653 compose_destroy(compose);
10658 * Add entry field for each address in list.
10659 * \param compose E-Mail composition object.
10660 * \param listAddress List of (formatted) E-Mail addresses.
10662 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10665 node = listAddress;
10667 addr = ( gchar * ) node->data;
10668 compose_entry_append( compose, addr, COMPOSE_TO );
10669 node = g_list_next( node );
10673 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10674 guint action, gboolean opening_multiple)
10676 gchar *body = NULL;
10677 GSList *new_msglist = NULL;
10678 MsgInfo *tmp_msginfo = NULL;
10679 gboolean originally_enc = FALSE;
10680 gboolean originally_sig = FALSE;
10681 Compose *compose = NULL;
10682 gchar *s_system = NULL;
10684 cm_return_if_fail(msgview != NULL);
10686 cm_return_if_fail(msginfo_list != NULL);
10688 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10689 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10690 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10692 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10693 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10694 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10695 orig_msginfo, mimeinfo);
10696 if (tmp_msginfo != NULL) {
10697 new_msglist = g_slist_append(NULL, tmp_msginfo);
10699 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10700 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10701 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10703 tmp_msginfo->folder = orig_msginfo->folder;
10704 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10705 if (orig_msginfo->tags)
10706 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10711 if (!opening_multiple)
10712 body = messageview_get_selection(msgview);
10715 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10716 procmsg_msginfo_free(tmp_msginfo);
10717 g_slist_free(new_msglist);
10719 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10721 if (compose && originally_enc) {
10722 compose_force_encryption(compose, compose->account, FALSE, s_system);
10725 if (compose && originally_sig && compose->account->default_sign_reply) {
10726 compose_force_signing(compose, compose->account, s_system);
10732 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10735 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10736 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10737 GSList *cur = msginfo_list;
10738 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10739 "messages. Opening the windows "
10740 "could take some time. Do you "
10741 "want to continue?"),
10742 g_slist_length(msginfo_list));
10743 if (g_slist_length(msginfo_list) > 9
10744 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10745 != G_ALERTALTERNATE) {
10750 /* We'll open multiple compose windows */
10751 /* let the WM place the next windows */
10752 compose_force_window_origin = FALSE;
10753 for (; cur; cur = cur->next) {
10755 tmplist.data = cur->data;
10756 tmplist.next = NULL;
10757 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10759 compose_force_window_origin = TRUE;
10761 /* forwarding multiple mails as attachments is done via a
10762 * single compose window */
10763 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10767 void compose_set_position(Compose *compose, gint pos)
10769 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10771 gtkut_text_view_set_position(text, pos);
10774 gboolean compose_search_string(Compose *compose,
10775 const gchar *str, gboolean case_sens)
10777 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10779 return gtkut_text_view_search_string(text, str, case_sens);
10782 gboolean compose_search_string_backward(Compose *compose,
10783 const gchar *str, gboolean case_sens)
10785 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10787 return gtkut_text_view_search_string_backward(text, str, case_sens);
10790 /* allocate a msginfo structure and populate its data from a compose data structure */
10791 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10793 MsgInfo *newmsginfo;
10795 gchar buf[BUFFSIZE];
10797 cm_return_val_if_fail( compose != NULL, NULL );
10799 newmsginfo = procmsg_msginfo_new();
10802 get_rfc822_date(buf, sizeof(buf));
10803 newmsginfo->date = g_strdup(buf);
10806 if (compose->from_name) {
10807 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10808 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10812 if (compose->subject_entry)
10813 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10815 /* to, cc, reply-to, newsgroups */
10816 for (list = compose->header_list; list; list = list->next) {
10817 gchar *header = gtk_editable_get_chars(
10819 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10820 gchar *entry = gtk_editable_get_chars(
10821 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10823 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10824 if ( newmsginfo->to == NULL ) {
10825 newmsginfo->to = g_strdup(entry);
10826 } else if (entry && *entry) {
10827 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10828 g_free(newmsginfo->to);
10829 newmsginfo->to = tmp;
10832 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10833 if ( newmsginfo->cc == NULL ) {
10834 newmsginfo->cc = g_strdup(entry);
10835 } else if (entry && *entry) {
10836 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10837 g_free(newmsginfo->cc);
10838 newmsginfo->cc = tmp;
10841 if ( strcasecmp(header,
10842 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10843 if ( newmsginfo->newsgroups == NULL ) {
10844 newmsginfo->newsgroups = g_strdup(entry);
10845 } else if (entry && *entry) {
10846 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10847 g_free(newmsginfo->newsgroups);
10848 newmsginfo->newsgroups = tmp;
10856 /* other data is unset */
10862 /* update compose's dictionaries from folder dict settings */
10863 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10864 FolderItem *folder_item)
10866 cm_return_if_fail(compose != NULL);
10868 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10869 FolderItemPrefs *prefs = folder_item->prefs;
10871 if (prefs->enable_default_dictionary)
10872 gtkaspell_change_dict(compose->gtkaspell,
10873 prefs->default_dictionary, FALSE);
10874 if (folder_item->prefs->enable_default_alt_dictionary)
10875 gtkaspell_change_alt_dict(compose->gtkaspell,
10876 prefs->default_alt_dictionary);
10877 if (prefs->enable_default_dictionary
10878 || prefs->enable_default_alt_dictionary)
10879 compose_spell_menu_changed(compose);