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 int 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);
499 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
500 ComposeHeaderEntry *headerentry);
502 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
504 static void compose_allow_user_actions (Compose *compose, gboolean allow);
506 static void compose_nothing_cb (GtkAction *action, gpointer data)
512 static void compose_check_all (GtkAction *action, gpointer data);
513 static void compose_highlight_all (GtkAction *action, gpointer data);
514 static void compose_check_backwards (GtkAction *action, gpointer data);
515 static void compose_check_forwards_go (GtkAction *action, gpointer data);
518 static gint compose_defer_auto_save_draft (Compose *compose);
519 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
521 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
524 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
525 FolderItem *folder_item);
527 static void compose_attach_update_label(Compose *compose);
529 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
531 static GtkActionEntry compose_popup_entries[] =
533 {"Compose", NULL, "Compose" },
534 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
535 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
536 {"Compose/---", NULL, "---", NULL, NULL, NULL },
537 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
540 static GtkActionEntry compose_entries[] =
542 {"Menu", NULL, "Menu" },
544 {"Message", NULL, N_("_Message") },
545 {"Edit", NULL, N_("_Edit") },
547 {"Spelling", NULL, N_("_Spelling") },
549 {"Options", NULL, N_("_Options") },
550 {"Tools", NULL, N_("_Tools") },
551 {"Help", NULL, N_("_Help") },
553 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
554 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
555 {"Message/---", NULL, "---" },
557 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
558 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
559 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
560 /* {"Message/---", NULL, "---" }, */
561 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
562 /* {"Message/---", NULL, "---" }, */
563 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
566 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
567 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
568 {"Edit/---", NULL, "---" },
570 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
571 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
572 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
574 {"Edit/SpecialPaste", NULL, N_("Special paste") },
575 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
576 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
577 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
579 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
581 {"Edit/Advanced", NULL, N_("A_dvanced") },
582 {"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*/
583 {"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*/
584 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
585 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"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*/
590 {"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*/
591 {"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*/
592 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
593 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
594 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
595 {"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*/
597 /* {"Edit/---", NULL, "---" }, */
598 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
600 /* {"Edit/---", NULL, "---" }, */
601 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
602 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
603 /* {"Edit/---", NULL, "---" }, */
604 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
607 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
608 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
609 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
610 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
612 {"Spelling/---", NULL, "---" },
613 {"Spelling/Options", NULL, N_("_Options") },
618 {"Options/ReplyMode", NULL, N_("Reply _mode") },
619 {"Options/---", NULL, "---" },
620 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
621 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
623 /* {"Options/---", NULL, "---" }, */
625 {"Options/Priority", NULL, N_("_Priority") },
627 {"Options/Encoding", NULL, N_("Character _encoding") },
628 {"Options/Encoding/---", NULL, "---" },
629 #define ENC_ACTION(cs_char,c_char,string) \
630 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
632 {"Options/Encoding/Western", NULL, N_("Western European") },
633 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
634 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
635 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
636 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
637 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
638 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
639 {"Options/Encoding/Korean", NULL, N_("Korean") },
640 {"Options/Encoding/Thai", NULL, N_("Thai") },
643 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
645 {"Tools/Template", NULL, N_("_Template") },
646 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Tools/Actions", NULL, N_("Actio_ns") },
648 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
651 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
654 static GtkToggleActionEntry compose_toggle_entries[] =
656 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
657 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
658 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
659 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
660 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
661 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
662 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
665 static GtkRadioActionEntry compose_radio_rm_entries[] =
667 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
668 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
669 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
670 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
673 static GtkRadioActionEntry compose_radio_prio_entries[] =
675 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
677 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
678 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
679 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
682 static GtkRadioActionEntry compose_radio_enc_entries[] =
684 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
717 static GtkTargetEntry compose_mime_types[] =
719 {"text/uri-list", 0, 0},
720 {"UTF8_STRING", 0, 0},
724 static gboolean compose_put_existing_to_front(MsgInfo *info)
726 GList *compose_list = compose_get_compose_list();
730 for (elem = compose_list; elem != NULL && elem->data != NULL;
732 Compose *c = (Compose*)elem->data;
734 if (!c->targetinfo || !c->targetinfo->msgid ||
738 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
739 gtkut_window_popup(c->window);
747 static GdkColor quote_color1 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
749 static GdkColor quote_color2 =
750 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
751 static GdkColor quote_color3 =
752 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor1 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_bgcolor2 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_bgcolor3 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor signature_color = {
768 static GdkColor uri_color = {
775 static void compose_create_tags(GtkTextView *text, Compose *compose)
777 GtkTextBuffer *buffer;
778 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 buffer = gtk_text_view_get_buffer(text);
786 if (prefs_common.enable_color) {
787 /* grab the quote colors, converting from an int to a GdkColor */
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
798 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
800 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
805 signature_color = quote_color1 = quote_color2 = quote_color3 =
806 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
809 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
810 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
811 "foreground-gdk", "e_color1,
812 "paragraph-background-gdk", "e_bgcolor1,
814 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
815 "foreground-gdk", "e_color2,
816 "paragraph-background-gdk", "e_bgcolor2,
818 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
819 "foreground-gdk", "e_color3,
820 "paragraph-background-gdk", "e_bgcolor3,
823 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
824 "foreground-gdk", "e_color1,
826 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
827 "foreground-gdk", "e_color2,
829 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
830 "foreground-gdk", "e_color3,
834 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
835 "foreground-gdk", &signature_color,
838 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
839 "foreground-gdk", &uri_color,
841 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
842 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
844 color[0] = quote_color1;
845 color[1] = quote_color2;
846 color[2] = quote_color3;
847 color[3] = quote_bgcolor1;
848 color[4] = quote_bgcolor2;
849 color[5] = quote_bgcolor3;
850 color[6] = signature_color;
851 color[7] = uri_color;
852 cmap = gdk_drawable_get_colormap(compose->window->window);
853 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
855 for (i = 0; i < 8; i++) {
856 if (success[i] == FALSE) {
859 g_warning("Compose: color allocation failed.\n");
860 style = gtk_widget_get_style(GTK_WIDGET(text));
861 quote_color1 = quote_color2 = quote_color3 =
862 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
863 signature_color = uri_color = black;
868 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
869 GPtrArray *attach_files)
871 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
874 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
876 return compose_generic_new(account, mailto, item, NULL, NULL);
879 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
881 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
884 #define SCROLL_TO_CURSOR(compose) { \
885 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
886 gtk_text_view_get_buffer( \
887 GTK_TEXT_VIEW(compose->text))); \
888 gtk_text_view_scroll_mark_onscreen( \
889 GTK_TEXT_VIEW(compose->text), \
893 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
896 if (folderidentifier) {
897 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
898 prefs_common.compose_save_to_history = add_history(
899 prefs_common.compose_save_to_history, folderidentifier);
900 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
901 prefs_common.compose_save_to_history);
904 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
905 if (folderidentifier)
906 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
908 gtk_entry_set_text(GTK_ENTRY(entry), "");
911 static gchar *compose_get_save_to(Compose *compose)
914 gchar *result = NULL;
915 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
916 result = gtk_editable_get_chars(entry, 0, -1);
919 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
920 prefs_common.compose_save_to_history = add_history(
921 prefs_common.compose_save_to_history, result);
922 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
923 prefs_common.compose_save_to_history);
928 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
929 GPtrArray *attach_files, GList *listAddress )
932 GtkTextView *textview;
933 GtkTextBuffer *textbuf;
935 const gchar *subject_format = NULL;
936 const gchar *body_format = NULL;
937 gchar *mailto_from = NULL;
938 PrefsAccount *mailto_account = NULL;
939 MsgInfo* dummyinfo = NULL;
941 /* check if mailto defines a from */
942 if (mailto && *mailto != '\0') {
943 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
944 /* mailto defines a from, check if we can get account prefs from it,
945 if not, the account prefs will be guessed using other ways, but we'll keep
948 mailto_account = account_find_from_address(mailto_from, TRUE);
950 account = mailto_account;
953 /* if no account prefs set from mailto, set if from folder prefs (if any) */
954 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
955 account = account_find_from_id(item->prefs->default_account);
957 /* if no account prefs set, fallback to the current one */
958 if (!account) account = cur_account;
959 cm_return_val_if_fail(account != NULL, NULL);
961 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
963 /* override from name if mailto asked for it */
965 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
968 /* override from name according to folder properties */
969 if (item && item->prefs &&
970 item->prefs->compose_with_format &&
971 item->prefs->compose_override_from_format &&
972 *item->prefs->compose_override_from_format != '\0') {
977 dummyinfo = compose_msginfo_new_from_compose(compose);
979 /* decode \-escape sequences in the internal representation of the quote format */
980 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
981 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
984 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
987 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
989 quote_fmt_scan_string(tmp);
992 buf = quote_fmt_get_buffer();
994 alertpanel_error(_("New message From format error."));
996 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
997 quote_fmt_reset_vartable();
1002 compose->replyinfo = NULL;
1003 compose->fwdinfo = NULL;
1005 textview = GTK_TEXT_VIEW(compose->text);
1006 textbuf = gtk_text_view_get_buffer(textview);
1007 compose_create_tags(textview, compose);
1009 undo_block(compose->undostruct);
1011 compose_set_dictionaries_from_folder_prefs(compose, item);
1014 if (account->auto_sig)
1015 compose_insert_sig(compose, FALSE);
1016 gtk_text_buffer_get_start_iter(textbuf, &iter);
1017 gtk_text_buffer_place_cursor(textbuf, &iter);
1019 if (account->protocol != A_NNTP) {
1020 if (mailto && *mailto != '\0') {
1021 compose_entries_set(compose, mailto, COMPOSE_TO);
1023 } else if (item && item->prefs) {
1024 if (item->prefs->enable_default_bcc) {
1025 compose_entry_append(compose, item->prefs->default_bcc, COMPOSE_BCC);
1027 if (item->prefs->enable_default_cc) {
1028 compose_entry_append(compose, item->prefs->default_cc, COMPOSE_CC);
1030 if (item->prefs->enable_default_replyto) {
1031 compose_entry_append(compose, item->prefs->default_replyto, COMPOSE_REPLYTO);
1033 if (item->prefs->enable_default_to) {
1034 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1035 compose_entry_mark_default_to(compose, item->prefs->default_to);
1038 if (item && item->ret_rcpt) {
1039 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1042 if (mailto && *mailto != '\0') {
1043 if (!strchr(mailto, '@'))
1044 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1046 compose_entries_set(compose, mailto, COMPOSE_TO);
1047 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1048 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1051 * CLAWS: just don't allow return receipt request, even if the user
1052 * may want to send an email. simple but foolproof.
1054 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1056 compose_add_field_list( compose, listAddress );
1058 if (item && item->prefs && item->prefs->compose_with_format) {
1059 subject_format = item->prefs->compose_subject_format;
1060 body_format = item->prefs->compose_body_format;
1061 } else if (account->compose_with_format) {
1062 subject_format = account->compose_subject_format;
1063 body_format = account->compose_body_format;
1064 } else if (prefs_common.compose_with_format) {
1065 subject_format = prefs_common.compose_subject_format;
1066 body_format = prefs_common.compose_body_format;
1069 if (subject_format || body_format) {
1072 && *subject_format != '\0' )
1074 gchar *subject = NULL;
1079 dummyinfo = compose_msginfo_new_from_compose(compose);
1081 /* decode \-escape sequences in the internal representation of the quote format */
1082 tmp = malloc(strlen(subject_format)+1);
1083 pref_get_unescaped_pref(tmp, subject_format);
1085 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1087 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1088 compose->gtkaspell);
1090 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1092 quote_fmt_scan_string(tmp);
1095 buf = quote_fmt_get_buffer();
1097 alertpanel_error(_("New message subject format error."));
1099 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1100 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1101 quote_fmt_reset_vartable();
1108 && *body_format != '\0' )
1111 GtkTextBuffer *buffer;
1112 GtkTextIter start, end;
1116 dummyinfo = compose_msginfo_new_from_compose(compose);
1118 text = GTK_TEXT_VIEW(compose->text);
1119 buffer = gtk_text_view_get_buffer(text);
1120 gtk_text_buffer_get_start_iter(buffer, &start);
1121 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1122 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1124 compose_quote_fmt(compose, dummyinfo,
1126 NULL, tmp, FALSE, TRUE,
1127 _("New message body format error at line %d."));
1128 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1129 quote_fmt_reset_vartable();
1135 procmsg_msginfo_free( dummyinfo );
1141 for (i = 0; i < attach_files->len; i++) {
1142 file = g_ptr_array_index(attach_files, i);
1143 compose_attach_append(compose, file, file, NULL);
1147 compose_show_first_last_header(compose, TRUE);
1149 /* Set save folder */
1150 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1151 gchar *folderidentifier;
1153 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1154 folderidentifier = folder_item_get_identifier(item);
1155 compose_set_save_to(compose, folderidentifier);
1156 g_free(folderidentifier);
1159 gtk_widget_grab_focus(compose->header_last->entry);
1161 undo_unblock(compose->undostruct);
1163 if (prefs_common.auto_exteditor)
1164 compose_exec_ext_editor(compose);
1166 compose->draft_timeout_tag = -1;
1167 SCROLL_TO_CURSOR(compose);
1169 compose->modified = FALSE;
1170 compose_set_title(compose);
1174 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1175 gboolean override_pref, const gchar *system)
1177 const gchar *privacy = NULL;
1179 cm_return_if_fail(compose != NULL);
1180 cm_return_if_fail(account != NULL);
1182 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1187 else if (account->default_privacy_system
1188 && strlen(account->default_privacy_system)) {
1189 privacy = account->default_privacy_system;
1191 GSList *privacy_avail = privacy_get_system_ids();
1192 if (privacy_avail && g_slist_length(privacy_avail)) {
1193 privacy = (gchar *)(privacy_avail->data);
1196 if (privacy != NULL) {
1198 g_free(compose->privacy_system);
1199 compose->privacy_system = NULL;
1201 if (compose->privacy_system == NULL)
1202 compose->privacy_system = g_strdup(privacy);
1203 else if (*(compose->privacy_system) == '\0') {
1204 g_free(compose->privacy_system);
1205 compose->privacy_system = g_strdup(privacy);
1207 compose_update_privacy_system_menu_item(compose, FALSE);
1208 compose_use_encryption(compose, TRUE);
1212 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1214 const gchar *privacy = NULL;
1218 else if (account->default_privacy_system
1219 && strlen(account->default_privacy_system)) {
1220 privacy = account->default_privacy_system;
1222 GSList *privacy_avail = privacy_get_system_ids();
1223 if (privacy_avail && g_slist_length(privacy_avail)) {
1224 privacy = (gchar *)(privacy_avail->data);
1228 if (privacy != NULL) {
1230 g_free(compose->privacy_system);
1231 compose->privacy_system = NULL;
1233 if (compose->privacy_system == NULL)
1234 compose->privacy_system = g_strdup(privacy);
1235 compose_update_privacy_system_menu_item(compose, FALSE);
1236 compose_use_signing(compose, TRUE);
1240 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1244 Compose *compose = NULL;
1246 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1248 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1249 cm_return_val_if_fail(msginfo != NULL, NULL);
1251 list_len = g_slist_length(msginfo_list);
1255 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1256 FALSE, prefs_common.default_reply_list, FALSE, body);
1258 case COMPOSE_REPLY_WITH_QUOTE:
1259 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1260 FALSE, prefs_common.default_reply_list, FALSE, body);
1262 case COMPOSE_REPLY_WITHOUT_QUOTE:
1263 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1264 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1266 case COMPOSE_REPLY_TO_SENDER:
1267 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1268 FALSE, FALSE, TRUE, body);
1270 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1271 compose = compose_followup_and_reply_to(msginfo,
1272 COMPOSE_QUOTE_CHECK,
1273 FALSE, FALSE, body);
1275 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1276 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1277 FALSE, FALSE, TRUE, body);
1279 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1280 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1281 FALSE, FALSE, TRUE, NULL);
1283 case COMPOSE_REPLY_TO_ALL:
1284 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1285 TRUE, FALSE, FALSE, body);
1287 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1288 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1289 TRUE, FALSE, FALSE, body);
1291 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1292 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1293 TRUE, FALSE, FALSE, NULL);
1295 case COMPOSE_REPLY_TO_LIST:
1296 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1297 FALSE, TRUE, FALSE, body);
1299 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1300 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1301 FALSE, TRUE, FALSE, body);
1303 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1304 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1305 FALSE, TRUE, FALSE, NULL);
1307 case COMPOSE_FORWARD:
1308 if (prefs_common.forward_as_attachment) {
1309 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1312 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1316 case COMPOSE_FORWARD_INLINE:
1317 /* check if we reply to more than one Message */
1318 if (list_len == 1) {
1319 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1322 /* more messages FALL THROUGH */
1323 case COMPOSE_FORWARD_AS_ATTACH:
1324 compose = compose_forward_multiple(NULL, msginfo_list);
1326 case COMPOSE_REDIRECT:
1327 compose = compose_redirect(NULL, msginfo, FALSE);
1330 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1333 if (compose == NULL) {
1334 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1338 compose->rmode = mode;
1339 switch (compose->rmode) {
1341 case COMPOSE_REPLY_WITH_QUOTE:
1342 case COMPOSE_REPLY_WITHOUT_QUOTE:
1343 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1344 debug_print("reply mode Normal\n");
1345 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1346 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1348 case COMPOSE_REPLY_TO_SENDER:
1349 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1350 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1351 debug_print("reply mode Sender\n");
1352 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1354 case COMPOSE_REPLY_TO_ALL:
1355 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1356 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1357 debug_print("reply mode All\n");
1358 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1360 case COMPOSE_REPLY_TO_LIST:
1361 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1362 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1363 debug_print("reply mode List\n");
1364 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1372 static Compose *compose_reply(MsgInfo *msginfo,
1373 ComposeQuoteMode quote_mode,
1379 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1380 to_sender, FALSE, body);
1383 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1384 ComposeQuoteMode quote_mode,
1389 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1390 to_sender, TRUE, body);
1393 static void compose_extract_original_charset(Compose *compose)
1395 MsgInfo *info = NULL;
1396 if (compose->replyinfo) {
1397 info = compose->replyinfo;
1398 } else if (compose->fwdinfo) {
1399 info = compose->fwdinfo;
1400 } else if (compose->targetinfo) {
1401 info = compose->targetinfo;
1404 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1405 MimeInfo *partinfo = mimeinfo;
1406 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1407 partinfo = procmime_mimeinfo_next(partinfo);
1409 compose->orig_charset =
1410 g_strdup(procmime_mimeinfo_get_parameter(
1411 partinfo, "charset"));
1413 procmime_mimeinfo_free_all(mimeinfo);
1417 #define SIGNAL_BLOCK(buffer) { \
1418 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1419 G_CALLBACK(compose_changed_cb), \
1421 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1422 G_CALLBACK(text_inserted), \
1426 #define SIGNAL_UNBLOCK(buffer) { \
1427 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1428 G_CALLBACK(compose_changed_cb), \
1430 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1431 G_CALLBACK(text_inserted), \
1435 static Compose *compose_generic_reply(MsgInfo *msginfo,
1436 ComposeQuoteMode quote_mode,
1437 gboolean to_all, gboolean to_ml,
1439 gboolean followup_and_reply_to,
1443 PrefsAccount *account = NULL;
1444 GtkTextView *textview;
1445 GtkTextBuffer *textbuf;
1446 gboolean quote = FALSE;
1447 const gchar *qmark = NULL;
1448 const gchar *body_fmt = NULL;
1449 gchar *s_system = NULL;
1451 cm_return_val_if_fail(msginfo != NULL, NULL);
1452 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1454 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1456 cm_return_val_if_fail(account != NULL, NULL);
1458 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1460 compose->updating = TRUE;
1462 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1465 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1466 if (!compose->replyinfo)
1467 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1469 compose_extract_original_charset(compose);
1471 if (msginfo->folder && msginfo->folder->ret_rcpt)
1472 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1474 /* Set save folder */
1475 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1476 gchar *folderidentifier;
1478 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1479 folderidentifier = folder_item_get_identifier(msginfo->folder);
1480 compose_set_save_to(compose, folderidentifier);
1481 g_free(folderidentifier);
1484 if (compose_parse_header(compose, msginfo) < 0) {
1485 compose->updating = FALSE;
1486 compose_destroy(compose);
1490 /* override from name according to folder properties */
1491 if (msginfo->folder && msginfo->folder->prefs &&
1492 msginfo->folder->prefs->reply_with_format &&
1493 msginfo->folder->prefs->reply_override_from_format &&
1494 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1499 /* decode \-escape sequences in the internal representation of the quote format */
1500 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1501 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1504 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1505 compose->gtkaspell);
1507 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1509 quote_fmt_scan_string(tmp);
1512 buf = quote_fmt_get_buffer();
1514 alertpanel_error(_("Message reply From format error."));
1516 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1517 quote_fmt_reset_vartable();
1522 textview = (GTK_TEXT_VIEW(compose->text));
1523 textbuf = gtk_text_view_get_buffer(textview);
1524 compose_create_tags(textview, compose);
1526 undo_block(compose->undostruct);
1528 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1531 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1532 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1533 /* use the reply format of folder (if enabled), or the account's one
1534 (if enabled) or fallback to the global reply format, which is always
1535 enabled (even if empty), and use the relevant quotemark */
1537 if (msginfo->folder && msginfo->folder->prefs &&
1538 msginfo->folder->prefs->reply_with_format) {
1539 qmark = msginfo->folder->prefs->reply_quotemark;
1540 body_fmt = msginfo->folder->prefs->reply_body_format;
1542 } else if (account->reply_with_format) {
1543 qmark = account->reply_quotemark;
1544 body_fmt = account->reply_body_format;
1547 qmark = prefs_common.quotemark;
1548 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1549 body_fmt = gettext(prefs_common.quotefmt);
1556 /* empty quotemark is not allowed */
1557 if (qmark == NULL || *qmark == '\0')
1559 compose_quote_fmt(compose, compose->replyinfo,
1560 body_fmt, qmark, body, FALSE, TRUE,
1561 _("Message reply format error at line %d."));
1562 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1563 quote_fmt_reset_vartable();
1566 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1567 compose_force_encryption(compose, account, FALSE, s_system);
1570 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1571 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1572 compose_force_signing(compose, account, s_system);
1576 SIGNAL_BLOCK(textbuf);
1578 if (account->auto_sig)
1579 compose_insert_sig(compose, FALSE);
1581 compose_wrap_all(compose);
1583 SIGNAL_UNBLOCK(textbuf);
1585 gtk_widget_grab_focus(compose->text);
1587 undo_unblock(compose->undostruct);
1589 if (prefs_common.auto_exteditor)
1590 compose_exec_ext_editor(compose);
1592 compose->modified = FALSE;
1593 compose_set_title(compose);
1595 compose->updating = FALSE;
1596 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1597 SCROLL_TO_CURSOR(compose);
1599 if (compose->deferred_destroy) {
1600 compose_destroy(compose);
1607 #define INSERT_FW_HEADER(var, hdr) \
1608 if (msginfo->var && *msginfo->var) { \
1609 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1610 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1611 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1614 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1615 gboolean as_attach, const gchar *body,
1616 gboolean no_extedit,
1620 GtkTextView *textview;
1621 GtkTextBuffer *textbuf;
1624 cm_return_val_if_fail(msginfo != NULL, NULL);
1625 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1628 !(account = compose_guess_forward_account_from_msginfo
1630 account = cur_account;
1632 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1634 compose->updating = TRUE;
1635 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1636 if (!compose->fwdinfo)
1637 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1639 compose_extract_original_charset(compose);
1641 if (msginfo->subject && *msginfo->subject) {
1642 gchar *buf, *buf2, *p;
1644 buf = p = g_strdup(msginfo->subject);
1645 p += subject_get_prefix_length(p);
1646 memmove(buf, p, strlen(p) + 1);
1648 buf2 = g_strdup_printf("Fw: %s", buf);
1649 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1655 /* override from name according to folder properties */
1656 if (msginfo->folder && msginfo->folder->prefs &&
1657 msginfo->folder->prefs->forward_with_format &&
1658 msginfo->folder->prefs->forward_override_from_format &&
1659 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1663 MsgInfo *full_msginfo = NULL;
1666 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1668 full_msginfo = procmsg_msginfo_copy(msginfo);
1670 /* decode \-escape sequences in the internal representation of the quote format */
1671 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1672 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1675 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1676 compose->gtkaspell);
1678 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1680 quote_fmt_scan_string(tmp);
1683 buf = quote_fmt_get_buffer();
1685 alertpanel_error(_("Message forward From format error."));
1687 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1688 quote_fmt_reset_vartable();
1691 procmsg_msginfo_free(full_msginfo);
1694 textview = GTK_TEXT_VIEW(compose->text);
1695 textbuf = gtk_text_view_get_buffer(textview);
1696 compose_create_tags(textview, compose);
1698 undo_block(compose->undostruct);
1702 msgfile = procmsg_get_message_file(msginfo);
1703 if (!is_file_exist(msgfile))
1704 g_warning("%s: file not exist\n", msgfile);
1706 compose_attach_append(compose, msgfile, msgfile,
1711 const gchar *qmark = NULL;
1712 const gchar *body_fmt = NULL;
1713 MsgInfo *full_msginfo;
1715 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1716 body_fmt = gettext(prefs_common.fw_quotefmt);
1720 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1722 full_msginfo = procmsg_msginfo_copy(msginfo);
1724 /* use the forward format of folder (if enabled), or the account's one
1725 (if enabled) or fallback to the global forward format, which is always
1726 enabled (even if empty), and use the relevant quotemark */
1727 if (msginfo->folder && msginfo->folder->prefs &&
1728 msginfo->folder->prefs->forward_with_format) {
1729 qmark = msginfo->folder->prefs->forward_quotemark;
1730 body_fmt = msginfo->folder->prefs->forward_body_format;
1732 } else if (account->forward_with_format) {
1733 qmark = account->forward_quotemark;
1734 body_fmt = account->forward_body_format;
1737 qmark = prefs_common.fw_quotemark;
1738 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1739 body_fmt = gettext(prefs_common.fw_quotefmt);
1744 /* empty quotemark is not allowed */
1745 if (qmark == NULL || *qmark == '\0')
1748 compose_quote_fmt(compose, full_msginfo,
1749 body_fmt, qmark, body, FALSE, TRUE,
1750 _("Message forward format error at line %d."));
1751 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1752 quote_fmt_reset_vartable();
1753 compose_attach_parts(compose, msginfo);
1755 procmsg_msginfo_free(full_msginfo);
1758 SIGNAL_BLOCK(textbuf);
1760 if (account->auto_sig)
1761 compose_insert_sig(compose, FALSE);
1763 compose_wrap_all(compose);
1765 SIGNAL_UNBLOCK(textbuf);
1767 gtk_text_buffer_get_start_iter(textbuf, &iter);
1768 gtk_text_buffer_place_cursor(textbuf, &iter);
1770 gtk_widget_grab_focus(compose->header_last->entry);
1772 if (!no_extedit && prefs_common.auto_exteditor)
1773 compose_exec_ext_editor(compose);
1776 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1777 gchar *folderidentifier;
1779 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1780 folderidentifier = folder_item_get_identifier(msginfo->folder);
1781 compose_set_save_to(compose, folderidentifier);
1782 g_free(folderidentifier);
1785 undo_unblock(compose->undostruct);
1787 compose->modified = FALSE;
1788 compose_set_title(compose);
1790 compose->updating = FALSE;
1791 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1792 SCROLL_TO_CURSOR(compose);
1794 if (compose->deferred_destroy) {
1795 compose_destroy(compose);
1802 #undef INSERT_FW_HEADER
1804 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1807 GtkTextView *textview;
1808 GtkTextBuffer *textbuf;
1812 gboolean single_mail = TRUE;
1814 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1816 if (g_slist_length(msginfo_list) > 1)
1817 single_mail = FALSE;
1819 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1820 if (((MsgInfo *)msginfo->data)->folder == NULL)
1823 /* guess account from first selected message */
1825 !(account = compose_guess_forward_account_from_msginfo
1826 (msginfo_list->data)))
1827 account = cur_account;
1829 cm_return_val_if_fail(account != NULL, NULL);
1831 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1832 if (msginfo->data) {
1833 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1834 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1838 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1839 g_warning("no msginfo_list");
1843 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1845 compose->updating = TRUE;
1847 /* override from name according to folder properties */
1848 if (msginfo_list->data) {
1849 MsgInfo *msginfo = msginfo_list->data;
1851 if (msginfo->folder && msginfo->folder->prefs &&
1852 msginfo->folder->prefs->forward_with_format &&
1853 msginfo->folder->prefs->forward_override_from_format &&
1854 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1859 /* decode \-escape sequences in the internal representation of the quote format */
1860 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1861 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1864 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1865 compose->gtkaspell);
1867 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1869 quote_fmt_scan_string(tmp);
1872 buf = quote_fmt_get_buffer();
1874 alertpanel_error(_("Message forward From format error."));
1876 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1877 quote_fmt_reset_vartable();
1883 textview = GTK_TEXT_VIEW(compose->text);
1884 textbuf = gtk_text_view_get_buffer(textview);
1885 compose_create_tags(textview, compose);
1887 undo_block(compose->undostruct);
1888 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1889 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1891 if (!is_file_exist(msgfile))
1892 g_warning("%s: file not exist\n", msgfile);
1894 compose_attach_append(compose, msgfile, msgfile,
1900 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1901 if (info->subject && *info->subject) {
1902 gchar *buf, *buf2, *p;
1904 buf = p = g_strdup(info->subject);
1905 p += subject_get_prefix_length(p);
1906 memmove(buf, p, strlen(p) + 1);
1908 buf2 = g_strdup_printf("Fw: %s", buf);
1909 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1915 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1916 _("Fw: multiple emails"));
1919 SIGNAL_BLOCK(textbuf);
1921 if (account->auto_sig)
1922 compose_insert_sig(compose, FALSE);
1924 compose_wrap_all(compose);
1926 SIGNAL_UNBLOCK(textbuf);
1928 gtk_text_buffer_get_start_iter(textbuf, &iter);
1929 gtk_text_buffer_place_cursor(textbuf, &iter);
1931 gtk_widget_grab_focus(compose->header_last->entry);
1932 undo_unblock(compose->undostruct);
1933 compose->modified = FALSE;
1934 compose_set_title(compose);
1936 compose->updating = FALSE;
1937 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1938 SCROLL_TO_CURSOR(compose);
1940 if (compose->deferred_destroy) {
1941 compose_destroy(compose);
1948 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1950 GtkTextIter start = *iter;
1951 GtkTextIter end_iter;
1952 int start_pos = gtk_text_iter_get_offset(&start);
1954 if (!compose->account->sig_sep)
1957 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1958 start_pos+strlen(compose->account->sig_sep));
1960 /* check sig separator */
1961 str = gtk_text_iter_get_text(&start, &end_iter);
1962 if (!strcmp(str, compose->account->sig_sep)) {
1964 /* check end of line (\n) */
1965 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1966 start_pos+strlen(compose->account->sig_sep));
1967 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1968 start_pos+strlen(compose->account->sig_sep)+1);
1969 tmp = gtk_text_iter_get_text(&start, &end_iter);
1970 if (!strcmp(tmp,"\n")) {
1982 static void compose_colorize_signature(Compose *compose)
1984 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1986 GtkTextIter end_iter;
1987 gtk_text_buffer_get_start_iter(buffer, &iter);
1988 while (gtk_text_iter_forward_line(&iter))
1989 if (compose_is_sig_separator(compose, buffer, &iter)) {
1990 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1991 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1995 #define BLOCK_WRAP() { \
1996 prev_autowrap = compose->autowrap; \
1997 buffer = gtk_text_view_get_buffer( \
1998 GTK_TEXT_VIEW(compose->text)); \
1999 compose->autowrap = FALSE; \
2001 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2002 G_CALLBACK(compose_changed_cb), \
2004 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2005 G_CALLBACK(text_inserted), \
2008 #define UNBLOCK_WRAP() { \
2009 compose->autowrap = prev_autowrap; \
2010 if (compose->autowrap) { \
2011 gint old = compose->draft_timeout_tag; \
2012 compose->draft_timeout_tag = -2; \
2013 compose_wrap_all(compose); \
2014 compose->draft_timeout_tag = old; \
2017 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2018 G_CALLBACK(compose_changed_cb), \
2020 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2021 G_CALLBACK(text_inserted), \
2025 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2027 Compose *compose = NULL;
2028 PrefsAccount *account = NULL;
2029 GtkTextView *textview;
2030 GtkTextBuffer *textbuf;
2034 gchar buf[BUFFSIZE];
2035 gboolean use_signing = FALSE;
2036 gboolean use_encryption = FALSE;
2037 gchar *privacy_system = NULL;
2038 int priority = PRIORITY_NORMAL;
2039 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2040 gboolean autowrap = prefs_common.autowrap;
2041 gboolean autoindent = prefs_common.auto_indent;
2043 cm_return_val_if_fail(msginfo != NULL, NULL);
2044 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2046 if (compose_put_existing_to_front(msginfo)) {
2050 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2051 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2052 gchar queueheader_buf[BUFFSIZE];
2055 /* Select Account from queue headers */
2056 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2057 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2058 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2059 account = account_find_from_id(id);
2061 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2062 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2063 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2064 account = account_find_from_id(id);
2066 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2067 sizeof(queueheader_buf), "NAID:")) {
2068 id = atoi(&queueheader_buf[strlen("NAID:")]);
2069 account = account_find_from_id(id);
2071 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2072 sizeof(queueheader_buf), "MAID:")) {
2073 id = atoi(&queueheader_buf[strlen("MAID:")]);
2074 account = account_find_from_id(id);
2076 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2077 sizeof(queueheader_buf), "S:")) {
2078 account = account_find_from_address(queueheader_buf, FALSE);
2080 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2081 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2082 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2083 use_signing = param;
2086 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2087 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2088 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2089 use_signing = param;
2092 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2093 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2094 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2095 use_encryption = param;
2097 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2098 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2099 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2100 use_encryption = param;
2102 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2103 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2104 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2107 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2108 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2109 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2112 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2113 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2114 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2116 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2117 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2118 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2121 sizeof(queueheader_buf), "X-Priority: ")) {
2122 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2125 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2126 sizeof(queueheader_buf), "RMID:")) {
2127 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2128 if (tokens[0] && tokens[1] && tokens[2]) {
2129 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2130 if (orig_item != NULL) {
2131 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2136 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "FMID:")) {
2138 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2139 if (tokens[0] && tokens[1] && tokens[2]) {
2140 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2141 if (orig_item != NULL) {
2142 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2148 account = msginfo->folder->folder->account;
2151 if (!account && prefs_common.reedit_account_autosel) {
2152 gchar from[BUFFSIZE];
2153 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2154 extract_address(from);
2155 account = account_find_from_address(from, FALSE);
2159 account = cur_account;
2161 cm_return_val_if_fail(account != NULL, NULL);
2163 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2165 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2166 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2167 compose->autowrap = autowrap;
2168 compose->replyinfo = replyinfo;
2169 compose->fwdinfo = fwdinfo;
2171 compose->updating = TRUE;
2172 compose->priority = priority;
2174 if (privacy_system != NULL) {
2175 compose->privacy_system = privacy_system;
2176 compose_use_signing(compose, use_signing);
2177 compose_use_encryption(compose, use_encryption);
2178 compose_update_privacy_system_menu_item(compose, FALSE);
2180 activate_privacy_system(compose, account, FALSE);
2183 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2185 compose_extract_original_charset(compose);
2187 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2188 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2189 gchar queueheader_buf[BUFFSIZE];
2191 /* Set message save folder */
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2193 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2194 compose_set_save_to(compose, &queueheader_buf[4]);
2196 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2197 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2199 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2204 if (compose_parse_header(compose, msginfo) < 0) {
2205 compose->updating = FALSE;
2206 compose_destroy(compose);
2209 compose_reedit_set_entry(compose, msginfo);
2211 textview = GTK_TEXT_VIEW(compose->text);
2212 textbuf = gtk_text_view_get_buffer(textview);
2213 compose_create_tags(textview, compose);
2215 mark = gtk_text_buffer_get_insert(textbuf);
2216 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2218 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2219 G_CALLBACK(compose_changed_cb),
2222 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2223 fp = procmime_get_first_encrypted_text_content(msginfo);
2225 compose_force_encryption(compose, account, TRUE, NULL);
2228 fp = procmime_get_first_text_content(msginfo);
2231 g_warning("Can't get text part\n");
2235 gboolean prev_autowrap = compose->autowrap;
2236 GtkTextBuffer *buffer = textbuf;
2238 while (fgets(buf, sizeof(buf), fp) != NULL) {
2240 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2246 compose_attach_parts(compose, msginfo);
2248 compose_colorize_signature(compose);
2250 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2251 G_CALLBACK(compose_changed_cb),
2254 gtk_widget_grab_focus(compose->text);
2256 if (prefs_common.auto_exteditor) {
2257 compose_exec_ext_editor(compose);
2259 compose->modified = FALSE;
2260 compose_set_title(compose);
2262 compose->updating = FALSE;
2263 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2264 SCROLL_TO_CURSOR(compose);
2266 if (compose->deferred_destroy) {
2267 compose_destroy(compose);
2271 compose->sig_str = account_get_signature_str(compose->account);
2276 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2283 cm_return_val_if_fail(msginfo != NULL, NULL);
2286 account = account_get_reply_account(msginfo,
2287 prefs_common.reply_account_autosel);
2288 cm_return_val_if_fail(account != NULL, NULL);
2290 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2292 compose->updating = TRUE;
2294 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2295 compose->replyinfo = NULL;
2296 compose->fwdinfo = NULL;
2298 compose_show_first_last_header(compose, TRUE);
2300 gtk_widget_grab_focus(compose->header_last->entry);
2302 filename = procmsg_get_message_file(msginfo);
2304 if (filename == NULL) {
2305 compose->updating = FALSE;
2306 compose_destroy(compose);
2311 compose->redirect_filename = filename;
2313 /* Set save folder */
2314 item = msginfo->folder;
2315 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2316 gchar *folderidentifier;
2318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2319 folderidentifier = folder_item_get_identifier(item);
2320 compose_set_save_to(compose, folderidentifier);
2321 g_free(folderidentifier);
2324 compose_attach_parts(compose, msginfo);
2326 if (msginfo->subject)
2327 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2329 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2331 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2332 _("Message redirect format error at line %d."));
2333 quote_fmt_reset_vartable();
2334 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2336 compose_colorize_signature(compose);
2339 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2340 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2341 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2344 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2345 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2346 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2347 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2348 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2349 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2350 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2352 if (compose->toolbar->draft_btn)
2353 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2354 if (compose->toolbar->insert_btn)
2355 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2356 if (compose->toolbar->attach_btn)
2357 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2358 if (compose->toolbar->sig_btn)
2359 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2360 if (compose->toolbar->exteditor_btn)
2361 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2362 if (compose->toolbar->linewrap_current_btn)
2363 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2364 if (compose->toolbar->linewrap_all_btn)
2365 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2367 compose->modified = FALSE;
2368 compose_set_title(compose);
2369 compose->updating = FALSE;
2370 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2371 SCROLL_TO_CURSOR(compose);
2373 if (compose->deferred_destroy) {
2374 compose_destroy(compose);
2381 GList *compose_get_compose_list(void)
2383 return compose_list;
2386 void compose_entry_append(Compose *compose, const gchar *address,
2387 ComposeEntryType type)
2389 const gchar *header;
2391 gboolean in_quote = FALSE;
2392 if (!address || *address == '\0') return;
2399 header = N_("Bcc:");
2401 case COMPOSE_REPLYTO:
2402 header = N_("Reply-To:");
2404 case COMPOSE_NEWSGROUPS:
2405 header = N_("Newsgroups:");
2407 case COMPOSE_FOLLOWUPTO:
2408 header = N_( "Followup-To:");
2415 header = prefs_common_translated_header_name(header);
2417 cur = begin = (gchar *)address;
2419 /* we separate the line by commas, but not if we're inside a quoted
2421 while (*cur != '\0') {
2423 in_quote = !in_quote;
2424 if (*cur == ',' && !in_quote) {
2425 gchar *tmp = g_strdup(begin);
2427 tmp[cur-begin]='\0';
2430 while (*tmp == ' ' || *tmp == '\t')
2432 compose_add_header_entry(compose, header, tmp);
2439 gchar *tmp = g_strdup(begin);
2441 tmp[cur-begin]='\0';
2444 while (*tmp == ' ' || *tmp == '\t')
2446 compose_add_header_entry(compose, header, tmp);
2451 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2453 static GdkColor yellow;
2454 static GdkColor black;
2455 static gboolean yellow_initialised = FALSE;
2459 if (!yellow_initialised) {
2460 gdk_color_parse("#f5f6be", &yellow);
2461 gdk_color_parse("#000000", &black);
2462 yellow_initialised = gdk_colormap_alloc_color(
2463 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2464 yellow_initialised &= gdk_colormap_alloc_color(
2465 gdk_colormap_get_system(), &black, FALSE, TRUE);
2468 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2469 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2470 if (gtk_entry_get_text(entry) &&
2471 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2472 if (yellow_initialised) {
2473 gtk_widget_modify_base(
2474 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2475 GTK_STATE_NORMAL, &yellow);
2476 gtk_widget_modify_text(
2477 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2478 GTK_STATE_NORMAL, &black);
2484 void compose_toolbar_cb(gint action, gpointer data)
2486 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2487 Compose *compose = (Compose*)toolbar_item->parent;
2489 cm_return_if_fail(compose != NULL);
2493 compose_send_cb(NULL, compose);
2496 compose_send_later_cb(NULL, compose);
2499 compose_draft(compose, COMPOSE_QUIT_EDITING);
2502 compose_insert_file_cb(NULL, compose);
2505 compose_attach_cb(NULL, compose);
2508 compose_insert_sig(compose, FALSE);
2511 compose_ext_editor_cb(NULL, compose);
2513 case A_LINEWRAP_CURRENT:
2514 compose_beautify_paragraph(compose, NULL, TRUE);
2516 case A_LINEWRAP_ALL:
2517 compose_wrap_all_full(compose, TRUE);
2520 compose_address_cb(NULL, compose);
2523 case A_CHECK_SPELLING:
2524 compose_check_all(NULL, compose);
2532 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2537 gchar *subject = NULL;
2541 gchar **attach = NULL;
2543 /* get mailto parts but skip from */
2544 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2547 compose_entry_append(compose, to, to_type);
2549 compose_entry_append(compose, cc, COMPOSE_CC);
2551 compose_entry_append(compose, bcc, COMPOSE_BCC);
2553 if (!g_utf8_validate (subject, -1, NULL)) {
2554 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2555 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2558 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2562 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2563 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2566 gboolean prev_autowrap = compose->autowrap;
2568 compose->autowrap = FALSE;
2570 mark = gtk_text_buffer_get_insert(buffer);
2571 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2573 if (!g_utf8_validate (body, -1, NULL)) {
2574 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2575 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2578 gtk_text_buffer_insert(buffer, &iter, body, -1);
2580 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2582 compose->autowrap = prev_autowrap;
2583 if (compose->autowrap)
2584 compose_wrap_all(compose);
2588 gint i = 0, att = 0;
2589 gchar *warn_files = NULL;
2590 while (attach[i] != NULL) {
2591 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2592 if (utf8_filename) {
2593 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2594 gchar *tmp = g_strdup_printf("%s%s\n",
2595 warn_files?warn_files:"",
2601 g_free(utf8_filename);
2603 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2608 alertpanel_notice(ngettext(
2609 "The following file has been attached: \n%s",
2610 "The following files have been attached: \n%s", att), warn_files);
2622 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2624 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2625 {"Cc:", NULL, TRUE},
2626 {"References:", NULL, FALSE},
2627 {"Bcc:", NULL, TRUE},
2628 {"Newsgroups:", NULL, TRUE},
2629 {"Followup-To:", NULL, TRUE},
2630 {"List-Post:", NULL, FALSE},
2631 {"X-Priority:", NULL, FALSE},
2632 {NULL, NULL, FALSE}};
2648 cm_return_val_if_fail(msginfo != NULL, -1);
2650 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2651 procheader_get_header_fields(fp, hentry);
2654 if (hentry[H_REPLY_TO].body != NULL) {
2655 if (hentry[H_REPLY_TO].body[0] != '\0') {
2657 conv_unmime_header(hentry[H_REPLY_TO].body,
2660 g_free(hentry[H_REPLY_TO].body);
2661 hentry[H_REPLY_TO].body = NULL;
2663 if (hentry[H_CC].body != NULL) {
2664 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2665 g_free(hentry[H_CC].body);
2666 hentry[H_CC].body = NULL;
2668 if (hentry[H_REFERENCES].body != NULL) {
2669 if (compose->mode == COMPOSE_REEDIT)
2670 compose->references = hentry[H_REFERENCES].body;
2672 compose->references = compose_parse_references
2673 (hentry[H_REFERENCES].body, msginfo->msgid);
2674 g_free(hentry[H_REFERENCES].body);
2676 hentry[H_REFERENCES].body = NULL;
2678 if (hentry[H_BCC].body != NULL) {
2679 if (compose->mode == COMPOSE_REEDIT)
2681 conv_unmime_header(hentry[H_BCC].body, NULL);
2682 g_free(hentry[H_BCC].body);
2683 hentry[H_BCC].body = NULL;
2685 if (hentry[H_NEWSGROUPS].body != NULL) {
2686 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2687 hentry[H_NEWSGROUPS].body = NULL;
2689 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2690 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2691 compose->followup_to =
2692 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2695 g_free(hentry[H_FOLLOWUP_TO].body);
2696 hentry[H_FOLLOWUP_TO].body = NULL;
2698 if (hentry[H_LIST_POST].body != NULL) {
2701 extract_address(hentry[H_LIST_POST].body);
2702 if (hentry[H_LIST_POST].body[0] != '\0') {
2703 scan_mailto_url(hentry[H_LIST_POST].body,
2704 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2706 g_free(compose->ml_post);
2707 compose->ml_post = to;
2710 g_free(hentry[H_LIST_POST].body);
2711 hentry[H_LIST_POST].body = NULL;
2714 /* CLAWS - X-Priority */
2715 if (compose->mode == COMPOSE_REEDIT)
2716 if (hentry[H_X_PRIORITY].body != NULL) {
2719 priority = atoi(hentry[H_X_PRIORITY].body);
2720 g_free(hentry[H_X_PRIORITY].body);
2722 hentry[H_X_PRIORITY].body = NULL;
2724 if (priority < PRIORITY_HIGHEST ||
2725 priority > PRIORITY_LOWEST)
2726 priority = PRIORITY_NORMAL;
2728 compose->priority = priority;
2731 if (compose->mode == COMPOSE_REEDIT) {
2732 if (msginfo->inreplyto && *msginfo->inreplyto)
2733 compose->inreplyto = g_strdup(msginfo->inreplyto);
2737 if (msginfo->msgid && *msginfo->msgid)
2738 compose->inreplyto = g_strdup(msginfo->msgid);
2740 if (!compose->references) {
2741 if (msginfo->msgid && *msginfo->msgid) {
2742 if (msginfo->inreplyto && *msginfo->inreplyto)
2743 compose->references =
2744 g_strdup_printf("<%s>\n\t<%s>",
2748 compose->references =
2749 g_strconcat("<", msginfo->msgid, ">",
2751 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2752 compose->references =
2753 g_strconcat("<", msginfo->inreplyto, ">",
2761 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2763 GSList *ref_id_list, *cur;
2767 ref_id_list = references_list_append(NULL, ref);
2768 if (!ref_id_list) return NULL;
2769 if (msgid && *msgid)
2770 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2775 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2776 /* "<" + Message-ID + ">" + CR+LF+TAB */
2777 len += strlen((gchar *)cur->data) + 5;
2779 if (len > MAX_REFERENCES_LEN) {
2780 /* remove second message-ID */
2781 if (ref_id_list && ref_id_list->next &&
2782 ref_id_list->next->next) {
2783 g_free(ref_id_list->next->data);
2784 ref_id_list = g_slist_remove
2785 (ref_id_list, ref_id_list->next->data);
2787 slist_free_strings(ref_id_list);
2788 g_slist_free(ref_id_list);
2795 new_ref = g_string_new("");
2796 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2797 if (new_ref->len > 0)
2798 g_string_append(new_ref, "\n\t");
2799 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2802 slist_free_strings(ref_id_list);
2803 g_slist_free(ref_id_list);
2805 new_ref_str = new_ref->str;
2806 g_string_free(new_ref, FALSE);
2811 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2812 const gchar *fmt, const gchar *qmark,
2813 const gchar *body, gboolean rewrap,
2814 gboolean need_unescape,
2815 const gchar *err_msg)
2817 MsgInfo* dummyinfo = NULL;
2818 gchar *quote_str = NULL;
2820 gboolean prev_autowrap;
2821 const gchar *trimmed_body = body;
2822 gint cursor_pos = -1;
2823 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2824 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2829 SIGNAL_BLOCK(buffer);
2832 dummyinfo = compose_msginfo_new_from_compose(compose);
2833 msginfo = dummyinfo;
2836 if (qmark != NULL) {
2838 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2839 compose->gtkaspell);
2841 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2843 quote_fmt_scan_string(qmark);
2846 buf = quote_fmt_get_buffer();
2848 alertpanel_error(_("Quote mark format error."));
2850 Xstrdup_a(quote_str, buf, goto error)
2853 if (fmt && *fmt != '\0') {
2856 while (*trimmed_body == '\n')
2860 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2861 compose->gtkaspell);
2863 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2865 if (need_unescape) {
2868 /* decode \-escape sequences in the internal representation of the quote format */
2869 tmp = malloc(strlen(fmt)+1);
2870 pref_get_unescaped_pref(tmp, fmt);
2871 quote_fmt_scan_string(tmp);
2875 quote_fmt_scan_string(fmt);
2879 buf = quote_fmt_get_buffer();
2881 gint line = quote_fmt_get_line();
2882 alertpanel_error(err_msg, line);
2888 prev_autowrap = compose->autowrap;
2889 compose->autowrap = FALSE;
2891 mark = gtk_text_buffer_get_insert(buffer);
2892 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2893 if (g_utf8_validate(buf, -1, NULL)) {
2894 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2896 gchar *tmpout = NULL;
2897 tmpout = conv_codeset_strdup
2898 (buf, conv_get_locale_charset_str_no_utf8(),
2900 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2902 tmpout = g_malloc(strlen(buf)*2+1);
2903 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2905 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2909 cursor_pos = quote_fmt_get_cursor_pos();
2910 if (cursor_pos == -1)
2911 cursor_pos = gtk_text_iter_get_offset(&iter);
2912 compose->set_cursor_pos = cursor_pos;
2914 gtk_text_buffer_get_start_iter(buffer, &iter);
2915 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2916 gtk_text_buffer_place_cursor(buffer, &iter);
2918 compose->autowrap = prev_autowrap;
2919 if (compose->autowrap && rewrap)
2920 compose_wrap_all(compose);
2927 SIGNAL_UNBLOCK(buffer);
2929 procmsg_msginfo_free( dummyinfo );
2934 /* if ml_post is of type addr@host and from is of type
2935 * addr-anything@host, return TRUE
2937 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2939 gchar *left_ml = NULL;
2940 gchar *right_ml = NULL;
2941 gchar *left_from = NULL;
2942 gchar *right_from = NULL;
2943 gboolean result = FALSE;
2945 if (!ml_post || !from)
2948 left_ml = g_strdup(ml_post);
2949 if (strstr(left_ml, "@")) {
2950 right_ml = strstr(left_ml, "@")+1;
2951 *(strstr(left_ml, "@")) = '\0';
2954 left_from = g_strdup(from);
2955 if (strstr(left_from, "@")) {
2956 right_from = strstr(left_from, "@")+1;
2957 *(strstr(left_from, "@")) = '\0';
2960 if (left_ml && left_from && right_ml && right_from
2961 && !strncmp(left_from, left_ml, strlen(left_ml))
2962 && !strcmp(right_from, right_ml)) {
2971 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2973 gchar *my_addr1, *my_addr2;
2975 if (!addr1 || !addr2)
2978 Xstrdup_a(my_addr1, addr1, return FALSE);
2979 Xstrdup_a(my_addr2, addr2, return FALSE);
2981 extract_address(my_addr1);
2982 extract_address(my_addr2);
2984 return !strcasecmp(my_addr1, my_addr2);
2987 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2988 gboolean to_all, gboolean to_ml,
2990 gboolean followup_and_reply_to)
2992 GSList *cc_list = NULL;
2995 gchar *replyto = NULL;
2996 GHashTable *to_table;
2998 gboolean reply_to_ml = FALSE;
2999 gboolean default_reply_to = FALSE;
3001 cm_return_if_fail(compose->account != NULL);
3002 cm_return_if_fail(msginfo != NULL);
3004 reply_to_ml = to_ml && compose->ml_post;
3006 default_reply_to = msginfo->folder &&
3007 msginfo->folder->prefs->enable_default_reply_to;
3009 if (compose->account->protocol != A_NNTP) {
3010 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3011 if (msginfo->folder->prefs->enable_default_replyto) {
3012 compose_entry_append(compose, msginfo->folder->prefs->default_replyto, COMPOSE_REPLYTO);
3014 if (msginfo->folder->prefs->enable_default_bcc) {
3015 compose_entry_append(compose, msginfo->folder->prefs->default_bcc, COMPOSE_BCC);
3017 if (msginfo->folder->prefs->enable_default_cc) {
3018 compose_entry_append(compose, msginfo->folder->prefs->default_cc, COMPOSE_CC);
3021 if (reply_to_ml && !default_reply_to) {
3023 gboolean is_subscr = is_subscription(compose->ml_post,
3026 /* normal answer to ml post with a reply-to */
3027 compose_entry_append(compose,
3030 if (compose->replyto
3031 && !same_address(compose->ml_post, compose->replyto))
3032 compose_entry_append(compose,
3036 /* answer to subscription confirmation */
3037 if (compose->replyto)
3038 compose_entry_append(compose,
3041 else if (msginfo->from)
3042 compose_entry_append(compose,
3047 else if (!(to_all || to_sender) && default_reply_to) {
3048 compose_entry_append(compose,
3049 msginfo->folder->prefs->default_reply_to,
3051 compose_entry_mark_default_to(compose,
3052 msginfo->folder->prefs->default_reply_to);
3057 Xstrdup_a(tmp1, msginfo->from, return);
3058 extract_address(tmp1);
3059 if (to_all || to_sender ||
3060 !account_find_from_address(tmp1, FALSE))
3061 compose_entry_append(compose,
3062 (compose->replyto && !to_sender)
3063 ? compose->replyto :
3064 msginfo->from ? msginfo->from : "",
3066 else if (!to_all && !to_sender) {
3067 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3068 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3069 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3070 if (compose->replyto) {
3071 compose_entry_append(compose,
3075 compose_entry_append(compose,
3076 msginfo->from ? msginfo->from : "",
3080 /* replying to own mail, use original recp */
3081 compose_entry_append(compose,
3082 msginfo->to ? msginfo->to : "",
3084 compose_entry_append(compose,
3085 msginfo->cc ? msginfo->cc : "",
3091 if (to_sender || (compose->followup_to &&
3092 !strncmp(compose->followup_to, "poster", 6)))
3093 compose_entry_append
3095 (compose->replyto ? compose->replyto :
3096 msginfo->from ? msginfo->from : ""),
3099 else if (followup_and_reply_to || to_all) {
3100 compose_entry_append
3102 (compose->replyto ? compose->replyto :
3103 msginfo->from ? msginfo->from : ""),
3106 compose_entry_append
3108 compose->followup_to ? compose->followup_to :
3109 compose->newsgroups ? compose->newsgroups : "",
3110 COMPOSE_NEWSGROUPS);
3113 compose_entry_append
3115 compose->followup_to ? compose->followup_to :
3116 compose->newsgroups ? compose->newsgroups : "",
3117 COMPOSE_NEWSGROUPS);
3120 if (msginfo->subject && *msginfo->subject) {
3124 buf = p = g_strdup(msginfo->subject);
3125 p += subject_get_prefix_length(p);
3126 memmove(buf, p, strlen(p) + 1);
3128 buf2 = g_strdup_printf("Re: %s", buf);
3129 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3134 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3136 if (to_ml && compose->ml_post) return;
3137 if (!to_all || compose->account->protocol == A_NNTP) return;
3139 if (compose->replyto) {
3140 Xstrdup_a(replyto, compose->replyto, return);
3141 extract_address(replyto);
3143 if (msginfo->from) {
3144 Xstrdup_a(from, msginfo->from, return);
3145 extract_address(from);
3148 if (replyto && from)
3149 cc_list = address_list_append_with_comments(cc_list, from);
3150 if (to_all && msginfo->folder &&
3151 msginfo->folder->prefs->enable_default_reply_to)
3152 cc_list = address_list_append_with_comments(cc_list,
3153 msginfo->folder->prefs->default_reply_to);
3154 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3155 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3157 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3159 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3160 if (compose->account) {
3161 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3162 GINT_TO_POINTER(1));
3164 /* remove address on To: and that of current account */
3165 for (cur = cc_list; cur != NULL; ) {
3166 GSList *next = cur->next;
3169 addr = g_utf8_strdown(cur->data, -1);
3170 extract_address(addr);
3172 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3173 cc_list = g_slist_remove(cc_list, cur->data);
3175 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3179 hash_free_strings(to_table);
3180 g_hash_table_destroy(to_table);
3183 for (cur = cc_list; cur != NULL; cur = cur->next)
3184 compose_entry_append(compose, (gchar *)cur->data,
3186 slist_free_strings(cc_list);
3187 g_slist_free(cc_list);
3192 #define SET_ENTRY(entry, str) \
3195 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3198 #define SET_ADDRESS(type, str) \
3201 compose_entry_append(compose, str, type); \
3204 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3206 cm_return_if_fail(msginfo != NULL);
3208 SET_ENTRY(subject_entry, msginfo->subject);
3209 SET_ENTRY(from_name, msginfo->from);
3210 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3211 SET_ADDRESS(COMPOSE_CC, compose->cc);
3212 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3213 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3214 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3215 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3217 compose_update_priority_menu_item(compose);
3218 compose_update_privacy_system_menu_item(compose, FALSE);
3219 compose_show_first_last_header(compose, TRUE);
3225 static void compose_insert_sig(Compose *compose, gboolean replace)
3227 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3228 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3230 GtkTextIter iter, iter_end;
3231 gint cur_pos, ins_pos;
3232 gboolean prev_autowrap;
3233 gboolean found = FALSE;
3234 gboolean exists = FALSE;
3236 cm_return_if_fail(compose->account != NULL);
3240 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3241 G_CALLBACK(compose_changed_cb),
3244 mark = gtk_text_buffer_get_insert(buffer);
3245 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3246 cur_pos = gtk_text_iter_get_offset (&iter);
3249 gtk_text_buffer_get_end_iter(buffer, &iter);
3251 exists = (compose->sig_str != NULL);
3254 GtkTextIter first_iter, start_iter, end_iter;
3256 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3258 if (!exists || compose->sig_str[0] == '\0')
3261 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3262 compose->signature_tag);
3265 /* include previous \n\n */
3266 gtk_text_iter_backward_chars(&first_iter, 1);
3267 start_iter = first_iter;
3268 end_iter = first_iter;
3270 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3271 compose->signature_tag);
3272 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3273 compose->signature_tag);
3275 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3281 g_free(compose->sig_str);
3282 compose->sig_str = account_get_signature_str(compose->account);
3284 cur_pos = gtk_text_iter_get_offset(&iter);
3286 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3287 g_free(compose->sig_str);
3288 compose->sig_str = NULL;
3290 if (compose->sig_inserted == FALSE)
3291 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3292 compose->sig_inserted = TRUE;
3294 cur_pos = gtk_text_iter_get_offset(&iter);
3295 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3297 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3298 gtk_text_iter_forward_chars(&iter, 1);
3299 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3300 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3302 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3303 cur_pos = gtk_text_buffer_get_char_count (buffer);
3306 /* put the cursor where it should be
3307 * either where the quote_fmt says, either where it was */
3308 if (compose->set_cursor_pos < 0)
3309 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3311 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3312 compose->set_cursor_pos);
3314 compose->set_cursor_pos = -1;
3315 gtk_text_buffer_place_cursor(buffer, &iter);
3316 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3317 G_CALLBACK(compose_changed_cb),
3323 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3326 GtkTextBuffer *buffer;
3329 const gchar *cur_encoding;
3330 gchar buf[BUFFSIZE];
3333 gboolean prev_autowrap;
3334 gboolean badtxt = FALSE;
3336 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3338 if ((fp = g_fopen(file, "rb")) == NULL) {
3339 FILE_OP_ERROR(file, "fopen");
3340 return COMPOSE_INSERT_READ_ERROR;
3343 prev_autowrap = compose->autowrap;
3344 compose->autowrap = FALSE;
3346 text = GTK_TEXT_VIEW(compose->text);
3347 buffer = gtk_text_view_get_buffer(text);
3348 mark = gtk_text_buffer_get_insert(buffer);
3349 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3351 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3352 G_CALLBACK(text_inserted),
3355 cur_encoding = conv_get_locale_charset_str_no_utf8();
3357 while (fgets(buf, sizeof(buf), fp) != NULL) {
3360 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3361 str = g_strdup(buf);
3363 str = conv_codeset_strdup
3364 (buf, cur_encoding, CS_INTERNAL);
3367 /* strip <CR> if DOS/Windows file,
3368 replace <CR> with <LF> if Macintosh file. */
3371 if (len > 0 && str[len - 1] != '\n') {
3373 if (str[len] == '\r') str[len] = '\n';
3376 gtk_text_buffer_insert(buffer, &iter, str, -1);
3380 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3381 G_CALLBACK(text_inserted),
3383 compose->autowrap = prev_autowrap;
3384 if (compose->autowrap)
3385 compose_wrap_all(compose);
3390 return COMPOSE_INSERT_INVALID_CHARACTER;
3392 return COMPOSE_INSERT_SUCCESS;
3395 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3396 const gchar *filename,
3397 const gchar *content_type)
3405 GtkListStore *store;
3407 gboolean has_binary = FALSE;
3409 if (!is_file_exist(file)) {
3410 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3411 gboolean result = FALSE;
3412 if (file_from_uri && is_file_exist(file_from_uri)) {
3413 result = compose_attach_append(
3414 compose, file_from_uri,
3418 g_free(file_from_uri);
3421 alertpanel_error("File %s doesn't exist\n", filename);
3424 if ((size = get_file_size(file)) < 0) {
3425 alertpanel_error("Can't get file size of %s\n", filename);
3429 alertpanel_error(_("File %s is empty."), filename);
3432 if ((fp = g_fopen(file, "rb")) == NULL) {
3433 alertpanel_error(_("Can't read %s."), filename);
3438 ainfo = g_new0(AttachInfo, 1);
3439 auto_ainfo = g_auto_pointer_new_with_free
3440 (ainfo, (GFreeFunc) compose_attach_info_free);
3441 ainfo->file = g_strdup(file);
3444 ainfo->content_type = g_strdup(content_type);
3445 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3447 MsgFlags flags = {0, 0};
3449 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3450 ainfo->encoding = ENC_7BIT;
3452 ainfo->encoding = ENC_8BIT;
3454 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3455 if (msginfo && msginfo->subject)
3456 name = g_strdup(msginfo->subject);
3458 name = g_path_get_basename(filename ? filename : file);
3460 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3462 procmsg_msginfo_free(msginfo);
3464 if (!g_ascii_strncasecmp(content_type, "text", 4))
3465 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3467 ainfo->encoding = ENC_BASE64;
3468 name = g_path_get_basename(filename ? filename : file);
3469 ainfo->name = g_strdup(name);
3473 ainfo->content_type = procmime_get_mime_type(file);
3474 if (!ainfo->content_type) {
3475 ainfo->content_type =
3476 g_strdup("application/octet-stream");
3477 ainfo->encoding = ENC_BASE64;
3478 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3480 procmime_get_encoding_for_text_file(file, &has_binary);
3482 ainfo->encoding = ENC_BASE64;
3483 name = g_path_get_basename(filename ? filename : file);
3484 ainfo->name = g_strdup(name);
3488 if (ainfo->name != NULL
3489 && !strcmp(ainfo->name, ".")) {
3490 g_free(ainfo->name);
3494 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3495 g_free(ainfo->content_type);
3496 ainfo->content_type = g_strdup("application/octet-stream");
3499 ainfo->size = (goffset)size;
3500 size_text = to_human_readable((goffset)size);
3502 store = GTK_LIST_STORE(gtk_tree_view_get_model
3503 (GTK_TREE_VIEW(compose->attach_clist)));
3505 gtk_list_store_append(store, &iter);
3506 gtk_list_store_set(store, &iter,
3507 COL_MIMETYPE, ainfo->content_type,
3508 COL_SIZE, size_text,
3509 COL_NAME, ainfo->name,
3511 COL_AUTODATA, auto_ainfo,
3514 g_auto_pointer_free(auto_ainfo);
3515 compose_attach_update_label(compose);
3519 static void compose_use_signing(Compose *compose, gboolean use_signing)
3521 compose->use_signing = use_signing;
3522 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3525 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3527 compose->use_encryption = use_encryption;
3528 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3531 #define NEXT_PART_NOT_CHILD(info) \
3533 node = info->node; \
3534 while (node->children) \
3535 node = g_node_last_child(node); \
3536 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3539 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3543 MimeInfo *firsttext = NULL;
3544 MimeInfo *encrypted = NULL;
3547 const gchar *partname = NULL;
3549 mimeinfo = procmime_scan_message(msginfo);
3550 if (!mimeinfo) return;
3552 if (mimeinfo->node->children == NULL) {
3553 procmime_mimeinfo_free_all(mimeinfo);
3557 /* find first content part */
3558 child = (MimeInfo *) mimeinfo->node->children->data;
3559 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3560 child = (MimeInfo *)child->node->children->data;
3563 if (child->type == MIMETYPE_TEXT) {
3565 debug_print("First text part found\n");
3566 } else if (compose->mode == COMPOSE_REEDIT &&
3567 child->type == MIMETYPE_APPLICATION &&
3568 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3569 encrypted = (MimeInfo *)child->node->parent->data;
3572 child = (MimeInfo *) mimeinfo->node->children->data;
3573 while (child != NULL) {
3576 if (child == encrypted) {
3577 /* skip this part of tree */
3578 NEXT_PART_NOT_CHILD(child);
3582 if (child->type == MIMETYPE_MULTIPART) {
3583 /* get the actual content */
3584 child = procmime_mimeinfo_next(child);
3588 if (child == firsttext) {
3589 child = procmime_mimeinfo_next(child);
3593 outfile = procmime_get_tmp_file_name(child);
3594 if ((err = procmime_get_part(outfile, child)) < 0)
3595 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3597 gchar *content_type;
3599 content_type = procmime_get_content_type_str(child->type, child->subtype);
3601 /* if we meet a pgp signature, we don't attach it, but
3602 * we force signing. */
3603 if ((strcmp(content_type, "application/pgp-signature") &&
3604 strcmp(content_type, "application/pkcs7-signature") &&
3605 strcmp(content_type, "application/x-pkcs7-signature"))
3606 || compose->mode == COMPOSE_REDIRECT) {
3607 partname = procmime_mimeinfo_get_parameter(child, "filename");
3608 if (partname == NULL)
3609 partname = procmime_mimeinfo_get_parameter(child, "name");
3610 if (partname == NULL)
3612 compose_attach_append(compose, outfile,
3613 partname, content_type);
3615 compose_force_signing(compose, compose->account, NULL);
3617 g_free(content_type);
3620 NEXT_PART_NOT_CHILD(child);
3622 procmime_mimeinfo_free_all(mimeinfo);
3625 #undef NEXT_PART_NOT_CHILD
3630 WAIT_FOR_INDENT_CHAR,
3631 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3634 /* return indent length, we allow:
3635 indent characters followed by indent characters or spaces/tabs,
3636 alphabets and numbers immediately followed by indent characters,
3637 and the repeating sequences of the above
3638 If quote ends with multiple spaces, only the first one is included. */
3639 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3640 const GtkTextIter *start, gint *len)
3642 GtkTextIter iter = *start;
3646 IndentState state = WAIT_FOR_INDENT_CHAR;
3649 gint alnum_count = 0;
3650 gint space_count = 0;
3653 if (prefs_common.quote_chars == NULL) {
3657 while (!gtk_text_iter_ends_line(&iter)) {
3658 wc = gtk_text_iter_get_char(&iter);
3659 if (g_unichar_iswide(wc))
3661 clen = g_unichar_to_utf8(wc, ch);
3665 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3666 is_space = g_unichar_isspace(wc);
3668 if (state == WAIT_FOR_INDENT_CHAR) {
3669 if (!is_indent && !g_unichar_isalnum(wc))
3672 quote_len += alnum_count + space_count + 1;
3673 alnum_count = space_count = 0;
3674 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3677 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3678 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3682 else if (is_indent) {
3683 quote_len += alnum_count + space_count + 1;
3684 alnum_count = space_count = 0;
3687 state = WAIT_FOR_INDENT_CHAR;
3691 gtk_text_iter_forward_char(&iter);
3694 if (quote_len > 0 && space_count > 0)
3700 if (quote_len > 0) {
3702 gtk_text_iter_forward_chars(&iter, quote_len);
3703 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3709 /* return >0 if the line is itemized */
3710 static int compose_itemized_length(GtkTextBuffer *buffer,
3711 const GtkTextIter *start)
3713 GtkTextIter iter = *start;
3718 if (gtk_text_iter_ends_line(&iter))
3723 wc = gtk_text_iter_get_char(&iter);
3724 if (!g_unichar_isspace(wc))
3726 gtk_text_iter_forward_char(&iter);
3727 if (gtk_text_iter_ends_line(&iter))
3731 clen = g_unichar_to_utf8(wc, ch);
3735 if (!strchr("*-+", ch[0]))
3738 gtk_text_iter_forward_char(&iter);
3739 if (gtk_text_iter_ends_line(&iter))
3741 wc = gtk_text_iter_get_char(&iter);
3742 if (g_unichar_isspace(wc)) {
3748 /* return the string at the start of the itemization */
3749 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3750 const GtkTextIter *start)
3752 GtkTextIter iter = *start;
3755 GString *item_chars = g_string_new("");
3758 if (gtk_text_iter_ends_line(&iter))
3763 wc = gtk_text_iter_get_char(&iter);
3764 if (!g_unichar_isspace(wc))
3766 gtk_text_iter_forward_char(&iter);
3767 if (gtk_text_iter_ends_line(&iter))
3769 g_string_append_unichar(item_chars, wc);
3772 str = item_chars->str;
3773 g_string_free(item_chars, FALSE);
3777 /* return the number of spaces at a line's start */
3778 static int compose_left_offset_length(GtkTextBuffer *buffer,
3779 const GtkTextIter *start)
3781 GtkTextIter iter = *start;
3784 if (gtk_text_iter_ends_line(&iter))
3788 wc = gtk_text_iter_get_char(&iter);
3789 if (!g_unichar_isspace(wc))
3792 gtk_text_iter_forward_char(&iter);
3793 if (gtk_text_iter_ends_line(&iter))
3797 gtk_text_iter_forward_char(&iter);
3798 if (gtk_text_iter_ends_line(&iter))
3803 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3804 const GtkTextIter *start,
3805 GtkTextIter *break_pos,
3809 GtkTextIter iter = *start, line_end = *start;
3810 PangoLogAttr *attrs;
3817 gboolean can_break = FALSE;
3818 gboolean do_break = FALSE;
3819 gboolean was_white = FALSE;
3820 gboolean prev_dont_break = FALSE;
3822 gtk_text_iter_forward_to_line_end(&line_end);
3823 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3824 len = g_utf8_strlen(str, -1);
3828 g_warning("compose_get_line_break_pos: len = 0!\n");
3832 /* g_print("breaking line: %d: %s (len = %d)\n",
3833 gtk_text_iter_get_line(&iter), str, len); */
3835 attrs = g_new(PangoLogAttr, len + 1);
3837 pango_default_break(str, -1, NULL, attrs, len + 1);
3841 /* skip quote and leading spaces */
3842 for (i = 0; *p != '\0' && i < len; i++) {
3845 wc = g_utf8_get_char(p);
3846 if (i >= quote_len && !g_unichar_isspace(wc))
3848 if (g_unichar_iswide(wc))
3850 else if (*p == '\t')
3854 p = g_utf8_next_char(p);
3857 for (; *p != '\0' && i < len; i++) {
3858 PangoLogAttr *attr = attrs + i;
3862 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3865 was_white = attr->is_white;
3867 /* don't wrap URI */
3868 if ((uri_len = get_uri_len(p)) > 0) {
3870 if (pos > 0 && col > max_col) {
3880 wc = g_utf8_get_char(p);
3881 if (g_unichar_iswide(wc)) {
3883 if (prev_dont_break && can_break && attr->is_line_break)
3885 } else if (*p == '\t')
3889 if (pos > 0 && col > max_col) {
3894 if (*p == '-' || *p == '/')
3895 prev_dont_break = TRUE;
3897 prev_dont_break = FALSE;
3899 p = g_utf8_next_char(p);
3903 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3908 *break_pos = *start;
3909 gtk_text_iter_set_line_offset(break_pos, pos);
3914 static gboolean compose_join_next_line(Compose *compose,
3915 GtkTextBuffer *buffer,
3917 const gchar *quote_str)
3919 GtkTextIter iter_ = *iter, cur, prev, next, end;
3920 PangoLogAttr attrs[3];
3922 gchar *next_quote_str;
3925 gboolean keep_cursor = FALSE;
3927 if (!gtk_text_iter_forward_line(&iter_) ||
3928 gtk_text_iter_ends_line(&iter_)) {
3931 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3933 if ((quote_str || next_quote_str) &&
3934 strcmp2(quote_str, next_quote_str) != 0) {
3935 g_free(next_quote_str);
3938 g_free(next_quote_str);
3941 if (quote_len > 0) {
3942 gtk_text_iter_forward_chars(&end, quote_len);
3943 if (gtk_text_iter_ends_line(&end)) {
3948 /* don't join itemized lines */
3949 if (compose_itemized_length(buffer, &end) > 0) {
3953 /* don't join signature separator */
3954 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3957 /* delete quote str */
3959 gtk_text_buffer_delete(buffer, &iter_, &end);
3961 /* don't join line breaks put by the user */
3963 gtk_text_iter_backward_char(&cur);
3964 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3965 gtk_text_iter_forward_char(&cur);
3969 gtk_text_iter_forward_char(&cur);
3970 /* delete linebreak and extra spaces */
3971 while (gtk_text_iter_backward_char(&cur)) {
3972 wc1 = gtk_text_iter_get_char(&cur);
3973 if (!g_unichar_isspace(wc1))
3978 while (!gtk_text_iter_ends_line(&cur)) {
3979 wc1 = gtk_text_iter_get_char(&cur);
3980 if (!g_unichar_isspace(wc1))
3982 gtk_text_iter_forward_char(&cur);
3985 if (!gtk_text_iter_equal(&prev, &next)) {
3988 mark = gtk_text_buffer_get_insert(buffer);
3989 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3990 if (gtk_text_iter_equal(&prev, &cur))
3992 gtk_text_buffer_delete(buffer, &prev, &next);
3996 /* insert space if required */
3997 gtk_text_iter_backward_char(&prev);
3998 wc1 = gtk_text_iter_get_char(&prev);
3999 wc2 = gtk_text_iter_get_char(&next);
4000 gtk_text_iter_forward_char(&next);
4001 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4002 pango_default_break(str, -1, NULL, attrs, 3);
4003 if (!attrs[1].is_line_break ||
4004 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4005 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4007 gtk_text_iter_backward_char(&iter_);
4008 gtk_text_buffer_place_cursor(buffer, &iter_);
4017 #define ADD_TXT_POS(bp_, ep_, pti_) \
4018 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4019 last = last->next; \
4020 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4021 last->next = NULL; \
4023 g_warning("alloc error scanning URIs\n"); \
4026 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4028 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4029 GtkTextBuffer *buffer;
4030 GtkTextIter iter, break_pos, end_of_line;
4031 gchar *quote_str = NULL;
4033 gboolean wrap_quote = prefs_common.linewrap_quote;
4034 gboolean prev_autowrap = compose->autowrap;
4035 gint startq_offset = -1, noq_offset = -1;
4036 gint uri_start = -1, uri_stop = -1;
4037 gint nouri_start = -1, nouri_stop = -1;
4038 gint num_blocks = 0;
4039 gint quotelevel = -1;
4040 gboolean modified = force;
4041 gboolean removed = FALSE;
4042 gboolean modified_before_remove = FALSE;
4044 gboolean start = TRUE;
4045 gint itemized_len = 0, rem_item_len = 0;
4046 gchar *itemized_chars = NULL;
4047 gboolean item_continuation = FALSE;
4052 if (compose->draft_timeout_tag == -2) {
4056 compose->autowrap = FALSE;
4058 buffer = gtk_text_view_get_buffer(text);
4059 undo_wrapping(compose->undostruct, TRUE);
4064 mark = gtk_text_buffer_get_insert(buffer);
4065 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4069 if (compose->draft_timeout_tag == -2) {
4070 if (gtk_text_iter_ends_line(&iter)) {
4071 while (gtk_text_iter_ends_line(&iter) &&
4072 gtk_text_iter_forward_line(&iter))
4075 while (gtk_text_iter_backward_line(&iter)) {
4076 if (gtk_text_iter_ends_line(&iter)) {
4077 gtk_text_iter_forward_line(&iter);
4083 /* move to line start */
4084 gtk_text_iter_set_line_offset(&iter, 0);
4087 itemized_len = compose_itemized_length(buffer, &iter);
4089 if (!itemized_len) {
4090 itemized_len = compose_left_offset_length(buffer, &iter);
4091 item_continuation = TRUE;
4095 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4097 /* go until paragraph end (empty line) */
4098 while (start || !gtk_text_iter_ends_line(&iter)) {
4099 gchar *scanpos = NULL;
4100 /* parse table - in order of priority */
4102 const gchar *needle; /* token */
4104 /* token search function */
4105 gchar *(*search) (const gchar *haystack,
4106 const gchar *needle);
4107 /* part parsing function */
4108 gboolean (*parse) (const gchar *start,
4109 const gchar *scanpos,
4113 /* part to URI function */
4114 gchar *(*build_uri) (const gchar *bp,
4118 static struct table parser[] = {
4119 {"http://", strcasestr, get_uri_part, make_uri_string},
4120 {"https://", strcasestr, get_uri_part, make_uri_string},
4121 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4122 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4123 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4124 {"www.", strcasestr, get_uri_part, make_http_string},
4125 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4126 {"@", strcasestr, get_email_part, make_email_string}
4128 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4129 gint last_index = PARSE_ELEMS;
4131 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4135 if (!prev_autowrap && num_blocks == 0) {
4137 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4138 G_CALLBACK(text_inserted),
4141 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4144 uri_start = uri_stop = -1;
4146 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4149 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4150 if (startq_offset == -1)
4151 startq_offset = gtk_text_iter_get_offset(&iter);
4152 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4153 if (quotelevel > 2) {
4154 /* recycle colors */
4155 if (prefs_common.recycle_quote_colors)
4164 if (startq_offset == -1)
4165 noq_offset = gtk_text_iter_get_offset(&iter);
4169 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4172 if (gtk_text_iter_ends_line(&iter)) {
4174 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4175 prefs_common.linewrap_len,
4177 GtkTextIter prev, next, cur;
4178 if (prev_autowrap != FALSE || force) {
4179 compose->automatic_break = TRUE;
4181 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4182 compose->automatic_break = FALSE;
4183 if (itemized_len && compose->autoindent) {
4184 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4185 if (!item_continuation)
4186 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4188 } else if (quote_str && wrap_quote) {
4189 compose->automatic_break = TRUE;
4191 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4192 compose->automatic_break = FALSE;
4193 if (itemized_len && compose->autoindent) {
4194 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4195 if (!item_continuation)
4196 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4200 /* remove trailing spaces */
4202 rem_item_len = itemized_len;
4203 while (compose->autoindent && rem_item_len-- > 0)
4204 gtk_text_iter_backward_char(&cur);
4205 gtk_text_iter_backward_char(&cur);
4208 while (!gtk_text_iter_starts_line(&cur)) {
4211 gtk_text_iter_backward_char(&cur);
4212 wc = gtk_text_iter_get_char(&cur);
4213 if (!g_unichar_isspace(wc))
4217 if (!gtk_text_iter_equal(&prev, &next)) {
4218 gtk_text_buffer_delete(buffer, &prev, &next);
4220 gtk_text_iter_forward_char(&break_pos);
4224 gtk_text_buffer_insert(buffer, &break_pos,
4228 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4230 /* move iter to current line start */
4231 gtk_text_iter_set_line_offset(&iter, 0);
4238 /* move iter to next line start */
4244 if (!prev_autowrap && num_blocks > 0) {
4246 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4247 G_CALLBACK(text_inserted),
4251 while (!gtk_text_iter_ends_line(&end_of_line)) {
4252 gtk_text_iter_forward_char(&end_of_line);
4254 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4256 nouri_start = gtk_text_iter_get_offset(&iter);
4257 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4259 walk_pos = gtk_text_iter_get_offset(&iter);
4260 /* FIXME: this looks phony. scanning for anything in the parse table */
4261 for (n = 0; n < PARSE_ELEMS; n++) {
4264 tmp = parser[n].search(walk, parser[n].needle);
4266 if (scanpos == NULL || tmp < scanpos) {
4275 /* check if URI can be parsed */
4276 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4277 (const gchar **)&ep, FALSE)
4278 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4282 strlen(parser[last_index].needle);
4285 uri_start = walk_pos + (bp - o_walk);
4286 uri_stop = walk_pos + (ep - o_walk);
4290 gtk_text_iter_forward_line(&iter);
4293 if (startq_offset != -1) {
4294 GtkTextIter startquote, endquote;
4295 gtk_text_buffer_get_iter_at_offset(
4296 buffer, &startquote, startq_offset);
4299 switch (quotelevel) {
4301 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4302 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4303 gtk_text_buffer_apply_tag_by_name(
4304 buffer, "quote0", &startquote, &endquote);
4305 gtk_text_buffer_remove_tag_by_name(
4306 buffer, "quote1", &startquote, &endquote);
4307 gtk_text_buffer_remove_tag_by_name(
4308 buffer, "quote2", &startquote, &endquote);
4313 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4314 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4315 gtk_text_buffer_apply_tag_by_name(
4316 buffer, "quote1", &startquote, &endquote);
4317 gtk_text_buffer_remove_tag_by_name(
4318 buffer, "quote0", &startquote, &endquote);
4319 gtk_text_buffer_remove_tag_by_name(
4320 buffer, "quote2", &startquote, &endquote);
4325 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4326 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4327 gtk_text_buffer_apply_tag_by_name(
4328 buffer, "quote2", &startquote, &endquote);
4329 gtk_text_buffer_remove_tag_by_name(
4330 buffer, "quote0", &startquote, &endquote);
4331 gtk_text_buffer_remove_tag_by_name(
4332 buffer, "quote1", &startquote, &endquote);
4338 } else if (noq_offset != -1) {
4339 GtkTextIter startnoquote, endnoquote;
4340 gtk_text_buffer_get_iter_at_offset(
4341 buffer, &startnoquote, noq_offset);
4344 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4345 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4346 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4347 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4348 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4349 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4350 gtk_text_buffer_remove_tag_by_name(
4351 buffer, "quote0", &startnoquote, &endnoquote);
4352 gtk_text_buffer_remove_tag_by_name(
4353 buffer, "quote1", &startnoquote, &endnoquote);
4354 gtk_text_buffer_remove_tag_by_name(
4355 buffer, "quote2", &startnoquote, &endnoquote);
4361 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4362 GtkTextIter nouri_start_iter, nouri_end_iter;
4363 gtk_text_buffer_get_iter_at_offset(
4364 buffer, &nouri_start_iter, nouri_start);
4365 gtk_text_buffer_get_iter_at_offset(
4366 buffer, &nouri_end_iter, nouri_stop);
4367 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4368 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4369 gtk_text_buffer_remove_tag_by_name(
4370 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4371 modified_before_remove = modified;
4376 if (uri_start >= 0 && uri_stop > 0) {
4377 GtkTextIter uri_start_iter, uri_end_iter, back;
4378 gtk_text_buffer_get_iter_at_offset(
4379 buffer, &uri_start_iter, uri_start);
4380 gtk_text_buffer_get_iter_at_offset(
4381 buffer, &uri_end_iter, uri_stop);
4382 back = uri_end_iter;
4383 gtk_text_iter_backward_char(&back);
4384 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4385 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4386 gtk_text_buffer_apply_tag_by_name(
4387 buffer, "link", &uri_start_iter, &uri_end_iter);
4389 if (removed && !modified_before_remove) {
4395 // debug_print("not modified, out after %d lines\n", lines);
4399 // debug_print("modified, out after %d lines\n", lines);
4401 g_free(itemized_chars);
4404 undo_wrapping(compose->undostruct, FALSE);
4405 compose->autowrap = prev_autowrap;
4410 void compose_action_cb(void *data)
4412 Compose *compose = (Compose *)data;
4413 compose_wrap_all(compose);
4416 static void compose_wrap_all(Compose *compose)
4418 compose_wrap_all_full(compose, FALSE);
4421 static void compose_wrap_all_full(Compose *compose, gboolean force)
4423 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4424 GtkTextBuffer *buffer;
4426 gboolean modified = TRUE;
4428 buffer = gtk_text_view_get_buffer(text);
4430 gtk_text_buffer_get_start_iter(buffer, &iter);
4431 while (!gtk_text_iter_is_end(&iter) && modified)
4432 modified = compose_beautify_paragraph(compose, &iter, force);
4436 static void compose_set_title(Compose *compose)
4442 edited = compose->modified ? _(" [Edited]") : "";
4444 subject = gtk_editable_get_chars(
4445 GTK_EDITABLE(compose->subject_entry), 0, -1);
4447 #ifndef GENERIC_UMPC
4448 if (subject && strlen(subject))
4449 str = g_strdup_printf(_("%s - Compose message%s"),
4452 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4454 str = g_strdup(_("Compose message"));
4457 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4463 * compose_current_mail_account:
4465 * Find a current mail account (the currently selected account, or the
4466 * default account, if a news account is currently selected). If a
4467 * mail account cannot be found, display an error message.
4469 * Return value: Mail account, or NULL if not found.
4471 static PrefsAccount *
4472 compose_current_mail_account(void)
4476 if (cur_account && cur_account->protocol != A_NNTP)
4479 ac = account_get_default();
4480 if (!ac || ac->protocol == A_NNTP) {
4481 alertpanel_error(_("Account for sending mail is not specified.\n"
4482 "Please select a mail account before sending."));
4489 #define QUOTE_IF_REQUIRED(out, str) \
4491 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4495 len = strlen(str) + 3; \
4496 if ((__tmp = alloca(len)) == NULL) { \
4497 g_warning("can't allocate memory\n"); \
4498 g_string_free(header, TRUE); \
4501 g_snprintf(__tmp, len, "\"%s\"", str); \
4506 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4507 g_warning("can't allocate memory\n"); \
4508 g_string_free(header, TRUE); \
4511 strcpy(__tmp, str); \
4517 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4519 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4523 len = strlen(str) + 3; \
4524 if ((__tmp = alloca(len)) == NULL) { \
4525 g_warning("can't allocate memory\n"); \
4528 g_snprintf(__tmp, len, "\"%s\"", str); \
4533 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4534 g_warning("can't allocate memory\n"); \
4537 strcpy(__tmp, str); \
4543 static void compose_select_account(Compose *compose, PrefsAccount *account,
4546 gchar *from = NULL, *header;
4547 ComposeHeaderEntry *header_entry;
4549 cm_return_if_fail(account != NULL);
4551 compose->account = account;
4553 if (account->name && *account->name) {
4555 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4556 from = g_strdup_printf("%s <%s>",
4557 buf, account->address);
4558 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4560 from = g_strdup_printf("<%s>",
4562 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4567 compose_set_title(compose);
4569 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4572 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4573 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4574 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4576 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4578 activate_privacy_system(compose, account, FALSE);
4580 if (!init && compose->mode != COMPOSE_REDIRECT) {
4581 undo_block(compose->undostruct);
4582 compose_insert_sig(compose, TRUE);
4583 undo_unblock(compose->undostruct);
4586 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4587 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4589 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4590 if (account->protocol == A_NNTP) {
4591 if (!strcmp(header, _("To:")))
4592 combobox_select_by_text(
4593 GTK_COMBO_BOX(header_entry->combo),
4596 if (!strcmp(header, _("Newsgroups:")))
4597 combobox_select_by_text(
4598 GTK_COMBO_BOX(header_entry->combo),
4606 /* use account's dict info if set */
4607 if (compose->gtkaspell) {
4608 if (account->enable_default_dictionary)
4609 gtkaspell_change_dict(compose->gtkaspell,
4610 account->default_dictionary, FALSE);
4611 if (account->enable_default_alt_dictionary)
4612 gtkaspell_change_alt_dict(compose->gtkaspell,
4613 account->default_alt_dictionary);
4614 if (account->enable_default_dictionary
4615 || account->enable_default_alt_dictionary)
4616 compose_spell_menu_changed(compose);
4621 gboolean compose_check_for_valid_recipient(Compose *compose) {
4622 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4623 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4624 gboolean recipient_found = FALSE;
4628 /* free to and newsgroup list */
4629 slist_free_strings(compose->to_list);
4630 g_slist_free(compose->to_list);
4631 compose->to_list = NULL;
4633 slist_free_strings(compose->newsgroup_list);
4634 g_slist_free(compose->newsgroup_list);
4635 compose->newsgroup_list = NULL;
4637 /* search header entries for to and newsgroup entries */
4638 for (list = compose->header_list; list; list = list->next) {
4641 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4642 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4645 if (entry[0] != '\0') {
4646 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4647 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4648 compose->to_list = address_list_append(compose->to_list, entry);
4649 recipient_found = TRUE;
4652 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4653 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4654 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4655 recipient_found = TRUE;
4662 return recipient_found;
4665 static gboolean compose_check_for_set_recipients(Compose *compose)
4667 if (compose->account->set_autocc && compose->account->auto_cc) {
4668 gboolean found_other = FALSE;
4670 /* search header entries for to and newsgroup entries */
4671 for (list = compose->header_list; list; list = list->next) {
4674 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4675 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4678 if (strcmp(entry, compose->account->auto_cc)
4679 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4689 if (compose->batch) {
4690 gtk_widget_show_all(compose->window);
4692 aval = alertpanel(_("Send"),
4693 _("The only recipient is the default CC address. Send anyway?"),
4694 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4695 if (aval != G_ALERTALTERNATE)
4699 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4700 gboolean found_other = FALSE;
4702 /* search header entries for to and newsgroup entries */
4703 for (list = compose->header_list; list; list = list->next) {
4706 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4707 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4710 if (strcmp(entry, compose->account->auto_bcc)
4711 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4721 if (compose->batch) {
4722 gtk_widget_show_all(compose->window);
4724 aval = alertpanel(_("Send"),
4725 _("The only recipient is the default BCC address. Send anyway?"),
4726 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4727 if (aval != G_ALERTALTERNATE)
4734 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4738 if (compose_check_for_valid_recipient(compose) == FALSE) {
4739 if (compose->batch) {
4740 gtk_widget_show_all(compose->window);
4742 alertpanel_error(_("Recipient is not specified."));
4746 if (compose_check_for_set_recipients(compose) == FALSE) {
4750 if (!compose->batch) {
4751 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4752 if (*str == '\0' && check_everything == TRUE &&
4753 compose->mode != COMPOSE_REDIRECT) {
4755 gchar *button_label;
4758 if (compose->sending)
4759 button_label = _("+_Send");
4761 button_label = _("+_Queue");
4762 message = g_strdup_printf(_("Subject is empty. %s"),
4763 compose->sending?_("Send it anyway?"):
4764 _("Queue it anyway?"));
4766 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4767 GTK_STOCK_CANCEL, button_label, NULL);
4769 if (aval != G_ALERTALTERNATE)
4774 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4780 gint compose_send(Compose *compose)
4783 FolderItem *folder = NULL;
4785 gchar *msgpath = NULL;
4786 gboolean discard_window = FALSE;
4787 gchar *errstr = NULL;
4788 gchar *tmsgid = NULL;
4789 MainWindow *mainwin = mainwindow_get_mainwindow();
4790 gboolean queued_removed = FALSE;
4792 if (prefs_common.send_dialog_invisible
4793 || compose->batch == TRUE)
4794 discard_window = TRUE;
4796 compose_allow_user_actions (compose, FALSE);
4797 compose->sending = TRUE;
4799 if (compose_check_entries(compose, TRUE) == FALSE) {
4800 if (compose->batch) {
4801 gtk_widget_show_all(compose->window);
4807 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4810 if (compose->batch) {
4811 gtk_widget_show_all(compose->window);
4814 alertpanel_error(_("Could not queue message for sending:\n\n"
4815 "Charset conversion failed."));
4816 } else if (val == -5) {
4817 alertpanel_error(_("Could not queue message for sending:\n\n"
4818 "Couldn't get recipient encryption key."));
4819 } else if (val == -6) {
4821 } else if (val == -3) {
4822 if (privacy_peek_error())
4823 alertpanel_error(_("Could not queue message for sending:\n\n"
4824 "Signature failed: %s"), privacy_get_error());
4825 } else if (val == -2 && errno != 0) {
4826 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4828 alertpanel_error(_("Could not queue message for sending."));
4833 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4834 if (discard_window) {
4835 compose->sending = FALSE;
4836 compose_close(compose);
4837 /* No more compose access in the normal codepath
4838 * after this point! */
4843 alertpanel_error(_("The message was queued but could not be "
4844 "sent.\nUse \"Send queued messages\" from "
4845 "the main window to retry."));
4846 if (!discard_window) {
4853 if (msgpath == NULL) {
4854 msgpath = folder_item_fetch_msg(folder, msgnum);
4855 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4858 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4859 claws_unlink(msgpath);
4862 if (!discard_window) {
4864 if (!queued_removed)
4865 folder_item_remove_msg(folder, msgnum);
4866 folder_item_scan(folder);
4868 /* make sure we delete that */
4869 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4871 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4872 folder_item_remove_msg(folder, tmp->msgnum);
4873 procmsg_msginfo_free(tmp);
4880 if (!queued_removed)
4881 folder_item_remove_msg(folder, msgnum);
4882 folder_item_scan(folder);
4884 /* make sure we delete that */
4885 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4887 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4888 folder_item_remove_msg(folder, tmp->msgnum);
4889 procmsg_msginfo_free(tmp);
4892 if (!discard_window) {
4893 compose->sending = FALSE;
4894 compose_allow_user_actions (compose, TRUE);
4895 compose_close(compose);
4899 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4900 "the main window to retry."), errstr);
4903 alertpanel_error_log(_("The message was queued but could not be "
4904 "sent.\nUse \"Send queued messages\" from "
4905 "the main window to retry."));
4907 if (!discard_window) {
4916 toolbar_main_set_sensitive(mainwin);
4917 main_window_set_menu_sensitive(mainwin);
4923 compose_allow_user_actions (compose, TRUE);
4924 compose->sending = FALSE;
4925 compose->modified = TRUE;
4926 toolbar_main_set_sensitive(mainwin);
4927 main_window_set_menu_sensitive(mainwin);
4932 static gboolean compose_use_attach(Compose *compose)
4934 GtkTreeModel *model = gtk_tree_view_get_model
4935 (GTK_TREE_VIEW(compose->attach_clist));
4936 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4939 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4942 gchar buf[BUFFSIZE];
4944 gboolean first_to_address;
4945 gboolean first_cc_address;
4947 ComposeHeaderEntry *headerentry;
4948 const gchar *headerentryname;
4949 const gchar *cc_hdr;
4950 const gchar *to_hdr;
4951 gboolean err = FALSE;
4953 debug_print("Writing redirect header\n");
4955 cc_hdr = prefs_common_translated_header_name("Cc:");
4956 to_hdr = prefs_common_translated_header_name("To:");
4958 first_to_address = TRUE;
4959 for (list = compose->header_list; list; list = list->next) {
4960 headerentry = ((ComposeHeaderEntry *)list->data);
4961 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4963 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4964 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4965 Xstrdup_a(str, entstr, return -1);
4967 if (str[0] != '\0') {
4968 compose_convert_header
4969 (compose, buf, sizeof(buf), str,
4970 strlen("Resent-To") + 2, TRUE);
4972 if (first_to_address) {
4973 err |= (fprintf(fp, "Resent-To: ") < 0);
4974 first_to_address = FALSE;
4976 err |= (fprintf(fp, ",") < 0);
4978 err |= (fprintf(fp, "%s", buf) < 0);
4982 if (!first_to_address) {
4983 err |= (fprintf(fp, "\n") < 0);
4986 first_cc_address = TRUE;
4987 for (list = compose->header_list; list; list = list->next) {
4988 headerentry = ((ComposeHeaderEntry *)list->data);
4989 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4991 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4992 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4993 Xstrdup_a(str, strg, return -1);
4995 if (str[0] != '\0') {
4996 compose_convert_header
4997 (compose, buf, sizeof(buf), str,
4998 strlen("Resent-Cc") + 2, TRUE);
5000 if (first_cc_address) {
5001 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5002 first_cc_address = FALSE;
5004 err |= (fprintf(fp, ",") < 0);
5006 err |= (fprintf(fp, "%s", buf) < 0);
5010 if (!first_cc_address) {
5011 err |= (fprintf(fp, "\n") < 0);
5014 return (err ? -1:0);
5017 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5019 gchar buf[BUFFSIZE];
5021 const gchar *entstr;
5022 /* struct utsname utsbuf; */
5023 gboolean err = FALSE;
5025 cm_return_val_if_fail(fp != NULL, -1);
5026 cm_return_val_if_fail(compose->account != NULL, -1);
5027 cm_return_val_if_fail(compose->account->address != NULL, -1);
5030 get_rfc822_date(buf, sizeof(buf));
5031 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5034 if (compose->account->name && *compose->account->name) {
5035 compose_convert_header
5036 (compose, buf, sizeof(buf), compose->account->name,
5037 strlen("From: "), TRUE);
5038 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5039 buf, compose->account->address) < 0);
5041 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5044 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5045 if (*entstr != '\0') {
5046 Xstrdup_a(str, entstr, return -1);
5049 compose_convert_header(compose, buf, sizeof(buf), str,
5050 strlen("Subject: "), FALSE);
5051 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5055 /* Resent-Message-ID */
5056 if (compose->account->set_domain && compose->account->domain) {
5057 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5058 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5059 g_snprintf(buf, sizeof(buf), "%s",
5060 strchr(compose->account->address, '@') ?
5061 strchr(compose->account->address, '@')+1 :
5062 compose->account->address);
5064 g_snprintf(buf, sizeof(buf), "%s", "");
5067 if (compose->account->gen_msgid) {
5068 generate_msgid(buf, sizeof(buf));
5069 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5070 compose->msgid = g_strdup(buf);
5072 compose->msgid = NULL;
5075 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5078 /* separator between header and body */
5079 err |= (fputs("\n", fp) == EOF);
5081 return (err ? -1:0);
5084 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5088 gchar buf[BUFFSIZE];
5090 gboolean skip = FALSE;
5091 gboolean err = FALSE;
5092 gchar *not_included[]={
5093 "Return-Path:", "Delivered-To:", "Received:",
5094 "Subject:", "X-UIDL:", "AF:",
5095 "NF:", "PS:", "SRH:",
5096 "SFN:", "DSR:", "MID:",
5097 "CFG:", "PT:", "S:",
5098 "RQ:", "SSV:", "NSV:",
5099 "SSH:", "R:", "MAID:",
5100 "NAID:", "RMID:", "FMID:",
5101 "SCF:", "RRCPT:", "NG:",
5102 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5103 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5104 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5105 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5106 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5109 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5110 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5114 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5116 for (i = 0; not_included[i] != NULL; i++) {
5117 if (g_ascii_strncasecmp(buf, not_included[i],
5118 strlen(not_included[i])) == 0) {
5125 if (fputs(buf, fdest) == -1)
5128 if (!prefs_common.redirect_keep_from) {
5129 if (g_ascii_strncasecmp(buf, "From:",
5130 strlen("From:")) == 0) {
5131 err |= (fputs(" (by way of ", fdest) == EOF);
5132 if (compose->account->name
5133 && *compose->account->name) {
5134 compose_convert_header
5135 (compose, buf, sizeof(buf),
5136 compose->account->name,
5139 err |= (fprintf(fdest, "%s <%s>",
5141 compose->account->address) < 0);
5143 err |= (fprintf(fdest, "%s",
5144 compose->account->address) < 0);
5145 err |= (fputs(")", fdest) == EOF);
5149 if (fputs("\n", fdest) == -1)
5156 if (compose_redirect_write_headers(compose, fdest))
5159 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5160 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5173 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5175 GtkTextBuffer *buffer;
5176 GtkTextIter start, end;
5179 const gchar *out_codeset;
5180 EncodingType encoding = ENC_UNKNOWN;
5181 MimeInfo *mimemsg, *mimetext;
5183 const gchar *src_codeset = CS_INTERNAL;
5184 gchar *from_addr = NULL;
5185 gchar *from_name = NULL;
5187 if (action == COMPOSE_WRITE_FOR_SEND)
5188 attach_parts = TRUE;
5190 /* create message MimeInfo */
5191 mimemsg = procmime_mimeinfo_new();
5192 mimemsg->type = MIMETYPE_MESSAGE;
5193 mimemsg->subtype = g_strdup("rfc822");
5194 mimemsg->content = MIMECONTENT_MEM;
5195 mimemsg->tmp = TRUE; /* must free content later */
5196 mimemsg->data.mem = compose_get_header(compose);
5198 /* Create text part MimeInfo */
5199 /* get all composed text */
5200 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5201 gtk_text_buffer_get_start_iter(buffer, &start);
5202 gtk_text_buffer_get_end_iter(buffer, &end);
5203 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5205 out_codeset = conv_get_charset_str(compose->out_encoding);
5207 if (!out_codeset && is_ascii_str(chars)) {
5208 out_codeset = CS_US_ASCII;
5209 } else if (prefs_common.outgoing_fallback_to_ascii &&
5210 is_ascii_str(chars)) {
5211 out_codeset = CS_US_ASCII;
5212 encoding = ENC_7BIT;
5216 gchar *test_conv_global_out = NULL;
5217 gchar *test_conv_reply = NULL;
5219 /* automatic mode. be automatic. */
5220 codeconv_set_strict(TRUE);
5222 out_codeset = conv_get_outgoing_charset_str();
5224 debug_print("trying to convert to %s\n", out_codeset);
5225 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5228 if (!test_conv_global_out && compose->orig_charset
5229 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5230 out_codeset = compose->orig_charset;
5231 debug_print("failure; trying to convert to %s\n", out_codeset);
5232 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5235 if (!test_conv_global_out && !test_conv_reply) {
5237 out_codeset = CS_INTERNAL;
5238 debug_print("failure; finally using %s\n", out_codeset);
5240 g_free(test_conv_global_out);
5241 g_free(test_conv_reply);
5242 codeconv_set_strict(FALSE);
5245 if (encoding == ENC_UNKNOWN) {
5246 if (prefs_common.encoding_method == CTE_BASE64)
5247 encoding = ENC_BASE64;
5248 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5249 encoding = ENC_QUOTED_PRINTABLE;
5250 else if (prefs_common.encoding_method == CTE_8BIT)
5251 encoding = ENC_8BIT;
5253 encoding = procmime_get_encoding_for_charset(out_codeset);
5256 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5257 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5259 if (action == COMPOSE_WRITE_FOR_SEND) {
5260 codeconv_set_strict(TRUE);
5261 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5262 codeconv_set_strict(FALSE);
5268 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5269 "to the specified %s charset.\n"
5270 "Send it as %s?"), out_codeset, src_codeset);
5271 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5272 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5275 if (aval != G_ALERTALTERNATE) {
5280 out_codeset = src_codeset;
5286 out_codeset = src_codeset;
5291 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5292 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5293 strstr(buf, "\nFrom ") != NULL) {
5294 encoding = ENC_QUOTED_PRINTABLE;
5298 mimetext = procmime_mimeinfo_new();
5299 mimetext->content = MIMECONTENT_MEM;
5300 mimetext->tmp = TRUE; /* must free content later */
5301 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5302 * and free the data, which we need later. */
5303 mimetext->data.mem = g_strdup(buf);
5304 mimetext->type = MIMETYPE_TEXT;
5305 mimetext->subtype = g_strdup("plain");
5306 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5307 g_strdup(out_codeset));
5309 /* protect trailing spaces when signing message */
5310 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5311 privacy_system_can_sign(compose->privacy_system)) {
5312 encoding = ENC_QUOTED_PRINTABLE;
5315 debug_print("main text: %zd bytes encoded as %s in %d\n",
5316 strlen(buf), out_codeset, encoding);
5318 /* check for line length limit */
5319 if (action == COMPOSE_WRITE_FOR_SEND &&
5320 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5321 check_line_length(buf, 1000, &line) < 0) {
5325 msg = g_strdup_printf
5326 (_("Line %d exceeds the line length limit (998 bytes).\n"
5327 "The contents of the message might be broken on the way to the delivery.\n"
5329 "Send it anyway?"), line + 1);
5330 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5332 if (aval != G_ALERTALTERNATE) {
5338 if (encoding != ENC_UNKNOWN)
5339 procmime_encode_content(mimetext, encoding);
5341 /* append attachment parts */
5342 if (compose_use_attach(compose) && attach_parts) {
5343 MimeInfo *mimempart;
5344 gchar *boundary = NULL;
5345 mimempart = procmime_mimeinfo_new();
5346 mimempart->content = MIMECONTENT_EMPTY;
5347 mimempart->type = MIMETYPE_MULTIPART;
5348 mimempart->subtype = g_strdup("mixed");
5352 boundary = generate_mime_boundary(NULL);
5353 } while (strstr(buf, boundary) != NULL);
5355 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5358 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5360 g_node_append(mimempart->node, mimetext->node);
5361 g_node_append(mimemsg->node, mimempart->node);
5363 if (compose_add_attachments(compose, mimempart) < 0)
5366 g_node_append(mimemsg->node, mimetext->node);
5370 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5371 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5372 /* extract name and address */
5373 if (strstr(spec, " <") && strstr(spec, ">")) {
5374 from_addr = g_strdup(strrchr(spec, '<')+1);
5375 *(strrchr(from_addr, '>')) = '\0';
5376 from_name = g_strdup(spec);
5377 *(strrchr(from_name, '<')) = '\0';
5384 /* sign message if sending */
5385 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5386 privacy_system_can_sign(compose->privacy_system))
5387 if (!privacy_sign(compose->privacy_system, mimemsg,
5388 compose->account, from_addr)) {
5395 procmime_write_mimeinfo(mimemsg, fp);
5397 procmime_mimeinfo_free_all(mimemsg);
5402 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5404 GtkTextBuffer *buffer;
5405 GtkTextIter start, end;
5410 if ((fp = g_fopen(file, "wb")) == NULL) {
5411 FILE_OP_ERROR(file, "fopen");
5415 /* chmod for security */
5416 if (change_file_mode_rw(fp, file) < 0) {
5417 FILE_OP_ERROR(file, "chmod");
5418 g_warning("can't change file mode\n");
5421 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5422 gtk_text_buffer_get_start_iter(buffer, &start);
5423 gtk_text_buffer_get_end_iter(buffer, &end);
5424 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5426 chars = conv_codeset_strdup
5427 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5430 if (!chars) return -1;
5433 len = strlen(chars);
5434 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5435 FILE_OP_ERROR(file, "fwrite");
5444 if (fclose(fp) == EOF) {
5445 FILE_OP_ERROR(file, "fclose");
5452 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5455 MsgInfo *msginfo = compose->targetinfo;
5457 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5458 if (!msginfo) return -1;
5460 if (!force && MSG_IS_LOCKED(msginfo->flags))
5463 item = msginfo->folder;
5464 cm_return_val_if_fail(item != NULL, -1);
5466 if (procmsg_msg_exist(msginfo) &&
5467 (folder_has_parent_of_type(item, F_QUEUE) ||
5468 folder_has_parent_of_type(item, F_DRAFT)
5469 || msginfo == compose->autosaved_draft)) {
5470 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5471 g_warning("can't remove the old message\n");
5474 debug_print("removed reedit target %d\n", msginfo->msgnum);
5481 static void compose_remove_draft(Compose *compose)
5484 MsgInfo *msginfo = compose->targetinfo;
5485 drafts = account_get_special_folder(compose->account, F_DRAFT);
5487 if (procmsg_msg_exist(msginfo)) {
5488 folder_item_remove_msg(drafts, msginfo->msgnum);
5493 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5494 gboolean remove_reedit_target)
5496 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5499 static gboolean compose_warn_encryption(Compose *compose)
5501 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5502 AlertValue val = G_ALERTALTERNATE;
5504 if (warning == NULL)
5507 val = alertpanel_full(_("Encryption warning"), warning,
5508 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5509 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5510 if (val & G_ALERTDISABLE) {
5511 val &= ~G_ALERTDISABLE;
5512 if (val == G_ALERTALTERNATE)
5513 privacy_inhibit_encrypt_warning(compose->privacy_system,
5517 if (val == G_ALERTALTERNATE) {
5524 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5525 gchar **msgpath, gboolean check_subject,
5526 gboolean remove_reedit_target)
5533 static gboolean lock = FALSE;
5534 PrefsAccount *mailac = NULL, *newsac = NULL;
5535 gboolean err = FALSE;
5537 debug_print("queueing message...\n");
5538 cm_return_val_if_fail(compose->account != NULL, -1);
5542 if (compose_check_entries(compose, check_subject) == FALSE) {
5544 if (compose->batch) {
5545 gtk_widget_show_all(compose->window);
5550 if (!compose->to_list && !compose->newsgroup_list) {
5551 g_warning("can't get recipient list.");
5556 if (compose->to_list) {
5557 if (compose->account->protocol != A_NNTP)
5558 mailac = compose->account;
5559 else if (cur_account && cur_account->protocol != A_NNTP)
5560 mailac = cur_account;
5561 else if (!(mailac = compose_current_mail_account())) {
5563 alertpanel_error(_("No account for sending mails available!"));
5568 if (compose->newsgroup_list) {
5569 if (compose->account->protocol == A_NNTP)
5570 newsac = compose->account;
5573 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5578 /* write queue header */
5579 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5580 G_DIR_SEPARATOR, compose, (guint) rand());
5581 debug_print("queuing to %s\n", tmp);
5582 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5583 FILE_OP_ERROR(tmp, "fopen");
5589 if (change_file_mode_rw(fp, tmp) < 0) {
5590 FILE_OP_ERROR(tmp, "chmod");
5591 g_warning("can't change file mode\n");
5594 /* queueing variables */
5595 err |= (fprintf(fp, "AF:\n") < 0);
5596 err |= (fprintf(fp, "NF:0\n") < 0);
5597 err |= (fprintf(fp, "PS:10\n") < 0);
5598 err |= (fprintf(fp, "SRH:1\n") < 0);
5599 err |= (fprintf(fp, "SFN:\n") < 0);
5600 err |= (fprintf(fp, "DSR:\n") < 0);
5602 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5604 err |= (fprintf(fp, "MID:\n") < 0);
5605 err |= (fprintf(fp, "CFG:\n") < 0);
5606 err |= (fprintf(fp, "PT:0\n") < 0);
5607 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5608 err |= (fprintf(fp, "RQ:\n") < 0);
5610 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5612 err |= (fprintf(fp, "SSV:\n") < 0);
5614 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5616 err |= (fprintf(fp, "NSV:\n") < 0);
5617 err |= (fprintf(fp, "SSH:\n") < 0);
5618 /* write recepient list */
5619 if (compose->to_list) {
5620 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5621 for (cur = compose->to_list->next; cur != NULL;
5623 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5624 err |= (fprintf(fp, "\n") < 0);
5626 /* write newsgroup list */
5627 if (compose->newsgroup_list) {
5628 err |= (fprintf(fp, "NG:") < 0);
5629 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5630 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5631 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5632 err |= (fprintf(fp, "\n") < 0);
5634 /* Sylpheed account IDs */
5636 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5638 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5641 if (compose->privacy_system != NULL) {
5642 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5643 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5644 if (compose->use_encryption) {
5646 if (!compose_warn_encryption(compose)) {
5653 if (mailac && mailac->encrypt_to_self) {
5654 GSList *tmp_list = g_slist_copy(compose->to_list);
5655 tmp_list = g_slist_append(tmp_list, compose->account->address);
5656 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5657 g_slist_free(tmp_list);
5659 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5661 if (encdata != NULL) {
5662 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5663 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5664 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5666 } /* else we finally dont want to encrypt */
5668 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5669 /* and if encdata was null, it means there's been a problem in
5681 /* Save copy folder */
5682 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5683 gchar *savefolderid;
5685 savefolderid = compose_get_save_to(compose);
5686 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5687 g_free(savefolderid);
5689 /* Save copy folder */
5690 if (compose->return_receipt) {
5691 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5693 /* Message-ID of message replying to */
5694 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5697 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5698 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5701 /* Message-ID of message forwarding to */
5702 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5705 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5706 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5710 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5711 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5713 /* end of headers */
5714 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5716 if (compose->redirect_filename != NULL) {
5717 if (compose_redirect_write_to_file(compose, fp) < 0) {
5726 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5731 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5735 g_warning("failed to write queue message\n");
5742 if (fclose(fp) == EOF) {
5743 FILE_OP_ERROR(tmp, "fclose");
5750 if (item && *item) {
5753 queue = account_get_special_folder(compose->account, F_QUEUE);
5756 g_warning("can't find queue folder\n");
5762 folder_item_scan(queue);
5763 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5764 g_warning("can't queue the message\n");
5771 if (msgpath == NULL) {
5777 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5778 compose_remove_reedit_target(compose, FALSE);
5781 if ((msgnum != NULL) && (item != NULL)) {
5789 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5792 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5794 struct stat statbuf;
5795 gchar *type, *subtype;
5796 GtkTreeModel *model;
5799 model = gtk_tree_view_get_model(tree_view);
5801 if (!gtk_tree_model_get_iter_first(model, &iter))
5804 gtk_tree_model_get(model, &iter,
5808 if (!is_file_exist(ainfo->file)) {
5809 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5810 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5811 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5813 if (val == G_ALERTDEFAULT) {
5818 mimepart = procmime_mimeinfo_new();
5819 mimepart->content = MIMECONTENT_FILE;
5820 mimepart->data.filename = g_strdup(ainfo->file);
5821 mimepart->tmp = FALSE; /* or we destroy our attachment */
5822 mimepart->offset = 0;
5824 g_stat(ainfo->file, &statbuf);
5825 mimepart->length = statbuf.st_size;
5827 type = g_strdup(ainfo->content_type);
5829 if (!strchr(type, '/')) {
5831 type = g_strdup("application/octet-stream");
5834 subtype = strchr(type, '/') + 1;
5835 *(subtype - 1) = '\0';
5836 mimepart->type = procmime_get_media_type(type);
5837 mimepart->subtype = g_strdup(subtype);
5840 if (mimepart->type == MIMETYPE_MESSAGE &&
5841 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5842 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5845 if (mimepart->type == MIMETYPE_APPLICATION &&
5846 !strcmp2(mimepart->subtype, "octet-stream"))
5847 g_hash_table_insert(mimepart->typeparameters,
5848 g_strdup("name"), g_strdup(ainfo->name));
5849 g_hash_table_insert(mimepart->dispositionparameters,
5850 g_strdup("filename"), g_strdup(ainfo->name));
5851 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5855 if (compose->use_signing) {
5856 if (ainfo->encoding == ENC_7BIT)
5857 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5858 else if (ainfo->encoding == ENC_8BIT)
5859 ainfo->encoding = ENC_BASE64;
5862 procmime_encode_content(mimepart, ainfo->encoding);
5864 g_node_append(parent->node, mimepart->node);
5865 } while (gtk_tree_model_iter_next(model, &iter));
5870 #define IS_IN_CUSTOM_HEADER(header) \
5871 (compose->account->add_customhdr && \
5872 custom_header_find(compose->account->customhdr_list, header) != NULL)
5874 static void compose_add_headerfield_from_headerlist(Compose *compose,
5876 const gchar *fieldname,
5877 const gchar *seperator)
5879 gchar *str, *fieldname_w_colon;
5880 gboolean add_field = FALSE;
5882 ComposeHeaderEntry *headerentry;
5883 const gchar *headerentryname;
5884 const gchar *trans_fieldname;
5887 if (IS_IN_CUSTOM_HEADER(fieldname))
5890 debug_print("Adding %s-fields\n", fieldname);
5892 fieldstr = g_string_sized_new(64);
5894 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5895 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5897 for (list = compose->header_list; list; list = list->next) {
5898 headerentry = ((ComposeHeaderEntry *)list->data);
5899 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5901 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5902 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5904 if (str[0] != '\0') {
5906 g_string_append(fieldstr, seperator);
5907 g_string_append(fieldstr, str);
5916 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5917 compose_convert_header
5918 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5919 strlen(fieldname) + 2, TRUE);
5920 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5924 g_free(fieldname_w_colon);
5925 g_string_free(fieldstr, TRUE);
5930 static gchar *compose_get_header(Compose *compose)
5932 gchar buf[BUFFSIZE];
5933 const gchar *entry_str;
5937 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5939 gchar *from_name = NULL, *from_address = NULL;
5942 cm_return_val_if_fail(compose->account != NULL, NULL);
5943 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5945 header = g_string_sized_new(64);
5948 get_rfc822_date(buf, sizeof(buf));
5949 g_string_append_printf(header, "Date: %s\n", buf);
5953 if (compose->account->name && *compose->account->name) {
5955 QUOTE_IF_REQUIRED(buf, compose->account->name);
5956 tmp = g_strdup_printf("%s <%s>",
5957 buf, compose->account->address);
5959 tmp = g_strdup_printf("%s",
5960 compose->account->address);
5962 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5963 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5965 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5966 from_address = g_strdup(compose->account->address);
5968 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5969 /* extract name and address */
5970 if (strstr(spec, " <") && strstr(spec, ">")) {
5971 from_address = g_strdup(strrchr(spec, '<')+1);
5972 *(strrchr(from_address, '>')) = '\0';
5973 from_name = g_strdup(spec);
5974 *(strrchr(from_name, '<')) = '\0';
5977 from_address = g_strdup(spec);
5984 if (from_name && *from_name) {
5985 compose_convert_header
5986 (compose, buf, sizeof(buf), from_name,
5987 strlen("From: "), TRUE);
5988 QUOTE_IF_REQUIRED(name, buf);
5990 g_string_append_printf(header, "From: %s <%s>\n",
5991 name, from_address);
5993 g_string_append_printf(header, "From: %s\n", from_address);
5996 g_free(from_address);
5999 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6002 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6005 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6008 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6011 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6013 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6016 compose_convert_header(compose, buf, sizeof(buf), str,
6017 strlen("Subject: "), FALSE);
6018 g_string_append_printf(header, "Subject: %s\n", buf);
6024 if (compose->account->set_domain && compose->account->domain) {
6025 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6026 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6027 g_snprintf(buf, sizeof(buf), "%s",
6028 strchr(compose->account->address, '@') ?
6029 strchr(compose->account->address, '@')+1 :
6030 compose->account->address);
6032 g_snprintf(buf, sizeof(buf), "%s", "");
6035 if (compose->account->gen_msgid) {
6036 generate_msgid(buf, sizeof(buf));
6037 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6038 compose->msgid = g_strdup(buf);
6040 compose->msgid = NULL;
6043 if (compose->remove_references == FALSE) {
6045 if (compose->inreplyto && compose->to_list)
6046 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6049 if (compose->references)
6050 g_string_append_printf(header, "References: %s\n", compose->references);
6054 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6057 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6060 if (compose->account->organization &&
6061 strlen(compose->account->organization) &&
6062 !IS_IN_CUSTOM_HEADER("Organization")) {
6063 compose_convert_header(compose, buf, sizeof(buf),
6064 compose->account->organization,
6065 strlen("Organization: "), FALSE);
6066 g_string_append_printf(header, "Organization: %s\n", buf);
6069 /* Program version and system info */
6070 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6071 !compose->newsgroup_list) {
6072 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6074 gtk_major_version, gtk_minor_version, gtk_micro_version,
6077 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6078 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6080 gtk_major_version, gtk_minor_version, gtk_micro_version,
6084 /* custom headers */
6085 if (compose->account->add_customhdr) {
6088 for (cur = compose->account->customhdr_list; cur != NULL;
6090 CustomHeader *chdr = (CustomHeader *)cur->data;
6092 if (custom_header_is_allowed(chdr->name)) {
6093 compose_convert_header
6094 (compose, buf, sizeof(buf),
6095 chdr->value ? chdr->value : "",
6096 strlen(chdr->name) + 2, FALSE);
6097 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6102 /* Automatic Faces and X-Faces */
6103 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6104 g_string_append_printf(header, "X-Face: %s\n", buf);
6106 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6107 g_string_append_printf(header, "X-Face: %s\n", buf);
6109 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6110 g_string_append_printf(header, "Face: %s\n", buf);
6112 else if (get_default_face (buf, sizeof(buf)) == 0) {
6113 g_string_append_printf(header, "Face: %s\n", buf);
6117 switch (compose->priority) {
6118 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6119 "X-Priority: 1 (Highest)\n");
6121 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6122 "X-Priority: 2 (High)\n");
6124 case PRIORITY_NORMAL: break;
6125 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6126 "X-Priority: 4 (Low)\n");
6128 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6129 "X-Priority: 5 (Lowest)\n");
6131 default: debug_print("compose: priority unknown : %d\n",
6135 /* Request Return Receipt */
6136 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6137 if (compose->return_receipt) {
6138 if (compose->account->name
6139 && *compose->account->name) {
6140 compose_convert_header(compose, buf, sizeof(buf),
6141 compose->account->name,
6142 strlen("Disposition-Notification-To: "),
6144 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6146 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6150 /* get special headers */
6151 for (list = compose->header_list; list; list = list->next) {
6152 ComposeHeaderEntry *headerentry;
6155 gchar *headername_wcolon;
6156 const gchar *headername_trans;
6159 gboolean standard_header = FALSE;
6161 headerentry = ((ComposeHeaderEntry *)list->data);
6163 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6165 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6170 if (!strstr(tmp, ":")) {
6171 headername_wcolon = g_strconcat(tmp, ":", NULL);
6172 headername = g_strdup(tmp);
6174 headername_wcolon = g_strdup(tmp);
6175 headername = g_strdup(strtok(tmp, ":"));
6179 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6180 Xstrdup_a(headervalue, entry_str, return NULL);
6181 subst_char(headervalue, '\r', ' ');
6182 subst_char(headervalue, '\n', ' ');
6183 string = std_headers;
6184 while (*string != NULL) {
6185 headername_trans = prefs_common_translated_header_name(*string);
6186 if (!strcmp(headername_trans, headername_wcolon))
6187 standard_header = TRUE;
6190 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6191 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6194 g_free(headername_wcolon);
6198 g_string_free(header, FALSE);
6203 #undef IS_IN_CUSTOM_HEADER
6205 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6206 gint header_len, gboolean addr_field)
6208 gchar *tmpstr = NULL;
6209 const gchar *out_codeset = NULL;
6211 cm_return_if_fail(src != NULL);
6212 cm_return_if_fail(dest != NULL);
6214 if (len < 1) return;
6216 tmpstr = g_strdup(src);
6218 subst_char(tmpstr, '\n', ' ');
6219 subst_char(tmpstr, '\r', ' ');
6222 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6223 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6224 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6229 codeconv_set_strict(TRUE);
6230 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6231 conv_get_charset_str(compose->out_encoding));
6232 codeconv_set_strict(FALSE);
6234 if (!dest || *dest == '\0') {
6235 gchar *test_conv_global_out = NULL;
6236 gchar *test_conv_reply = NULL;
6238 /* automatic mode. be automatic. */
6239 codeconv_set_strict(TRUE);
6241 out_codeset = conv_get_outgoing_charset_str();
6243 debug_print("trying to convert to %s\n", out_codeset);
6244 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6247 if (!test_conv_global_out && compose->orig_charset
6248 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6249 out_codeset = compose->orig_charset;
6250 debug_print("failure; trying to convert to %s\n", out_codeset);
6251 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6254 if (!test_conv_global_out && !test_conv_reply) {
6256 out_codeset = CS_INTERNAL;
6257 debug_print("finally using %s\n", out_codeset);
6259 g_free(test_conv_global_out);
6260 g_free(test_conv_reply);
6261 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6263 codeconv_set_strict(FALSE);
6268 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6272 cm_return_if_fail(user_data != NULL);
6274 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6275 g_strstrip(address);
6276 if (*address != '\0') {
6277 gchar *name = procheader_get_fromname(address);
6278 extract_address(address);
6279 addressbook_add_contact(name, address, NULL, NULL);
6284 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6286 GtkWidget *menuitem;
6289 cm_return_if_fail(menu != NULL);
6290 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6292 menuitem = gtk_separator_menu_item_new();
6293 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6294 gtk_widget_show(menuitem);
6296 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6297 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6299 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6300 g_strstrip(address);
6301 if (*address == '\0') {
6302 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6305 g_signal_connect(G_OBJECT(menuitem), "activate",
6306 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6307 gtk_widget_show(menuitem);
6310 static void compose_create_header_entry(Compose *compose)
6312 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6319 const gchar *header = NULL;
6320 ComposeHeaderEntry *headerentry;
6321 gboolean standard_header = FALSE;
6322 #if !(GTK_CHECK_VERSION(2,12,0))
6323 GtkTooltips *tips = compose->tooltips;
6326 headerentry = g_new0(ComposeHeaderEntry, 1);
6329 combo = gtk_combo_box_entry_new_text();
6331 while(*string != NULL) {
6332 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6333 (gchar*)prefs_common_translated_header_name(*string));
6336 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6337 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6338 G_CALLBACK(compose_grab_focus_cb), compose);
6339 gtk_widget_show(combo);
6340 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6341 compose->header_nextrow, compose->header_nextrow+1,
6342 GTK_SHRINK, GTK_FILL, 0, 0);
6343 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6344 const gchar *last_header_entry = gtk_entry_get_text(
6345 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6347 while (*string != NULL) {
6348 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6349 standard_header = TRUE;
6352 if (standard_header)
6353 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6355 if (!compose->header_last || !standard_header) {
6356 switch(compose->account->protocol) {
6358 header = prefs_common_translated_header_name("Newsgroups:");
6361 header = prefs_common_translated_header_name("To:");
6366 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6368 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6369 G_CALLBACK(compose_grab_focus_cb), compose);
6371 /* Entry field with cleanup button */
6372 button = gtk_button_new();
6373 gtk_button_set_image(GTK_BUTTON(button),
6374 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON));
6375 gtk_widget_show(button);
6376 CLAWS_SET_TIP(button,
6377 _("Delete entry contents"));
6378 entry = gtk_entry_new();
6379 gtk_widget_show(entry);
6380 CLAWS_SET_TIP(entry,
6381 _("Use <tab> to autocomplete from addressbook"));
6382 hbox = gtk_hbox_new (FALSE, 0);
6383 gtk_widget_show(hbox);
6384 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6385 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6386 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6387 compose->header_nextrow, compose->header_nextrow+1,
6388 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6390 g_signal_connect(G_OBJECT(entry), "key-press-event",
6391 G_CALLBACK(compose_headerentry_key_press_event_cb),
6393 g_signal_connect(G_OBJECT(entry), "changed",
6394 G_CALLBACK(compose_headerentry_changed_cb),
6396 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6397 G_CALLBACK(compose_grab_focus_cb), compose);
6399 g_signal_connect(G_OBJECT(button), "clicked",
6400 G_CALLBACK(compose_headerentry_button_clicked_cb),
6404 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6405 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6406 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6407 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6408 G_CALLBACK(compose_header_drag_received_cb),
6410 g_signal_connect(G_OBJECT(entry), "drag-drop",
6411 G_CALLBACK(compose_drag_drop),
6413 g_signal_connect(G_OBJECT(entry), "populate-popup",
6414 G_CALLBACK(compose_entry_popup_extend),
6417 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6419 headerentry->compose = compose;
6420 headerentry->combo = combo;
6421 headerentry->entry = entry;
6422 headerentry->button = button;
6423 headerentry->hbox = hbox;
6424 headerentry->headernum = compose->header_nextrow;
6426 compose->header_nextrow++;
6427 compose->header_last = headerentry;
6428 compose->header_list =
6429 g_slist_append(compose->header_list,
6433 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6435 ComposeHeaderEntry *last_header;
6437 last_header = compose->header_last;
6439 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6440 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6443 static void compose_remove_header_entries(Compose *compose)
6446 for (list = compose->header_list; list; list = list->next) {
6447 ComposeHeaderEntry *headerentry =
6448 (ComposeHeaderEntry *)list->data;
6449 gtk_widget_destroy(headerentry->combo);
6450 gtk_widget_destroy(headerentry->entry);
6451 g_free(headerentry);
6453 compose->header_last = NULL;
6454 g_slist_free(compose->header_list);
6455 compose->header_list = NULL;
6456 compose->header_nextrow = 1;
6457 compose_create_header_entry(compose);
6460 static GtkWidget *compose_create_header(Compose *compose)
6462 GtkWidget *from_optmenu_hbox;
6463 GtkWidget *header_scrolledwin;
6464 GtkWidget *header_table;
6468 /* header labels and entries */
6469 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6470 gtk_widget_show(header_scrolledwin);
6471 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6473 header_table = gtk_table_new(2, 2, FALSE);
6474 gtk_widget_show(header_table);
6475 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6476 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6477 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6480 /* option menu for selecting accounts */
6481 from_optmenu_hbox = compose_account_option_menu_create(compose);
6482 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6483 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6486 compose->header_table = header_table;
6487 compose->header_list = NULL;
6488 compose->header_nextrow = count;
6490 compose_create_header_entry(compose);
6492 compose->table = NULL;
6494 return header_scrolledwin ;
6497 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6499 Compose *compose = (Compose *)data;
6500 GdkEventButton event;
6503 event.time = gtk_get_current_event_time();
6505 return attach_button_pressed(compose->attach_clist, &event, compose);
6508 static GtkWidget *compose_create_attach(Compose *compose)
6510 GtkWidget *attach_scrwin;
6511 GtkWidget *attach_clist;
6513 GtkListStore *store;
6514 GtkCellRenderer *renderer;
6515 GtkTreeViewColumn *column;
6516 GtkTreeSelection *selection;
6518 /* attachment list */
6519 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6520 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6521 GTK_POLICY_AUTOMATIC,
6522 GTK_POLICY_AUTOMATIC);
6523 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6525 store = gtk_list_store_new(N_ATTACH_COLS,
6530 G_TYPE_AUTO_POINTER,
6532 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6533 (GTK_TREE_MODEL(store)));
6534 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6535 g_object_unref(store);
6537 renderer = gtk_cell_renderer_text_new();
6538 column = gtk_tree_view_column_new_with_attributes
6539 (_("Mime type"), renderer, "text",
6540 COL_MIMETYPE, NULL);
6541 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6543 renderer = gtk_cell_renderer_text_new();
6544 column = gtk_tree_view_column_new_with_attributes
6545 (_("Size"), renderer, "text",
6547 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6549 renderer = gtk_cell_renderer_text_new();
6550 column = gtk_tree_view_column_new_with_attributes
6551 (_("Name"), renderer, "text",
6553 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6555 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6556 prefs_common.use_stripes_everywhere);
6557 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6558 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6560 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6561 G_CALLBACK(attach_selected), compose);
6562 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6563 G_CALLBACK(attach_button_pressed), compose);
6565 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6566 G_CALLBACK(popup_attach_button_pressed), compose);
6568 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6569 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6570 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6571 G_CALLBACK(popup_attach_button_pressed), compose);
6573 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6574 G_CALLBACK(attach_key_pressed), compose);
6577 gtk_drag_dest_set(attach_clist,
6578 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6579 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6580 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6581 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6582 G_CALLBACK(compose_attach_drag_received_cb),
6584 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6585 G_CALLBACK(compose_drag_drop),
6588 compose->attach_scrwin = attach_scrwin;
6589 compose->attach_clist = attach_clist;
6591 return attach_scrwin;
6594 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6595 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6597 static GtkWidget *compose_create_others(Compose *compose)
6600 GtkWidget *savemsg_checkbtn;
6601 GtkWidget *savemsg_combo;
6602 GtkWidget *savemsg_select;
6605 gchar *folderidentifier;
6607 /* Table for settings */
6608 table = gtk_table_new(3, 1, FALSE);
6609 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6610 gtk_widget_show(table);
6611 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6614 /* Save Message to folder */
6615 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6616 gtk_widget_show(savemsg_checkbtn);
6617 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6618 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6619 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6621 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6622 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6624 savemsg_combo = gtk_combo_box_entry_new_text();
6625 compose->savemsg_checkbtn = savemsg_checkbtn;
6626 compose->savemsg_combo = savemsg_combo;
6627 gtk_widget_show(savemsg_combo);
6629 if (prefs_common.compose_save_to_history)
6630 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6631 prefs_common.compose_save_to_history);
6633 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6634 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6635 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6636 G_CALLBACK(compose_grab_focus_cb), compose);
6637 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6638 folderidentifier = folder_item_get_identifier(account_get_special_folder
6639 (compose->account, F_OUTBOX));
6640 compose_set_save_to(compose, folderidentifier);
6641 g_free(folderidentifier);
6644 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6645 gtk_widget_show(savemsg_select);
6646 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6647 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6648 G_CALLBACK(compose_savemsg_select_cb),
6656 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6658 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6659 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6662 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6667 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6670 path = folder_item_get_identifier(dest);
6672 compose_set_save_to(compose, path);
6676 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6677 GdkAtom clip, GtkTextIter *insert_place);
6680 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6684 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6686 if (event->button == 3) {
6688 GtkTextIter sel_start, sel_end;
6689 gboolean stuff_selected;
6691 /* move the cursor to allow GtkAspell to check the word
6692 * under the mouse */
6693 if (event->x && event->y) {
6694 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6695 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6697 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6700 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6701 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6704 stuff_selected = gtk_text_buffer_get_selection_bounds(
6706 &sel_start, &sel_end);
6708 gtk_text_buffer_place_cursor (buffer, &iter);
6709 /* reselect stuff */
6711 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6712 gtk_text_buffer_select_range(buffer,
6713 &sel_start, &sel_end);
6715 return FALSE; /* pass the event so that the right-click goes through */
6718 if (event->button == 2) {
6723 /* get the middle-click position to paste at the correct place */
6724 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6725 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6727 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6730 entry_paste_clipboard(compose, text,
6731 prefs_common.linewrap_pastes,
6732 GDK_SELECTION_PRIMARY, &iter);
6740 static void compose_spell_menu_changed(void *data)
6742 Compose *compose = (Compose *)data;
6744 GtkWidget *menuitem;
6745 GtkWidget *parent_item;
6746 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6749 if (compose->gtkaspell == NULL)
6752 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6753 "/Menu/Spelling/Options");
6755 /* setting the submenu removes /Spelling/Options from the factory
6756 * so we need to save it */
6758 if (parent_item == NULL) {
6759 parent_item = compose->aspell_options_menu;
6760 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6762 compose->aspell_options_menu = parent_item;
6764 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6766 spell_menu = g_slist_reverse(spell_menu);
6767 for (items = spell_menu;
6768 items; items = items->next) {
6769 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6770 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6771 gtk_widget_show(GTK_WIDGET(menuitem));
6773 g_slist_free(spell_menu);
6775 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6776 gtk_widget_show(parent_item);
6779 static void compose_dict_changed(void *data)
6781 Compose *compose = (Compose *) data;
6783 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6786 gtkaspell_highlight_all(compose->gtkaspell);
6787 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6791 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6793 Compose *compose = (Compose *)data;
6794 GdkEventButton event;
6797 event.time = gtk_get_current_event_time();
6801 return text_clicked(compose->text, &event, compose);
6804 static gboolean compose_force_window_origin = TRUE;
6805 static Compose *compose_create(PrefsAccount *account,
6814 GtkWidget *handlebox;
6816 GtkWidget *notebook;
6818 GtkWidget *attach_hbox;
6819 GtkWidget *attach_lab1;
6820 GtkWidget *attach_lab2;
6825 GtkWidget *subject_hbox;
6826 GtkWidget *subject_frame;
6827 GtkWidget *subject_entry;
6831 GtkWidget *edit_vbox;
6832 GtkWidget *ruler_hbox;
6834 GtkWidget *scrolledwin;
6836 GtkTextBuffer *buffer;
6837 GtkClipboard *clipboard;
6840 UndoMain *undostruct;
6842 gchar *titles[N_ATTACH_COLS];
6843 GtkWidget *popupmenu;
6844 GtkWidget *tmpl_menu;
6845 GtkActionGroup *action_group = NULL;
6848 GtkAspell * gtkaspell = NULL;
6851 static GdkGeometry geometry;
6853 cm_return_val_if_fail(account != NULL, NULL);
6855 debug_print("Creating compose window...\n");
6856 compose = g_new0(Compose, 1);
6858 titles[COL_MIMETYPE] = _("MIME type");
6859 titles[COL_SIZE] = _("Size");
6860 titles[COL_NAME] = _("Name");
6862 compose->batch = batch;
6863 compose->account = account;
6864 compose->folder = folder;
6866 compose->mutex = g_mutex_new();
6867 compose->set_cursor_pos = -1;
6869 #if !(GTK_CHECK_VERSION(2,12,0))
6870 compose->tooltips = tips;
6873 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6875 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6876 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6878 if (!geometry.max_width) {
6879 geometry.max_width = gdk_screen_width();
6880 geometry.max_height = gdk_screen_height();
6883 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6884 &geometry, GDK_HINT_MAX_SIZE);
6885 if (!geometry.min_width) {
6886 geometry.min_width = 600;
6887 geometry.min_height = 440;
6889 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6890 &geometry, GDK_HINT_MIN_SIZE);
6892 #ifndef GENERIC_UMPC
6893 if (compose_force_window_origin)
6894 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6895 prefs_common.compose_y);
6897 g_signal_connect(G_OBJECT(window), "delete_event",
6898 G_CALLBACK(compose_delete_cb), compose);
6899 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6900 gtk_widget_realize(window);
6902 gtkut_widget_set_composer_icon(window);
6904 vbox = gtk_vbox_new(FALSE, 0);
6905 gtk_container_add(GTK_CONTAINER(window), vbox);
6907 compose->ui_manager = gtk_ui_manager_new();
6908 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6909 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6910 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6911 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6912 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6913 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6914 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6915 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6916 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6917 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6925 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6930 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6957 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6967 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6973 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6988 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6996 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7001 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7002 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7003 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7004 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7005 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7007 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7009 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7010 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7011 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7014 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7015 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7016 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7017 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7019 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7023 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7025 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7026 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7030 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7031 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7033 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7034 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7036 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7037 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)
7038 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)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7043 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7044 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)
7045 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)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7050 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)
7051 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7053 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7054 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)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7060 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)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7063 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7065 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7066 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)
7067 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)
7068 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7069 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7071 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7072 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7073 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7074 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7075 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7077 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7078 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7079 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)
7081 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7082 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7083 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7087 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7088 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7089 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7090 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7091 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7092 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7095 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7097 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7098 gtk_widget_show_all(menubar);
7100 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7102 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7104 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7107 if (prefs_common.toolbar_detachable) {
7108 handlebox = gtk_handle_box_new();
7110 handlebox = gtk_hbox_new(FALSE, 0);
7112 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7114 gtk_widget_realize(handlebox);
7116 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7119 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7123 vbox2 = gtk_vbox_new(FALSE, 2);
7124 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7125 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7128 notebook = gtk_notebook_new();
7129 gtk_widget_set_size_request(notebook, -1, 130);
7130 gtk_widget_show(notebook);
7132 /* header labels and entries */
7133 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7134 compose_create_header(compose),
7135 gtk_label_new_with_mnemonic(_("Hea_der")));
7136 /* attachment list */
7137 attach_hbox = gtk_hbox_new(FALSE, 0);
7138 gtk_widget_show(attach_hbox);
7140 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7141 gtk_widget_show(attach_lab1);
7142 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7144 attach_lab2 = gtk_label_new("");
7145 gtk_widget_show(attach_lab2);
7146 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7148 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7149 compose_create_attach(compose),
7152 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7153 compose_create_others(compose),
7154 gtk_label_new_with_mnemonic(_("Othe_rs")));
7157 subject_hbox = gtk_hbox_new(FALSE, 0);
7158 gtk_widget_show(subject_hbox);
7160 subject_frame = gtk_frame_new(NULL);
7161 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7162 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7163 gtk_widget_show(subject_frame);
7165 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7166 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7167 gtk_widget_show(subject);
7169 label = gtk_label_new(_("Subject:"));
7170 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7171 gtk_widget_show(label);
7174 subject_entry = claws_spell_entry_new();
7176 subject_entry = gtk_entry_new();
7178 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7179 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7180 G_CALLBACK(compose_grab_focus_cb), compose);
7181 gtk_widget_show(subject_entry);
7182 compose->subject_entry = subject_entry;
7183 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7185 edit_vbox = gtk_vbox_new(FALSE, 0);
7187 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7190 ruler_hbox = gtk_hbox_new(FALSE, 0);
7191 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7193 ruler = gtk_shruler_new();
7194 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7195 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7199 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7200 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7201 GTK_POLICY_AUTOMATIC,
7202 GTK_POLICY_AUTOMATIC);
7203 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7205 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7206 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7208 text = gtk_text_view_new();
7209 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7210 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7211 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7212 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7213 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7215 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7217 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7218 G_CALLBACK(compose_edit_size_alloc),
7220 g_signal_connect(G_OBJECT(buffer), "changed",
7221 G_CALLBACK(compose_changed_cb), compose);
7222 g_signal_connect(G_OBJECT(text), "grab_focus",
7223 G_CALLBACK(compose_grab_focus_cb), compose);
7224 g_signal_connect(G_OBJECT(buffer), "insert_text",
7225 G_CALLBACK(text_inserted), compose);
7226 g_signal_connect(G_OBJECT(text), "button_press_event",
7227 G_CALLBACK(text_clicked), compose);
7229 g_signal_connect(G_OBJECT(text), "popup-menu",
7230 G_CALLBACK(compose_popup_menu), compose);
7232 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7233 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7234 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7235 G_CALLBACK(compose_popup_menu), compose);
7237 g_signal_connect(G_OBJECT(subject_entry), "changed",
7238 G_CALLBACK(compose_changed_cb), compose);
7241 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7242 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7243 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7244 g_signal_connect(G_OBJECT(text), "drag_data_received",
7245 G_CALLBACK(compose_insert_drag_received_cb),
7247 g_signal_connect(G_OBJECT(text), "drag-drop",
7248 G_CALLBACK(compose_drag_drop),
7250 gtk_widget_show_all(vbox);
7252 /* pane between attach clist and text */
7253 paned = gtk_vpaned_new();
7254 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7256 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7257 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7259 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7261 gtk_paned_add1(GTK_PANED(paned), notebook);
7262 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7263 gtk_widget_show_all(paned);
7266 if (prefs_common.textfont) {
7267 PangoFontDescription *font_desc;
7269 font_desc = pango_font_description_from_string
7270 (prefs_common.textfont);
7272 gtk_widget_modify_font(text, font_desc);
7273 pango_font_description_free(font_desc);
7277 gtk_action_group_add_actions(action_group, compose_popup_entries,
7278 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7280 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7286 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7288 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7289 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7290 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7292 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7294 undostruct = undo_init(text);
7295 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7298 address_completion_start(window);
7300 compose->window = window;
7301 compose->vbox = vbox;
7302 compose->menubar = menubar;
7303 compose->handlebox = handlebox;
7305 compose->vbox2 = vbox2;
7307 compose->paned = paned;
7309 compose->attach_label = attach_lab2;
7311 compose->notebook = notebook;
7312 compose->edit_vbox = edit_vbox;
7313 compose->ruler_hbox = ruler_hbox;
7314 compose->ruler = ruler;
7315 compose->scrolledwin = scrolledwin;
7316 compose->text = text;
7318 compose->focused_editable = NULL;
7320 compose->popupmenu = popupmenu;
7322 compose->tmpl_menu = tmpl_menu;
7324 compose->mode = mode;
7325 compose->rmode = mode;
7327 compose->targetinfo = NULL;
7328 compose->replyinfo = NULL;
7329 compose->fwdinfo = NULL;
7331 compose->replyto = NULL;
7333 compose->bcc = NULL;
7334 compose->followup_to = NULL;
7336 compose->ml_post = NULL;
7338 compose->inreplyto = NULL;
7339 compose->references = NULL;
7340 compose->msgid = NULL;
7341 compose->boundary = NULL;
7343 compose->autowrap = prefs_common.autowrap;
7344 compose->autoindent = prefs_common.auto_indent;
7345 compose->use_signing = FALSE;
7346 compose->use_encryption = FALSE;
7347 compose->privacy_system = NULL;
7349 compose->modified = FALSE;
7351 compose->return_receipt = FALSE;
7353 compose->to_list = NULL;
7354 compose->newsgroup_list = NULL;
7356 compose->undostruct = undostruct;
7358 compose->sig_str = NULL;
7360 compose->exteditor_file = NULL;
7361 compose->exteditor_pid = -1;
7362 compose->exteditor_tag = -1;
7363 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7366 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7367 if (mode != COMPOSE_REDIRECT) {
7368 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7369 strcmp(prefs_common.dictionary, "")) {
7370 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7371 prefs_common.alt_dictionary,
7372 conv_get_locale_charset_str(),
7373 prefs_common.misspelled_col,
7374 prefs_common.check_while_typing,
7375 prefs_common.recheck_when_changing_dict,
7376 prefs_common.use_alternate,
7377 prefs_common.use_both_dicts,
7378 GTK_TEXT_VIEW(text),
7379 GTK_WINDOW(compose->window),
7380 compose_dict_changed,
7381 compose_spell_menu_changed,
7384 alertpanel_error(_("Spell checker could not "
7386 gtkaspell_checkers_strerror());
7387 gtkaspell_checkers_reset_error();
7389 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7393 compose->gtkaspell = gtkaspell;
7394 compose_spell_menu_changed(compose);
7395 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7398 compose_select_account(compose, account, TRUE);
7400 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7401 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7403 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7404 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7406 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7407 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7409 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7410 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7413 if (account->protocol != A_NNTP)
7414 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7415 prefs_common_translated_header_name("To:"));
7417 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7418 prefs_common_translated_header_name("Newsgroups:"));
7420 addressbook_set_target_compose(compose);
7422 if (mode != COMPOSE_REDIRECT)
7423 compose_set_template_menu(compose);
7425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7428 compose_list = g_list_append(compose_list, compose);
7430 if (!prefs_common.show_ruler)
7431 gtk_widget_hide(ruler_hbox);
7433 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7436 compose->priority = PRIORITY_NORMAL;
7437 compose_update_priority_menu_item(compose);
7439 compose_set_out_encoding(compose);
7442 compose_update_actions_menu(compose);
7444 /* Privacy Systems menu */
7445 compose_update_privacy_systems_menu(compose);
7447 activate_privacy_system(compose, account, TRUE);
7448 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7450 gtk_widget_realize(window);
7452 gtk_widget_show(window);
7454 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7455 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7462 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7467 GtkWidget *optmenubox;
7470 GtkWidget *from_name = NULL;
7471 #if !(GTK_CHECK_VERSION(2,12,0))
7472 GtkTooltips *tips = compose->tooltips;
7475 gint num = 0, def_menu = 0;
7477 accounts = account_get_list();
7478 cm_return_val_if_fail(accounts != NULL, NULL);
7480 optmenubox = gtk_event_box_new();
7481 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7482 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7484 hbox = gtk_hbox_new(FALSE, 6);
7485 from_name = gtk_entry_new();
7487 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7488 G_CALLBACK(compose_grab_focus_cb), compose);
7490 for (; accounts != NULL; accounts = accounts->next, num++) {
7491 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7492 gchar *name, *from = NULL;
7494 if (ac == compose->account) def_menu = num;
7496 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7499 if (ac == compose->account) {
7500 if (ac->name && *ac->name) {
7502 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7503 from = g_strdup_printf("%s <%s>",
7505 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7507 from = g_strdup_printf("%s",
7509 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7512 COMBOBOX_ADD(menu, name, ac->account_id);
7517 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7519 g_signal_connect(G_OBJECT(optmenu), "changed",
7520 G_CALLBACK(account_activated),
7522 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7523 G_CALLBACK(compose_entry_popup_extend),
7526 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7527 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7529 CLAWS_SET_TIP(optmenubox,
7530 _("Account to use for this email"));
7531 CLAWS_SET_TIP(from_name,
7532 _("Sender address to be used"));
7534 compose->from_name = from_name;
7539 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7541 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7542 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7543 Compose *compose = (Compose *) data;
7545 compose->priority = value;
7549 static void compose_reply_change_mode(Compose *compose,
7552 gboolean was_modified = compose->modified;
7554 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7556 cm_return_if_fail(compose->replyinfo != NULL);
7558 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7560 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7562 if (action == COMPOSE_REPLY_TO_ALL)
7564 if (action == COMPOSE_REPLY_TO_SENDER)
7566 if (action == COMPOSE_REPLY_TO_LIST)
7569 compose_remove_header_entries(compose);
7570 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7571 if (compose->account->set_autocc && compose->account->auto_cc)
7572 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7574 if (compose->account->set_autobcc && compose->account->auto_bcc)
7575 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7577 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7578 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7579 compose_show_first_last_header(compose, TRUE);
7580 compose->modified = was_modified;
7581 compose_set_title(compose);
7584 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7586 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7587 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7588 Compose *compose = (Compose *) data;
7591 compose_reply_change_mode(compose, value);
7594 static void compose_update_priority_menu_item(Compose * compose)
7596 GtkWidget *menuitem = NULL;
7597 switch (compose->priority) {
7598 case PRIORITY_HIGHEST:
7599 menuitem = gtk_ui_manager_get_widget
7600 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7603 menuitem = gtk_ui_manager_get_widget
7604 (compose->ui_manager, "/Menu/Options/Priority/High");
7606 case PRIORITY_NORMAL:
7607 menuitem = gtk_ui_manager_get_widget
7608 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7611 menuitem = gtk_ui_manager_get_widget
7612 (compose->ui_manager, "/Menu/Options/Priority/Low");
7614 case PRIORITY_LOWEST:
7615 menuitem = gtk_ui_manager_get_widget
7616 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7619 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7622 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7624 Compose *compose = (Compose *) data;
7626 gboolean can_sign = FALSE, can_encrypt = FALSE;
7628 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7630 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7633 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7634 g_free(compose->privacy_system);
7635 compose->privacy_system = NULL;
7636 if (systemid != NULL) {
7637 compose->privacy_system = g_strdup(systemid);
7639 can_sign = privacy_system_can_sign(systemid);
7640 can_encrypt = privacy_system_can_encrypt(systemid);
7643 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7645 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7646 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7649 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7651 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7652 GtkWidget *menuitem = NULL;
7654 gboolean can_sign = FALSE, can_encrypt = FALSE;
7655 gboolean found = FALSE;
7657 if (compose->privacy_system != NULL) {
7659 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7660 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7661 cm_return_if_fail(menuitem != NULL);
7663 amenu = GTK_MENU_SHELL(menuitem)->children;
7665 while (amenu != NULL) {
7666 GList *alist = amenu->next;
7668 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7669 if (systemid != NULL) {
7670 if (strcmp(systemid, compose->privacy_system) == 0 &&
7671 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7672 menuitem = GTK_WIDGET(amenu->data);
7674 can_sign = privacy_system_can_sign(systemid);
7675 can_encrypt = privacy_system_can_encrypt(systemid);
7679 } else if (strlen(compose->privacy_system) == 0 &&
7680 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7681 menuitem = GTK_WIDGET(amenu->data);
7684 can_encrypt = FALSE;
7691 if (menuitem != NULL)
7692 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7694 if (warn && !found && strlen(compose->privacy_system)) {
7695 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7696 "will not be able to sign or encrypt this message."),
7697 compose->privacy_system);
7701 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7702 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7705 static void compose_set_out_encoding(Compose *compose)
7707 CharSet out_encoding;
7708 const gchar *branch = NULL;
7709 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7711 switch(out_encoding) {
7712 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7713 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7714 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7715 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7716 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7717 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7718 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7719 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7720 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7721 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7722 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7723 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7724 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7725 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7726 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7727 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7728 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7729 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7730 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7731 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7732 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7733 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7734 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7735 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7736 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7737 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7738 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7739 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7740 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7741 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7742 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7743 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7745 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7748 static void compose_set_template_menu(Compose *compose)
7750 GSList *tmpl_list, *cur;
7754 tmpl_list = template_get_config();
7756 menu = gtk_menu_new();
7758 gtk_menu_set_accel_group (GTK_MENU (menu),
7759 gtk_ui_manager_get_accel_group(compose->ui_manager));
7760 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7761 Template *tmpl = (Template *)cur->data;
7762 gchar *accel_path = NULL;
7763 item = gtk_menu_item_new_with_label(tmpl->name);
7764 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7765 g_signal_connect(G_OBJECT(item), "activate",
7766 G_CALLBACK(compose_template_activate_cb),
7768 g_object_set_data(G_OBJECT(item), "template", tmpl);
7769 gtk_widget_show(item);
7770 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7771 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7775 gtk_widget_show(menu);
7776 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7779 void compose_update_actions_menu(Compose *compose)
7781 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7784 static void compose_update_privacy_systems_menu(Compose *compose)
7786 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7787 GSList *systems, *cur;
7789 GtkWidget *system_none;
7791 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7792 GtkWidget *privacy_menu = gtk_menu_new();
7794 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7795 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7797 g_signal_connect(G_OBJECT(system_none), "activate",
7798 G_CALLBACK(compose_set_privacy_system_cb), compose);
7800 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7801 gtk_widget_show(system_none);
7803 systems = privacy_get_system_ids();
7804 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7805 gchar *systemid = cur->data;
7807 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7808 widget = gtk_radio_menu_item_new_with_label(group,
7809 privacy_system_get_name(systemid));
7810 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7811 g_strdup(systemid), g_free);
7812 g_signal_connect(G_OBJECT(widget), "activate",
7813 G_CALLBACK(compose_set_privacy_system_cb), compose);
7815 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7816 gtk_widget_show(widget);
7819 g_slist_free(systems);
7820 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7821 gtk_widget_show_all(privacy_menu);
7822 gtk_widget_show_all(privacy_menuitem);
7825 void compose_reflect_prefs_all(void)
7830 for (cur = compose_list; cur != NULL; cur = cur->next) {
7831 compose = (Compose *)cur->data;
7832 compose_set_template_menu(compose);
7836 void compose_reflect_prefs_pixmap_theme(void)
7841 for (cur = compose_list; cur != NULL; cur = cur->next) {
7842 compose = (Compose *)cur->data;
7843 toolbar_update(TOOLBAR_COMPOSE, compose);
7847 static const gchar *compose_quote_char_from_context(Compose *compose)
7849 const gchar *qmark = NULL;
7851 cm_return_val_if_fail(compose != NULL, NULL);
7853 switch (compose->mode) {
7854 /* use forward-specific quote char */
7855 case COMPOSE_FORWARD:
7856 case COMPOSE_FORWARD_AS_ATTACH:
7857 case COMPOSE_FORWARD_INLINE:
7858 if (compose->folder && compose->folder->prefs &&
7859 compose->folder->prefs->forward_with_format)
7860 qmark = compose->folder->prefs->forward_quotemark;
7861 else if (compose->account->forward_with_format)
7862 qmark = compose->account->forward_quotemark;
7864 qmark = prefs_common.fw_quotemark;
7867 /* use reply-specific quote char in all other modes */
7869 if (compose->folder && compose->folder->prefs &&
7870 compose->folder->prefs->reply_with_format)
7871 qmark = compose->folder->prefs->reply_quotemark;
7872 else if (compose->account->reply_with_format)
7873 qmark = compose->account->reply_quotemark;
7875 qmark = prefs_common.quotemark;
7879 if (qmark == NULL || *qmark == '\0')
7885 static void compose_template_apply(Compose *compose, Template *tmpl,
7889 GtkTextBuffer *buffer;
7893 gchar *parsed_str = NULL;
7894 gint cursor_pos = 0;
7895 const gchar *err_msg = _("Template body format error at line %d.");
7898 /* process the body */
7900 text = GTK_TEXT_VIEW(compose->text);
7901 buffer = gtk_text_view_get_buffer(text);
7904 qmark = compose_quote_char_from_context(compose);
7906 if (compose->replyinfo != NULL) {
7909 gtk_text_buffer_set_text(buffer, "", -1);
7910 mark = gtk_text_buffer_get_insert(buffer);
7911 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7913 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7914 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7916 } else if (compose->fwdinfo != NULL) {
7919 gtk_text_buffer_set_text(buffer, "", -1);
7920 mark = gtk_text_buffer_get_insert(buffer);
7921 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7923 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7924 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7927 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7929 GtkTextIter start, end;
7932 gtk_text_buffer_get_start_iter(buffer, &start);
7933 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7934 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7936 /* clear the buffer now */
7938 gtk_text_buffer_set_text(buffer, "", -1);
7940 parsed_str = compose_quote_fmt(compose, dummyinfo,
7941 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7942 procmsg_msginfo_free( dummyinfo );
7948 gtk_text_buffer_set_text(buffer, "", -1);
7949 mark = gtk_text_buffer_get_insert(buffer);
7950 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7953 if (replace && parsed_str && compose->account->auto_sig)
7954 compose_insert_sig(compose, FALSE);
7956 if (replace && parsed_str) {
7957 gtk_text_buffer_get_start_iter(buffer, &iter);
7958 gtk_text_buffer_place_cursor(buffer, &iter);
7962 cursor_pos = quote_fmt_get_cursor_pos();
7963 compose->set_cursor_pos = cursor_pos;
7964 if (cursor_pos == -1)
7966 gtk_text_buffer_get_start_iter(buffer, &iter);
7967 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7968 gtk_text_buffer_place_cursor(buffer, &iter);
7971 /* process the other fields */
7973 compose_template_apply_fields(compose, tmpl);
7974 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7975 quote_fmt_reset_vartable();
7976 compose_changed_cb(NULL, compose);
7979 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7981 MsgInfo* dummyinfo = NULL;
7982 MsgInfo *msginfo = NULL;
7985 if (compose->replyinfo != NULL)
7986 msginfo = compose->replyinfo;
7987 else if (compose->fwdinfo != NULL)
7988 msginfo = compose->fwdinfo;
7990 dummyinfo = compose_msginfo_new_from_compose(compose);
7991 msginfo = dummyinfo;
7994 if (tmpl->from && *tmpl->from != '\0') {
7996 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7997 compose->gtkaspell);
7999 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8001 quote_fmt_scan_string(tmpl->from);
8004 buf = quote_fmt_get_buffer();
8006 alertpanel_error(_("Template From format error."));
8008 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8012 if (tmpl->to && *tmpl->to != '\0') {
8014 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8015 compose->gtkaspell);
8017 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8019 quote_fmt_scan_string(tmpl->to);
8022 buf = quote_fmt_get_buffer();
8024 alertpanel_error(_("Template To format error."));
8026 compose_entry_append(compose, buf, COMPOSE_TO);
8030 if (tmpl->cc && *tmpl->cc != '\0') {
8032 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8033 compose->gtkaspell);
8035 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8037 quote_fmt_scan_string(tmpl->cc);
8040 buf = quote_fmt_get_buffer();
8042 alertpanel_error(_("Template Cc format error."));
8044 compose_entry_append(compose, buf, COMPOSE_CC);
8048 if (tmpl->bcc && *tmpl->bcc != '\0') {
8050 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8051 compose->gtkaspell);
8053 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8055 quote_fmt_scan_string(tmpl->bcc);
8058 buf = quote_fmt_get_buffer();
8060 alertpanel_error(_("Template Bcc format error."));
8062 compose_entry_append(compose, buf, COMPOSE_BCC);
8066 /* process the subject */
8067 if (tmpl->subject && *tmpl->subject != '\0') {
8069 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8070 compose->gtkaspell);
8072 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8074 quote_fmt_scan_string(tmpl->subject);
8077 buf = quote_fmt_get_buffer();
8079 alertpanel_error(_("Template subject format error."));
8081 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8085 procmsg_msginfo_free( dummyinfo );
8088 static void compose_destroy(Compose *compose)
8090 GtkTextBuffer *buffer;
8091 GtkClipboard *clipboard;
8093 compose_list = g_list_remove(compose_list, compose);
8095 if (compose->updating) {
8096 debug_print("danger, not destroying anything now\n");
8097 compose->deferred_destroy = TRUE;
8100 /* NOTE: address_completion_end() does nothing with the window
8101 * however this may change. */
8102 address_completion_end(compose->window);
8104 slist_free_strings(compose->to_list);
8105 g_slist_free(compose->to_list);
8106 slist_free_strings(compose->newsgroup_list);
8107 g_slist_free(compose->newsgroup_list);
8108 slist_free_strings(compose->header_list);
8109 g_slist_free(compose->header_list);
8111 procmsg_msginfo_free(compose->targetinfo);
8112 procmsg_msginfo_free(compose->replyinfo);
8113 procmsg_msginfo_free(compose->fwdinfo);
8115 g_free(compose->replyto);
8116 g_free(compose->cc);
8117 g_free(compose->bcc);
8118 g_free(compose->newsgroups);
8119 g_free(compose->followup_to);
8121 g_free(compose->ml_post);
8123 g_free(compose->inreplyto);
8124 g_free(compose->references);
8125 g_free(compose->msgid);
8126 g_free(compose->boundary);
8128 g_free(compose->redirect_filename);
8129 if (compose->undostruct)
8130 undo_destroy(compose->undostruct);
8132 g_free(compose->sig_str);
8134 g_free(compose->exteditor_file);
8136 g_free(compose->orig_charset);
8138 g_free(compose->privacy_system);
8140 if (addressbook_get_target_compose() == compose)
8141 addressbook_set_target_compose(NULL);
8144 if (compose->gtkaspell) {
8145 gtkaspell_delete(compose->gtkaspell);
8146 compose->gtkaspell = NULL;
8150 if (!compose->batch) {
8151 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8152 prefs_common.compose_height = compose->window->allocation.height;
8155 if (!gtk_widget_get_parent(compose->paned))
8156 gtk_widget_destroy(compose->paned);
8157 gtk_widget_destroy(compose->popupmenu);
8159 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8160 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8161 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8163 gtk_widget_destroy(compose->window);
8164 toolbar_destroy(compose->toolbar);
8165 g_free(compose->toolbar);
8166 g_mutex_free(compose->mutex);
8170 static void compose_attach_info_free(AttachInfo *ainfo)
8172 g_free(ainfo->file);
8173 g_free(ainfo->content_type);
8174 g_free(ainfo->name);
8178 static void compose_attach_update_label(Compose *compose)
8183 GtkTreeModel *model;
8188 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8189 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8190 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8194 while(gtk_tree_model_iter_next(model, &iter))
8197 text = g_strdup_printf("(%d)", i);
8198 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8202 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8204 Compose *compose = (Compose *)data;
8205 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8206 GtkTreeSelection *selection;
8208 GtkTreeModel *model;
8210 selection = gtk_tree_view_get_selection(tree_view);
8211 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8216 for (cur = sel; cur != NULL; cur = cur->next) {
8217 GtkTreePath *path = cur->data;
8218 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8221 gtk_tree_path_free(path);
8224 for (cur = sel; cur != NULL; cur = cur->next) {
8225 GtkTreeRowReference *ref = cur->data;
8226 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8229 if (gtk_tree_model_get_iter(model, &iter, path))
8230 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8232 gtk_tree_path_free(path);
8233 gtk_tree_row_reference_free(ref);
8237 compose_attach_update_label(compose);
8240 static struct _AttachProperty
8243 GtkWidget *mimetype_entry;
8244 GtkWidget *encoding_optmenu;
8245 GtkWidget *path_entry;
8246 GtkWidget *filename_entry;
8248 GtkWidget *cancel_btn;
8251 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8253 gtk_tree_path_free((GtkTreePath *)ptr);
8256 static void compose_attach_property(GtkAction *action, gpointer data)
8258 Compose *compose = (Compose *)data;
8259 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8261 GtkComboBox *optmenu;
8262 GtkTreeSelection *selection;
8264 GtkTreeModel *model;
8267 static gboolean cancelled;
8269 /* only if one selected */
8270 selection = gtk_tree_view_get_selection(tree_view);
8271 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8274 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8278 path = (GtkTreePath *) sel->data;
8279 gtk_tree_model_get_iter(model, &iter, path);
8280 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8283 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8289 if (!attach_prop.window)
8290 compose_attach_property_create(&cancelled);
8291 gtk_widget_grab_focus(attach_prop.ok_btn);
8292 gtk_widget_show(attach_prop.window);
8293 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8295 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8296 if (ainfo->encoding == ENC_UNKNOWN)
8297 combobox_select_by_data(optmenu, ENC_BASE64);
8299 combobox_select_by_data(optmenu, ainfo->encoding);
8301 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8302 ainfo->content_type ? ainfo->content_type : "");
8303 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8304 ainfo->file ? ainfo->file : "");
8305 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8306 ainfo->name ? ainfo->name : "");
8309 const gchar *entry_text;
8311 gchar *cnttype = NULL;
8318 gtk_widget_hide(attach_prop.window);
8323 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8324 if (*entry_text != '\0') {
8327 text = g_strstrip(g_strdup(entry_text));
8328 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8329 cnttype = g_strdup(text);
8332 alertpanel_error(_("Invalid MIME type."));
8338 ainfo->encoding = combobox_get_active_data(optmenu);
8340 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8341 if (*entry_text != '\0') {
8342 if (is_file_exist(entry_text) &&
8343 (size = get_file_size(entry_text)) > 0)
8344 file = g_strdup(entry_text);
8347 (_("File doesn't exist or is empty."));
8353 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8354 if (*entry_text != '\0') {
8355 g_free(ainfo->name);
8356 ainfo->name = g_strdup(entry_text);
8360 g_free(ainfo->content_type);
8361 ainfo->content_type = cnttype;
8364 g_free(ainfo->file);
8368 ainfo->size = (goffset)size;
8370 /* update tree store */
8371 text = to_human_readable(ainfo->size);
8372 gtk_tree_model_get_iter(model, &iter, path);
8373 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8374 COL_MIMETYPE, ainfo->content_type,
8376 COL_NAME, ainfo->name,
8382 gtk_tree_path_free(path);
8385 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8387 label = gtk_label_new(str); \
8388 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8389 GTK_FILL, 0, 0, 0); \
8390 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8392 entry = gtk_entry_new(); \
8393 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8394 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8397 static void compose_attach_property_create(gboolean *cancelled)
8403 GtkWidget *mimetype_entry;
8406 GtkListStore *optmenu_menu;
8407 GtkWidget *path_entry;
8408 GtkWidget *filename_entry;
8411 GtkWidget *cancel_btn;
8412 GList *mime_type_list, *strlist;
8415 debug_print("Creating attach_property window...\n");
8417 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8418 gtk_widget_set_size_request(window, 480, -1);
8419 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8420 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8421 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8422 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8423 g_signal_connect(G_OBJECT(window), "delete_event",
8424 G_CALLBACK(attach_property_delete_event),
8426 g_signal_connect(G_OBJECT(window), "key_press_event",
8427 G_CALLBACK(attach_property_key_pressed),
8430 vbox = gtk_vbox_new(FALSE, 8);
8431 gtk_container_add(GTK_CONTAINER(window), vbox);
8433 table = gtk_table_new(4, 2, FALSE);
8434 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8435 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8436 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8438 label = gtk_label_new(_("MIME type"));
8439 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8441 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8442 mimetype_entry = gtk_combo_box_entry_new_text();
8443 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8444 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8446 /* stuff with list */
8447 mime_type_list = procmime_get_mime_type_list();
8449 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8450 MimeType *type = (MimeType *) mime_type_list->data;
8453 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8455 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8458 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8459 (GCompareFunc)strcmp2);
8462 for (mime_type_list = strlist; mime_type_list != NULL;
8463 mime_type_list = mime_type_list->next) {
8464 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8465 g_free(mime_type_list->data);
8467 g_list_free(strlist);
8468 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8469 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8471 label = gtk_label_new(_("Encoding"));
8472 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8474 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8476 hbox = gtk_hbox_new(FALSE, 0);
8477 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8478 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8480 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8481 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8483 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8484 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8485 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8486 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8487 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8489 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8491 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8492 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8494 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8495 &ok_btn, GTK_STOCK_OK,
8497 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8498 gtk_widget_grab_default(ok_btn);
8500 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8501 G_CALLBACK(attach_property_ok),
8503 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8504 G_CALLBACK(attach_property_cancel),
8507 gtk_widget_show_all(vbox);
8509 attach_prop.window = window;
8510 attach_prop.mimetype_entry = mimetype_entry;
8511 attach_prop.encoding_optmenu = optmenu;
8512 attach_prop.path_entry = path_entry;
8513 attach_prop.filename_entry = filename_entry;
8514 attach_prop.ok_btn = ok_btn;
8515 attach_prop.cancel_btn = cancel_btn;
8518 #undef SET_LABEL_AND_ENTRY
8520 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8526 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8532 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8533 gboolean *cancelled)
8541 static gboolean attach_property_key_pressed(GtkWidget *widget,
8543 gboolean *cancelled)
8545 if (event && event->keyval == GDK_Escape) {
8549 if (event && event->keyval == GDK_Return) {
8556 static void compose_exec_ext_editor(Compose *compose)
8563 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8564 G_DIR_SEPARATOR, compose);
8566 if (pipe(pipe_fds) < 0) {
8572 if ((pid = fork()) < 0) {
8579 /* close the write side of the pipe */
8582 compose->exteditor_file = g_strdup(tmp);
8583 compose->exteditor_pid = pid;
8585 compose_set_ext_editor_sensitive(compose, FALSE);
8588 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8590 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8592 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8596 } else { /* process-monitoring process */
8602 /* close the read side of the pipe */
8605 if (compose_write_body_to_file(compose, tmp) < 0) {
8606 fd_write_all(pipe_fds[1], "2\n", 2);
8610 pid_ed = compose_exec_ext_editor_real(tmp);
8612 fd_write_all(pipe_fds[1], "1\n", 2);
8616 /* wait until editor is terminated */
8617 waitpid(pid_ed, NULL, 0);
8619 fd_write_all(pipe_fds[1], "0\n", 2);
8626 #endif /* G_OS_UNIX */
8630 static gint compose_exec_ext_editor_real(const gchar *file)
8637 cm_return_val_if_fail(file != NULL, -1);
8639 if ((pid = fork()) < 0) {
8644 if (pid != 0) return pid;
8646 /* grandchild process */
8648 if (setpgid(0, getppid()))
8651 if (prefs_common_get_ext_editor_cmd() &&
8652 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8653 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8654 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8656 if (prefs_common_get_ext_editor_cmd())
8657 g_warning("External editor command-line is invalid: '%s'\n",
8658 prefs_common_get_ext_editor_cmd());
8659 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8662 cmdline = strsplit_with_quote(buf, " ", 1024);
8663 execvp(cmdline[0], cmdline);
8666 g_strfreev(cmdline);
8671 static gboolean compose_ext_editor_kill(Compose *compose)
8673 pid_t pgid = compose->exteditor_pid * -1;
8676 ret = kill(pgid, 0);
8678 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8682 msg = g_strdup_printf
8683 (_("The external editor is still working.\n"
8684 "Force terminating the process?\n"
8685 "process group id: %d"), -pgid);
8686 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8687 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8691 if (val == G_ALERTALTERNATE) {
8692 g_source_remove(compose->exteditor_tag);
8693 g_io_channel_shutdown(compose->exteditor_ch,
8695 g_io_channel_unref(compose->exteditor_ch);
8697 if (kill(pgid, SIGTERM) < 0) perror("kill");
8698 waitpid(compose->exteditor_pid, NULL, 0);
8700 g_warning("Terminated process group id: %d", -pgid);
8701 g_warning("Temporary file: %s",
8702 compose->exteditor_file);
8704 compose_set_ext_editor_sensitive(compose, TRUE);
8706 g_free(compose->exteditor_file);
8707 compose->exteditor_file = NULL;
8708 compose->exteditor_pid = -1;
8709 compose->exteditor_ch = NULL;
8710 compose->exteditor_tag = -1;
8718 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8722 Compose *compose = (Compose *)data;
8725 debug_print(_("Compose: input from monitoring process\n"));
8727 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8729 g_io_channel_shutdown(source, FALSE, NULL);
8730 g_io_channel_unref(source);
8732 waitpid(compose->exteditor_pid, NULL, 0);
8734 if (buf[0] == '0') { /* success */
8735 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8736 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8738 gtk_text_buffer_set_text(buffer, "", -1);
8739 compose_insert_file(compose, compose->exteditor_file);
8740 compose_changed_cb(NULL, compose);
8741 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8743 if (claws_unlink(compose->exteditor_file) < 0)
8744 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8745 } else if (buf[0] == '1') { /* failed */
8746 g_warning("Couldn't exec external editor\n");
8747 if (claws_unlink(compose->exteditor_file) < 0)
8748 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8749 } else if (buf[0] == '2') {
8750 g_warning("Couldn't write to file\n");
8751 } else if (buf[0] == '3') {
8752 g_warning("Pipe read failed\n");
8755 compose_set_ext_editor_sensitive(compose, TRUE);
8757 g_free(compose->exteditor_file);
8758 compose->exteditor_file = NULL;
8759 compose->exteditor_pid = -1;
8760 compose->exteditor_ch = NULL;
8761 compose->exteditor_tag = -1;
8766 static void compose_set_ext_editor_sensitive(Compose *compose,
8769 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8770 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8771 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8772 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8773 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8774 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8775 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8777 gtk_widget_set_sensitive(compose->text, sensitive);
8778 if (compose->toolbar->send_btn)
8779 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8780 if (compose->toolbar->sendl_btn)
8781 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8782 if (compose->toolbar->draft_btn)
8783 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8784 if (compose->toolbar->insert_btn)
8785 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8786 if (compose->toolbar->sig_btn)
8787 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8788 if (compose->toolbar->exteditor_btn)
8789 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8790 if (compose->toolbar->linewrap_current_btn)
8791 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8792 if (compose->toolbar->linewrap_all_btn)
8793 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8795 #endif /* G_OS_UNIX */
8798 * compose_undo_state_changed:
8800 * Change the sensivity of the menuentries undo and redo
8802 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8803 gint redo_state, gpointer data)
8805 Compose *compose = (Compose *)data;
8807 switch (undo_state) {
8808 case UNDO_STATE_TRUE:
8809 if (!undostruct->undo_state) {
8810 undostruct->undo_state = TRUE;
8811 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8814 case UNDO_STATE_FALSE:
8815 if (undostruct->undo_state) {
8816 undostruct->undo_state = FALSE;
8817 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8820 case UNDO_STATE_UNCHANGED:
8822 case UNDO_STATE_REFRESH:
8823 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8826 g_warning("Undo state not recognized");
8830 switch (redo_state) {
8831 case UNDO_STATE_TRUE:
8832 if (!undostruct->redo_state) {
8833 undostruct->redo_state = TRUE;
8834 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8837 case UNDO_STATE_FALSE:
8838 if (undostruct->redo_state) {
8839 undostruct->redo_state = FALSE;
8840 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8843 case UNDO_STATE_UNCHANGED:
8845 case UNDO_STATE_REFRESH:
8846 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8849 g_warning("Redo state not recognized");
8854 /* callback functions */
8856 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8857 * includes "non-client" (windows-izm) in calculation, so this calculation
8858 * may not be accurate.
8860 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8861 GtkAllocation *allocation,
8862 GtkSHRuler *shruler)
8864 if (prefs_common.show_ruler) {
8865 gint char_width = 0, char_height = 0;
8866 gint line_width_in_chars;
8868 gtkut_get_font_size(GTK_WIDGET(widget),
8869 &char_width, &char_height);
8870 line_width_in_chars =
8871 (allocation->width - allocation->x) / char_width;
8873 /* got the maximum */
8874 gtk_ruler_set_range(GTK_RULER(shruler),
8875 0.0, line_width_in_chars, 0,
8876 /*line_width_in_chars*/ char_width);
8882 static void account_activated(GtkComboBox *optmenu, gpointer data)
8884 Compose *compose = (Compose *)data;
8887 gchar *folderidentifier;
8888 gint account_id = 0;
8892 /* Get ID of active account in the combo box */
8893 menu = gtk_combo_box_get_model(optmenu);
8894 gtk_combo_box_get_active_iter(optmenu, &iter);
8895 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8897 ac = account_find_from_id(account_id);
8898 cm_return_if_fail(ac != NULL);
8900 if (ac != compose->account)
8901 compose_select_account(compose, ac, FALSE);
8903 /* Set message save folder */
8904 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8905 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8907 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8908 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8910 compose_set_save_to(compose, NULL);
8911 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8912 folderidentifier = folder_item_get_identifier(account_get_special_folder
8913 (compose->account, F_OUTBOX));
8914 compose_set_save_to(compose, folderidentifier);
8915 g_free(folderidentifier);
8919 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8920 GtkTreeViewColumn *column, Compose *compose)
8922 compose_attach_property(NULL, compose);
8925 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8928 Compose *compose = (Compose *)data;
8929 GtkTreeSelection *attach_selection;
8930 gint attach_nr_selected;
8932 if (!event) return FALSE;
8934 if (event->button == 3) {
8935 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8936 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8938 if (attach_nr_selected > 0)
8940 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8941 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8943 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8944 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8947 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8948 NULL, NULL, event->button, event->time);
8955 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8958 Compose *compose = (Compose *)data;
8960 if (!event) return FALSE;
8962 switch (event->keyval) {
8964 compose_attach_remove_selected(NULL, compose);
8970 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8972 toolbar_comp_set_sensitive(compose, allow);
8973 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8974 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8976 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8978 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8979 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8980 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8982 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8986 static void compose_send_cb(GtkAction *action, gpointer data)
8988 Compose *compose = (Compose *)data;
8990 if (prefs_common.work_offline &&
8991 !inc_offline_should_override(TRUE,
8992 _("Claws Mail needs network access in order "
8993 "to send this email.")))
8996 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8997 g_source_remove(compose->draft_timeout_tag);
8998 compose->draft_timeout_tag = -1;
9001 compose_send(compose);
9004 static void compose_send_later_cb(GtkAction *action, gpointer data)
9006 Compose *compose = (Compose *)data;
9010 compose_allow_user_actions(compose, FALSE);
9011 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9012 compose_allow_user_actions(compose, TRUE);
9016 compose_close(compose);
9017 } else if (val == -1) {
9018 alertpanel_error(_("Could not queue message."));
9019 } else if (val == -2) {
9020 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9021 } else if (val == -3) {
9022 if (privacy_peek_error())
9023 alertpanel_error(_("Could not queue message for sending:\n\n"
9024 "Signature failed: %s"), privacy_get_error());
9025 } else if (val == -4) {
9026 alertpanel_error(_("Could not queue message for sending:\n\n"
9027 "Charset conversion failed."));
9028 } else if (val == -5) {
9029 alertpanel_error(_("Could not queue message for sending:\n\n"
9030 "Couldn't get recipient encryption key."));
9031 } else if (val == -6) {
9034 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9037 #define DRAFTED_AT_EXIT "drafted_at_exit"
9038 static void compose_register_draft(MsgInfo *info)
9040 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9041 DRAFTED_AT_EXIT, NULL);
9042 FILE *fp = g_fopen(filepath, "ab");
9045 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9053 gboolean compose_draft (gpointer data, guint action)
9055 Compose *compose = (Compose *)data;
9059 MsgFlags flag = {0, 0};
9060 static gboolean lock = FALSE;
9061 MsgInfo *newmsginfo;
9063 gboolean target_locked = FALSE;
9064 gboolean err = FALSE;
9066 if (lock) return FALSE;
9068 if (compose->sending)
9071 draft = account_get_special_folder(compose->account, F_DRAFT);
9072 cm_return_val_if_fail(draft != NULL, FALSE);
9074 if (!g_mutex_trylock(compose->mutex)) {
9075 /* we don't want to lock the mutex once it's available,
9076 * because as the only other part of compose.c locking
9077 * it is compose_close - which means once unlocked,
9078 * the compose struct will be freed */
9079 debug_print("couldn't lock mutex, probably sending\n");
9085 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9086 G_DIR_SEPARATOR, compose);
9087 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9088 FILE_OP_ERROR(tmp, "fopen");
9092 /* chmod for security */
9093 if (change_file_mode_rw(fp, tmp) < 0) {
9094 FILE_OP_ERROR(tmp, "chmod");
9095 g_warning("can't change file mode\n");
9098 /* Save draft infos */
9099 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9100 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9102 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9103 gchar *savefolderid;
9105 savefolderid = compose_get_save_to(compose);
9106 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9107 g_free(savefolderid);
9109 if (compose->return_receipt) {
9110 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9112 if (compose->privacy_system) {
9113 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9114 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9115 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9118 /* Message-ID of message replying to */
9119 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9122 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9123 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9126 /* Message-ID of message forwarding to */
9127 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9130 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9131 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9135 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9136 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9138 /* end of headers */
9139 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9146 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9150 if (fclose(fp) == EOF) {
9154 if (compose->targetinfo) {
9155 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9156 flag.perm_flags = target_locked?MSG_LOCKED:0;
9158 flag.tmp_flags = MSG_DRAFT;
9160 folder_item_scan(draft);
9161 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9162 MsgInfo *tmpinfo = NULL;
9163 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9164 if (compose->msgid) {
9165 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9168 msgnum = tmpinfo->msgnum;
9169 procmsg_msginfo_free(tmpinfo);
9170 debug_print("got draft msgnum %d from scanning\n", msgnum);
9172 debug_print("didn't get draft msgnum after scanning\n");
9175 debug_print("got draft msgnum %d from adding\n", msgnum);
9181 if (action != COMPOSE_AUTO_SAVE) {
9182 if (action != COMPOSE_DRAFT_FOR_EXIT)
9183 alertpanel_error(_("Could not save draft."));
9186 gtkut_window_popup(compose->window);
9187 val = alertpanel_full(_("Could not save draft"),
9188 _("Could not save draft.\n"
9189 "Do you want to cancel exit or discard this email?"),
9190 _("_Cancel exit"), _("_Discard email"), NULL,
9191 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9192 if (val == G_ALERTALTERNATE) {
9194 g_mutex_unlock(compose->mutex); /* must be done before closing */
9195 compose_close(compose);
9199 g_mutex_unlock(compose->mutex); /* must be done before closing */
9208 if (compose->mode == COMPOSE_REEDIT) {
9209 compose_remove_reedit_target(compose, TRUE);
9212 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9215 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9217 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9219 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9220 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9221 procmsg_msginfo_set_flags(newmsginfo, 0,
9222 MSG_HAS_ATTACHMENT);
9224 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9225 compose_register_draft(newmsginfo);
9227 procmsg_msginfo_free(newmsginfo);
9230 folder_item_scan(draft);
9232 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9234 g_mutex_unlock(compose->mutex); /* must be done before closing */
9235 compose_close(compose);
9241 path = folder_item_fetch_msg(draft, msgnum);
9243 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9246 if (g_stat(path, &s) < 0) {
9247 FILE_OP_ERROR(path, "stat");
9253 procmsg_msginfo_free(compose->targetinfo);
9254 compose->targetinfo = procmsg_msginfo_new();
9255 compose->targetinfo->msgnum = msgnum;
9256 compose->targetinfo->size = (goffset)s.st_size;
9257 compose->targetinfo->mtime = s.st_mtime;
9258 compose->targetinfo->folder = draft;
9260 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9261 compose->mode = COMPOSE_REEDIT;
9263 if (action == COMPOSE_AUTO_SAVE) {
9264 compose->autosaved_draft = compose->targetinfo;
9266 compose->modified = FALSE;
9267 compose_set_title(compose);
9271 g_mutex_unlock(compose->mutex);
9275 void compose_clear_exit_drafts(void)
9277 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9278 DRAFTED_AT_EXIT, NULL);
9279 if (is_file_exist(filepath))
9280 claws_unlink(filepath);
9285 void compose_reopen_exit_drafts(void)
9287 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9288 DRAFTED_AT_EXIT, NULL);
9289 FILE *fp = g_fopen(filepath, "rb");
9293 while (fgets(buf, sizeof(buf), fp)) {
9294 gchar **parts = g_strsplit(buf, "\t", 2);
9295 const gchar *folder = parts[0];
9296 int msgnum = parts[1] ? atoi(parts[1]):-1;
9298 if (folder && *folder && msgnum > -1) {
9299 FolderItem *item = folder_find_item_from_identifier(folder);
9300 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9302 compose_reedit(info, FALSE);
9309 compose_clear_exit_drafts();
9312 static void compose_save_cb(GtkAction *action, gpointer data)
9314 Compose *compose = (Compose *)data;
9315 compose_draft(compose, COMPOSE_KEEP_EDITING);
9316 compose->rmode = COMPOSE_REEDIT;
9319 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9321 if (compose && file_list) {
9324 for ( tmp = file_list; tmp; tmp = tmp->next) {
9325 gchar *file = (gchar *) tmp->data;
9326 gchar *utf8_filename = conv_filename_to_utf8(file);
9327 compose_attach_append(compose, file, utf8_filename, NULL);
9328 compose_changed_cb(NULL, compose);
9333 g_free(utf8_filename);
9338 static void compose_attach_cb(GtkAction *action, gpointer data)
9340 Compose *compose = (Compose *)data;
9343 if (compose->redirect_filename != NULL)
9346 file_list = filesel_select_multiple_files_open(_("Select file"));
9349 compose_attach_from_list(compose, file_list, TRUE);
9350 g_list_free(file_list);
9354 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9356 Compose *compose = (Compose *)data;
9359 file_list = filesel_select_multiple_files_open(_("Select file"));
9364 for ( tmp = file_list; tmp; tmp = tmp->next) {
9365 gchar *file = (gchar *) tmp->data;
9366 gchar *filedup = g_strdup(file);
9367 gchar *shortfile = g_path_get_basename(filedup);
9368 ComposeInsertResult res;
9370 res = compose_insert_file(compose, file);
9371 if (res == COMPOSE_INSERT_READ_ERROR) {
9372 alertpanel_error(_("File '%s' could not be read."), shortfile);
9373 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9374 alertpanel_error(_("File '%s' contained invalid characters\n"
9375 "for the current encoding, insertion may be incorrect."), shortfile);
9381 g_list_free(file_list);
9385 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9387 Compose *compose = (Compose *)data;
9389 compose_insert_sig(compose, FALSE);
9392 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9396 Compose *compose = (Compose *)data;
9398 gtkut_widget_get_uposition(widget, &x, &y);
9399 if (!compose->batch) {
9400 prefs_common.compose_x = x;
9401 prefs_common.compose_y = y;
9403 if (compose->sending || compose->updating)
9405 compose_close_cb(NULL, compose);
9409 void compose_close_toolbar(Compose *compose)
9411 compose_close_cb(NULL, compose);
9414 static void compose_close_cb(GtkAction *action, gpointer data)
9416 Compose *compose = (Compose *)data;
9420 if (compose->exteditor_tag != -1) {
9421 if (!compose_ext_editor_kill(compose))
9426 if (compose->modified) {
9427 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9428 if (!g_mutex_trylock(compose->mutex)) {
9429 /* we don't want to lock the mutex once it's available,
9430 * because as the only other part of compose.c locking
9431 * it is compose_close - which means once unlocked,
9432 * the compose struct will be freed */
9433 debug_print("couldn't lock mutex, probably sending\n");
9437 val = alertpanel(_("Discard message"),
9438 _("This message has been modified. Discard it?"),
9439 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9441 val = alertpanel(_("Save changes"),
9442 _("This message has been modified. Save the latest changes?"),
9443 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9445 g_mutex_unlock(compose->mutex);
9447 case G_ALERTDEFAULT:
9448 if (prefs_common.autosave && !reedit)
9449 compose_remove_draft(compose);
9451 case G_ALERTALTERNATE:
9452 compose_draft(data, COMPOSE_QUIT_EDITING);
9459 compose_close(compose);
9462 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9464 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9465 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9466 Compose *compose = (Compose *) data;
9469 compose->out_encoding = (CharSet)value;
9472 static void compose_address_cb(GtkAction *action, gpointer data)
9474 Compose *compose = (Compose *)data;
9476 addressbook_open(compose);
9479 static void about_show_cb(GtkAction *action, gpointer data)
9484 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9486 Compose *compose = (Compose *)data;
9491 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9492 cm_return_if_fail(tmpl != NULL);
9494 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9496 val = alertpanel(_("Apply template"), msg,
9497 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9500 if (val == G_ALERTDEFAULT)
9501 compose_template_apply(compose, tmpl, TRUE);
9502 else if (val == G_ALERTALTERNATE)
9503 compose_template_apply(compose, tmpl, FALSE);
9506 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9508 Compose *compose = (Compose *)data;
9510 compose_exec_ext_editor(compose);
9513 static void compose_undo_cb(GtkAction *action, gpointer data)
9515 Compose *compose = (Compose *)data;
9516 gboolean prev_autowrap = compose->autowrap;
9518 compose->autowrap = FALSE;
9519 undo_undo(compose->undostruct);
9520 compose->autowrap = prev_autowrap;
9523 static void compose_redo_cb(GtkAction *action, gpointer data)
9525 Compose *compose = (Compose *)data;
9526 gboolean prev_autowrap = compose->autowrap;
9528 compose->autowrap = FALSE;
9529 undo_redo(compose->undostruct);
9530 compose->autowrap = prev_autowrap;
9533 static void entry_cut_clipboard(GtkWidget *entry)
9535 if (GTK_IS_EDITABLE(entry))
9536 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9537 else if (GTK_IS_TEXT_VIEW(entry))
9538 gtk_text_buffer_cut_clipboard(
9539 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9540 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9544 static void entry_copy_clipboard(GtkWidget *entry)
9546 if (GTK_IS_EDITABLE(entry))
9547 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9548 else if (GTK_IS_TEXT_VIEW(entry))
9549 gtk_text_buffer_copy_clipboard(
9550 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9551 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9554 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9555 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9557 if (GTK_IS_TEXT_VIEW(entry)) {
9558 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9559 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9560 GtkTextIter start_iter, end_iter;
9562 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9564 if (contents == NULL)
9567 /* we shouldn't delete the selection when middle-click-pasting, or we
9568 * can't mid-click-paste our own selection */
9569 if (clip != GDK_SELECTION_PRIMARY) {
9570 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9571 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9574 if (insert_place == NULL) {
9575 /* if insert_place isn't specified, insert at the cursor.
9576 * used for Ctrl-V pasting */
9577 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9578 start = gtk_text_iter_get_offset(&start_iter);
9579 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9581 /* if insert_place is specified, paste here.
9582 * used for mid-click-pasting */
9583 start = gtk_text_iter_get_offset(insert_place);
9584 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9585 if (prefs_common.primary_paste_unselects)
9586 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9590 /* paste unwrapped: mark the paste so it's not wrapped later */
9591 end = start + strlen(contents);
9592 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9593 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9594 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9595 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9596 /* rewrap paragraph now (after a mid-click-paste) */
9597 mark_start = gtk_text_buffer_get_insert(buffer);
9598 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9599 gtk_text_iter_backward_char(&start_iter);
9600 compose_beautify_paragraph(compose, &start_iter, TRUE);
9602 } else if (GTK_IS_EDITABLE(entry))
9603 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9605 compose->modified = TRUE;
9608 static void entry_allsel(GtkWidget *entry)
9610 if (GTK_IS_EDITABLE(entry))
9611 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9612 else if (GTK_IS_TEXT_VIEW(entry)) {
9613 GtkTextIter startiter, enditer;
9614 GtkTextBuffer *textbuf;
9616 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9617 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9618 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9620 gtk_text_buffer_move_mark_by_name(textbuf,
9621 "selection_bound", &startiter);
9622 gtk_text_buffer_move_mark_by_name(textbuf,
9623 "insert", &enditer);
9627 static void compose_cut_cb(GtkAction *action, gpointer data)
9629 Compose *compose = (Compose *)data;
9630 if (compose->focused_editable
9631 #ifndef GENERIC_UMPC
9632 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9635 entry_cut_clipboard(compose->focused_editable);
9638 static void compose_copy_cb(GtkAction *action, gpointer data)
9640 Compose *compose = (Compose *)data;
9641 if (compose->focused_editable
9642 #ifndef GENERIC_UMPC
9643 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9646 entry_copy_clipboard(compose->focused_editable);
9649 static void compose_paste_cb(GtkAction *action, gpointer data)
9651 Compose *compose = (Compose *)data;
9653 GtkTextBuffer *buffer;
9655 if (compose->focused_editable &&
9656 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9657 entry_paste_clipboard(compose, compose->focused_editable,
9658 prefs_common.linewrap_pastes,
9659 GDK_SELECTION_CLIPBOARD, NULL);
9663 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9665 Compose *compose = (Compose *)data;
9666 gint wrap_quote = prefs_common.linewrap_quote;
9667 if (compose->focused_editable
9668 #ifndef GENERIC_UMPC
9669 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9672 /* let text_insert() (called directly or at a later time
9673 * after the gtk_editable_paste_clipboard) know that
9674 * text is to be inserted as a quotation. implemented
9675 * by using a simple refcount... */
9676 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9677 G_OBJECT(compose->focused_editable),
9678 "paste_as_quotation"));
9679 g_object_set_data(G_OBJECT(compose->focused_editable),
9680 "paste_as_quotation",
9681 GINT_TO_POINTER(paste_as_quotation + 1));
9682 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9683 entry_paste_clipboard(compose, compose->focused_editable,
9684 prefs_common.linewrap_pastes,
9685 GDK_SELECTION_CLIPBOARD, NULL);
9686 prefs_common.linewrap_quote = wrap_quote;
9690 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9692 Compose *compose = (Compose *)data;
9694 GtkTextBuffer *buffer;
9696 if (compose->focused_editable
9697 #ifndef GENERIC_UMPC
9698 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9701 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9702 GDK_SELECTION_CLIPBOARD, NULL);
9706 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9708 Compose *compose = (Compose *)data;
9710 GtkTextBuffer *buffer;
9712 if (compose->focused_editable
9713 #ifndef GENERIC_UMPC
9714 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9717 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9718 GDK_SELECTION_CLIPBOARD, NULL);
9722 static void compose_allsel_cb(GtkAction *action, gpointer data)
9724 Compose *compose = (Compose *)data;
9725 if (compose->focused_editable
9726 #ifndef GENERIC_UMPC
9727 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9730 entry_allsel(compose->focused_editable);
9733 static void textview_move_beginning_of_line (GtkTextView *text)
9735 GtkTextBuffer *buffer;
9739 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9741 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9742 mark = gtk_text_buffer_get_insert(buffer);
9743 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9744 gtk_text_iter_set_line_offset(&ins, 0);
9745 gtk_text_buffer_place_cursor(buffer, &ins);
9748 static void textview_move_forward_character (GtkTextView *text)
9750 GtkTextBuffer *buffer;
9754 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9756 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9757 mark = gtk_text_buffer_get_insert(buffer);
9758 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9759 if (gtk_text_iter_forward_cursor_position(&ins))
9760 gtk_text_buffer_place_cursor(buffer, &ins);
9763 static void textview_move_backward_character (GtkTextView *text)
9765 GtkTextBuffer *buffer;
9769 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9771 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9772 mark = gtk_text_buffer_get_insert(buffer);
9773 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9774 if (gtk_text_iter_backward_cursor_position(&ins))
9775 gtk_text_buffer_place_cursor(buffer, &ins);
9778 static void textview_move_forward_word (GtkTextView *text)
9780 GtkTextBuffer *buffer;
9785 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9787 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9788 mark = gtk_text_buffer_get_insert(buffer);
9789 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9790 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9791 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9792 gtk_text_iter_backward_word_start(&ins);
9793 gtk_text_buffer_place_cursor(buffer, &ins);
9797 static void textview_move_backward_word (GtkTextView *text)
9799 GtkTextBuffer *buffer;
9804 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9806 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9807 mark = gtk_text_buffer_get_insert(buffer);
9808 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9809 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9810 if (gtk_text_iter_backward_word_starts(&ins, 1))
9811 gtk_text_buffer_place_cursor(buffer, &ins);
9814 static void textview_move_end_of_line (GtkTextView *text)
9816 GtkTextBuffer *buffer;
9820 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9822 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9823 mark = gtk_text_buffer_get_insert(buffer);
9824 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9825 if (gtk_text_iter_forward_to_line_end(&ins))
9826 gtk_text_buffer_place_cursor(buffer, &ins);
9829 static void textview_move_next_line (GtkTextView *text)
9831 GtkTextBuffer *buffer;
9836 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9838 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9839 mark = gtk_text_buffer_get_insert(buffer);
9840 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9841 offset = gtk_text_iter_get_line_offset(&ins);
9842 if (gtk_text_iter_forward_line(&ins)) {
9843 gtk_text_iter_set_line_offset(&ins, offset);
9844 gtk_text_buffer_place_cursor(buffer, &ins);
9848 static void textview_move_previous_line (GtkTextView *text)
9850 GtkTextBuffer *buffer;
9855 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9857 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9858 mark = gtk_text_buffer_get_insert(buffer);
9859 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9860 offset = gtk_text_iter_get_line_offset(&ins);
9861 if (gtk_text_iter_backward_line(&ins)) {
9862 gtk_text_iter_set_line_offset(&ins, offset);
9863 gtk_text_buffer_place_cursor(buffer, &ins);
9867 static void textview_delete_forward_character (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_char(&end_iter)) {
9880 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9884 static void textview_delete_backward_character (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_char(&end_iter)) {
9897 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9901 static void textview_delete_forward_word (GtkTextView *text)
9903 GtkTextBuffer *buffer;
9905 GtkTextIter ins, 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);
9913 if (gtk_text_iter_forward_word_end(&end_iter)) {
9914 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9918 static void textview_delete_backward_word (GtkTextView *text)
9920 GtkTextBuffer *buffer;
9922 GtkTextIter ins, end_iter;
9924 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9926 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9927 mark = gtk_text_buffer_get_insert(buffer);
9928 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9930 if (gtk_text_iter_backward_word_start(&end_iter)) {
9931 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9935 static void textview_delete_line (GtkTextView *text)
9937 GtkTextBuffer *buffer;
9939 GtkTextIter ins, start_iter, end_iter;
9941 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9943 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9944 mark = gtk_text_buffer_get_insert(buffer);
9945 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9948 gtk_text_iter_set_line_offset(&start_iter, 0);
9951 if (gtk_text_iter_ends_line(&end_iter)){
9952 if (!gtk_text_iter_forward_char(&end_iter))
9953 gtk_text_iter_backward_char(&start_iter);
9956 gtk_text_iter_forward_to_line_end(&end_iter);
9957 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9960 static void textview_delete_to_line_end (GtkTextView *text)
9962 GtkTextBuffer *buffer;
9964 GtkTextIter ins, end_iter;
9966 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9968 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9969 mark = gtk_text_buffer_get_insert(buffer);
9970 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9972 if (gtk_text_iter_ends_line(&end_iter))
9973 gtk_text_iter_forward_char(&end_iter);
9975 gtk_text_iter_forward_to_line_end(&end_iter);
9976 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9979 #define DO_ACTION(name, act) { \
9980 if(!strcmp(name, a_name)) { \
9984 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9986 const gchar *a_name = gtk_action_get_name(action);
9987 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9988 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9989 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9990 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9991 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9992 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9993 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9994 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9995 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9996 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9997 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9998 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9999 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10000 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10004 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10006 Compose *compose = (Compose *)data;
10007 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10008 ComposeCallAdvancedAction action = -1;
10010 action = compose_call_advanced_action_from_path(gaction);
10013 void (*do_action) (GtkTextView *text);
10014 } action_table[] = {
10015 {textview_move_beginning_of_line},
10016 {textview_move_forward_character},
10017 {textview_move_backward_character},
10018 {textview_move_forward_word},
10019 {textview_move_backward_word},
10020 {textview_move_end_of_line},
10021 {textview_move_next_line},
10022 {textview_move_previous_line},
10023 {textview_delete_forward_character},
10024 {textview_delete_backward_character},
10025 {textview_delete_forward_word},
10026 {textview_delete_backward_word},
10027 {textview_delete_line},
10028 {textview_delete_to_line_end}
10031 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10033 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10034 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10035 if (action_table[action].do_action)
10036 action_table[action].do_action(text);
10038 g_warning("Not implemented yet.");
10042 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10046 if (GTK_IS_EDITABLE(widget)) {
10047 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10048 gtk_editable_set_position(GTK_EDITABLE(widget),
10051 if (widget->parent && widget->parent->parent
10052 && widget->parent->parent->parent) {
10053 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10054 gint y = widget->allocation.y;
10055 gint height = widget->allocation.height;
10056 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10057 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10059 if (y < (int)shown->value) {
10060 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10062 if (y + height > (int)shown->value + (int)shown->page_size) {
10063 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10064 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10065 y + height - (int)shown->page_size - 1);
10067 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10068 (int)shown->upper - (int)shown->page_size - 1);
10075 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10076 compose->focused_editable = widget;
10078 #ifdef GENERIC_UMPC
10079 if (GTK_IS_TEXT_VIEW(widget)
10080 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10081 g_object_ref(compose->notebook);
10082 g_object_ref(compose->edit_vbox);
10083 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10084 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10085 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10086 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10087 g_object_unref(compose->notebook);
10088 g_object_unref(compose->edit_vbox);
10089 g_signal_handlers_block_by_func(G_OBJECT(widget),
10090 G_CALLBACK(compose_grab_focus_cb),
10092 gtk_widget_grab_focus(widget);
10093 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10094 G_CALLBACK(compose_grab_focus_cb),
10096 } else if (!GTK_IS_TEXT_VIEW(widget)
10097 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10098 g_object_ref(compose->notebook);
10099 g_object_ref(compose->edit_vbox);
10100 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10101 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10102 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10103 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10104 g_object_unref(compose->notebook);
10105 g_object_unref(compose->edit_vbox);
10106 g_signal_handlers_block_by_func(G_OBJECT(widget),
10107 G_CALLBACK(compose_grab_focus_cb),
10109 gtk_widget_grab_focus(widget);
10110 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10111 G_CALLBACK(compose_grab_focus_cb),
10117 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10119 compose->modified = TRUE;
10120 // compose_beautify_paragraph(compose, NULL, TRUE);
10121 #ifndef GENERIC_UMPC
10122 compose_set_title(compose);
10126 static void compose_wrap_cb(GtkAction *action, gpointer data)
10128 Compose *compose = (Compose *)data;
10129 compose_beautify_paragraph(compose, NULL, TRUE);
10132 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10134 Compose *compose = (Compose *)data;
10135 compose_wrap_all_full(compose, TRUE);
10138 static void compose_find_cb(GtkAction *action, gpointer data)
10140 Compose *compose = (Compose *)data;
10142 message_search_compose(compose);
10145 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10148 Compose *compose = (Compose *)data;
10149 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10150 if (compose->autowrap)
10151 compose_wrap_all_full(compose, TRUE);
10152 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10155 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10158 Compose *compose = (Compose *)data;
10159 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10162 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10164 Compose *compose = (Compose *)data;
10166 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10169 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10171 Compose *compose = (Compose *)data;
10173 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10176 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10178 g_free(compose->privacy_system);
10180 compose->privacy_system = g_strdup(account->default_privacy_system);
10181 compose_update_privacy_system_menu_item(compose, warn);
10184 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10186 Compose *compose = (Compose *)data;
10188 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10189 gtk_widget_show(compose->ruler_hbox);
10190 prefs_common.show_ruler = TRUE;
10192 gtk_widget_hide(compose->ruler_hbox);
10193 gtk_widget_queue_resize(compose->edit_vbox);
10194 prefs_common.show_ruler = FALSE;
10198 static void compose_attach_drag_received_cb (GtkWidget *widget,
10199 GdkDragContext *context,
10202 GtkSelectionData *data,
10205 gpointer user_data)
10207 Compose *compose = (Compose *)user_data;
10210 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10212 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10214 ) && gtk_drag_get_source_widget(context) !=
10215 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10216 list = uri_list_extract_filenames((const gchar *)data->data);
10217 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10218 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10219 compose_attach_append
10220 (compose, (const gchar *)tmp->data,
10221 utf8_filename, NULL);
10222 g_free(utf8_filename);
10224 if (list) compose_changed_cb(NULL, compose);
10225 list_free_strings(list);
10227 } else if (gtk_drag_get_source_widget(context)
10228 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10229 /* comes from our summaryview */
10230 SummaryView * summaryview = NULL;
10231 GSList * list = NULL, *cur = NULL;
10233 if (mainwindow_get_mainwindow())
10234 summaryview = mainwindow_get_mainwindow()->summaryview;
10237 list = summary_get_selected_msg_list(summaryview);
10239 for (cur = list; cur; cur = cur->next) {
10240 MsgInfo *msginfo = (MsgInfo *)cur->data;
10241 gchar *file = NULL;
10243 file = procmsg_get_message_file_full(msginfo,
10246 compose_attach_append(compose, (const gchar *)file,
10247 (const gchar *)file, "message/rfc822");
10251 g_slist_free(list);
10255 static gboolean compose_drag_drop(GtkWidget *widget,
10256 GdkDragContext *drag_context,
10258 guint time, gpointer user_data)
10260 /* not handling this signal makes compose_insert_drag_received_cb
10265 static void compose_insert_drag_received_cb (GtkWidget *widget,
10266 GdkDragContext *drag_context,
10269 GtkSelectionData *data,
10272 gpointer user_data)
10274 Compose *compose = (Compose *)user_data;
10277 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10280 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10282 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10284 AlertValue val = G_ALERTDEFAULT;
10286 list = uri_list_extract_filenames((const gchar *)data->data);
10287 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10288 /* Assume a list of no files, and data has ://, is a remote link */
10289 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10290 gchar *tmpfile = get_tmp_file();
10291 str_write_to_file(tmpdata, tmpfile);
10293 compose_insert_file(compose, tmpfile);
10294 claws_unlink(tmpfile);
10296 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10297 compose_beautify_paragraph(compose, NULL, TRUE);
10300 switch (prefs_common.compose_dnd_mode) {
10301 case COMPOSE_DND_ASK:
10302 val = alertpanel_full(_("Insert or attach?"),
10303 _("Do you want to insert the contents of the file(s) "
10304 "into the message body, or attach it to the email?"),
10305 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10306 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10308 case COMPOSE_DND_INSERT:
10309 val = G_ALERTALTERNATE;
10311 case COMPOSE_DND_ATTACH:
10312 val = G_ALERTOTHER;
10315 /* unexpected case */
10316 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10319 if (val & G_ALERTDISABLE) {
10320 val &= ~G_ALERTDISABLE;
10321 /* remember what action to perform by default, only if we don't click Cancel */
10322 if (val == G_ALERTALTERNATE)
10323 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10324 else if (val == G_ALERTOTHER)
10325 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10328 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10329 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10330 list_free_strings(list);
10333 } else if (val == G_ALERTOTHER) {
10334 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10335 list_free_strings(list);
10340 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10341 compose_insert_file(compose, (const gchar *)tmp->data);
10343 list_free_strings(list);
10345 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10348 #if GTK_CHECK_VERSION(2, 8, 0)
10349 /* do nothing, handled by GTK */
10351 gchar *tmpfile = get_tmp_file();
10352 str_write_to_file((const gchar *)data->data, tmpfile);
10353 compose_insert_file(compose, tmpfile);
10354 claws_unlink(tmpfile);
10356 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10360 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10363 static void compose_header_drag_received_cb (GtkWidget *widget,
10364 GdkDragContext *drag_context,
10367 GtkSelectionData *data,
10370 gpointer user_data)
10372 GtkEditable *entry = (GtkEditable *)user_data;
10373 gchar *email = (gchar *)data->data;
10375 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10378 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10379 gchar *decoded=g_new(gchar, strlen(email));
10382 email += strlen("mailto:");
10383 decode_uri(decoded, email); /* will fit */
10384 gtk_editable_delete_text(entry, 0, -1);
10385 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10386 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10390 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10393 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10395 Compose *compose = (Compose *)data;
10397 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10398 compose->return_receipt = TRUE;
10400 compose->return_receipt = FALSE;
10403 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10405 Compose *compose = (Compose *)data;
10407 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10408 compose->remove_references = TRUE;
10410 compose->remove_references = FALSE;
10413 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10414 ComposeHeaderEntry *headerentry)
10416 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10420 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10421 GdkEventKey *event,
10422 ComposeHeaderEntry *headerentry)
10424 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10425 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10426 !(event->state & GDK_MODIFIER_MASK) &&
10427 (event->keyval == GDK_BackSpace) &&
10428 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10429 gtk_container_remove
10430 (GTK_CONTAINER(headerentry->compose->header_table),
10431 headerentry->combo);
10432 gtk_container_remove
10433 (GTK_CONTAINER(headerentry->compose->header_table),
10434 headerentry->entry);
10435 headerentry->compose->header_list =
10436 g_slist_remove(headerentry->compose->header_list,
10438 g_free(headerentry);
10439 } else if (event->keyval == GDK_Tab) {
10440 if (headerentry->compose->header_last == headerentry) {
10441 /* Override default next focus, and give it to subject_entry
10442 * instead of notebook tabs
10444 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10445 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10452 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10453 ComposeHeaderEntry *headerentry)
10455 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10456 compose_create_header_entry(headerentry->compose);
10457 g_signal_handlers_disconnect_matched
10458 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10459 0, 0, NULL, NULL, headerentry);
10461 /* Automatically scroll down */
10462 compose_show_first_last_header(headerentry->compose, FALSE);
10468 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10470 GtkAdjustment *vadj;
10472 cm_return_if_fail(compose);
10473 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10474 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10476 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10477 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10478 gtk_adjustment_changed(vadj);
10481 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10482 const gchar *text, gint len, Compose *compose)
10484 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10485 (G_OBJECT(compose->text), "paste_as_quotation"));
10488 cm_return_if_fail(text != NULL);
10490 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10491 G_CALLBACK(text_inserted),
10493 if (paste_as_quotation) {
10495 const gchar *qmark;
10497 GtkTextIter start_iter;
10500 len = strlen(text);
10502 new_text = g_strndup(text, len);
10504 qmark = compose_quote_char_from_context(compose);
10506 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10507 gtk_text_buffer_place_cursor(buffer, iter);
10509 pos = gtk_text_iter_get_offset(iter);
10511 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10512 _("Quote format error at line %d."));
10513 quote_fmt_reset_vartable();
10515 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10516 GINT_TO_POINTER(paste_as_quotation - 1));
10518 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10519 gtk_text_buffer_place_cursor(buffer, iter);
10520 gtk_text_buffer_delete_mark(buffer, mark);
10522 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10523 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10524 compose_beautify_paragraph(compose, &start_iter, FALSE);
10525 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10526 gtk_text_buffer_delete_mark(buffer, mark);
10528 if (strcmp(text, "\n") || compose->automatic_break
10529 || gtk_text_iter_starts_line(iter)) {
10530 GtkTextIter before_ins;
10531 gtk_text_buffer_insert(buffer, iter, text, len);
10532 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10533 before_ins = *iter;
10534 gtk_text_iter_backward_chars(&before_ins, len);
10535 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10538 /* check if the preceding is just whitespace or quote */
10539 GtkTextIter start_line;
10540 gchar *tmp = NULL, *quote = NULL;
10541 gint quote_len = 0, is_normal = 0;
10542 start_line = *iter;
10543 gtk_text_iter_set_line_offset(&start_line, 0);
10544 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10547 if (*tmp == '\0') {
10550 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10558 gtk_text_buffer_insert(buffer, iter, text, len);
10560 gtk_text_buffer_insert_with_tags_by_name(buffer,
10561 iter, text, len, "no_join", NULL);
10566 if (!paste_as_quotation) {
10567 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10568 compose_beautify_paragraph(compose, iter, FALSE);
10569 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10570 gtk_text_buffer_delete_mark(buffer, mark);
10573 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10574 G_CALLBACK(text_inserted),
10576 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10578 if (prefs_common.autosave &&
10579 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10580 compose->draft_timeout_tag != -2 /* disabled while loading */)
10581 compose->draft_timeout_tag = g_timeout_add
10582 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10584 static gint compose_defer_auto_save_draft(Compose *compose)
10586 compose->draft_timeout_tag = -1;
10587 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10592 static void compose_check_all(GtkAction *action, gpointer data)
10594 Compose *compose = (Compose *)data;
10595 if (!compose->gtkaspell)
10598 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10599 claws_spell_entry_check_all(
10600 CLAWS_SPELL_ENTRY(compose->subject_entry));
10602 gtkaspell_check_all(compose->gtkaspell);
10605 static void compose_highlight_all(GtkAction *action, gpointer data)
10607 Compose *compose = (Compose *)data;
10608 if (compose->gtkaspell) {
10609 claws_spell_entry_recheck_all(
10610 CLAWS_SPELL_ENTRY(compose->subject_entry));
10611 gtkaspell_highlight_all(compose->gtkaspell);
10615 static void compose_check_backwards(GtkAction *action, gpointer data)
10617 Compose *compose = (Compose *)data;
10618 if (!compose->gtkaspell) {
10619 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10623 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10624 claws_spell_entry_check_backwards(
10625 CLAWS_SPELL_ENTRY(compose->subject_entry));
10627 gtkaspell_check_backwards(compose->gtkaspell);
10630 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10632 Compose *compose = (Compose *)data;
10633 if (!compose->gtkaspell) {
10634 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10638 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10639 claws_spell_entry_check_forwards_go(
10640 CLAWS_SPELL_ENTRY(compose->subject_entry));
10642 gtkaspell_check_forwards_go(compose->gtkaspell);
10647 *\brief Guess originating forward account from MsgInfo and several
10648 * "common preference" settings. Return NULL if no guess.
10650 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10652 PrefsAccount *account = NULL;
10654 cm_return_val_if_fail(msginfo, NULL);
10655 cm_return_val_if_fail(msginfo->folder, NULL);
10656 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10658 if (msginfo->folder->prefs->enable_default_account)
10659 account = account_find_from_id(msginfo->folder->prefs->default_account);
10662 account = msginfo->folder->folder->account;
10664 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10666 Xstrdup_a(to, msginfo->to, return NULL);
10667 extract_address(to);
10668 account = account_find_from_address(to, FALSE);
10671 if (!account && prefs_common.forward_account_autosel) {
10672 gchar cc[BUFFSIZE];
10673 if (!procheader_get_header_from_msginfo
10674 (msginfo, cc,sizeof cc , "Cc:")) {
10675 gchar *buf = cc + strlen("Cc:");
10676 extract_address(buf);
10677 account = account_find_from_address(buf, FALSE);
10681 if (!account && prefs_common.forward_account_autosel) {
10682 gchar deliveredto[BUFFSIZE];
10683 if (!procheader_get_header_from_msginfo
10684 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10685 gchar *buf = deliveredto + strlen("Delivered-To:");
10686 extract_address(buf);
10687 account = account_find_from_address(buf, FALSE);
10694 gboolean compose_close(Compose *compose)
10698 if (!g_mutex_trylock(compose->mutex)) {
10699 /* we have to wait for the (possibly deferred by auto-save)
10700 * drafting to be done, before destroying the compose under
10702 debug_print("waiting for drafting to finish...\n");
10703 compose_allow_user_actions(compose, FALSE);
10704 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10707 cm_return_val_if_fail(compose, FALSE);
10708 gtkut_widget_get_uposition(compose->window, &x, &y);
10709 if (!compose->batch) {
10710 prefs_common.compose_x = x;
10711 prefs_common.compose_y = y;
10713 g_mutex_unlock(compose->mutex);
10714 compose_destroy(compose);
10719 * Add entry field for each address in list.
10720 * \param compose E-Mail composition object.
10721 * \param listAddress List of (formatted) E-Mail addresses.
10723 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10726 node = listAddress;
10728 addr = ( gchar * ) node->data;
10729 compose_entry_append( compose, addr, COMPOSE_TO );
10730 node = g_list_next( node );
10734 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10735 guint action, gboolean opening_multiple)
10737 gchar *body = NULL;
10738 GSList *new_msglist = NULL;
10739 MsgInfo *tmp_msginfo = NULL;
10740 gboolean originally_enc = FALSE;
10741 gboolean originally_sig = FALSE;
10742 Compose *compose = NULL;
10743 gchar *s_system = NULL;
10745 cm_return_if_fail(msgview != NULL);
10747 cm_return_if_fail(msginfo_list != NULL);
10749 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10750 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10751 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10753 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10754 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10755 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10756 orig_msginfo, mimeinfo);
10757 if (tmp_msginfo != NULL) {
10758 new_msglist = g_slist_append(NULL, tmp_msginfo);
10760 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10761 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10762 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10764 tmp_msginfo->folder = orig_msginfo->folder;
10765 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10766 if (orig_msginfo->tags) {
10767 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10768 tmp_msginfo->folder->tags_dirty = TRUE;
10774 if (!opening_multiple)
10775 body = messageview_get_selection(msgview);
10778 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10779 procmsg_msginfo_free(tmp_msginfo);
10780 g_slist_free(new_msglist);
10782 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10784 if (compose && originally_enc) {
10785 compose_force_encryption(compose, compose->account, FALSE, s_system);
10788 if (compose && originally_sig && compose->account->default_sign_reply) {
10789 compose_force_signing(compose, compose->account, s_system);
10795 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10798 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10799 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10800 GSList *cur = msginfo_list;
10801 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10802 "messages. Opening the windows "
10803 "could take some time. Do you "
10804 "want to continue?"),
10805 g_slist_length(msginfo_list));
10806 if (g_slist_length(msginfo_list) > 9
10807 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10808 != G_ALERTALTERNATE) {
10813 /* We'll open multiple compose windows */
10814 /* let the WM place the next windows */
10815 compose_force_window_origin = FALSE;
10816 for (; cur; cur = cur->next) {
10818 tmplist.data = cur->data;
10819 tmplist.next = NULL;
10820 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10822 compose_force_window_origin = TRUE;
10824 /* forwarding multiple mails as attachments is done via a
10825 * single compose window */
10826 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10830 void compose_set_position(Compose *compose, gint pos)
10832 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10834 gtkut_text_view_set_position(text, pos);
10837 gboolean compose_search_string(Compose *compose,
10838 const gchar *str, gboolean case_sens)
10840 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10842 return gtkut_text_view_search_string(text, str, case_sens);
10845 gboolean compose_search_string_backward(Compose *compose,
10846 const gchar *str, gboolean case_sens)
10848 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10850 return gtkut_text_view_search_string_backward(text, str, case_sens);
10853 /* allocate a msginfo structure and populate its data from a compose data structure */
10854 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10856 MsgInfo *newmsginfo;
10858 gchar buf[BUFFSIZE];
10860 cm_return_val_if_fail( compose != NULL, NULL );
10862 newmsginfo = procmsg_msginfo_new();
10865 get_rfc822_date(buf, sizeof(buf));
10866 newmsginfo->date = g_strdup(buf);
10869 if (compose->from_name) {
10870 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10871 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10875 if (compose->subject_entry)
10876 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10878 /* to, cc, reply-to, newsgroups */
10879 for (list = compose->header_list; list; list = list->next) {
10880 gchar *header = gtk_editable_get_chars(
10882 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10883 gchar *entry = gtk_editable_get_chars(
10884 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10886 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10887 if ( newmsginfo->to == NULL ) {
10888 newmsginfo->to = g_strdup(entry);
10889 } else if (entry && *entry) {
10890 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10891 g_free(newmsginfo->to);
10892 newmsginfo->to = tmp;
10895 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10896 if ( newmsginfo->cc == NULL ) {
10897 newmsginfo->cc = g_strdup(entry);
10898 } else if (entry && *entry) {
10899 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10900 g_free(newmsginfo->cc);
10901 newmsginfo->cc = tmp;
10904 if ( strcasecmp(header,
10905 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10906 if ( newmsginfo->newsgroups == NULL ) {
10907 newmsginfo->newsgroups = g_strdup(entry);
10908 } else if (entry && *entry) {
10909 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10910 g_free(newmsginfo->newsgroups);
10911 newmsginfo->newsgroups = tmp;
10919 /* other data is unset */
10925 /* update compose's dictionaries from folder dict settings */
10926 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10927 FolderItem *folder_item)
10929 cm_return_if_fail(compose != NULL);
10931 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10932 FolderItemPrefs *prefs = folder_item->prefs;
10934 if (prefs->enable_default_dictionary)
10935 gtkaspell_change_dict(compose->gtkaspell,
10936 prefs->default_dictionary, FALSE);
10937 if (folder_item->prefs->enable_default_alt_dictionary)
10938 gtkaspell_change_alt_dict(compose->gtkaspell,
10939 prefs->default_alt_dictionary);
10940 if (prefs->enable_default_dictionary
10941 || prefs->enable_default_alt_dictionary)
10942 compose_spell_menu_changed(compose);