2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
165 #define B64_LINE_SIZE 57
166 #define B64_BUFFSIZE 77
168 #define MAX_REFERENCES_LEN 999
170 static GList *compose_list = NULL;
172 static Compose *compose_generic_new (PrefsAccount *account,
175 GPtrArray *attach_files,
176 GList *listAddress );
178 static Compose *compose_create (PrefsAccount *account,
183 static void compose_entry_mark_default_to (Compose *compose,
184 const gchar *address);
185 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
186 ComposeQuoteMode quote_mode,
190 static Compose *compose_forward_multiple (PrefsAccount *account,
191 GSList *msginfo_list);
192 static Compose *compose_reply (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
198 static Compose *compose_reply_mode (ComposeMode mode,
199 GSList *msginfo_list,
201 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
202 static void compose_update_privacy_systems_menu(Compose *compose);
204 static GtkWidget *compose_account_option_menu_create
206 static void compose_set_out_encoding (Compose *compose);
207 static void compose_set_template_menu (Compose *compose);
208 static void compose_template_apply (Compose *compose,
211 static void compose_destroy (Compose *compose);
213 static void compose_entries_set (Compose *compose,
215 ComposeEntryType to_type);
216 static gint compose_parse_header (Compose *compose,
218 static gchar *compose_parse_references (const gchar *ref,
221 static gchar *compose_quote_fmt (Compose *compose,
227 gboolean need_unescape,
228 const gchar *err_msg);
230 static void compose_reply_set_entry (Compose *compose,
236 followup_and_reply_to);
237 static void compose_reedit_set_entry (Compose *compose,
240 static void compose_insert_sig (Compose *compose,
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static void compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
337 static void compose_dict_changed (void *data);
339 static void compose_add_field_list ( Compose *compose,
340 GList *listAddress );
342 /* callback functions */
344 static gboolean compose_edit_size_alloc (GtkEditable *widget,
345 GtkAllocation *allocation,
346 GtkSHRuler *shruler);
347 static void account_activated (GtkComboBox *optmenu,
349 static void attach_selected (GtkTreeView *tree_view,
350 GtkTreePath *tree_path,
351 GtkTreeViewColumn *column,
353 static gboolean attach_button_pressed (GtkWidget *widget,
354 GdkEventButton *event,
356 static gboolean attach_key_pressed (GtkWidget *widget,
359 static void compose_send_cb (GtkAction *action, gpointer data);
360 static void compose_send_later_cb (GtkAction *action, gpointer data);
362 static void compose_save_cb (GtkAction *action,
365 static void compose_attach_cb (GtkAction *action,
367 static void compose_insert_file_cb (GtkAction *action,
369 static void compose_insert_sig_cb (GtkAction *action,
372 static void compose_close_cb (GtkAction *action,
375 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
377 static void compose_address_cb (GtkAction *action,
379 static void about_show_cb (GtkAction *action,
381 static void compose_template_activate_cb(GtkWidget *widget,
384 static void compose_ext_editor_cb (GtkAction *action,
387 static gint compose_delete_cb (GtkWidget *widget,
391 static void compose_undo_cb (GtkAction *action,
393 static void compose_redo_cb (GtkAction *action,
395 static void compose_cut_cb (GtkAction *action,
397 static void compose_copy_cb (GtkAction *action,
399 static void compose_paste_cb (GtkAction *action,
401 static void compose_paste_as_quote_cb (GtkAction *action,
403 static void compose_paste_no_wrap_cb (GtkAction *action,
405 static void compose_paste_wrap_cb (GtkAction *action,
407 static void compose_allsel_cb (GtkAction *action,
410 static void compose_advanced_action_cb (GtkAction *action,
413 static void compose_grab_focus_cb (GtkWidget *widget,
416 static void compose_changed_cb (GtkTextBuffer *textbuf,
419 static void compose_wrap_cb (GtkAction *action,
421 static void compose_wrap_all_cb (GtkAction *action,
423 static void compose_find_cb (GtkAction *action,
425 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
430 static void compose_toggle_ruler_cb (GtkToggleAction *action,
432 static void compose_toggle_sign_cb (GtkToggleAction *action,
434 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
436 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
437 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
438 static void activate_privacy_system (Compose *compose,
439 PrefsAccount *account,
441 static void compose_use_signing(Compose *compose, gboolean use_signing);
442 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
443 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
445 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
447 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
449 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
451 static void compose_attach_drag_received_cb (GtkWidget *widget,
452 GdkDragContext *drag_context,
455 GtkSelectionData *data,
459 static void compose_insert_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_header_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
476 static gboolean compose_drag_drop (GtkWidget *widget,
477 GdkDragContext *drag_context,
479 guint time, gpointer user_data);
481 static void text_inserted (GtkTextBuffer *buffer,
486 static Compose *compose_generic_reply(MsgInfo *msginfo,
487 ComposeQuoteMode quote_mode,
491 gboolean followup_and_reply_to,
494 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
496 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
498 ComposeHeaderEntry *headerentry);
500 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
502 static void compose_allow_user_actions (Compose *compose, gboolean allow);
504 static void compose_nothing_cb (GtkAction *action, gpointer data)
510 static void compose_check_all (GtkAction *action, gpointer data);
511 static void compose_highlight_all (GtkAction *action, gpointer data);
512 static void compose_check_backwards (GtkAction *action, gpointer data);
513 static void compose_check_forwards_go (GtkAction *action, gpointer data);
516 static gint compose_defer_auto_save_draft (Compose *compose);
517 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
519 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
522 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
523 FolderItem *folder_item);
525 static void compose_attach_update_label(Compose *compose);
527 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
529 static GtkActionEntry compose_popup_entries[] =
531 {"Compose", NULL, "Compose" },
532 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
533 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
534 {"Compose/---", NULL, "---", NULL, NULL, NULL },
535 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
538 static GtkActionEntry compose_entries[] =
540 {"Menu", NULL, "Menu" },
542 {"Message", NULL, N_("_Message") },
543 {"Edit", NULL, N_("_Edit") },
545 {"Spelling", NULL, N_("_Spelling") },
547 {"Options", NULL, N_("_Options") },
548 {"Tools", NULL, N_("_Tools") },
549 {"Help", NULL, N_("_Help") },
551 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
552 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
553 {"Message/---", NULL, "---" },
555 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
556 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
557 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
560 /* {"Message/---", NULL, "---" }, */
561 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
564 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
565 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
566 {"Edit/---", NULL, "---" },
568 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
569 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
570 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
572 {"Edit/SpecialPaste", NULL, N_("Special paste") },
573 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
574 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
575 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
577 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
579 {"Edit/Advanced", NULL, N_("A_dvanced") },
580 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
581 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
582 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
583 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
584 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
585 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
586 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
587 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
588 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
589 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
590 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
591 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
592 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
593 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
595 /* {"Edit/---", NULL, "---" }, */
596 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
598 /* {"Edit/---", NULL, "---" }, */
599 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
600 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
601 /* {"Edit/---", NULL, "---" }, */
602 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
605 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
606 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
607 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
608 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
610 {"Spelling/---", NULL, "---" },
611 {"Spelling/Options", NULL, N_("_Options") },
616 {"Options/ReplyMode", NULL, N_("Reply _mode") },
617 {"Options/---", NULL, "---" },
618 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
619 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
621 /* {"Options/---", NULL, "---" }, */
623 {"Options/Priority", NULL, N_("_Priority") },
625 {"Options/Encoding", NULL, N_("Character _encoding") },
626 {"Options/Encoding/---", NULL, "---" },
627 #define ENC_ACTION(cs_char,c_char,string) \
628 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
630 {"Options/Encoding/Western", NULL, N_("Western European") },
631 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
632 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
633 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
634 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
635 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
636 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
637 {"Options/Encoding/Korean", NULL, N_("Korean") },
638 {"Options/Encoding/Thai", NULL, N_("Thai") },
641 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
643 {"Tools/Template", NULL, N_("_Template") },
644 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
645 {"Tools/Actions", NULL, N_("Actio_ns") },
646 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
649 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
652 static GtkToggleActionEntry compose_toggle_entries[] =
654 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
655 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
656 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
657 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
658 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
659 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
660 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
663 static GtkRadioActionEntry compose_radio_rm_entries[] =
665 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
667 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
668 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
671 static GtkRadioActionEntry compose_radio_prio_entries[] =
673 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
677 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
680 static GtkRadioActionEntry compose_radio_enc_entries[] =
682 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
715 static GtkTargetEntry compose_mime_types[] =
717 {"text/uri-list", 0, 0},
718 {"UTF8_STRING", 0, 0},
722 static gboolean compose_put_existing_to_front(MsgInfo *info)
724 GList *compose_list = compose_get_compose_list();
728 for (elem = compose_list; elem != NULL && elem->data != NULL;
730 Compose *c = (Compose*)elem->data;
732 if (!c->targetinfo || !c->targetinfo->msgid ||
736 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
737 gtkut_window_popup(c->window);
745 static GdkColor quote_color1 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color2 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
749 static GdkColor quote_color3 =
750 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor1 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor2 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_bgcolor3 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor signature_color = {
766 static GdkColor uri_color = {
773 static void compose_create_tags(GtkTextView *text, Compose *compose)
775 GtkTextBuffer *buffer;
776 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 buffer = gtk_text_view_get_buffer(text);
784 if (prefs_common.enable_color) {
785 /* grab the quote colors, converting from an int to a GdkColor */
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
798 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
800 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
803 signature_color = quote_color1 = quote_color2 = quote_color3 =
804 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
807 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
808 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
809 "foreground-gdk", "e_color1,
810 "paragraph-background-gdk", "e_bgcolor1,
812 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
813 "foreground-gdk", "e_color2,
814 "paragraph-background-gdk", "e_bgcolor2,
816 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
817 "foreground-gdk", "e_color3,
818 "paragraph-background-gdk", "e_bgcolor3,
821 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
822 "foreground-gdk", "e_color1,
824 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
825 "foreground-gdk", "e_color2,
827 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
828 "foreground-gdk", "e_color3,
832 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
833 "foreground-gdk", &signature_color,
836 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
837 "foreground-gdk", &uri_color,
839 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
840 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
842 color[0] = quote_color1;
843 color[1] = quote_color2;
844 color[2] = quote_color3;
845 color[3] = quote_bgcolor1;
846 color[4] = quote_bgcolor2;
847 color[5] = quote_bgcolor3;
848 color[6] = signature_color;
849 color[7] = uri_color;
850 cmap = gdk_drawable_get_colormap(compose->window->window);
851 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
853 for (i = 0; i < 8; i++) {
854 if (success[i] == FALSE) {
857 g_warning("Compose: color allocation failed.\n");
858 style = gtk_widget_get_style(GTK_WIDGET(text));
859 quote_color1 = quote_color2 = quote_color3 =
860 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
861 signature_color = uri_color = black;
866 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
867 GPtrArray *attach_files)
869 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
872 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
874 return compose_generic_new(account, mailto, item, NULL, NULL);
877 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
879 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
882 #define SCROLL_TO_CURSOR(compose) { \
883 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
884 gtk_text_view_get_buffer( \
885 GTK_TEXT_VIEW(compose->text))); \
886 gtk_text_view_scroll_mark_onscreen( \
887 GTK_TEXT_VIEW(compose->text), \
891 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
894 if (folderidentifier) {
895 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
896 prefs_common.compose_save_to_history = add_history(
897 prefs_common.compose_save_to_history, folderidentifier);
898 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
899 prefs_common.compose_save_to_history);
902 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
903 if (folderidentifier)
904 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
906 gtk_entry_set_text(GTK_ENTRY(entry), "");
909 static gchar *compose_get_save_to(Compose *compose)
912 gchar *result = NULL;
913 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
914 result = gtk_editable_get_chars(entry, 0, -1);
917 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
918 prefs_common.compose_save_to_history = add_history(
919 prefs_common.compose_save_to_history, result);
920 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
921 prefs_common.compose_save_to_history);
926 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
927 GPtrArray *attach_files, GList *listAddress )
930 GtkTextView *textview;
931 GtkTextBuffer *textbuf;
933 const gchar *subject_format = NULL;
934 const gchar *body_format = NULL;
935 gchar *mailto_from = NULL;
936 PrefsAccount *mailto_account = NULL;
937 MsgInfo* dummyinfo = NULL;
939 /* check if mailto defines a from */
940 if (mailto && *mailto != '\0') {
941 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
942 /* mailto defines a from, check if we can get account prefs from it,
943 if not, the account prefs will be guessed using other ways, but we'll keep
946 mailto_account = account_find_from_address(mailto_from, TRUE);
948 account = mailto_account;
951 /* if no account prefs set from mailto, set if from folder prefs (if any) */
952 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
953 account = account_find_from_id(item->prefs->default_account);
955 /* if no account prefs set, fallback to the current one */
956 if (!account) account = cur_account;
957 cm_return_val_if_fail(account != NULL, NULL);
959 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
961 /* override from name if mailto asked for it */
963 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
966 /* override from name according to folder properties */
967 if (item && item->prefs &&
968 item->prefs->compose_with_format &&
969 item->prefs->compose_override_from_format &&
970 *item->prefs->compose_override_from_format != '\0') {
975 dummyinfo = compose_msginfo_new_from_compose(compose);
977 /* decode \-escape sequences in the internal representation of the quote format */
978 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
979 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
982 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
985 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
987 quote_fmt_scan_string(tmp);
990 buf = quote_fmt_get_buffer();
992 alertpanel_error(_("New message From format error."));
994 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
995 quote_fmt_reset_vartable();
1000 compose->replyinfo = NULL;
1001 compose->fwdinfo = NULL;
1003 textview = GTK_TEXT_VIEW(compose->text);
1004 textbuf = gtk_text_view_get_buffer(textview);
1005 compose_create_tags(textview, compose);
1007 undo_block(compose->undostruct);
1009 compose_set_dictionaries_from_folder_prefs(compose, item);
1012 if (account->auto_sig)
1013 compose_insert_sig(compose, FALSE);
1014 gtk_text_buffer_get_start_iter(textbuf, &iter);
1015 gtk_text_buffer_place_cursor(textbuf, &iter);
1017 if (account->protocol != A_NNTP) {
1018 if (mailto && *mailto != '\0') {
1019 compose_entries_set(compose, mailto, COMPOSE_TO);
1021 } else if (item && item->prefs) {
1022 if (item->prefs->enable_default_bcc) {
1023 compose_entry_append(compose, item->prefs->default_bcc, COMPOSE_BCC);
1025 if (item->prefs->enable_default_cc) {
1026 compose_entry_append(compose, item->prefs->default_cc, COMPOSE_CC);
1028 if (item->prefs->enable_default_replyto) {
1029 compose_entry_append(compose, item->prefs->default_replyto, COMPOSE_REPLYTO);
1031 if (item->prefs->enable_default_to) {
1032 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1033 compose_entry_mark_default_to(compose, item->prefs->default_to);
1036 if (item && item->ret_rcpt) {
1037 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1040 if (mailto && *mailto != '\0') {
1041 if (!strchr(mailto, '@'))
1042 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1044 compose_entries_set(compose, mailto, COMPOSE_TO);
1045 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1046 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1049 * CLAWS: just don't allow return receipt request, even if the user
1050 * may want to send an email. simple but foolproof.
1052 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1054 compose_add_field_list( compose, listAddress );
1056 if (item && item->prefs && item->prefs->compose_with_format) {
1057 subject_format = item->prefs->compose_subject_format;
1058 body_format = item->prefs->compose_body_format;
1059 } else if (account->compose_with_format) {
1060 subject_format = account->compose_subject_format;
1061 body_format = account->compose_body_format;
1062 } else if (prefs_common.compose_with_format) {
1063 subject_format = prefs_common.compose_subject_format;
1064 body_format = prefs_common.compose_body_format;
1067 if (subject_format || body_format) {
1070 && *subject_format != '\0' )
1072 gchar *subject = NULL;
1077 dummyinfo = compose_msginfo_new_from_compose(compose);
1079 /* decode \-escape sequences in the internal representation of the quote format */
1080 tmp = malloc(strlen(subject_format)+1);
1081 pref_get_unescaped_pref(tmp, subject_format);
1083 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1085 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1086 compose->gtkaspell);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1090 quote_fmt_scan_string(tmp);
1093 buf = quote_fmt_get_buffer();
1095 alertpanel_error(_("New message subject format error."));
1097 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1098 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1099 quote_fmt_reset_vartable();
1106 && *body_format != '\0' )
1109 GtkTextBuffer *buffer;
1110 GtkTextIter start, end;
1114 dummyinfo = compose_msginfo_new_from_compose(compose);
1116 text = GTK_TEXT_VIEW(compose->text);
1117 buffer = gtk_text_view_get_buffer(text);
1118 gtk_text_buffer_get_start_iter(buffer, &start);
1119 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1120 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1122 compose_quote_fmt(compose, dummyinfo,
1124 NULL, tmp, FALSE, TRUE,
1125 _("New message body format error at line %d."));
1126 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1127 quote_fmt_reset_vartable();
1133 procmsg_msginfo_free( dummyinfo );
1139 for (i = 0; i < attach_files->len; i++) {
1140 file = g_ptr_array_index(attach_files, i);
1141 compose_attach_append(compose, file, file, NULL);
1145 compose_show_first_last_header(compose, TRUE);
1147 /* Set save folder */
1148 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1149 gchar *folderidentifier;
1151 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1152 folderidentifier = folder_item_get_identifier(item);
1153 compose_set_save_to(compose, folderidentifier);
1154 g_free(folderidentifier);
1157 gtk_widget_grab_focus(compose->header_last->entry);
1159 undo_unblock(compose->undostruct);
1161 if (prefs_common.auto_exteditor)
1162 compose_exec_ext_editor(compose);
1164 compose->draft_timeout_tag = -1;
1165 SCROLL_TO_CURSOR(compose);
1167 compose->modified = FALSE;
1168 compose_set_title(compose);
1172 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1173 gboolean override_pref, const gchar *system)
1175 const gchar *privacy = NULL;
1177 cm_return_if_fail(compose != NULL);
1178 cm_return_if_fail(account != NULL);
1180 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1185 else if (account->default_privacy_system
1186 && strlen(account->default_privacy_system)) {
1187 privacy = account->default_privacy_system;
1189 GSList *privacy_avail = privacy_get_system_ids();
1190 if (privacy_avail && g_slist_length(privacy_avail)) {
1191 privacy = (gchar *)(privacy_avail->data);
1194 if (privacy != NULL) {
1196 g_free(compose->privacy_system);
1197 compose->privacy_system = NULL;
1199 if (compose->privacy_system == NULL)
1200 compose->privacy_system = g_strdup(privacy);
1201 else if (*(compose->privacy_system) == '\0') {
1202 g_free(compose->privacy_system);
1203 compose->privacy_system = g_strdup(privacy);
1205 compose_update_privacy_system_menu_item(compose, FALSE);
1206 compose_use_encryption(compose, TRUE);
1210 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1212 const gchar *privacy = NULL;
1216 else if (account->default_privacy_system
1217 && strlen(account->default_privacy_system)) {
1218 privacy = account->default_privacy_system;
1220 GSList *privacy_avail = privacy_get_system_ids();
1221 if (privacy_avail && g_slist_length(privacy_avail)) {
1222 privacy = (gchar *)(privacy_avail->data);
1226 if (privacy != NULL) {
1228 g_free(compose->privacy_system);
1229 compose->privacy_system = NULL;
1231 if (compose->privacy_system == NULL)
1232 compose->privacy_system = g_strdup(privacy);
1233 compose_update_privacy_system_menu_item(compose, FALSE);
1234 compose_use_signing(compose, TRUE);
1238 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1242 Compose *compose = NULL;
1244 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1246 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1247 cm_return_val_if_fail(msginfo != NULL, NULL);
1249 list_len = g_slist_length(msginfo_list);
1253 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1254 FALSE, prefs_common.default_reply_list, FALSE, body);
1256 case COMPOSE_REPLY_WITH_QUOTE:
1257 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1258 FALSE, prefs_common.default_reply_list, FALSE, body);
1260 case COMPOSE_REPLY_WITHOUT_QUOTE:
1261 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1262 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1264 case COMPOSE_REPLY_TO_SENDER:
1265 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1266 FALSE, FALSE, TRUE, body);
1268 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1269 compose = compose_followup_and_reply_to(msginfo,
1270 COMPOSE_QUOTE_CHECK,
1271 FALSE, FALSE, body);
1273 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1274 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1275 FALSE, FALSE, TRUE, body);
1277 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1278 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1279 FALSE, FALSE, TRUE, NULL);
1281 case COMPOSE_REPLY_TO_ALL:
1282 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1283 TRUE, FALSE, FALSE, body);
1285 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1286 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1287 TRUE, FALSE, FALSE, body);
1289 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1290 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1291 TRUE, FALSE, FALSE, NULL);
1293 case COMPOSE_REPLY_TO_LIST:
1294 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1295 FALSE, TRUE, FALSE, body);
1297 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1298 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1299 FALSE, TRUE, FALSE, body);
1301 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1302 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1303 FALSE, TRUE, FALSE, NULL);
1305 case COMPOSE_FORWARD:
1306 if (prefs_common.forward_as_attachment) {
1307 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1310 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1314 case COMPOSE_FORWARD_INLINE:
1315 /* check if we reply to more than one Message */
1316 if (list_len == 1) {
1317 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1320 /* more messages FALL THROUGH */
1321 case COMPOSE_FORWARD_AS_ATTACH:
1322 compose = compose_forward_multiple(NULL, msginfo_list);
1324 case COMPOSE_REDIRECT:
1325 compose = compose_redirect(NULL, msginfo, FALSE);
1328 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1331 if (compose == NULL) {
1332 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1336 compose->rmode = mode;
1337 switch (compose->rmode) {
1339 case COMPOSE_REPLY_WITH_QUOTE:
1340 case COMPOSE_REPLY_WITHOUT_QUOTE:
1341 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1342 debug_print("reply mode Normal\n");
1343 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1344 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1346 case COMPOSE_REPLY_TO_SENDER:
1347 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1348 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1349 debug_print("reply mode Sender\n");
1350 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1352 case COMPOSE_REPLY_TO_ALL:
1353 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1354 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1355 debug_print("reply mode All\n");
1356 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1358 case COMPOSE_REPLY_TO_LIST:
1359 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1360 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1361 debug_print("reply mode List\n");
1362 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1370 static Compose *compose_reply(MsgInfo *msginfo,
1371 ComposeQuoteMode quote_mode,
1377 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1378 to_sender, FALSE, body);
1381 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1382 ComposeQuoteMode quote_mode,
1387 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1388 to_sender, TRUE, body);
1391 static void compose_extract_original_charset(Compose *compose)
1393 MsgInfo *info = NULL;
1394 if (compose->replyinfo) {
1395 info = compose->replyinfo;
1396 } else if (compose->fwdinfo) {
1397 info = compose->fwdinfo;
1398 } else if (compose->targetinfo) {
1399 info = compose->targetinfo;
1402 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1403 MimeInfo *partinfo = mimeinfo;
1404 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1405 partinfo = procmime_mimeinfo_next(partinfo);
1407 compose->orig_charset =
1408 g_strdup(procmime_mimeinfo_get_parameter(
1409 partinfo, "charset"));
1411 procmime_mimeinfo_free_all(mimeinfo);
1415 #define SIGNAL_BLOCK(buffer) { \
1416 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1417 G_CALLBACK(compose_changed_cb), \
1419 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1420 G_CALLBACK(text_inserted), \
1424 #define SIGNAL_UNBLOCK(buffer) { \
1425 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1426 G_CALLBACK(compose_changed_cb), \
1428 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1429 G_CALLBACK(text_inserted), \
1433 static Compose *compose_generic_reply(MsgInfo *msginfo,
1434 ComposeQuoteMode quote_mode,
1435 gboolean to_all, gboolean to_ml,
1437 gboolean followup_and_reply_to,
1441 PrefsAccount *account = NULL;
1442 GtkTextView *textview;
1443 GtkTextBuffer *textbuf;
1444 gboolean quote = FALSE;
1445 const gchar *qmark = NULL;
1446 const gchar *body_fmt = NULL;
1447 gchar *s_system = NULL;
1449 cm_return_val_if_fail(msginfo != NULL, NULL);
1450 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1452 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1454 cm_return_val_if_fail(account != NULL, NULL);
1456 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1458 compose->updating = TRUE;
1460 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1461 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1463 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1464 if (!compose->replyinfo)
1465 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1467 compose_extract_original_charset(compose);
1469 if (msginfo->folder && msginfo->folder->ret_rcpt)
1470 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1472 /* Set save folder */
1473 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1474 gchar *folderidentifier;
1476 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1477 folderidentifier = folder_item_get_identifier(msginfo->folder);
1478 compose_set_save_to(compose, folderidentifier);
1479 g_free(folderidentifier);
1482 if (compose_parse_header(compose, msginfo) < 0) {
1483 compose->updating = FALSE;
1484 compose_destroy(compose);
1488 /* override from name according to folder properties */
1489 if (msginfo->folder && msginfo->folder->prefs &&
1490 msginfo->folder->prefs->reply_with_format &&
1491 msginfo->folder->prefs->reply_override_from_format &&
1492 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1497 /* decode \-escape sequences in the internal representation of the quote format */
1498 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1499 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1502 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1503 compose->gtkaspell);
1505 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1507 quote_fmt_scan_string(tmp);
1510 buf = quote_fmt_get_buffer();
1512 alertpanel_error(_("Message reply From format error."));
1514 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1515 quote_fmt_reset_vartable();
1520 textview = (GTK_TEXT_VIEW(compose->text));
1521 textbuf = gtk_text_view_get_buffer(textview);
1522 compose_create_tags(textview, compose);
1524 undo_block(compose->undostruct);
1526 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1529 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1530 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1531 /* use the reply format of folder (if enabled), or the account's one
1532 (if enabled) or fallback to the global reply format, which is always
1533 enabled (even if empty), and use the relevant quotemark */
1535 if (msginfo->folder && msginfo->folder->prefs &&
1536 msginfo->folder->prefs->reply_with_format) {
1537 qmark = msginfo->folder->prefs->reply_quotemark;
1538 body_fmt = msginfo->folder->prefs->reply_body_format;
1540 } else if (account->reply_with_format) {
1541 qmark = account->reply_quotemark;
1542 body_fmt = account->reply_body_format;
1545 qmark = prefs_common.quotemark;
1546 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1547 body_fmt = gettext(prefs_common.quotefmt);
1554 /* empty quotemark is not allowed */
1555 if (qmark == NULL || *qmark == '\0')
1557 compose_quote_fmt(compose, compose->replyinfo,
1558 body_fmt, qmark, body, FALSE, TRUE,
1559 _("Message reply format error at line %d."));
1560 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1561 quote_fmt_reset_vartable();
1564 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1565 compose_force_encryption(compose, account, FALSE, s_system);
1568 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1569 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1570 compose_force_signing(compose, account, s_system);
1574 SIGNAL_BLOCK(textbuf);
1576 if (account->auto_sig)
1577 compose_insert_sig(compose, FALSE);
1579 compose_wrap_all(compose);
1581 SIGNAL_UNBLOCK(textbuf);
1583 gtk_widget_grab_focus(compose->text);
1585 undo_unblock(compose->undostruct);
1587 if (prefs_common.auto_exteditor)
1588 compose_exec_ext_editor(compose);
1590 compose->modified = FALSE;
1591 compose_set_title(compose);
1593 compose->updating = FALSE;
1594 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1595 SCROLL_TO_CURSOR(compose);
1597 if (compose->deferred_destroy) {
1598 compose_destroy(compose);
1605 #define INSERT_FW_HEADER(var, hdr) \
1606 if (msginfo->var && *msginfo->var) { \
1607 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1608 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1609 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1612 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1613 gboolean as_attach, const gchar *body,
1614 gboolean no_extedit,
1618 GtkTextView *textview;
1619 GtkTextBuffer *textbuf;
1622 cm_return_val_if_fail(msginfo != NULL, NULL);
1623 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1626 !(account = compose_guess_forward_account_from_msginfo
1628 account = cur_account;
1630 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1632 compose->updating = TRUE;
1633 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1634 if (!compose->fwdinfo)
1635 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1637 compose_extract_original_charset(compose);
1639 if (msginfo->subject && *msginfo->subject) {
1640 gchar *buf, *buf2, *p;
1642 buf = p = g_strdup(msginfo->subject);
1643 p += subject_get_prefix_length(p);
1644 memmove(buf, p, strlen(p) + 1);
1646 buf2 = g_strdup_printf("Fw: %s", buf);
1647 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1653 /* override from name according to folder properties */
1654 if (msginfo->folder && msginfo->folder->prefs &&
1655 msginfo->folder->prefs->forward_with_format &&
1656 msginfo->folder->prefs->forward_override_from_format &&
1657 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1661 MsgInfo *full_msginfo = NULL;
1664 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1666 full_msginfo = procmsg_msginfo_copy(msginfo);
1668 /* decode \-escape sequences in the internal representation of the quote format */
1669 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1670 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1673 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1674 compose->gtkaspell);
1676 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1678 quote_fmt_scan_string(tmp);
1681 buf = quote_fmt_get_buffer();
1683 alertpanel_error(_("Message forward From format error."));
1685 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1686 quote_fmt_reset_vartable();
1689 procmsg_msginfo_free(full_msginfo);
1692 textview = GTK_TEXT_VIEW(compose->text);
1693 textbuf = gtk_text_view_get_buffer(textview);
1694 compose_create_tags(textview, compose);
1696 undo_block(compose->undostruct);
1700 msgfile = procmsg_get_message_file(msginfo);
1701 if (!is_file_exist(msgfile))
1702 g_warning("%s: file not exist\n", msgfile);
1704 compose_attach_append(compose, msgfile, msgfile,
1709 const gchar *qmark = NULL;
1710 const gchar *body_fmt = NULL;
1711 MsgInfo *full_msginfo;
1713 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1714 body_fmt = gettext(prefs_common.fw_quotefmt);
1718 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1720 full_msginfo = procmsg_msginfo_copy(msginfo);
1722 /* use the forward format of folder (if enabled), or the account's one
1723 (if enabled) or fallback to the global forward format, which is always
1724 enabled (even if empty), and use the relevant quotemark */
1725 if (msginfo->folder && msginfo->folder->prefs &&
1726 msginfo->folder->prefs->forward_with_format) {
1727 qmark = msginfo->folder->prefs->forward_quotemark;
1728 body_fmt = msginfo->folder->prefs->forward_body_format;
1730 } else if (account->forward_with_format) {
1731 qmark = account->forward_quotemark;
1732 body_fmt = account->forward_body_format;
1735 qmark = prefs_common.fw_quotemark;
1736 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1737 body_fmt = gettext(prefs_common.fw_quotefmt);
1742 /* empty quotemark is not allowed */
1743 if (qmark == NULL || *qmark == '\0')
1746 compose_quote_fmt(compose, full_msginfo,
1747 body_fmt, qmark, body, FALSE, TRUE,
1748 _("Message forward format error at line %d."));
1749 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1750 quote_fmt_reset_vartable();
1751 compose_attach_parts(compose, msginfo);
1753 procmsg_msginfo_free(full_msginfo);
1756 SIGNAL_BLOCK(textbuf);
1758 if (account->auto_sig)
1759 compose_insert_sig(compose, FALSE);
1761 compose_wrap_all(compose);
1763 SIGNAL_UNBLOCK(textbuf);
1765 gtk_text_buffer_get_start_iter(textbuf, &iter);
1766 gtk_text_buffer_place_cursor(textbuf, &iter);
1768 gtk_widget_grab_focus(compose->header_last->entry);
1770 if (!no_extedit && prefs_common.auto_exteditor)
1771 compose_exec_ext_editor(compose);
1774 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1775 gchar *folderidentifier;
1777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1778 folderidentifier = folder_item_get_identifier(msginfo->folder);
1779 compose_set_save_to(compose, folderidentifier);
1780 g_free(folderidentifier);
1783 undo_unblock(compose->undostruct);
1785 compose->modified = FALSE;
1786 compose_set_title(compose);
1788 compose->updating = FALSE;
1789 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1790 SCROLL_TO_CURSOR(compose);
1792 if (compose->deferred_destroy) {
1793 compose_destroy(compose);
1800 #undef INSERT_FW_HEADER
1802 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1805 GtkTextView *textview;
1806 GtkTextBuffer *textbuf;
1810 gboolean single_mail = TRUE;
1812 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1814 if (g_slist_length(msginfo_list) > 1)
1815 single_mail = FALSE;
1817 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1818 if (((MsgInfo *)msginfo->data)->folder == NULL)
1821 /* guess account from first selected message */
1823 !(account = compose_guess_forward_account_from_msginfo
1824 (msginfo_list->data)))
1825 account = cur_account;
1827 cm_return_val_if_fail(account != NULL, NULL);
1829 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1830 if (msginfo->data) {
1831 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1832 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1836 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1837 g_warning("no msginfo_list");
1841 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1843 compose->updating = TRUE;
1845 /* override from name according to folder properties */
1846 if (msginfo_list->data) {
1847 MsgInfo *msginfo = msginfo_list->data;
1849 if (msginfo->folder && msginfo->folder->prefs &&
1850 msginfo->folder->prefs->forward_with_format &&
1851 msginfo->folder->prefs->forward_override_from_format &&
1852 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1857 /* decode \-escape sequences in the internal representation of the quote format */
1858 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1859 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1862 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1863 compose->gtkaspell);
1865 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1867 quote_fmt_scan_string(tmp);
1870 buf = quote_fmt_get_buffer();
1872 alertpanel_error(_("Message forward From format error."));
1874 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1875 quote_fmt_reset_vartable();
1881 textview = GTK_TEXT_VIEW(compose->text);
1882 textbuf = gtk_text_view_get_buffer(textview);
1883 compose_create_tags(textview, compose);
1885 undo_block(compose->undostruct);
1886 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1887 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1889 if (!is_file_exist(msgfile))
1890 g_warning("%s: file not exist\n", msgfile);
1892 compose_attach_append(compose, msgfile, msgfile,
1898 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1899 if (info->subject && *info->subject) {
1900 gchar *buf, *buf2, *p;
1902 buf = p = g_strdup(info->subject);
1903 p += subject_get_prefix_length(p);
1904 memmove(buf, p, strlen(p) + 1);
1906 buf2 = g_strdup_printf("Fw: %s", buf);
1907 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1913 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1914 _("Fw: multiple emails"));
1917 SIGNAL_BLOCK(textbuf);
1919 if (account->auto_sig)
1920 compose_insert_sig(compose, FALSE);
1922 compose_wrap_all(compose);
1924 SIGNAL_UNBLOCK(textbuf);
1926 gtk_text_buffer_get_start_iter(textbuf, &iter);
1927 gtk_text_buffer_place_cursor(textbuf, &iter);
1929 gtk_widget_grab_focus(compose->header_last->entry);
1930 undo_unblock(compose->undostruct);
1931 compose->modified = FALSE;
1932 compose_set_title(compose);
1934 compose->updating = FALSE;
1935 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1936 SCROLL_TO_CURSOR(compose);
1938 if (compose->deferred_destroy) {
1939 compose_destroy(compose);
1946 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1948 GtkTextIter start = *iter;
1949 GtkTextIter end_iter;
1950 int start_pos = gtk_text_iter_get_offset(&start);
1952 if (!compose->account->sig_sep)
1955 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1956 start_pos+strlen(compose->account->sig_sep));
1958 /* check sig separator */
1959 str = gtk_text_iter_get_text(&start, &end_iter);
1960 if (!strcmp(str, compose->account->sig_sep)) {
1962 /* check end of line (\n) */
1963 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1964 start_pos+strlen(compose->account->sig_sep));
1965 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1966 start_pos+strlen(compose->account->sig_sep)+1);
1967 tmp = gtk_text_iter_get_text(&start, &end_iter);
1968 if (!strcmp(tmp,"\n")) {
1980 static void compose_colorize_signature(Compose *compose)
1982 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1984 GtkTextIter end_iter;
1985 gtk_text_buffer_get_start_iter(buffer, &iter);
1986 while (gtk_text_iter_forward_line(&iter))
1987 if (compose_is_sig_separator(compose, buffer, &iter)) {
1988 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1989 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1993 #define BLOCK_WRAP() { \
1994 prev_autowrap = compose->autowrap; \
1995 buffer = gtk_text_view_get_buffer( \
1996 GTK_TEXT_VIEW(compose->text)); \
1997 compose->autowrap = FALSE; \
1999 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2000 G_CALLBACK(compose_changed_cb), \
2002 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2003 G_CALLBACK(text_inserted), \
2006 #define UNBLOCK_WRAP() { \
2007 compose->autowrap = prev_autowrap; \
2008 if (compose->autowrap) { \
2009 gint old = compose->draft_timeout_tag; \
2010 compose->draft_timeout_tag = -2; \
2011 compose_wrap_all(compose); \
2012 compose->draft_timeout_tag = old; \
2015 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2016 G_CALLBACK(compose_changed_cb), \
2018 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2019 G_CALLBACK(text_inserted), \
2023 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2025 Compose *compose = NULL;
2026 PrefsAccount *account = NULL;
2027 GtkTextView *textview;
2028 GtkTextBuffer *textbuf;
2032 gchar buf[BUFFSIZE];
2033 gboolean use_signing = FALSE;
2034 gboolean use_encryption = FALSE;
2035 gchar *privacy_system = NULL;
2036 int priority = PRIORITY_NORMAL;
2037 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2038 gboolean autowrap = prefs_common.autowrap;
2039 gboolean autoindent = prefs_common.auto_indent;
2041 cm_return_val_if_fail(msginfo != NULL, NULL);
2042 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2044 if (compose_put_existing_to_front(msginfo)) {
2048 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2049 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2050 gchar queueheader_buf[BUFFSIZE];
2053 /* Select Account from queue headers */
2054 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2055 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2056 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2057 account = account_find_from_id(id);
2059 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2060 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2061 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2062 account = account_find_from_id(id);
2064 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2065 sizeof(queueheader_buf), "NAID:")) {
2066 id = atoi(&queueheader_buf[strlen("NAID:")]);
2067 account = account_find_from_id(id);
2069 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2070 sizeof(queueheader_buf), "MAID:")) {
2071 id = atoi(&queueheader_buf[strlen("MAID:")]);
2072 account = account_find_from_id(id);
2074 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2075 sizeof(queueheader_buf), "S:")) {
2076 account = account_find_from_address(queueheader_buf, FALSE);
2078 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2079 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2080 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2081 use_signing = param;
2084 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2085 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2086 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2087 use_signing = param;
2090 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2091 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2092 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2093 use_encryption = param;
2095 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2096 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2097 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2098 use_encryption = param;
2100 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2101 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2102 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2105 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2106 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2107 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2110 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2111 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2112 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2114 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2115 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2116 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2118 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2119 sizeof(queueheader_buf), "X-Priority: ")) {
2120 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2123 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2124 sizeof(queueheader_buf), "RMID:")) {
2125 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2126 if (tokens[0] && tokens[1] && tokens[2]) {
2127 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2128 if (orig_item != NULL) {
2129 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2134 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2135 sizeof(queueheader_buf), "FMID:")) {
2136 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2137 if (tokens[0] && tokens[1] && tokens[2]) {
2138 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2139 if (orig_item != NULL) {
2140 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2146 account = msginfo->folder->folder->account;
2149 if (!account && prefs_common.reedit_account_autosel) {
2150 gchar from[BUFFSIZE];
2151 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2152 extract_address(from);
2153 account = account_find_from_address(from, FALSE);
2157 account = cur_account;
2159 cm_return_val_if_fail(account != NULL, NULL);
2161 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2163 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2164 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2165 compose->autowrap = autowrap;
2166 compose->replyinfo = replyinfo;
2167 compose->fwdinfo = fwdinfo;
2169 compose->updating = TRUE;
2170 compose->priority = priority;
2172 if (privacy_system != NULL) {
2173 compose->privacy_system = privacy_system;
2174 compose_use_signing(compose, use_signing);
2175 compose_use_encryption(compose, use_encryption);
2176 compose_update_privacy_system_menu_item(compose, FALSE);
2178 activate_privacy_system(compose, account, FALSE);
2181 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2183 compose_extract_original_charset(compose);
2185 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2186 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2187 gchar queueheader_buf[BUFFSIZE];
2189 /* Set message save folder */
2190 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2191 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2192 compose_set_save_to(compose, &queueheader_buf[4]);
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2195 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2197 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2202 if (compose_parse_header(compose, msginfo) < 0) {
2203 compose->updating = FALSE;
2204 compose_destroy(compose);
2207 compose_reedit_set_entry(compose, msginfo);
2209 textview = GTK_TEXT_VIEW(compose->text);
2210 textbuf = gtk_text_view_get_buffer(textview);
2211 compose_create_tags(textview, compose);
2213 mark = gtk_text_buffer_get_insert(textbuf);
2214 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2216 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2217 G_CALLBACK(compose_changed_cb),
2220 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2221 fp = procmime_get_first_encrypted_text_content(msginfo);
2223 compose_force_encryption(compose, account, TRUE, NULL);
2226 fp = procmime_get_first_text_content(msginfo);
2229 g_warning("Can't get text part\n");
2233 gboolean prev_autowrap = compose->autowrap;
2234 GtkTextBuffer *buffer = textbuf;
2236 while (fgets(buf, sizeof(buf), fp) != NULL) {
2238 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2244 compose_attach_parts(compose, msginfo);
2246 compose_colorize_signature(compose);
2248 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2249 G_CALLBACK(compose_changed_cb),
2252 gtk_widget_grab_focus(compose->text);
2254 if (prefs_common.auto_exteditor) {
2255 compose_exec_ext_editor(compose);
2257 compose->modified = FALSE;
2258 compose_set_title(compose);
2260 compose->updating = FALSE;
2261 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2262 SCROLL_TO_CURSOR(compose);
2264 if (compose->deferred_destroy) {
2265 compose_destroy(compose);
2269 compose->sig_str = account_get_signature_str(compose->account);
2274 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2281 cm_return_val_if_fail(msginfo != NULL, NULL);
2284 account = account_get_reply_account(msginfo,
2285 prefs_common.reply_account_autosel);
2286 cm_return_val_if_fail(account != NULL, NULL);
2288 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2290 compose->updating = TRUE;
2292 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2293 compose->replyinfo = NULL;
2294 compose->fwdinfo = NULL;
2296 compose_show_first_last_header(compose, TRUE);
2298 gtk_widget_grab_focus(compose->header_last->entry);
2300 filename = procmsg_get_message_file(msginfo);
2302 if (filename == NULL) {
2303 compose->updating = FALSE;
2304 compose_destroy(compose);
2309 compose->redirect_filename = filename;
2311 /* Set save folder */
2312 item = msginfo->folder;
2313 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2314 gchar *folderidentifier;
2316 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2317 folderidentifier = folder_item_get_identifier(item);
2318 compose_set_save_to(compose, folderidentifier);
2319 g_free(folderidentifier);
2322 compose_attach_parts(compose, msginfo);
2324 if (msginfo->subject)
2325 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2327 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2329 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2330 _("Message redirect format error at line %d."));
2331 quote_fmt_reset_vartable();
2332 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2334 compose_colorize_signature(compose);
2337 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2338 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2339 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2341 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2342 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2344 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2345 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2346 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2347 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2348 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2350 if (compose->toolbar->draft_btn)
2351 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2352 if (compose->toolbar->insert_btn)
2353 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2354 if (compose->toolbar->attach_btn)
2355 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2356 if (compose->toolbar->sig_btn)
2357 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2358 if (compose->toolbar->exteditor_btn)
2359 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2360 if (compose->toolbar->linewrap_current_btn)
2361 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2362 if (compose->toolbar->linewrap_all_btn)
2363 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2365 compose->modified = FALSE;
2366 compose_set_title(compose);
2367 compose->updating = FALSE;
2368 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2369 SCROLL_TO_CURSOR(compose);
2371 if (compose->deferred_destroy) {
2372 compose_destroy(compose);
2379 GList *compose_get_compose_list(void)
2381 return compose_list;
2384 void compose_entry_append(Compose *compose, const gchar *address,
2385 ComposeEntryType type)
2387 const gchar *header;
2389 gboolean in_quote = FALSE;
2390 if (!address || *address == '\0') return;
2397 header = N_("Bcc:");
2399 case COMPOSE_REPLYTO:
2400 header = N_("Reply-To:");
2402 case COMPOSE_NEWSGROUPS:
2403 header = N_("Newsgroups:");
2405 case COMPOSE_FOLLOWUPTO:
2406 header = N_( "Followup-To:");
2413 header = prefs_common_translated_header_name(header);
2415 cur = begin = (gchar *)address;
2417 /* we separate the line by commas, but not if we're inside a quoted
2419 while (*cur != '\0') {
2421 in_quote = !in_quote;
2422 if (*cur == ',' && !in_quote) {
2423 gchar *tmp = g_strdup(begin);
2425 tmp[cur-begin]='\0';
2428 while (*tmp == ' ' || *tmp == '\t')
2430 compose_add_header_entry(compose, header, tmp);
2437 gchar *tmp = g_strdup(begin);
2439 tmp[cur-begin]='\0';
2442 while (*tmp == ' ' || *tmp == '\t')
2444 compose_add_header_entry(compose, header, tmp);
2449 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2451 static GdkColor yellow;
2452 static GdkColor black;
2453 static gboolean yellow_initialised = FALSE;
2457 if (!yellow_initialised) {
2458 gdk_color_parse("#f5f6be", &yellow);
2459 gdk_color_parse("#000000", &black);
2460 yellow_initialised = gdk_colormap_alloc_color(
2461 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2462 yellow_initialised &= gdk_colormap_alloc_color(
2463 gdk_colormap_get_system(), &black, FALSE, TRUE);
2466 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2467 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2468 if (gtk_entry_get_text(entry) &&
2469 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2470 if (yellow_initialised) {
2471 gtk_widget_modify_base(
2472 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2473 GTK_STATE_NORMAL, &yellow);
2474 gtk_widget_modify_text(
2475 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2476 GTK_STATE_NORMAL, &black);
2482 void compose_toolbar_cb(gint action, gpointer data)
2484 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2485 Compose *compose = (Compose*)toolbar_item->parent;
2487 cm_return_if_fail(compose != NULL);
2491 compose_send_cb(NULL, compose);
2494 compose_send_later_cb(NULL, compose);
2497 compose_draft(compose, COMPOSE_QUIT_EDITING);
2500 compose_insert_file_cb(NULL, compose);
2503 compose_attach_cb(NULL, compose);
2506 compose_insert_sig(compose, FALSE);
2509 compose_ext_editor_cb(NULL, compose);
2511 case A_LINEWRAP_CURRENT:
2512 compose_beautify_paragraph(compose, NULL, TRUE);
2514 case A_LINEWRAP_ALL:
2515 compose_wrap_all_full(compose, TRUE);
2518 compose_address_cb(NULL, compose);
2521 case A_CHECK_SPELLING:
2522 compose_check_all(NULL, compose);
2530 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2535 gchar *subject = NULL;
2539 gchar **attach = NULL;
2541 /* get mailto parts but skip from */
2542 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2545 compose_entry_append(compose, to, to_type);
2547 compose_entry_append(compose, cc, COMPOSE_CC);
2549 compose_entry_append(compose, bcc, COMPOSE_BCC);
2551 if (!g_utf8_validate (subject, -1, NULL)) {
2552 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2553 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2556 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2560 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2561 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2564 gboolean prev_autowrap = compose->autowrap;
2566 compose->autowrap = FALSE;
2568 mark = gtk_text_buffer_get_insert(buffer);
2569 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2571 if (!g_utf8_validate (body, -1, NULL)) {
2572 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2573 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2576 gtk_text_buffer_insert(buffer, &iter, body, -1);
2578 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2580 compose->autowrap = prev_autowrap;
2581 if (compose->autowrap)
2582 compose_wrap_all(compose);
2586 gint i = 0, att = 0;
2587 gchar *warn_files = NULL;
2588 while (attach[i] != NULL) {
2589 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2590 if (utf8_filename) {
2591 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2592 gchar *tmp = g_strdup_printf("%s%s\n",
2593 warn_files?warn_files:"",
2599 g_free(utf8_filename);
2601 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2606 alertpanel_notice(ngettext(
2607 "The following file has been attached: \n%s",
2608 "The following files have been attached: \n%s", att), warn_files);
2620 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2622 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2623 {"Cc:", NULL, TRUE},
2624 {"References:", NULL, FALSE},
2625 {"Bcc:", NULL, TRUE},
2626 {"Newsgroups:", NULL, TRUE},
2627 {"Followup-To:", NULL, TRUE},
2628 {"List-Post:", NULL, FALSE},
2629 {"X-Priority:", NULL, FALSE},
2630 {NULL, NULL, FALSE}};
2646 cm_return_val_if_fail(msginfo != NULL, -1);
2648 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2649 procheader_get_header_fields(fp, hentry);
2652 if (hentry[H_REPLY_TO].body != NULL) {
2653 if (hentry[H_REPLY_TO].body[0] != '\0') {
2655 conv_unmime_header(hentry[H_REPLY_TO].body,
2658 g_free(hentry[H_REPLY_TO].body);
2659 hentry[H_REPLY_TO].body = NULL;
2661 if (hentry[H_CC].body != NULL) {
2662 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2663 g_free(hentry[H_CC].body);
2664 hentry[H_CC].body = NULL;
2666 if (hentry[H_REFERENCES].body != NULL) {
2667 if (compose->mode == COMPOSE_REEDIT)
2668 compose->references = hentry[H_REFERENCES].body;
2670 compose->references = compose_parse_references
2671 (hentry[H_REFERENCES].body, msginfo->msgid);
2672 g_free(hentry[H_REFERENCES].body);
2674 hentry[H_REFERENCES].body = NULL;
2676 if (hentry[H_BCC].body != NULL) {
2677 if (compose->mode == COMPOSE_REEDIT)
2679 conv_unmime_header(hentry[H_BCC].body, NULL);
2680 g_free(hentry[H_BCC].body);
2681 hentry[H_BCC].body = NULL;
2683 if (hentry[H_NEWSGROUPS].body != NULL) {
2684 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2685 hentry[H_NEWSGROUPS].body = NULL;
2687 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2688 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2689 compose->followup_to =
2690 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2693 g_free(hentry[H_FOLLOWUP_TO].body);
2694 hentry[H_FOLLOWUP_TO].body = NULL;
2696 if (hentry[H_LIST_POST].body != NULL) {
2699 extract_address(hentry[H_LIST_POST].body);
2700 if (hentry[H_LIST_POST].body[0] != '\0') {
2701 scan_mailto_url(hentry[H_LIST_POST].body,
2702 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2704 g_free(compose->ml_post);
2705 compose->ml_post = to;
2708 g_free(hentry[H_LIST_POST].body);
2709 hentry[H_LIST_POST].body = NULL;
2712 /* CLAWS - X-Priority */
2713 if (compose->mode == COMPOSE_REEDIT)
2714 if (hentry[H_X_PRIORITY].body != NULL) {
2717 priority = atoi(hentry[H_X_PRIORITY].body);
2718 g_free(hentry[H_X_PRIORITY].body);
2720 hentry[H_X_PRIORITY].body = NULL;
2722 if (priority < PRIORITY_HIGHEST ||
2723 priority > PRIORITY_LOWEST)
2724 priority = PRIORITY_NORMAL;
2726 compose->priority = priority;
2729 if (compose->mode == COMPOSE_REEDIT) {
2730 if (msginfo->inreplyto && *msginfo->inreplyto)
2731 compose->inreplyto = g_strdup(msginfo->inreplyto);
2735 if (msginfo->msgid && *msginfo->msgid)
2736 compose->inreplyto = g_strdup(msginfo->msgid);
2738 if (!compose->references) {
2739 if (msginfo->msgid && *msginfo->msgid) {
2740 if (msginfo->inreplyto && *msginfo->inreplyto)
2741 compose->references =
2742 g_strdup_printf("<%s>\n\t<%s>",
2746 compose->references =
2747 g_strconcat("<", msginfo->msgid, ">",
2749 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2750 compose->references =
2751 g_strconcat("<", msginfo->inreplyto, ">",
2759 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2761 GSList *ref_id_list, *cur;
2765 ref_id_list = references_list_append(NULL, ref);
2766 if (!ref_id_list) return NULL;
2767 if (msgid && *msgid)
2768 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2773 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2774 /* "<" + Message-ID + ">" + CR+LF+TAB */
2775 len += strlen((gchar *)cur->data) + 5;
2777 if (len > MAX_REFERENCES_LEN) {
2778 /* remove second message-ID */
2779 if (ref_id_list && ref_id_list->next &&
2780 ref_id_list->next->next) {
2781 g_free(ref_id_list->next->data);
2782 ref_id_list = g_slist_remove
2783 (ref_id_list, ref_id_list->next->data);
2785 slist_free_strings(ref_id_list);
2786 g_slist_free(ref_id_list);
2793 new_ref = g_string_new("");
2794 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2795 if (new_ref->len > 0)
2796 g_string_append(new_ref, "\n\t");
2797 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2800 slist_free_strings(ref_id_list);
2801 g_slist_free(ref_id_list);
2803 new_ref_str = new_ref->str;
2804 g_string_free(new_ref, FALSE);
2809 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2810 const gchar *fmt, const gchar *qmark,
2811 const gchar *body, gboolean rewrap,
2812 gboolean need_unescape,
2813 const gchar *err_msg)
2815 MsgInfo* dummyinfo = NULL;
2816 gchar *quote_str = NULL;
2818 gboolean prev_autowrap;
2819 const gchar *trimmed_body = body;
2820 gint cursor_pos = -1;
2821 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2822 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2827 SIGNAL_BLOCK(buffer);
2830 dummyinfo = compose_msginfo_new_from_compose(compose);
2831 msginfo = dummyinfo;
2834 if (qmark != NULL) {
2836 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2837 compose->gtkaspell);
2839 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2841 quote_fmt_scan_string(qmark);
2844 buf = quote_fmt_get_buffer();
2846 alertpanel_error(_("Quote mark format error."));
2848 Xstrdup_a(quote_str, buf, goto error)
2851 if (fmt && *fmt != '\0') {
2854 while (*trimmed_body == '\n')
2858 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2859 compose->gtkaspell);
2861 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2863 if (need_unescape) {
2866 /* decode \-escape sequences in the internal representation of the quote format */
2867 tmp = malloc(strlen(fmt)+1);
2868 pref_get_unescaped_pref(tmp, fmt);
2869 quote_fmt_scan_string(tmp);
2873 quote_fmt_scan_string(fmt);
2877 buf = quote_fmt_get_buffer();
2879 gint line = quote_fmt_get_line();
2880 alertpanel_error(err_msg, line);
2886 prev_autowrap = compose->autowrap;
2887 compose->autowrap = FALSE;
2889 mark = gtk_text_buffer_get_insert(buffer);
2890 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2891 if (g_utf8_validate(buf, -1, NULL)) {
2892 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2894 gchar *tmpout = NULL;
2895 tmpout = conv_codeset_strdup
2896 (buf, conv_get_locale_charset_str_no_utf8(),
2898 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2900 tmpout = g_malloc(strlen(buf)*2+1);
2901 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2903 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2907 cursor_pos = quote_fmt_get_cursor_pos();
2908 if (cursor_pos == -1)
2909 cursor_pos = gtk_text_iter_get_offset(&iter);
2910 compose->set_cursor_pos = cursor_pos;
2912 gtk_text_buffer_get_start_iter(buffer, &iter);
2913 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2914 gtk_text_buffer_place_cursor(buffer, &iter);
2916 compose->autowrap = prev_autowrap;
2917 if (compose->autowrap && rewrap)
2918 compose_wrap_all(compose);
2925 SIGNAL_UNBLOCK(buffer);
2927 procmsg_msginfo_free( dummyinfo );
2932 /* if ml_post is of type addr@host and from is of type
2933 * addr-anything@host, return TRUE
2935 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2937 gchar *left_ml = NULL;
2938 gchar *right_ml = NULL;
2939 gchar *left_from = NULL;
2940 gchar *right_from = NULL;
2941 gboolean result = FALSE;
2943 if (!ml_post || !from)
2946 left_ml = g_strdup(ml_post);
2947 if (strstr(left_ml, "@")) {
2948 right_ml = strstr(left_ml, "@")+1;
2949 *(strstr(left_ml, "@")) = '\0';
2952 left_from = g_strdup(from);
2953 if (strstr(left_from, "@")) {
2954 right_from = strstr(left_from, "@")+1;
2955 *(strstr(left_from, "@")) = '\0';
2958 if (left_ml && left_from && right_ml && right_from
2959 && !strncmp(left_from, left_ml, strlen(left_ml))
2960 && !strcmp(right_from, right_ml)) {
2969 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2971 gchar *my_addr1, *my_addr2;
2973 if (!addr1 || !addr2)
2976 Xstrdup_a(my_addr1, addr1, return FALSE);
2977 Xstrdup_a(my_addr2, addr2, return FALSE);
2979 extract_address(my_addr1);
2980 extract_address(my_addr2);
2982 return !strcasecmp(my_addr1, my_addr2);
2985 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2986 gboolean to_all, gboolean to_ml,
2988 gboolean followup_and_reply_to)
2990 GSList *cc_list = NULL;
2993 gchar *replyto = NULL;
2994 GHashTable *to_table;
2996 gboolean reply_to_ml = FALSE;
2997 gboolean default_reply_to = FALSE;
2999 cm_return_if_fail(compose->account != NULL);
3000 cm_return_if_fail(msginfo != NULL);
3002 reply_to_ml = to_ml && compose->ml_post;
3004 default_reply_to = msginfo->folder &&
3005 msginfo->folder->prefs->enable_default_reply_to;
3007 if (compose->account->protocol != A_NNTP) {
3008 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3009 if (msginfo->folder->prefs->enable_default_replyto) {
3010 compose_entry_append(compose, msginfo->folder->prefs->default_replyto, COMPOSE_REPLYTO);
3012 if (msginfo->folder->prefs->enable_default_bcc) {
3013 compose_entry_append(compose, msginfo->folder->prefs->default_bcc, COMPOSE_BCC);
3015 if (msginfo->folder->prefs->enable_default_cc) {
3016 compose_entry_append(compose, msginfo->folder->prefs->default_cc, COMPOSE_CC);
3019 if (reply_to_ml && !default_reply_to) {
3021 gboolean is_subscr = is_subscription(compose->ml_post,
3024 /* normal answer to ml post with a reply-to */
3025 compose_entry_append(compose,
3028 if (compose->replyto
3029 && !same_address(compose->ml_post, compose->replyto))
3030 compose_entry_append(compose,
3034 /* answer to subscription confirmation */
3035 if (compose->replyto)
3036 compose_entry_append(compose,
3039 else if (msginfo->from)
3040 compose_entry_append(compose,
3045 else if (!(to_all || to_sender) && default_reply_to) {
3046 compose_entry_append(compose,
3047 msginfo->folder->prefs->default_reply_to,
3049 compose_entry_mark_default_to(compose,
3050 msginfo->folder->prefs->default_reply_to);
3055 Xstrdup_a(tmp1, msginfo->from, return);
3056 extract_address(tmp1);
3057 if (to_all || to_sender ||
3058 !account_find_from_address(tmp1, FALSE))
3059 compose_entry_append(compose,
3060 (compose->replyto && !to_sender)
3061 ? compose->replyto :
3062 msginfo->from ? msginfo->from : "",
3064 else if (!to_all && !to_sender) {
3065 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3066 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3067 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3068 if (compose->replyto) {
3069 compose_entry_append(compose,
3073 compose_entry_append(compose,
3074 msginfo->from ? msginfo->from : "",
3078 /* replying to own mail, use original recp */
3079 compose_entry_append(compose,
3080 msginfo->to ? msginfo->to : "",
3082 compose_entry_append(compose,
3083 msginfo->cc ? msginfo->cc : "",
3089 if (to_sender || (compose->followup_to &&
3090 !strncmp(compose->followup_to, "poster", 6)))
3091 compose_entry_append
3093 (compose->replyto ? compose->replyto :
3094 msginfo->from ? msginfo->from : ""),
3097 else if (followup_and_reply_to || to_all) {
3098 compose_entry_append
3100 (compose->replyto ? compose->replyto :
3101 msginfo->from ? msginfo->from : ""),
3104 compose_entry_append
3106 compose->followup_to ? compose->followup_to :
3107 compose->newsgroups ? compose->newsgroups : "",
3108 COMPOSE_NEWSGROUPS);
3111 compose_entry_append
3113 compose->followup_to ? compose->followup_to :
3114 compose->newsgroups ? compose->newsgroups : "",
3115 COMPOSE_NEWSGROUPS);
3118 if (msginfo->subject && *msginfo->subject) {
3122 buf = p = g_strdup(msginfo->subject);
3123 p += subject_get_prefix_length(p);
3124 memmove(buf, p, strlen(p) + 1);
3126 buf2 = g_strdup_printf("Re: %s", buf);
3127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3132 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3134 if (to_ml && compose->ml_post) return;
3135 if (!to_all || compose->account->protocol == A_NNTP) return;
3137 if (compose->replyto) {
3138 Xstrdup_a(replyto, compose->replyto, return);
3139 extract_address(replyto);
3141 if (msginfo->from) {
3142 Xstrdup_a(from, msginfo->from, return);
3143 extract_address(from);
3146 if (replyto && from)
3147 cc_list = address_list_append_with_comments(cc_list, from);
3148 if (to_all && msginfo->folder &&
3149 msginfo->folder->prefs->enable_default_reply_to)
3150 cc_list = address_list_append_with_comments(cc_list,
3151 msginfo->folder->prefs->default_reply_to);
3152 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3153 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3155 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3157 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3158 if (compose->account) {
3159 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3160 GINT_TO_POINTER(1));
3162 /* remove address on To: and that of current account */
3163 for (cur = cc_list; cur != NULL; ) {
3164 GSList *next = cur->next;
3167 addr = g_utf8_strdown(cur->data, -1);
3168 extract_address(addr);
3170 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3171 cc_list = g_slist_remove(cc_list, cur->data);
3173 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3177 hash_free_strings(to_table);
3178 g_hash_table_destroy(to_table);
3181 for (cur = cc_list; cur != NULL; cur = cur->next)
3182 compose_entry_append(compose, (gchar *)cur->data,
3184 slist_free_strings(cc_list);
3185 g_slist_free(cc_list);
3190 #define SET_ENTRY(entry, str) \
3193 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3196 #define SET_ADDRESS(type, str) \
3199 compose_entry_append(compose, str, type); \
3202 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3204 cm_return_if_fail(msginfo != NULL);
3206 SET_ENTRY(subject_entry, msginfo->subject);
3207 SET_ENTRY(from_name, msginfo->from);
3208 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3209 SET_ADDRESS(COMPOSE_CC, compose->cc);
3210 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3211 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3212 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3213 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3215 compose_update_priority_menu_item(compose);
3216 compose_update_privacy_system_menu_item(compose, FALSE);
3217 compose_show_first_last_header(compose, TRUE);
3223 static void compose_insert_sig(Compose *compose, gboolean replace)
3225 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3226 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3228 GtkTextIter iter, iter_end;
3229 gint cur_pos, ins_pos;
3230 gboolean prev_autowrap;
3231 gboolean found = FALSE;
3232 gboolean exists = FALSE;
3234 cm_return_if_fail(compose->account != NULL);
3238 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3239 G_CALLBACK(compose_changed_cb),
3242 mark = gtk_text_buffer_get_insert(buffer);
3243 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3244 cur_pos = gtk_text_iter_get_offset (&iter);
3247 gtk_text_buffer_get_end_iter(buffer, &iter);
3249 exists = (compose->sig_str != NULL);
3252 GtkTextIter first_iter, start_iter, end_iter;
3254 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3256 if (!exists || compose->sig_str[0] == '\0')
3259 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3260 compose->signature_tag);
3263 /* include previous \n\n */
3264 gtk_text_iter_backward_chars(&first_iter, 1);
3265 start_iter = first_iter;
3266 end_iter = first_iter;
3268 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3269 compose->signature_tag);
3270 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3271 compose->signature_tag);
3273 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3279 g_free(compose->sig_str);
3280 compose->sig_str = account_get_signature_str(compose->account);
3282 cur_pos = gtk_text_iter_get_offset(&iter);
3284 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3285 g_free(compose->sig_str);
3286 compose->sig_str = NULL;
3288 if (compose->sig_inserted == FALSE)
3289 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3290 compose->sig_inserted = TRUE;
3292 cur_pos = gtk_text_iter_get_offset(&iter);
3293 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3295 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3296 gtk_text_iter_forward_chars(&iter, 1);
3297 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3298 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3300 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3301 cur_pos = gtk_text_buffer_get_char_count (buffer);
3304 /* put the cursor where it should be
3305 * either where the quote_fmt says, either where it was */
3306 if (compose->set_cursor_pos < 0)
3307 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3309 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3310 compose->set_cursor_pos);
3312 compose->set_cursor_pos = -1;
3313 gtk_text_buffer_place_cursor(buffer, &iter);
3314 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3315 G_CALLBACK(compose_changed_cb),
3321 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3324 GtkTextBuffer *buffer;
3327 const gchar *cur_encoding;
3328 gchar buf[BUFFSIZE];
3331 gboolean prev_autowrap;
3332 gboolean badtxt = FALSE;
3334 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3336 if ((fp = g_fopen(file, "rb")) == NULL) {
3337 FILE_OP_ERROR(file, "fopen");
3338 return COMPOSE_INSERT_READ_ERROR;
3341 prev_autowrap = compose->autowrap;
3342 compose->autowrap = FALSE;
3344 text = GTK_TEXT_VIEW(compose->text);
3345 buffer = gtk_text_view_get_buffer(text);
3346 mark = gtk_text_buffer_get_insert(buffer);
3347 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3349 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3350 G_CALLBACK(text_inserted),
3353 cur_encoding = conv_get_locale_charset_str_no_utf8();
3355 while (fgets(buf, sizeof(buf), fp) != NULL) {
3358 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3359 str = g_strdup(buf);
3361 str = conv_codeset_strdup
3362 (buf, cur_encoding, CS_INTERNAL);
3365 /* strip <CR> if DOS/Windows file,
3366 replace <CR> with <LF> if Macintosh file. */
3369 if (len > 0 && str[len - 1] != '\n') {
3371 if (str[len] == '\r') str[len] = '\n';
3374 gtk_text_buffer_insert(buffer, &iter, str, -1);
3378 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3379 G_CALLBACK(text_inserted),
3381 compose->autowrap = prev_autowrap;
3382 if (compose->autowrap)
3383 compose_wrap_all(compose);
3388 return COMPOSE_INSERT_INVALID_CHARACTER;
3390 return COMPOSE_INSERT_SUCCESS;
3393 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3394 const gchar *filename,
3395 const gchar *content_type)
3403 GtkListStore *store;
3405 gboolean has_binary = FALSE;
3407 if (!is_file_exist(file)) {
3408 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3409 gboolean result = FALSE;
3410 if (file_from_uri && is_file_exist(file_from_uri)) {
3411 result = compose_attach_append(
3412 compose, file_from_uri,
3416 g_free(file_from_uri);
3419 alertpanel_error("File %s doesn't exist\n", filename);
3422 if ((size = get_file_size(file)) < 0) {
3423 alertpanel_error("Can't get file size of %s\n", filename);
3427 alertpanel_error(_("File %s is empty."), filename);
3430 if ((fp = g_fopen(file, "rb")) == NULL) {
3431 alertpanel_error(_("Can't read %s."), filename);
3436 ainfo = g_new0(AttachInfo, 1);
3437 auto_ainfo = g_auto_pointer_new_with_free
3438 (ainfo, (GFreeFunc) compose_attach_info_free);
3439 ainfo->file = g_strdup(file);
3442 ainfo->content_type = g_strdup(content_type);
3443 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3445 MsgFlags flags = {0, 0};
3447 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3448 ainfo->encoding = ENC_7BIT;
3450 ainfo->encoding = ENC_8BIT;
3452 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3453 if (msginfo && msginfo->subject)
3454 name = g_strdup(msginfo->subject);
3456 name = g_path_get_basename(filename ? filename : file);
3458 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3460 procmsg_msginfo_free(msginfo);
3462 if (!g_ascii_strncasecmp(content_type, "text", 4))
3463 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3465 ainfo->encoding = ENC_BASE64;
3466 name = g_path_get_basename(filename ? filename : file);
3467 ainfo->name = g_strdup(name);
3471 ainfo->content_type = procmime_get_mime_type(file);
3472 if (!ainfo->content_type) {
3473 ainfo->content_type =
3474 g_strdup("application/octet-stream");
3475 ainfo->encoding = ENC_BASE64;
3476 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3478 procmime_get_encoding_for_text_file(file, &has_binary);
3480 ainfo->encoding = ENC_BASE64;
3481 name = g_path_get_basename(filename ? filename : file);
3482 ainfo->name = g_strdup(name);
3486 if (ainfo->name != NULL
3487 && !strcmp(ainfo->name, ".")) {
3488 g_free(ainfo->name);
3492 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3493 g_free(ainfo->content_type);
3494 ainfo->content_type = g_strdup("application/octet-stream");
3497 ainfo->size = (goffset)size;
3498 size_text = to_human_readable((goffset)size);
3500 store = GTK_LIST_STORE(gtk_tree_view_get_model
3501 (GTK_TREE_VIEW(compose->attach_clist)));
3503 gtk_list_store_append(store, &iter);
3504 gtk_list_store_set(store, &iter,
3505 COL_MIMETYPE, ainfo->content_type,
3506 COL_SIZE, size_text,
3507 COL_NAME, ainfo->name,
3509 COL_AUTODATA, auto_ainfo,
3512 g_auto_pointer_free(auto_ainfo);
3513 compose_attach_update_label(compose);
3517 static void compose_use_signing(Compose *compose, gboolean use_signing)
3519 compose->use_signing = use_signing;
3520 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3523 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3525 compose->use_encryption = use_encryption;
3526 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3529 #define NEXT_PART_NOT_CHILD(info) \
3531 node = info->node; \
3532 while (node->children) \
3533 node = g_node_last_child(node); \
3534 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3537 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3541 MimeInfo *firsttext = NULL;
3542 MimeInfo *encrypted = NULL;
3545 const gchar *partname = NULL;
3547 mimeinfo = procmime_scan_message(msginfo);
3548 if (!mimeinfo) return;
3550 if (mimeinfo->node->children == NULL) {
3551 procmime_mimeinfo_free_all(mimeinfo);
3555 /* find first content part */
3556 child = (MimeInfo *) mimeinfo->node->children->data;
3557 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3558 child = (MimeInfo *)child->node->children->data;
3561 if (child->type == MIMETYPE_TEXT) {
3563 debug_print("First text part found\n");
3564 } else if (compose->mode == COMPOSE_REEDIT &&
3565 child->type == MIMETYPE_APPLICATION &&
3566 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3567 encrypted = (MimeInfo *)child->node->parent->data;
3570 child = (MimeInfo *) mimeinfo->node->children->data;
3571 while (child != NULL) {
3574 if (child == encrypted) {
3575 /* skip this part of tree */
3576 NEXT_PART_NOT_CHILD(child);
3580 if (child->type == MIMETYPE_MULTIPART) {
3581 /* get the actual content */
3582 child = procmime_mimeinfo_next(child);
3586 if (child == firsttext) {
3587 child = procmime_mimeinfo_next(child);
3591 outfile = procmime_get_tmp_file_name(child);
3592 if ((err = procmime_get_part(outfile, child)) < 0)
3593 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3595 gchar *content_type;
3597 content_type = procmime_get_content_type_str(child->type, child->subtype);
3599 /* if we meet a pgp signature, we don't attach it, but
3600 * we force signing. */
3601 if ((strcmp(content_type, "application/pgp-signature") &&
3602 strcmp(content_type, "application/pkcs7-signature") &&
3603 strcmp(content_type, "application/x-pkcs7-signature"))
3604 || compose->mode == COMPOSE_REDIRECT) {
3605 partname = procmime_mimeinfo_get_parameter(child, "filename");
3606 if (partname == NULL)
3607 partname = procmime_mimeinfo_get_parameter(child, "name");
3608 if (partname == NULL)
3610 compose_attach_append(compose, outfile,
3611 partname, content_type);
3613 compose_force_signing(compose, compose->account, NULL);
3615 g_free(content_type);
3618 NEXT_PART_NOT_CHILD(child);
3620 procmime_mimeinfo_free_all(mimeinfo);
3623 #undef NEXT_PART_NOT_CHILD
3628 WAIT_FOR_INDENT_CHAR,
3629 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3632 /* return indent length, we allow:
3633 indent characters followed by indent characters or spaces/tabs,
3634 alphabets and numbers immediately followed by indent characters,
3635 and the repeating sequences of the above
3636 If quote ends with multiple spaces, only the first one is included. */
3637 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3638 const GtkTextIter *start, gint *len)
3640 GtkTextIter iter = *start;
3644 IndentState state = WAIT_FOR_INDENT_CHAR;
3647 gint alnum_count = 0;
3648 gint space_count = 0;
3651 if (prefs_common.quote_chars == NULL) {
3655 while (!gtk_text_iter_ends_line(&iter)) {
3656 wc = gtk_text_iter_get_char(&iter);
3657 if (g_unichar_iswide(wc))
3659 clen = g_unichar_to_utf8(wc, ch);
3663 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3664 is_space = g_unichar_isspace(wc);
3666 if (state == WAIT_FOR_INDENT_CHAR) {
3667 if (!is_indent && !g_unichar_isalnum(wc))
3670 quote_len += alnum_count + space_count + 1;
3671 alnum_count = space_count = 0;
3672 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3675 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3676 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3680 else if (is_indent) {
3681 quote_len += alnum_count + space_count + 1;
3682 alnum_count = space_count = 0;
3685 state = WAIT_FOR_INDENT_CHAR;
3689 gtk_text_iter_forward_char(&iter);
3692 if (quote_len > 0 && space_count > 0)
3698 if (quote_len > 0) {
3700 gtk_text_iter_forward_chars(&iter, quote_len);
3701 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3707 /* return >0 if the line is itemized */
3708 static int compose_itemized_length(GtkTextBuffer *buffer,
3709 const GtkTextIter *start)
3711 GtkTextIter iter = *start;
3716 if (gtk_text_iter_ends_line(&iter))
3721 wc = gtk_text_iter_get_char(&iter);
3722 if (!g_unichar_isspace(wc))
3724 gtk_text_iter_forward_char(&iter);
3725 if (gtk_text_iter_ends_line(&iter))
3729 clen = g_unichar_to_utf8(wc, ch);
3733 if (!strchr("*-+", ch[0]))
3736 gtk_text_iter_forward_char(&iter);
3737 if (gtk_text_iter_ends_line(&iter))
3739 wc = gtk_text_iter_get_char(&iter);
3740 if (g_unichar_isspace(wc)) {
3746 /* return the string at the start of the itemization */
3747 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3748 const GtkTextIter *start)
3750 GtkTextIter iter = *start;
3753 GString *item_chars = g_string_new("");
3756 if (gtk_text_iter_ends_line(&iter))
3761 wc = gtk_text_iter_get_char(&iter);
3762 if (!g_unichar_isspace(wc))
3764 gtk_text_iter_forward_char(&iter);
3765 if (gtk_text_iter_ends_line(&iter))
3767 g_string_append_unichar(item_chars, wc);
3770 str = item_chars->str;
3771 g_string_free(item_chars, FALSE);
3775 /* return the number of spaces at a line's start */
3776 static int compose_left_offset_length(GtkTextBuffer *buffer,
3777 const GtkTextIter *start)
3779 GtkTextIter iter = *start;
3782 if (gtk_text_iter_ends_line(&iter))
3786 wc = gtk_text_iter_get_char(&iter);
3787 if (!g_unichar_isspace(wc))
3790 gtk_text_iter_forward_char(&iter);
3791 if (gtk_text_iter_ends_line(&iter))
3795 gtk_text_iter_forward_char(&iter);
3796 if (gtk_text_iter_ends_line(&iter))
3801 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3802 const GtkTextIter *start,
3803 GtkTextIter *break_pos,
3807 GtkTextIter iter = *start, line_end = *start;
3808 PangoLogAttr *attrs;
3815 gboolean can_break = FALSE;
3816 gboolean do_break = FALSE;
3817 gboolean was_white = FALSE;
3818 gboolean prev_dont_break = FALSE;
3820 gtk_text_iter_forward_to_line_end(&line_end);
3821 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3822 len = g_utf8_strlen(str, -1);
3826 g_warning("compose_get_line_break_pos: len = 0!\n");
3830 /* g_print("breaking line: %d: %s (len = %d)\n",
3831 gtk_text_iter_get_line(&iter), str, len); */
3833 attrs = g_new(PangoLogAttr, len + 1);
3835 pango_default_break(str, -1, NULL, attrs, len + 1);
3839 /* skip quote and leading spaces */
3840 for (i = 0; *p != '\0' && i < len; i++) {
3843 wc = g_utf8_get_char(p);
3844 if (i >= quote_len && !g_unichar_isspace(wc))
3846 if (g_unichar_iswide(wc))
3848 else if (*p == '\t')
3852 p = g_utf8_next_char(p);
3855 for (; *p != '\0' && i < len; i++) {
3856 PangoLogAttr *attr = attrs + i;
3860 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3863 was_white = attr->is_white;
3865 /* don't wrap URI */
3866 if ((uri_len = get_uri_len(p)) > 0) {
3868 if (pos > 0 && col > max_col) {
3878 wc = g_utf8_get_char(p);
3879 if (g_unichar_iswide(wc)) {
3881 if (prev_dont_break && can_break && attr->is_line_break)
3883 } else if (*p == '\t')
3887 if (pos > 0 && col > max_col) {
3892 if (*p == '-' || *p == '/')
3893 prev_dont_break = TRUE;
3895 prev_dont_break = FALSE;
3897 p = g_utf8_next_char(p);
3901 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3906 *break_pos = *start;
3907 gtk_text_iter_set_line_offset(break_pos, pos);
3912 static gboolean compose_join_next_line(Compose *compose,
3913 GtkTextBuffer *buffer,
3915 const gchar *quote_str)
3917 GtkTextIter iter_ = *iter, cur, prev, next, end;
3918 PangoLogAttr attrs[3];
3920 gchar *next_quote_str;
3923 gboolean keep_cursor = FALSE;
3925 if (!gtk_text_iter_forward_line(&iter_) ||
3926 gtk_text_iter_ends_line(&iter_)) {
3929 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3931 if ((quote_str || next_quote_str) &&
3932 strcmp2(quote_str, next_quote_str) != 0) {
3933 g_free(next_quote_str);
3936 g_free(next_quote_str);
3939 if (quote_len > 0) {
3940 gtk_text_iter_forward_chars(&end, quote_len);
3941 if (gtk_text_iter_ends_line(&end)) {
3946 /* don't join itemized lines */
3947 if (compose_itemized_length(buffer, &end) > 0) {
3951 /* don't join signature separator */
3952 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3955 /* delete quote str */
3957 gtk_text_buffer_delete(buffer, &iter_, &end);
3959 /* don't join line breaks put by the user */
3961 gtk_text_iter_backward_char(&cur);
3962 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3963 gtk_text_iter_forward_char(&cur);
3967 gtk_text_iter_forward_char(&cur);
3968 /* delete linebreak and extra spaces */
3969 while (gtk_text_iter_backward_char(&cur)) {
3970 wc1 = gtk_text_iter_get_char(&cur);
3971 if (!g_unichar_isspace(wc1))
3976 while (!gtk_text_iter_ends_line(&cur)) {
3977 wc1 = gtk_text_iter_get_char(&cur);
3978 if (!g_unichar_isspace(wc1))
3980 gtk_text_iter_forward_char(&cur);
3983 if (!gtk_text_iter_equal(&prev, &next)) {
3986 mark = gtk_text_buffer_get_insert(buffer);
3987 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3988 if (gtk_text_iter_equal(&prev, &cur))
3990 gtk_text_buffer_delete(buffer, &prev, &next);
3994 /* insert space if required */
3995 gtk_text_iter_backward_char(&prev);
3996 wc1 = gtk_text_iter_get_char(&prev);
3997 wc2 = gtk_text_iter_get_char(&next);
3998 gtk_text_iter_forward_char(&next);
3999 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4000 pango_default_break(str, -1, NULL, attrs, 3);
4001 if (!attrs[1].is_line_break ||
4002 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4003 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4005 gtk_text_iter_backward_char(&iter_);
4006 gtk_text_buffer_place_cursor(buffer, &iter_);
4015 #define ADD_TXT_POS(bp_, ep_, pti_) \
4016 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4017 last = last->next; \
4018 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4019 last->next = NULL; \
4021 g_warning("alloc error scanning URIs\n"); \
4024 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4026 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4027 GtkTextBuffer *buffer;
4028 GtkTextIter iter, break_pos, end_of_line;
4029 gchar *quote_str = NULL;
4031 gboolean wrap_quote = prefs_common.linewrap_quote;
4032 gboolean prev_autowrap = compose->autowrap;
4033 gint startq_offset = -1, noq_offset = -1;
4034 gint uri_start = -1, uri_stop = -1;
4035 gint nouri_start = -1, nouri_stop = -1;
4036 gint num_blocks = 0;
4037 gint quotelevel = -1;
4038 gboolean modified = force;
4039 gboolean removed = FALSE;
4040 gboolean modified_before_remove = FALSE;
4042 gboolean start = TRUE;
4043 gint itemized_len = 0, rem_item_len = 0;
4044 gchar *itemized_chars = NULL;
4045 gboolean item_continuation = FALSE;
4050 if (compose->draft_timeout_tag == -2) {
4054 compose->autowrap = FALSE;
4056 buffer = gtk_text_view_get_buffer(text);
4057 undo_wrapping(compose->undostruct, TRUE);
4062 mark = gtk_text_buffer_get_insert(buffer);
4063 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4067 if (compose->draft_timeout_tag == -2) {
4068 if (gtk_text_iter_ends_line(&iter)) {
4069 while (gtk_text_iter_ends_line(&iter) &&
4070 gtk_text_iter_forward_line(&iter))
4073 while (gtk_text_iter_backward_line(&iter)) {
4074 if (gtk_text_iter_ends_line(&iter)) {
4075 gtk_text_iter_forward_line(&iter);
4081 /* move to line start */
4082 gtk_text_iter_set_line_offset(&iter, 0);
4085 itemized_len = compose_itemized_length(buffer, &iter);
4087 if (!itemized_len) {
4088 itemized_len = compose_left_offset_length(buffer, &iter);
4089 item_continuation = TRUE;
4093 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4095 /* go until paragraph end (empty line) */
4096 while (start || !gtk_text_iter_ends_line(&iter)) {
4097 gchar *scanpos = NULL;
4098 /* parse table - in order of priority */
4100 const gchar *needle; /* token */
4102 /* token search function */
4103 gchar *(*search) (const gchar *haystack,
4104 const gchar *needle);
4105 /* part parsing function */
4106 gboolean (*parse) (const gchar *start,
4107 const gchar *scanpos,
4111 /* part to URI function */
4112 gchar *(*build_uri) (const gchar *bp,
4116 static struct table parser[] = {
4117 {"http://", strcasestr, get_uri_part, make_uri_string},
4118 {"https://", strcasestr, get_uri_part, make_uri_string},
4119 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4120 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4121 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4122 {"www.", strcasestr, get_uri_part, make_http_string},
4123 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4124 {"@", strcasestr, get_email_part, make_email_string}
4126 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4127 gint last_index = PARSE_ELEMS;
4129 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4133 if (!prev_autowrap && num_blocks == 0) {
4135 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4136 G_CALLBACK(text_inserted),
4139 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4142 uri_start = uri_stop = -1;
4144 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4147 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4148 if (startq_offset == -1)
4149 startq_offset = gtk_text_iter_get_offset(&iter);
4150 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4151 if (quotelevel > 2) {
4152 /* recycle colors */
4153 if (prefs_common.recycle_quote_colors)
4162 if (startq_offset == -1)
4163 noq_offset = gtk_text_iter_get_offset(&iter);
4167 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4170 if (gtk_text_iter_ends_line(&iter)) {
4172 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4173 prefs_common.linewrap_len,
4175 GtkTextIter prev, next, cur;
4176 if (prev_autowrap != FALSE || force) {
4177 compose->automatic_break = TRUE;
4179 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4180 compose->automatic_break = FALSE;
4181 if (itemized_len && compose->autoindent) {
4182 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4183 if (!item_continuation)
4184 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4186 } else if (quote_str && wrap_quote) {
4187 compose->automatic_break = TRUE;
4189 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4190 compose->automatic_break = FALSE;
4191 if (itemized_len && compose->autoindent) {
4192 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4193 if (!item_continuation)
4194 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4198 /* remove trailing spaces */
4200 rem_item_len = itemized_len;
4201 while (compose->autoindent && rem_item_len-- > 0)
4202 gtk_text_iter_backward_char(&cur);
4203 gtk_text_iter_backward_char(&cur);
4206 while (!gtk_text_iter_starts_line(&cur)) {
4209 gtk_text_iter_backward_char(&cur);
4210 wc = gtk_text_iter_get_char(&cur);
4211 if (!g_unichar_isspace(wc))
4215 if (!gtk_text_iter_equal(&prev, &next)) {
4216 gtk_text_buffer_delete(buffer, &prev, &next);
4218 gtk_text_iter_forward_char(&break_pos);
4222 gtk_text_buffer_insert(buffer, &break_pos,
4226 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4228 /* move iter to current line start */
4229 gtk_text_iter_set_line_offset(&iter, 0);
4236 /* move iter to next line start */
4242 if (!prev_autowrap && num_blocks > 0) {
4244 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4245 G_CALLBACK(text_inserted),
4249 while (!gtk_text_iter_ends_line(&end_of_line)) {
4250 gtk_text_iter_forward_char(&end_of_line);
4252 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4254 nouri_start = gtk_text_iter_get_offset(&iter);
4255 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4257 walk_pos = gtk_text_iter_get_offset(&iter);
4258 /* FIXME: this looks phony. scanning for anything in the parse table */
4259 for (n = 0; n < PARSE_ELEMS; n++) {
4262 tmp = parser[n].search(walk, parser[n].needle);
4264 if (scanpos == NULL || tmp < scanpos) {
4273 /* check if URI can be parsed */
4274 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4275 (const gchar **)&ep, FALSE)
4276 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4280 strlen(parser[last_index].needle);
4283 uri_start = walk_pos + (bp - o_walk);
4284 uri_stop = walk_pos + (ep - o_walk);
4288 gtk_text_iter_forward_line(&iter);
4291 if (startq_offset != -1) {
4292 GtkTextIter startquote, endquote;
4293 gtk_text_buffer_get_iter_at_offset(
4294 buffer, &startquote, startq_offset);
4297 switch (quotelevel) {
4299 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4300 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4301 gtk_text_buffer_apply_tag_by_name(
4302 buffer, "quote0", &startquote, &endquote);
4303 gtk_text_buffer_remove_tag_by_name(
4304 buffer, "quote1", &startquote, &endquote);
4305 gtk_text_buffer_remove_tag_by_name(
4306 buffer, "quote2", &startquote, &endquote);
4311 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4312 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4313 gtk_text_buffer_apply_tag_by_name(
4314 buffer, "quote1", &startquote, &endquote);
4315 gtk_text_buffer_remove_tag_by_name(
4316 buffer, "quote0", &startquote, &endquote);
4317 gtk_text_buffer_remove_tag_by_name(
4318 buffer, "quote2", &startquote, &endquote);
4323 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4324 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4325 gtk_text_buffer_apply_tag_by_name(
4326 buffer, "quote2", &startquote, &endquote);
4327 gtk_text_buffer_remove_tag_by_name(
4328 buffer, "quote0", &startquote, &endquote);
4329 gtk_text_buffer_remove_tag_by_name(
4330 buffer, "quote1", &startquote, &endquote);
4336 } else if (noq_offset != -1) {
4337 GtkTextIter startnoquote, endnoquote;
4338 gtk_text_buffer_get_iter_at_offset(
4339 buffer, &startnoquote, noq_offset);
4342 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4343 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4344 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4345 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4346 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4347 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4348 gtk_text_buffer_remove_tag_by_name(
4349 buffer, "quote0", &startnoquote, &endnoquote);
4350 gtk_text_buffer_remove_tag_by_name(
4351 buffer, "quote1", &startnoquote, &endnoquote);
4352 gtk_text_buffer_remove_tag_by_name(
4353 buffer, "quote2", &startnoquote, &endnoquote);
4359 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4360 GtkTextIter nouri_start_iter, nouri_end_iter;
4361 gtk_text_buffer_get_iter_at_offset(
4362 buffer, &nouri_start_iter, nouri_start);
4363 gtk_text_buffer_get_iter_at_offset(
4364 buffer, &nouri_end_iter, nouri_stop);
4365 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4366 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4367 gtk_text_buffer_remove_tag_by_name(
4368 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4369 modified_before_remove = modified;
4374 if (uri_start >= 0 && uri_stop > 0) {
4375 GtkTextIter uri_start_iter, uri_end_iter, back;
4376 gtk_text_buffer_get_iter_at_offset(
4377 buffer, &uri_start_iter, uri_start);
4378 gtk_text_buffer_get_iter_at_offset(
4379 buffer, &uri_end_iter, uri_stop);
4380 back = uri_end_iter;
4381 gtk_text_iter_backward_char(&back);
4382 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4383 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4384 gtk_text_buffer_apply_tag_by_name(
4385 buffer, "link", &uri_start_iter, &uri_end_iter);
4387 if (removed && !modified_before_remove) {
4393 // debug_print("not modified, out after %d lines\n", lines);
4397 // debug_print("modified, out after %d lines\n", lines);
4399 g_free(itemized_chars);
4402 undo_wrapping(compose->undostruct, FALSE);
4403 compose->autowrap = prev_autowrap;
4408 void compose_action_cb(void *data)
4410 Compose *compose = (Compose *)data;
4411 compose_wrap_all(compose);
4414 static void compose_wrap_all(Compose *compose)
4416 compose_wrap_all_full(compose, FALSE);
4419 static void compose_wrap_all_full(Compose *compose, gboolean force)
4421 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4422 GtkTextBuffer *buffer;
4424 gboolean modified = TRUE;
4426 buffer = gtk_text_view_get_buffer(text);
4428 gtk_text_buffer_get_start_iter(buffer, &iter);
4429 while (!gtk_text_iter_is_end(&iter) && modified)
4430 modified = compose_beautify_paragraph(compose, &iter, force);
4434 static void compose_set_title(Compose *compose)
4440 edited = compose->modified ? _(" [Edited]") : "";
4442 subject = gtk_editable_get_chars(
4443 GTK_EDITABLE(compose->subject_entry), 0, -1);
4445 #ifndef GENERIC_UMPC
4446 if (subject && strlen(subject))
4447 str = g_strdup_printf(_("%s - Compose message%s"),
4450 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4452 str = g_strdup(_("Compose message"));
4455 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4461 * compose_current_mail_account:
4463 * Find a current mail account (the currently selected account, or the
4464 * default account, if a news account is currently selected). If a
4465 * mail account cannot be found, display an error message.
4467 * Return value: Mail account, or NULL if not found.
4469 static PrefsAccount *
4470 compose_current_mail_account(void)
4474 if (cur_account && cur_account->protocol != A_NNTP)
4477 ac = account_get_default();
4478 if (!ac || ac->protocol == A_NNTP) {
4479 alertpanel_error(_("Account for sending mail is not specified.\n"
4480 "Please select a mail account before sending."));
4487 #define QUOTE_IF_REQUIRED(out, str) \
4489 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4493 len = strlen(str) + 3; \
4494 if ((__tmp = alloca(len)) == NULL) { \
4495 g_warning("can't allocate memory\n"); \
4496 g_string_free(header, TRUE); \
4499 g_snprintf(__tmp, len, "\"%s\"", str); \
4504 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4505 g_warning("can't allocate memory\n"); \
4506 g_string_free(header, TRUE); \
4509 strcpy(__tmp, str); \
4515 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4517 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4521 len = strlen(str) + 3; \
4522 if ((__tmp = alloca(len)) == NULL) { \
4523 g_warning("can't allocate memory\n"); \
4526 g_snprintf(__tmp, len, "\"%s\"", str); \
4531 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4532 g_warning("can't allocate memory\n"); \
4535 strcpy(__tmp, str); \
4541 static void compose_select_account(Compose *compose, PrefsAccount *account,
4544 gchar *from = NULL, *header;
4545 ComposeHeaderEntry *header_entry;
4547 cm_return_if_fail(account != NULL);
4549 compose->account = account;
4551 if (account->name && *account->name) {
4553 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4554 from = g_strdup_printf("%s <%s>",
4555 buf, account->address);
4556 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4558 from = g_strdup_printf("<%s>",
4560 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4565 compose_set_title(compose);
4567 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4568 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4571 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4572 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4574 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4576 activate_privacy_system(compose, account, FALSE);
4578 if (!init && compose->mode != COMPOSE_REDIRECT) {
4579 undo_block(compose->undostruct);
4580 compose_insert_sig(compose, TRUE);
4581 undo_unblock(compose->undostruct);
4584 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4585 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4587 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4588 if (account->protocol == A_NNTP) {
4589 if (!strcmp(header, _("To:")))
4590 combobox_select_by_text(
4591 GTK_COMBO_BOX(header_entry->combo),
4594 if (!strcmp(header, _("Newsgroups:")))
4595 combobox_select_by_text(
4596 GTK_COMBO_BOX(header_entry->combo),
4604 /* use account's dict info if set */
4605 if (compose->gtkaspell) {
4606 if (account->enable_default_dictionary)
4607 gtkaspell_change_dict(compose->gtkaspell,
4608 account->default_dictionary, FALSE);
4609 if (account->enable_default_alt_dictionary)
4610 gtkaspell_change_alt_dict(compose->gtkaspell,
4611 account->default_alt_dictionary);
4612 if (account->enable_default_dictionary
4613 || account->enable_default_alt_dictionary)
4614 compose_spell_menu_changed(compose);
4619 gboolean compose_check_for_valid_recipient(Compose *compose) {
4620 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4621 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4622 gboolean recipient_found = FALSE;
4626 /* free to and newsgroup list */
4627 slist_free_strings(compose->to_list);
4628 g_slist_free(compose->to_list);
4629 compose->to_list = NULL;
4631 slist_free_strings(compose->newsgroup_list);
4632 g_slist_free(compose->newsgroup_list);
4633 compose->newsgroup_list = NULL;
4635 /* search header entries for to and newsgroup entries */
4636 for (list = compose->header_list; list; list = list->next) {
4639 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4640 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4643 if (entry[0] != '\0') {
4644 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4645 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4646 compose->to_list = address_list_append(compose->to_list, entry);
4647 recipient_found = TRUE;
4650 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4651 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4652 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4653 recipient_found = TRUE;
4660 return recipient_found;
4663 static gboolean compose_check_for_set_recipients(Compose *compose)
4665 if (compose->account->set_autocc && compose->account->auto_cc) {
4666 gboolean found_other = FALSE;
4668 /* search header entries for to and newsgroup entries */
4669 for (list = compose->header_list; list; list = list->next) {
4672 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4673 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4676 if (strcmp(entry, compose->account->auto_cc)
4677 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4687 if (compose->batch) {
4688 gtk_widget_show_all(compose->window);
4690 aval = alertpanel(_("Send"),
4691 _("The only recipient is the default CC address. Send anyway?"),
4692 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4693 if (aval != G_ALERTALTERNATE)
4697 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4698 gboolean found_other = FALSE;
4700 /* search header entries for to and newsgroup entries */
4701 for (list = compose->header_list; list; list = list->next) {
4704 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4705 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4708 if (strcmp(entry, compose->account->auto_bcc)
4709 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4719 if (compose->batch) {
4720 gtk_widget_show_all(compose->window);
4722 aval = alertpanel(_("Send"),
4723 _("The only recipient is the default BCC address. Send anyway?"),
4724 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4725 if (aval != G_ALERTALTERNATE)
4732 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4736 if (compose_check_for_valid_recipient(compose) == FALSE) {
4737 if (compose->batch) {
4738 gtk_widget_show_all(compose->window);
4740 alertpanel_error(_("Recipient is not specified."));
4744 if (compose_check_for_set_recipients(compose) == FALSE) {
4748 if (!compose->batch) {
4749 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4750 if (*str == '\0' && check_everything == TRUE &&
4751 compose->mode != COMPOSE_REDIRECT) {
4753 gchar *button_label;
4756 if (compose->sending)
4757 button_label = _("+_Send");
4759 button_label = _("+_Queue");
4760 message = g_strdup_printf(_("Subject is empty. %s"),
4761 compose->sending?_("Send it anyway?"):
4762 _("Queue it anyway?"));
4764 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4765 GTK_STOCK_CANCEL, button_label, NULL);
4767 if (aval != G_ALERTALTERNATE)
4772 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4778 gint compose_send(Compose *compose)
4781 FolderItem *folder = NULL;
4783 gchar *msgpath = NULL;
4784 gboolean discard_window = FALSE;
4785 gchar *errstr = NULL;
4786 gchar *tmsgid = NULL;
4787 MainWindow *mainwin = mainwindow_get_mainwindow();
4788 gboolean queued_removed = FALSE;
4790 if (prefs_common.send_dialog_invisible
4791 || compose->batch == TRUE)
4792 discard_window = TRUE;
4794 compose_allow_user_actions (compose, FALSE);
4795 compose->sending = TRUE;
4797 if (compose_check_entries(compose, TRUE) == FALSE) {
4798 if (compose->batch) {
4799 gtk_widget_show_all(compose->window);
4805 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4808 if (compose->batch) {
4809 gtk_widget_show_all(compose->window);
4812 alertpanel_error(_("Could not queue message for sending:\n\n"
4813 "Charset conversion failed."));
4814 } else if (val == -5) {
4815 alertpanel_error(_("Could not queue message for sending:\n\n"
4816 "Couldn't get recipient encryption key."));
4817 } else if (val == -6) {
4819 } else if (val == -3) {
4820 if (privacy_peek_error())
4821 alertpanel_error(_("Could not queue message for sending:\n\n"
4822 "Signature failed: %s"), privacy_get_error());
4823 } else if (val == -2 && errno != 0) {
4824 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4826 alertpanel_error(_("Could not queue message for sending."));
4831 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4832 if (discard_window) {
4833 compose->sending = FALSE;
4834 compose_close(compose);
4835 /* No more compose access in the normal codepath
4836 * after this point! */
4841 alertpanel_error(_("The message was queued but could not be "
4842 "sent.\nUse \"Send queued messages\" from "
4843 "the main window to retry."));
4844 if (!discard_window) {
4851 if (msgpath == NULL) {
4852 msgpath = folder_item_fetch_msg(folder, msgnum);
4853 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4856 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4857 claws_unlink(msgpath);
4860 if (!discard_window) {
4862 if (!queued_removed)
4863 folder_item_remove_msg(folder, msgnum);
4864 folder_item_scan(folder);
4866 /* make sure we delete that */
4867 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4869 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4870 folder_item_remove_msg(folder, tmp->msgnum);
4871 procmsg_msginfo_free(tmp);
4878 if (!queued_removed)
4879 folder_item_remove_msg(folder, msgnum);
4880 folder_item_scan(folder);
4882 /* make sure we delete that */
4883 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4885 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4886 folder_item_remove_msg(folder, tmp->msgnum);
4887 procmsg_msginfo_free(tmp);
4890 if (!discard_window) {
4891 compose->sending = FALSE;
4892 compose_allow_user_actions (compose, TRUE);
4893 compose_close(compose);
4897 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4898 "the main window to retry."), errstr);
4901 alertpanel_error_log(_("The message was queued but could not be "
4902 "sent.\nUse \"Send queued messages\" from "
4903 "the main window to retry."));
4905 if (!discard_window) {
4914 toolbar_main_set_sensitive(mainwin);
4915 main_window_set_menu_sensitive(mainwin);
4921 compose_allow_user_actions (compose, TRUE);
4922 compose->sending = FALSE;
4923 compose->modified = TRUE;
4924 toolbar_main_set_sensitive(mainwin);
4925 main_window_set_menu_sensitive(mainwin);
4930 static gboolean compose_use_attach(Compose *compose)
4932 GtkTreeModel *model = gtk_tree_view_get_model
4933 (GTK_TREE_VIEW(compose->attach_clist));
4934 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4937 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4940 gchar buf[BUFFSIZE];
4942 gboolean first_to_address;
4943 gboolean first_cc_address;
4945 ComposeHeaderEntry *headerentry;
4946 const gchar *headerentryname;
4947 const gchar *cc_hdr;
4948 const gchar *to_hdr;
4949 gboolean err = FALSE;
4951 debug_print("Writing redirect header\n");
4953 cc_hdr = prefs_common_translated_header_name("Cc:");
4954 to_hdr = prefs_common_translated_header_name("To:");
4956 first_to_address = TRUE;
4957 for (list = compose->header_list; list; list = list->next) {
4958 headerentry = ((ComposeHeaderEntry *)list->data);
4959 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4961 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4962 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4963 Xstrdup_a(str, entstr, return -1);
4965 if (str[0] != '\0') {
4966 compose_convert_header
4967 (compose, buf, sizeof(buf), str,
4968 strlen("Resent-To") + 2, TRUE);
4970 if (first_to_address) {
4971 err |= (fprintf(fp, "Resent-To: ") < 0);
4972 first_to_address = FALSE;
4974 err |= (fprintf(fp, ",") < 0);
4976 err |= (fprintf(fp, "%s", buf) < 0);
4980 if (!first_to_address) {
4981 err |= (fprintf(fp, "\n") < 0);
4984 first_cc_address = TRUE;
4985 for (list = compose->header_list; list; list = list->next) {
4986 headerentry = ((ComposeHeaderEntry *)list->data);
4987 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4989 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4990 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4991 Xstrdup_a(str, strg, return -1);
4993 if (str[0] != '\0') {
4994 compose_convert_header
4995 (compose, buf, sizeof(buf), str,
4996 strlen("Resent-Cc") + 2, TRUE);
4998 if (first_cc_address) {
4999 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5000 first_cc_address = FALSE;
5002 err |= (fprintf(fp, ",") < 0);
5004 err |= (fprintf(fp, "%s", buf) < 0);
5008 if (!first_cc_address) {
5009 err |= (fprintf(fp, "\n") < 0);
5012 return (err ? -1:0);
5015 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5017 gchar buf[BUFFSIZE];
5019 const gchar *entstr;
5020 /* struct utsname utsbuf; */
5021 gboolean err = FALSE;
5023 cm_return_val_if_fail(fp != NULL, -1);
5024 cm_return_val_if_fail(compose->account != NULL, -1);
5025 cm_return_val_if_fail(compose->account->address != NULL, -1);
5028 get_rfc822_date(buf, sizeof(buf));
5029 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5032 if (compose->account->name && *compose->account->name) {
5033 compose_convert_header
5034 (compose, buf, sizeof(buf), compose->account->name,
5035 strlen("From: "), TRUE);
5036 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5037 buf, compose->account->address) < 0);
5039 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5042 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5043 if (*entstr != '\0') {
5044 Xstrdup_a(str, entstr, return -1);
5047 compose_convert_header(compose, buf, sizeof(buf), str,
5048 strlen("Subject: "), FALSE);
5049 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5053 /* Resent-Message-ID */
5054 if (compose->account->set_domain && compose->account->domain) {
5055 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5056 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5057 g_snprintf(buf, sizeof(buf), "%s",
5058 strchr(compose->account->address, '@') ?
5059 strchr(compose->account->address, '@')+1 :
5060 compose->account->address);
5062 g_snprintf(buf, sizeof(buf), "%s", "");
5065 if (compose->account->gen_msgid) {
5066 generate_msgid(buf, sizeof(buf));
5067 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5068 compose->msgid = g_strdup(buf);
5070 compose->msgid = NULL;
5073 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5076 /* separator between header and body */
5077 err |= (fputs("\n", fp) == EOF);
5079 return (err ? -1:0);
5082 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5086 gchar buf[BUFFSIZE];
5088 gboolean skip = FALSE;
5089 gboolean err = FALSE;
5090 gchar *not_included[]={
5091 "Return-Path:", "Delivered-To:", "Received:",
5092 "Subject:", "X-UIDL:", "AF:",
5093 "NF:", "PS:", "SRH:",
5094 "SFN:", "DSR:", "MID:",
5095 "CFG:", "PT:", "S:",
5096 "RQ:", "SSV:", "NSV:",
5097 "SSH:", "R:", "MAID:",
5098 "NAID:", "RMID:", "FMID:",
5099 "SCF:", "RRCPT:", "NG:",
5100 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5101 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5102 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5103 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5104 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5107 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5108 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5112 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5114 for (i = 0; not_included[i] != NULL; i++) {
5115 if (g_ascii_strncasecmp(buf, not_included[i],
5116 strlen(not_included[i])) == 0) {
5123 if (fputs(buf, fdest) == -1)
5126 if (!prefs_common.redirect_keep_from) {
5127 if (g_ascii_strncasecmp(buf, "From:",
5128 strlen("From:")) == 0) {
5129 err |= (fputs(" (by way of ", fdest) == EOF);
5130 if (compose->account->name
5131 && *compose->account->name) {
5132 compose_convert_header
5133 (compose, buf, sizeof(buf),
5134 compose->account->name,
5137 err |= (fprintf(fdest, "%s <%s>",
5139 compose->account->address) < 0);
5141 err |= (fprintf(fdest, "%s",
5142 compose->account->address) < 0);
5143 err |= (fputs(")", fdest) == EOF);
5147 if (fputs("\n", fdest) == -1)
5154 if (compose_redirect_write_headers(compose, fdest))
5157 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5158 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5171 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5173 GtkTextBuffer *buffer;
5174 GtkTextIter start, end;
5177 const gchar *out_codeset;
5178 EncodingType encoding = ENC_UNKNOWN;
5179 MimeInfo *mimemsg, *mimetext;
5181 const gchar *src_codeset = CS_INTERNAL;
5182 gchar *from_addr = NULL;
5183 gchar *from_name = NULL;
5185 if (action == COMPOSE_WRITE_FOR_SEND)
5186 attach_parts = TRUE;
5188 /* create message MimeInfo */
5189 mimemsg = procmime_mimeinfo_new();
5190 mimemsg->type = MIMETYPE_MESSAGE;
5191 mimemsg->subtype = g_strdup("rfc822");
5192 mimemsg->content = MIMECONTENT_MEM;
5193 mimemsg->tmp = TRUE; /* must free content later */
5194 mimemsg->data.mem = compose_get_header(compose);
5196 /* Create text part MimeInfo */
5197 /* get all composed text */
5198 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5199 gtk_text_buffer_get_start_iter(buffer, &start);
5200 gtk_text_buffer_get_end_iter(buffer, &end);
5201 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5203 out_codeset = conv_get_charset_str(compose->out_encoding);
5205 if (!out_codeset && is_ascii_str(chars)) {
5206 out_codeset = CS_US_ASCII;
5207 } else if (prefs_common.outgoing_fallback_to_ascii &&
5208 is_ascii_str(chars)) {
5209 out_codeset = CS_US_ASCII;
5210 encoding = ENC_7BIT;
5214 gchar *test_conv_global_out = NULL;
5215 gchar *test_conv_reply = NULL;
5217 /* automatic mode. be automatic. */
5218 codeconv_set_strict(TRUE);
5220 out_codeset = conv_get_outgoing_charset_str();
5222 debug_print("trying to convert to %s\n", out_codeset);
5223 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5226 if (!test_conv_global_out && compose->orig_charset
5227 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5228 out_codeset = compose->orig_charset;
5229 debug_print("failure; trying to convert to %s\n", out_codeset);
5230 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5233 if (!test_conv_global_out && !test_conv_reply) {
5235 out_codeset = CS_INTERNAL;
5236 debug_print("failure; finally using %s\n", out_codeset);
5238 g_free(test_conv_global_out);
5239 g_free(test_conv_reply);
5240 codeconv_set_strict(FALSE);
5243 if (encoding == ENC_UNKNOWN) {
5244 if (prefs_common.encoding_method == CTE_BASE64)
5245 encoding = ENC_BASE64;
5246 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5247 encoding = ENC_QUOTED_PRINTABLE;
5248 else if (prefs_common.encoding_method == CTE_8BIT)
5249 encoding = ENC_8BIT;
5251 encoding = procmime_get_encoding_for_charset(out_codeset);
5254 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5255 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5257 if (action == COMPOSE_WRITE_FOR_SEND) {
5258 codeconv_set_strict(TRUE);
5259 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5260 codeconv_set_strict(FALSE);
5266 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5267 "to the specified %s charset.\n"
5268 "Send it as %s?"), out_codeset, src_codeset);
5269 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5270 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5273 if (aval != G_ALERTALTERNATE) {
5278 out_codeset = src_codeset;
5284 out_codeset = src_codeset;
5289 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5290 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5291 strstr(buf, "\nFrom ") != NULL) {
5292 encoding = ENC_QUOTED_PRINTABLE;
5296 mimetext = procmime_mimeinfo_new();
5297 mimetext->content = MIMECONTENT_MEM;
5298 mimetext->tmp = TRUE; /* must free content later */
5299 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5300 * and free the data, which we need later. */
5301 mimetext->data.mem = g_strdup(buf);
5302 mimetext->type = MIMETYPE_TEXT;
5303 mimetext->subtype = g_strdup("plain");
5304 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5305 g_strdup(out_codeset));
5307 /* protect trailing spaces when signing message */
5308 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5309 privacy_system_can_sign(compose->privacy_system)) {
5310 encoding = ENC_QUOTED_PRINTABLE;
5313 debug_print("main text: %zd bytes encoded as %s in %d\n",
5314 strlen(buf), out_codeset, encoding);
5316 /* check for line length limit */
5317 if (action == COMPOSE_WRITE_FOR_SEND &&
5318 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5319 check_line_length(buf, 1000, &line) < 0) {
5323 msg = g_strdup_printf
5324 (_("Line %d exceeds the line length limit (998 bytes).\n"
5325 "The contents of the message might be broken on the way to the delivery.\n"
5327 "Send it anyway?"), line + 1);
5328 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5330 if (aval != G_ALERTALTERNATE) {
5336 if (encoding != ENC_UNKNOWN)
5337 procmime_encode_content(mimetext, encoding);
5339 /* append attachment parts */
5340 if (compose_use_attach(compose) && attach_parts) {
5341 MimeInfo *mimempart;
5342 gchar *boundary = NULL;
5343 mimempart = procmime_mimeinfo_new();
5344 mimempart->content = MIMECONTENT_EMPTY;
5345 mimempart->type = MIMETYPE_MULTIPART;
5346 mimempart->subtype = g_strdup("mixed");
5350 boundary = generate_mime_boundary(NULL);
5351 } while (strstr(buf, boundary) != NULL);
5353 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5356 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5358 g_node_append(mimempart->node, mimetext->node);
5359 g_node_append(mimemsg->node, mimempart->node);
5361 compose_add_attachments(compose, mimempart);
5363 g_node_append(mimemsg->node, mimetext->node);
5367 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5368 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5369 /* extract name and address */
5370 if (strstr(spec, " <") && strstr(spec, ">")) {
5371 from_addr = g_strdup(strrchr(spec, '<')+1);
5372 *(strrchr(from_addr, '>')) = '\0';
5373 from_name = g_strdup(spec);
5374 *(strrchr(from_name, '<')) = '\0';
5381 /* sign message if sending */
5382 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5383 privacy_system_can_sign(compose->privacy_system))
5384 if (!privacy_sign(compose->privacy_system, mimemsg,
5385 compose->account, from_addr)) {
5392 procmime_write_mimeinfo(mimemsg, fp);
5394 procmime_mimeinfo_free_all(mimemsg);
5399 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5401 GtkTextBuffer *buffer;
5402 GtkTextIter start, end;
5407 if ((fp = g_fopen(file, "wb")) == NULL) {
5408 FILE_OP_ERROR(file, "fopen");
5412 /* chmod for security */
5413 if (change_file_mode_rw(fp, file) < 0) {
5414 FILE_OP_ERROR(file, "chmod");
5415 g_warning("can't change file mode\n");
5418 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5419 gtk_text_buffer_get_start_iter(buffer, &start);
5420 gtk_text_buffer_get_end_iter(buffer, &end);
5421 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5423 chars = conv_codeset_strdup
5424 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5427 if (!chars) return -1;
5430 len = strlen(chars);
5431 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5432 FILE_OP_ERROR(file, "fwrite");
5441 if (fclose(fp) == EOF) {
5442 FILE_OP_ERROR(file, "fclose");
5449 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5452 MsgInfo *msginfo = compose->targetinfo;
5454 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5455 if (!msginfo) return -1;
5457 if (!force && MSG_IS_LOCKED(msginfo->flags))
5460 item = msginfo->folder;
5461 cm_return_val_if_fail(item != NULL, -1);
5463 if (procmsg_msg_exist(msginfo) &&
5464 (folder_has_parent_of_type(item, F_QUEUE) ||
5465 folder_has_parent_of_type(item, F_DRAFT)
5466 || msginfo == compose->autosaved_draft)) {
5467 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5468 g_warning("can't remove the old message\n");
5471 debug_print("removed reedit target %d\n", msginfo->msgnum);
5478 static void compose_remove_draft(Compose *compose)
5481 MsgInfo *msginfo = compose->targetinfo;
5482 drafts = account_get_special_folder(compose->account, F_DRAFT);
5484 if (procmsg_msg_exist(msginfo)) {
5485 folder_item_remove_msg(drafts, msginfo->msgnum);
5490 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5491 gboolean remove_reedit_target)
5493 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5496 static gboolean compose_warn_encryption(Compose *compose)
5498 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5499 AlertValue val = G_ALERTALTERNATE;
5501 if (warning == NULL)
5504 val = alertpanel_full(_("Encryption warning"), warning,
5505 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5506 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5507 if (val & G_ALERTDISABLE) {
5508 val &= ~G_ALERTDISABLE;
5509 if (val == G_ALERTALTERNATE)
5510 privacy_inhibit_encrypt_warning(compose->privacy_system,
5514 if (val == G_ALERTALTERNATE) {
5521 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5522 gchar **msgpath, gboolean check_subject,
5523 gboolean remove_reedit_target)
5530 static gboolean lock = FALSE;
5531 PrefsAccount *mailac = NULL, *newsac = NULL;
5532 gboolean err = FALSE;
5534 debug_print("queueing message...\n");
5535 cm_return_val_if_fail(compose->account != NULL, -1);
5539 if (compose_check_entries(compose, check_subject) == FALSE) {
5541 if (compose->batch) {
5542 gtk_widget_show_all(compose->window);
5547 if (!compose->to_list && !compose->newsgroup_list) {
5548 g_warning("can't get recipient list.");
5553 if (compose->to_list) {
5554 if (compose->account->protocol != A_NNTP)
5555 mailac = compose->account;
5556 else if (cur_account && cur_account->protocol != A_NNTP)
5557 mailac = cur_account;
5558 else if (!(mailac = compose_current_mail_account())) {
5560 alertpanel_error(_("No account for sending mails available!"));
5565 if (compose->newsgroup_list) {
5566 if (compose->account->protocol == A_NNTP)
5567 newsac = compose->account;
5570 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5575 /* write queue header */
5576 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5577 G_DIR_SEPARATOR, compose, (guint) rand());
5578 debug_print("queuing to %s\n", tmp);
5579 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5580 FILE_OP_ERROR(tmp, "fopen");
5586 if (change_file_mode_rw(fp, tmp) < 0) {
5587 FILE_OP_ERROR(tmp, "chmod");
5588 g_warning("can't change file mode\n");
5591 /* queueing variables */
5592 err |= (fprintf(fp, "AF:\n") < 0);
5593 err |= (fprintf(fp, "NF:0\n") < 0);
5594 err |= (fprintf(fp, "PS:10\n") < 0);
5595 err |= (fprintf(fp, "SRH:1\n") < 0);
5596 err |= (fprintf(fp, "SFN:\n") < 0);
5597 err |= (fprintf(fp, "DSR:\n") < 0);
5599 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5601 err |= (fprintf(fp, "MID:\n") < 0);
5602 err |= (fprintf(fp, "CFG:\n") < 0);
5603 err |= (fprintf(fp, "PT:0\n") < 0);
5604 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5605 err |= (fprintf(fp, "RQ:\n") < 0);
5607 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5609 err |= (fprintf(fp, "SSV:\n") < 0);
5611 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5613 err |= (fprintf(fp, "NSV:\n") < 0);
5614 err |= (fprintf(fp, "SSH:\n") < 0);
5615 /* write recepient list */
5616 if (compose->to_list) {
5617 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5618 for (cur = compose->to_list->next; cur != NULL;
5620 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5621 err |= (fprintf(fp, "\n") < 0);
5623 /* write newsgroup list */
5624 if (compose->newsgroup_list) {
5625 err |= (fprintf(fp, "NG:") < 0);
5626 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5627 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5628 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5629 err |= (fprintf(fp, "\n") < 0);
5631 /* Sylpheed account IDs */
5633 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5635 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5638 if (compose->privacy_system != NULL) {
5639 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5640 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5641 if (compose->use_encryption) {
5643 if (!compose_warn_encryption(compose)) {
5650 if (mailac && mailac->encrypt_to_self) {
5651 GSList *tmp_list = g_slist_copy(compose->to_list);
5652 tmp_list = g_slist_append(tmp_list, compose->account->address);
5653 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5654 g_slist_free(tmp_list);
5656 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5658 if (encdata != NULL) {
5659 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5660 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5661 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5663 } /* else we finally dont want to encrypt */
5665 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5666 /* and if encdata was null, it means there's been a problem in
5678 /* Save copy folder */
5679 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5680 gchar *savefolderid;
5682 savefolderid = compose_get_save_to(compose);
5683 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5684 g_free(savefolderid);
5686 /* Save copy folder */
5687 if (compose->return_receipt) {
5688 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5690 /* Message-ID of message replying to */
5691 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5694 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5695 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5698 /* Message-ID of message forwarding to */
5699 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5702 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5703 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5707 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5708 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5710 /* end of headers */
5711 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5713 if (compose->redirect_filename != NULL) {
5714 if (compose_redirect_write_to_file(compose, fp) < 0) {
5723 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5728 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5732 g_warning("failed to write queue message\n");
5739 if (fclose(fp) == EOF) {
5740 FILE_OP_ERROR(tmp, "fclose");
5747 if (item && *item) {
5750 queue = account_get_special_folder(compose->account, F_QUEUE);
5753 g_warning("can't find queue folder\n");
5759 folder_item_scan(queue);
5760 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5761 g_warning("can't queue the message\n");
5768 if (msgpath == NULL) {
5774 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5775 compose_remove_reedit_target(compose, FALSE);
5778 if ((msgnum != NULL) && (item != NULL)) {
5786 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5789 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5791 struct stat statbuf;
5792 gchar *type, *subtype;
5793 GtkTreeModel *model;
5796 model = gtk_tree_view_get_model(tree_view);
5798 if (!gtk_tree_model_get_iter_first(model, &iter))
5801 gtk_tree_model_get(model, &iter,
5805 mimepart = procmime_mimeinfo_new();
5806 mimepart->content = MIMECONTENT_FILE;
5807 mimepart->data.filename = g_strdup(ainfo->file);
5808 mimepart->tmp = FALSE; /* or we destroy our attachment */
5809 mimepart->offset = 0;
5811 g_stat(ainfo->file, &statbuf);
5812 mimepart->length = statbuf.st_size;
5814 type = g_strdup(ainfo->content_type);
5816 if (!strchr(type, '/')) {
5818 type = g_strdup("application/octet-stream");
5821 subtype = strchr(type, '/') + 1;
5822 *(subtype - 1) = '\0';
5823 mimepart->type = procmime_get_media_type(type);
5824 mimepart->subtype = g_strdup(subtype);
5827 if (mimepart->type == MIMETYPE_MESSAGE &&
5828 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5829 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5832 if (mimepart->type == MIMETYPE_APPLICATION &&
5833 !strcmp2(mimepart->subtype, "octet-stream"))
5834 g_hash_table_insert(mimepart->typeparameters,
5835 g_strdup("name"), g_strdup(ainfo->name));
5836 g_hash_table_insert(mimepart->dispositionparameters,
5837 g_strdup("filename"), g_strdup(ainfo->name));
5838 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5842 if (compose->use_signing) {
5843 if (ainfo->encoding == ENC_7BIT)
5844 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5845 else if (ainfo->encoding == ENC_8BIT)
5846 ainfo->encoding = ENC_BASE64;
5849 procmime_encode_content(mimepart, ainfo->encoding);
5851 g_node_append(parent->node, mimepart->node);
5852 } while (gtk_tree_model_iter_next(model, &iter));
5855 #define IS_IN_CUSTOM_HEADER(header) \
5856 (compose->account->add_customhdr && \
5857 custom_header_find(compose->account->customhdr_list, header) != NULL)
5859 static void compose_add_headerfield_from_headerlist(Compose *compose,
5861 const gchar *fieldname,
5862 const gchar *seperator)
5864 gchar *str, *fieldname_w_colon;
5865 gboolean add_field = FALSE;
5867 ComposeHeaderEntry *headerentry;
5868 const gchar *headerentryname;
5869 const gchar *trans_fieldname;
5872 if (IS_IN_CUSTOM_HEADER(fieldname))
5875 debug_print("Adding %s-fields\n", fieldname);
5877 fieldstr = g_string_sized_new(64);
5879 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5880 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5882 for (list = compose->header_list; list; list = list->next) {
5883 headerentry = ((ComposeHeaderEntry *)list->data);
5884 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5886 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5887 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5889 if (str[0] != '\0') {
5891 g_string_append(fieldstr, seperator);
5892 g_string_append(fieldstr, str);
5901 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5902 compose_convert_header
5903 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5904 strlen(fieldname) + 2, TRUE);
5905 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5909 g_free(fieldname_w_colon);
5910 g_string_free(fieldstr, TRUE);
5915 static gchar *compose_get_header(Compose *compose)
5917 gchar buf[BUFFSIZE];
5918 const gchar *entry_str;
5922 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5924 gchar *from_name = NULL, *from_address = NULL;
5927 cm_return_val_if_fail(compose->account != NULL, NULL);
5928 cm_return_val_if_fail(compose->account->address != NULL, NULL);
5930 header = g_string_sized_new(64);
5933 get_rfc822_date(buf, sizeof(buf));
5934 g_string_append_printf(header, "Date: %s\n", buf);
5938 if (compose->account->name && *compose->account->name) {
5940 QUOTE_IF_REQUIRED(buf, compose->account->name);
5941 tmp = g_strdup_printf("%s <%s>",
5942 buf, compose->account->address);
5944 tmp = g_strdup_printf("%s",
5945 compose->account->address);
5947 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5948 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5950 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5951 from_address = g_strdup(compose->account->address);
5953 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5954 /* extract name and address */
5955 if (strstr(spec, " <") && strstr(spec, ">")) {
5956 from_address = g_strdup(strrchr(spec, '<')+1);
5957 *(strrchr(from_address, '>')) = '\0';
5958 from_name = g_strdup(spec);
5959 *(strrchr(from_name, '<')) = '\0';
5962 from_address = g_strdup(spec);
5969 if (from_name && *from_name) {
5970 compose_convert_header
5971 (compose, buf, sizeof(buf), from_name,
5972 strlen("From: "), TRUE);
5973 QUOTE_IF_REQUIRED(name, buf);
5975 g_string_append_printf(header, "From: %s <%s>\n",
5976 name, from_address);
5978 g_string_append_printf(header, "From: %s\n", from_address);
5981 g_free(from_address);
5984 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5987 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5990 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5993 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5996 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5998 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6001 compose_convert_header(compose, buf, sizeof(buf), str,
6002 strlen("Subject: "), FALSE);
6003 g_string_append_printf(header, "Subject: %s\n", buf);
6009 if (compose->account->set_domain && compose->account->domain) {
6010 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6011 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6012 g_snprintf(buf, sizeof(buf), "%s",
6013 strchr(compose->account->address, '@') ?
6014 strchr(compose->account->address, '@')+1 :
6015 compose->account->address);
6017 g_snprintf(buf, sizeof(buf), "%s", "");
6020 if (compose->account->gen_msgid) {
6021 generate_msgid(buf, sizeof(buf));
6022 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6023 compose->msgid = g_strdup(buf);
6025 compose->msgid = NULL;
6028 if (compose->remove_references == FALSE) {
6030 if (compose->inreplyto && compose->to_list)
6031 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6034 if (compose->references)
6035 g_string_append_printf(header, "References: %s\n", compose->references);
6039 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6042 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6045 if (compose->account->organization &&
6046 strlen(compose->account->organization) &&
6047 !IS_IN_CUSTOM_HEADER("Organization")) {
6048 compose_convert_header(compose, buf, sizeof(buf),
6049 compose->account->organization,
6050 strlen("Organization: "), FALSE);
6051 g_string_append_printf(header, "Organization: %s\n", buf);
6054 /* Program version and system info */
6055 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6056 !compose->newsgroup_list) {
6057 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6059 gtk_major_version, gtk_minor_version, gtk_micro_version,
6062 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6063 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6065 gtk_major_version, gtk_minor_version, gtk_micro_version,
6069 /* custom headers */
6070 if (compose->account->add_customhdr) {
6073 for (cur = compose->account->customhdr_list; cur != NULL;
6075 CustomHeader *chdr = (CustomHeader *)cur->data;
6077 if (custom_header_is_allowed(chdr->name)) {
6078 compose_convert_header
6079 (compose, buf, sizeof(buf),
6080 chdr->value ? chdr->value : "",
6081 strlen(chdr->name) + 2, FALSE);
6082 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6087 /* Automatic Faces and X-Faces */
6088 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6089 g_string_append_printf(header, "X-Face: %s\n", buf);
6091 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6092 g_string_append_printf(header, "X-Face: %s\n", buf);
6094 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6095 g_string_append_printf(header, "Face: %s\n", buf);
6097 else if (get_default_face (buf, sizeof(buf)) == 0) {
6098 g_string_append_printf(header, "Face: %s\n", buf);
6102 switch (compose->priority) {
6103 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6104 "X-Priority: 1 (Highest)\n");
6106 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6107 "X-Priority: 2 (High)\n");
6109 case PRIORITY_NORMAL: break;
6110 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6111 "X-Priority: 4 (Low)\n");
6113 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6114 "X-Priority: 5 (Lowest)\n");
6116 default: debug_print("compose: priority unknown : %d\n",
6120 /* Request Return Receipt */
6121 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6122 if (compose->return_receipt) {
6123 if (compose->account->name
6124 && *compose->account->name) {
6125 compose_convert_header(compose, buf, sizeof(buf),
6126 compose->account->name,
6127 strlen("Disposition-Notification-To: "),
6129 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6131 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6135 /* get special headers */
6136 for (list = compose->header_list; list; list = list->next) {
6137 ComposeHeaderEntry *headerentry;
6140 gchar *headername_wcolon;
6141 const gchar *headername_trans;
6144 gboolean standard_header = FALSE;
6146 headerentry = ((ComposeHeaderEntry *)list->data);
6148 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6150 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6155 if (!strstr(tmp, ":")) {
6156 headername_wcolon = g_strconcat(tmp, ":", NULL);
6157 headername = g_strdup(tmp);
6159 headername_wcolon = g_strdup(tmp);
6160 headername = g_strdup(strtok(tmp, ":"));
6164 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6165 Xstrdup_a(headervalue, entry_str, return NULL);
6166 subst_char(headervalue, '\r', ' ');
6167 subst_char(headervalue, '\n', ' ');
6168 string = std_headers;
6169 while (*string != NULL) {
6170 headername_trans = prefs_common_translated_header_name(*string);
6171 if (!strcmp(headername_trans, headername_wcolon))
6172 standard_header = TRUE;
6175 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6176 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6179 g_free(headername_wcolon);
6183 g_string_free(header, FALSE);
6188 #undef IS_IN_CUSTOM_HEADER
6190 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6191 gint header_len, gboolean addr_field)
6193 gchar *tmpstr = NULL;
6194 const gchar *out_codeset = NULL;
6196 cm_return_if_fail(src != NULL);
6197 cm_return_if_fail(dest != NULL);
6199 if (len < 1) return;
6201 tmpstr = g_strdup(src);
6203 subst_char(tmpstr, '\n', ' ');
6204 subst_char(tmpstr, '\r', ' ');
6207 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6208 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6209 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6214 codeconv_set_strict(TRUE);
6215 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6216 conv_get_charset_str(compose->out_encoding));
6217 codeconv_set_strict(FALSE);
6219 if (!dest || *dest == '\0') {
6220 gchar *test_conv_global_out = NULL;
6221 gchar *test_conv_reply = NULL;
6223 /* automatic mode. be automatic. */
6224 codeconv_set_strict(TRUE);
6226 out_codeset = conv_get_outgoing_charset_str();
6228 debug_print("trying to convert to %s\n", out_codeset);
6229 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6232 if (!test_conv_global_out && compose->orig_charset
6233 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6234 out_codeset = compose->orig_charset;
6235 debug_print("failure; trying to convert to %s\n", out_codeset);
6236 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6239 if (!test_conv_global_out && !test_conv_reply) {
6241 out_codeset = CS_INTERNAL;
6242 debug_print("finally using %s\n", out_codeset);
6244 g_free(test_conv_global_out);
6245 g_free(test_conv_reply);
6246 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6248 codeconv_set_strict(FALSE);
6253 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6257 cm_return_if_fail(user_data != NULL);
6259 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6260 g_strstrip(address);
6261 if (*address != '\0') {
6262 gchar *name = procheader_get_fromname(address);
6263 extract_address(address);
6264 addressbook_add_contact(name, address, NULL, NULL);
6269 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6271 GtkWidget *menuitem;
6274 cm_return_if_fail(menu != NULL);
6275 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6277 menuitem = gtk_separator_menu_item_new();
6278 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6279 gtk_widget_show(menuitem);
6281 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6282 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6284 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6285 g_strstrip(address);
6286 if (*address == '\0') {
6287 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6290 g_signal_connect(G_OBJECT(menuitem), "activate",
6291 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6292 gtk_widget_show(menuitem);
6295 static void compose_create_header_entry(Compose *compose)
6297 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6302 const gchar *header = NULL;
6303 ComposeHeaderEntry *headerentry;
6304 gboolean standard_header = FALSE;
6305 #if !(GTK_CHECK_VERSION(2,12,0))
6306 GtkTooltips *tips = compose->tooltips;
6309 headerentry = g_new0(ComposeHeaderEntry, 1);
6312 combo = gtk_combo_box_entry_new_text();
6314 while(*string != NULL) {
6315 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6316 (gchar*)prefs_common_translated_header_name(*string));
6319 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6320 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6321 G_CALLBACK(compose_grab_focus_cb), compose);
6322 gtk_widget_show(combo);
6323 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6324 compose->header_nextrow, compose->header_nextrow+1,
6325 GTK_SHRINK, GTK_FILL, 0, 0);
6326 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6327 const gchar *last_header_entry = gtk_entry_get_text(
6328 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6330 while (*string != NULL) {
6331 if (!strcmp(*string, last_header_entry))
6332 standard_header = TRUE;
6335 if (standard_header)
6336 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6338 if (!compose->header_last || !standard_header) {
6339 switch(compose->account->protocol) {
6341 header = prefs_common_translated_header_name("Newsgroups:");
6344 header = prefs_common_translated_header_name("To:");
6349 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6351 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6352 G_CALLBACK(compose_grab_focus_cb), compose);
6355 entry = gtk_entry_new();
6356 gtk_widget_show(entry);
6357 CLAWS_SET_TIP(entry,
6358 _("Use <tab> to autocomplete from addressbook"));
6359 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6360 compose->header_nextrow, compose->header_nextrow+1,
6361 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6363 g_signal_connect(G_OBJECT(entry), "key-press-event",
6364 G_CALLBACK(compose_headerentry_key_press_event_cb),
6366 g_signal_connect(G_OBJECT(entry), "changed",
6367 G_CALLBACK(compose_headerentry_changed_cb),
6369 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6370 G_CALLBACK(compose_grab_focus_cb), compose);
6373 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6374 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6375 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6376 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6377 G_CALLBACK(compose_header_drag_received_cb),
6379 g_signal_connect(G_OBJECT(entry), "drag-drop",
6380 G_CALLBACK(compose_drag_drop),
6382 g_signal_connect(G_OBJECT(entry), "populate-popup",
6383 G_CALLBACK(compose_entry_popup_extend),
6386 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6388 headerentry->compose = compose;
6389 headerentry->combo = combo;
6390 headerentry->entry = entry;
6391 headerentry->headernum = compose->header_nextrow;
6393 compose->header_nextrow++;
6394 compose->header_last = headerentry;
6395 compose->header_list =
6396 g_slist_append(compose->header_list,
6400 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6402 ComposeHeaderEntry *last_header;
6404 last_header = compose->header_last;
6406 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6407 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6410 static void compose_remove_header_entries(Compose *compose)
6413 for (list = compose->header_list; list; list = list->next) {
6414 ComposeHeaderEntry *headerentry =
6415 (ComposeHeaderEntry *)list->data;
6416 gtk_widget_destroy(headerentry->combo);
6417 gtk_widget_destroy(headerentry->entry);
6418 g_free(headerentry);
6420 compose->header_last = NULL;
6421 g_slist_free(compose->header_list);
6422 compose->header_list = NULL;
6423 compose->header_nextrow = 1;
6424 compose_create_header_entry(compose);
6427 static GtkWidget *compose_create_header(Compose *compose)
6429 GtkWidget *from_optmenu_hbox;
6430 GtkWidget *header_scrolledwin;
6431 GtkWidget *header_table;
6435 /* header labels and entries */
6436 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6437 gtk_widget_show(header_scrolledwin);
6438 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6440 header_table = gtk_table_new(2, 2, FALSE);
6441 gtk_widget_show(header_table);
6442 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6443 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6444 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6447 /* option menu for selecting accounts */
6448 from_optmenu_hbox = compose_account_option_menu_create(compose);
6449 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6450 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6453 compose->header_table = header_table;
6454 compose->header_list = NULL;
6455 compose->header_nextrow = count;
6457 compose_create_header_entry(compose);
6459 compose->table = NULL;
6461 return header_scrolledwin ;
6464 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6466 Compose *compose = (Compose *)data;
6467 GdkEventButton event;
6470 event.time = gtk_get_current_event_time();
6472 return attach_button_pressed(compose->attach_clist, &event, compose);
6475 static GtkWidget *compose_create_attach(Compose *compose)
6477 GtkWidget *attach_scrwin;
6478 GtkWidget *attach_clist;
6480 GtkListStore *store;
6481 GtkCellRenderer *renderer;
6482 GtkTreeViewColumn *column;
6483 GtkTreeSelection *selection;
6485 /* attachment list */
6486 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6487 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6488 GTK_POLICY_AUTOMATIC,
6489 GTK_POLICY_AUTOMATIC);
6490 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6492 store = gtk_list_store_new(N_ATTACH_COLS,
6497 G_TYPE_AUTO_POINTER,
6499 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6500 (GTK_TREE_MODEL(store)));
6501 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6502 g_object_unref(store);
6504 renderer = gtk_cell_renderer_text_new();
6505 column = gtk_tree_view_column_new_with_attributes
6506 (_("Mime type"), renderer, "text",
6507 COL_MIMETYPE, NULL);
6508 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6510 renderer = gtk_cell_renderer_text_new();
6511 column = gtk_tree_view_column_new_with_attributes
6512 (_("Size"), renderer, "text",
6514 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6516 renderer = gtk_cell_renderer_text_new();
6517 column = gtk_tree_view_column_new_with_attributes
6518 (_("Name"), renderer, "text",
6520 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6522 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6523 prefs_common.use_stripes_everywhere);
6524 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6525 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6527 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6528 G_CALLBACK(attach_selected), compose);
6529 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6530 G_CALLBACK(attach_button_pressed), compose);
6532 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6533 G_CALLBACK(popup_attach_button_pressed), compose);
6535 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6536 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6537 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6538 G_CALLBACK(popup_attach_button_pressed), compose);
6540 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6541 G_CALLBACK(attach_key_pressed), compose);
6544 gtk_drag_dest_set(attach_clist,
6545 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6546 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6547 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6548 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6549 G_CALLBACK(compose_attach_drag_received_cb),
6551 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6552 G_CALLBACK(compose_drag_drop),
6555 compose->attach_scrwin = attach_scrwin;
6556 compose->attach_clist = attach_clist;
6558 return attach_scrwin;
6561 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6562 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6564 static GtkWidget *compose_create_others(Compose *compose)
6567 GtkWidget *savemsg_checkbtn;
6568 GtkWidget *savemsg_combo;
6569 GtkWidget *savemsg_select;
6572 gchar *folderidentifier;
6574 /* Table for settings */
6575 table = gtk_table_new(3, 1, FALSE);
6576 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6577 gtk_widget_show(table);
6578 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6581 /* Save Message to folder */
6582 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6583 gtk_widget_show(savemsg_checkbtn);
6584 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6585 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6588 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6589 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6591 savemsg_combo = gtk_combo_box_entry_new_text();
6592 compose->savemsg_checkbtn = savemsg_checkbtn;
6593 compose->savemsg_combo = savemsg_combo;
6594 gtk_widget_show(savemsg_combo);
6596 if (prefs_common.compose_save_to_history)
6597 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6598 prefs_common.compose_save_to_history);
6600 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6601 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6602 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6603 G_CALLBACK(compose_grab_focus_cb), compose);
6604 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6605 folderidentifier = folder_item_get_identifier(account_get_special_folder
6606 (compose->account, F_OUTBOX));
6607 compose_set_save_to(compose, folderidentifier);
6608 g_free(folderidentifier);
6611 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6612 gtk_widget_show(savemsg_select);
6613 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6614 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6615 G_CALLBACK(compose_savemsg_select_cb),
6623 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6625 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6626 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6629 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6634 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6637 path = folder_item_get_identifier(dest);
6639 compose_set_save_to(compose, path);
6643 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6644 GdkAtom clip, GtkTextIter *insert_place);
6647 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6651 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6653 if (event->button == 3) {
6655 GtkTextIter sel_start, sel_end;
6656 gboolean stuff_selected;
6658 /* move the cursor to allow GtkAspell to check the word
6659 * under the mouse */
6660 if (event->x && event->y) {
6661 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6662 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6664 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6667 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6668 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6671 stuff_selected = gtk_text_buffer_get_selection_bounds(
6673 &sel_start, &sel_end);
6675 gtk_text_buffer_place_cursor (buffer, &iter);
6676 /* reselect stuff */
6678 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6679 gtk_text_buffer_select_range(buffer,
6680 &sel_start, &sel_end);
6682 return FALSE; /* pass the event so that the right-click goes through */
6685 if (event->button == 2) {
6690 /* get the middle-click position to paste at the correct place */
6691 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6692 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6694 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6697 entry_paste_clipboard(compose, text,
6698 prefs_common.linewrap_pastes,
6699 GDK_SELECTION_PRIMARY, &iter);
6707 static void compose_spell_menu_changed(void *data)
6709 Compose *compose = (Compose *)data;
6711 GtkWidget *menuitem;
6712 GtkWidget *parent_item;
6713 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6716 if (compose->gtkaspell == NULL)
6719 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6720 "/Menu/Spelling/Options");
6722 /* setting the submenu removes /Spelling/Options from the factory
6723 * so we need to save it */
6725 if (parent_item == NULL) {
6726 parent_item = compose->aspell_options_menu;
6727 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6729 compose->aspell_options_menu = parent_item;
6731 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6733 spell_menu = g_slist_reverse(spell_menu);
6734 for (items = spell_menu;
6735 items; items = items->next) {
6736 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6737 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6738 gtk_widget_show(GTK_WIDGET(menuitem));
6740 g_slist_free(spell_menu);
6742 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6743 gtk_widget_show(parent_item);
6746 static void compose_dict_changed(void *data)
6748 Compose *compose = (Compose *) data;
6750 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6753 gtkaspell_highlight_all(compose->gtkaspell);
6754 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6758 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6760 Compose *compose = (Compose *)data;
6761 GdkEventButton event;
6764 event.time = gtk_get_current_event_time();
6768 return text_clicked(compose->text, &event, compose);
6771 static gboolean compose_force_window_origin = TRUE;
6772 static Compose *compose_create(PrefsAccount *account,
6781 GtkWidget *handlebox;
6783 GtkWidget *notebook;
6785 GtkWidget *attach_hbox;
6786 GtkWidget *attach_lab1;
6787 GtkWidget *attach_lab2;
6792 GtkWidget *subject_hbox;
6793 GtkWidget *subject_frame;
6794 GtkWidget *subject_entry;
6798 GtkWidget *edit_vbox;
6799 GtkWidget *ruler_hbox;
6801 GtkWidget *scrolledwin;
6803 GtkTextBuffer *buffer;
6804 GtkClipboard *clipboard;
6807 UndoMain *undostruct;
6809 gchar *titles[N_ATTACH_COLS];
6810 GtkWidget *popupmenu;
6811 GtkWidget *tmpl_menu;
6812 GtkActionGroup *action_group = NULL;
6815 GtkAspell * gtkaspell = NULL;
6818 static GdkGeometry geometry;
6820 cm_return_val_if_fail(account != NULL, NULL);
6822 debug_print("Creating compose window...\n");
6823 compose = g_new0(Compose, 1);
6825 titles[COL_MIMETYPE] = _("MIME type");
6826 titles[COL_SIZE] = _("Size");
6827 titles[COL_NAME] = _("Name");
6829 compose->batch = batch;
6830 compose->account = account;
6831 compose->folder = folder;
6833 compose->mutex = g_mutex_new();
6834 compose->set_cursor_pos = -1;
6836 #if !(GTK_CHECK_VERSION(2,12,0))
6837 compose->tooltips = tips;
6840 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6842 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6843 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6845 if (!geometry.max_width) {
6846 geometry.max_width = gdk_screen_width();
6847 geometry.max_height = gdk_screen_height();
6850 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6851 &geometry, GDK_HINT_MAX_SIZE);
6852 if (!geometry.min_width) {
6853 geometry.min_width = 600;
6854 geometry.min_height = 440;
6856 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6857 &geometry, GDK_HINT_MIN_SIZE);
6859 #ifndef GENERIC_UMPC
6860 if (compose_force_window_origin)
6861 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6862 prefs_common.compose_y);
6864 g_signal_connect(G_OBJECT(window), "delete_event",
6865 G_CALLBACK(compose_delete_cb), compose);
6866 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6867 gtk_widget_realize(window);
6869 gtkut_widget_set_composer_icon(window);
6871 vbox = gtk_vbox_new(FALSE, 0);
6872 gtk_container_add(GTK_CONTAINER(window), vbox);
6874 compose->ui_manager = gtk_ui_manager_new();
6875 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6876 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6877 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6878 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6879 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6880 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6881 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6882 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6883 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6884 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6887 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6893 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6916 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6924 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6925 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6930 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6948 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6982 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6987 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6989 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6990 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6991 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6997 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6999 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7000 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7001 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7003 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7004 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)
7005 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)
7006 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7008 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7010 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7011 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)
7012 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)
7014 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7016 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7017 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)
7018 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7020 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7021 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)
7022 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7024 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7026 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7027 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)
7028 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7029 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7030 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7032 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7033 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)
7034 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)
7035 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7036 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7044 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7046 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)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7054 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7055 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7062 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7064 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7065 gtk_widget_show_all(menubar);
7067 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7069 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7071 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7074 if (prefs_common.toolbar_detachable) {
7075 handlebox = gtk_handle_box_new();
7077 handlebox = gtk_hbox_new(FALSE, 0);
7079 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7081 gtk_widget_realize(handlebox);
7083 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7086 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7090 vbox2 = gtk_vbox_new(FALSE, 2);
7091 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7092 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7095 notebook = gtk_notebook_new();
7096 gtk_widget_set_size_request(notebook, -1, 130);
7097 gtk_widget_show(notebook);
7099 /* header labels and entries */
7100 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7101 compose_create_header(compose),
7102 gtk_label_new_with_mnemonic(_("Hea_der")));
7103 /* attachment list */
7104 attach_hbox = gtk_hbox_new(FALSE, 0);
7105 gtk_widget_show(attach_hbox);
7107 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7108 gtk_widget_show(attach_lab1);
7109 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7111 attach_lab2 = gtk_label_new("");
7112 gtk_widget_show(attach_lab2);
7113 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7115 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7116 compose_create_attach(compose),
7119 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7120 compose_create_others(compose),
7121 gtk_label_new_with_mnemonic(_("Othe_rs")));
7124 subject_hbox = gtk_hbox_new(FALSE, 0);
7125 gtk_widget_show(subject_hbox);
7127 subject_frame = gtk_frame_new(NULL);
7128 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7129 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7130 gtk_widget_show(subject_frame);
7132 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7133 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7134 gtk_widget_show(subject);
7136 label = gtk_label_new(_("Subject:"));
7137 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7138 gtk_widget_show(label);
7141 subject_entry = claws_spell_entry_new();
7143 subject_entry = gtk_entry_new();
7145 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7146 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7147 G_CALLBACK(compose_grab_focus_cb), compose);
7148 gtk_widget_show(subject_entry);
7149 compose->subject_entry = subject_entry;
7150 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7152 edit_vbox = gtk_vbox_new(FALSE, 0);
7154 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7157 ruler_hbox = gtk_hbox_new(FALSE, 0);
7158 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7160 ruler = gtk_shruler_new();
7161 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7162 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7166 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7167 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7168 GTK_POLICY_AUTOMATIC,
7169 GTK_POLICY_AUTOMATIC);
7170 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7172 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7173 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7175 text = gtk_text_view_new();
7176 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7177 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7178 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7179 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7180 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7182 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7184 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7185 G_CALLBACK(compose_edit_size_alloc),
7187 g_signal_connect(G_OBJECT(buffer), "changed",
7188 G_CALLBACK(compose_changed_cb), compose);
7189 g_signal_connect(G_OBJECT(text), "grab_focus",
7190 G_CALLBACK(compose_grab_focus_cb), compose);
7191 g_signal_connect(G_OBJECT(buffer), "insert_text",
7192 G_CALLBACK(text_inserted), compose);
7193 g_signal_connect(G_OBJECT(text), "button_press_event",
7194 G_CALLBACK(text_clicked), compose);
7196 g_signal_connect(G_OBJECT(text), "popup-menu",
7197 G_CALLBACK(compose_popup_menu), compose);
7199 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7200 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7201 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7202 G_CALLBACK(compose_popup_menu), compose);
7204 g_signal_connect(G_OBJECT(subject_entry), "changed",
7205 G_CALLBACK(compose_changed_cb), compose);
7208 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7209 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7210 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7211 g_signal_connect(G_OBJECT(text), "drag_data_received",
7212 G_CALLBACK(compose_insert_drag_received_cb),
7214 g_signal_connect(G_OBJECT(text), "drag-drop",
7215 G_CALLBACK(compose_drag_drop),
7217 gtk_widget_show_all(vbox);
7219 /* pane between attach clist and text */
7220 paned = gtk_vpaned_new();
7221 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7223 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7224 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7226 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7228 gtk_paned_add1(GTK_PANED(paned), notebook);
7229 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7230 gtk_widget_show_all(paned);
7233 if (prefs_common.textfont) {
7234 PangoFontDescription *font_desc;
7236 font_desc = pango_font_description_from_string
7237 (prefs_common.textfont);
7239 gtk_widget_modify_font(text, font_desc);
7240 pango_font_description_free(font_desc);
7244 gtk_action_group_add_actions(action_group, compose_popup_entries,
7245 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7253 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7255 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7256 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7257 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7259 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7261 undostruct = undo_init(text);
7262 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7265 address_completion_start(window);
7267 compose->window = window;
7268 compose->vbox = vbox;
7269 compose->menubar = menubar;
7270 compose->handlebox = handlebox;
7272 compose->vbox2 = vbox2;
7274 compose->paned = paned;
7276 compose->attach_label = attach_lab2;
7278 compose->notebook = notebook;
7279 compose->edit_vbox = edit_vbox;
7280 compose->ruler_hbox = ruler_hbox;
7281 compose->ruler = ruler;
7282 compose->scrolledwin = scrolledwin;
7283 compose->text = text;
7285 compose->focused_editable = NULL;
7287 compose->popupmenu = popupmenu;
7289 compose->tmpl_menu = tmpl_menu;
7291 compose->mode = mode;
7292 compose->rmode = mode;
7294 compose->targetinfo = NULL;
7295 compose->replyinfo = NULL;
7296 compose->fwdinfo = NULL;
7298 compose->replyto = NULL;
7300 compose->bcc = NULL;
7301 compose->followup_to = NULL;
7303 compose->ml_post = NULL;
7305 compose->inreplyto = NULL;
7306 compose->references = NULL;
7307 compose->msgid = NULL;
7308 compose->boundary = NULL;
7310 compose->autowrap = prefs_common.autowrap;
7311 compose->autoindent = prefs_common.auto_indent;
7312 compose->use_signing = FALSE;
7313 compose->use_encryption = FALSE;
7314 compose->privacy_system = NULL;
7316 compose->modified = FALSE;
7318 compose->return_receipt = FALSE;
7320 compose->to_list = NULL;
7321 compose->newsgroup_list = NULL;
7323 compose->undostruct = undostruct;
7325 compose->sig_str = NULL;
7327 compose->exteditor_file = NULL;
7328 compose->exteditor_pid = -1;
7329 compose->exteditor_tag = -1;
7330 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7333 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7334 if (mode != COMPOSE_REDIRECT) {
7335 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7336 strcmp(prefs_common.dictionary, "")) {
7337 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7338 prefs_common.alt_dictionary,
7339 conv_get_locale_charset_str(),
7340 prefs_common.misspelled_col,
7341 prefs_common.check_while_typing,
7342 prefs_common.recheck_when_changing_dict,
7343 prefs_common.use_alternate,
7344 prefs_common.use_both_dicts,
7345 GTK_TEXT_VIEW(text),
7346 GTK_WINDOW(compose->window),
7347 compose_dict_changed,
7348 compose_spell_menu_changed,
7351 alertpanel_error(_("Spell checker could not "
7353 gtkaspell_checkers_strerror());
7354 gtkaspell_checkers_reset_error();
7356 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7360 compose->gtkaspell = gtkaspell;
7361 compose_spell_menu_changed(compose);
7362 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7365 compose_select_account(compose, account, TRUE);
7367 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7368 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7370 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7371 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7373 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7374 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7376 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7377 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7379 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7380 if (account->protocol != A_NNTP)
7381 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7382 prefs_common_translated_header_name("To:"));
7384 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7385 prefs_common_translated_header_name("Newsgroups:"));
7387 addressbook_set_target_compose(compose);
7389 if (mode != COMPOSE_REDIRECT)
7390 compose_set_template_menu(compose);
7392 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7395 compose_list = g_list_append(compose_list, compose);
7397 if (!prefs_common.show_ruler)
7398 gtk_widget_hide(ruler_hbox);
7400 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7403 compose->priority = PRIORITY_NORMAL;
7404 compose_update_priority_menu_item(compose);
7406 compose_set_out_encoding(compose);
7409 compose_update_actions_menu(compose);
7411 /* Privacy Systems menu */
7412 compose_update_privacy_systems_menu(compose);
7414 activate_privacy_system(compose, account, TRUE);
7415 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7417 gtk_widget_realize(window);
7419 gtk_widget_show(window);
7421 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7422 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7429 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7434 GtkWidget *optmenubox;
7437 GtkWidget *from_name = NULL;
7438 #if !(GTK_CHECK_VERSION(2,12,0))
7439 GtkTooltips *tips = compose->tooltips;
7442 gint num = 0, def_menu = 0;
7444 accounts = account_get_list();
7445 cm_return_val_if_fail(accounts != NULL, NULL);
7447 optmenubox = gtk_event_box_new();
7448 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7449 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7451 hbox = gtk_hbox_new(FALSE, 6);
7452 from_name = gtk_entry_new();
7454 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7455 G_CALLBACK(compose_grab_focus_cb), compose);
7457 for (; accounts != NULL; accounts = accounts->next, num++) {
7458 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7459 gchar *name, *from = NULL;
7461 if (ac == compose->account) def_menu = num;
7463 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7466 if (ac == compose->account) {
7467 if (ac->name && *ac->name) {
7469 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7470 from = g_strdup_printf("%s <%s>",
7472 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7474 from = g_strdup_printf("%s",
7476 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7479 COMBOBOX_ADD(menu, name, ac->account_id);
7484 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7486 g_signal_connect(G_OBJECT(optmenu), "changed",
7487 G_CALLBACK(account_activated),
7489 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7490 G_CALLBACK(compose_entry_popup_extend),
7493 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7494 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7496 CLAWS_SET_TIP(optmenubox,
7497 _("Account to use for this email"));
7498 CLAWS_SET_TIP(from_name,
7499 _("Sender address to be used"));
7501 compose->from_name = from_name;
7506 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7508 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7509 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7510 Compose *compose = (Compose *) data;
7512 compose->priority = value;
7516 static void compose_reply_change_mode(Compose *compose,
7519 gboolean was_modified = compose->modified;
7521 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7523 cm_return_if_fail(compose->replyinfo != NULL);
7525 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7527 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7529 if (action == COMPOSE_REPLY_TO_ALL)
7531 if (action == COMPOSE_REPLY_TO_SENDER)
7533 if (action == COMPOSE_REPLY_TO_LIST)
7536 compose_remove_header_entries(compose);
7537 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7538 if (compose->account->set_autocc && compose->account->auto_cc)
7539 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7541 if (compose->account->set_autobcc && compose->account->auto_bcc)
7542 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7544 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7545 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7546 compose_show_first_last_header(compose, TRUE);
7547 compose->modified = was_modified;
7548 compose_set_title(compose);
7551 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7553 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7554 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7555 Compose *compose = (Compose *) data;
7558 compose_reply_change_mode(compose, value);
7561 static void compose_update_priority_menu_item(Compose * compose)
7563 GtkWidget *menuitem = NULL;
7564 switch (compose->priority) {
7565 case PRIORITY_HIGHEST:
7566 menuitem = gtk_ui_manager_get_widget
7567 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7570 menuitem = gtk_ui_manager_get_widget
7571 (compose->ui_manager, "/Menu/Options/Priority/High");
7573 case PRIORITY_NORMAL:
7574 menuitem = gtk_ui_manager_get_widget
7575 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7578 menuitem = gtk_ui_manager_get_widget
7579 (compose->ui_manager, "/Menu/Options/Priority/Low");
7581 case PRIORITY_LOWEST:
7582 menuitem = gtk_ui_manager_get_widget
7583 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7586 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7589 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7591 Compose *compose = (Compose *) data;
7593 gboolean can_sign = FALSE, can_encrypt = FALSE;
7595 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7597 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7600 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7601 g_free(compose->privacy_system);
7602 compose->privacy_system = NULL;
7603 if (systemid != NULL) {
7604 compose->privacy_system = g_strdup(systemid);
7606 can_sign = privacy_system_can_sign(systemid);
7607 can_encrypt = privacy_system_can_encrypt(systemid);
7610 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7612 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7613 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7616 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7618 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7619 GtkWidget *menuitem = NULL;
7621 gboolean can_sign = FALSE, can_encrypt = FALSE;
7622 gboolean found = FALSE;
7624 if (compose->privacy_system != NULL) {
7626 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7627 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7628 cm_return_if_fail(menuitem != NULL);
7630 amenu = GTK_MENU_SHELL(menuitem)->children;
7632 while (amenu != NULL) {
7633 GList *alist = amenu->next;
7635 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7636 if (systemid != NULL) {
7637 if (strcmp(systemid, compose->privacy_system) == 0 &&
7638 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7639 menuitem = GTK_WIDGET(amenu->data);
7641 can_sign = privacy_system_can_sign(systemid);
7642 can_encrypt = privacy_system_can_encrypt(systemid);
7646 } else if (strlen(compose->privacy_system) == 0 &&
7647 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7648 menuitem = GTK_WIDGET(amenu->data);
7651 can_encrypt = FALSE;
7658 if (menuitem != NULL)
7659 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7661 if (warn && !found && strlen(compose->privacy_system)) {
7662 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7663 "will not be able to sign or encrypt this message."),
7664 compose->privacy_system);
7668 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7669 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7672 static void compose_set_out_encoding(Compose *compose)
7674 CharSet out_encoding;
7675 const gchar *branch = NULL;
7676 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7678 switch(out_encoding) {
7679 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7680 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7681 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7682 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7683 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7684 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7685 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7686 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7687 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7688 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7689 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7690 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7691 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7692 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7693 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7694 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7695 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7696 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7697 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7698 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7699 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7700 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7701 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7702 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7703 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7704 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7705 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7706 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7707 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7708 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7709 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7710 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7712 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7715 static void compose_set_template_menu(Compose *compose)
7717 GSList *tmpl_list, *cur;
7721 tmpl_list = template_get_config();
7723 menu = gtk_menu_new();
7725 gtk_menu_set_accel_group (GTK_MENU (menu),
7726 gtk_ui_manager_get_accel_group(compose->ui_manager));
7727 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7728 Template *tmpl = (Template *)cur->data;
7729 gchar *accel_path = NULL;
7730 item = gtk_menu_item_new_with_label(tmpl->name);
7731 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7732 g_signal_connect(G_OBJECT(item), "activate",
7733 G_CALLBACK(compose_template_activate_cb),
7735 g_object_set_data(G_OBJECT(item), "template", tmpl);
7736 gtk_widget_show(item);
7737 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7738 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7742 gtk_widget_show(menu);
7743 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7746 void compose_update_actions_menu(Compose *compose)
7748 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7751 static void compose_update_privacy_systems_menu(Compose *compose)
7753 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7754 GSList *systems, *cur;
7756 GtkWidget *system_none;
7758 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7759 GtkWidget *privacy_menu = gtk_menu_new();
7761 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7762 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7764 g_signal_connect(G_OBJECT(system_none), "activate",
7765 G_CALLBACK(compose_set_privacy_system_cb), compose);
7767 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7768 gtk_widget_show(system_none);
7770 systems = privacy_get_system_ids();
7771 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7772 gchar *systemid = cur->data;
7774 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7775 widget = gtk_radio_menu_item_new_with_label(group,
7776 privacy_system_get_name(systemid));
7777 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7778 g_strdup(systemid), g_free);
7779 g_signal_connect(G_OBJECT(widget), "activate",
7780 G_CALLBACK(compose_set_privacy_system_cb), compose);
7782 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7783 gtk_widget_show(widget);
7786 g_slist_free(systems);
7787 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7788 gtk_widget_show_all(privacy_menu);
7789 gtk_widget_show_all(privacy_menuitem);
7792 void compose_reflect_prefs_all(void)
7797 for (cur = compose_list; cur != NULL; cur = cur->next) {
7798 compose = (Compose *)cur->data;
7799 compose_set_template_menu(compose);
7803 void compose_reflect_prefs_pixmap_theme(void)
7808 for (cur = compose_list; cur != NULL; cur = cur->next) {
7809 compose = (Compose *)cur->data;
7810 toolbar_update(TOOLBAR_COMPOSE, compose);
7814 static const gchar *compose_quote_char_from_context(Compose *compose)
7816 const gchar *qmark = NULL;
7818 cm_return_val_if_fail(compose != NULL, NULL);
7820 switch (compose->mode) {
7821 /* use forward-specific quote char */
7822 case COMPOSE_FORWARD:
7823 case COMPOSE_FORWARD_AS_ATTACH:
7824 case COMPOSE_FORWARD_INLINE:
7825 if (compose->folder && compose->folder->prefs &&
7826 compose->folder->prefs->forward_with_format)
7827 qmark = compose->folder->prefs->forward_quotemark;
7828 else if (compose->account->forward_with_format)
7829 qmark = compose->account->forward_quotemark;
7831 qmark = prefs_common.fw_quotemark;
7834 /* use reply-specific quote char in all other modes */
7836 if (compose->folder && compose->folder->prefs &&
7837 compose->folder->prefs->reply_with_format)
7838 qmark = compose->folder->prefs->reply_quotemark;
7839 else if (compose->account->reply_with_format)
7840 qmark = compose->account->reply_quotemark;
7842 qmark = prefs_common.quotemark;
7846 if (qmark == NULL || *qmark == '\0')
7852 static void compose_template_apply(Compose *compose, Template *tmpl,
7856 GtkTextBuffer *buffer;
7860 gchar *parsed_str = NULL;
7861 gint cursor_pos = 0;
7862 const gchar *err_msg = _("Template body format error at line %d.");
7865 /* process the body */
7867 text = GTK_TEXT_VIEW(compose->text);
7868 buffer = gtk_text_view_get_buffer(text);
7871 qmark = compose_quote_char_from_context(compose);
7873 if (compose->replyinfo != NULL) {
7876 gtk_text_buffer_set_text(buffer, "", -1);
7877 mark = gtk_text_buffer_get_insert(buffer);
7878 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7880 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7881 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7883 } else if (compose->fwdinfo != NULL) {
7886 gtk_text_buffer_set_text(buffer, "", -1);
7887 mark = gtk_text_buffer_get_insert(buffer);
7888 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7890 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7891 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7894 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7896 GtkTextIter start, end;
7899 gtk_text_buffer_get_start_iter(buffer, &start);
7900 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7901 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7903 /* clear the buffer now */
7905 gtk_text_buffer_set_text(buffer, "", -1);
7907 parsed_str = compose_quote_fmt(compose, dummyinfo,
7908 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7909 procmsg_msginfo_free( dummyinfo );
7915 gtk_text_buffer_set_text(buffer, "", -1);
7916 mark = gtk_text_buffer_get_insert(buffer);
7917 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7920 if (replace && parsed_str && compose->account->auto_sig)
7921 compose_insert_sig(compose, FALSE);
7923 if (replace && parsed_str) {
7924 gtk_text_buffer_get_start_iter(buffer, &iter);
7925 gtk_text_buffer_place_cursor(buffer, &iter);
7929 cursor_pos = quote_fmt_get_cursor_pos();
7930 compose->set_cursor_pos = cursor_pos;
7931 if (cursor_pos == -1)
7933 gtk_text_buffer_get_start_iter(buffer, &iter);
7934 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7935 gtk_text_buffer_place_cursor(buffer, &iter);
7938 /* process the other fields */
7940 compose_template_apply_fields(compose, tmpl);
7941 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7942 quote_fmt_reset_vartable();
7943 compose_changed_cb(NULL, compose);
7946 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7948 MsgInfo* dummyinfo = NULL;
7949 MsgInfo *msginfo = NULL;
7952 if (compose->replyinfo != NULL)
7953 msginfo = compose->replyinfo;
7954 else if (compose->fwdinfo != NULL)
7955 msginfo = compose->fwdinfo;
7957 dummyinfo = compose_msginfo_new_from_compose(compose);
7958 msginfo = dummyinfo;
7961 if (tmpl->from && *tmpl->from != '\0') {
7963 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7964 compose->gtkaspell);
7966 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7968 quote_fmt_scan_string(tmpl->from);
7971 buf = quote_fmt_get_buffer();
7973 alertpanel_error(_("Template From format error."));
7975 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7979 if (tmpl->to && *tmpl->to != '\0') {
7981 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7982 compose->gtkaspell);
7984 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7986 quote_fmt_scan_string(tmpl->to);
7989 buf = quote_fmt_get_buffer();
7991 alertpanel_error(_("Template To format error."));
7993 compose_entry_append(compose, buf, COMPOSE_TO);
7997 if (tmpl->cc && *tmpl->cc != '\0') {
7999 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8000 compose->gtkaspell);
8002 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8004 quote_fmt_scan_string(tmpl->cc);
8007 buf = quote_fmt_get_buffer();
8009 alertpanel_error(_("Template Cc format error."));
8011 compose_entry_append(compose, buf, COMPOSE_CC);
8015 if (tmpl->bcc && *tmpl->bcc != '\0') {
8017 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8018 compose->gtkaspell);
8020 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8022 quote_fmt_scan_string(tmpl->bcc);
8025 buf = quote_fmt_get_buffer();
8027 alertpanel_error(_("Template Bcc format error."));
8029 compose_entry_append(compose, buf, COMPOSE_BCC);
8033 /* process the subject */
8034 if (tmpl->subject && *tmpl->subject != '\0') {
8036 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8037 compose->gtkaspell);
8039 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8041 quote_fmt_scan_string(tmpl->subject);
8044 buf = quote_fmt_get_buffer();
8046 alertpanel_error(_("Template subject format error."));
8048 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8052 procmsg_msginfo_free( dummyinfo );
8055 static void compose_destroy(Compose *compose)
8057 GtkTextBuffer *buffer;
8058 GtkClipboard *clipboard;
8060 compose_list = g_list_remove(compose_list, compose);
8062 if (compose->updating) {
8063 debug_print("danger, not destroying anything now\n");
8064 compose->deferred_destroy = TRUE;
8067 /* NOTE: address_completion_end() does nothing with the window
8068 * however this may change. */
8069 address_completion_end(compose->window);
8071 slist_free_strings(compose->to_list);
8072 g_slist_free(compose->to_list);
8073 slist_free_strings(compose->newsgroup_list);
8074 g_slist_free(compose->newsgroup_list);
8075 slist_free_strings(compose->header_list);
8076 g_slist_free(compose->header_list);
8078 procmsg_msginfo_free(compose->targetinfo);
8079 procmsg_msginfo_free(compose->replyinfo);
8080 procmsg_msginfo_free(compose->fwdinfo);
8082 g_free(compose->replyto);
8083 g_free(compose->cc);
8084 g_free(compose->bcc);
8085 g_free(compose->newsgroups);
8086 g_free(compose->followup_to);
8088 g_free(compose->ml_post);
8090 g_free(compose->inreplyto);
8091 g_free(compose->references);
8092 g_free(compose->msgid);
8093 g_free(compose->boundary);
8095 g_free(compose->redirect_filename);
8096 if (compose->undostruct)
8097 undo_destroy(compose->undostruct);
8099 g_free(compose->sig_str);
8101 g_free(compose->exteditor_file);
8103 g_free(compose->orig_charset);
8105 g_free(compose->privacy_system);
8107 if (addressbook_get_target_compose() == compose)
8108 addressbook_set_target_compose(NULL);
8111 if (compose->gtkaspell) {
8112 gtkaspell_delete(compose->gtkaspell);
8113 compose->gtkaspell = NULL;
8117 if (!compose->batch) {
8118 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8119 prefs_common.compose_height = compose->window->allocation.height;
8122 if (!gtk_widget_get_parent(compose->paned))
8123 gtk_widget_destroy(compose->paned);
8124 gtk_widget_destroy(compose->popupmenu);
8126 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8127 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8128 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8130 gtk_widget_destroy(compose->window);
8131 toolbar_destroy(compose->toolbar);
8132 g_free(compose->toolbar);
8133 g_mutex_free(compose->mutex);
8137 static void compose_attach_info_free(AttachInfo *ainfo)
8139 g_free(ainfo->file);
8140 g_free(ainfo->content_type);
8141 g_free(ainfo->name);
8145 static void compose_attach_update_label(Compose *compose)
8150 GtkTreeModel *model;
8155 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8156 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8157 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8161 while(gtk_tree_model_iter_next(model, &iter))
8164 text = g_strdup_printf("(%d)", i);
8165 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8169 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8171 Compose *compose = (Compose *)data;
8172 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8173 GtkTreeSelection *selection;
8175 GtkTreeModel *model;
8177 selection = gtk_tree_view_get_selection(tree_view);
8178 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8183 for (cur = sel; cur != NULL; cur = cur->next) {
8184 GtkTreePath *path = cur->data;
8185 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8188 gtk_tree_path_free(path);
8191 for (cur = sel; cur != NULL; cur = cur->next) {
8192 GtkTreeRowReference *ref = cur->data;
8193 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8196 if (gtk_tree_model_get_iter(model, &iter, path))
8197 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8199 gtk_tree_path_free(path);
8200 gtk_tree_row_reference_free(ref);
8204 compose_attach_update_label(compose);
8207 static struct _AttachProperty
8210 GtkWidget *mimetype_entry;
8211 GtkWidget *encoding_optmenu;
8212 GtkWidget *path_entry;
8213 GtkWidget *filename_entry;
8215 GtkWidget *cancel_btn;
8218 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8220 gtk_tree_path_free((GtkTreePath *)ptr);
8223 static void compose_attach_property(GtkAction *action, gpointer data)
8225 Compose *compose = (Compose *)data;
8226 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8228 GtkComboBox *optmenu;
8229 GtkTreeSelection *selection;
8231 GtkTreeModel *model;
8234 static gboolean cancelled;
8236 /* only if one selected */
8237 selection = gtk_tree_view_get_selection(tree_view);
8238 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8241 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8245 path = (GtkTreePath *) sel->data;
8246 gtk_tree_model_get_iter(model, &iter, path);
8247 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8250 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8256 if (!attach_prop.window)
8257 compose_attach_property_create(&cancelled);
8258 gtk_widget_grab_focus(attach_prop.ok_btn);
8259 gtk_widget_show(attach_prop.window);
8260 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8262 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8263 if (ainfo->encoding == ENC_UNKNOWN)
8264 combobox_select_by_data(optmenu, ENC_BASE64);
8266 combobox_select_by_data(optmenu, ainfo->encoding);
8268 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8269 ainfo->content_type ? ainfo->content_type : "");
8270 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8271 ainfo->file ? ainfo->file : "");
8272 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8273 ainfo->name ? ainfo->name : "");
8276 const gchar *entry_text;
8278 gchar *cnttype = NULL;
8285 gtk_widget_hide(attach_prop.window);
8290 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8291 if (*entry_text != '\0') {
8294 text = g_strstrip(g_strdup(entry_text));
8295 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8296 cnttype = g_strdup(text);
8299 alertpanel_error(_("Invalid MIME type."));
8305 ainfo->encoding = combobox_get_active_data(optmenu);
8307 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8308 if (*entry_text != '\0') {
8309 if (is_file_exist(entry_text) &&
8310 (size = get_file_size(entry_text)) > 0)
8311 file = g_strdup(entry_text);
8314 (_("File doesn't exist or is empty."));
8320 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8321 if (*entry_text != '\0') {
8322 g_free(ainfo->name);
8323 ainfo->name = g_strdup(entry_text);
8327 g_free(ainfo->content_type);
8328 ainfo->content_type = cnttype;
8331 g_free(ainfo->file);
8335 ainfo->size = (goffset)size;
8337 /* update tree store */
8338 text = to_human_readable(ainfo->size);
8339 gtk_tree_model_get_iter(model, &iter, path);
8340 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8341 COL_MIMETYPE, ainfo->content_type,
8343 COL_NAME, ainfo->name,
8349 gtk_tree_path_free(path);
8352 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8354 label = gtk_label_new(str); \
8355 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8356 GTK_FILL, 0, 0, 0); \
8357 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8359 entry = gtk_entry_new(); \
8360 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8361 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8364 static void compose_attach_property_create(gboolean *cancelled)
8370 GtkWidget *mimetype_entry;
8373 GtkListStore *optmenu_menu;
8374 GtkWidget *path_entry;
8375 GtkWidget *filename_entry;
8378 GtkWidget *cancel_btn;
8379 GList *mime_type_list, *strlist;
8382 debug_print("Creating attach_property window...\n");
8384 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8385 gtk_widget_set_size_request(window, 480, -1);
8386 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8387 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8388 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8389 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8390 g_signal_connect(G_OBJECT(window), "delete_event",
8391 G_CALLBACK(attach_property_delete_event),
8393 g_signal_connect(G_OBJECT(window), "key_press_event",
8394 G_CALLBACK(attach_property_key_pressed),
8397 vbox = gtk_vbox_new(FALSE, 8);
8398 gtk_container_add(GTK_CONTAINER(window), vbox);
8400 table = gtk_table_new(4, 2, FALSE);
8401 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8402 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8403 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8405 label = gtk_label_new(_("MIME type"));
8406 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8408 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8409 mimetype_entry = gtk_combo_box_entry_new_text();
8410 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8411 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8413 /* stuff with list */
8414 mime_type_list = procmime_get_mime_type_list();
8416 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8417 MimeType *type = (MimeType *) mime_type_list->data;
8420 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8422 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8425 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8426 (GCompareFunc)strcmp2);
8429 for (mime_type_list = strlist; mime_type_list != NULL;
8430 mime_type_list = mime_type_list->next) {
8431 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8432 g_free(mime_type_list->data);
8434 g_list_free(strlist);
8435 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8436 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8438 label = gtk_label_new(_("Encoding"));
8439 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8441 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8443 hbox = gtk_hbox_new(FALSE, 0);
8444 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8445 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8447 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8448 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8450 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8451 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8452 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8453 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8454 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8456 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8458 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8459 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8461 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8462 &ok_btn, GTK_STOCK_OK,
8464 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8465 gtk_widget_grab_default(ok_btn);
8467 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8468 G_CALLBACK(attach_property_ok),
8470 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8471 G_CALLBACK(attach_property_cancel),
8474 gtk_widget_show_all(vbox);
8476 attach_prop.window = window;
8477 attach_prop.mimetype_entry = mimetype_entry;
8478 attach_prop.encoding_optmenu = optmenu;
8479 attach_prop.path_entry = path_entry;
8480 attach_prop.filename_entry = filename_entry;
8481 attach_prop.ok_btn = ok_btn;
8482 attach_prop.cancel_btn = cancel_btn;
8485 #undef SET_LABEL_AND_ENTRY
8487 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8493 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8499 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8500 gboolean *cancelled)
8508 static gboolean attach_property_key_pressed(GtkWidget *widget,
8510 gboolean *cancelled)
8512 if (event && event->keyval == GDK_Escape) {
8516 if (event && event->keyval == GDK_Return) {
8523 static void compose_exec_ext_editor(Compose *compose)
8530 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8531 G_DIR_SEPARATOR, compose);
8533 if (pipe(pipe_fds) < 0) {
8539 if ((pid = fork()) < 0) {
8546 /* close the write side of the pipe */
8549 compose->exteditor_file = g_strdup(tmp);
8550 compose->exteditor_pid = pid;
8552 compose_set_ext_editor_sensitive(compose, FALSE);
8555 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8557 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8559 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8563 } else { /* process-monitoring process */
8569 /* close the read side of the pipe */
8572 if (compose_write_body_to_file(compose, tmp) < 0) {
8573 fd_write_all(pipe_fds[1], "2\n", 2);
8577 pid_ed = compose_exec_ext_editor_real(tmp);
8579 fd_write_all(pipe_fds[1], "1\n", 2);
8583 /* wait until editor is terminated */
8584 waitpid(pid_ed, NULL, 0);
8586 fd_write_all(pipe_fds[1], "0\n", 2);
8593 #endif /* G_OS_UNIX */
8597 static gint compose_exec_ext_editor_real(const gchar *file)
8604 cm_return_val_if_fail(file != NULL, -1);
8606 if ((pid = fork()) < 0) {
8611 if (pid != 0) return pid;
8613 /* grandchild process */
8615 if (setpgid(0, getppid()))
8618 if (prefs_common_get_ext_editor_cmd() &&
8619 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8620 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8621 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8623 if (prefs_common_get_ext_editor_cmd())
8624 g_warning("External editor command-line is invalid: '%s'\n",
8625 prefs_common_get_ext_editor_cmd());
8626 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8629 cmdline = strsplit_with_quote(buf, " ", 1024);
8630 execvp(cmdline[0], cmdline);
8633 g_strfreev(cmdline);
8638 static gboolean compose_ext_editor_kill(Compose *compose)
8640 pid_t pgid = compose->exteditor_pid * -1;
8643 ret = kill(pgid, 0);
8645 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8649 msg = g_strdup_printf
8650 (_("The external editor is still working.\n"
8651 "Force terminating the process?\n"
8652 "process group id: %d"), -pgid);
8653 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8654 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8658 if (val == G_ALERTALTERNATE) {
8659 g_source_remove(compose->exteditor_tag);
8660 g_io_channel_shutdown(compose->exteditor_ch,
8662 g_io_channel_unref(compose->exteditor_ch);
8664 if (kill(pgid, SIGTERM) < 0) perror("kill");
8665 waitpid(compose->exteditor_pid, NULL, 0);
8667 g_warning("Terminated process group id: %d", -pgid);
8668 g_warning("Temporary file: %s",
8669 compose->exteditor_file);
8671 compose_set_ext_editor_sensitive(compose, TRUE);
8673 g_free(compose->exteditor_file);
8674 compose->exteditor_file = NULL;
8675 compose->exteditor_pid = -1;
8676 compose->exteditor_ch = NULL;
8677 compose->exteditor_tag = -1;
8685 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8689 Compose *compose = (Compose *)data;
8692 debug_print(_("Compose: input from monitoring process\n"));
8694 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8696 g_io_channel_shutdown(source, FALSE, NULL);
8697 g_io_channel_unref(source);
8699 waitpid(compose->exteditor_pid, NULL, 0);
8701 if (buf[0] == '0') { /* success */
8702 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8703 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8705 gtk_text_buffer_set_text(buffer, "", -1);
8706 compose_insert_file(compose, compose->exteditor_file);
8707 compose_changed_cb(NULL, compose);
8708 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8710 if (claws_unlink(compose->exteditor_file) < 0)
8711 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8712 } else if (buf[0] == '1') { /* failed */
8713 g_warning("Couldn't exec external editor\n");
8714 if (claws_unlink(compose->exteditor_file) < 0)
8715 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8716 } else if (buf[0] == '2') {
8717 g_warning("Couldn't write to file\n");
8718 } else if (buf[0] == '3') {
8719 g_warning("Pipe read failed\n");
8722 compose_set_ext_editor_sensitive(compose, TRUE);
8724 g_free(compose->exteditor_file);
8725 compose->exteditor_file = NULL;
8726 compose->exteditor_pid = -1;
8727 compose->exteditor_ch = NULL;
8728 compose->exteditor_tag = -1;
8733 static void compose_set_ext_editor_sensitive(Compose *compose,
8736 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8737 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8738 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8739 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8740 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8741 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8742 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8744 gtk_widget_set_sensitive(compose->text, sensitive);
8745 if (compose->toolbar->send_btn)
8746 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8747 if (compose->toolbar->sendl_btn)
8748 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8749 if (compose->toolbar->draft_btn)
8750 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8751 if (compose->toolbar->insert_btn)
8752 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8753 if (compose->toolbar->sig_btn)
8754 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8755 if (compose->toolbar->exteditor_btn)
8756 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8757 if (compose->toolbar->linewrap_current_btn)
8758 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8759 if (compose->toolbar->linewrap_all_btn)
8760 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8762 #endif /* G_OS_UNIX */
8765 * compose_undo_state_changed:
8767 * Change the sensivity of the menuentries undo and redo
8769 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8770 gint redo_state, gpointer data)
8772 Compose *compose = (Compose *)data;
8774 switch (undo_state) {
8775 case UNDO_STATE_TRUE:
8776 if (!undostruct->undo_state) {
8777 undostruct->undo_state = TRUE;
8778 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8781 case UNDO_STATE_FALSE:
8782 if (undostruct->undo_state) {
8783 undostruct->undo_state = FALSE;
8784 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8787 case UNDO_STATE_UNCHANGED:
8789 case UNDO_STATE_REFRESH:
8790 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8793 g_warning("Undo state not recognized");
8797 switch (redo_state) {
8798 case UNDO_STATE_TRUE:
8799 if (!undostruct->redo_state) {
8800 undostruct->redo_state = TRUE;
8801 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8804 case UNDO_STATE_FALSE:
8805 if (undostruct->redo_state) {
8806 undostruct->redo_state = FALSE;
8807 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8810 case UNDO_STATE_UNCHANGED:
8812 case UNDO_STATE_REFRESH:
8813 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8816 g_warning("Redo state not recognized");
8821 /* callback functions */
8823 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8824 * includes "non-client" (windows-izm) in calculation, so this calculation
8825 * may not be accurate.
8827 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8828 GtkAllocation *allocation,
8829 GtkSHRuler *shruler)
8831 if (prefs_common.show_ruler) {
8832 gint char_width = 0, char_height = 0;
8833 gint line_width_in_chars;
8835 gtkut_get_font_size(GTK_WIDGET(widget),
8836 &char_width, &char_height);
8837 line_width_in_chars =
8838 (allocation->width - allocation->x) / char_width;
8840 /* got the maximum */
8841 gtk_ruler_set_range(GTK_RULER(shruler),
8842 0.0, line_width_in_chars, 0,
8843 /*line_width_in_chars*/ char_width);
8849 static void account_activated(GtkComboBox *optmenu, gpointer data)
8851 Compose *compose = (Compose *)data;
8854 gchar *folderidentifier;
8855 gint account_id = 0;
8859 /* Get ID of active account in the combo box */
8860 menu = gtk_combo_box_get_model(optmenu);
8861 gtk_combo_box_get_active_iter(optmenu, &iter);
8862 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8864 ac = account_find_from_id(account_id);
8865 cm_return_if_fail(ac != NULL);
8867 if (ac != compose->account)
8868 compose_select_account(compose, ac, FALSE);
8870 /* Set message save folder */
8871 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8872 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8874 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8875 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8877 compose_set_save_to(compose, NULL);
8878 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8879 folderidentifier = folder_item_get_identifier(account_get_special_folder
8880 (compose->account, F_OUTBOX));
8881 compose_set_save_to(compose, folderidentifier);
8882 g_free(folderidentifier);
8886 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8887 GtkTreeViewColumn *column, Compose *compose)
8889 compose_attach_property(NULL, compose);
8892 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8895 Compose *compose = (Compose *)data;
8896 GtkTreeSelection *attach_selection;
8897 gint attach_nr_selected;
8899 if (!event) return FALSE;
8901 if (event->button == 3) {
8902 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8903 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8905 if (attach_nr_selected > 0)
8907 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8908 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8910 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8911 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8914 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8915 NULL, NULL, event->button, event->time);
8922 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8925 Compose *compose = (Compose *)data;
8927 if (!event) return FALSE;
8929 switch (event->keyval) {
8931 compose_attach_remove_selected(NULL, compose);
8937 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8939 toolbar_comp_set_sensitive(compose, allow);
8940 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8941 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8943 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8945 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8946 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8947 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8949 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8953 static void compose_send_cb(GtkAction *action, gpointer data)
8955 Compose *compose = (Compose *)data;
8957 if (prefs_common.work_offline &&
8958 !inc_offline_should_override(TRUE,
8959 _("Claws Mail needs network access in order "
8960 "to send this email.")))
8963 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8964 g_source_remove(compose->draft_timeout_tag);
8965 compose->draft_timeout_tag = -1;
8968 compose_send(compose);
8971 static void compose_send_later_cb(GtkAction *action, gpointer data)
8973 Compose *compose = (Compose *)data;
8977 compose_allow_user_actions(compose, FALSE);
8978 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8979 compose_allow_user_actions(compose, TRUE);
8983 compose_close(compose);
8984 } else if (val == -1) {
8985 alertpanel_error(_("Could not queue message."));
8986 } else if (val == -2) {
8987 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8988 } else if (val == -3) {
8989 if (privacy_peek_error())
8990 alertpanel_error(_("Could not queue message for sending:\n\n"
8991 "Signature failed: %s"), privacy_get_error());
8992 } else if (val == -4) {
8993 alertpanel_error(_("Could not queue message for sending:\n\n"
8994 "Charset conversion failed."));
8995 } else if (val == -5) {
8996 alertpanel_error(_("Could not queue message for sending:\n\n"
8997 "Couldn't get recipient encryption key."));
8998 } else if (val == -6) {
9001 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9004 #define DRAFTED_AT_EXIT "drafted_at_exit"
9005 static void compose_register_draft(MsgInfo *info)
9007 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9008 DRAFTED_AT_EXIT, NULL);
9009 FILE *fp = g_fopen(filepath, "ab");
9012 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9020 gboolean compose_draft (gpointer data, guint action)
9022 Compose *compose = (Compose *)data;
9026 MsgFlags flag = {0, 0};
9027 static gboolean lock = FALSE;
9028 MsgInfo *newmsginfo;
9030 gboolean target_locked = FALSE;
9031 gboolean err = FALSE;
9033 if (lock) return FALSE;
9035 if (compose->sending)
9038 draft = account_get_special_folder(compose->account, F_DRAFT);
9039 cm_return_val_if_fail(draft != NULL, FALSE);
9041 if (!g_mutex_trylock(compose->mutex)) {
9042 /* we don't want to lock the mutex once it's available,
9043 * because as the only other part of compose.c locking
9044 * it is compose_close - which means once unlocked,
9045 * the compose struct will be freed */
9046 debug_print("couldn't lock mutex, probably sending\n");
9052 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9053 G_DIR_SEPARATOR, compose);
9054 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9055 FILE_OP_ERROR(tmp, "fopen");
9059 /* chmod for security */
9060 if (change_file_mode_rw(fp, tmp) < 0) {
9061 FILE_OP_ERROR(tmp, "chmod");
9062 g_warning("can't change file mode\n");
9065 /* Save draft infos */
9066 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9067 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9069 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9070 gchar *savefolderid;
9072 savefolderid = compose_get_save_to(compose);
9073 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9074 g_free(savefolderid);
9076 if (compose->return_receipt) {
9077 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9079 if (compose->privacy_system) {
9080 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9081 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9082 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9085 /* Message-ID of message replying to */
9086 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9089 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9090 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9093 /* Message-ID of message forwarding to */
9094 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9097 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9098 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9102 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9103 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9105 /* end of headers */
9106 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9113 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9117 if (fclose(fp) == EOF) {
9121 if (compose->targetinfo) {
9122 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9123 flag.perm_flags = target_locked?MSG_LOCKED:0;
9125 flag.tmp_flags = MSG_DRAFT;
9127 folder_item_scan(draft);
9128 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9129 MsgInfo *tmpinfo = NULL;
9130 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9131 if (compose->msgid) {
9132 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9135 msgnum = tmpinfo->msgnum;
9136 procmsg_msginfo_free(tmpinfo);
9137 debug_print("got draft msgnum %d from scanning\n", msgnum);
9139 debug_print("didn't get draft msgnum after scanning\n");
9142 debug_print("got draft msgnum %d from adding\n", msgnum);
9148 if (action != COMPOSE_AUTO_SAVE) {
9149 if (action != COMPOSE_DRAFT_FOR_EXIT)
9150 alertpanel_error(_("Could not save draft."));
9153 gtkut_window_popup(compose->window);
9154 val = alertpanel_full(_("Could not save draft"),
9155 _("Could not save draft.\n"
9156 "Do you want to cancel exit or discard this email?"),
9157 _("_Cancel exit"), _("_Discard email"), NULL,
9158 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9159 if (val == G_ALERTALTERNATE) {
9161 g_mutex_unlock(compose->mutex); /* must be done before closing */
9162 compose_close(compose);
9166 g_mutex_unlock(compose->mutex); /* must be done before closing */
9175 if (compose->mode == COMPOSE_REEDIT) {
9176 compose_remove_reedit_target(compose, TRUE);
9179 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9182 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9184 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9186 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9187 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9188 procmsg_msginfo_set_flags(newmsginfo, 0,
9189 MSG_HAS_ATTACHMENT);
9191 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9192 compose_register_draft(newmsginfo);
9194 procmsg_msginfo_free(newmsginfo);
9197 folder_item_scan(draft);
9199 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9201 g_mutex_unlock(compose->mutex); /* must be done before closing */
9202 compose_close(compose);
9208 path = folder_item_fetch_msg(draft, msgnum);
9210 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9213 if (g_stat(path, &s) < 0) {
9214 FILE_OP_ERROR(path, "stat");
9220 procmsg_msginfo_free(compose->targetinfo);
9221 compose->targetinfo = procmsg_msginfo_new();
9222 compose->targetinfo->msgnum = msgnum;
9223 compose->targetinfo->size = (goffset)s.st_size;
9224 compose->targetinfo->mtime = s.st_mtime;
9225 compose->targetinfo->folder = draft;
9227 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9228 compose->mode = COMPOSE_REEDIT;
9230 if (action == COMPOSE_AUTO_SAVE) {
9231 compose->autosaved_draft = compose->targetinfo;
9233 compose->modified = FALSE;
9234 compose_set_title(compose);
9238 g_mutex_unlock(compose->mutex);
9242 void compose_clear_exit_drafts(void)
9244 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9245 DRAFTED_AT_EXIT, NULL);
9246 if (is_file_exist(filepath))
9247 claws_unlink(filepath);
9252 void compose_reopen_exit_drafts(void)
9254 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9255 DRAFTED_AT_EXIT, NULL);
9256 FILE *fp = g_fopen(filepath, "rb");
9260 while (fgets(buf, sizeof(buf), fp)) {
9261 gchar **parts = g_strsplit(buf, "\t", 2);
9262 const gchar *folder = parts[0];
9263 int msgnum = parts[1] ? atoi(parts[1]):-1;
9265 if (folder && *folder && msgnum > -1) {
9266 FolderItem *item = folder_find_item_from_identifier(folder);
9267 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9269 compose_reedit(info, FALSE);
9276 compose_clear_exit_drafts();
9279 static void compose_save_cb(GtkAction *action, gpointer data)
9281 Compose *compose = (Compose *)data;
9282 compose_draft(compose, COMPOSE_KEEP_EDITING);
9283 compose->rmode = COMPOSE_REEDIT;
9286 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9288 if (compose && file_list) {
9291 for ( tmp = file_list; tmp; tmp = tmp->next) {
9292 gchar *file = (gchar *) tmp->data;
9293 gchar *utf8_filename = conv_filename_to_utf8(file);
9294 compose_attach_append(compose, file, utf8_filename, NULL);
9295 compose_changed_cb(NULL, compose);
9300 g_free(utf8_filename);
9305 static void compose_attach_cb(GtkAction *action, gpointer data)
9307 Compose *compose = (Compose *)data;
9310 if (compose->redirect_filename != NULL)
9313 file_list = filesel_select_multiple_files_open(_("Select file"));
9316 compose_attach_from_list(compose, file_list, TRUE);
9317 g_list_free(file_list);
9321 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9323 Compose *compose = (Compose *)data;
9326 file_list = filesel_select_multiple_files_open(_("Select file"));
9331 for ( tmp = file_list; tmp; tmp = tmp->next) {
9332 gchar *file = (gchar *) tmp->data;
9333 gchar *filedup = g_strdup(file);
9334 gchar *shortfile = g_path_get_basename(filedup);
9335 ComposeInsertResult res;
9337 res = compose_insert_file(compose, file);
9338 if (res == COMPOSE_INSERT_READ_ERROR) {
9339 alertpanel_error(_("File '%s' could not be read."), shortfile);
9340 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9341 alertpanel_error(_("File '%s' contained invalid characters\n"
9342 "for the current encoding, insertion may be incorrect."), shortfile);
9348 g_list_free(file_list);
9352 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9354 Compose *compose = (Compose *)data;
9356 compose_insert_sig(compose, FALSE);
9359 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9363 Compose *compose = (Compose *)data;
9365 gtkut_widget_get_uposition(widget, &x, &y);
9366 if (!compose->batch) {
9367 prefs_common.compose_x = x;
9368 prefs_common.compose_y = y;
9370 if (compose->sending || compose->updating)
9372 compose_close_cb(NULL, compose);
9376 void compose_close_toolbar(Compose *compose)
9378 compose_close_cb(NULL, compose);
9381 static void compose_close_cb(GtkAction *action, gpointer data)
9383 Compose *compose = (Compose *)data;
9387 if (compose->exteditor_tag != -1) {
9388 if (!compose_ext_editor_kill(compose))
9393 if (compose->modified) {
9394 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9395 if (!g_mutex_trylock(compose->mutex)) {
9396 /* we don't want to lock the mutex once it's available,
9397 * because as the only other part of compose.c locking
9398 * it is compose_close - which means once unlocked,
9399 * the compose struct will be freed */
9400 debug_print("couldn't lock mutex, probably sending\n");
9404 val = alertpanel(_("Discard message"),
9405 _("This message has been modified. Discard it?"),
9406 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9408 val = alertpanel(_("Save changes"),
9409 _("This message has been modified. Save the latest changes?"),
9410 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9412 g_mutex_unlock(compose->mutex);
9414 case G_ALERTDEFAULT:
9415 if (prefs_common.autosave && !reedit)
9416 compose_remove_draft(compose);
9418 case G_ALERTALTERNATE:
9419 compose_draft(data, COMPOSE_QUIT_EDITING);
9426 compose_close(compose);
9429 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9431 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9432 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9433 Compose *compose = (Compose *) data;
9436 compose->out_encoding = (CharSet)value;
9439 static void compose_address_cb(GtkAction *action, gpointer data)
9441 Compose *compose = (Compose *)data;
9443 addressbook_open(compose);
9446 static void about_show_cb(GtkAction *action, gpointer data)
9451 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9453 Compose *compose = (Compose *)data;
9458 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9459 cm_return_if_fail(tmpl != NULL);
9461 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9463 val = alertpanel(_("Apply template"), msg,
9464 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9467 if (val == G_ALERTDEFAULT)
9468 compose_template_apply(compose, tmpl, TRUE);
9469 else if (val == G_ALERTALTERNATE)
9470 compose_template_apply(compose, tmpl, FALSE);
9473 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9475 Compose *compose = (Compose *)data;
9477 compose_exec_ext_editor(compose);
9480 static void compose_undo_cb(GtkAction *action, gpointer data)
9482 Compose *compose = (Compose *)data;
9483 gboolean prev_autowrap = compose->autowrap;
9485 compose->autowrap = FALSE;
9486 undo_undo(compose->undostruct);
9487 compose->autowrap = prev_autowrap;
9490 static void compose_redo_cb(GtkAction *action, gpointer data)
9492 Compose *compose = (Compose *)data;
9493 gboolean prev_autowrap = compose->autowrap;
9495 compose->autowrap = FALSE;
9496 undo_redo(compose->undostruct);
9497 compose->autowrap = prev_autowrap;
9500 static void entry_cut_clipboard(GtkWidget *entry)
9502 if (GTK_IS_EDITABLE(entry))
9503 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9504 else if (GTK_IS_TEXT_VIEW(entry))
9505 gtk_text_buffer_cut_clipboard(
9506 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9507 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9511 static void entry_copy_clipboard(GtkWidget *entry)
9513 if (GTK_IS_EDITABLE(entry))
9514 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9515 else if (GTK_IS_TEXT_VIEW(entry))
9516 gtk_text_buffer_copy_clipboard(
9517 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9518 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9521 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9522 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9524 if (GTK_IS_TEXT_VIEW(entry)) {
9525 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9526 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9527 GtkTextIter start_iter, end_iter;
9529 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9531 if (contents == NULL)
9534 /* we shouldn't delete the selection when middle-click-pasting, or we
9535 * can't mid-click-paste our own selection */
9536 if (clip != GDK_SELECTION_PRIMARY) {
9537 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9538 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9541 if (insert_place == NULL) {
9542 /* if insert_place isn't specified, insert at the cursor.
9543 * used for Ctrl-V pasting */
9544 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9545 start = gtk_text_iter_get_offset(&start_iter);
9546 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9548 /* if insert_place is specified, paste here.
9549 * used for mid-click-pasting */
9550 start = gtk_text_iter_get_offset(insert_place);
9551 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9552 if (prefs_common.primary_paste_unselects)
9553 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9557 /* paste unwrapped: mark the paste so it's not wrapped later */
9558 end = start + strlen(contents);
9559 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9560 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9561 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9562 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9563 /* rewrap paragraph now (after a mid-click-paste) */
9564 mark_start = gtk_text_buffer_get_insert(buffer);
9565 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9566 gtk_text_iter_backward_char(&start_iter);
9567 compose_beautify_paragraph(compose, &start_iter, TRUE);
9569 } else if (GTK_IS_EDITABLE(entry))
9570 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9572 compose->modified = TRUE;
9575 static void entry_allsel(GtkWidget *entry)
9577 if (GTK_IS_EDITABLE(entry))
9578 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9579 else if (GTK_IS_TEXT_VIEW(entry)) {
9580 GtkTextIter startiter, enditer;
9581 GtkTextBuffer *textbuf;
9583 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9584 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9585 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9587 gtk_text_buffer_move_mark_by_name(textbuf,
9588 "selection_bound", &startiter);
9589 gtk_text_buffer_move_mark_by_name(textbuf,
9590 "insert", &enditer);
9594 static void compose_cut_cb(GtkAction *action, gpointer data)
9596 Compose *compose = (Compose *)data;
9597 if (compose->focused_editable
9598 #ifndef GENERIC_UMPC
9599 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9602 entry_cut_clipboard(compose->focused_editable);
9605 static void compose_copy_cb(GtkAction *action, gpointer data)
9607 Compose *compose = (Compose *)data;
9608 if (compose->focused_editable
9609 #ifndef GENERIC_UMPC
9610 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9613 entry_copy_clipboard(compose->focused_editable);
9616 static void compose_paste_cb(GtkAction *action, gpointer data)
9618 Compose *compose = (Compose *)data;
9620 GtkTextBuffer *buffer;
9622 if (compose->focused_editable &&
9623 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9624 entry_paste_clipboard(compose, compose->focused_editable,
9625 prefs_common.linewrap_pastes,
9626 GDK_SELECTION_CLIPBOARD, NULL);
9630 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9632 Compose *compose = (Compose *)data;
9633 gint wrap_quote = prefs_common.linewrap_quote;
9634 if (compose->focused_editable
9635 #ifndef GENERIC_UMPC
9636 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9639 /* let text_insert() (called directly or at a later time
9640 * after the gtk_editable_paste_clipboard) know that
9641 * text is to be inserted as a quotation. implemented
9642 * by using a simple refcount... */
9643 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9644 G_OBJECT(compose->focused_editable),
9645 "paste_as_quotation"));
9646 g_object_set_data(G_OBJECT(compose->focused_editable),
9647 "paste_as_quotation",
9648 GINT_TO_POINTER(paste_as_quotation + 1));
9649 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9650 entry_paste_clipboard(compose, compose->focused_editable,
9651 prefs_common.linewrap_pastes,
9652 GDK_SELECTION_CLIPBOARD, NULL);
9653 prefs_common.linewrap_quote = wrap_quote;
9657 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9659 Compose *compose = (Compose *)data;
9661 GtkTextBuffer *buffer;
9663 if (compose->focused_editable
9664 #ifndef GENERIC_UMPC
9665 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9668 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9669 GDK_SELECTION_CLIPBOARD, NULL);
9673 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9675 Compose *compose = (Compose *)data;
9677 GtkTextBuffer *buffer;
9679 if (compose->focused_editable
9680 #ifndef GENERIC_UMPC
9681 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9684 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9685 GDK_SELECTION_CLIPBOARD, NULL);
9689 static void compose_allsel_cb(GtkAction *action, gpointer data)
9691 Compose *compose = (Compose *)data;
9692 if (compose->focused_editable
9693 #ifndef GENERIC_UMPC
9694 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9697 entry_allsel(compose->focused_editable);
9700 static void textview_move_beginning_of_line (GtkTextView *text)
9702 GtkTextBuffer *buffer;
9706 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9708 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9709 mark = gtk_text_buffer_get_insert(buffer);
9710 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9711 gtk_text_iter_set_line_offset(&ins, 0);
9712 gtk_text_buffer_place_cursor(buffer, &ins);
9715 static void textview_move_forward_character (GtkTextView *text)
9717 GtkTextBuffer *buffer;
9721 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9723 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9724 mark = gtk_text_buffer_get_insert(buffer);
9725 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9726 if (gtk_text_iter_forward_cursor_position(&ins))
9727 gtk_text_buffer_place_cursor(buffer, &ins);
9730 static void textview_move_backward_character (GtkTextView *text)
9732 GtkTextBuffer *buffer;
9736 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9738 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9739 mark = gtk_text_buffer_get_insert(buffer);
9740 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9741 if (gtk_text_iter_backward_cursor_position(&ins))
9742 gtk_text_buffer_place_cursor(buffer, &ins);
9745 static void textview_move_forward_word (GtkTextView *text)
9747 GtkTextBuffer *buffer;
9752 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9754 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9755 mark = gtk_text_buffer_get_insert(buffer);
9756 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9757 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9758 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9759 gtk_text_iter_backward_word_start(&ins);
9760 gtk_text_buffer_place_cursor(buffer, &ins);
9764 static void textview_move_backward_word (GtkTextView *text)
9766 GtkTextBuffer *buffer;
9771 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9773 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9774 mark = gtk_text_buffer_get_insert(buffer);
9775 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9776 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9777 if (gtk_text_iter_backward_word_starts(&ins, 1))
9778 gtk_text_buffer_place_cursor(buffer, &ins);
9781 static void textview_move_end_of_line (GtkTextView *text)
9783 GtkTextBuffer *buffer;
9787 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9789 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9790 mark = gtk_text_buffer_get_insert(buffer);
9791 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9792 if (gtk_text_iter_forward_to_line_end(&ins))
9793 gtk_text_buffer_place_cursor(buffer, &ins);
9796 static void textview_move_next_line (GtkTextView *text)
9798 GtkTextBuffer *buffer;
9803 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9805 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9806 mark = gtk_text_buffer_get_insert(buffer);
9807 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9808 offset = gtk_text_iter_get_line_offset(&ins);
9809 if (gtk_text_iter_forward_line(&ins)) {
9810 gtk_text_iter_set_line_offset(&ins, offset);
9811 gtk_text_buffer_place_cursor(buffer, &ins);
9815 static void textview_move_previous_line (GtkTextView *text)
9817 GtkTextBuffer *buffer;
9822 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9824 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9825 mark = gtk_text_buffer_get_insert(buffer);
9826 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9827 offset = gtk_text_iter_get_line_offset(&ins);
9828 if (gtk_text_iter_backward_line(&ins)) {
9829 gtk_text_iter_set_line_offset(&ins, offset);
9830 gtk_text_buffer_place_cursor(buffer, &ins);
9834 static void textview_delete_forward_character (GtkTextView *text)
9836 GtkTextBuffer *buffer;
9838 GtkTextIter ins, end_iter;
9840 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9842 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9843 mark = gtk_text_buffer_get_insert(buffer);
9844 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9846 if (gtk_text_iter_forward_char(&end_iter)) {
9847 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9851 static void textview_delete_backward_character (GtkTextView *text)
9853 GtkTextBuffer *buffer;
9855 GtkTextIter ins, end_iter;
9857 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9859 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9860 mark = gtk_text_buffer_get_insert(buffer);
9861 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9863 if (gtk_text_iter_backward_char(&end_iter)) {
9864 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9868 static void textview_delete_forward_word (GtkTextView *text)
9870 GtkTextBuffer *buffer;
9872 GtkTextIter ins, end_iter;
9874 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9876 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9877 mark = gtk_text_buffer_get_insert(buffer);
9878 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9880 if (gtk_text_iter_forward_word_end(&end_iter)) {
9881 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9885 static void textview_delete_backward_word (GtkTextView *text)
9887 GtkTextBuffer *buffer;
9889 GtkTextIter ins, end_iter;
9891 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9893 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9894 mark = gtk_text_buffer_get_insert(buffer);
9895 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9897 if (gtk_text_iter_backward_word_start(&end_iter)) {
9898 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9902 static void textview_delete_line (GtkTextView *text)
9904 GtkTextBuffer *buffer;
9906 GtkTextIter ins, start_iter, end_iter;
9908 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9910 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9911 mark = gtk_text_buffer_get_insert(buffer);
9912 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9915 gtk_text_iter_set_line_offset(&start_iter, 0);
9918 if (gtk_text_iter_ends_line(&end_iter)){
9919 if (!gtk_text_iter_forward_char(&end_iter))
9920 gtk_text_iter_backward_char(&start_iter);
9923 gtk_text_iter_forward_to_line_end(&end_iter);
9924 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9927 static void textview_delete_to_line_end (GtkTextView *text)
9929 GtkTextBuffer *buffer;
9931 GtkTextIter ins, end_iter;
9933 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9935 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9936 mark = gtk_text_buffer_get_insert(buffer);
9937 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9939 if (gtk_text_iter_ends_line(&end_iter))
9940 gtk_text_iter_forward_char(&end_iter);
9942 gtk_text_iter_forward_to_line_end(&end_iter);
9943 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9946 #define DO_ACTION(name, act) { \
9947 if(!strcmp(name, a_name)) { \
9951 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9953 const gchar *a_name = gtk_action_get_name(action);
9954 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9955 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9956 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9957 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9958 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9959 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9960 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9961 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9962 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9963 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9964 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9965 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9966 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9967 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9971 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9973 Compose *compose = (Compose *)data;
9974 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9975 ComposeCallAdvancedAction action = -1;
9977 action = compose_call_advanced_action_from_path(gaction);
9980 void (*do_action) (GtkTextView *text);
9981 } action_table[] = {
9982 {textview_move_beginning_of_line},
9983 {textview_move_forward_character},
9984 {textview_move_backward_character},
9985 {textview_move_forward_word},
9986 {textview_move_backward_word},
9987 {textview_move_end_of_line},
9988 {textview_move_next_line},
9989 {textview_move_previous_line},
9990 {textview_delete_forward_character},
9991 {textview_delete_backward_character},
9992 {textview_delete_forward_word},
9993 {textview_delete_backward_word},
9994 {textview_delete_line},
9995 {textview_delete_to_line_end}
9998 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10000 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10001 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10002 if (action_table[action].do_action)
10003 action_table[action].do_action(text);
10005 g_warning("Not implemented yet.");
10009 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10013 if (GTK_IS_EDITABLE(widget)) {
10014 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10015 gtk_editable_set_position(GTK_EDITABLE(widget),
10018 if (widget->parent && widget->parent->parent
10019 && widget->parent->parent->parent) {
10020 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10021 gint y = widget->allocation.y;
10022 gint height = widget->allocation.height;
10023 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10024 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10026 if (y < (int)shown->value) {
10027 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10029 if (y + height > (int)shown->value + (int)shown->page_size) {
10030 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10031 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10032 y + height - (int)shown->page_size - 1);
10034 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10035 (int)shown->upper - (int)shown->page_size - 1);
10042 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10043 compose->focused_editable = widget;
10045 #ifdef GENERIC_UMPC
10046 if (GTK_IS_TEXT_VIEW(widget)
10047 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10048 g_object_ref(compose->notebook);
10049 g_object_ref(compose->edit_vbox);
10050 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10051 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10052 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10053 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10054 g_object_unref(compose->notebook);
10055 g_object_unref(compose->edit_vbox);
10056 g_signal_handlers_block_by_func(G_OBJECT(widget),
10057 G_CALLBACK(compose_grab_focus_cb),
10059 gtk_widget_grab_focus(widget);
10060 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10061 G_CALLBACK(compose_grab_focus_cb),
10063 } else if (!GTK_IS_TEXT_VIEW(widget)
10064 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10065 g_object_ref(compose->notebook);
10066 g_object_ref(compose->edit_vbox);
10067 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10068 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10069 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10070 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10071 g_object_unref(compose->notebook);
10072 g_object_unref(compose->edit_vbox);
10073 g_signal_handlers_block_by_func(G_OBJECT(widget),
10074 G_CALLBACK(compose_grab_focus_cb),
10076 gtk_widget_grab_focus(widget);
10077 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10078 G_CALLBACK(compose_grab_focus_cb),
10084 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10086 compose->modified = TRUE;
10087 // compose_beautify_paragraph(compose, NULL, TRUE);
10088 #ifndef GENERIC_UMPC
10089 compose_set_title(compose);
10093 static void compose_wrap_cb(GtkAction *action, gpointer data)
10095 Compose *compose = (Compose *)data;
10096 compose_beautify_paragraph(compose, NULL, TRUE);
10099 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10101 Compose *compose = (Compose *)data;
10102 compose_wrap_all_full(compose, TRUE);
10105 static void compose_find_cb(GtkAction *action, gpointer data)
10107 Compose *compose = (Compose *)data;
10109 message_search_compose(compose);
10112 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10115 Compose *compose = (Compose *)data;
10116 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10117 if (compose->autowrap)
10118 compose_wrap_all_full(compose, TRUE);
10119 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10122 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10125 Compose *compose = (Compose *)data;
10126 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10129 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10131 Compose *compose = (Compose *)data;
10133 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10136 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10138 Compose *compose = (Compose *)data;
10140 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10143 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10145 g_free(compose->privacy_system);
10147 compose->privacy_system = g_strdup(account->default_privacy_system);
10148 compose_update_privacy_system_menu_item(compose, warn);
10151 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10153 Compose *compose = (Compose *)data;
10155 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10156 gtk_widget_show(compose->ruler_hbox);
10157 prefs_common.show_ruler = TRUE;
10159 gtk_widget_hide(compose->ruler_hbox);
10160 gtk_widget_queue_resize(compose->edit_vbox);
10161 prefs_common.show_ruler = FALSE;
10165 static void compose_attach_drag_received_cb (GtkWidget *widget,
10166 GdkDragContext *context,
10169 GtkSelectionData *data,
10172 gpointer user_data)
10174 Compose *compose = (Compose *)user_data;
10177 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10179 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10181 ) && gtk_drag_get_source_widget(context) !=
10182 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10183 list = uri_list_extract_filenames((const gchar *)data->data);
10184 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10185 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10186 compose_attach_append
10187 (compose, (const gchar *)tmp->data,
10188 utf8_filename, NULL);
10189 g_free(utf8_filename);
10191 if (list) compose_changed_cb(NULL, compose);
10192 list_free_strings(list);
10194 } else if (gtk_drag_get_source_widget(context)
10195 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10196 /* comes from our summaryview */
10197 SummaryView * summaryview = NULL;
10198 GSList * list = NULL, *cur = NULL;
10200 if (mainwindow_get_mainwindow())
10201 summaryview = mainwindow_get_mainwindow()->summaryview;
10204 list = summary_get_selected_msg_list(summaryview);
10206 for (cur = list; cur; cur = cur->next) {
10207 MsgInfo *msginfo = (MsgInfo *)cur->data;
10208 gchar *file = NULL;
10210 file = procmsg_get_message_file_full(msginfo,
10213 compose_attach_append(compose, (const gchar *)file,
10214 (const gchar *)file, "message/rfc822");
10218 g_slist_free(list);
10222 static gboolean compose_drag_drop(GtkWidget *widget,
10223 GdkDragContext *drag_context,
10225 guint time, gpointer user_data)
10227 /* not handling this signal makes compose_insert_drag_received_cb
10232 static void compose_insert_drag_received_cb (GtkWidget *widget,
10233 GdkDragContext *drag_context,
10236 GtkSelectionData *data,
10239 gpointer user_data)
10241 Compose *compose = (Compose *)user_data;
10244 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10247 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10249 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10251 AlertValue val = G_ALERTDEFAULT;
10253 list = uri_list_extract_filenames((const gchar *)data->data);
10254 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10255 /* Assume a list of no files, and data has ://, is a remote link */
10256 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10257 gchar *tmpfile = get_tmp_file();
10258 str_write_to_file(tmpdata, tmpfile);
10260 compose_insert_file(compose, tmpfile);
10261 claws_unlink(tmpfile);
10263 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10264 compose_beautify_paragraph(compose, NULL, TRUE);
10267 switch (prefs_common.compose_dnd_mode) {
10268 case COMPOSE_DND_ASK:
10269 val = alertpanel_full(_("Insert or attach?"),
10270 _("Do you want to insert the contents of the file(s) "
10271 "into the message body, or attach it to the email?"),
10272 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10273 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10275 case COMPOSE_DND_INSERT:
10276 val = G_ALERTALTERNATE;
10278 case COMPOSE_DND_ATTACH:
10279 val = G_ALERTOTHER;
10282 /* unexpected case */
10283 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10286 if (val & G_ALERTDISABLE) {
10287 val &= ~G_ALERTDISABLE;
10288 /* remember what action to perform by default, only if we don't click Cancel */
10289 if (val == G_ALERTALTERNATE)
10290 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10291 else if (val == G_ALERTOTHER)
10292 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10295 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10296 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10297 list_free_strings(list);
10300 } else if (val == G_ALERTOTHER) {
10301 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10302 list_free_strings(list);
10307 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10308 compose_insert_file(compose, (const gchar *)tmp->data);
10310 list_free_strings(list);
10312 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10315 #if GTK_CHECK_VERSION(2, 8, 0)
10316 /* do nothing, handled by GTK */
10318 gchar *tmpfile = get_tmp_file();
10319 str_write_to_file((const gchar *)data->data, tmpfile);
10320 compose_insert_file(compose, tmpfile);
10321 claws_unlink(tmpfile);
10323 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10327 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10330 static void compose_header_drag_received_cb (GtkWidget *widget,
10331 GdkDragContext *drag_context,
10334 GtkSelectionData *data,
10337 gpointer user_data)
10339 GtkEditable *entry = (GtkEditable *)user_data;
10340 gchar *email = (gchar *)data->data;
10342 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10345 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10346 gchar *decoded=g_new(gchar, strlen(email));
10349 email += strlen("mailto:");
10350 decode_uri(decoded, email); /* will fit */
10351 gtk_editable_delete_text(entry, 0, -1);
10352 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10353 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10357 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10360 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10362 Compose *compose = (Compose *)data;
10364 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10365 compose->return_receipt = TRUE;
10367 compose->return_receipt = FALSE;
10370 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10372 Compose *compose = (Compose *)data;
10374 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10375 compose->remove_references = TRUE;
10377 compose->remove_references = FALSE;
10380 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10381 GdkEventKey *event,
10382 ComposeHeaderEntry *headerentry)
10384 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10385 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10386 !(event->state & GDK_MODIFIER_MASK) &&
10387 (event->keyval == GDK_BackSpace) &&
10388 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10389 gtk_container_remove
10390 (GTK_CONTAINER(headerentry->compose->header_table),
10391 headerentry->combo);
10392 gtk_container_remove
10393 (GTK_CONTAINER(headerentry->compose->header_table),
10394 headerentry->entry);
10395 headerentry->compose->header_list =
10396 g_slist_remove(headerentry->compose->header_list,
10398 g_free(headerentry);
10399 } else if (event->keyval == GDK_Tab) {
10400 if (headerentry->compose->header_last == headerentry) {
10401 /* Override default next focus, and give it to subject_entry
10402 * instead of notebook tabs
10404 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10405 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10412 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10413 ComposeHeaderEntry *headerentry)
10415 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10416 compose_create_header_entry(headerentry->compose);
10417 g_signal_handlers_disconnect_matched
10418 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10419 0, 0, NULL, NULL, headerentry);
10421 /* Automatically scroll down */
10422 compose_show_first_last_header(headerentry->compose, FALSE);
10428 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10430 GtkAdjustment *vadj;
10432 cm_return_if_fail(compose);
10433 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10434 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10436 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10437 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10438 gtk_adjustment_changed(vadj);
10441 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10442 const gchar *text, gint len, Compose *compose)
10444 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10445 (G_OBJECT(compose->text), "paste_as_quotation"));
10448 cm_return_if_fail(text != NULL);
10450 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10451 G_CALLBACK(text_inserted),
10453 if (paste_as_quotation) {
10455 const gchar *qmark;
10457 GtkTextIter start_iter;
10460 len = strlen(text);
10462 new_text = g_strndup(text, len);
10464 qmark = compose_quote_char_from_context(compose);
10466 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10467 gtk_text_buffer_place_cursor(buffer, iter);
10469 pos = gtk_text_iter_get_offset(iter);
10471 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10472 _("Quote format error at line %d."));
10473 quote_fmt_reset_vartable();
10475 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10476 GINT_TO_POINTER(paste_as_quotation - 1));
10478 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10479 gtk_text_buffer_place_cursor(buffer, iter);
10480 gtk_text_buffer_delete_mark(buffer, mark);
10482 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10483 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10484 compose_beautify_paragraph(compose, &start_iter, FALSE);
10485 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10486 gtk_text_buffer_delete_mark(buffer, mark);
10488 if (strcmp(text, "\n") || compose->automatic_break
10489 || gtk_text_iter_starts_line(iter)) {
10490 GtkTextIter before_ins;
10491 gtk_text_buffer_insert(buffer, iter, text, len);
10492 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10493 before_ins = *iter;
10494 gtk_text_iter_backward_chars(&before_ins, len);
10495 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10498 /* check if the preceding is just whitespace or quote */
10499 GtkTextIter start_line;
10500 gchar *tmp = NULL, *quote = NULL;
10501 gint quote_len = 0, is_normal = 0;
10502 start_line = *iter;
10503 gtk_text_iter_set_line_offset(&start_line, 0);
10504 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10507 if (*tmp == '\0') {
10510 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10518 gtk_text_buffer_insert(buffer, iter, text, len);
10520 gtk_text_buffer_insert_with_tags_by_name(buffer,
10521 iter, text, len, "no_join", NULL);
10526 if (!paste_as_quotation) {
10527 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10528 compose_beautify_paragraph(compose, iter, FALSE);
10529 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10530 gtk_text_buffer_delete_mark(buffer, mark);
10533 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10534 G_CALLBACK(text_inserted),
10536 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10538 if (prefs_common.autosave &&
10539 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10540 compose->draft_timeout_tag != -2 /* disabled while loading */)
10541 compose->draft_timeout_tag = g_timeout_add
10542 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10544 static gint compose_defer_auto_save_draft(Compose *compose)
10546 compose->draft_timeout_tag = -1;
10547 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10552 static void compose_check_all(GtkAction *action, gpointer data)
10554 Compose *compose = (Compose *)data;
10555 if (!compose->gtkaspell)
10558 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10559 claws_spell_entry_check_all(
10560 CLAWS_SPELL_ENTRY(compose->subject_entry));
10562 gtkaspell_check_all(compose->gtkaspell);
10565 static void compose_highlight_all(GtkAction *action, gpointer data)
10567 Compose *compose = (Compose *)data;
10568 if (compose->gtkaspell) {
10569 claws_spell_entry_recheck_all(
10570 CLAWS_SPELL_ENTRY(compose->subject_entry));
10571 gtkaspell_highlight_all(compose->gtkaspell);
10575 static void compose_check_backwards(GtkAction *action, gpointer data)
10577 Compose *compose = (Compose *)data;
10578 if (!compose->gtkaspell) {
10579 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10583 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10584 claws_spell_entry_check_backwards(
10585 CLAWS_SPELL_ENTRY(compose->subject_entry));
10587 gtkaspell_check_backwards(compose->gtkaspell);
10590 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10592 Compose *compose = (Compose *)data;
10593 if (!compose->gtkaspell) {
10594 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10598 if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10599 claws_spell_entry_check_forwards_go(
10600 CLAWS_SPELL_ENTRY(compose->subject_entry));
10602 gtkaspell_check_forwards_go(compose->gtkaspell);
10607 *\brief Guess originating forward account from MsgInfo and several
10608 * "common preference" settings. Return NULL if no guess.
10610 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10612 PrefsAccount *account = NULL;
10614 cm_return_val_if_fail(msginfo, NULL);
10615 cm_return_val_if_fail(msginfo->folder, NULL);
10616 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10618 if (msginfo->folder->prefs->enable_default_account)
10619 account = account_find_from_id(msginfo->folder->prefs->default_account);
10622 account = msginfo->folder->folder->account;
10624 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10626 Xstrdup_a(to, msginfo->to, return NULL);
10627 extract_address(to);
10628 account = account_find_from_address(to, FALSE);
10631 if (!account && prefs_common.forward_account_autosel) {
10632 gchar cc[BUFFSIZE];
10633 if (!procheader_get_header_from_msginfo
10634 (msginfo, cc,sizeof cc , "Cc:")) {
10635 gchar *buf = cc + strlen("Cc:");
10636 extract_address(buf);
10637 account = account_find_from_address(buf, FALSE);
10641 if (!account && prefs_common.forward_account_autosel) {
10642 gchar deliveredto[BUFFSIZE];
10643 if (!procheader_get_header_from_msginfo
10644 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10645 gchar *buf = deliveredto + strlen("Delivered-To:");
10646 extract_address(buf);
10647 account = account_find_from_address(buf, FALSE);
10654 gboolean compose_close(Compose *compose)
10658 if (!g_mutex_trylock(compose->mutex)) {
10659 /* we have to wait for the (possibly deferred by auto-save)
10660 * drafting to be done, before destroying the compose under
10662 debug_print("waiting for drafting to finish...\n");
10663 compose_allow_user_actions(compose, FALSE);
10664 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10667 cm_return_val_if_fail(compose, FALSE);
10668 gtkut_widget_get_uposition(compose->window, &x, &y);
10669 if (!compose->batch) {
10670 prefs_common.compose_x = x;
10671 prefs_common.compose_y = y;
10673 g_mutex_unlock(compose->mutex);
10674 compose_destroy(compose);
10679 * Add entry field for each address in list.
10680 * \param compose E-Mail composition object.
10681 * \param listAddress List of (formatted) E-Mail addresses.
10683 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10686 node = listAddress;
10688 addr = ( gchar * ) node->data;
10689 compose_entry_append( compose, addr, COMPOSE_TO );
10690 node = g_list_next( node );
10694 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10695 guint action, gboolean opening_multiple)
10697 gchar *body = NULL;
10698 GSList *new_msglist = NULL;
10699 MsgInfo *tmp_msginfo = NULL;
10700 gboolean originally_enc = FALSE;
10701 gboolean originally_sig = FALSE;
10702 Compose *compose = NULL;
10703 gchar *s_system = NULL;
10705 cm_return_if_fail(msgview != NULL);
10707 cm_return_if_fail(msginfo_list != NULL);
10709 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10710 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10711 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10713 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10714 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10715 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10716 orig_msginfo, mimeinfo);
10717 if (tmp_msginfo != NULL) {
10718 new_msglist = g_slist_append(NULL, tmp_msginfo);
10720 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10721 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10722 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10724 tmp_msginfo->folder = orig_msginfo->folder;
10725 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10726 if (orig_msginfo->tags) {
10727 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10728 tmp_msginfo->folder->tags_dirty = TRUE;
10734 if (!opening_multiple)
10735 body = messageview_get_selection(msgview);
10738 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10739 procmsg_msginfo_free(tmp_msginfo);
10740 g_slist_free(new_msglist);
10742 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10744 if (compose && originally_enc) {
10745 compose_force_encryption(compose, compose->account, FALSE, s_system);
10748 if (compose && originally_sig && compose->account->default_sign_reply) {
10749 compose_force_signing(compose, compose->account, s_system);
10755 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10758 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10759 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10760 GSList *cur = msginfo_list;
10761 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10762 "messages. Opening the windows "
10763 "could take some time. Do you "
10764 "want to continue?"),
10765 g_slist_length(msginfo_list));
10766 if (g_slist_length(msginfo_list) > 9
10767 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10768 != G_ALERTALTERNATE) {
10773 /* We'll open multiple compose windows */
10774 /* let the WM place the next windows */
10775 compose_force_window_origin = FALSE;
10776 for (; cur; cur = cur->next) {
10778 tmplist.data = cur->data;
10779 tmplist.next = NULL;
10780 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10782 compose_force_window_origin = TRUE;
10784 /* forwarding multiple mails as attachments is done via a
10785 * single compose window */
10786 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10790 void compose_set_position(Compose *compose, gint pos)
10792 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10794 gtkut_text_view_set_position(text, pos);
10797 gboolean compose_search_string(Compose *compose,
10798 const gchar *str, gboolean case_sens)
10800 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10802 return gtkut_text_view_search_string(text, str, case_sens);
10805 gboolean compose_search_string_backward(Compose *compose,
10806 const gchar *str, gboolean case_sens)
10808 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10810 return gtkut_text_view_search_string_backward(text, str, case_sens);
10813 /* allocate a msginfo structure and populate its data from a compose data structure */
10814 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10816 MsgInfo *newmsginfo;
10818 gchar buf[BUFFSIZE];
10820 cm_return_val_if_fail( compose != NULL, NULL );
10822 newmsginfo = procmsg_msginfo_new();
10825 get_rfc822_date(buf, sizeof(buf));
10826 newmsginfo->date = g_strdup(buf);
10829 if (compose->from_name) {
10830 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10831 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10835 if (compose->subject_entry)
10836 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10838 /* to, cc, reply-to, newsgroups */
10839 for (list = compose->header_list; list; list = list->next) {
10840 gchar *header = gtk_editable_get_chars(
10842 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10843 gchar *entry = gtk_editable_get_chars(
10844 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10846 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10847 if ( newmsginfo->to == NULL ) {
10848 newmsginfo->to = g_strdup(entry);
10849 } else if (entry && *entry) {
10850 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10851 g_free(newmsginfo->to);
10852 newmsginfo->to = tmp;
10855 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10856 if ( newmsginfo->cc == NULL ) {
10857 newmsginfo->cc = g_strdup(entry);
10858 } else if (entry && *entry) {
10859 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10860 g_free(newmsginfo->cc);
10861 newmsginfo->cc = tmp;
10864 if ( strcasecmp(header,
10865 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10866 if ( newmsginfo->newsgroups == NULL ) {
10867 newmsginfo->newsgroups = g_strdup(entry);
10868 } else if (entry && *entry) {
10869 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10870 g_free(newmsginfo->newsgroups);
10871 newmsginfo->newsgroups = tmp;
10879 /* other data is unset */
10885 /* update compose's dictionaries from folder dict settings */
10886 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10887 FolderItem *folder_item)
10889 cm_return_if_fail(compose != NULL);
10891 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10892 FolderItemPrefs *prefs = folder_item->prefs;
10894 if (prefs->enable_default_dictionary)
10895 gtkaspell_change_dict(compose->gtkaspell,
10896 prefs->default_dictionary, FALSE);
10897 if (folder_item->prefs->enable_default_alt_dictionary)
10898 gtkaspell_change_alt_dict(compose->gtkaspell,
10899 prefs->default_alt_dictionary);
10900 if (prefs->enable_default_dictionary
10901 || prefs->enable_default_alt_dictionary)
10902 compose_spell_menu_changed(compose);