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,
4543 gchar *from = NULL, *header;
4544 ComposeHeaderEntry *header_entry;
4546 cm_return_if_fail(account != NULL);
4548 compose->account = account;
4550 if (account->name && *account->name) {
4552 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4553 from = g_strdup_printf("%s <%s>",
4554 buf, account->address);
4555 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4557 from = g_strdup_printf("<%s>",
4559 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4564 compose_set_title(compose);
4566 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4567 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4569 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4570 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4571 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4573 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4575 activate_privacy_system(compose, account, FALSE);
4577 if (!init && compose->mode != COMPOSE_REDIRECT) {
4578 undo_block(compose->undostruct);
4579 compose_insert_sig(compose, TRUE);
4580 undo_unblock(compose->undostruct);
4583 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4584 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4586 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4587 if (account->protocol == A_NNTP) {
4588 if (!strcmp(header, _("To:")))
4589 combobox_select_by_text(
4590 GTK_COMBO_BOX(header_entry->combo),
4593 if (!strcmp(header, _("Newsgroups:")))
4594 combobox_select_by_text(
4595 GTK_COMBO_BOX(header_entry->combo),
4603 /* use account's dict info if set */
4604 if (compose->gtkaspell) {
4605 if (account->enable_default_dictionary)
4606 gtkaspell_change_dict(compose->gtkaspell,
4607 account->default_dictionary, FALSE);
4608 if (account->enable_default_alt_dictionary)
4609 gtkaspell_change_alt_dict(compose->gtkaspell,
4610 account->default_alt_dictionary);
4611 if (account->enable_default_dictionary
4612 || account->enable_default_alt_dictionary)
4613 compose_spell_menu_changed(compose);
4618 gboolean compose_check_for_valid_recipient(Compose *compose) {
4619 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4620 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4621 gboolean recipient_found = FALSE;
4625 /* free to and newsgroup list */
4626 slist_free_strings(compose->to_list);
4627 g_slist_free(compose->to_list);
4628 compose->to_list = NULL;
4630 slist_free_strings(compose->newsgroup_list);
4631 g_slist_free(compose->newsgroup_list);
4632 compose->newsgroup_list = NULL;
4634 /* search header entries for to and newsgroup entries */
4635 for (list = compose->header_list; list; list = list->next) {
4638 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4639 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4642 if (entry[0] != '\0') {
4643 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4644 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4645 compose->to_list = address_list_append(compose->to_list, entry);
4646 recipient_found = TRUE;
4649 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4650 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4651 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4652 recipient_found = TRUE;
4659 return recipient_found;
4662 static gboolean compose_check_for_set_recipients(Compose *compose)
4664 if (compose->account->set_autocc && compose->account->auto_cc) {
4665 gboolean found_other = FALSE;
4667 /* search header entries for to and newsgroup entries */
4668 for (list = compose->header_list; list; list = list->next) {
4671 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4672 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4675 if (strcmp(entry, compose->account->auto_cc)
4676 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4686 if (compose->batch) {
4687 gtk_widget_show_all(compose->window);
4689 aval = alertpanel(_("Send"),
4690 _("The only recipient is the default CC address. Send anyway?"),
4691 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4692 if (aval != G_ALERTALTERNATE)
4696 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4697 gboolean found_other = FALSE;
4699 /* search header entries for to and newsgroup entries */
4700 for (list = compose->header_list; list; list = list->next) {
4703 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4704 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4707 if (strcmp(entry, compose->account->auto_bcc)
4708 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4718 if (compose->batch) {
4719 gtk_widget_show_all(compose->window);
4721 aval = alertpanel(_("Send"),
4722 _("The only recipient is the default BCC address. Send anyway?"),
4723 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4724 if (aval != G_ALERTALTERNATE)
4731 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4735 if (compose_check_for_valid_recipient(compose) == FALSE) {
4736 if (compose->batch) {
4737 gtk_widget_show_all(compose->window);
4739 alertpanel_error(_("Recipient is not specified."));
4743 if (compose_check_for_set_recipients(compose) == FALSE) {
4747 if (!compose->batch) {
4748 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4749 if (*str == '\0' && check_everything == TRUE &&
4750 compose->mode != COMPOSE_REDIRECT) {
4752 gchar *button_label;
4755 if (compose->sending)
4756 button_label = _("+_Send");
4758 button_label = _("+_Queue");
4759 message = g_strdup_printf(_("Subject is empty. %s"),
4760 compose->sending?_("Send it anyway?"):
4761 _("Queue it anyway?"));
4763 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4764 GTK_STOCK_CANCEL, button_label, NULL);
4766 if (aval != G_ALERTALTERNATE)
4771 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4777 gint compose_send(Compose *compose)
4780 FolderItem *folder = NULL;
4782 gchar *msgpath = NULL;
4783 gboolean discard_window = FALSE;
4784 gchar *errstr = NULL;
4785 gchar *tmsgid = NULL;
4786 MainWindow *mainwin = mainwindow_get_mainwindow();
4787 gboolean queued_removed = FALSE;
4789 if (prefs_common.send_dialog_invisible
4790 || compose->batch == TRUE)
4791 discard_window = TRUE;
4793 compose_allow_user_actions (compose, FALSE);
4794 compose->sending = TRUE;
4796 if (compose_check_entries(compose, TRUE) == FALSE) {
4797 if (compose->batch) {
4798 gtk_widget_show_all(compose->window);
4804 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4807 if (compose->batch) {
4808 gtk_widget_show_all(compose->window);
4811 alertpanel_error(_("Could not queue message for sending:\n\n"
4812 "Charset conversion failed."));
4813 } else if (val == -5) {
4814 alertpanel_error(_("Could not queue message for sending:\n\n"
4815 "Couldn't get recipient encryption key."));
4816 } else if (val == -6) {
4818 } else if (val == -3) {
4819 if (privacy_peek_error())
4820 alertpanel_error(_("Could not queue message for sending:\n\n"
4821 "Signature failed: %s"), privacy_get_error());
4822 } else if (val == -2 && errno != 0) {
4823 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4825 alertpanel_error(_("Could not queue message for sending."));
4830 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4831 if (discard_window) {
4832 compose->sending = FALSE;
4833 compose_close(compose);
4834 /* No more compose access in the normal codepath
4835 * after this point! */
4840 alertpanel_error(_("The message was queued but could not be "
4841 "sent.\nUse \"Send queued messages\" from "
4842 "the main window to retry."));
4843 if (!discard_window) {
4850 if (msgpath == NULL) {
4851 msgpath = folder_item_fetch_msg(folder, msgnum);
4852 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4855 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4856 claws_unlink(msgpath);
4859 if (!discard_window) {
4861 if (!queued_removed)
4862 folder_item_remove_msg(folder, msgnum);
4863 folder_item_scan(folder);
4865 /* make sure we delete that */
4866 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4868 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4869 folder_item_remove_msg(folder, tmp->msgnum);
4870 procmsg_msginfo_free(tmp);
4877 if (!queued_removed)
4878 folder_item_remove_msg(folder, msgnum);
4879 folder_item_scan(folder);
4881 /* make sure we delete that */
4882 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4884 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4885 folder_item_remove_msg(folder, tmp->msgnum);
4886 procmsg_msginfo_free(tmp);
4889 if (!discard_window) {
4890 compose->sending = FALSE;
4891 compose_allow_user_actions (compose, TRUE);
4892 compose_close(compose);
4896 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4897 "the main window to retry."), errstr);
4900 alertpanel_error_log(_("The message was queued but could not be "
4901 "sent.\nUse \"Send queued messages\" from "
4902 "the main window to retry."));
4904 if (!discard_window) {
4913 toolbar_main_set_sensitive(mainwin);
4914 main_window_set_menu_sensitive(mainwin);
4920 compose_allow_user_actions (compose, TRUE);
4921 compose->sending = FALSE;
4922 compose->modified = TRUE;
4923 toolbar_main_set_sensitive(mainwin);
4924 main_window_set_menu_sensitive(mainwin);
4929 static gboolean compose_use_attach(Compose *compose)
4931 GtkTreeModel *model = gtk_tree_view_get_model
4932 (GTK_TREE_VIEW(compose->attach_clist));
4933 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4936 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4939 gchar buf[BUFFSIZE];
4941 gboolean first_to_address;
4942 gboolean first_cc_address;
4944 ComposeHeaderEntry *headerentry;
4945 const gchar *headerentryname;
4946 const gchar *cc_hdr;
4947 const gchar *to_hdr;
4948 gboolean err = FALSE;
4950 debug_print("Writing redirect header\n");
4952 cc_hdr = prefs_common_translated_header_name("Cc:");
4953 to_hdr = prefs_common_translated_header_name("To:");
4955 first_to_address = TRUE;
4956 for (list = compose->header_list; list; list = list->next) {
4957 headerentry = ((ComposeHeaderEntry *)list->data);
4958 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4960 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4961 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4962 Xstrdup_a(str, entstr, return -1);
4964 if (str[0] != '\0') {
4965 compose_convert_header
4966 (compose, buf, sizeof(buf), str,
4967 strlen("Resent-To") + 2, TRUE);
4969 if (first_to_address) {
4970 err |= (fprintf(fp, "Resent-To: ") < 0);
4971 first_to_address = FALSE;
4973 err |= (fprintf(fp, ",") < 0);
4975 err |= (fprintf(fp, "%s", buf) < 0);
4979 if (!first_to_address) {
4980 err |= (fprintf(fp, "\n") < 0);
4983 first_cc_address = TRUE;
4984 for (list = compose->header_list; list; list = list->next) {
4985 headerentry = ((ComposeHeaderEntry *)list->data);
4986 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4988 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4989 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4990 Xstrdup_a(str, strg, return -1);
4992 if (str[0] != '\0') {
4993 compose_convert_header
4994 (compose, buf, sizeof(buf), str,
4995 strlen("Resent-Cc") + 2, TRUE);
4997 if (first_cc_address) {
4998 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4999 first_cc_address = FALSE;
5001 err |= (fprintf(fp, ",") < 0);
5003 err |= (fprintf(fp, "%s", buf) < 0);
5007 if (!first_cc_address) {
5008 err |= (fprintf(fp, "\n") < 0);
5011 return (err ? -1:0);
5014 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5016 gchar buf[BUFFSIZE];
5018 const gchar *entstr;
5019 /* struct utsname utsbuf; */
5020 gboolean err = FALSE;
5022 cm_return_val_if_fail(fp != NULL, -1);
5023 cm_return_val_if_fail(compose->account != NULL, -1);
5024 cm_return_val_if_fail(compose->account->address != NULL, -1);
5027 get_rfc822_date(buf, sizeof(buf));
5028 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5031 if (compose->account->name && *compose->account->name) {
5032 compose_convert_header
5033 (compose, buf, sizeof(buf), compose->account->name,
5034 strlen("From: "), TRUE);
5035 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5036 buf, compose->account->address) < 0);
5038 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5041 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5042 if (*entstr != '\0') {
5043 Xstrdup_a(str, entstr, return -1);
5046 compose_convert_header(compose, buf, sizeof(buf), str,
5047 strlen("Subject: "), FALSE);
5048 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5052 /* Resent-Message-ID */
5053 if (compose->account->set_domain && compose->account->domain) {
5054 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5055 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5056 g_snprintf(buf, sizeof(buf), "%s",
5057 strchr(compose->account->address, '@') ?
5058 strchr(compose->account->address, '@')+1 :
5059 compose->account->address);
5061 g_snprintf(buf, sizeof(buf), "%s", "");
5064 if (compose->account->gen_msgid) {
5065 generate_msgid(buf, sizeof(buf));
5066 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5067 compose->msgid = g_strdup(buf);
5069 compose->msgid = NULL;
5072 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5075 /* separator between header and body */
5076 err |= (fputs("\n", fp) == EOF);
5078 return (err ? -1:0);
5081 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5085 gchar buf[BUFFSIZE];
5087 gboolean skip = FALSE;
5088 gboolean err = FALSE;
5089 gchar *not_included[]={
5090 "Return-Path:", "Delivered-To:", "Received:",
5091 "Subject:", "X-UIDL:", "AF:",
5092 "NF:", "PS:", "SRH:",
5093 "SFN:", "DSR:", "MID:",
5094 "CFG:", "PT:", "S:",
5095 "RQ:", "SSV:", "NSV:",
5096 "SSH:", "R:", "MAID:",
5097 "NAID:", "RMID:", "FMID:",
5098 "SCF:", "RRCPT:", "NG:",
5099 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5100 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5101 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5102 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5103 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5106 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5107 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5111 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5113 for (i = 0; not_included[i] != NULL; i++) {
5114 if (g_ascii_strncasecmp(buf, not_included[i],
5115 strlen(not_included[i])) == 0) {
5122 if (fputs(buf, fdest) == -1)
5125 if (!prefs_common.redirect_keep_from) {
5126 if (g_ascii_strncasecmp(buf, "From:",
5127 strlen("From:")) == 0) {
5128 err |= (fputs(" (by way of ", fdest) == EOF);
5129 if (compose->account->name
5130 && *compose->account->name) {
5131 compose_convert_header
5132 (compose, buf, sizeof(buf),
5133 compose->account->name,
5136 err |= (fprintf(fdest, "%s <%s>",
5138 compose->account->address) < 0);
5140 err |= (fprintf(fdest, "%s",
5141 compose->account->address) < 0);
5142 err |= (fputs(")", fdest) == EOF);
5146 if (fputs("\n", fdest) == -1)
5153 if (compose_redirect_write_headers(compose, fdest))
5156 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5157 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5170 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5172 GtkTextBuffer *buffer;
5173 GtkTextIter start, end;
5176 const gchar *out_codeset;
5177 EncodingType encoding = ENC_UNKNOWN;
5178 MimeInfo *mimemsg, *mimetext;
5180 const gchar *src_codeset = CS_INTERNAL;
5181 gchar *from_addr = NULL;
5182 gchar *from_name = NULL;
5184 if (action == COMPOSE_WRITE_FOR_SEND)
5185 attach_parts = TRUE;
5187 /* create message MimeInfo */
5188 mimemsg = procmime_mimeinfo_new();
5189 mimemsg->type = MIMETYPE_MESSAGE;
5190 mimemsg->subtype = g_strdup("rfc822");
5191 mimemsg->content = MIMECONTENT_MEM;
5192 mimemsg->tmp = TRUE; /* must free content later */
5193 mimemsg->data.mem = compose_get_header(compose);
5195 /* Create text part MimeInfo */
5196 /* get all composed text */
5197 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5198 gtk_text_buffer_get_start_iter(buffer, &start);
5199 gtk_text_buffer_get_end_iter(buffer, &end);
5200 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5202 out_codeset = conv_get_charset_str(compose->out_encoding);
5204 if (!out_codeset && is_ascii_str(chars)) {
5205 out_codeset = CS_US_ASCII;
5206 } else if (prefs_common.outgoing_fallback_to_ascii &&
5207 is_ascii_str(chars)) {
5208 out_codeset = CS_US_ASCII;
5209 encoding = ENC_7BIT;
5213 gchar *test_conv_global_out = NULL;
5214 gchar *test_conv_reply = NULL;
5216 /* automatic mode. be automatic. */
5217 codeconv_set_strict(TRUE);
5219 out_codeset = conv_get_outgoing_charset_str();
5221 debug_print("trying to convert to %s\n", out_codeset);
5222 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5225 if (!test_conv_global_out && compose->orig_charset
5226 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5227 out_codeset = compose->orig_charset;
5228 debug_print("failure; trying to convert to %s\n", out_codeset);
5229 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5232 if (!test_conv_global_out && !test_conv_reply) {
5234 out_codeset = CS_INTERNAL;
5235 debug_print("failure; finally using %s\n", out_codeset);
5237 g_free(test_conv_global_out);
5238 g_free(test_conv_reply);
5239 codeconv_set_strict(FALSE);
5242 if (encoding == ENC_UNKNOWN) {
5243 if (prefs_common.encoding_method == CTE_BASE64)
5244 encoding = ENC_BASE64;
5245 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5246 encoding = ENC_QUOTED_PRINTABLE;
5247 else if (prefs_common.encoding_method == CTE_8BIT)
5248 encoding = ENC_8BIT;
5250 encoding = procmime_get_encoding_for_charset(out_codeset);
5253 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5254 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5256 if (action == COMPOSE_WRITE_FOR_SEND) {
5257 codeconv_set_strict(TRUE);
5258 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5259 codeconv_set_strict(FALSE);
5265 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5266 "to the specified %s charset.\n"
5267 "Send it as %s?"), out_codeset, src_codeset);
5268 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5269 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5272 if (aval != G_ALERTALTERNATE) {
5277 out_codeset = src_codeset;
5283 out_codeset = src_codeset;
5288 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5289 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5290 strstr(buf, "\nFrom ") != NULL) {
5291 encoding = ENC_QUOTED_PRINTABLE;
5295 mimetext = procmime_mimeinfo_new();
5296 mimetext->content = MIMECONTENT_MEM;
5297 mimetext->tmp = TRUE; /* must free content later */
5298 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5299 * and free the data, which we need later. */
5300 mimetext->data.mem = g_strdup(buf);
5301 mimetext->type = MIMETYPE_TEXT;
5302 mimetext->subtype = g_strdup("plain");
5303 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5304 g_strdup(out_codeset));
5306 /* protect trailing spaces when signing message */
5307 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5308 privacy_system_can_sign(compose->privacy_system)) {
5309 encoding = ENC_QUOTED_PRINTABLE;
5312 debug_print("main text: %zd bytes encoded as %s in %d\n",
5313 strlen(buf), out_codeset, encoding);
5315 /* check for line length limit */
5316 if (action == COMPOSE_WRITE_FOR_SEND &&
5317 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5318 check_line_length(buf, 1000, &line) < 0) {
5322 msg = g_strdup_printf
5323 (_("Line %d exceeds the line length limit (998 bytes).\n"
5324 "The contents of the message might be broken on the way to the delivery.\n"
5326 "Send it anyway?"), line + 1);
5327 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5329 if (aval != G_ALERTALTERNATE) {
5335 if (encoding != ENC_UNKNOWN)
5336 procmime_encode_content(mimetext, encoding);
5338 /* append attachment parts */
5339 if (compose_use_attach(compose) && attach_parts) {
5340 MimeInfo *mimempart;
5341 gchar *boundary = NULL;
5342 mimempart = procmime_mimeinfo_new();
5343 mimempart->content = MIMECONTENT_EMPTY;
5344 mimempart->type = MIMETYPE_MULTIPART;
5345 mimempart->subtype = g_strdup("mixed");
5349 boundary = generate_mime_boundary(NULL);
5350 } while (strstr(buf, boundary) != NULL);
5352 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5355 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5357 g_node_append(mimempart->node, mimetext->node);
5358 g_node_append(mimemsg->node, mimempart->node);
5360 compose_add_attachments(compose, mimempart);
5362 g_node_append(mimemsg->node, mimetext->node);
5366 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5367 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5368 /* extract name and address */
5369 if (strstr(spec, " <") && strstr(spec, ">")) {
5370 from_addr = g_strdup(strrchr(spec, '<')+1);
5371 *(strrchr(from_addr, '>')) = '\0';
5372 from_name = g_strdup(spec);
5373 *(strrchr(from_name, '<')) = '\0';
5380 /* sign message if sending */
5381 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5382 privacy_system_can_sign(compose->privacy_system))
5383 if (!privacy_sign(compose->privacy_system, mimemsg,
5384 compose->account, from_addr)) {
5391 procmime_write_mimeinfo(mimemsg, fp);
5393 procmime_mimeinfo_free_all(mimemsg);
5398 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5400 GtkTextBuffer *buffer;
5401 GtkTextIter start, end;
5406 if ((fp = g_fopen(file, "wb")) == NULL) {
5407 FILE_OP_ERROR(file, "fopen");
5411 /* chmod for security */
5412 if (change_file_mode_rw(fp, file) < 0) {
5413 FILE_OP_ERROR(file, "chmod");
5414 g_warning("can't change file mode\n");
5417 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5418 gtk_text_buffer_get_start_iter(buffer, &start);
5419 gtk_text_buffer_get_end_iter(buffer, &end);
5420 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5422 chars = conv_codeset_strdup
5423 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5426 if (!chars) return -1;
5429 len = strlen(chars);
5430 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5431 FILE_OP_ERROR(file, "fwrite");
5440 if (fclose(fp) == EOF) {
5441 FILE_OP_ERROR(file, "fclose");
5448 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5451 MsgInfo *msginfo = compose->targetinfo;
5453 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5454 if (!msginfo) return -1;
5456 if (!force && MSG_IS_LOCKED(msginfo->flags))
5459 item = msginfo->folder;
5460 cm_return_val_if_fail(item != NULL, -1);
5462 if (procmsg_msg_exist(msginfo) &&
5463 (folder_has_parent_of_type(item, F_QUEUE) ||
5464 folder_has_parent_of_type(item, F_DRAFT)
5465 || msginfo == compose->autosaved_draft)) {
5466 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5467 g_warning("can't remove the old message\n");
5470 debug_print("removed reedit target %d\n", msginfo->msgnum);
5477 static void compose_remove_draft(Compose *compose)
5480 MsgInfo *msginfo = compose->targetinfo;
5481 drafts = account_get_special_folder(compose->account, F_DRAFT);
5483 if (procmsg_msg_exist(msginfo)) {
5484 folder_item_remove_msg(drafts, msginfo->msgnum);
5489 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5490 gboolean remove_reedit_target)
5492 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5495 static gboolean compose_warn_encryption(Compose *compose)
5497 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5498 AlertValue val = G_ALERTALTERNATE;
5500 if (warning == NULL)
5503 val = alertpanel_full(_("Encryption warning"), warning,
5504 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5505 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5506 if (val & G_ALERTDISABLE) {
5507 val &= ~G_ALERTDISABLE;
5508 if (val == G_ALERTALTERNATE)
5509 privacy_inhibit_encrypt_warning(compose->privacy_system,
5513 if (val == G_ALERTALTERNATE) {
5520 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5521 gchar **msgpath, gboolean check_subject,
5522 gboolean remove_reedit_target)
5529 static gboolean lock = FALSE;
5530 PrefsAccount *mailac = NULL, *newsac = NULL;
5531 gboolean err = FALSE;
5533 debug_print("queueing message...\n");
5534 cm_return_val_if_fail(compose->account != NULL, -1);
5538 if (compose_check_entries(compose, check_subject) == FALSE) {
5540 if (compose->batch) {
5541 gtk_widget_show_all(compose->window);
5546 if (!compose->to_list && !compose->newsgroup_list) {
5547 g_warning("can't get recipient list.");
5552 if (compose->to_list) {
5553 if (compose->account->protocol != A_NNTP)
5554 mailac = compose->account;
5555 else if (cur_account && cur_account->protocol != A_NNTP)
5556 mailac = cur_account;
5557 else if (!(mailac = compose_current_mail_account())) {
5559 alertpanel_error(_("No account for sending mails available!"));
5564 if (compose->newsgroup_list) {
5565 if (compose->account->protocol == A_NNTP)
5566 newsac = compose->account;
5569 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5574 /* write queue header */
5575 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5576 G_DIR_SEPARATOR, compose, (guint) rand());
5577 debug_print("queuing to %s\n", tmp);
5578 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5579 FILE_OP_ERROR(tmp, "fopen");
5585 if (change_file_mode_rw(fp, tmp) < 0) {
5586 FILE_OP_ERROR(tmp, "chmod");
5587 g_warning("can't change file mode\n");
5590 /* queueing variables */
5591 err |= (fprintf(fp, "AF:\n") < 0);
5592 err |= (fprintf(fp, "NF:0\n") < 0);
5593 err |= (fprintf(fp, "PS:10\n") < 0);
5594 err |= (fprintf(fp, "SRH:1\n") < 0);
5595 err |= (fprintf(fp, "SFN:\n") < 0);
5596 err |= (fprintf(fp, "DSR:\n") < 0);
5598 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5600 err |= (fprintf(fp, "MID:\n") < 0);
5601 err |= (fprintf(fp, "CFG:\n") < 0);
5602 err |= (fprintf(fp, "PT:0\n") < 0);
5603 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5604 err |= (fprintf(fp, "RQ:\n") < 0);
5606 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5608 err |= (fprintf(fp, "SSV:\n") < 0);
5610 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5612 err |= (fprintf(fp, "NSV:\n") < 0);
5613 err |= (fprintf(fp, "SSH:\n") < 0);
5614 /* write recepient list */
5615 if (compose->to_list) {
5616 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5617 for (cur = compose->to_list->next; cur != NULL;
5619 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5620 err |= (fprintf(fp, "\n") < 0);
5622 /* write newsgroup list */
5623 if (compose->newsgroup_list) {
5624 err |= (fprintf(fp, "NG:") < 0);
5625 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5626 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5627 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5628 err |= (fprintf(fp, "\n") < 0);
5630 /* Sylpheed account IDs */
5632 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5634 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5637 if (compose->privacy_system != NULL) {
5638 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5639 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5640 if (compose->use_encryption) {
5642 if (!compose_warn_encryption(compose)) {
5649 if (mailac && mailac->encrypt_to_self) {
5650 GSList *tmp_list = g_slist_copy(compose->to_list);
5651 tmp_list = g_slist_append(tmp_list, compose->account->address);
5652 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5653 g_slist_free(tmp_list);
5655 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5657 if (encdata != NULL) {
5658 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5659 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5660 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5662 } /* else we finally dont want to encrypt */
5664 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5665 /* and if encdata was null, it means there's been a problem in
5677 /* Save copy folder */
5678 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5679 gchar *savefolderid;
5681 savefolderid = compose_get_save_to(compose);
5682 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5683 g_free(savefolderid);
5685 /* Save copy folder */
5686 if (compose->return_receipt) {
5687 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5689 /* Message-ID of message replying to */
5690 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5693 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5694 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5697 /* Message-ID of message forwarding to */
5698 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5701 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5702 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5706 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5707 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5709 /* end of headers */
5710 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5712 if (compose->redirect_filename != NULL) {
5713 if (compose_redirect_write_to_file(compose, fp) < 0) {
5722 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5727 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5731 g_warning("failed to write queue message\n");
5738 if (fclose(fp) == EOF) {
5739 FILE_OP_ERROR(tmp, "fclose");
5746 if (item && *item) {
5749 queue = account_get_special_folder(compose->account, F_QUEUE);
5752 g_warning("can't find queue folder\n");
5758 folder_item_scan(queue);
5759 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5760 g_warning("can't queue the message\n");
5767 if (msgpath == NULL) {
5773 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5774 compose_remove_reedit_target(compose, FALSE);
5777 if ((msgnum != NULL) && (item != NULL)) {
5785 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5788 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5790 struct stat statbuf;
5791 gchar *type, *subtype;
5792 GtkTreeModel *model;
5795 model = gtk_tree_view_get_model(tree_view);
5797 if (!gtk_tree_model_get_iter_first(model, &iter))
5800 gtk_tree_model_get(model, &iter,
5804 mimepart = procmime_mimeinfo_new();
5805 mimepart->content = MIMECONTENT_FILE;
5806 mimepart->data.filename = g_strdup(ainfo->file);
5807 mimepart->tmp = FALSE; /* or we destroy our attachment */
5808 mimepart->offset = 0;
5810 g_stat(ainfo->file, &statbuf);
5811 mimepart->length = statbuf.st_size;
5813 type = g_strdup(ainfo->content_type);
5815 if (!strchr(type, '/')) {
5817 type = g_strdup("application/octet-stream");
5820 subtype = strchr(type, '/') + 1;
5821 *(subtype - 1) = '\0';
5822 mimepart->type = procmime_get_media_type(type);
5823 mimepart->subtype = g_strdup(subtype);
5826 if (mimepart->type == MIMETYPE_MESSAGE &&
5827 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5828 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5831 if (mimepart->type == MIMETYPE_APPLICATION &&
5832 !strcmp2(mimepart->subtype, "octet-stream"))
5833 g_hash_table_insert(mimepart->typeparameters,
5834 g_strdup("name"), g_strdup(ainfo->name));
5835 g_hash_table_insert(mimepart->dispositionparameters,
5836 g_strdup("filename"), g_strdup(ainfo->name));
5837 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5841 if (compose->use_signing) {
5842 if (ainfo->encoding == ENC_7BIT)
5843 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5844 else if (ainfo->encoding == ENC_8BIT)
5845 ainfo->encoding = ENC_BASE64;
5848 procmime_encode_content(mimepart, ainfo->encoding);
5850 g_node_append(parent->node, mimepart->node);
5851 } while (gtk_tree_model_iter_next(model, &iter));
5854 #define IS_IN_CUSTOM_HEADER(header) \
5855 (compose->account->add_customhdr && \
5856 custom_header_find(compose->account->customhdr_list, header) != NULL)
5858 static void compose_add_headerfield_from_headerlist(Compose *compose,
5860 const gchar *fieldname,
5861 const gchar *seperator)
5863 gchar *str, *fieldname_w_colon;
5864 gboolean add_field = FALSE;
5866 ComposeHeaderEntry *headerentry;
5867 const gchar *headerentryname;
5868 const gchar *trans_fieldname;
5871 if (IS_IN_CUSTOM_HEADER(fieldname))
5874 debug_print("Adding %s-fields\n", fieldname);
5876 fieldstr = g_string_sized_new(64);
5878 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5879 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5881 for (list = compose->header_list; list; list = list->next) {
5882 headerentry = ((ComposeHeaderEntry *)list->data);
5883 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5885 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5886 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5888 if (str[0] != '\0') {
5890 g_string_append(fieldstr, seperator);
5891 g_string_append(fieldstr, str);
5900 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5901 compose_convert_header
5902 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5903 strlen(fieldname) + 2, TRUE);
5904 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5908 g_free(fieldname_w_colon);
5909 g_string_free(fieldstr, TRUE);
5914 static gchar *compose_get_header(Compose *compose)
5916 gchar buf[BUFFSIZE];
5917 const gchar *entry_str;
5921 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5923 gchar *from_name = NULL, *from_address = NULL;
5926 cm_return_val_if_fail(compose->account != NULL, NULL);
5927 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5929 header = g_string_sized_new(64);
5932 get_rfc822_date(buf, sizeof(buf));
5933 g_string_append_printf(header, "Date: %s\n", buf);
5937 if (compose->account->name && *compose->account->name) {
5939 QUOTE_IF_REQUIRED(buf, compose->account->name);
5940 tmp = g_strdup_printf("%s <%s>",
5941 buf, compose->account->address);
5943 tmp = g_strdup_printf("%s",
5944 compose->account->address);
5946 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5947 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5949 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5950 from_address = g_strdup(compose->account->address);
5952 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5953 /* extract name and address */
5954 if (strstr(spec, " <") && strstr(spec, ">")) {
5955 from_address = g_strdup(strrchr(spec, '<')+1);
5956 *(strrchr(from_address, '>')) = '\0';
5957 from_name = g_strdup(spec);
5958 *(strrchr(from_name, '<')) = '\0';
5961 from_address = g_strdup(spec);
5968 if (from_name && *from_name) {
5969 compose_convert_header
5970 (compose, buf, sizeof(buf), from_name,
5971 strlen("From: "), TRUE);
5972 QUOTE_IF_REQUIRED(name, buf);
5974 g_string_append_printf(header, "From: %s <%s>\n",
5975 name, from_address);
5977 g_string_append_printf(header, "From: %s\n", from_address);
5980 g_free(from_address);
5983 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5986 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5989 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5992 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5995 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5997 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6000 compose_convert_header(compose, buf, sizeof(buf), str,
6001 strlen("Subject: "), FALSE);
6002 g_string_append_printf(header, "Subject: %s\n", buf);
6008 if (compose->account->set_domain && compose->account->domain) {
6009 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6010 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6011 g_snprintf(buf, sizeof(buf), "%s",
6012 strchr(compose->account->address, '@') ?
6013 strchr(compose->account->address, '@')+1 :
6014 compose->account->address);
6016 g_snprintf(buf, sizeof(buf), "%s", "");
6019 if (compose->account->gen_msgid) {
6020 generate_msgid(buf, sizeof(buf));
6021 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6022 compose->msgid = g_strdup(buf);
6024 compose->msgid = NULL;
6027 if (compose->remove_references == FALSE) {
6029 if (compose->inreplyto && compose->to_list)
6030 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6033 if (compose->references)
6034 g_string_append_printf(header, "References: %s\n", compose->references);
6038 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6041 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6044 if (compose->account->organization &&
6045 strlen(compose->account->organization) &&
6046 !IS_IN_CUSTOM_HEADER("Organization")) {
6047 compose_convert_header(compose, buf, sizeof(buf),
6048 compose->account->organization,
6049 strlen("Organization: "), FALSE);
6050 g_string_append_printf(header, "Organization: %s\n", buf);
6053 /* Program version and system info */
6054 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6055 !compose->newsgroup_list) {
6056 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6058 gtk_major_version, gtk_minor_version, gtk_micro_version,
6061 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6062 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6064 gtk_major_version, gtk_minor_version, gtk_micro_version,
6068 /* custom headers */
6069 if (compose->account->add_customhdr) {
6072 for (cur = compose->account->customhdr_list; cur != NULL;
6074 CustomHeader *chdr = (CustomHeader *)cur->data;
6076 if (custom_header_is_allowed(chdr->name)) {
6077 compose_convert_header
6078 (compose, buf, sizeof(buf),
6079 chdr->value ? chdr->value : "",
6080 strlen(chdr->name) + 2, FALSE);
6081 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6086 /* Automatic Faces and X-Faces */
6087 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6088 g_string_append_printf(header, "X-Face: %s\n", buf);
6090 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6091 g_string_append_printf(header, "X-Face: %s\n", buf);
6093 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6094 g_string_append_printf(header, "Face: %s\n", buf);
6096 else if (get_default_face (buf, sizeof(buf)) == 0) {
6097 g_string_append_printf(header, "Face: %s\n", buf);
6101 switch (compose->priority) {
6102 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6103 "X-Priority: 1 (Highest)\n");
6105 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6106 "X-Priority: 2 (High)\n");
6108 case PRIORITY_NORMAL: break;
6109 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6110 "X-Priority: 4 (Low)\n");
6112 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6113 "X-Priority: 5 (Lowest)\n");
6115 default: debug_print("compose: priority unknown : %d\n",
6119 /* Request Return Receipt */
6120 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6121 if (compose->return_receipt) {
6122 if (compose->account->name
6123 && *compose->account->name) {
6124 compose_convert_header(compose, buf, sizeof(buf),
6125 compose->account->name,
6126 strlen("Disposition-Notification-To: "),
6128 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6130 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6134 /* get special headers */
6135 for (list = compose->header_list; list; list = list->next) {
6136 ComposeHeaderEntry *headerentry;
6139 gchar *headername_wcolon;
6140 const gchar *headername_trans;
6143 gboolean standard_header = FALSE;
6145 headerentry = ((ComposeHeaderEntry *)list->data);
6147 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6149 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6154 if (!strstr(tmp, ":")) {
6155 headername_wcolon = g_strconcat(tmp, ":", NULL);
6156 headername = g_strdup(tmp);
6158 headername_wcolon = g_strdup(tmp);
6159 headername = g_strdup(strtok(tmp, ":"));
6163 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6164 Xstrdup_a(headervalue, entry_str, return NULL);
6165 subst_char(headervalue, '\r', ' ');
6166 subst_char(headervalue, '\n', ' ');
6167 string = std_headers;
6168 while (*string != NULL) {
6169 headername_trans = prefs_common_translated_header_name(*string);
6170 if (!strcmp(headername_trans, headername_wcolon))
6171 standard_header = TRUE;
6174 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6175 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6178 g_free(headername_wcolon);
6182 g_string_free(header, FALSE);
6187 #undef IS_IN_CUSTOM_HEADER
6189 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6190 gint header_len, gboolean addr_field)
6192 gchar *tmpstr = NULL;
6193 const gchar *out_codeset = NULL;
6195 cm_return_if_fail(src != NULL);
6196 cm_return_if_fail(dest != NULL);
6198 if (len < 1) return;
6200 tmpstr = g_strdup(src);
6202 subst_char(tmpstr, '\n', ' ');
6203 subst_char(tmpstr, '\r', ' ');
6206 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6207 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6208 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6213 codeconv_set_strict(TRUE);
6214 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6215 conv_get_charset_str(compose->out_encoding));
6216 codeconv_set_strict(FALSE);
6218 if (!dest || *dest == '\0') {
6219 gchar *test_conv_global_out = NULL;
6220 gchar *test_conv_reply = NULL;
6222 /* automatic mode. be automatic. */
6223 codeconv_set_strict(TRUE);
6225 out_codeset = conv_get_outgoing_charset_str();
6227 debug_print("trying to convert to %s\n", out_codeset);
6228 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6231 if (!test_conv_global_out && compose->orig_charset
6232 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6233 out_codeset = compose->orig_charset;
6234 debug_print("failure; trying to convert to %s\n", out_codeset);
6235 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6238 if (!test_conv_global_out && !test_conv_reply) {
6240 out_codeset = CS_INTERNAL;
6241 debug_print("finally using %s\n", out_codeset);
6243 g_free(test_conv_global_out);
6244 g_free(test_conv_reply);
6245 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6247 codeconv_set_strict(FALSE);
6252 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6256 cm_return_if_fail(user_data != NULL);
6258 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6259 g_strstrip(address);
6260 if (*address != '\0') {
6261 gchar *name = procheader_get_fromname(address);
6262 extract_address(address);
6263 addressbook_add_contact(name, address, NULL, NULL);
6268 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6270 GtkWidget *menuitem;
6273 cm_return_if_fail(menu != NULL);
6274 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6276 menuitem = gtk_separator_menu_item_new();
6277 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6278 gtk_widget_show(menuitem);
6280 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6281 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6283 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6284 g_strstrip(address);
6285 if (*address == '\0') {
6286 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6289 g_signal_connect(G_OBJECT(menuitem), "activate",
6290 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6291 gtk_widget_show(menuitem);
6294 static void compose_create_header_entry(Compose *compose)
6296 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6301 const gchar *header = NULL;
6302 ComposeHeaderEntry *headerentry;
6303 gboolean standard_header = FALSE;
6304 #if !(GTK_CHECK_VERSION(2,12,0))
6305 GtkTooltips *tips = compose->tooltips;
6308 headerentry = g_new0(ComposeHeaderEntry, 1);
6311 combo = gtk_combo_box_entry_new_text();
6313 while(*string != NULL) {
6314 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6315 (gchar*)prefs_common_translated_header_name(*string));
6318 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6319 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6320 G_CALLBACK(compose_grab_focus_cb), compose);
6321 gtk_widget_show(combo);
6322 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6323 compose->header_nextrow, compose->header_nextrow+1,
6324 GTK_SHRINK, GTK_FILL, 0, 0);
6325 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6326 const gchar *last_header_entry = gtk_entry_get_text(
6327 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6329 while (*string != NULL) {
6330 if (!strcmp(*string, last_header_entry))
6331 standard_header = TRUE;
6334 if (standard_header)
6335 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6337 if (!compose->header_last || !standard_header) {
6338 switch(compose->account->protocol) {
6340 header = prefs_common_translated_header_name("Newsgroups:");
6343 header = prefs_common_translated_header_name("To:");
6348 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6350 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6351 G_CALLBACK(compose_grab_focus_cb), compose);
6354 entry = gtk_entry_new();
6355 gtk_widget_show(entry);
6356 CLAWS_SET_TIP(entry,
6357 _("Use <tab> to autocomplete from addressbook"));
6358 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6359 compose->header_nextrow, compose->header_nextrow+1,
6360 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6362 g_signal_connect(G_OBJECT(entry), "key-press-event",
6363 G_CALLBACK(compose_headerentry_key_press_event_cb),
6365 g_signal_connect(G_OBJECT(entry), "changed",
6366 G_CALLBACK(compose_headerentry_changed_cb),
6368 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6369 G_CALLBACK(compose_grab_focus_cb), compose);
6372 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6373 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6374 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6375 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6376 G_CALLBACK(compose_header_drag_received_cb),
6378 g_signal_connect(G_OBJECT(entry), "drag-drop",
6379 G_CALLBACK(compose_drag_drop),
6381 g_signal_connect(G_OBJECT(entry), "populate-popup",
6382 G_CALLBACK(compose_entry_popup_extend),
6385 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6387 headerentry->compose = compose;
6388 headerentry->combo = combo;
6389 headerentry->entry = entry;
6390 headerentry->headernum = compose->header_nextrow;
6392 compose->header_nextrow++;
6393 compose->header_last = headerentry;
6394 compose->header_list =
6395 g_slist_append(compose->header_list,
6399 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6401 ComposeHeaderEntry *last_header;
6403 last_header = compose->header_last;
6405 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6406 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6409 static void compose_remove_header_entries(Compose *compose)
6412 for (list = compose->header_list; list; list = list->next) {
6413 ComposeHeaderEntry *headerentry =
6414 (ComposeHeaderEntry *)list->data;
6415 gtk_widget_destroy(headerentry->combo);
6416 gtk_widget_destroy(headerentry->entry);
6417 g_free(headerentry);
6419 compose->header_last = NULL;
6420 g_slist_free(compose->header_list);
6421 compose->header_list = NULL;
6422 compose->header_nextrow = 1;
6423 compose_create_header_entry(compose);
6426 static GtkWidget *compose_create_header(Compose *compose)
6428 GtkWidget *from_optmenu_hbox;
6429 GtkWidget *header_scrolledwin;
6430 GtkWidget *header_table;
6434 /* header labels and entries */
6435 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6436 gtk_widget_show(header_scrolledwin);
6437 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6439 header_table = gtk_table_new(2, 2, FALSE);
6440 gtk_widget_show(header_table);
6441 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6442 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6443 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6446 /* option menu for selecting accounts */
6447 from_optmenu_hbox = compose_account_option_menu_create(compose);
6448 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6449 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6452 compose->header_table = header_table;
6453 compose->header_list = NULL;
6454 compose->header_nextrow = count;
6456 compose_create_header_entry(compose);
6458 compose->table = NULL;
6460 return header_scrolledwin ;
6463 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6465 Compose *compose = (Compose *)data;
6466 GdkEventButton event;
6469 event.time = gtk_get_current_event_time();
6471 return attach_button_pressed(compose->attach_clist, &event, compose);
6474 static GtkWidget *compose_create_attach(Compose *compose)
6476 GtkWidget *attach_scrwin;
6477 GtkWidget *attach_clist;
6479 GtkListStore *store;
6480 GtkCellRenderer *renderer;
6481 GtkTreeViewColumn *column;
6482 GtkTreeSelection *selection;
6484 /* attachment list */
6485 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6486 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6487 GTK_POLICY_AUTOMATIC,
6488 GTK_POLICY_AUTOMATIC);
6489 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6491 store = gtk_list_store_new(N_ATTACH_COLS,
6496 G_TYPE_AUTO_POINTER,
6498 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6499 (GTK_TREE_MODEL(store)));
6500 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6501 g_object_unref(store);
6503 renderer = gtk_cell_renderer_text_new();
6504 column = gtk_tree_view_column_new_with_attributes
6505 (_("Mime type"), renderer, "text",
6506 COL_MIMETYPE, NULL);
6507 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6509 renderer = gtk_cell_renderer_text_new();
6510 column = gtk_tree_view_column_new_with_attributes
6511 (_("Size"), renderer, "text",
6513 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6515 renderer = gtk_cell_renderer_text_new();
6516 column = gtk_tree_view_column_new_with_attributes
6517 (_("Name"), renderer, "text",
6519 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6521 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6522 prefs_common.use_stripes_everywhere);
6523 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6524 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6526 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6527 G_CALLBACK(attach_selected), compose);
6528 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6529 G_CALLBACK(attach_button_pressed), compose);
6531 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6532 G_CALLBACK(popup_attach_button_pressed), compose);
6534 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6535 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6536 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6537 G_CALLBACK(popup_attach_button_pressed), compose);
6539 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6540 G_CALLBACK(attach_key_pressed), compose);
6543 gtk_drag_dest_set(attach_clist,
6544 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6545 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6546 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6547 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6548 G_CALLBACK(compose_attach_drag_received_cb),
6550 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6551 G_CALLBACK(compose_drag_drop),
6554 compose->attach_scrwin = attach_scrwin;
6555 compose->attach_clist = attach_clist;
6557 return attach_scrwin;
6560 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6561 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6563 static GtkWidget *compose_create_others(Compose *compose)
6566 GtkWidget *savemsg_checkbtn;
6567 GtkWidget *savemsg_combo;
6568 GtkWidget *savemsg_select;
6571 gchar *folderidentifier;
6573 /* Table for settings */
6574 table = gtk_table_new(3, 1, FALSE);
6575 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6576 gtk_widget_show(table);
6577 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6580 /* Save Message to folder */
6581 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6582 gtk_widget_show(savemsg_checkbtn);
6583 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6584 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6585 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6587 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6588 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6590 savemsg_combo = gtk_combo_box_entry_new_text();
6591 compose->savemsg_checkbtn = savemsg_checkbtn;
6592 compose->savemsg_combo = savemsg_combo;
6593 gtk_widget_show(savemsg_combo);
6595 if (prefs_common.compose_save_to_history)
6596 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6597 prefs_common.compose_save_to_history);
6599 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6600 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6601 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6602 G_CALLBACK(compose_grab_focus_cb), compose);
6603 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6604 folderidentifier = folder_item_get_identifier(account_get_special_folder
6605 (compose->account, F_OUTBOX));
6606 compose_set_save_to(compose, folderidentifier);
6607 g_free(folderidentifier);
6610 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6611 gtk_widget_show(savemsg_select);
6612 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6613 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6614 G_CALLBACK(compose_savemsg_select_cb),
6622 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6624 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6625 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6628 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6633 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6636 path = folder_item_get_identifier(dest);
6638 compose_set_save_to(compose, path);
6642 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6643 GdkAtom clip, GtkTextIter *insert_place);
6646 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6650 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6652 if (event->button == 3) {
6654 GtkTextIter sel_start, sel_end;
6655 gboolean stuff_selected;
6657 /* move the cursor to allow GtkAspell to check the word
6658 * under the mouse */
6659 if (event->x && event->y) {
6660 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6661 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6663 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6666 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6667 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6670 stuff_selected = gtk_text_buffer_get_selection_bounds(
6672 &sel_start, &sel_end);
6674 gtk_text_buffer_place_cursor (buffer, &iter);
6675 /* reselect stuff */
6677 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6678 gtk_text_buffer_select_range(buffer,
6679 &sel_start, &sel_end);
6681 return FALSE; /* pass the event so that the right-click goes through */
6684 if (event->button == 2) {
6689 /* get the middle-click position to paste at the correct place */
6690 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6691 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6693 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6696 entry_paste_clipboard(compose, text,
6697 prefs_common.linewrap_pastes,
6698 GDK_SELECTION_PRIMARY, &iter);
6706 static void compose_spell_menu_changed(void *data)
6708 Compose *compose = (Compose *)data;
6710 GtkWidget *menuitem;
6711 GtkWidget *parent_item;
6712 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6715 if (compose->gtkaspell == NULL)
6718 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6719 "/Menu/Spelling/Options");
6721 /* setting the submenu removes /Spelling/Options from the factory
6722 * so we need to save it */
6724 if (parent_item == NULL) {
6725 parent_item = compose->aspell_options_menu;
6726 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6728 compose->aspell_options_menu = parent_item;
6730 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6732 spell_menu = g_slist_reverse(spell_menu);
6733 for (items = spell_menu;
6734 items; items = items->next) {
6735 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6736 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6737 gtk_widget_show(GTK_WIDGET(menuitem));
6739 g_slist_free(spell_menu);
6741 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6742 gtk_widget_show(parent_item);
6745 static void compose_dict_changed(void *data)
6747 Compose *compose = (Compose *) data;
6749 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6752 gtkaspell_highlight_all(compose->gtkaspell);
6753 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6757 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6759 Compose *compose = (Compose *)data;
6760 GdkEventButton event;
6763 event.time = gtk_get_current_event_time();
6767 return text_clicked(compose->text, &event, compose);
6770 static gboolean compose_force_window_origin = TRUE;
6771 static Compose *compose_create(PrefsAccount *account,
6780 GtkWidget *handlebox;
6782 GtkWidget *notebook;
6784 GtkWidget *attach_hbox;
6785 GtkWidget *attach_lab1;
6786 GtkWidget *attach_lab2;
6791 GtkWidget *subject_hbox;
6792 GtkWidget *subject_frame;
6793 GtkWidget *subject_entry;
6797 GtkWidget *edit_vbox;
6798 GtkWidget *ruler_hbox;
6800 GtkWidget *scrolledwin;
6802 GtkTextBuffer *buffer;
6803 GtkClipboard *clipboard;
6806 UndoMain *undostruct;
6808 gchar *titles[N_ATTACH_COLS];
6809 GtkWidget *popupmenu;
6810 GtkWidget *tmpl_menu;
6811 GtkActionGroup *action_group = NULL;
6814 GtkAspell * gtkaspell = NULL;
6817 static GdkGeometry geometry;
6819 cm_return_val_if_fail(account != NULL, NULL);
6821 debug_print("Creating compose window...\n");
6822 compose = g_new0(Compose, 1);
6824 titles[COL_MIMETYPE] = _("MIME type");
6825 titles[COL_SIZE] = _("Size");
6826 titles[COL_NAME] = _("Name");
6828 compose->batch = batch;
6829 compose->account = account;
6830 compose->folder = folder;
6832 compose->mutex = g_mutex_new();
6833 compose->set_cursor_pos = -1;
6835 #if !(GTK_CHECK_VERSION(2,12,0))
6836 compose->tooltips = tips;
6839 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6841 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6842 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6844 if (!geometry.max_width) {
6845 geometry.max_width = gdk_screen_width();
6846 geometry.max_height = gdk_screen_height();
6849 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6850 &geometry, GDK_HINT_MAX_SIZE);
6851 if (!geometry.min_width) {
6852 geometry.min_width = 600;
6853 geometry.min_height = 440;
6855 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6856 &geometry, GDK_HINT_MIN_SIZE);
6858 #ifndef GENERIC_UMPC
6859 if (compose_force_window_origin)
6860 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6861 prefs_common.compose_y);
6863 g_signal_connect(G_OBJECT(window), "delete_event",
6864 G_CALLBACK(compose_delete_cb), compose);
6865 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6866 gtk_widget_realize(window);
6868 gtkut_widget_set_composer_icon(window);
6870 vbox = gtk_vbox_new(FALSE, 0);
6871 gtk_container_add(GTK_CONTAINER(window), vbox);
6873 compose->ui_manager = gtk_ui_manager_new();
6874 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6875 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6876 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6877 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6878 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6879 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6880 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6881 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6882 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6883 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6888 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6894 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6901 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6924 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6930 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6946 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6973 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6988 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6990 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6991 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6996 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6999 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7000 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7002 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7003 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)
7004 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)
7005 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7010 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)
7011 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)
7013 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7016 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)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7020 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)
7021 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7025 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7026 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)
7027 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7032 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)
7033 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)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7037 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7043 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7045 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)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7063 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7064 gtk_widget_show_all(menubar);
7066 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7068 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7070 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7073 if (prefs_common.toolbar_detachable) {
7074 handlebox = gtk_handle_box_new();
7076 handlebox = gtk_hbox_new(FALSE, 0);
7078 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7080 gtk_widget_realize(handlebox);
7082 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7085 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7089 vbox2 = gtk_vbox_new(FALSE, 2);
7090 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7091 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7094 notebook = gtk_notebook_new();
7095 gtk_widget_set_size_request(notebook, -1, 130);
7096 gtk_widget_show(notebook);
7098 /* header labels and entries */
7099 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7100 compose_create_header(compose),
7101 gtk_label_new_with_mnemonic(_("Hea_der")));
7102 /* attachment list */
7103 attach_hbox = gtk_hbox_new(FALSE, 0);
7104 gtk_widget_show(attach_hbox);
7106 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7107 gtk_widget_show(attach_lab1);
7108 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7110 attach_lab2 = gtk_label_new("");
7111 gtk_widget_show(attach_lab2);
7112 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7114 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7115 compose_create_attach(compose),
7118 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7119 compose_create_others(compose),
7120 gtk_label_new_with_mnemonic(_("Othe_rs")));
7123 subject_hbox = gtk_hbox_new(FALSE, 0);
7124 gtk_widget_show(subject_hbox);
7126 subject_frame = gtk_frame_new(NULL);
7127 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7128 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7129 gtk_widget_show(subject_frame);
7131 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7132 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7133 gtk_widget_show(subject);
7135 label = gtk_label_new(_("Subject:"));
7136 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7137 gtk_widget_show(label);
7140 subject_entry = claws_spell_entry_new();
7142 subject_entry = gtk_entry_new();
7144 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7145 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7146 G_CALLBACK(compose_grab_focus_cb), compose);
7147 gtk_widget_show(subject_entry);
7148 compose->subject_entry = subject_entry;
7149 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7151 edit_vbox = gtk_vbox_new(FALSE, 0);
7153 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7156 ruler_hbox = gtk_hbox_new(FALSE, 0);
7157 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7159 ruler = gtk_shruler_new();
7160 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7161 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7165 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7166 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7167 GTK_POLICY_AUTOMATIC,
7168 GTK_POLICY_AUTOMATIC);
7169 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7171 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7172 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7174 text = gtk_text_view_new();
7175 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7176 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7177 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7178 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7179 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7181 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7183 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7184 G_CALLBACK(compose_edit_size_alloc),
7186 g_signal_connect(G_OBJECT(buffer), "changed",
7187 G_CALLBACK(compose_changed_cb), compose);
7188 g_signal_connect(G_OBJECT(text), "grab_focus",
7189 G_CALLBACK(compose_grab_focus_cb), compose);
7190 g_signal_connect(G_OBJECT(buffer), "insert_text",
7191 G_CALLBACK(text_inserted), compose);
7192 g_signal_connect(G_OBJECT(text), "button_press_event",
7193 G_CALLBACK(text_clicked), compose);
7195 g_signal_connect(G_OBJECT(text), "popup-menu",
7196 G_CALLBACK(compose_popup_menu), compose);
7198 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7199 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7200 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7201 G_CALLBACK(compose_popup_menu), compose);
7203 g_signal_connect(G_OBJECT(subject_entry), "changed",
7204 G_CALLBACK(compose_changed_cb), compose);
7207 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7208 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7209 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7210 g_signal_connect(G_OBJECT(text), "drag_data_received",
7211 G_CALLBACK(compose_insert_drag_received_cb),
7213 g_signal_connect(G_OBJECT(text), "drag-drop",
7214 G_CALLBACK(compose_drag_drop),
7216 gtk_widget_show_all(vbox);
7218 /* pane between attach clist and text */
7219 paned = gtk_vpaned_new();
7220 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7222 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7223 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7225 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7227 gtk_paned_add1(GTK_PANED(paned), notebook);
7228 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7229 gtk_widget_show_all(paned);
7232 if (prefs_common.textfont) {
7233 PangoFontDescription *font_desc;
7235 font_desc = pango_font_description_from_string
7236 (prefs_common.textfont);
7238 gtk_widget_modify_font(text, font_desc);
7239 pango_font_description_free(font_desc);
7243 gtk_action_group_add_actions(action_group, compose_popup_entries,
7244 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7252 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7254 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7255 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7256 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7258 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7260 undostruct = undo_init(text);
7261 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7264 address_completion_start(window);
7266 compose->window = window;
7267 compose->vbox = vbox;
7268 compose->menubar = menubar;
7269 compose->handlebox = handlebox;
7271 compose->vbox2 = vbox2;
7273 compose->paned = paned;
7275 compose->attach_label = attach_lab2;
7277 compose->notebook = notebook;
7278 compose->edit_vbox = edit_vbox;
7279 compose->ruler_hbox = ruler_hbox;
7280 compose->ruler = ruler;
7281 compose->scrolledwin = scrolledwin;
7282 compose->text = text;
7284 compose->focused_editable = NULL;
7286 compose->popupmenu = popupmenu;
7288 compose->tmpl_menu = tmpl_menu;
7290 compose->mode = mode;
7291 compose->rmode = mode;
7293 compose->targetinfo = NULL;
7294 compose->replyinfo = NULL;
7295 compose->fwdinfo = NULL;
7297 compose->replyto = NULL;
7299 compose->bcc = NULL;
7300 compose->followup_to = NULL;
7302 compose->ml_post = NULL;
7304 compose->inreplyto = NULL;
7305 compose->references = NULL;
7306 compose->msgid = NULL;
7307 compose->boundary = NULL;
7309 compose->autowrap = prefs_common.autowrap;
7310 compose->autoindent = prefs_common.auto_indent;
7311 compose->use_signing = FALSE;
7312 compose->use_encryption = FALSE;
7313 compose->privacy_system = NULL;
7315 compose->modified = FALSE;
7317 compose->return_receipt = FALSE;
7319 compose->to_list = NULL;
7320 compose->newsgroup_list = NULL;
7322 compose->undostruct = undostruct;
7324 compose->sig_str = NULL;
7326 compose->exteditor_file = NULL;
7327 compose->exteditor_pid = -1;
7328 compose->exteditor_tag = -1;
7329 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7332 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7333 if (mode != COMPOSE_REDIRECT) {
7334 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7335 strcmp(prefs_common.dictionary, "")) {
7336 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7337 prefs_common.alt_dictionary,
7338 conv_get_locale_charset_str(),
7339 prefs_common.misspelled_col,
7340 prefs_common.check_while_typing,
7341 prefs_common.recheck_when_changing_dict,
7342 prefs_common.use_alternate,
7343 prefs_common.use_both_dicts,
7344 GTK_TEXT_VIEW(text),
7345 GTK_WINDOW(compose->window),
7346 compose_dict_changed,
7347 compose_spell_menu_changed,
7350 alertpanel_error(_("Spell checker could not "
7352 gtkaspell_checkers_strerror());
7353 gtkaspell_checkers_reset_error();
7355 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7359 compose->gtkaspell = gtkaspell;
7360 compose_spell_menu_changed(compose);
7361 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7364 compose_select_account(compose, account, TRUE);
7366 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7367 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7369 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7370 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7372 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7373 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7375 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7376 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7378 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7379 if (account->protocol != A_NNTP)
7380 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7381 prefs_common_translated_header_name("To:"));
7383 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7384 prefs_common_translated_header_name("Newsgroups:"));
7386 addressbook_set_target_compose(compose);
7388 if (mode != COMPOSE_REDIRECT)
7389 compose_set_template_menu(compose);
7391 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7394 compose_list = g_list_append(compose_list, compose);
7396 if (!prefs_common.show_ruler)
7397 gtk_widget_hide(ruler_hbox);
7399 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7402 compose->priority = PRIORITY_NORMAL;
7403 compose_update_priority_menu_item(compose);
7405 compose_set_out_encoding(compose);
7408 compose_update_actions_menu(compose);
7410 /* Privacy Systems menu */
7411 compose_update_privacy_systems_menu(compose);
7413 activate_privacy_system(compose, account, TRUE);
7414 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7416 gtk_widget_realize(window);
7418 gtk_widget_show(window);
7420 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7421 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7428 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7433 GtkWidget *optmenubox;
7436 GtkWidget *from_name = NULL;
7437 #if !(GTK_CHECK_VERSION(2,12,0))
7438 GtkTooltips *tips = compose->tooltips;
7441 gint num = 0, def_menu = 0;
7443 accounts = account_get_list();
7444 cm_return_val_if_fail(accounts != NULL, NULL);
7446 optmenubox = gtk_event_box_new();
7447 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7448 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7450 hbox = gtk_hbox_new(FALSE, 6);
7451 from_name = gtk_entry_new();
7453 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7454 G_CALLBACK(compose_grab_focus_cb), compose);
7456 for (; accounts != NULL; accounts = accounts->next, num++) {
7457 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7458 gchar *name, *from = NULL;
7460 if (ac == compose->account) def_menu = num;
7462 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7465 if (ac == compose->account) {
7466 if (ac->name && *ac->name) {
7468 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7469 from = g_strdup_printf("%s <%s>",
7471 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7473 from = g_strdup_printf("%s",
7475 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7478 COMBOBOX_ADD(menu, name, ac->account_id);
7483 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7485 g_signal_connect(G_OBJECT(optmenu), "changed",
7486 G_CALLBACK(account_activated),
7488 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7489 G_CALLBACK(compose_entry_popup_extend),
7492 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7493 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7495 CLAWS_SET_TIP(optmenubox,
7496 _("Account to use for this email"));
7497 CLAWS_SET_TIP(from_name,
7498 _("Sender address to be used"));
7500 compose->from_name = from_name;
7505 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7507 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7508 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7509 Compose *compose = (Compose *) data;
7511 compose->priority = value;
7515 static void compose_reply_change_mode(Compose *compose,
7518 gboolean was_modified = compose->modified;
7520 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7522 cm_return_if_fail(compose->replyinfo != NULL);
7524 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7526 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7528 if (action == COMPOSE_REPLY_TO_ALL)
7530 if (action == COMPOSE_REPLY_TO_SENDER)
7532 if (action == COMPOSE_REPLY_TO_LIST)
7535 compose_remove_header_entries(compose);
7536 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7537 if (compose->account->set_autocc && compose->account->auto_cc)
7538 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7540 if (compose->account->set_autobcc && compose->account->auto_bcc)
7541 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7543 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7544 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7545 compose_show_first_last_header(compose, TRUE);
7546 compose->modified = was_modified;
7547 compose_set_title(compose);
7550 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7552 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7553 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7554 Compose *compose = (Compose *) data;
7557 compose_reply_change_mode(compose, value);
7560 static void compose_update_priority_menu_item(Compose * compose)
7562 GtkWidget *menuitem = NULL;
7563 switch (compose->priority) {
7564 case PRIORITY_HIGHEST:
7565 menuitem = gtk_ui_manager_get_widget
7566 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7569 menuitem = gtk_ui_manager_get_widget
7570 (compose->ui_manager, "/Menu/Options/Priority/High");
7572 case PRIORITY_NORMAL:
7573 menuitem = gtk_ui_manager_get_widget
7574 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7577 menuitem = gtk_ui_manager_get_widget
7578 (compose->ui_manager, "/Menu/Options/Priority/Low");
7580 case PRIORITY_LOWEST:
7581 menuitem = gtk_ui_manager_get_widget
7582 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7585 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7588 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7590 Compose *compose = (Compose *) data;
7592 gboolean can_sign = FALSE, can_encrypt = FALSE;
7594 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7596 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7599 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7600 g_free(compose->privacy_system);
7601 compose->privacy_system = NULL;
7602 if (systemid != NULL) {
7603 compose->privacy_system = g_strdup(systemid);
7605 can_sign = privacy_system_can_sign(systemid);
7606 can_encrypt = privacy_system_can_encrypt(systemid);
7609 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7611 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7612 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7615 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7617 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7618 GtkWidget *menuitem = NULL;
7620 gboolean can_sign = FALSE, can_encrypt = FALSE;
7621 gboolean found = FALSE;
7623 if (compose->privacy_system != NULL) {
7625 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7626 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7627 cm_return_if_fail(menuitem != NULL);
7629 amenu = GTK_MENU_SHELL(menuitem)->children;
7631 while (amenu != NULL) {
7632 GList *alist = amenu->next;
7634 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7635 if (systemid != NULL) {
7636 if (strcmp(systemid, compose->privacy_system) == 0 &&
7637 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7638 menuitem = GTK_WIDGET(amenu->data);
7640 can_sign = privacy_system_can_sign(systemid);
7641 can_encrypt = privacy_system_can_encrypt(systemid);
7645 } else if (strlen(compose->privacy_system) == 0 &&
7646 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7647 menuitem = GTK_WIDGET(amenu->data);
7650 can_encrypt = FALSE;
7657 if (menuitem != NULL)
7658 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7660 if (warn && !found && strlen(compose->privacy_system)) {
7661 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7662 "will not be able to sign or encrypt this message."),
7663 compose->privacy_system);
7667 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7668 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7671 static void compose_set_out_encoding(Compose *compose)
7673 CharSet out_encoding;
7674 const gchar *branch = NULL;
7675 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7677 switch(out_encoding) {
7678 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7679 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7680 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7681 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7682 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7683 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7684 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7685 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7686 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7687 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7688 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7689 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7690 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7691 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7692 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7693 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7694 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7695 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7696 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7697 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7698 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7699 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7700 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7701 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7702 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7703 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7704 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7705 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7706 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7707 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7708 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7709 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7711 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7714 static void compose_set_template_menu(Compose *compose)
7716 GSList *tmpl_list, *cur;
7720 tmpl_list = template_get_config();
7722 menu = gtk_menu_new();
7724 gtk_menu_set_accel_group (GTK_MENU (menu),
7725 gtk_ui_manager_get_accel_group(compose->ui_manager));
7726 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7727 Template *tmpl = (Template *)cur->data;
7728 gchar *accel_path = NULL;
7729 item = gtk_menu_item_new_with_label(tmpl->name);
7730 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7731 g_signal_connect(G_OBJECT(item), "activate",
7732 G_CALLBACK(compose_template_activate_cb),
7734 g_object_set_data(G_OBJECT(item), "template", tmpl);
7735 gtk_widget_show(item);
7736 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7737 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7741 gtk_widget_show(menu);
7742 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7745 void compose_update_actions_menu(Compose *compose)
7747 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7750 static void compose_update_privacy_systems_menu(Compose *compose)
7752 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7753 GSList *systems, *cur;
7755 GtkWidget *system_none;
7757 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7758 GtkWidget *privacy_menu = gtk_menu_new();
7760 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7761 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7763 g_signal_connect(G_OBJECT(system_none), "activate",
7764 G_CALLBACK(compose_set_privacy_system_cb), compose);
7766 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7767 gtk_widget_show(system_none);
7769 systems = privacy_get_system_ids();
7770 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7771 gchar *systemid = cur->data;
7773 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7774 widget = gtk_radio_menu_item_new_with_label(group,
7775 privacy_system_get_name(systemid));
7776 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7777 g_strdup(systemid), g_free);
7778 g_signal_connect(G_OBJECT(widget), "activate",
7779 G_CALLBACK(compose_set_privacy_system_cb), compose);
7781 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7782 gtk_widget_show(widget);
7785 g_slist_free(systems);
7786 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7787 gtk_widget_show_all(privacy_menu);
7788 gtk_widget_show_all(privacy_menuitem);
7791 void compose_reflect_prefs_all(void)
7796 for (cur = compose_list; cur != NULL; cur = cur->next) {
7797 compose = (Compose *)cur->data;
7798 compose_set_template_menu(compose);
7802 void compose_reflect_prefs_pixmap_theme(void)
7807 for (cur = compose_list; cur != NULL; cur = cur->next) {
7808 compose = (Compose *)cur->data;
7809 toolbar_update(TOOLBAR_COMPOSE, compose);
7813 static const gchar *compose_quote_char_from_context(Compose *compose)
7815 const gchar *qmark = NULL;
7817 cm_return_val_if_fail(compose != NULL, NULL);
7819 switch (compose->mode) {
7820 /* use forward-specific quote char */
7821 case COMPOSE_FORWARD:
7822 case COMPOSE_FORWARD_AS_ATTACH:
7823 case COMPOSE_FORWARD_INLINE:
7824 if (compose->folder && compose->folder->prefs &&
7825 compose->folder->prefs->forward_with_format)
7826 qmark = compose->folder->prefs->forward_quotemark;
7827 else if (compose->account->forward_with_format)
7828 qmark = compose->account->forward_quotemark;
7830 qmark = prefs_common.fw_quotemark;
7833 /* use reply-specific quote char in all other modes */
7835 if (compose->folder && compose->folder->prefs &&
7836 compose->folder->prefs->reply_with_format)
7837 qmark = compose->folder->prefs->reply_quotemark;
7838 else if (compose->account->reply_with_format)
7839 qmark = compose->account->reply_quotemark;
7841 qmark = prefs_common.quotemark;
7845 if (qmark == NULL || *qmark == '\0')
7851 static void compose_template_apply(Compose *compose, Template *tmpl,
7855 GtkTextBuffer *buffer;
7859 gchar *parsed_str = NULL;
7860 gint cursor_pos = 0;
7861 const gchar *err_msg = _("Template body format error at line %d.");
7864 /* process the body */
7866 text = GTK_TEXT_VIEW(compose->text);
7867 buffer = gtk_text_view_get_buffer(text);
7870 qmark = compose_quote_char_from_context(compose);
7872 if (compose->replyinfo != NULL) {
7875 gtk_text_buffer_set_text(buffer, "", -1);
7876 mark = gtk_text_buffer_get_insert(buffer);
7877 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7879 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7880 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7882 } else if (compose->fwdinfo != NULL) {
7885 gtk_text_buffer_set_text(buffer, "", -1);
7886 mark = gtk_text_buffer_get_insert(buffer);
7887 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7889 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7890 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7893 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7895 GtkTextIter start, end;
7898 gtk_text_buffer_get_start_iter(buffer, &start);
7899 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7900 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7902 /* clear the buffer now */
7904 gtk_text_buffer_set_text(buffer, "", -1);
7906 parsed_str = compose_quote_fmt(compose, dummyinfo,
7907 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7908 procmsg_msginfo_free( dummyinfo );
7914 gtk_text_buffer_set_text(buffer, "", -1);
7915 mark = gtk_text_buffer_get_insert(buffer);
7916 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7919 if (replace && parsed_str && compose->account->auto_sig)
7920 compose_insert_sig(compose, FALSE);
7922 if (replace && parsed_str) {
7923 gtk_text_buffer_get_start_iter(buffer, &iter);
7924 gtk_text_buffer_place_cursor(buffer, &iter);
7928 cursor_pos = quote_fmt_get_cursor_pos();
7929 compose->set_cursor_pos = cursor_pos;
7930 if (cursor_pos == -1)
7932 gtk_text_buffer_get_start_iter(buffer, &iter);
7933 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7934 gtk_text_buffer_place_cursor(buffer, &iter);
7937 /* process the other fields */
7939 compose_template_apply_fields(compose, tmpl);
7940 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7941 quote_fmt_reset_vartable();
7942 compose_changed_cb(NULL, compose);
7945 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7947 MsgInfo* dummyinfo = NULL;
7948 MsgInfo *msginfo = NULL;
7951 if (compose->replyinfo != NULL)
7952 msginfo = compose->replyinfo;
7953 else if (compose->fwdinfo != NULL)
7954 msginfo = compose->fwdinfo;
7956 dummyinfo = compose_msginfo_new_from_compose(compose);
7957 msginfo = dummyinfo;
7960 if (tmpl->from && *tmpl->from != '\0') {
7962 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7963 compose->gtkaspell);
7965 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7967 quote_fmt_scan_string(tmpl->from);
7970 buf = quote_fmt_get_buffer();
7972 alertpanel_error(_("Template From format error."));
7974 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7978 if (tmpl->to && *tmpl->to != '\0') {
7980 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7981 compose->gtkaspell);
7983 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7985 quote_fmt_scan_string(tmpl->to);
7988 buf = quote_fmt_get_buffer();
7990 alertpanel_error(_("Template To format error."));
7992 compose_entry_append(compose, buf, COMPOSE_TO);
7996 if (tmpl->cc && *tmpl->cc != '\0') {
7998 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7999 compose->gtkaspell);
8001 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8003 quote_fmt_scan_string(tmpl->cc);
8006 buf = quote_fmt_get_buffer();
8008 alertpanel_error(_("Template Cc format error."));
8010 compose_entry_append(compose, buf, COMPOSE_CC);
8014 if (tmpl->bcc && *tmpl->bcc != '\0') {
8016 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8017 compose->gtkaspell);
8019 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8021 quote_fmt_scan_string(tmpl->bcc);
8024 buf = quote_fmt_get_buffer();
8026 alertpanel_error(_("Template Bcc format error."));
8028 compose_entry_append(compose, buf, COMPOSE_BCC);
8032 /* process the subject */
8033 if (tmpl->subject && *tmpl->subject != '\0') {
8035 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8036 compose->gtkaspell);
8038 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8040 quote_fmt_scan_string(tmpl->subject);
8043 buf = quote_fmt_get_buffer();
8045 alertpanel_error(_("Template subject format error."));
8047 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8051 procmsg_msginfo_free( dummyinfo );
8054 static void compose_destroy(Compose *compose)
8056 GtkTextBuffer *buffer;
8057 GtkClipboard *clipboard;
8059 compose_list = g_list_remove(compose_list, compose);
8061 if (compose->updating) {
8062 debug_print("danger, not destroying anything now\n");
8063 compose->deferred_destroy = TRUE;
8066 /* NOTE: address_completion_end() does nothing with the window
8067 * however this may change. */
8068 address_completion_end(compose->window);
8070 slist_free_strings(compose->to_list);
8071 g_slist_free(compose->to_list);
8072 slist_free_strings(compose->newsgroup_list);
8073 g_slist_free(compose->newsgroup_list);
8074 slist_free_strings(compose->header_list);
8075 g_slist_free(compose->header_list);
8077 procmsg_msginfo_free(compose->targetinfo);
8078 procmsg_msginfo_free(compose->replyinfo);
8079 procmsg_msginfo_free(compose->fwdinfo);
8081 g_free(compose->replyto);
8082 g_free(compose->cc);
8083 g_free(compose->bcc);
8084 g_free(compose->newsgroups);
8085 g_free(compose->followup_to);
8087 g_free(compose->ml_post);
8089 g_free(compose->inreplyto);
8090 g_free(compose->references);
8091 g_free(compose->msgid);
8092 g_free(compose->boundary);
8094 g_free(compose->redirect_filename);
8095 if (compose->undostruct)
8096 undo_destroy(compose->undostruct);
8098 g_free(compose->sig_str);
8100 g_free(compose->exteditor_file);
8102 g_free(compose->orig_charset);
8104 g_free(compose->privacy_system);
8106 if (addressbook_get_target_compose() == compose)
8107 addressbook_set_target_compose(NULL);
8110 if (compose->gtkaspell) {
8111 gtkaspell_delete(compose->gtkaspell);
8112 compose->gtkaspell = NULL;
8116 if (!compose->batch) {
8117 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8118 prefs_common.compose_height = compose->window->allocation.height;
8121 if (!gtk_widget_get_parent(compose->paned))
8122 gtk_widget_destroy(compose->paned);
8123 gtk_widget_destroy(compose->popupmenu);
8125 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8126 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8127 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8129 gtk_widget_destroy(compose->window);
8130 toolbar_destroy(compose->toolbar);
8131 g_free(compose->toolbar);
8132 g_mutex_free(compose->mutex);
8136 static void compose_attach_info_free(AttachInfo *ainfo)
8138 g_free(ainfo->file);
8139 g_free(ainfo->content_type);
8140 g_free(ainfo->name);
8144 static void compose_attach_update_label(Compose *compose)
8149 GtkTreeModel *model;
8154 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8155 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8156 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8160 while(gtk_tree_model_iter_next(model, &iter))
8163 text = g_strdup_printf("(%d)", i);
8164 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8168 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8170 Compose *compose = (Compose *)data;
8171 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8172 GtkTreeSelection *selection;
8174 GtkTreeModel *model;
8176 selection = gtk_tree_view_get_selection(tree_view);
8177 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8182 for (cur = sel; cur != NULL; cur = cur->next) {
8183 GtkTreePath *path = cur->data;
8184 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8187 gtk_tree_path_free(path);
8190 for (cur = sel; cur != NULL; cur = cur->next) {
8191 GtkTreeRowReference *ref = cur->data;
8192 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8195 if (gtk_tree_model_get_iter(model, &iter, path))
8196 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8198 gtk_tree_path_free(path);
8199 gtk_tree_row_reference_free(ref);
8203 compose_attach_update_label(compose);
8206 static struct _AttachProperty
8209 GtkWidget *mimetype_entry;
8210 GtkWidget *encoding_optmenu;
8211 GtkWidget *path_entry;
8212 GtkWidget *filename_entry;
8214 GtkWidget *cancel_btn;
8217 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8219 gtk_tree_path_free((GtkTreePath *)ptr);
8222 static void compose_attach_property(GtkAction *action, gpointer data)
8224 Compose *compose = (Compose *)data;
8225 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8227 GtkComboBox *optmenu;
8228 GtkTreeSelection *selection;
8230 GtkTreeModel *model;
8233 static gboolean cancelled;
8235 /* only if one selected */
8236 selection = gtk_tree_view_get_selection(tree_view);
8237 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8240 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8244 path = (GtkTreePath *) sel->data;
8245 gtk_tree_model_get_iter(model, &iter, path);
8246 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8249 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8255 if (!attach_prop.window)
8256 compose_attach_property_create(&cancelled);
8257 gtk_widget_grab_focus(attach_prop.ok_btn);
8258 gtk_widget_show(attach_prop.window);
8259 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8261 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8262 if (ainfo->encoding == ENC_UNKNOWN)
8263 combobox_select_by_data(optmenu, ENC_BASE64);
8265 combobox_select_by_data(optmenu, ainfo->encoding);
8267 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8268 ainfo->content_type ? ainfo->content_type : "");
8269 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8270 ainfo->file ? ainfo->file : "");
8271 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8272 ainfo->name ? ainfo->name : "");
8275 const gchar *entry_text;
8277 gchar *cnttype = NULL;
8284 gtk_widget_hide(attach_prop.window);
8289 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8290 if (*entry_text != '\0') {
8293 text = g_strstrip(g_strdup(entry_text));
8294 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8295 cnttype = g_strdup(text);
8298 alertpanel_error(_("Invalid MIME type."));
8304 ainfo->encoding = combobox_get_active_data(optmenu);
8306 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8307 if (*entry_text != '\0') {
8308 if (is_file_exist(entry_text) &&
8309 (size = get_file_size(entry_text)) > 0)
8310 file = g_strdup(entry_text);
8313 (_("File doesn't exist or is empty."));
8319 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8320 if (*entry_text != '\0') {
8321 g_free(ainfo->name);
8322 ainfo->name = g_strdup(entry_text);
8326 g_free(ainfo->content_type);
8327 ainfo->content_type = cnttype;
8330 g_free(ainfo->file);
8334 ainfo->size = (goffset)size;
8336 /* update tree store */
8337 text = to_human_readable(ainfo->size);
8338 gtk_tree_model_get_iter(model, &iter, path);
8339 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8340 COL_MIMETYPE, ainfo->content_type,
8342 COL_NAME, ainfo->name,
8348 gtk_tree_path_free(path);
8351 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8353 label = gtk_label_new(str); \
8354 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8355 GTK_FILL, 0, 0, 0); \
8356 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8358 entry = gtk_entry_new(); \
8359 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8360 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8363 static void compose_attach_property_create(gboolean *cancelled)
8369 GtkWidget *mimetype_entry;
8372 GtkListStore *optmenu_menu;
8373 GtkWidget *path_entry;
8374 GtkWidget *filename_entry;
8377 GtkWidget *cancel_btn;
8378 GList *mime_type_list, *strlist;
8381 debug_print("Creating attach_property window...\n");
8383 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8384 gtk_widget_set_size_request(window, 480, -1);
8385 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8386 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8387 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8388 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8389 g_signal_connect(G_OBJECT(window), "delete_event",
8390 G_CALLBACK(attach_property_delete_event),
8392 g_signal_connect(G_OBJECT(window), "key_press_event",
8393 G_CALLBACK(attach_property_key_pressed),
8396 vbox = gtk_vbox_new(FALSE, 8);
8397 gtk_container_add(GTK_CONTAINER(window), vbox);
8399 table = gtk_table_new(4, 2, FALSE);
8400 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8401 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8402 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8404 label = gtk_label_new(_("MIME type"));
8405 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8407 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8408 mimetype_entry = gtk_combo_box_entry_new_text();
8409 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8410 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8412 /* stuff with list */
8413 mime_type_list = procmime_get_mime_type_list();
8415 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8416 MimeType *type = (MimeType *) mime_type_list->data;
8419 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8421 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8424 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8425 (GCompareFunc)strcmp2);
8428 for (mime_type_list = strlist; mime_type_list != NULL;
8429 mime_type_list = mime_type_list->next) {
8430 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8431 g_free(mime_type_list->data);
8433 g_list_free(strlist);
8434 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8435 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8437 label = gtk_label_new(_("Encoding"));
8438 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8440 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8442 hbox = gtk_hbox_new(FALSE, 0);
8443 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8444 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8446 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8447 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8449 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8450 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8451 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8452 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8453 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8455 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8457 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8458 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8460 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8461 &ok_btn, GTK_STOCK_OK,
8463 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8464 gtk_widget_grab_default(ok_btn);
8466 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8467 G_CALLBACK(attach_property_ok),
8469 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8470 G_CALLBACK(attach_property_cancel),
8473 gtk_widget_show_all(vbox);
8475 attach_prop.window = window;
8476 attach_prop.mimetype_entry = mimetype_entry;
8477 attach_prop.encoding_optmenu = optmenu;
8478 attach_prop.path_entry = path_entry;
8479 attach_prop.filename_entry = filename_entry;
8480 attach_prop.ok_btn = ok_btn;
8481 attach_prop.cancel_btn = cancel_btn;
8484 #undef SET_LABEL_AND_ENTRY
8486 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8492 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8498 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8499 gboolean *cancelled)
8507 static gboolean attach_property_key_pressed(GtkWidget *widget,
8509 gboolean *cancelled)
8511 if (event && event->keyval == GDK_Escape) {
8515 if (event && event->keyval == GDK_Return) {
8522 static void compose_exec_ext_editor(Compose *compose)
8529 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8530 G_DIR_SEPARATOR, compose);
8532 if (pipe(pipe_fds) < 0) {
8538 if ((pid = fork()) < 0) {
8545 /* close the write side of the pipe */
8548 compose->exteditor_file = g_strdup(tmp);
8549 compose->exteditor_pid = pid;
8551 compose_set_ext_editor_sensitive(compose, FALSE);
8554 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8556 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8558 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8562 } else { /* process-monitoring process */
8568 /* close the read side of the pipe */
8571 if (compose_write_body_to_file(compose, tmp) < 0) {
8572 fd_write_all(pipe_fds[1], "2\n", 2);
8576 pid_ed = compose_exec_ext_editor_real(tmp);
8578 fd_write_all(pipe_fds[1], "1\n", 2);
8582 /* wait until editor is terminated */
8583 waitpid(pid_ed, NULL, 0);
8585 fd_write_all(pipe_fds[1], "0\n", 2);
8592 #endif /* G_OS_UNIX */
8596 static gint compose_exec_ext_editor_real(const gchar *file)
8603 cm_return_val_if_fail(file != NULL, -1);
8605 if ((pid = fork()) < 0) {
8610 if (pid != 0) return pid;
8612 /* grandchild process */
8614 if (setpgid(0, getppid()))
8617 if (prefs_common_get_ext_editor_cmd() &&
8618 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8619 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8620 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8622 if (prefs_common_get_ext_editor_cmd())
8623 g_warning("External editor command-line is invalid: '%s'\n",
8624 prefs_common_get_ext_editor_cmd());
8625 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8628 cmdline = strsplit_with_quote(buf, " ", 1024);
8629 execvp(cmdline[0], cmdline);
8632 g_strfreev(cmdline);
8637 static gboolean compose_ext_editor_kill(Compose *compose)
8639 pid_t pgid = compose->exteditor_pid * -1;
8642 ret = kill(pgid, 0);
8644 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8648 msg = g_strdup_printf
8649 (_("The external editor is still working.\n"
8650 "Force terminating the process?\n"
8651 "process group id: %d"), -pgid);
8652 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8653 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8657 if (val == G_ALERTALTERNATE) {
8658 g_source_remove(compose->exteditor_tag);
8659 g_io_channel_shutdown(compose->exteditor_ch,
8661 g_io_channel_unref(compose->exteditor_ch);
8663 if (kill(pgid, SIGTERM) < 0) perror("kill");
8664 waitpid(compose->exteditor_pid, NULL, 0);
8666 g_warning("Terminated process group id: %d", -pgid);
8667 g_warning("Temporary file: %s",
8668 compose->exteditor_file);
8670 compose_set_ext_editor_sensitive(compose, TRUE);
8672 g_free(compose->exteditor_file);
8673 compose->exteditor_file = NULL;
8674 compose->exteditor_pid = -1;
8675 compose->exteditor_ch = NULL;
8676 compose->exteditor_tag = -1;
8684 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8688 Compose *compose = (Compose *)data;
8691 debug_print(_("Compose: input from monitoring process\n"));
8693 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8695 g_io_channel_shutdown(source, FALSE, NULL);
8696 g_io_channel_unref(source);
8698 waitpid(compose->exteditor_pid, NULL, 0);
8700 if (buf[0] == '0') { /* success */
8701 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8702 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8704 gtk_text_buffer_set_text(buffer, "", -1);
8705 compose_insert_file(compose, compose->exteditor_file);
8706 compose_changed_cb(NULL, compose);
8707 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8709 if (claws_unlink(compose->exteditor_file) < 0)
8710 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8711 } else if (buf[0] == '1') { /* failed */
8712 g_warning("Couldn't exec external editor\n");
8713 if (claws_unlink(compose->exteditor_file) < 0)
8714 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8715 } else if (buf[0] == '2') {
8716 g_warning("Couldn't write to file\n");
8717 } else if (buf[0] == '3') {
8718 g_warning("Pipe read failed\n");
8721 compose_set_ext_editor_sensitive(compose, TRUE);
8723 g_free(compose->exteditor_file);
8724 compose->exteditor_file = NULL;
8725 compose->exteditor_pid = -1;
8726 compose->exteditor_ch = NULL;
8727 compose->exteditor_tag = -1;
8732 static void compose_set_ext_editor_sensitive(Compose *compose,
8735 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8736 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8737 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8738 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8739 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8740 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8741 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8743 gtk_widget_set_sensitive(compose->text, sensitive);
8744 if (compose->toolbar->send_btn)
8745 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8746 if (compose->toolbar->sendl_btn)
8747 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8748 if (compose->toolbar->draft_btn)
8749 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8750 if (compose->toolbar->insert_btn)
8751 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8752 if (compose->toolbar->sig_btn)
8753 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8754 if (compose->toolbar->exteditor_btn)
8755 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8756 if (compose->toolbar->linewrap_current_btn)
8757 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8758 if (compose->toolbar->linewrap_all_btn)
8759 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8761 #endif /* G_OS_UNIX */
8764 * compose_undo_state_changed:
8766 * Change the sensivity of the menuentries undo and redo
8768 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8769 gint redo_state, gpointer data)
8771 Compose *compose = (Compose *)data;
8773 switch (undo_state) {
8774 case UNDO_STATE_TRUE:
8775 if (!undostruct->undo_state) {
8776 undostruct->undo_state = TRUE;
8777 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8780 case UNDO_STATE_FALSE:
8781 if (undostruct->undo_state) {
8782 undostruct->undo_state = FALSE;
8783 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8786 case UNDO_STATE_UNCHANGED:
8788 case UNDO_STATE_REFRESH:
8789 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8792 g_warning("Undo state not recognized");
8796 switch (redo_state) {
8797 case UNDO_STATE_TRUE:
8798 if (!undostruct->redo_state) {
8799 undostruct->redo_state = TRUE;
8800 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8803 case UNDO_STATE_FALSE:
8804 if (undostruct->redo_state) {
8805 undostruct->redo_state = FALSE;
8806 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8809 case UNDO_STATE_UNCHANGED:
8811 case UNDO_STATE_REFRESH:
8812 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8815 g_warning("Redo state not recognized");
8820 /* callback functions */
8822 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8823 * includes "non-client" (windows-izm) in calculation, so this calculation
8824 * may not be accurate.
8826 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8827 GtkAllocation *allocation,
8828 GtkSHRuler *shruler)
8830 if (prefs_common.show_ruler) {
8831 gint char_width = 0, char_height = 0;
8832 gint line_width_in_chars;
8834 gtkut_get_font_size(GTK_WIDGET(widget),
8835 &char_width, &char_height);
8836 line_width_in_chars =
8837 (allocation->width - allocation->x) / char_width;
8839 /* got the maximum */
8840 gtk_ruler_set_range(GTK_RULER(shruler),
8841 0.0, line_width_in_chars, 0,
8842 /*line_width_in_chars*/ char_width);
8848 static void account_activated(GtkComboBox *optmenu, gpointer data)
8850 Compose *compose = (Compose *)data;
8853 gchar *folderidentifier;
8854 gint account_id = 0;
8858 /* Get ID of active account in the combo box */
8859 menu = gtk_combo_box_get_model(optmenu);
8860 gtk_combo_box_get_active_iter(optmenu, &iter);
8861 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8863 ac = account_find_from_id(account_id);
8864 cm_return_if_fail(ac != NULL);
8866 if (ac != compose->account)
8867 compose_select_account(compose, ac, FALSE);
8869 /* Set message save folder */
8870 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8871 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8873 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8874 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8876 compose_set_save_to(compose, NULL);
8877 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8878 folderidentifier = folder_item_get_identifier(account_get_special_folder
8879 (compose->account, F_OUTBOX));
8880 compose_set_save_to(compose, folderidentifier);
8881 g_free(folderidentifier);
8885 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8886 GtkTreeViewColumn *column, Compose *compose)
8888 compose_attach_property(NULL, compose);
8891 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8894 Compose *compose = (Compose *)data;
8895 GtkTreeSelection *attach_selection;
8896 gint attach_nr_selected;
8898 if (!event) return FALSE;
8900 if (event->button == 3) {
8901 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8902 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8904 if (attach_nr_selected > 0)
8906 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8907 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8909 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8910 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8913 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8914 NULL, NULL, event->button, event->time);
8921 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8924 Compose *compose = (Compose *)data;
8926 if (!event) return FALSE;
8928 switch (event->keyval) {
8930 compose_attach_remove_selected(NULL, compose);
8936 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8938 toolbar_comp_set_sensitive(compose, allow);
8939 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8940 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8942 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8944 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8945 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8946 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8948 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8952 static void compose_send_cb(GtkAction *action, gpointer data)
8954 Compose *compose = (Compose *)data;
8956 if (prefs_common.work_offline &&
8957 !inc_offline_should_override(TRUE,
8958 _("Claws Mail needs network access in order "
8959 "to send this email.")))
8962 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8963 g_source_remove(compose->draft_timeout_tag);
8964 compose->draft_timeout_tag = -1;
8967 compose_send(compose);
8970 static void compose_send_later_cb(GtkAction *action, gpointer data)
8972 Compose *compose = (Compose *)data;
8976 compose_allow_user_actions(compose, FALSE);
8977 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8978 compose_allow_user_actions(compose, TRUE);
8982 compose_close(compose);
8983 } else if (val == -1) {
8984 alertpanel_error(_("Could not queue message."));
8985 } else if (val == -2) {
8986 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8987 } else if (val == -3) {
8988 if (privacy_peek_error())
8989 alertpanel_error(_("Could not queue message for sending:\n\n"
8990 "Signature failed: %s"), privacy_get_error());
8991 } else if (val == -4) {
8992 alertpanel_error(_("Could not queue message for sending:\n\n"
8993 "Charset conversion failed."));
8994 } else if (val == -5) {
8995 alertpanel_error(_("Could not queue message for sending:\n\n"
8996 "Couldn't get recipient encryption key."));
8997 } else if (val == -6) {
9000 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9003 #define DRAFTED_AT_EXIT "drafted_at_exit"
9004 static void compose_register_draft(MsgInfo *info)
9006 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9007 DRAFTED_AT_EXIT, NULL);
9008 FILE *fp = g_fopen(filepath, "ab");
9011 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9019 gboolean compose_draft (gpointer data, guint action)
9021 Compose *compose = (Compose *)data;
9025 MsgFlags flag = {0, 0};
9026 static gboolean lock = FALSE;
9027 MsgInfo *newmsginfo;
9029 gboolean target_locked = FALSE;
9030 gboolean err = FALSE;
9032 if (lock) return FALSE;
9034 if (compose->sending)
9037 draft = account_get_special_folder(compose->account, F_DRAFT);
9038 cm_return_val_if_fail(draft != NULL, FALSE);
9040 if (!g_mutex_trylock(compose->mutex)) {
9041 /* we don't want to lock the mutex once it's available,
9042 * because as the only other part of compose.c locking
9043 * it is compose_close - which means once unlocked,
9044 * the compose struct will be freed */
9045 debug_print("couldn't lock mutex, probably sending\n");
9051 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9052 G_DIR_SEPARATOR, compose);
9053 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9054 FILE_OP_ERROR(tmp, "fopen");
9058 /* chmod for security */
9059 if (change_file_mode_rw(fp, tmp) < 0) {
9060 FILE_OP_ERROR(tmp, "chmod");
9061 g_warning("can't change file mode\n");
9064 /* Save draft infos */
9065 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9066 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9068 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9069 gchar *savefolderid;
9071 savefolderid = compose_get_save_to(compose);
9072 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9073 g_free(savefolderid);
9075 if (compose->return_receipt) {
9076 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9078 if (compose->privacy_system) {
9079 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9080 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9081 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9084 /* Message-ID of message replying to */
9085 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9088 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9089 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9092 /* Message-ID of message forwarding to */
9093 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9096 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9097 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9101 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9102 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9104 /* end of headers */
9105 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9112 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9116 if (fclose(fp) == EOF) {
9120 if (compose->targetinfo) {
9121 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9122 flag.perm_flags = target_locked?MSG_LOCKED:0;
9124 flag.tmp_flags = MSG_DRAFT;
9126 folder_item_scan(draft);
9127 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9128 MsgInfo *tmpinfo = NULL;
9129 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9130 if (compose->msgid) {
9131 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9134 msgnum = tmpinfo->msgnum;
9135 procmsg_msginfo_free(tmpinfo);
9136 debug_print("got draft msgnum %d from scanning\n", msgnum);
9138 debug_print("didn't get draft msgnum after scanning\n");
9141 debug_print("got draft msgnum %d from adding\n", msgnum);
9147 if (action != COMPOSE_AUTO_SAVE) {
9148 if (action != COMPOSE_DRAFT_FOR_EXIT)
9149 alertpanel_error(_("Could not save draft."));
9152 gtkut_window_popup(compose->window);
9153 val = alertpanel_full(_("Could not save draft"),
9154 _("Could not save draft.\n"
9155 "Do you want to cancel exit or discard this email?"),
9156 _("_Cancel exit"), _("_Discard email"), NULL,
9157 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9158 if (val == G_ALERTALTERNATE) {
9160 g_mutex_unlock(compose->mutex); /* must be done before closing */
9161 compose_close(compose);
9165 g_mutex_unlock(compose->mutex); /* must be done before closing */
9174 if (compose->mode == COMPOSE_REEDIT) {
9175 compose_remove_reedit_target(compose, TRUE);
9178 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9181 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9183 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9185 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9186 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9187 procmsg_msginfo_set_flags(newmsginfo, 0,
9188 MSG_HAS_ATTACHMENT);
9190 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9191 compose_register_draft(newmsginfo);
9193 procmsg_msginfo_free(newmsginfo);
9196 folder_item_scan(draft);
9198 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9200 g_mutex_unlock(compose->mutex); /* must be done before closing */
9201 compose_close(compose);
9207 path = folder_item_fetch_msg(draft, msgnum);
9209 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9212 if (g_stat(path, &s) < 0) {
9213 FILE_OP_ERROR(path, "stat");
9219 procmsg_msginfo_free(compose->targetinfo);
9220 compose->targetinfo = procmsg_msginfo_new();
9221 compose->targetinfo->msgnum = msgnum;
9222 compose->targetinfo->size = (goffset)s.st_size;
9223 compose->targetinfo->mtime = s.st_mtime;
9224 compose->targetinfo->folder = draft;
9226 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9227 compose->mode = COMPOSE_REEDIT;
9229 if (action == COMPOSE_AUTO_SAVE) {
9230 compose->autosaved_draft = compose->targetinfo;
9232 compose->modified = FALSE;
9233 compose_set_title(compose);
9237 g_mutex_unlock(compose->mutex);
9241 void compose_clear_exit_drafts(void)
9243 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9244 DRAFTED_AT_EXIT, NULL);
9245 if (is_file_exist(filepath))
9246 claws_unlink(filepath);
9251 void compose_reopen_exit_drafts(void)
9253 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9254 DRAFTED_AT_EXIT, NULL);
9255 FILE *fp = g_fopen(filepath, "rb");
9259 while (fgets(buf, sizeof(buf), fp)) {
9260 gchar **parts = g_strsplit(buf, "\t", 2);
9261 const gchar *folder = parts[0];
9262 int msgnum = parts[1] ? atoi(parts[1]):-1;
9264 if (folder && *folder && msgnum > -1) {
9265 FolderItem *item = folder_find_item_from_identifier(folder);
9266 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9268 compose_reedit(info, FALSE);
9275 compose_clear_exit_drafts();
9278 static void compose_save_cb(GtkAction *action, gpointer data)
9280 Compose *compose = (Compose *)data;
9281 compose_draft(compose, COMPOSE_KEEP_EDITING);
9282 compose->rmode = COMPOSE_REEDIT;
9285 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9287 if (compose && file_list) {
9290 for ( tmp = file_list; tmp; tmp = tmp->next) {
9291 gchar *file = (gchar *) tmp->data;
9292 gchar *utf8_filename = conv_filename_to_utf8(file);
9293 compose_attach_append(compose, file, utf8_filename, NULL);
9294 compose_changed_cb(NULL, compose);
9299 g_free(utf8_filename);
9304 static void compose_attach_cb(GtkAction *action, gpointer data)
9306 Compose *compose = (Compose *)data;
9309 if (compose->redirect_filename != NULL)
9312 file_list = filesel_select_multiple_files_open(_("Select file"));
9315 compose_attach_from_list(compose, file_list, TRUE);
9316 g_list_free(file_list);
9320 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9322 Compose *compose = (Compose *)data;
9325 file_list = filesel_select_multiple_files_open(_("Select file"));
9330 for ( tmp = file_list; tmp; tmp = tmp->next) {
9331 gchar *file = (gchar *) tmp->data;
9332 gchar *filedup = g_strdup(file);
9333 gchar *shortfile = g_path_get_basename(filedup);
9334 ComposeInsertResult res;
9336 res = compose_insert_file(compose, file);
9337 if (res == COMPOSE_INSERT_READ_ERROR) {
9338 alertpanel_error(_("File '%s' could not be read."), shortfile);
9339 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9340 alertpanel_error(_("File '%s' contained invalid characters\n"
9341 "for the current encoding, insertion may be incorrect."), shortfile);
9347 g_list_free(file_list);
9351 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9353 Compose *compose = (Compose *)data;
9355 compose_insert_sig(compose, FALSE);
9358 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9362 Compose *compose = (Compose *)data;
9364 gtkut_widget_get_uposition(widget, &x, &y);
9365 if (!compose->batch) {
9366 prefs_common.compose_x = x;
9367 prefs_common.compose_y = y;
9369 if (compose->sending || compose->updating)
9371 compose_close_cb(NULL, compose);
9375 void compose_close_toolbar(Compose *compose)
9377 compose_close_cb(NULL, compose);
9380 static void compose_close_cb(GtkAction *action, gpointer data)
9382 Compose *compose = (Compose *)data;
9386 if (compose->exteditor_tag != -1) {
9387 if (!compose_ext_editor_kill(compose))
9392 if (compose->modified) {
9393 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9394 if (!g_mutex_trylock(compose->mutex)) {
9395 /* we don't want to lock the mutex once it's available,
9396 * because as the only other part of compose.c locking
9397 * it is compose_close - which means once unlocked,
9398 * the compose struct will be freed */
9399 debug_print("couldn't lock mutex, probably sending\n");
9403 val = alertpanel(_("Discard message"),
9404 _("This message has been modified. Discard it?"),
9405 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9407 val = alertpanel(_("Save changes"),
9408 _("This message has been modified. Save the latest changes?"),
9409 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9411 g_mutex_unlock(compose->mutex);
9413 case G_ALERTDEFAULT:
9414 if (prefs_common.autosave && !reedit)
9415 compose_remove_draft(compose);
9417 case G_ALERTALTERNATE:
9418 compose_draft(data, COMPOSE_QUIT_EDITING);
9425 compose_close(compose);
9428 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9430 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9431 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9432 Compose *compose = (Compose *) data;
9435 compose->out_encoding = (CharSet)value;
9438 static void compose_address_cb(GtkAction *action, gpointer data)
9440 Compose *compose = (Compose *)data;
9442 addressbook_open(compose);
9445 static void about_show_cb(GtkAction *action, gpointer data)
9450 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9452 Compose *compose = (Compose *)data;
9457 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9458 cm_return_if_fail(tmpl != NULL);
9460 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9462 val = alertpanel(_("Apply template"), msg,
9463 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9466 if (val == G_ALERTDEFAULT)
9467 compose_template_apply(compose, tmpl, TRUE);
9468 else if (val == G_ALERTALTERNATE)
9469 compose_template_apply(compose, tmpl, FALSE);
9472 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9474 Compose *compose = (Compose *)data;
9476 compose_exec_ext_editor(compose);
9479 static void compose_undo_cb(GtkAction *action, gpointer data)
9481 Compose *compose = (Compose *)data;
9482 gboolean prev_autowrap = compose->autowrap;
9484 compose->autowrap = FALSE;
9485 undo_undo(compose->undostruct);
9486 compose->autowrap = prev_autowrap;
9489 static void compose_redo_cb(GtkAction *action, gpointer data)
9491 Compose *compose = (Compose *)data;
9492 gboolean prev_autowrap = compose->autowrap;
9494 compose->autowrap = FALSE;
9495 undo_redo(compose->undostruct);
9496 compose->autowrap = prev_autowrap;
9499 static void entry_cut_clipboard(GtkWidget *entry)
9501 if (GTK_IS_EDITABLE(entry))
9502 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9503 else if (GTK_IS_TEXT_VIEW(entry))
9504 gtk_text_buffer_cut_clipboard(
9505 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9506 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9510 static void entry_copy_clipboard(GtkWidget *entry)
9512 if (GTK_IS_EDITABLE(entry))
9513 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9514 else if (GTK_IS_TEXT_VIEW(entry))
9515 gtk_text_buffer_copy_clipboard(
9516 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9517 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9520 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9521 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9523 if (GTK_IS_TEXT_VIEW(entry)) {
9524 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9525 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9526 GtkTextIter start_iter, end_iter;
9528 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9530 if (contents == NULL)
9533 /* we shouldn't delete the selection when middle-click-pasting, or we
9534 * can't mid-click-paste our own selection */
9535 if (clip != GDK_SELECTION_PRIMARY) {
9536 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9537 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9540 if (insert_place == NULL) {
9541 /* if insert_place isn't specified, insert at the cursor.
9542 * used for Ctrl-V pasting */
9543 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9544 start = gtk_text_iter_get_offset(&start_iter);
9545 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9547 /* if insert_place is specified, paste here.
9548 * used for mid-click-pasting */
9549 start = gtk_text_iter_get_offset(insert_place);
9550 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9551 if (prefs_common.primary_paste_unselects)
9552 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9556 /* paste unwrapped: mark the paste so it's not wrapped later */
9557 end = start + strlen(contents);
9558 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9559 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9560 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9561 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9562 /* rewrap paragraph now (after a mid-click-paste) */
9563 mark_start = gtk_text_buffer_get_insert(buffer);
9564 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9565 gtk_text_iter_backward_char(&start_iter);
9566 compose_beautify_paragraph(compose, &start_iter, TRUE);
9568 } else if (GTK_IS_EDITABLE(entry))
9569 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9571 compose->modified = TRUE;
9574 static void entry_allsel(GtkWidget *entry)
9576 if (GTK_IS_EDITABLE(entry))
9577 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9578 else if (GTK_IS_TEXT_VIEW(entry)) {
9579 GtkTextIter startiter, enditer;
9580 GtkTextBuffer *textbuf;
9582 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9583 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9584 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9586 gtk_text_buffer_move_mark_by_name(textbuf,
9587 "selection_bound", &startiter);
9588 gtk_text_buffer_move_mark_by_name(textbuf,
9589 "insert", &enditer);
9593 static void compose_cut_cb(GtkAction *action, gpointer data)
9595 Compose *compose = (Compose *)data;
9596 if (compose->focused_editable
9597 #ifndef GENERIC_UMPC
9598 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9601 entry_cut_clipboard(compose->focused_editable);
9604 static void compose_copy_cb(GtkAction *action, gpointer data)
9606 Compose *compose = (Compose *)data;
9607 if (compose->focused_editable
9608 #ifndef GENERIC_UMPC
9609 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9612 entry_copy_clipboard(compose->focused_editable);
9615 static void compose_paste_cb(GtkAction *action, gpointer data)
9617 Compose *compose = (Compose *)data;
9619 GtkTextBuffer *buffer;
9621 if (compose->focused_editable &&
9622 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9623 entry_paste_clipboard(compose, compose->focused_editable,
9624 prefs_common.linewrap_pastes,
9625 GDK_SELECTION_CLIPBOARD, NULL);
9629 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9631 Compose *compose = (Compose *)data;
9632 gint wrap_quote = prefs_common.linewrap_quote;
9633 if (compose->focused_editable
9634 #ifndef GENERIC_UMPC
9635 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9638 /* let text_insert() (called directly or at a later time
9639 * after the gtk_editable_paste_clipboard) know that
9640 * text is to be inserted as a quotation. implemented
9641 * by using a simple refcount... */
9642 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9643 G_OBJECT(compose->focused_editable),
9644 "paste_as_quotation"));
9645 g_object_set_data(G_OBJECT(compose->focused_editable),
9646 "paste_as_quotation",
9647 GINT_TO_POINTER(paste_as_quotation + 1));
9648 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9649 entry_paste_clipboard(compose, compose->focused_editable,
9650 prefs_common.linewrap_pastes,
9651 GDK_SELECTION_CLIPBOARD, NULL);
9652 prefs_common.linewrap_quote = wrap_quote;
9656 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9658 Compose *compose = (Compose *)data;
9660 GtkTextBuffer *buffer;
9662 if (compose->focused_editable
9663 #ifndef GENERIC_UMPC
9664 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9667 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9668 GDK_SELECTION_CLIPBOARD, NULL);
9672 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9674 Compose *compose = (Compose *)data;
9676 GtkTextBuffer *buffer;
9678 if (compose->focused_editable
9679 #ifndef GENERIC_UMPC
9680 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9683 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9684 GDK_SELECTION_CLIPBOARD, NULL);
9688 static void compose_allsel_cb(GtkAction *action, gpointer data)
9690 Compose *compose = (Compose *)data;
9691 if (compose->focused_editable
9692 #ifndef GENERIC_UMPC
9693 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9696 entry_allsel(compose->focused_editable);
9699 static void textview_move_beginning_of_line (GtkTextView *text)
9701 GtkTextBuffer *buffer;
9705 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9707 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9708 mark = gtk_text_buffer_get_insert(buffer);
9709 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9710 gtk_text_iter_set_line_offset(&ins, 0);
9711 gtk_text_buffer_place_cursor(buffer, &ins);
9714 static void textview_move_forward_character (GtkTextView *text)
9716 GtkTextBuffer *buffer;
9720 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9722 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9723 mark = gtk_text_buffer_get_insert(buffer);
9724 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9725 if (gtk_text_iter_forward_cursor_position(&ins))
9726 gtk_text_buffer_place_cursor(buffer, &ins);
9729 static void textview_move_backward_character (GtkTextView *text)
9731 GtkTextBuffer *buffer;
9735 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9737 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9738 mark = gtk_text_buffer_get_insert(buffer);
9739 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9740 if (gtk_text_iter_backward_cursor_position(&ins))
9741 gtk_text_buffer_place_cursor(buffer, &ins);
9744 static void textview_move_forward_word (GtkTextView *text)
9746 GtkTextBuffer *buffer;
9751 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9753 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9754 mark = gtk_text_buffer_get_insert(buffer);
9755 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9756 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9757 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9758 gtk_text_iter_backward_word_start(&ins);
9759 gtk_text_buffer_place_cursor(buffer, &ins);
9763 static void textview_move_backward_word (GtkTextView *text)
9765 GtkTextBuffer *buffer;
9770 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9772 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9773 mark = gtk_text_buffer_get_insert(buffer);
9774 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9775 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9776 if (gtk_text_iter_backward_word_starts(&ins, 1))
9777 gtk_text_buffer_place_cursor(buffer, &ins);
9780 static void textview_move_end_of_line (GtkTextView *text)
9782 GtkTextBuffer *buffer;
9786 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9788 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9789 mark = gtk_text_buffer_get_insert(buffer);
9790 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9791 if (gtk_text_iter_forward_to_line_end(&ins))
9792 gtk_text_buffer_place_cursor(buffer, &ins);
9795 static void textview_move_next_line (GtkTextView *text)
9797 GtkTextBuffer *buffer;
9802 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9804 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9805 mark = gtk_text_buffer_get_insert(buffer);
9806 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9807 offset = gtk_text_iter_get_line_offset(&ins);
9808 if (gtk_text_iter_forward_line(&ins)) {
9809 gtk_text_iter_set_line_offset(&ins, offset);
9810 gtk_text_buffer_place_cursor(buffer, &ins);
9814 static void textview_move_previous_line (GtkTextView *text)
9816 GtkTextBuffer *buffer;
9821 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9823 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9824 mark = gtk_text_buffer_get_insert(buffer);
9825 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9826 offset = gtk_text_iter_get_line_offset(&ins);
9827 if (gtk_text_iter_backward_line(&ins)) {
9828 gtk_text_iter_set_line_offset(&ins, offset);
9829 gtk_text_buffer_place_cursor(buffer, &ins);
9833 static void textview_delete_forward_character (GtkTextView *text)
9835 GtkTextBuffer *buffer;
9837 GtkTextIter ins, end_iter;
9839 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9841 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9842 mark = gtk_text_buffer_get_insert(buffer);
9843 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9845 if (gtk_text_iter_forward_char(&end_iter)) {
9846 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9850 static void textview_delete_backward_character (GtkTextView *text)
9852 GtkTextBuffer *buffer;
9854 GtkTextIter ins, end_iter;
9856 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9858 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9859 mark = gtk_text_buffer_get_insert(buffer);
9860 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9862 if (gtk_text_iter_backward_char(&end_iter)) {
9863 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9867 static void textview_delete_forward_word (GtkTextView *text)
9869 GtkTextBuffer *buffer;
9871 GtkTextIter ins, end_iter;
9873 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9875 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9876 mark = gtk_text_buffer_get_insert(buffer);
9877 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9879 if (gtk_text_iter_forward_word_end(&end_iter)) {
9880 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9884 static void textview_delete_backward_word (GtkTextView *text)
9886 GtkTextBuffer *buffer;
9888 GtkTextIter ins, end_iter;
9890 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9892 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9893 mark = gtk_text_buffer_get_insert(buffer);
9894 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9896 if (gtk_text_iter_backward_word_start(&end_iter)) {
9897 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9901 static void textview_delete_line (GtkTextView *text)
9903 GtkTextBuffer *buffer;
9905 GtkTextIter ins, start_iter, end_iter;
9907 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9909 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9910 mark = gtk_text_buffer_get_insert(buffer);
9911 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9914 gtk_text_iter_set_line_offset(&start_iter, 0);
9917 if (gtk_text_iter_ends_line(&end_iter)){
9918 if (!gtk_text_iter_forward_char(&end_iter))
9919 gtk_text_iter_backward_char(&start_iter);
9922 gtk_text_iter_forward_to_line_end(&end_iter);
9923 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9926 static void textview_delete_to_line_end (GtkTextView *text)
9928 GtkTextBuffer *buffer;
9930 GtkTextIter ins, end_iter;
9932 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9934 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9935 mark = gtk_text_buffer_get_insert(buffer);
9936 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9938 if (gtk_text_iter_ends_line(&end_iter))
9939 gtk_text_iter_forward_char(&end_iter);
9941 gtk_text_iter_forward_to_line_end(&end_iter);
9942 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9945 #define DO_ACTION(name, act) { \
9946 if(!strcmp(name, a_name)) { \
9950 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9952 const gchar *a_name = gtk_action_get_name(action);
9953 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9954 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9955 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9956 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9957 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9958 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9959 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9960 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9961 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9962 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9963 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9964 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9965 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9966 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9970 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9972 Compose *compose = (Compose *)data;
9973 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9974 ComposeCallAdvancedAction action = -1;
9976 action = compose_call_advanced_action_from_path(gaction);
9979 void (*do_action) (GtkTextView *text);
9980 } action_table[] = {
9981 {textview_move_beginning_of_line},
9982 {textview_move_forward_character},
9983 {textview_move_backward_character},
9984 {textview_move_forward_word},
9985 {textview_move_backward_word},
9986 {textview_move_end_of_line},
9987 {textview_move_next_line},
9988 {textview_move_previous_line},
9989 {textview_delete_forward_character},
9990 {textview_delete_backward_character},
9991 {textview_delete_forward_word},
9992 {textview_delete_backward_word},
9993 {textview_delete_line},
9994 {textview_delete_to_line_end}
9997 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9999 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10000 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10001 if (action_table[action].do_action)
10002 action_table[action].do_action(text);
10004 g_warning("Not implemented yet.");
10008 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10012 if (GTK_IS_EDITABLE(widget)) {
10013 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10014 gtk_editable_set_position(GTK_EDITABLE(widget),
10017 if (widget->parent && widget->parent->parent
10018 && widget->parent->parent->parent) {
10019 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10020 gint y = widget->allocation.y;
10021 gint height = widget->allocation.height;
10022 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10023 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10025 if (y < (int)shown->value) {
10026 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10028 if (y + height > (int)shown->value + (int)shown->page_size) {
10029 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10030 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10031 y + height - (int)shown->page_size - 1);
10033 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10034 (int)shown->upper - (int)shown->page_size - 1);
10041 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10042 compose->focused_editable = widget;
10044 #ifdef GENERIC_UMPC
10045 if (GTK_IS_TEXT_VIEW(widget)
10046 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10047 g_object_ref(compose->notebook);
10048 g_object_ref(compose->edit_vbox);
10049 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10050 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10051 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10052 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10053 g_object_unref(compose->notebook);
10054 g_object_unref(compose->edit_vbox);
10055 g_signal_handlers_block_by_func(G_OBJECT(widget),
10056 G_CALLBACK(compose_grab_focus_cb),
10058 gtk_widget_grab_focus(widget);
10059 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10060 G_CALLBACK(compose_grab_focus_cb),
10062 } else if (!GTK_IS_TEXT_VIEW(widget)
10063 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10064 g_object_ref(compose->notebook);
10065 g_object_ref(compose->edit_vbox);
10066 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10067 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10068 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10069 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10070 g_object_unref(compose->notebook);
10071 g_object_unref(compose->edit_vbox);
10072 g_signal_handlers_block_by_func(G_OBJECT(widget),
10073 G_CALLBACK(compose_grab_focus_cb),
10075 gtk_widget_grab_focus(widget);
10076 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10077 G_CALLBACK(compose_grab_focus_cb),
10083 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10085 compose->modified = TRUE;
10086 // compose_beautify_paragraph(compose, NULL, TRUE);
10087 #ifndef GENERIC_UMPC
10088 compose_set_title(compose);
10092 static void compose_wrap_cb(GtkAction *action, gpointer data)
10094 Compose *compose = (Compose *)data;
10095 compose_beautify_paragraph(compose, NULL, TRUE);
10098 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10100 Compose *compose = (Compose *)data;
10101 compose_wrap_all_full(compose, TRUE);
10104 static void compose_find_cb(GtkAction *action, gpointer data)
10106 Compose *compose = (Compose *)data;
10108 message_search_compose(compose);
10111 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10114 Compose *compose = (Compose *)data;
10115 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10116 if (compose->autowrap)
10117 compose_wrap_all_full(compose, TRUE);
10118 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10121 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10124 Compose *compose = (Compose *)data;
10125 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10128 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10130 Compose *compose = (Compose *)data;
10132 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10135 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10137 Compose *compose = (Compose *)data;
10139 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10142 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10144 g_free(compose->privacy_system);
10146 compose->privacy_system = g_strdup(account->default_privacy_system);
10147 compose_update_privacy_system_menu_item(compose, warn);
10150 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10152 Compose *compose = (Compose *)data;
10154 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10155 gtk_widget_show(compose->ruler_hbox);
10156 prefs_common.show_ruler = TRUE;
10158 gtk_widget_hide(compose->ruler_hbox);
10159 gtk_widget_queue_resize(compose->edit_vbox);
10160 prefs_common.show_ruler = FALSE;
10164 static void compose_attach_drag_received_cb (GtkWidget *widget,
10165 GdkDragContext *context,
10168 GtkSelectionData *data,
10171 gpointer user_data)
10173 Compose *compose = (Compose *)user_data;
10176 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10178 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10180 ) && gtk_drag_get_source_widget(context) !=
10181 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10182 list = uri_list_extract_filenames((const gchar *)data->data);
10183 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10184 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10185 compose_attach_append
10186 (compose, (const gchar *)tmp->data,
10187 utf8_filename, NULL);
10188 g_free(utf8_filename);
10190 if (list) compose_changed_cb(NULL, compose);
10191 list_free_strings(list);
10193 } else if (gtk_drag_get_source_widget(context)
10194 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10195 /* comes from our summaryview */
10196 SummaryView * summaryview = NULL;
10197 GSList * list = NULL, *cur = NULL;
10199 if (mainwindow_get_mainwindow())
10200 summaryview = mainwindow_get_mainwindow()->summaryview;
10203 list = summary_get_selected_msg_list(summaryview);
10205 for (cur = list; cur; cur = cur->next) {
10206 MsgInfo *msginfo = (MsgInfo *)cur->data;
10207 gchar *file = NULL;
10209 file = procmsg_get_message_file_full(msginfo,
10212 compose_attach_append(compose, (const gchar *)file,
10213 (const gchar *)file, "message/rfc822");
10217 g_slist_free(list);
10221 static gboolean compose_drag_drop(GtkWidget *widget,
10222 GdkDragContext *drag_context,
10224 guint time, gpointer user_data)
10226 /* not handling this signal makes compose_insert_drag_received_cb
10231 static void compose_insert_drag_received_cb (GtkWidget *widget,
10232 GdkDragContext *drag_context,
10235 GtkSelectionData *data,
10238 gpointer user_data)
10240 Compose *compose = (Compose *)user_data;
10243 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10246 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10248 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10250 AlertValue val = G_ALERTDEFAULT;
10252 list = uri_list_extract_filenames((const gchar *)data->data);
10253 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10254 /* Assume a list of no files, and data has ://, is a remote link */
10255 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10256 gchar *tmpfile = get_tmp_file();
10257 str_write_to_file(tmpdata, tmpfile);
10259 compose_insert_file(compose, tmpfile);
10260 claws_unlink(tmpfile);
10262 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10263 compose_beautify_paragraph(compose, NULL, TRUE);
10266 switch (prefs_common.compose_dnd_mode) {
10267 case COMPOSE_DND_ASK:
10268 val = alertpanel_full(_("Insert or attach?"),
10269 _("Do you want to insert the contents of the file(s) "
10270 "into the message body, or attach it to the email?"),
10271 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10272 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10274 case COMPOSE_DND_INSERT:
10275 val = G_ALERTALTERNATE;
10277 case COMPOSE_DND_ATTACH:
10278 val = G_ALERTOTHER;
10281 /* unexpected case */
10282 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10285 if (val & G_ALERTDISABLE) {
10286 val &= ~G_ALERTDISABLE;
10287 /* remember what action to perform by default, only if we don't click Cancel */
10288 if (val == G_ALERTALTERNATE)
10289 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10290 else if (val == G_ALERTOTHER)
10291 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10294 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10295 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10296 list_free_strings(list);
10299 } else if (val == G_ALERTOTHER) {
10300 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10301 list_free_strings(list);
10306 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10307 compose_insert_file(compose, (const gchar *)tmp->data);
10309 list_free_strings(list);
10311 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10314 #if GTK_CHECK_VERSION(2, 8, 0)
10315 /* do nothing, handled by GTK */
10317 gchar *tmpfile = get_tmp_file();
10318 str_write_to_file((const gchar *)data->data, tmpfile);
10319 compose_insert_file(compose, tmpfile);
10320 claws_unlink(tmpfile);
10322 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10326 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10329 static void compose_header_drag_received_cb (GtkWidget *widget,
10330 GdkDragContext *drag_context,
10333 GtkSelectionData *data,
10336 gpointer user_data)
10338 GtkEditable *entry = (GtkEditable *)user_data;
10339 gchar *email = (gchar *)data->data;
10341 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10344 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10345 gchar *decoded=g_new(gchar, strlen(email));
10348 email += strlen("mailto:");
10349 decode_uri(decoded, email); /* will fit */
10350 gtk_editable_delete_text(entry, 0, -1);
10351 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10352 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10356 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10359 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10361 Compose *compose = (Compose *)data;
10363 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10364 compose->return_receipt = TRUE;
10366 compose->return_receipt = FALSE;
10369 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10371 Compose *compose = (Compose *)data;
10373 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10374 compose->remove_references = TRUE;
10376 compose->remove_references = FALSE;
10379 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10380 GdkEventKey *event,
10381 ComposeHeaderEntry *headerentry)
10383 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10384 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10385 !(event->state & GDK_MODIFIER_MASK) &&
10386 (event->keyval == GDK_BackSpace) &&
10387 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10388 gtk_container_remove
10389 (GTK_CONTAINER(headerentry->compose->header_table),
10390 headerentry->combo);
10391 gtk_container_remove
10392 (GTK_CONTAINER(headerentry->compose->header_table),
10393 headerentry->entry);
10394 headerentry->compose->header_list =
10395 g_slist_remove(headerentry->compose->header_list,
10397 g_free(headerentry);
10398 } else if (event->keyval == GDK_Tab) {
10399 if (headerentry->compose->header_last == headerentry) {
10400 /* Override default next focus, and give it to subject_entry
10401 * instead of notebook tabs
10403 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10404 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10411 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10412 ComposeHeaderEntry *headerentry)
10414 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10415 compose_create_header_entry(headerentry->compose);
10416 g_signal_handlers_disconnect_matched
10417 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10418 0, 0, NULL, NULL, headerentry);
10420 /* Automatically scroll down */
10421 compose_show_first_last_header(headerentry->compose, FALSE);
10427 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10429 GtkAdjustment *vadj;
10431 cm_return_if_fail(compose);
10432 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10433 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10435 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10436 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10437 gtk_adjustment_changed(vadj);
10440 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10441 const gchar *text, gint len, Compose *compose)
10443 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10444 (G_OBJECT(compose->text), "paste_as_quotation"));
10447 cm_return_if_fail(text != NULL);
10449 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10450 G_CALLBACK(text_inserted),
10452 if (paste_as_quotation) {
10454 const gchar *qmark;
10456 GtkTextIter start_iter;
10459 len = strlen(text);
10461 new_text = g_strndup(text, len);
10463 qmark = compose_quote_char_from_context(compose);
10465 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10466 gtk_text_buffer_place_cursor(buffer, iter);
10468 pos = gtk_text_iter_get_offset(iter);
10470 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10471 _("Quote format error at line %d."));
10472 quote_fmt_reset_vartable();
10474 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10475 GINT_TO_POINTER(paste_as_quotation - 1));
10477 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10478 gtk_text_buffer_place_cursor(buffer, iter);
10479 gtk_text_buffer_delete_mark(buffer, mark);
10481 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10482 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10483 compose_beautify_paragraph(compose, &start_iter, FALSE);
10484 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10485 gtk_text_buffer_delete_mark(buffer, mark);
10487 if (strcmp(text, "\n") || compose->automatic_break
10488 || gtk_text_iter_starts_line(iter)) {
10489 GtkTextIter before_ins;
10490 gtk_text_buffer_insert(buffer, iter, text, len);
10491 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10492 before_ins = *iter;
10493 gtk_text_iter_backward_chars(&before_ins, len);
10494 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10497 /* check if the preceding is just whitespace or quote */
10498 GtkTextIter start_line;
10499 gchar *tmp = NULL, *quote = NULL;
10500 gint quote_len = 0, is_normal = 0;
10501 start_line = *iter;
10502 gtk_text_iter_set_line_offset(&start_line, 0);
10503 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10506 if (*tmp == '\0') {
10509 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10517 gtk_text_buffer_insert(buffer, iter, text, len);
10519 gtk_text_buffer_insert_with_tags_by_name(buffer,
10520 iter, text, len, "no_join", NULL);
10525 if (!paste_as_quotation) {
10526 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10527 compose_beautify_paragraph(compose, iter, FALSE);
10528 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10529 gtk_text_buffer_delete_mark(buffer, mark);
10532 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10533 G_CALLBACK(text_inserted),
10535 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10537 if (prefs_common.autosave &&
10538 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10539 compose->draft_timeout_tag != -2 /* disabled while loading */)
10540 compose->draft_timeout_tag = g_timeout_add
10541 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10543 static gint compose_defer_auto_save_draft(Compose *compose)
10545 compose->draft_timeout_tag = -1;
10546 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10551 static void compose_check_all(GtkAction *action, gpointer data)
10553 Compose *compose = (Compose *)data;
10554 if (!compose->gtkaspell)
10557 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10558 claws_spell_entry_check_all(
10559 CLAWS_SPELL_ENTRY(compose->subject_entry));
10561 gtkaspell_check_all(compose->gtkaspell);
10564 static void compose_highlight_all(GtkAction *action, gpointer data)
10566 Compose *compose = (Compose *)data;
10567 if (compose->gtkaspell) {
10568 claws_spell_entry_recheck_all(
10569 CLAWS_SPELL_ENTRY(compose->subject_entry));
10570 gtkaspell_highlight_all(compose->gtkaspell);
10574 static void compose_check_backwards(GtkAction *action, gpointer data)
10576 Compose *compose = (Compose *)data;
10577 if (!compose->gtkaspell) {
10578 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10582 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10583 claws_spell_entry_check_backwards(
10584 CLAWS_SPELL_ENTRY(compose->subject_entry));
10586 gtkaspell_check_backwards(compose->gtkaspell);
10589 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10591 Compose *compose = (Compose *)data;
10592 if (!compose->gtkaspell) {
10593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10597 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10598 claws_spell_entry_check_forwards_go(
10599 CLAWS_SPELL_ENTRY(compose->subject_entry));
10601 gtkaspell_check_forwards_go(compose->gtkaspell);
10606 *\brief Guess originating forward account from MsgInfo and several
10607 * "common preference" settings. Return NULL if no guess.
10609 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10611 PrefsAccount *account = NULL;
10613 cm_return_val_if_fail(msginfo, NULL);
10614 cm_return_val_if_fail(msginfo->folder, NULL);
10615 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10617 if (msginfo->folder->prefs->enable_default_account)
10618 account = account_find_from_id(msginfo->folder->prefs->default_account);
10621 account = msginfo->folder->folder->account;
10623 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10625 Xstrdup_a(to, msginfo->to, return NULL);
10626 extract_address(to);
10627 account = account_find_from_address(to, FALSE);
10630 if (!account && prefs_common.forward_account_autosel) {
10631 gchar cc[BUFFSIZE];
10632 if (!procheader_get_header_from_msginfo
10633 (msginfo, cc,sizeof cc , "Cc:")) {
10634 gchar *buf = cc + strlen("Cc:");
10635 extract_address(buf);
10636 account = account_find_from_address(buf, FALSE);
10640 if (!account && prefs_common.forward_account_autosel) {
10641 gchar deliveredto[BUFFSIZE];
10642 if (!procheader_get_header_from_msginfo
10643 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10644 gchar *buf = deliveredto + strlen("Delivered-To:");
10645 extract_address(buf);
10646 account = account_find_from_address(buf, FALSE);
10653 gboolean compose_close(Compose *compose)
10657 if (!g_mutex_trylock(compose->mutex)) {
10658 /* we have to wait for the (possibly deferred by auto-save)
10659 * drafting to be done, before destroying the compose under
10661 debug_print("waiting for drafting to finish...\n");
10662 compose_allow_user_actions(compose, FALSE);
10663 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10666 cm_return_val_if_fail(compose, FALSE);
10667 gtkut_widget_get_uposition(compose->window, &x, &y);
10668 if (!compose->batch) {
10669 prefs_common.compose_x = x;
10670 prefs_common.compose_y = y;
10672 g_mutex_unlock(compose->mutex);
10673 compose_destroy(compose);
10678 * Add entry field for each address in list.
10679 * \param compose E-Mail composition object.
10680 * \param listAddress List of (formatted) E-Mail addresses.
10682 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10685 node = listAddress;
10687 addr = ( gchar * ) node->data;
10688 compose_entry_append( compose, addr, COMPOSE_TO );
10689 node = g_list_next( node );
10693 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10694 guint action, gboolean opening_multiple)
10696 gchar *body = NULL;
10697 GSList *new_msglist = NULL;
10698 MsgInfo *tmp_msginfo = NULL;
10699 gboolean originally_enc = FALSE;
10700 gboolean originally_sig = FALSE;
10701 Compose *compose = NULL;
10702 gchar *s_system = NULL;
10704 cm_return_if_fail(msgview != NULL);
10706 cm_return_if_fail(msginfo_list != NULL);
10708 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10709 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10710 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10712 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10713 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10714 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10715 orig_msginfo, mimeinfo);
10716 if (tmp_msginfo != NULL) {
10717 new_msglist = g_slist_append(NULL, tmp_msginfo);
10719 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10720 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10721 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10723 tmp_msginfo->folder = orig_msginfo->folder;
10724 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10725 if (orig_msginfo->tags) {
10726 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10727 tmp_msginfo->folder->tags_dirty = TRUE;
10733 if (!opening_multiple)
10734 body = messageview_get_selection(msgview);
10737 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10738 procmsg_msginfo_free(tmp_msginfo);
10739 g_slist_free(new_msglist);
10741 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10743 if (compose && originally_enc) {
10744 compose_force_encryption(compose, compose->account, FALSE, s_system);
10747 if (compose && originally_sig && compose->account->default_sign_reply) {
10748 compose_force_signing(compose, compose->account, s_system);
10754 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10757 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10758 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10759 GSList *cur = msginfo_list;
10760 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10761 "messages. Opening the windows "
10762 "could take some time. Do you "
10763 "want to continue?"),
10764 g_slist_length(msginfo_list));
10765 if (g_slist_length(msginfo_list) > 9
10766 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10767 != G_ALERTALTERNATE) {
10772 /* We'll open multiple compose windows */
10773 /* let the WM place the next windows */
10774 compose_force_window_origin = FALSE;
10775 for (; cur; cur = cur->next) {
10777 tmplist.data = cur->data;
10778 tmplist.next = NULL;
10779 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10781 compose_force_window_origin = TRUE;
10783 /* forwarding multiple mails as attachments is done via a
10784 * single compose window */
10785 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10789 void compose_set_position(Compose *compose, gint pos)
10791 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10793 gtkut_text_view_set_position(text, pos);
10796 gboolean compose_search_string(Compose *compose,
10797 const gchar *str, gboolean case_sens)
10799 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10801 return gtkut_text_view_search_string(text, str, case_sens);
10804 gboolean compose_search_string_backward(Compose *compose,
10805 const gchar *str, gboolean case_sens)
10807 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10809 return gtkut_text_view_search_string_backward(text, str, case_sens);
10812 /* allocate a msginfo structure and populate its data from a compose data structure */
10813 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10815 MsgInfo *newmsginfo;
10817 gchar buf[BUFFSIZE];
10819 cm_return_val_if_fail( compose != NULL, NULL );
10821 newmsginfo = procmsg_msginfo_new();
10824 get_rfc822_date(buf, sizeof(buf));
10825 newmsginfo->date = g_strdup(buf);
10828 if (compose->from_name) {
10829 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10830 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10834 if (compose->subject_entry)
10835 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10837 /* to, cc, reply-to, newsgroups */
10838 for (list = compose->header_list; list; list = list->next) {
10839 gchar *header = gtk_editable_get_chars(
10841 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10842 gchar *entry = gtk_editable_get_chars(
10843 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10845 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10846 if ( newmsginfo->to == NULL ) {
10847 newmsginfo->to = g_strdup(entry);
10848 } else if (entry && *entry) {
10849 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10850 g_free(newmsginfo->to);
10851 newmsginfo->to = tmp;
10854 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10855 if ( newmsginfo->cc == NULL ) {
10856 newmsginfo->cc = g_strdup(entry);
10857 } else if (entry && *entry) {
10858 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10859 g_free(newmsginfo->cc);
10860 newmsginfo->cc = tmp;
10863 if ( strcasecmp(header,
10864 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10865 if ( newmsginfo->newsgroups == NULL ) {
10866 newmsginfo->newsgroups = g_strdup(entry);
10867 } else if (entry && *entry) {
10868 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10869 g_free(newmsginfo->newsgroups);
10870 newmsginfo->newsgroups = tmp;
10878 /* other data is unset */
10884 /* update compose's dictionaries from folder dict settings */
10885 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10886 FolderItem *folder_item)
10888 cm_return_if_fail(compose != NULL);
10890 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10891 FolderItemPrefs *prefs = folder_item->prefs;
10893 if (prefs->enable_default_dictionary)
10894 gtkaspell_change_dict(compose->gtkaspell,
10895 prefs->default_dictionary, FALSE);
10896 if (folder_item->prefs->enable_default_alt_dictionary)
10897 gtkaspell_change_alt_dict(compose->gtkaspell,
10898 prefs->default_alt_dictionary);
10899 if (prefs->enable_default_dictionary
10900 || prefs->enable_default_alt_dictionary)
10901 compose_spell_menu_changed(compose);