2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 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/>.
21 #include "claws-features.h"
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 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
121 #define N_ATTACH_COLS (N_COL_COLUMNS)
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
139 } ComposeCallAdvancedAction
;
143 PRIORITY_HIGHEST
= 1,
152 COMPOSE_INSERT_SUCCESS
,
153 COMPOSE_INSERT_READ_ERROR
,
154 COMPOSE_INSERT_INVALID_CHARACTER
,
155 COMPOSE_INSERT_NO_FILE
156 } ComposeInsertResult
;
160 COMPOSE_WRITE_FOR_SEND
,
161 COMPOSE_WRITE_FOR_STORE
166 COMPOSE_QUOTE_FORCED
,
173 SUBJECT_FIELD_PRESENT
,
178 #define B64_LINE_SIZE 57
179 #define B64_BUFFSIZE 77
181 #define MAX_REFERENCES_LEN 999
183 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
184 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
186 static GdkColor default_to_bgcolor
= {
193 static GdkColor default_to_color
= {
200 static GList
*compose_list
= NULL
;
201 static GSList
*extra_headers
= NULL
;
203 static Compose
*compose_generic_new (PrefsAccount
*account
,
207 GList
*listAddress
);
209 static Compose
*compose_create (PrefsAccount
*account
,
214 static void compose_entry_mark_default_to (Compose
*compose
,
215 const gchar
*address
);
216 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
217 ComposeQuoteMode quote_mode
,
221 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
222 GSList
*msginfo_list
);
223 static Compose
*compose_reply (MsgInfo
*msginfo
,
224 ComposeQuoteMode quote_mode
,
229 static Compose
*compose_reply_mode (ComposeMode mode
,
230 GSList
*msginfo_list
,
232 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
233 static void compose_update_privacy_systems_menu(Compose
*compose
);
235 static GtkWidget
*compose_account_option_menu_create
237 static void compose_set_out_encoding (Compose
*compose
);
238 static void compose_set_template_menu (Compose
*compose
);
239 static void compose_destroy (Compose
*compose
);
241 static MailField
compose_entries_set (Compose
*compose
,
243 ComposeEntryType to_type
);
244 static gint
compose_parse_header (Compose
*compose
,
246 static gint
compose_parse_manual_headers (Compose
*compose
,
248 HeaderEntry
*entries
);
249 static gchar
*compose_parse_references (const gchar
*ref
,
252 static gchar
*compose_quote_fmt (Compose
*compose
,
258 gboolean need_unescape
,
259 const gchar
*err_msg
);
261 static void compose_reply_set_entry (Compose
*compose
,
267 followup_and_reply_to
);
268 static void compose_reedit_set_entry (Compose
*compose
,
271 static void compose_insert_sig (Compose
*compose
,
273 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
276 static gboolean
compose_attach_append (Compose
*compose
,
279 const gchar
*content_type
,
280 const gchar
*charset
);
281 static void compose_attach_parts (Compose
*compose
,
284 static gboolean
compose_beautify_paragraph (Compose
*compose
,
285 GtkTextIter
*par_iter
,
287 static void compose_wrap_all (Compose
*compose
);
288 static void compose_wrap_all_full (Compose
*compose
,
291 static void compose_set_title (Compose
*compose
);
292 static void compose_select_account (Compose
*compose
,
293 PrefsAccount
*account
,
296 static PrefsAccount
*compose_current_mail_account(void);
297 /* static gint compose_send (Compose *compose); */
298 static gboolean compose_check_for_valid_recipient
300 static gboolean
compose_check_entries (Compose
*compose
,
301 gboolean check_everything
);
302 static gint
compose_write_to_file (Compose
*compose
,
305 gboolean attach_parts
);
306 static gint
compose_write_body_to_file (Compose
*compose
,
308 static gint
compose_remove_reedit_target (Compose
*compose
,
310 static void compose_remove_draft (Compose
*compose
);
311 static gint
compose_queue_sub (Compose
*compose
,
315 gboolean check_subject
,
316 gboolean remove_reedit_target
);
317 static int compose_add_attachments (Compose
*compose
,
319 static gchar
*compose_get_header (Compose
*compose
);
320 static gchar
*compose_get_manual_headers_info (Compose
*compose
);
322 static void compose_convert_header (Compose
*compose
,
327 gboolean addr_field
);
329 static void compose_attach_info_free (AttachInfo
*ainfo
);
330 static void compose_attach_remove_selected (GtkAction
*action
,
333 static void compose_template_apply (Compose
*compose
,
336 static void compose_attach_property (GtkAction
*action
,
338 static void compose_attach_property_create (gboolean
*cancelled
);
339 static void attach_property_ok (GtkWidget
*widget
,
340 gboolean
*cancelled
);
341 static void attach_property_cancel (GtkWidget
*widget
,
342 gboolean
*cancelled
);
343 static gint
attach_property_delete_event (GtkWidget
*widget
,
345 gboolean
*cancelled
);
346 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
348 gboolean
*cancelled
);
350 static void compose_exec_ext_editor (Compose
*compose
);
352 static gint
compose_exec_ext_editor_real (const gchar
*file
,
353 GdkNativeWindow socket_wid
);
354 static gboolean
compose_ext_editor_kill (Compose
*compose
);
355 static gboolean
compose_input_cb (GIOChannel
*source
,
356 GIOCondition condition
,
358 static void compose_set_ext_editor_sensitive (Compose
*compose
,
360 static gboolean
compose_get_ext_editor_cmd_valid();
361 static gboolean
compose_get_ext_editor_uses_socket();
362 static gboolean compose_ext_editor_plug_removed_cb
365 #endif /* G_OS_UNIX */
367 static void compose_undo_state_changed (UndoMain
*undostruct
,
372 static void compose_create_header_entry (Compose
*compose
);
373 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
374 gchar
*text
, ComposePrefType pref_type
);
375 static void compose_remove_header_entries(Compose
*compose
);
377 static void compose_update_priority_menu_item(Compose
* compose
);
379 static void compose_spell_menu_changed (void *data
);
380 static void compose_dict_changed (void *data
);
382 static void compose_add_field_list ( Compose
*compose
,
383 GList
*listAddress
);
385 /* callback functions */
387 static void compose_notebook_size_alloc (GtkNotebook
*notebook
,
388 GtkAllocation
*allocation
,
390 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
391 GtkAllocation
*allocation
,
392 GtkSHRuler
*shruler
);
393 static void account_activated (GtkComboBox
*optmenu
,
395 static void attach_selected (GtkTreeView
*tree_view
,
396 GtkTreePath
*tree_path
,
397 GtkTreeViewColumn
*column
,
399 static gboolean
attach_button_pressed (GtkWidget
*widget
,
400 GdkEventButton
*event
,
402 static gboolean
attach_key_pressed (GtkWidget
*widget
,
405 static void compose_send_cb (GtkAction
*action
, gpointer data
);
406 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
408 static void compose_save_cb (GtkAction
*action
,
411 static void compose_attach_cb (GtkAction
*action
,
413 static void compose_insert_file_cb (GtkAction
*action
,
415 static void compose_insert_sig_cb (GtkAction
*action
,
417 static void compose_replace_sig_cb (GtkAction
*action
,
420 static void compose_close_cb (GtkAction
*action
,
422 static void compose_print_cb (GtkAction
*action
,
425 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
427 static void compose_address_cb (GtkAction
*action
,
429 static void about_show_cb (GtkAction
*action
,
431 static void compose_template_activate_cb(GtkWidget
*widget
,
434 static void compose_ext_editor_cb (GtkAction
*action
,
437 static gint
compose_delete_cb (GtkWidget
*widget
,
441 static void compose_undo_cb (GtkAction
*action
,
443 static void compose_redo_cb (GtkAction
*action
,
445 static void compose_cut_cb (GtkAction
*action
,
447 static void compose_copy_cb (GtkAction
*action
,
449 static void compose_paste_cb (GtkAction
*action
,
451 static void compose_paste_as_quote_cb (GtkAction
*action
,
453 static void compose_paste_no_wrap_cb (GtkAction
*action
,
455 static void compose_paste_wrap_cb (GtkAction
*action
,
457 static void compose_allsel_cb (GtkAction
*action
,
460 static void compose_advanced_action_cb (GtkAction
*action
,
463 static void compose_grab_focus_cb (GtkWidget
*widget
,
466 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
469 static void compose_wrap_cb (GtkAction
*action
,
471 static void compose_wrap_all_cb (GtkAction
*action
,
473 static void compose_find_cb (GtkAction
*action
,
475 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
477 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
480 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
482 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
484 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
486 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
487 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
488 static void activate_privacy_system (Compose
*compose
,
489 PrefsAccount
*account
,
491 static void compose_use_signing(Compose
*compose
, gboolean use_signing
);
492 static void compose_use_encryption(Compose
*compose
, gboolean use_encryption
);
493 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
495 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
497 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
498 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
499 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
501 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
502 GdkDragContext
*drag_context
,
505 GtkSelectionData
*data
,
509 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
510 GdkDragContext
*drag_context
,
513 GtkSelectionData
*data
,
517 static void compose_header_drag_received_cb (GtkWidget
*widget
,
518 GdkDragContext
*drag_context
,
521 GtkSelectionData
*data
,
526 static gboolean
compose_drag_drop (GtkWidget
*widget
,
527 GdkDragContext
*drag_context
,
529 guint time
, gpointer user_data
);
530 static gboolean completion_set_focus_to_subject
535 static void text_inserted (GtkTextBuffer
*buffer
,
540 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
541 ComposeQuoteMode quote_mode
,
545 gboolean followup_and_reply_to
,
548 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
549 ComposeHeaderEntry
*headerentry
);
550 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
552 ComposeHeaderEntry
*headerentry
);
553 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
554 ComposeHeaderEntry
*headerentry
);
556 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
558 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
560 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
566 static void compose_check_all (GtkAction
*action
, gpointer data
);
567 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
568 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
569 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
572 static PrefsAccount
*compose_guess_forward_account_from_msginfo (MsgInfo
*msginfo
);
574 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
577 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
578 FolderItem
*folder_item
);
580 static void compose_attach_update_label(Compose
*compose
);
581 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
582 gboolean respect_default_to
);
583 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
);
584 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
);
586 static GtkActionEntry compose_popup_entries
[] =
588 {"Compose", NULL
, "Compose" },
589 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
590 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
591 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
592 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
595 static GtkActionEntry compose_entries
[] =
597 {"Menu", NULL
, "Menu" },
599 {"Message", NULL
, N_("_Message") },
600 {"Edit", NULL
, N_("_Edit") },
602 {"Spelling", NULL
, N_("_Spelling") },
604 {"Options", NULL
, N_("_Options") },
605 {"Tools", NULL
, N_("_Tools") },
606 {"Help", NULL
, N_("_Help") },
608 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
609 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
610 {"Message/---", NULL
, "---" },
612 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
613 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
614 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
615 {"Message/ReplaceSig", NULL
, N_("_Replace signature"), NULL
, NULL
, G_CALLBACK(compose_replace_sig_cb
) },
616 /* {"Message/---", NULL, "---" }, */
617 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
618 /* {"Message/---", NULL, "---" }, */
619 {"Message/Print", NULL
, N_("_Print"), NULL
, NULL
, G_CALLBACK(compose_print_cb
) },
620 /* {"Message/---", NULL, "---" }, */
621 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
624 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
625 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
626 {"Edit/---", NULL
, "---" },
628 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
629 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
630 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
632 {"Edit/SpecialPaste", NULL
, N_("_Special paste") },
633 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("As _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
634 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_Wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
635 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_Unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
637 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
639 {"Edit/Advanced", NULL
, N_("A_dvanced") },
640 {"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*/
641 {"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*/
642 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
643 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
644 {"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*/
645 {"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*/
646 {"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*/
647 {"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*/
648 {"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*/
649 {"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*/
650 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
651 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
652 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
653 {"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*/
655 /* {"Edit/---", NULL, "---" }, */
656 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
658 /* {"Edit/---", NULL, "---" }, */
659 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
660 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
661 /* {"Edit/---", NULL, "---" }, */
662 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
665 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
666 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
667 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
668 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
670 {"Spelling/---", NULL
, "---" },
671 {"Spelling/Options", NULL
, N_("_Options") },
676 {"Options/ReplyMode", NULL
, N_("Reply _mode") },
677 {"Options/---", NULL
, "---" },
678 {"Options/PrivacySystem", NULL
, N_("Privacy _System") },
679 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
681 /* {"Options/---", NULL, "---" }, */
683 {"Options/Priority", NULL
, N_("_Priority") },
685 {"Options/Encoding", NULL
, N_("Character _encoding") },
686 {"Options/Encoding/---", NULL
, "---" },
687 #define ENC_ACTION(cs_char,c_char,string) \
688 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
690 {"Options/Encoding/Western", NULL
, N_("Western European") },
691 {"Options/Encoding/Baltic", NULL
, N_("Baltic") },
692 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew") },
693 {"Options/Encoding/Arabic", NULL
, N_("Arabic") },
694 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic") },
695 {"Options/Encoding/Japanese", NULL
, N_("Japanese") },
696 {"Options/Encoding/Chinese", NULL
, N_("Chinese") },
697 {"Options/Encoding/Korean", NULL
, N_("Korean") },
698 {"Options/Encoding/Thai", NULL
, N_("Thai") },
701 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
703 {"Tools/Template", NULL
, N_("_Template") },
704 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
705 {"Tools/Actions", NULL
, N_("Actio_ns") },
706 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
709 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
712 static GtkToggleActionEntry compose_toggle_entries
[] =
714 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
) }, /* TOGGLE */
715 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
) }, /* TOGGLE */
716 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
) }, /* Toggle */
717 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
) }, /* Toggle */
718 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
) }, /* TOGGLE */
719 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
) }, /* TOGGLE */
720 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
) }, /* Toggle */
723 static GtkRadioActionEntry compose_radio_rm_entries
[] =
725 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
726 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
727 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
728 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
731 static GtkRadioActionEntry compose_radio_prio_entries
[] =
733 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
734 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
735 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
736 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
737 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
740 static GtkRadioActionEntry compose_radio_enc_entries
[] =
742 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_MACCYR
, C_MACCYR
, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
777 static GtkTargetEntry compose_mime_types
[] =
779 {"text/uri-list", 0, 0},
780 {"UTF8_STRING", 0, 0},
784 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
786 const GList
*compose_list
= compose_get_compose_list();
787 const GList
*elem
= NULL
;
790 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
792 Compose
*c
= (Compose
*)elem
->data
;
794 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
798 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
799 gtkut_window_popup(c
->window
);
807 static GdkColor quote_color1
=
808 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
809 static GdkColor quote_color2
=
810 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
811 static GdkColor quote_color3
=
812 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
814 static GdkColor quote_bgcolor1
=
815 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
816 static GdkColor quote_bgcolor2
=
817 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
818 static GdkColor quote_bgcolor3
=
819 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
821 static GdkColor signature_color
= {
828 static GdkColor uri_color
= {
835 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
837 GtkTextBuffer
*buffer
;
838 GdkColor black
= {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
839 #if !GTK_CHECK_VERSION(2, 24, 0)
846 buffer
= gtk_text_view_get_buffer(text
);
848 if (prefs_common
.enable_color
) {
849 /* grab the quote colors, converting from an int to a GdkColor */
850 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level1_col
,
852 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level2_col
,
854 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level3_col
,
856 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level1_bgcol
,
858 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level2_bgcol
,
860 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level3_bgcol
,
862 gtkut_convert_int_to_gdk_color(prefs_common
.signature_col
,
864 gtkut_convert_int_to_gdk_color(prefs_common
.uri_col
,
867 signature_color
= quote_color1
= quote_color2
= quote_color3
=
868 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
871 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
872 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
873 "foreground-gdk", "e_color1
,
874 "paragraph-background-gdk", "e_bgcolor1
,
876 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
877 "foreground-gdk", "e_color2
,
878 "paragraph-background-gdk", "e_bgcolor2
,
880 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
881 "foreground-gdk", "e_color3
,
882 "paragraph-background-gdk", "e_bgcolor3
,
885 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
886 "foreground-gdk", "e_color1
,
888 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
889 "foreground-gdk", "e_color2
,
891 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
892 "foreground-gdk", "e_color3
,
896 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
897 "foreground-gdk", &signature_color
,
900 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
901 "foreground-gdk", &uri_color
,
903 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
904 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
906 #if !GTK_CHECK_VERSION(2, 24, 0)
907 color
[0] = quote_color1
;
908 color
[1] = quote_color2
;
909 color
[2] = quote_color3
;
910 color
[3] = quote_bgcolor1
;
911 color
[4] = quote_bgcolor2
;
912 color
[5] = quote_bgcolor3
;
913 color
[6] = signature_color
;
914 color
[7] = uri_color
;
916 cmap
= gdk_drawable_get_colormap(gtk_widget_get_window(compose
->window
));
917 gdk_colormap_alloc_colors(cmap
, color
, 8, FALSE
, TRUE
, success
);
919 for (i
= 0; i
< 8; i
++) {
920 if (success
[i
] == FALSE
) {
921 g_warning("Compose: color allocation failed.");
922 quote_color1
= quote_color2
= quote_color3
=
923 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
=
924 signature_color
= uri_color
= black
;
930 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
933 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
936 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
938 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
941 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
943 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
946 #define SCROLL_TO_CURSOR(compose) { \
947 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
948 gtk_text_view_get_buffer( \
949 GTK_TEXT_VIEW(compose->text))); \
950 gtk_text_view_scroll_mark_onscreen( \
951 GTK_TEXT_VIEW(compose->text), \
955 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
958 if (folderidentifier
) {
959 #if !GTK_CHECK_VERSION(2, 24, 0)
960 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
));
962 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
964 prefs_common
.compose_save_to_history
= add_history(
965 prefs_common
.compose_save_to_history
, folderidentifier
);
966 #if !GTK_CHECK_VERSION(2, 24, 0)
967 combobox_set_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
),
968 prefs_common
.compose_save_to_history
);
970 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
971 prefs_common
.compose_save_to_history
);
975 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
976 if (folderidentifier
)
977 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
979 gtk_entry_set_text(GTK_ENTRY(entry
), "");
982 static gchar
*compose_get_save_to(Compose
*compose
)
985 gchar
*result
= NULL
;
986 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
987 result
= gtk_editable_get_chars(entry
, 0, -1);
990 #if !GTK_CHECK_VERSION(2, 24, 0)
991 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
));
993 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
995 prefs_common
.compose_save_to_history
= add_history(
996 prefs_common
.compose_save_to_history
, result
);
997 #if !GTK_CHECK_VERSION(2, 24, 0)
998 combobox_set_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
),
999 prefs_common
.compose_save_to_history
);
1001 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
1002 prefs_common
.compose_save_to_history
);
1008 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
1009 GList
*attach_files
, GList
*listAddress
)
1012 GtkTextView
*textview
;
1013 GtkTextBuffer
*textbuf
;
1015 const gchar
*subject_format
= NULL
;
1016 const gchar
*body_format
= NULL
;
1017 gchar
*mailto_from
= NULL
;
1018 PrefsAccount
*mailto_account
= NULL
;
1019 MsgInfo
* dummyinfo
= NULL
;
1020 gint cursor_pos
= -1;
1021 MailField mfield
= NO_FIELD_PRESENT
;
1025 /* check if mailto defines a from */
1026 if (mailto
&& *mailto
!= '\0') {
1027 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
1028 /* mailto defines a from, check if we can get account prefs from it,
1029 if not, the account prefs will be guessed using other ways, but we'll keep
1032 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
1033 if (mailto_account
== NULL
) {
1035 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
1036 extract_address(tmp_from
);
1037 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
1041 account
= mailto_account
;
1044 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1045 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
1046 account
= account_find_from_id(item
->prefs
->default_account
);
1048 /* if no account prefs set, fallback to the current one */
1049 if (!account
) account
= cur_account
;
1050 cm_return_val_if_fail(account
!= NULL
, NULL
);
1052 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1054 /* override from name if mailto asked for it */
1056 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1057 g_free(mailto_from
);
1059 /* override from name according to folder properties */
1060 if (item
&& item
->prefs
&&
1061 item
->prefs
->compose_with_format
&&
1062 item
->prefs
->compose_override_from_format
&&
1063 *item
->prefs
->compose_override_from_format
!= '\0') {
1068 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1070 /* decode \-escape sequences in the internal representation of the quote format */
1071 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1072 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1075 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1076 compose
->gtkaspell
);
1078 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1080 quote_fmt_scan_string(tmp
);
1083 buf
= quote_fmt_get_buffer();
1085 alertpanel_error(_("New message From format error."));
1087 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1088 quote_fmt_reset_vartable();
1093 compose
->replyinfo
= NULL
;
1094 compose
->fwdinfo
= NULL
;
1096 textview
= GTK_TEXT_VIEW(compose
->text
);
1097 textbuf
= gtk_text_view_get_buffer(textview
);
1098 compose_create_tags(textview
, compose
);
1100 undo_block(compose
->undostruct
);
1102 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1105 if (account
->auto_sig
)
1106 compose_insert_sig(compose
, FALSE
);
1107 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1108 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1110 if (account
->protocol
!= A_NNTP
) {
1111 if (mailto
&& *mailto
!= '\0') {
1112 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1115 compose_set_folder_prefs(compose
, item
, TRUE
);
1117 if (item
&& item
->ret_rcpt
) {
1118 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1121 if (mailto
&& *mailto
!= '\0') {
1122 if (!strchr(mailto
, '@'))
1123 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1125 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1126 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1127 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1128 mfield
= TO_FIELD_PRESENT
;
1131 * CLAWS: just don't allow return receipt request, even if the user
1132 * may want to send an email. simple but foolproof.
1134 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1136 compose_add_field_list( compose
, listAddress
);
1138 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1139 subject_format
= item
->prefs
->compose_subject_format
;
1140 body_format
= item
->prefs
->compose_body_format
;
1141 } else if (account
->compose_with_format
) {
1142 subject_format
= account
->compose_subject_format
;
1143 body_format
= account
->compose_body_format
;
1144 } else if (prefs_common
.compose_with_format
) {
1145 subject_format
= prefs_common
.compose_subject_format
;
1146 body_format
= prefs_common
.compose_body_format
;
1149 if (subject_format
|| body_format
) {
1152 && *subject_format
!= '\0' )
1154 gchar
*subject
= NULL
;
1159 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1161 /* decode \-escape sequences in the internal representation of the quote format */
1162 tmp
= g_malloc(strlen(subject_format
)+1);
1163 pref_get_unescaped_pref(tmp
, subject_format
);
1165 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1167 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1168 compose
->gtkaspell
);
1170 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1172 quote_fmt_scan_string(tmp
);
1175 buf
= quote_fmt_get_buffer();
1177 alertpanel_error(_("New message subject format error."));
1179 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1180 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1181 quote_fmt_reset_vartable();
1185 mfield
= SUBJECT_FIELD_PRESENT
;
1189 && *body_format
!= '\0' )
1192 GtkTextBuffer
*buffer
;
1193 GtkTextIter start
, end
;
1197 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1199 text
= GTK_TEXT_VIEW(compose
->text
);
1200 buffer
= gtk_text_view_get_buffer(text
);
1201 gtk_text_buffer_get_start_iter(buffer
, &start
);
1202 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1203 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1205 compose_quote_fmt(compose
, dummyinfo
,
1207 NULL
, tmp
, FALSE
, TRUE
,
1208 _("The body of the \"New message\" template has an error at line %d."));
1209 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1210 quote_fmt_reset_vartable();
1214 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1215 gtkaspell_highlight_all(compose
->gtkaspell
);
1217 mfield
= BODY_FIELD_PRESENT
;
1221 procmsg_msginfo_free( &dummyinfo
);
1227 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1228 ainfo
= (AttachInfo
*) curr
->data
;
1229 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1230 ainfo
->content_type
, ainfo
->charset
);
1234 compose_show_first_last_header(compose
, TRUE
);
1236 /* Set save folder */
1237 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1238 gchar
*folderidentifier
;
1240 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
1241 folderidentifier
= folder_item_get_identifier(item
);
1242 compose_set_save_to(compose
, folderidentifier
);
1243 g_free(folderidentifier
);
1246 /* Place cursor according to provided input (mfield) */
1248 case NO_FIELD_PRESENT
:
1249 if (compose
->header_last
)
1250 gtk_widget_grab_focus(compose
->header_last
->entry
);
1252 case TO_FIELD_PRESENT
:
1253 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1255 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1258 gtk_widget_grab_focus(compose
->subject_entry
);
1260 case SUBJECT_FIELD_PRESENT
:
1261 textview
= GTK_TEXT_VIEW(compose
->text
);
1264 textbuf
= gtk_text_view_get_buffer(textview
);
1267 mark
= gtk_text_buffer_get_insert(textbuf
);
1268 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1269 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1271 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272 * only defers where it comes to the variable body
1273 * is not null. If no body is present compose->text
1274 * will be null in which case you cannot place the
1275 * cursor inside the component so. An empty component
1276 * is therefore created before placing the cursor
1278 case BODY_FIELD_PRESENT
:
1279 cursor_pos
= quote_fmt_get_cursor_pos();
1280 if (cursor_pos
== -1)
1281 gtk_widget_grab_focus(compose
->header_last
->entry
);
1283 gtk_widget_grab_focus(compose
->text
);
1287 undo_unblock(compose
->undostruct
);
1289 if (prefs_common
.auto_exteditor
)
1290 compose_exec_ext_editor(compose
);
1292 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1294 SCROLL_TO_CURSOR(compose
);
1296 compose
->modified
= FALSE
;
1297 compose_set_title(compose
);
1299 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1304 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1305 gboolean override_pref
, const gchar
*system
)
1307 const gchar
*privacy
= NULL
;
1309 cm_return_if_fail(compose
!= NULL
);
1310 cm_return_if_fail(account
!= NULL
);
1312 if (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
)
1315 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1316 privacy
= account
->default_privacy_system
;
1320 GSList
*privacy_avail
= privacy_get_system_ids();
1321 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1322 privacy
= (gchar
*)(privacy_avail
->data
);
1325 if (privacy
!= NULL
) {
1327 g_free(compose
->privacy_system
);
1328 compose
->privacy_system
= NULL
;
1329 g_free(compose
->encdata
);
1330 compose
->encdata
= NULL
;
1332 if (compose
->privacy_system
== NULL
)
1333 compose
->privacy_system
= g_strdup(privacy
);
1334 else if (*(compose
->privacy_system
) == '\0') {
1335 g_free(compose
->privacy_system
);
1336 g_free(compose
->encdata
);
1337 compose
->encdata
= NULL
;
1338 compose
->privacy_system
= g_strdup(privacy
);
1340 compose_update_privacy_system_menu_item(compose
, FALSE
);
1341 compose_use_encryption(compose
, TRUE
);
1345 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1347 const gchar
*privacy
= NULL
;
1349 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1350 privacy
= account
->default_privacy_system
;
1354 GSList
*privacy_avail
= privacy_get_system_ids();
1355 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1356 privacy
= (gchar
*)(privacy_avail
->data
);
1360 if (privacy
!= NULL
) {
1362 g_free(compose
->privacy_system
);
1363 compose
->privacy_system
= NULL
;
1364 g_free(compose
->encdata
);
1365 compose
->encdata
= NULL
;
1367 if (compose
->privacy_system
== NULL
)
1368 compose
->privacy_system
= g_strdup(privacy
);
1369 compose_update_privacy_system_menu_item(compose
, FALSE
);
1370 compose_use_signing(compose
, TRUE
);
1374 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1378 Compose
*compose
= NULL
;
1380 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1382 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1383 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1385 list_len
= g_slist_length(msginfo_list
);
1389 case COMPOSE_REPLY_TO_ADDRESS
:
1390 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1391 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1393 case COMPOSE_REPLY_WITH_QUOTE
:
1394 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1395 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1397 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1398 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1399 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1401 case COMPOSE_REPLY_TO_SENDER
:
1402 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1403 FALSE
, FALSE
, TRUE
, body
);
1405 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1406 compose
= compose_followup_and_reply_to(msginfo
,
1407 COMPOSE_QUOTE_CHECK
,
1408 FALSE
, FALSE
, body
);
1410 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1411 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1412 FALSE
, FALSE
, TRUE
, body
);
1414 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1415 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1416 FALSE
, FALSE
, TRUE
, NULL
);
1418 case COMPOSE_REPLY_TO_ALL
:
1419 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1420 TRUE
, FALSE
, FALSE
, body
);
1422 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1423 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1424 TRUE
, FALSE
, FALSE
, body
);
1426 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1427 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1428 TRUE
, FALSE
, FALSE
, NULL
);
1430 case COMPOSE_REPLY_TO_LIST
:
1431 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1432 FALSE
, TRUE
, FALSE
, body
);
1434 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1435 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1436 FALSE
, TRUE
, FALSE
, body
);
1438 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1439 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1440 FALSE
, TRUE
, FALSE
, NULL
);
1442 case COMPOSE_FORWARD
:
1443 if (prefs_common
.forward_as_attachment
) {
1444 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1447 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1451 case COMPOSE_FORWARD_INLINE
:
1452 /* check if we reply to more than one Message */
1453 if (list_len
== 1) {
1454 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1457 /* more messages FALL THROUGH */
1458 case COMPOSE_FORWARD_AS_ATTACH
:
1459 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1461 case COMPOSE_REDIRECT
:
1462 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1465 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode
);
1468 if (compose
== NULL
) {
1469 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1473 compose
->rmode
= mode
;
1474 switch (compose
->rmode
) {
1476 case COMPOSE_REPLY_WITH_QUOTE
:
1477 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1478 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1479 debug_print("reply mode Normal\n");
1480 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1481 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1483 case COMPOSE_REPLY_TO_SENDER
:
1484 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1485 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1486 debug_print("reply mode Sender\n");
1487 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1489 case COMPOSE_REPLY_TO_ALL
:
1490 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1491 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1492 debug_print("reply mode All\n");
1493 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1495 case COMPOSE_REPLY_TO_LIST
:
1496 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1497 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1498 debug_print("reply mode List\n");
1499 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1501 case COMPOSE_REPLY_TO_ADDRESS
:
1502 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1510 static Compose
*compose_reply(MsgInfo
*msginfo
,
1511 ComposeQuoteMode quote_mode
,
1517 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1518 to_sender
, FALSE
, body
);
1521 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1522 ComposeQuoteMode quote_mode
,
1527 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1528 to_sender
, TRUE
, body
);
1531 static void compose_extract_original_charset(Compose
*compose
)
1533 MsgInfo
*info
= NULL
;
1534 if (compose
->replyinfo
) {
1535 info
= compose
->replyinfo
;
1536 } else if (compose
->fwdinfo
) {
1537 info
= compose
->fwdinfo
;
1538 } else if (compose
->targetinfo
) {
1539 info
= compose
->targetinfo
;
1542 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1543 MimeInfo
*partinfo
= mimeinfo
;
1544 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1545 partinfo
= procmime_mimeinfo_next(partinfo
);
1547 compose
->orig_charset
=
1548 g_strdup(procmime_mimeinfo_get_parameter(
1549 partinfo
, "charset"));
1551 procmime_mimeinfo_free_all(&mimeinfo
);
1555 #define SIGNAL_BLOCK(buffer) { \
1556 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1557 G_CALLBACK(compose_changed_cb), \
1559 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1560 G_CALLBACK(text_inserted), \
1564 #define SIGNAL_UNBLOCK(buffer) { \
1565 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1566 G_CALLBACK(compose_changed_cb), \
1568 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1569 G_CALLBACK(text_inserted), \
1573 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1574 ComposeQuoteMode quote_mode
,
1575 gboolean to_all
, gboolean to_ml
,
1577 gboolean followup_and_reply_to
,
1581 PrefsAccount
*account
= NULL
;
1582 GtkTextView
*textview
;
1583 GtkTextBuffer
*textbuf
;
1584 gboolean quote
= FALSE
;
1585 const gchar
*qmark
= NULL
;
1586 const gchar
*body_fmt
= NULL
;
1587 gchar
*s_system
= NULL
;
1589 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1590 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1592 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1594 cm_return_val_if_fail(account
!= NULL
, NULL
);
1596 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1598 compose
->updating
= TRUE
;
1600 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1601 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1603 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1604 if (!compose
->replyinfo
)
1605 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1607 compose_extract_original_charset(compose
);
1609 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1610 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1612 /* Set save folder */
1613 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1614 gchar
*folderidentifier
;
1616 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1617 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1618 compose_set_save_to(compose
, folderidentifier
);
1619 g_free(folderidentifier
);
1622 if (compose_parse_header(compose
, msginfo
) < 0) {
1623 compose
->updating
= FALSE
;
1624 compose_destroy(compose
);
1628 /* override from name according to folder properties */
1629 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1630 msginfo
->folder
->prefs
->reply_with_format
&&
1631 msginfo
->folder
->prefs
->reply_override_from_format
&&
1632 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1637 /* decode \-escape sequences in the internal representation of the quote format */
1638 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1639 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1642 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1643 compose
->gtkaspell
);
1645 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1647 quote_fmt_scan_string(tmp
);
1650 buf
= quote_fmt_get_buffer();
1652 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1654 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1655 quote_fmt_reset_vartable();
1660 textview
= (GTK_TEXT_VIEW(compose
->text
));
1661 textbuf
= gtk_text_view_get_buffer(textview
);
1662 compose_create_tags(textview
, compose
);
1664 undo_block(compose
->undostruct
);
1666 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1667 gtkaspell_block_check(compose
->gtkaspell
);
1670 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1671 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1672 /* use the reply format of folder (if enabled), or the account's one
1673 (if enabled) or fallback to the global reply format, which is always
1674 enabled (even if empty), and use the relevant quotemark */
1676 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1677 msginfo
->folder
->prefs
->reply_with_format
) {
1678 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1679 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1681 } else if (account
->reply_with_format
) {
1682 qmark
= account
->reply_quotemark
;
1683 body_fmt
= account
->reply_body_format
;
1686 qmark
= prefs_common
.quotemark
;
1687 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1688 body_fmt
= gettext(prefs_common
.quotefmt
);
1695 /* empty quotemark is not allowed */
1696 if (qmark
== NULL
|| *qmark
== '\0')
1698 compose_quote_fmt(compose
, compose
->replyinfo
,
1699 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1700 _("The body of the \"Reply\" template has an error at line %d."));
1701 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1702 quote_fmt_reset_vartable();
1705 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1706 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1709 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1710 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1711 compose_force_signing(compose
, account
, s_system
);
1715 SIGNAL_BLOCK(textbuf
);
1717 if (account
->auto_sig
)
1718 compose_insert_sig(compose
, FALSE
);
1720 compose_wrap_all(compose
);
1723 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1724 gtkaspell_highlight_all(compose
->gtkaspell
);
1725 gtkaspell_unblock_check(compose
->gtkaspell
);
1727 SIGNAL_UNBLOCK(textbuf
);
1729 gtk_widget_grab_focus(compose
->text
);
1731 undo_unblock(compose
->undostruct
);
1733 if (prefs_common
.auto_exteditor
)
1734 compose_exec_ext_editor(compose
);
1736 compose
->modified
= FALSE
;
1737 compose_set_title(compose
);
1739 compose
->updating
= FALSE
;
1740 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1741 SCROLL_TO_CURSOR(compose
);
1743 if (compose
->deferred_destroy
) {
1744 compose_destroy(compose
);
1752 #define INSERT_FW_HEADER(var, hdr) \
1753 if (msginfo->var && *msginfo->var) { \
1754 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1755 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1756 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1759 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1760 gboolean as_attach
, const gchar
*body
,
1761 gboolean no_extedit
,
1765 GtkTextView
*textview
;
1766 GtkTextBuffer
*textbuf
;
1767 gint cursor_pos
= -1;
1770 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1771 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1774 !(account
= compose_guess_forward_account_from_msginfo
1776 account
= cur_account
;
1778 if (!prefs_common
.forward_as_attachment
)
1779 mode
= COMPOSE_FORWARD_INLINE
;
1781 mode
= COMPOSE_FORWARD
;
1782 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1784 compose
->updating
= TRUE
;
1785 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1786 if (!compose
->fwdinfo
)
1787 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1789 compose_extract_original_charset(compose
);
1791 if (msginfo
->subject
&& *msginfo
->subject
) {
1792 gchar
*buf
, *buf2
, *p
;
1794 buf
= p
= g_strdup(msginfo
->subject
);
1795 p
+= subject_get_prefix_length(p
);
1796 memmove(buf
, p
, strlen(p
) + 1);
1798 buf2
= g_strdup_printf("Fw: %s", buf
);
1799 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1805 /* override from name according to folder properties */
1806 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1807 msginfo
->folder
->prefs
->forward_with_format
&&
1808 msginfo
->folder
->prefs
->forward_override_from_format
&&
1809 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1813 MsgInfo
*full_msginfo
= NULL
;
1816 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1818 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1820 /* decode \-escape sequences in the internal representation of the quote format */
1821 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1822 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1825 gtkaspell_block_check(compose
->gtkaspell
);
1826 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1827 compose
->gtkaspell
);
1829 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1831 quote_fmt_scan_string(tmp
);
1834 buf
= quote_fmt_get_buffer();
1836 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1838 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1839 quote_fmt_reset_vartable();
1842 procmsg_msginfo_free(&full_msginfo
);
1845 textview
= GTK_TEXT_VIEW(compose
->text
);
1846 textbuf
= gtk_text_view_get_buffer(textview
);
1847 compose_create_tags(textview
, compose
);
1849 undo_block(compose
->undostruct
);
1853 msgfile
= procmsg_get_message_file(msginfo
);
1854 if (!is_file_exist(msgfile
))
1855 g_warning("%s: file does not exist", msgfile
);
1857 compose_attach_append(compose
, msgfile
, msgfile
,
1858 "message/rfc822", NULL
);
1862 const gchar
*qmark
= NULL
;
1863 const gchar
*body_fmt
= NULL
;
1864 MsgInfo
*full_msginfo
;
1866 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1868 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1870 /* use the forward format of folder (if enabled), or the account's one
1871 (if enabled) or fallback to the global forward format, which is always
1872 enabled (even if empty), and use the relevant quotemark */
1873 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1874 msginfo
->folder
->prefs
->forward_with_format
) {
1875 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1876 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1878 } else if (account
->forward_with_format
) {
1879 qmark
= account
->forward_quotemark
;
1880 body_fmt
= account
->forward_body_format
;
1883 qmark
= prefs_common
.fw_quotemark
;
1884 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1885 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1890 /* empty quotemark is not allowed */
1891 if (qmark
== NULL
|| *qmark
== '\0')
1894 compose_quote_fmt(compose
, full_msginfo
,
1895 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1896 _("The body of the \"Forward\" template has an error at line %d."));
1897 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1898 quote_fmt_reset_vartable();
1899 compose_attach_parts(compose
, msginfo
);
1901 procmsg_msginfo_free(&full_msginfo
);
1904 SIGNAL_BLOCK(textbuf
);
1906 if (account
->auto_sig
)
1907 compose_insert_sig(compose
, FALSE
);
1909 compose_wrap_all(compose
);
1912 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1913 gtkaspell_highlight_all(compose
->gtkaspell
);
1914 gtkaspell_unblock_check(compose
->gtkaspell
);
1916 SIGNAL_UNBLOCK(textbuf
);
1918 cursor_pos
= quote_fmt_get_cursor_pos();
1919 if (cursor_pos
== -1)
1920 gtk_widget_grab_focus(compose
->header_last
->entry
);
1922 gtk_widget_grab_focus(compose
->text
);
1924 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1925 compose_exec_ext_editor(compose
);
1928 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1929 gchar
*folderidentifier
;
1931 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1932 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1933 compose_set_save_to(compose
, folderidentifier
);
1934 g_free(folderidentifier
);
1937 undo_unblock(compose
->undostruct
);
1939 compose
->modified
= FALSE
;
1940 compose_set_title(compose
);
1942 compose
->updating
= FALSE
;
1943 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1944 SCROLL_TO_CURSOR(compose
);
1946 if (compose
->deferred_destroy
) {
1947 compose_destroy(compose
);
1951 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1956 #undef INSERT_FW_HEADER
1958 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1961 GtkTextView
*textview
;
1962 GtkTextBuffer
*textbuf
;
1966 gboolean single_mail
= TRUE
;
1968 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1970 if (g_slist_length(msginfo_list
) > 1)
1971 single_mail
= FALSE
;
1973 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1974 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1977 /* guess account from first selected message */
1979 !(account
= compose_guess_forward_account_from_msginfo
1980 (msginfo_list
->data
)))
1981 account
= cur_account
;
1983 cm_return_val_if_fail(account
!= NULL
, NULL
);
1985 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1986 if (msginfo
->data
) {
1987 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1988 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1992 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1993 g_warning("no msginfo_list");
1997 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1999 compose
->updating
= TRUE
;
2001 /* override from name according to folder properties */
2002 if (msginfo_list
->data
) {
2003 MsgInfo
*msginfo
= msginfo_list
->data
;
2005 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
2006 msginfo
->folder
->prefs
->forward_with_format
&&
2007 msginfo
->folder
->prefs
->forward_override_from_format
&&
2008 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
2013 /* decode \-escape sequences in the internal representation of the quote format */
2014 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
2015 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
2018 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2019 compose
->gtkaspell
);
2021 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2023 quote_fmt_scan_string(tmp
);
2026 buf
= quote_fmt_get_buffer();
2028 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2030 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2031 quote_fmt_reset_vartable();
2037 textview
= GTK_TEXT_VIEW(compose
->text
);
2038 textbuf
= gtk_text_view_get_buffer(textview
);
2039 compose_create_tags(textview
, compose
);
2041 undo_block(compose
->undostruct
);
2042 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2043 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2045 if (!is_file_exist(msgfile
))
2046 g_warning("%s: file does not exist", msgfile
);
2048 compose_attach_append(compose
, msgfile
, msgfile
,
2049 "message/rfc822", NULL
);
2054 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2055 if (info
->subject
&& *info
->subject
) {
2056 gchar
*buf
, *buf2
, *p
;
2058 buf
= p
= g_strdup(info
->subject
);
2059 p
+= subject_get_prefix_length(p
);
2060 memmove(buf
, p
, strlen(p
) + 1);
2062 buf2
= g_strdup_printf("Fw: %s", buf
);
2063 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2069 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2070 _("Fw: multiple emails"));
2073 SIGNAL_BLOCK(textbuf
);
2075 if (account
->auto_sig
)
2076 compose_insert_sig(compose
, FALSE
);
2078 compose_wrap_all(compose
);
2080 SIGNAL_UNBLOCK(textbuf
);
2082 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2083 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2085 if (prefs_common
.auto_exteditor
)
2086 compose_exec_ext_editor(compose
);
2088 gtk_widget_grab_focus(compose
->header_last
->entry
);
2089 undo_unblock(compose
->undostruct
);
2090 compose
->modified
= FALSE
;
2091 compose_set_title(compose
);
2093 compose
->updating
= FALSE
;
2094 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2095 SCROLL_TO_CURSOR(compose
);
2097 if (compose
->deferred_destroy
) {
2098 compose_destroy(compose
);
2102 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2107 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2109 GtkTextIter start
= *iter
;
2110 GtkTextIter end_iter
;
2111 int start_pos
= gtk_text_iter_get_offset(&start
);
2113 if (!compose
->account
->sig_sep
)
2116 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2117 start_pos
+strlen(compose
->account
->sig_sep
));
2119 /* check sig separator */
2120 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2121 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2123 /* check end of line (\n) */
2124 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2125 start_pos
+strlen(compose
->account
->sig_sep
));
2126 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2127 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2128 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2129 if (!strcmp(tmp
,"\n")) {
2141 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2143 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2144 Compose
*compose
= (Compose
*)data
;
2145 FolderItem
*old_item
= NULL
;
2146 FolderItem
*new_item
= NULL
;
2147 gchar
*old_id
, *new_id
;
2149 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2150 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2153 old_item
= hookdata
->item
;
2154 new_item
= hookdata
->item2
;
2156 old_id
= folder_item_get_identifier(old_item
);
2157 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2159 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2160 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2161 compose
->targetinfo
->folder
= new_item
;
2164 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2165 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2166 compose
->replyinfo
->folder
= new_item
;
2169 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2170 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2171 compose
->fwdinfo
->folder
= new_item
;
2179 static void compose_colorize_signature(Compose
*compose
)
2181 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2183 GtkTextIter end_iter
;
2184 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2185 while (gtk_text_iter_forward_line(&iter
))
2186 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2187 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2188 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2192 #define BLOCK_WRAP() { \
2193 prev_autowrap = compose->autowrap; \
2194 buffer = gtk_text_view_get_buffer( \
2195 GTK_TEXT_VIEW(compose->text)); \
2196 compose->autowrap = FALSE; \
2198 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2199 G_CALLBACK(compose_changed_cb), \
2201 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2202 G_CALLBACK(text_inserted), \
2205 #define UNBLOCK_WRAP() { \
2206 compose->autowrap = prev_autowrap; \
2207 if (compose->autowrap) { \
2208 gint old = compose->draft_timeout_tag; \
2209 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2210 compose_wrap_all(compose); \
2211 compose->draft_timeout_tag = old; \
2214 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2215 G_CALLBACK(compose_changed_cb), \
2217 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2218 G_CALLBACK(text_inserted), \
2222 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2224 Compose
*compose
= NULL
;
2225 PrefsAccount
*account
= NULL
;
2226 GtkTextView
*textview
;
2227 GtkTextBuffer
*textbuf
;
2231 gchar buf
[BUFFSIZE
];
2232 gboolean use_signing
= FALSE
;
2233 gboolean use_encryption
= FALSE
;
2234 gchar
*privacy_system
= NULL
;
2235 int priority
= PRIORITY_NORMAL
;
2236 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2237 gboolean autowrap
= prefs_common
.autowrap
;
2238 gboolean autoindent
= prefs_common
.auto_indent
;
2239 HeaderEntry
*manual_headers
= NULL
;
2241 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2242 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2244 if (compose_put_existing_to_front(msginfo
)) {
2248 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2249 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2250 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2251 gchar queueheader_buf
[BUFFSIZE
];
2254 /* Select Account from queue headers */
2255 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2256 sizeof(queueheader_buf
), "X-Claws-Account-Id:")) {
2257 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2258 account
= account_find_from_id(id
);
2260 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2261 sizeof(queueheader_buf
), "X-Sylpheed-Account-Id:")) {
2262 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2263 account
= account_find_from_id(id
);
2265 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2266 sizeof(queueheader_buf
), "NAID:")) {
2267 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2268 account
= account_find_from_id(id
);
2270 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2271 sizeof(queueheader_buf
), "MAID:")) {
2272 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2273 account
= account_find_from_id(id
);
2275 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2276 sizeof(queueheader_buf
), "S:")) {
2277 account
= account_find_from_address(queueheader_buf
, FALSE
);
2279 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2280 sizeof(queueheader_buf
), "X-Claws-Sign:")) {
2281 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2282 use_signing
= param
;
2285 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2286 sizeof(queueheader_buf
), "X-Sylpheed-Sign:")) {
2287 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2288 use_signing
= param
;
2291 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2292 sizeof(queueheader_buf
), "X-Claws-Encrypt:")) {
2293 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2294 use_encryption
= param
;
2296 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2297 sizeof(queueheader_buf
), "X-Sylpheed-Encrypt:")) {
2298 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2299 use_encryption
= param
;
2301 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2302 sizeof(queueheader_buf
), "X-Claws-Auto-Wrapping:")) {
2303 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2306 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2307 sizeof(queueheader_buf
), "X-Claws-Auto-Indent:")) {
2308 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2311 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2312 sizeof(queueheader_buf
), "X-Claws-Privacy-System:")) {
2313 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2315 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2316 sizeof(queueheader_buf
), "X-Sylpheed-Privacy-System:")) {
2317 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2319 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2320 sizeof(queueheader_buf
), "X-Priority: ")) {
2321 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2324 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2325 sizeof(queueheader_buf
), "RMID:")) {
2326 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2327 if (tokens
[0] && tokens
[1] && tokens
[2]) {
2328 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2329 if (orig_item
!= NULL
) {
2330 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2335 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2336 sizeof(queueheader_buf
), "FMID:")) {
2337 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2338 if (tokens
[0] && tokens
[1] && tokens
[2]) {
2339 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2340 if (orig_item
!= NULL
) {
2341 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2346 /* Get manual headers */
2347 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "X-Claws-Manual-Headers:")) {
2348 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2349 if (*listmh
!= '\0') {
2350 debug_print("Got manual headers: %s\n", listmh
);
2351 manual_headers
= procheader_entries_from_str(listmh
);
2356 account
= msginfo
->folder
->folder
->account
;
2359 if (!account
&& prefs_common
.reedit_account_autosel
) {
2360 gchar from
[BUFFSIZE
];
2361 if (!procheader_get_header_from_msginfo(msginfo
, from
, sizeof(from
), "FROM:")) {
2362 extract_address(from
);
2363 account
= account_find_from_address(from
, FALSE
);
2367 account
= cur_account
;
2369 cm_return_val_if_fail(account
!= NULL
, NULL
);
2371 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2373 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2374 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2375 compose
->autowrap
= autowrap
;
2376 compose
->replyinfo
= replyinfo
;
2377 compose
->fwdinfo
= fwdinfo
;
2379 compose
->updating
= TRUE
;
2380 compose
->priority
= priority
;
2382 if (privacy_system
!= NULL
) {
2383 compose
->privacy_system
= privacy_system
;
2384 compose_use_signing(compose
, use_signing
);
2385 compose_use_encryption(compose
, use_encryption
);
2386 compose_update_privacy_system_menu_item(compose
, FALSE
);
2388 activate_privacy_system(compose
, account
, FALSE
);
2391 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2393 compose_extract_original_charset(compose
);
2395 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2396 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2397 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2398 gchar queueheader_buf
[BUFFSIZE
];
2400 /* Set message save folder */
2401 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "SCF:")) {
2402 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2403 compose_set_save_to(compose
, &queueheader_buf
[4]);
2405 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "RRCPT:")) {
2406 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2408 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2413 if (compose_parse_header(compose
, msginfo
) < 0) {
2414 compose
->updating
= FALSE
;
2415 compose_destroy(compose
);
2418 compose_reedit_set_entry(compose
, msginfo
);
2420 textview
= GTK_TEXT_VIEW(compose
->text
);
2421 textbuf
= gtk_text_view_get_buffer(textview
);
2422 compose_create_tags(textview
, compose
);
2424 mark
= gtk_text_buffer_get_insert(textbuf
);
2425 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2427 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2428 G_CALLBACK(compose_changed_cb
),
2431 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2432 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2434 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2437 fp
= procmime_get_first_text_content(msginfo
);
2440 g_warning("Can't get text part");
2444 gboolean prev_autowrap
;
2445 GtkTextBuffer
*buffer
;
2447 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2449 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2455 compose_attach_parts(compose
, msginfo
);
2457 compose_colorize_signature(compose
);
2459 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2460 G_CALLBACK(compose_changed_cb
),
2463 if (manual_headers
!= NULL
) {
2464 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2465 procheader_entries_free(manual_headers
);
2466 compose
->updating
= FALSE
;
2467 compose_destroy(compose
);
2470 procheader_entries_free(manual_headers
);
2473 gtk_widget_grab_focus(compose
->text
);
2475 if (prefs_common
.auto_exteditor
) {
2476 compose_exec_ext_editor(compose
);
2478 compose
->modified
= FALSE
;
2479 compose_set_title(compose
);
2481 compose
->updating
= FALSE
;
2482 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2483 SCROLL_TO_CURSOR(compose
);
2485 if (compose
->deferred_destroy
) {
2486 compose_destroy(compose
);
2490 compose
->sig_str
= account_get_signature_str(compose
->account
);
2492 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2497 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2504 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2507 account
= account_get_reply_account(msginfo
,
2508 prefs_common
.reply_account_autosel
);
2509 cm_return_val_if_fail(account
!= NULL
, NULL
);
2511 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2513 compose
->updating
= TRUE
;
2515 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2516 compose
->replyinfo
= NULL
;
2517 compose
->fwdinfo
= NULL
;
2519 compose_show_first_last_header(compose
, TRUE
);
2521 gtk_widget_grab_focus(compose
->header_last
->entry
);
2523 filename
= procmsg_get_message_file(msginfo
);
2525 if (filename
== NULL
) {
2526 compose
->updating
= FALSE
;
2527 compose_destroy(compose
);
2532 compose
->redirect_filename
= filename
;
2534 /* Set save folder */
2535 item
= msginfo
->folder
;
2536 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2537 gchar
*folderidentifier
;
2539 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
2540 folderidentifier
= folder_item_get_identifier(item
);
2541 compose_set_save_to(compose
, folderidentifier
);
2542 g_free(folderidentifier
);
2545 compose_attach_parts(compose
, msginfo
);
2547 if (msginfo
->subject
)
2548 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2550 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2552 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2553 _("The body of the \"Redirect\" template has an error at line %d."));
2554 quote_fmt_reset_vartable();
2555 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2557 compose_colorize_signature(compose
);
2560 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2561 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2562 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2564 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2565 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2566 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2567 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2568 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2569 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2570 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2571 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2572 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2574 if (compose
->toolbar
->draft_btn
)
2575 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2576 if (compose
->toolbar
->insert_btn
)
2577 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2578 if (compose
->toolbar
->attach_btn
)
2579 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2580 if (compose
->toolbar
->sig_btn
)
2581 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2582 if (compose
->toolbar
->exteditor_btn
)
2583 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2584 if (compose
->toolbar
->linewrap_current_btn
)
2585 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2586 if (compose
->toolbar
->linewrap_all_btn
)
2587 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2589 compose
->modified
= FALSE
;
2590 compose_set_title(compose
);
2591 compose
->updating
= FALSE
;
2592 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2593 SCROLL_TO_CURSOR(compose
);
2595 if (compose
->deferred_destroy
) {
2596 compose_destroy(compose
);
2600 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2605 const GList
*compose_get_compose_list(void)
2607 return compose_list
;
2610 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2611 ComposeEntryType type
, ComposePrefType pref_type
)
2613 const gchar
*header
;
2615 gboolean in_quote
= FALSE
;
2616 if (!address
|| *address
== '\0') return;
2623 header
= N_("Bcc:");
2625 case COMPOSE_REPLYTO
:
2626 header
= N_("Reply-To:");
2628 case COMPOSE_NEWSGROUPS
:
2629 header
= N_("Newsgroups:");
2631 case COMPOSE_FOLLOWUPTO
:
2632 header
= N_( "Followup-To:");
2634 case COMPOSE_INREPLYTO
:
2635 header
= N_( "In-Reply-To:");
2642 header
= prefs_common_translated_header_name(header
);
2644 cur
= begin
= (gchar
*)address
;
2646 /* we separate the line by commas, but not if we're inside a quoted
2648 while (*cur
!= '\0') {
2650 in_quote
= !in_quote
;
2651 if (*cur
== ',' && !in_quote
) {
2652 gchar
*tmp
= g_strdup(begin
);
2654 tmp
[cur
-begin
]='\0';
2657 while (*tmp
== ' ' || *tmp
== '\t')
2659 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2666 gchar
*tmp
= g_strdup(begin
);
2668 tmp
[cur
-begin
]='\0';
2669 while (*tmp
== ' ' || *tmp
== '\t')
2671 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2676 static void compose_entry_mark_default_to(Compose
*compose
, const gchar
*mailto
)
2681 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2682 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2683 if (gtk_entry_get_text(entry
) &&
2684 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2685 gtk_widget_modify_base(
2686 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2687 GTK_STATE_NORMAL
, &default_to_bgcolor
);
2688 gtk_widget_modify_text(
2689 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2690 GTK_STATE_NORMAL
, &default_to_color
);
2695 void compose_toolbar_cb(gint action
, gpointer data
)
2697 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2698 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2700 cm_return_if_fail(compose
!= NULL
);
2704 compose_send_cb(NULL
, compose
);
2707 compose_send_later_cb(NULL
, compose
);
2710 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2713 compose_insert_file_cb(NULL
, compose
);
2716 compose_attach_cb(NULL
, compose
);
2719 compose_insert_sig(compose
, FALSE
);
2722 compose_insert_sig(compose
, TRUE
);
2725 compose_ext_editor_cb(NULL
, compose
);
2727 case A_LINEWRAP_CURRENT
:
2728 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2730 case A_LINEWRAP_ALL
:
2731 compose_wrap_all_full(compose
, TRUE
);
2734 compose_address_cb(NULL
, compose
);
2737 case A_CHECK_SPELLING
:
2738 compose_check_all(NULL
, compose
);
2746 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2751 gchar
*subject
= NULL
;
2755 gchar
**attach
= NULL
;
2756 gchar
*inreplyto
= NULL
;
2757 MailField mfield
= NO_FIELD_PRESENT
;
2759 /* get mailto parts but skip from */
2760 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2763 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2764 mfield
= TO_FIELD_PRESENT
;
2767 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2769 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2771 if (!g_utf8_validate (subject
, -1, NULL
)) {
2772 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2773 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2776 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2778 mfield
= SUBJECT_FIELD_PRESENT
;
2781 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2782 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2785 gboolean prev_autowrap
= compose
->autowrap
;
2787 compose
->autowrap
= FALSE
;
2789 mark
= gtk_text_buffer_get_insert(buffer
);
2790 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2792 if (!g_utf8_validate (body
, -1, NULL
)) {
2793 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2794 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2797 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2799 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2801 compose
->autowrap
= prev_autowrap
;
2802 if (compose
->autowrap
)
2803 compose_wrap_all(compose
);
2804 mfield
= BODY_FIELD_PRESENT
;
2808 gint i
= 0, att
= 0;
2809 gchar
*warn_files
= NULL
;
2810 while (attach
[i
] != NULL
) {
2811 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2812 if (utf8_filename
) {
2813 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2814 gchar
*tmp
= g_strdup_printf("%s%s\n",
2815 warn_files
?warn_files
:"",
2821 g_free(utf8_filename
);
2823 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2828 alertpanel_notice(ngettext(
2829 "The following file has been attached: \n%s",
2830 "The following files have been attached: \n%s", att
), warn_files
);
2835 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2848 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2850 static HeaderEntry hentry
[] = {{"Reply-To:", NULL
, TRUE
},
2851 {"Cc:", NULL
, TRUE
},
2852 {"References:", NULL
, FALSE
},
2853 {"Bcc:", NULL
, TRUE
},
2854 {"Newsgroups:", NULL
, TRUE
},
2855 {"Followup-To:", NULL
, TRUE
},
2856 {"List-Post:", NULL
, FALSE
},
2857 {"X-Priority:", NULL
, FALSE
},
2858 {NULL
, NULL
, FALSE
}};
2874 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2876 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
2877 procheader_get_header_fields(fp
, hentry
);
2880 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2881 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2883 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2886 g_free(hentry
[H_REPLY_TO
].body
);
2887 hentry
[H_REPLY_TO
].body
= NULL
;
2889 if (hentry
[H_CC
].body
!= NULL
) {
2890 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2891 g_free(hentry
[H_CC
].body
);
2892 hentry
[H_CC
].body
= NULL
;
2894 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2895 if (compose
->mode
== COMPOSE_REEDIT
)
2896 compose
->references
= hentry
[H_REFERENCES
].body
;
2898 compose
->references
= compose_parse_references
2899 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2900 g_free(hentry
[H_REFERENCES
].body
);
2902 hentry
[H_REFERENCES
].body
= NULL
;
2904 if (hentry
[H_BCC
].body
!= NULL
) {
2905 if (compose
->mode
== COMPOSE_REEDIT
)
2907 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2908 g_free(hentry
[H_BCC
].body
);
2909 hentry
[H_BCC
].body
= NULL
;
2911 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2912 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2913 hentry
[H_NEWSGROUPS
].body
= NULL
;
2915 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2916 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2917 compose
->followup_to
=
2918 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2921 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2922 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2924 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2925 gchar
*to
= NULL
, *start
= NULL
;
2927 extract_address(hentry
[H_LIST_POST
].body
);
2928 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2929 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2931 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2932 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2935 g_free(compose
->ml_post
);
2936 compose
->ml_post
= to
;
2939 g_free(hentry
[H_LIST_POST
].body
);
2940 hentry
[H_LIST_POST
].body
= NULL
;
2943 /* CLAWS - X-Priority */
2944 if (compose
->mode
== COMPOSE_REEDIT
)
2945 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2948 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2949 g_free(hentry
[H_X_PRIORITY
].body
);
2951 hentry
[H_X_PRIORITY
].body
= NULL
;
2953 if (priority
< PRIORITY_HIGHEST
||
2954 priority
> PRIORITY_LOWEST
)
2955 priority
= PRIORITY_NORMAL
;
2957 compose
->priority
= priority
;
2960 if (compose
->mode
== COMPOSE_REEDIT
) {
2961 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2962 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
2964 if (msginfo
->msgid
&& *msginfo
->msgid
)
2965 compose
->msgid
= g_strdup(msginfo
->msgid
);
2967 if (msginfo
->msgid
&& *msginfo
->msgid
)
2968 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
2970 if (!compose
->references
) {
2971 if (msginfo
->msgid
&& *msginfo
->msgid
) {
2972 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2973 compose
->references
=
2974 g_strdup_printf("<%s>\n\t<%s>",
2978 compose
->references
=
2979 g_strconcat("<", msginfo
->msgid
, ">",
2981 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
2982 compose
->references
=
2983 g_strconcat("<", msginfo
->inreplyto
, ">",
2992 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
2997 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2999 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
3000 procheader_get_header_fields(fp
, entries
);
3004 while (he
!= NULL
&& he
->name
!= NULL
) {
3006 GtkListStore
*model
= NULL
;
3008 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3009 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3010 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3011 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3012 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3019 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3021 GSList
*ref_id_list
, *cur
;
3025 ref_id_list
= references_list_append(NULL
, ref
);
3026 if (!ref_id_list
) return NULL
;
3027 if (msgid
&& *msgid
)
3028 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3033 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3034 /* "<" + Message-ID + ">" + CR+LF+TAB */
3035 len
+= strlen((gchar
*)cur
->data
) + 5;
3037 if (len
> MAX_REFERENCES_LEN
) {
3038 /* remove second message-ID */
3039 if (ref_id_list
&& ref_id_list
->next
&&
3040 ref_id_list
->next
->next
) {
3041 g_free(ref_id_list
->next
->data
);
3042 ref_id_list
= g_slist_remove
3043 (ref_id_list
, ref_id_list
->next
->data
);
3045 slist_free_strings_full(ref_id_list
);
3052 new_ref
= g_string_new("");
3053 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3054 if (new_ref
->len
> 0)
3055 g_string_append(new_ref
, "\n\t");
3056 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3059 slist_free_strings_full(ref_id_list
);
3061 new_ref_str
= new_ref
->str
;
3062 g_string_free(new_ref
, FALSE
);
3067 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3068 const gchar
*fmt
, const gchar
*qmark
,
3069 const gchar
*body
, gboolean rewrap
,
3070 gboolean need_unescape
,
3071 const gchar
*err_msg
)
3073 MsgInfo
* dummyinfo
= NULL
;
3074 gchar
*quote_str
= NULL
;
3076 gboolean prev_autowrap
;
3077 const gchar
*trimmed_body
= body
;
3078 gint cursor_pos
= -1;
3079 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3080 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3085 SIGNAL_BLOCK(buffer
);
3088 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3089 msginfo
= dummyinfo
;
3092 if (qmark
!= NULL
) {
3094 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3095 compose
->gtkaspell
);
3097 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3099 quote_fmt_scan_string(qmark
);
3102 buf
= quote_fmt_get_buffer();
3104 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3106 Xstrdup_a(quote_str
, buf
, goto error
)
3109 if (fmt
&& *fmt
!= '\0') {
3112 while (*trimmed_body
== '\n')
3116 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3117 compose
->gtkaspell
);
3119 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3121 if (need_unescape
) {
3124 /* decode \-escape sequences in the internal representation of the quote format */
3125 tmp
= g_malloc(strlen(fmt
)+1);
3126 pref_get_unescaped_pref(tmp
, fmt
);
3127 quote_fmt_scan_string(tmp
);
3131 quote_fmt_scan_string(fmt
);
3135 buf
= quote_fmt_get_buffer();
3137 gint line
= quote_fmt_get_line();
3138 alertpanel_error(err_msg
, line
);
3144 prev_autowrap
= compose
->autowrap
;
3145 compose
->autowrap
= FALSE
;
3147 mark
= gtk_text_buffer_get_insert(buffer
);
3148 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3149 if (g_utf8_validate(buf
, -1, NULL
)) {
3150 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3152 gchar
*tmpout
= NULL
;
3153 tmpout
= conv_codeset_strdup
3154 (buf
, conv_get_locale_charset_str_no_utf8(),
3156 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3158 tmpout
= g_malloc(strlen(buf
)*2+1);
3159 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3161 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3165 cursor_pos
= quote_fmt_get_cursor_pos();
3166 if (cursor_pos
== -1)
3167 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3168 compose
->set_cursor_pos
= cursor_pos
;
3170 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3171 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3172 gtk_text_buffer_place_cursor(buffer
, &iter
);
3174 compose
->autowrap
= prev_autowrap
;
3175 if (compose
->autowrap
&& rewrap
)
3176 compose_wrap_all(compose
);
3183 SIGNAL_UNBLOCK(buffer
);
3185 procmsg_msginfo_free( &dummyinfo
);
3190 /* if ml_post is of type addr@host and from is of type
3191 * addr-anything@host, return TRUE
3193 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3195 gchar
*left_ml
= NULL
;
3196 gchar
*right_ml
= NULL
;
3197 gchar
*left_from
= NULL
;
3198 gchar
*right_from
= NULL
;
3199 gboolean result
= FALSE
;
3201 if (!ml_post
|| !from
)
3204 left_ml
= g_strdup(ml_post
);
3205 if (strstr(left_ml
, "@")) {
3206 right_ml
= strstr(left_ml
, "@")+1;
3207 *(strstr(left_ml
, "@")) = '\0';
3210 left_from
= g_strdup(from
);
3211 if (strstr(left_from
, "@")) {
3212 right_from
= strstr(left_from
, "@")+1;
3213 *(strstr(left_from
, "@")) = '\0';
3216 if (right_ml
&& right_from
3217 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3218 && !strcmp(right_from
, right_ml
)) {
3227 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3228 gboolean respect_default_to
)
3232 if (!folder
|| !folder
->prefs
)
3235 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3236 compose_entry_append(compose
, folder
->prefs
->default_to
,
3237 COMPOSE_TO
, PREF_FOLDER
);
3238 compose_entry_mark_default_to(compose
, folder
->prefs
->default_to
);
3240 if (folder
->prefs
->enable_default_cc
)
3241 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3242 COMPOSE_CC
, PREF_FOLDER
);
3243 if (folder
->prefs
->enable_default_bcc
)
3244 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3245 COMPOSE_BCC
, PREF_FOLDER
);
3246 if (folder
->prefs
->enable_default_replyto
)
3247 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3248 COMPOSE_REPLYTO
, PREF_FOLDER
);
3251 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3256 if (!compose
|| !msginfo
)
3259 if (msginfo
->subject
&& *msginfo
->subject
) {
3260 buf
= p
= g_strdup(msginfo
->subject
);
3261 p
+= subject_get_prefix_length(p
);
3262 memmove(buf
, p
, strlen(p
) + 1);
3264 buf2
= g_strdup_printf("Re: %s", buf
);
3265 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3270 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3273 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3274 gboolean to_all
, gboolean to_ml
,
3276 gboolean followup_and_reply_to
)
3278 GSList
*cc_list
= NULL
;
3281 gchar
*replyto
= NULL
;
3282 gchar
*ac_email
= NULL
;
3284 gboolean reply_to_ml
= FALSE
;
3285 gboolean default_reply_to
= FALSE
;
3287 cm_return_if_fail(compose
->account
!= NULL
);
3288 cm_return_if_fail(msginfo
!= NULL
);
3290 reply_to_ml
= to_ml
&& compose
->ml_post
;
3292 default_reply_to
= msginfo
->folder
&&
3293 msginfo
->folder
->prefs
->enable_default_reply_to
;
3295 if (compose
->account
->protocol
!= A_NNTP
) {
3296 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3298 if (reply_to_ml
&& !default_reply_to
) {
3300 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3303 /* normal answer to ml post with a reply-to */
3304 compose_entry_append(compose
,
3306 COMPOSE_TO
, PREF_ML
);
3307 if (compose
->replyto
)
3308 compose_entry_append(compose
,
3310 COMPOSE_CC
, PREF_ML
);
3312 /* answer to subscription confirmation */
3313 if (compose
->replyto
)
3314 compose_entry_append(compose
,
3316 COMPOSE_TO
, PREF_ML
);
3317 else if (msginfo
->from
)
3318 compose_entry_append(compose
,
3320 COMPOSE_TO
, PREF_ML
);
3323 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3324 compose_entry_append(compose
,
3325 msginfo
->folder
->prefs
->default_reply_to
,
3326 COMPOSE_TO
, PREF_FOLDER
);
3327 compose_entry_mark_default_to(compose
,
3328 msginfo
->folder
->prefs
->default_reply_to
);
3334 compose_entry_append(compose
, msginfo
->from
,
3335 COMPOSE_TO
, PREF_NONE
);
3337 Xstrdup_a(tmp1
, msginfo
->from
, return);
3338 extract_address(tmp1
);
3339 compose_entry_append(compose
,
3340 (!account_find_from_address(tmp1
, FALSE
))
3343 COMPOSE_TO
, PREF_NONE
);
3344 if (compose
->replyto
)
3345 compose_entry_append(compose
,
3347 COMPOSE_CC
, PREF_NONE
);
3349 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3350 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3351 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3352 if (compose
->replyto
) {
3353 compose_entry_append(compose
,
3355 COMPOSE_TO
, PREF_NONE
);
3357 compose_entry_append(compose
,
3358 msginfo
->from
? msginfo
->from
: "",
3359 COMPOSE_TO
, PREF_NONE
);
3362 /* replying to own mail, use original recp */
3363 compose_entry_append(compose
,
3364 msginfo
->to
? msginfo
->to
: "",
3365 COMPOSE_TO
, PREF_NONE
);
3366 compose_entry_append(compose
,
3367 msginfo
->cc
? msginfo
->cc
: "",
3368 COMPOSE_CC
, PREF_NONE
);
3373 if (to_sender
|| (compose
->followup_to
&&
3374 !strncmp(compose
->followup_to
, "poster", 6)))
3375 compose_entry_append
3377 (compose
->replyto
? compose
->replyto
:
3378 msginfo
->from
? msginfo
->from
: ""),
3379 COMPOSE_TO
, PREF_NONE
);
3381 else if (followup_and_reply_to
|| to_all
) {
3382 compose_entry_append
3384 (compose
->replyto
? compose
->replyto
:
3385 msginfo
->from
? msginfo
->from
: ""),
3386 COMPOSE_TO
, PREF_NONE
);
3388 compose_entry_append
3390 compose
->followup_to
? compose
->followup_to
:
3391 compose
->newsgroups
? compose
->newsgroups
: "",
3392 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3394 compose_entry_append
3396 msginfo
->cc
? msginfo
->cc
: "",
3397 COMPOSE_CC
, PREF_NONE
);
3400 compose_entry_append
3402 compose
->followup_to
? compose
->followup_to
:
3403 compose
->newsgroups
? compose
->newsgroups
: "",
3404 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3406 compose_reply_set_subject(compose
, msginfo
);
3408 if (to_ml
&& compose
->ml_post
) return;
3409 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3411 if (compose
->replyto
) {
3412 Xstrdup_a(replyto
, compose
->replyto
, return);
3413 extract_address(replyto
);
3415 if (msginfo
->from
) {
3416 Xstrdup_a(from
, msginfo
->from
, return);
3417 extract_address(from
);
3420 if (replyto
&& from
)
3421 cc_list
= address_list_append_with_comments(cc_list
, from
);
3422 if (to_all
&& msginfo
->folder
&&
3423 msginfo
->folder
->prefs
->enable_default_reply_to
)
3424 cc_list
= address_list_append_with_comments(cc_list
,
3425 msginfo
->folder
->prefs
->default_reply_to
);
3426 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3427 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3429 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3432 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3433 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3434 extract_address(addr
);
3436 if (strcmp(ac_email
, addr
))
3437 compose_entry_append(compose
, (gchar
*)cur
->data
,
3438 COMPOSE_CC
, PREF_NONE
);
3440 debug_print("Cc address same as compose account's, ignoring\n");
3445 slist_free_strings_full(cc_list
);
3451 #define SET_ENTRY(entry, str) \
3454 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3457 #define SET_ADDRESS(type, str) \
3460 compose_entry_append(compose, str, type, PREF_NONE); \
3463 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3465 cm_return_if_fail(msginfo
!= NULL
);
3467 SET_ENTRY(subject_entry
, msginfo
->subject
);
3468 SET_ENTRY(from_name
, msginfo
->from
);
3469 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3470 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3471 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3472 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3473 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3474 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3476 compose_update_priority_menu_item(compose
);
3477 compose_update_privacy_system_menu_item(compose
, FALSE
);
3478 compose_show_first_last_header(compose
, TRUE
);
3484 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3486 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3487 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3489 GtkTextIter iter
, iter_end
;
3490 gint cur_pos
, ins_pos
;
3491 gboolean prev_autowrap
;
3492 gboolean found
= FALSE
;
3493 gboolean exists
= FALSE
;
3495 cm_return_if_fail(compose
->account
!= NULL
);
3499 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3500 G_CALLBACK(compose_changed_cb
),
3503 mark
= gtk_text_buffer_get_insert(buffer
);
3504 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3505 cur_pos
= gtk_text_iter_get_offset (&iter
);
3508 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3510 exists
= (compose
->sig_str
!= NULL
);
3513 GtkTextIter first_iter
, start_iter
, end_iter
;
3515 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3517 if (!exists
|| compose
->sig_str
[0] == '\0')
3520 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3521 compose
->signature_tag
);
3524 /* include previous \n\n */
3525 gtk_text_iter_backward_chars(&first_iter
, 1);
3526 start_iter
= first_iter
;
3527 end_iter
= first_iter
;
3529 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3530 compose
->signature_tag
);
3531 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3532 compose
->signature_tag
);
3534 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3540 g_free(compose
->sig_str
);
3541 compose
->sig_str
= account_get_signature_str(compose
->account
);
3543 cur_pos
= gtk_text_iter_get_offset(&iter
);
3545 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3546 g_free(compose
->sig_str
);
3547 compose
->sig_str
= NULL
;
3549 if (compose
->sig_inserted
== FALSE
)
3550 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3551 compose
->sig_inserted
= TRUE
;
3553 cur_pos
= gtk_text_iter_get_offset(&iter
);
3554 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3556 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3557 gtk_text_iter_forward_chars(&iter
, 1);
3558 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3559 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3561 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3562 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3565 /* put the cursor where it should be
3566 * either where the quote_fmt says, either where it was */
3567 if (compose
->set_cursor_pos
< 0)
3568 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3570 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3571 compose
->set_cursor_pos
);
3573 compose
->set_cursor_pos
= -1;
3574 gtk_text_buffer_place_cursor(buffer
, &iter
);
3575 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3576 G_CALLBACK(compose_changed_cb
),
3582 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3585 GtkTextBuffer
*buffer
;
3588 const gchar
*cur_encoding
;
3589 gchar buf
[BUFFSIZE
];
3592 gboolean prev_autowrap
;
3595 GString
*file_contents
= NULL
;
3596 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3598 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3600 /* get the size of the file we are about to insert */
3601 ret
= g_stat(file
, &file_stat
);
3603 gchar
*shortfile
= g_path_get_basename(file
);
3604 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3606 return COMPOSE_INSERT_NO_FILE
;
3607 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3609 /* ask user for confirmation if the file is large */
3610 if (prefs_common
.warn_large_insert_size
< 0 ||
3611 file_stat
.st_size
> (prefs_common
.warn_large_insert_size
* 1024)) {
3615 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3616 "in the message body. Are you sure you want to do that?"),
3617 to_human_readable(file_stat
.st_size
));
3618 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3619 g_strconcat("+", _("_Insert"), NULL
), NULL
, TRUE
, NULL
, ALERT_QUESTION
, G_ALERTDEFAULT
);
3622 /* do we ask for confirmation next time? */
3623 if (aval
& G_ALERTDISABLE
) {
3624 /* no confirmation next time, disable feature in preferences */
3625 aval
&= ~G_ALERTDISABLE
;
3626 prefs_common
.warn_large_insert
= FALSE
;
3629 /* abort file insertion if user canceled action */
3630 if (aval
!= G_ALERTALTERNATE
) {
3631 return COMPOSE_INSERT_NO_FILE
;
3637 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
3638 FILE_OP_ERROR(file
, "fopen");
3639 return COMPOSE_INSERT_READ_ERROR
;
3642 prev_autowrap
= compose
->autowrap
;
3643 compose
->autowrap
= FALSE
;
3645 text
= GTK_TEXT_VIEW(compose
->text
);
3646 buffer
= gtk_text_view_get_buffer(text
);
3647 mark
= gtk_text_buffer_get_insert(buffer
);
3648 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3650 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3651 G_CALLBACK(text_inserted
),
3654 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3656 file_contents
= g_string_new("");
3657 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3660 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3661 str
= g_strdup(buf
);
3663 codeconv_set_strict(TRUE
);
3664 str
= conv_codeset_strdup
3665 (buf
, cur_encoding
, CS_INTERNAL
);
3666 codeconv_set_strict(FALSE
);
3669 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3675 /* strip <CR> if DOS/Windows file,
3676 replace <CR> with <LF> if Macintosh file. */
3679 if (len
> 0 && str
[len
- 1] != '\n') {
3681 if (str
[len
] == '\r') str
[len
] = '\n';
3684 file_contents
= g_string_append(file_contents
, str
);
3688 if (result
== COMPOSE_INSERT_SUCCESS
) {
3689 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3691 compose_changed_cb(NULL
, compose
);
3692 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3693 G_CALLBACK(text_inserted
),
3695 compose
->autowrap
= prev_autowrap
;
3696 if (compose
->autowrap
)
3697 compose_wrap_all(compose
);
3700 g_string_free(file_contents
, TRUE
);
3706 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3707 const gchar
*filename
,
3708 const gchar
*content_type
,
3709 const gchar
*charset
)
3717 GtkListStore
*store
;
3719 gboolean has_binary
= FALSE
;
3721 if (!is_file_exist(file
)) {
3722 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3723 gboolean result
= FALSE
;
3724 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3725 result
= compose_attach_append(
3726 compose
, file_from_uri
,
3727 filename
, content_type
,
3730 g_free(file_from_uri
);
3733 alertpanel_error("File %s doesn't exist\n", filename
);
3736 if ((size
= get_file_size(file
)) < 0) {
3737 alertpanel_error("Can't get file size of %s\n", filename
);
3741 /* In batch mode, we allow 0-length files to be attached no questions asked */
3742 if (size
== 0 && !compose
->batch
) {
3743 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3744 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3745 GTK_STOCK_CANCEL
, g_strconcat("+", _("_Attach anyway"), NULL
), NULL
, FALSE
,
3746 NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
3749 if (aval
!= G_ALERTALTERNATE
) {
3753 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
3754 alertpanel_error(_("Can't read %s."), filename
);
3759 ainfo
= g_new0(AttachInfo
, 1);
3760 auto_ainfo
= g_auto_pointer_new_with_free
3761 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3762 ainfo
->file
= g_strdup(file
);
3765 ainfo
->content_type
= g_strdup(content_type
);
3766 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3768 MsgFlags flags
= {0, 0};
3770 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3771 ainfo
->encoding
= ENC_7BIT
;
3773 ainfo
->encoding
= ENC_8BIT
;
3775 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3776 if (msginfo
&& msginfo
->subject
)
3777 name
= g_strdup(msginfo
->subject
);
3779 name
= g_path_get_basename(filename
? filename
: file
);
3781 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3783 procmsg_msginfo_free(&msginfo
);
3785 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3786 ainfo
->charset
= g_strdup(charset
);
3787 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3789 ainfo
->encoding
= ENC_BASE64
;
3791 name
= g_path_get_basename(filename
? filename
: file
);
3792 ainfo
->name
= g_strdup(name
);
3796 ainfo
->content_type
= procmime_get_mime_type(file
);
3797 if (!ainfo
->content_type
) {
3798 ainfo
->content_type
=
3799 g_strdup("application/octet-stream");
3800 ainfo
->encoding
= ENC_BASE64
;
3801 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3803 procmime_get_encoding_for_text_file(file
, &has_binary
);
3805 ainfo
->encoding
= ENC_BASE64
;
3806 name
= g_path_get_basename(filename
? filename
: file
);
3807 ainfo
->name
= g_strdup(name
);
3811 if (ainfo
->name
!= NULL
3812 && !strcmp(ainfo
->name
, ".")) {
3813 g_free(ainfo
->name
);
3817 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3818 g_free(ainfo
->content_type
);
3819 ainfo
->content_type
= g_strdup("application/octet-stream");
3820 g_free(ainfo
->charset
);
3821 ainfo
->charset
= NULL
;
3824 ainfo
->size
= (goffset
)size
;
3825 size_text
= to_human_readable((goffset
)size
);
3827 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3828 (GTK_TREE_VIEW(compose
->attach_clist
)));
3830 gtk_list_store_append(store
, &iter
);
3831 gtk_list_store_set(store
, &iter
,
3832 COL_MIMETYPE
, ainfo
->content_type
,
3833 COL_SIZE
, size_text
,
3834 COL_NAME
, ainfo
->name
,
3835 COL_CHARSET
, ainfo
->charset
,
3837 COL_AUTODATA
, auto_ainfo
,
3840 g_auto_pointer_free(auto_ainfo
);
3841 compose_attach_update_label(compose
);
3845 static void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3847 compose
->use_signing
= use_signing
;
3848 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3851 static void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3853 compose
->use_encryption
= use_encryption
;
3854 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3857 #define NEXT_PART_NOT_CHILD(info) \
3859 node = info->node; \
3860 while (node->children) \
3861 node = g_node_last_child(node); \
3862 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3865 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3869 MimeInfo
*firsttext
= NULL
;
3870 MimeInfo
*encrypted
= NULL
;
3873 const gchar
*partname
= NULL
;
3875 mimeinfo
= procmime_scan_message(msginfo
);
3876 if (!mimeinfo
) return;
3878 if (mimeinfo
->node
->children
== NULL
) {
3879 procmime_mimeinfo_free_all(&mimeinfo
);
3883 /* find first content part */
3884 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3885 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3886 child
= (MimeInfo
*)child
->node
->children
->data
;
3889 if (child
->type
== MIMETYPE_TEXT
) {
3891 debug_print("First text part found\n");
3892 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3893 child
->type
== MIMETYPE_APPLICATION
&&
3894 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3895 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3898 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3899 while (child
!= NULL
) {
3902 if (child
== encrypted
) {
3903 /* skip this part of tree */
3904 NEXT_PART_NOT_CHILD(child
);
3908 if (child
->type
== MIMETYPE_MULTIPART
) {
3909 /* get the actual content */
3910 child
= procmime_mimeinfo_next(child
);
3914 if (child
== firsttext
) {
3915 child
= procmime_mimeinfo_next(child
);
3919 outfile
= procmime_get_tmp_file_name(child
);
3920 if ((err
= procmime_get_part(outfile
, child
)) < 0)
3921 g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err
));
3923 gchar
*content_type
;
3925 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
3927 /* if we meet a pgp signature, we don't attach it, but
3928 * we force signing. */
3929 if ((strcmp(content_type
, "application/pgp-signature") &&
3930 strcmp(content_type
, "application/pkcs7-signature") &&
3931 strcmp(content_type
, "application/x-pkcs7-signature"))
3932 || compose
->mode
== COMPOSE_REDIRECT
) {
3933 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
3934 if (partname
== NULL
)
3935 partname
= procmime_mimeinfo_get_parameter(child
, "name");
3936 if (partname
== NULL
)
3938 compose_attach_append(compose
, outfile
,
3939 partname
, content_type
,
3940 procmime_mimeinfo_get_parameter(child
, "charset"));
3942 compose_force_signing(compose
, compose
->account
, NULL
);
3944 g_free(content_type
);
3947 NEXT_PART_NOT_CHILD(child
);
3949 procmime_mimeinfo_free_all(&mimeinfo
);
3952 #undef NEXT_PART_NOT_CHILD
3957 WAIT_FOR_INDENT_CHAR
,
3958 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
3961 /* return indent length, we allow:
3962 indent characters followed by indent characters or spaces/tabs,
3963 alphabets and numbers immediately followed by indent characters,
3964 and the repeating sequences of the above
3965 If quote ends with multiple spaces, only the first one is included. */
3966 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
3967 const GtkTextIter
*start
, gint
*len
)
3969 GtkTextIter iter
= *start
;
3973 IndentState state
= WAIT_FOR_INDENT_CHAR
;
3976 gint alnum_count
= 0;
3977 gint space_count
= 0;
3980 if (prefs_common
.quote_chars
== NULL
) {
3984 while (!gtk_text_iter_ends_line(&iter
)) {
3985 wc
= gtk_text_iter_get_char(&iter
);
3986 if (g_unichar_iswide(wc
))
3988 clen
= g_unichar_to_utf8(wc
, ch
);
3992 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
3993 is_space
= g_unichar_isspace(wc
);
3995 if (state
== WAIT_FOR_INDENT_CHAR
) {
3996 if (!is_indent
&& !g_unichar_isalnum(wc
))
3999 quote_len
+= alnum_count
+ space_count
+ 1;
4000 alnum_count
= space_count
= 0;
4001 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4004 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4005 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4009 else if (is_indent
) {
4010 quote_len
+= alnum_count
+ space_count
+ 1;
4011 alnum_count
= space_count
= 0;
4014 state
= WAIT_FOR_INDENT_CHAR
;
4018 gtk_text_iter_forward_char(&iter
);
4021 if (quote_len
> 0 && space_count
> 0)
4027 if (quote_len
> 0) {
4029 gtk_text_iter_forward_chars(&iter
, quote_len
);
4030 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4036 /* return >0 if the line is itemized */
4037 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4038 const GtkTextIter
*start
)
4040 GtkTextIter iter
= *start
;
4045 if (gtk_text_iter_ends_line(&iter
))
4050 wc
= gtk_text_iter_get_char(&iter
);
4051 if (!g_unichar_isspace(wc
))
4053 gtk_text_iter_forward_char(&iter
);
4054 if (gtk_text_iter_ends_line(&iter
))
4058 clen
= g_unichar_to_utf8(wc
, ch
);
4062 if (!strchr("*-+", ch
[0]))
4065 gtk_text_iter_forward_char(&iter
);
4066 if (gtk_text_iter_ends_line(&iter
))
4068 wc
= gtk_text_iter_get_char(&iter
);
4069 if (g_unichar_isspace(wc
)) {
4075 /* return the string at the start of the itemization */
4076 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4077 const GtkTextIter
*start
)
4079 GtkTextIter iter
= *start
;
4082 GString
*item_chars
= g_string_new("");
4085 if (gtk_text_iter_ends_line(&iter
))
4090 wc
= gtk_text_iter_get_char(&iter
);
4091 if (!g_unichar_isspace(wc
))
4093 gtk_text_iter_forward_char(&iter
);
4094 if (gtk_text_iter_ends_line(&iter
))
4096 g_string_append_unichar(item_chars
, wc
);
4099 str
= item_chars
->str
;
4100 g_string_free(item_chars
, FALSE
);
4104 /* return the number of spaces at a line's start */
4105 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4106 const GtkTextIter
*start
)
4108 GtkTextIter iter
= *start
;
4111 if (gtk_text_iter_ends_line(&iter
))
4115 wc
= gtk_text_iter_get_char(&iter
);
4116 if (!g_unichar_isspace(wc
))
4119 gtk_text_iter_forward_char(&iter
);
4120 if (gtk_text_iter_ends_line(&iter
))
4124 gtk_text_iter_forward_char(&iter
);
4125 if (gtk_text_iter_ends_line(&iter
))
4130 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4131 const GtkTextIter
*start
,
4132 GtkTextIter
*break_pos
,
4136 GtkTextIter iter
= *start
, line_end
= *start
;
4137 PangoLogAttr
*attrs
;
4144 gboolean can_break
= FALSE
;
4145 gboolean do_break
= FALSE
;
4146 gboolean was_white
= FALSE
;
4147 gboolean prev_dont_break
= FALSE
;
4149 gtk_text_iter_forward_to_line_end(&line_end
);
4150 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4151 len
= g_utf8_strlen(str
, -1);
4155 g_warning("compose_get_line_break_pos: len = 0!");
4159 /* g_print("breaking line: %d: %s (len = %d)\n",
4160 gtk_text_iter_get_line(&iter), str, len); */
4162 attrs
= g_new(PangoLogAttr
, len
+ 1);
4164 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4168 /* skip quote and leading spaces */
4169 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4172 wc
= g_utf8_get_char(p
);
4173 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4175 if (g_unichar_iswide(wc
))
4177 else if (*p
== '\t')
4181 p
= g_utf8_next_char(p
);
4184 for (; *p
!= '\0' && i
< len
; i
++) {
4185 PangoLogAttr
*attr
= attrs
+ i
;
4189 if (attr
->is_line_break
&& can_break
&& was_white
&& !prev_dont_break
)
4192 was_white
= attr
->is_white
;
4194 /* don't wrap URI */
4195 if ((uri_len
= get_uri_len(p
)) > 0) {
4197 if (pos
> 0 && col
> max_col
) {
4207 wc
= g_utf8_get_char(p
);
4208 if (g_unichar_iswide(wc
)) {
4210 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4212 } else if (*p
== '\t')
4216 if (pos
> 0 && col
> max_col
) {
4221 if (*p
== '-' || *p
== '/')
4222 prev_dont_break
= TRUE
;
4224 prev_dont_break
= FALSE
;
4226 p
= g_utf8_next_char(p
);
4230 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4235 *break_pos
= *start
;
4236 gtk_text_iter_set_line_offset(break_pos
, pos
);
4241 static gboolean
compose_join_next_line(Compose
*compose
,
4242 GtkTextBuffer
*buffer
,
4244 const gchar
*quote_str
)
4246 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4247 PangoLogAttr attrs
[3];
4249 gchar
*next_quote_str
;
4252 gboolean keep_cursor
= FALSE
;
4254 if (!gtk_text_iter_forward_line(&iter_
) ||
4255 gtk_text_iter_ends_line(&iter_
)) {
4258 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4260 if ((quote_str
|| next_quote_str
) &&
4261 strcmp2(quote_str
, next_quote_str
) != 0) {
4262 g_free(next_quote_str
);
4265 g_free(next_quote_str
);
4268 if (quote_len
> 0) {
4269 gtk_text_iter_forward_chars(&end
, quote_len
);
4270 if (gtk_text_iter_ends_line(&end
)) {
4275 /* don't join itemized lines */
4276 if (compose_itemized_length(buffer
, &end
) > 0) {
4280 /* don't join signature separator */
4281 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4284 /* delete quote str */
4286 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4288 /* don't join line breaks put by the user */
4290 gtk_text_iter_backward_char(&cur
);
4291 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4292 gtk_text_iter_forward_char(&cur
);
4296 gtk_text_iter_forward_char(&cur
);
4297 /* delete linebreak and extra spaces */
4298 while (gtk_text_iter_backward_char(&cur
)) {
4299 wc1
= gtk_text_iter_get_char(&cur
);
4300 if (!g_unichar_isspace(wc1
))
4305 while (!gtk_text_iter_ends_line(&cur
)) {
4306 wc1
= gtk_text_iter_get_char(&cur
);
4307 if (!g_unichar_isspace(wc1
))
4309 gtk_text_iter_forward_char(&cur
);
4312 if (!gtk_text_iter_equal(&prev
, &next
)) {
4315 mark
= gtk_text_buffer_get_insert(buffer
);
4316 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4317 if (gtk_text_iter_equal(&prev
, &cur
))
4319 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4323 /* insert space if required */
4324 gtk_text_iter_backward_char(&prev
);
4325 wc1
= gtk_text_iter_get_char(&prev
);
4326 wc2
= gtk_text_iter_get_char(&next
);
4327 gtk_text_iter_forward_char(&next
);
4328 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4329 pango_default_break(str
, -1, NULL
, attrs
, 3);
4330 if (!attrs
[1].is_line_break
||
4331 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4332 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4334 gtk_text_iter_backward_char(&iter_
);
4335 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4344 #define ADD_TXT_POS(bp_, ep_, pti_) \
4345 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4346 last = last->next; \
4347 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4348 last->next = NULL; \
4350 g_warning("alloc error scanning URIs"); \
4353 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4355 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4356 GtkTextBuffer
*buffer
;
4357 GtkTextIter iter
, break_pos
, end_of_line
;
4358 gchar
*quote_str
= NULL
;
4360 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4361 gboolean prev_autowrap
= compose
->autowrap
;
4362 gint startq_offset
= -1, noq_offset
= -1;
4363 gint uri_start
= -1, uri_stop
= -1;
4364 gint nouri_start
= -1, nouri_stop
= -1;
4365 gint num_blocks
= 0;
4366 gint quotelevel
= -1;
4367 gboolean modified
= force
;
4368 gboolean removed
= FALSE
;
4369 gboolean modified_before_remove
= FALSE
;
4371 gboolean start
= TRUE
;
4372 gint itemized_len
= 0, rem_item_len
= 0;
4373 gchar
*itemized_chars
= NULL
;
4374 gboolean item_continuation
= FALSE
;
4379 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4383 compose
->autowrap
= FALSE
;
4385 buffer
= gtk_text_view_get_buffer(text
);
4386 undo_wrapping(compose
->undostruct
, TRUE
);
4391 mark
= gtk_text_buffer_get_insert(buffer
);
4392 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4396 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4397 if (gtk_text_iter_ends_line(&iter
)) {
4398 while (gtk_text_iter_ends_line(&iter
) &&
4399 gtk_text_iter_forward_line(&iter
))
4402 while (gtk_text_iter_backward_line(&iter
)) {
4403 if (gtk_text_iter_ends_line(&iter
)) {
4404 gtk_text_iter_forward_line(&iter
);
4410 /* move to line start */
4411 gtk_text_iter_set_line_offset(&iter
, 0);
4414 itemized_len
= compose_itemized_length(buffer
, &iter
);
4416 if (!itemized_len
) {
4417 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4418 item_continuation
= TRUE
;
4422 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4424 /* go until paragraph end (empty line) */
4425 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4426 gchar
*scanpos
= NULL
;
4427 /* parse table - in order of priority */
4429 const gchar
*needle
; /* token */
4431 /* token search function */
4432 gchar
*(*search
) (const gchar
*haystack
,
4433 const gchar
*needle
);
4434 /* part parsing function */
4435 gboolean (*parse
) (const gchar
*start
,
4436 const gchar
*scanpos
,
4440 /* part to URI function */
4441 gchar
*(*build_uri
) (const gchar
*bp
,
4445 static struct table parser
[] = {
4446 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4447 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4448 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4449 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4450 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4451 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4452 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4453 {"@", strcasestr
, get_email_part
, make_email_string
}
4455 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4456 gint last_index
= PARSE_ELEMS
;
4458 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4462 if (!prev_autowrap
&& num_blocks
== 0) {
4464 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4465 G_CALLBACK(text_inserted
),
4468 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4471 uri_start
= uri_stop
= -1;
4473 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4476 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4477 if (startq_offset
== -1)
4478 startq_offset
= gtk_text_iter_get_offset(&iter
);
4479 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4480 if (quotelevel
> 2) {
4481 /* recycle colors */
4482 if (prefs_common
.recycle_quote_colors
)
4491 if (startq_offset
== -1)
4492 noq_offset
= gtk_text_iter_get_offset(&iter
);
4496 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4499 if (gtk_text_iter_ends_line(&iter
)) {
4501 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4502 prefs_common
.linewrap_len
,
4504 GtkTextIter prev
, next
, cur
;
4505 if (prev_autowrap
!= FALSE
|| force
) {
4506 compose
->automatic_break
= TRUE
;
4508 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4509 compose
->automatic_break
= FALSE
;
4510 if (itemized_len
&& compose
->autoindent
) {
4511 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4512 if (!item_continuation
)
4513 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4515 } else if (quote_str
&& wrap_quote
) {
4516 compose
->automatic_break
= TRUE
;
4518 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4519 compose
->automatic_break
= FALSE
;
4520 if (itemized_len
&& compose
->autoindent
) {
4521 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4522 if (!item_continuation
)
4523 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4527 /* remove trailing spaces */
4529 rem_item_len
= itemized_len
;
4530 while (compose
->autoindent
&& rem_item_len
-- > 0)
4531 gtk_text_iter_backward_char(&cur
);
4532 gtk_text_iter_backward_char(&cur
);
4535 while (!gtk_text_iter_starts_line(&cur
)) {
4538 gtk_text_iter_backward_char(&cur
);
4539 wc
= gtk_text_iter_get_char(&cur
);
4540 if (!g_unichar_isspace(wc
))
4544 if (!gtk_text_iter_equal(&prev
, &next
)) {
4545 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4547 gtk_text_iter_forward_char(&break_pos
);
4551 gtk_text_buffer_insert(buffer
, &break_pos
,
4555 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4557 /* move iter to current line start */
4558 gtk_text_iter_set_line_offset(&iter
, 0);
4565 /* move iter to next line start */
4571 if (!prev_autowrap
&& num_blocks
> 0) {
4573 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4574 G_CALLBACK(text_inserted
),
4578 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4579 gtk_text_iter_forward_char(&end_of_line
);
4581 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4583 nouri_start
= gtk_text_iter_get_offset(&iter
);
4584 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4586 walk_pos
= gtk_text_iter_get_offset(&iter
);
4587 /* FIXME: this looks phony. scanning for anything in the parse table */
4588 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4591 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4593 if (scanpos
== NULL
|| tmp
< scanpos
) {
4602 /* check if URI can be parsed */
4603 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4604 (const gchar
**)&ep
, FALSE
)
4605 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4609 strlen(parser
[last_index
].needle
);
4612 uri_start
= walk_pos
+ (bp
- o_walk
);
4613 uri_stop
= walk_pos
+ (ep
- o_walk
);
4617 gtk_text_iter_forward_line(&iter
);
4620 if (startq_offset
!= -1) {
4621 GtkTextIter startquote
, endquote
;
4622 gtk_text_buffer_get_iter_at_offset(
4623 buffer
, &startquote
, startq_offset
);
4626 switch (quotelevel
) {
4628 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4629 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4630 gtk_text_buffer_apply_tag_by_name(
4631 buffer
, "quote0", &startquote
, &endquote
);
4632 gtk_text_buffer_remove_tag_by_name(
4633 buffer
, "quote1", &startquote
, &endquote
);
4634 gtk_text_buffer_remove_tag_by_name(
4635 buffer
, "quote2", &startquote
, &endquote
);
4640 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4641 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4642 gtk_text_buffer_apply_tag_by_name(
4643 buffer
, "quote1", &startquote
, &endquote
);
4644 gtk_text_buffer_remove_tag_by_name(
4645 buffer
, "quote0", &startquote
, &endquote
);
4646 gtk_text_buffer_remove_tag_by_name(
4647 buffer
, "quote2", &startquote
, &endquote
);
4652 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4653 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4654 gtk_text_buffer_apply_tag_by_name(
4655 buffer
, "quote2", &startquote
, &endquote
);
4656 gtk_text_buffer_remove_tag_by_name(
4657 buffer
, "quote0", &startquote
, &endquote
);
4658 gtk_text_buffer_remove_tag_by_name(
4659 buffer
, "quote1", &startquote
, &endquote
);
4665 } else if (noq_offset
!= -1) {
4666 GtkTextIter startnoquote
, endnoquote
;
4667 gtk_text_buffer_get_iter_at_offset(
4668 buffer
, &startnoquote
, noq_offset
);
4671 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4672 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4673 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4674 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4675 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4676 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4677 gtk_text_buffer_remove_tag_by_name(
4678 buffer
, "quote0", &startnoquote
, &endnoquote
);
4679 gtk_text_buffer_remove_tag_by_name(
4680 buffer
, "quote1", &startnoquote
, &endnoquote
);
4681 gtk_text_buffer_remove_tag_by_name(
4682 buffer
, "quote2", &startnoquote
, &endnoquote
);
4688 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4689 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4690 gtk_text_buffer_get_iter_at_offset(
4691 buffer
, &nouri_start_iter
, nouri_start
);
4692 gtk_text_buffer_get_iter_at_offset(
4693 buffer
, &nouri_end_iter
, nouri_stop
);
4694 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4695 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4696 gtk_text_buffer_remove_tag_by_name(
4697 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4698 modified_before_remove
= modified
;
4703 if (uri_start
>= 0 && uri_stop
> 0) {
4704 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4705 gtk_text_buffer_get_iter_at_offset(
4706 buffer
, &uri_start_iter
, uri_start
);
4707 gtk_text_buffer_get_iter_at_offset(
4708 buffer
, &uri_end_iter
, uri_stop
);
4709 back
= uri_end_iter
;
4710 gtk_text_iter_backward_char(&back
);
4711 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4712 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4713 gtk_text_buffer_apply_tag_by_name(
4714 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4716 if (removed
&& !modified_before_remove
) {
4722 // debug_print("not modified, out after %d lines\n", lines);
4726 // debug_print("modified, out after %d lines\n", lines);
4728 g_free(itemized_chars
);
4731 undo_wrapping(compose
->undostruct
, FALSE
);
4732 compose
->autowrap
= prev_autowrap
;
4737 void compose_action_cb(void *data
)
4739 Compose
*compose
= (Compose
*)data
;
4740 compose_wrap_all(compose
);
4743 static void compose_wrap_all(Compose
*compose
)
4745 compose_wrap_all_full(compose
, FALSE
);
4748 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4750 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4751 GtkTextBuffer
*buffer
;
4753 gboolean modified
= TRUE
;
4755 buffer
= gtk_text_view_get_buffer(text
);
4757 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4759 undo_wrapping(compose
->undostruct
, TRUE
);
4761 while (!gtk_text_iter_is_end(&iter
) && modified
)
4762 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4764 undo_wrapping(compose
->undostruct
, FALSE
);
4768 static void compose_set_title(Compose
*compose
)
4774 edited
= compose
->modified
? _(" [Edited]") : "";
4776 subject
= gtk_editable_get_chars(
4777 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4779 #ifndef GENERIC_UMPC
4780 if (subject
&& strlen(subject
))
4781 str
= g_strdup_printf(_("%s - Compose message%s"),
4784 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4786 str
= g_strdup(_("Compose message"));
4789 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4795 * compose_current_mail_account:
4797 * Find a current mail account (the currently selected account, or the
4798 * default account, if a news account is currently selected). If a
4799 * mail account cannot be found, display an error message.
4801 * Return value: Mail account, or NULL if not found.
4803 static PrefsAccount
*
4804 compose_current_mail_account(void)
4808 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4811 ac
= account_get_default();
4812 if (!ac
|| ac
->protocol
== A_NNTP
) {
4813 alertpanel_error(_("Account for sending mail is not specified.\n"
4814 "Please select a mail account before sending."));
4821 #define QUOTE_IF_REQUIRED(out, str) \
4823 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4827 len = strlen(str) + 3; \
4828 if ((__tmp = alloca(len)) == NULL) { \
4829 g_warning("can't allocate memory"); \
4830 g_string_free(header, TRUE); \
4833 g_snprintf(__tmp, len, "\"%s\"", str); \
4838 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4839 g_warning("can't allocate memory"); \
4840 g_string_free(header, TRUE); \
4843 strcpy(__tmp, str); \
4849 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4851 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4855 len = strlen(str) + 3; \
4856 if ((__tmp = alloca(len)) == NULL) { \
4857 g_warning("can't allocate memory"); \
4860 g_snprintf(__tmp, len, "\"%s\"", str); \
4865 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4866 g_warning("can't allocate memory"); \
4869 strcpy(__tmp, str); \
4875 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4878 gchar
*from
= NULL
, *header
= NULL
;
4879 ComposeHeaderEntry
*header_entry
;
4880 #if GTK_CHECK_VERSION(2, 24, 0)
4884 cm_return_if_fail(account
!= NULL
);
4886 compose
->account
= account
;
4887 if (account
->name
&& *account
->name
) {
4889 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4890 qbuf
= escape_internal_quotes(buf
, '"');
4891 from
= g_strdup_printf("%s <%s>",
4892 qbuf
, account
->address
);
4895 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4897 from
= g_strdup_printf("<%s>",
4899 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4904 compose_set_title(compose
);
4906 if (account
->default_sign
&& compose
->mode
!= COMPOSE_REDIRECT
)
4907 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
4909 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
4910 if (account
->default_encrypt
&& compose
->mode
!= COMPOSE_REDIRECT
)
4911 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
4913 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
4915 activate_privacy_system(compose
, account
, FALSE
);
4917 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
4918 undo_block(compose
->undostruct
);
4919 compose_insert_sig(compose
, TRUE
);
4920 undo_unblock(compose
->undostruct
);
4923 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
4924 #if !GTK_CHECK_VERSION(2, 24, 0)
4925 header
= gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry
->combo
));
4927 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
4928 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4929 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
4932 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
4933 if (account
->protocol
== A_NNTP
) {
4934 if (!strcmp(header
, _("To:")))
4935 combobox_select_by_text(
4936 GTK_COMBO_BOX(header_entry
->combo
),
4939 if (!strcmp(header
, _("Newsgroups:")))
4940 combobox_select_by_text(
4941 GTK_COMBO_BOX(header_entry
->combo
),
4949 /* use account's dict info if set */
4950 if (compose
->gtkaspell
) {
4951 if (account
->enable_default_dictionary
)
4952 gtkaspell_change_dict(compose
->gtkaspell
,
4953 account
->default_dictionary
, FALSE
);
4954 if (account
->enable_default_alt_dictionary
)
4955 gtkaspell_change_alt_dict(compose
->gtkaspell
,
4956 account
->default_alt_dictionary
);
4957 if (account
->enable_default_dictionary
4958 || account
->enable_default_alt_dictionary
)
4959 compose_spell_menu_changed(compose
);
4964 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
4965 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
4966 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
4967 gboolean recipient_found
= FALSE
;
4971 /* free to and newsgroup list */
4972 slist_free_strings_full(compose
->to_list
);
4973 compose
->to_list
= NULL
;
4975 slist_free_strings_full(compose
->newsgroup_list
);
4976 compose
->newsgroup_list
= NULL
;
4978 /* search header entries for to and newsgroup entries */
4979 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4982 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
4983 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
4986 if (entry
[0] != '\0') {
4987 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
4988 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
4989 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
4990 recipient_found
= TRUE
;
4993 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
4994 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
4995 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
4996 recipient_found
= TRUE
;
5003 return recipient_found
;
5006 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5008 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5009 gboolean found_other
= FALSE
;
5011 /* search header entries for to and newsgroup entries */
5012 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5015 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5016 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5019 if (strcmp(entry
, compose
->account
->auto_cc
)
5020 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5030 if (compose
->batch
) {
5031 gtk_widget_show_all(compose
->window
);
5033 aval
= alertpanel(_("Send"),
5034 _("The only recipient is the default CC address. Send anyway?"),
5035 GTK_STOCK_CANCEL
, g_strconcat("+", _("_Send"), NULL
), NULL
);
5036 if (aval
!= G_ALERTALTERNATE
)
5040 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5041 gboolean found_other
= FALSE
;
5043 /* search header entries for to and newsgroup entries */
5044 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5047 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5048 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5051 if (strcmp(entry
, compose
->account
->auto_bcc
)
5052 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5062 if (compose
->batch
) {
5063 gtk_widget_show_all(compose
->window
);
5065 aval
= alertpanel(_("Send"),
5066 _("The only recipient is the default BCC address. Send anyway?"),
5067 GTK_STOCK_CANCEL
, g_strconcat("+", _("_Send"), NULL
), NULL
);
5068 if (aval
!= G_ALERTALTERNATE
)
5075 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5079 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5080 if (compose
->batch
) {
5081 gtk_widget_show_all(compose
->window
);
5083 alertpanel_error(_("Recipient is not specified."));
5087 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5091 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5092 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5093 if (*str
== '\0' && check_everything
== TRUE
&&
5094 compose
->mode
!= COMPOSE_REDIRECT
) {
5096 gchar
*button_label
;
5099 if (compose
->sending
)
5100 button_label
= g_strconcat("+", _("_Send"), NULL
);
5102 button_label
= g_strconcat("+", _("_Queue"), NULL
);
5103 message
= g_strdup_printf(_("Subject is empty. %s"),
5104 compose
->sending
?_("Send it anyway?"):
5105 _("Queue it anyway?"));
5107 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5108 GTK_STOCK_CANCEL
, button_label
, NULL
, TRUE
, NULL
,
5109 ALERT_QUESTION
, G_ALERTDEFAULT
);
5111 if (aval
& G_ALERTDISABLE
) {
5112 aval
&= ~G_ALERTDISABLE
;
5113 prefs_common
.warn_empty_subj
= FALSE
;
5115 if (aval
!= G_ALERTALTERNATE
)
5120 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5126 gint
compose_send(Compose
*compose
)
5129 FolderItem
*folder
= NULL
;
5131 gchar
*msgpath
= NULL
;
5132 gboolean discard_window
= FALSE
;
5133 gchar
*errstr
= NULL
;
5134 gchar
*tmsgid
= NULL
;
5135 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5136 gboolean queued_removed
= FALSE
;
5138 if (prefs_common
.send_dialog_invisible
5139 || compose
->batch
== TRUE
)
5140 discard_window
= TRUE
;
5142 compose_allow_user_actions (compose
, FALSE
);
5143 compose
->sending
= TRUE
;
5145 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5146 if (compose
->batch
) {
5147 gtk_widget_show_all(compose
->window
);
5153 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5156 if (compose
->batch
) {
5157 gtk_widget_show_all(compose
->window
);
5160 alertpanel_error(_("Could not queue message for sending:\n\n"
5161 "Charset conversion failed."));
5162 } else if (val
== -5) {
5163 alertpanel_error(_("Could not queue message for sending:\n\n"
5164 "Couldn't get recipient encryption key."));
5165 } else if (val
== -6) {
5167 } else if (val
== -3) {
5168 if (privacy_peek_error())
5169 alertpanel_error(_("Could not queue message for sending:\n\n"
5170 "Signature failed: %s"), privacy_get_error());
5171 } else if (val
== -2 && errno
!= 0) {
5172 alertpanel_error(_("Could not queue message for sending:\n\n%s."), g_strerror(errno
));
5174 alertpanel_error(_("Could not queue message for sending."));
5179 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5180 if (discard_window
) {
5181 compose
->sending
= FALSE
;
5182 compose_close(compose
);
5183 /* No more compose access in the normal codepath
5184 * after this point! */
5189 alertpanel_error(_("The message was queued but could not be "
5190 "sent.\nUse \"Send queued messages\" from "
5191 "the main window to retry."));
5192 if (!discard_window
) {
5199 if (msgpath
== NULL
) {
5200 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5201 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5204 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5205 claws_unlink(msgpath
);
5208 if (!discard_window
) {
5210 if (!queued_removed
)
5211 folder_item_remove_msg(folder
, msgnum
);
5212 folder_item_scan(folder
);
5214 /* make sure we delete that */
5215 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5217 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5218 folder_item_remove_msg(folder
, tmp
->msgnum
);
5219 procmsg_msginfo_free(&tmp
);
5226 if (!queued_removed
)
5227 folder_item_remove_msg(folder
, msgnum
);
5228 folder_item_scan(folder
);
5230 /* make sure we delete that */
5231 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5233 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5234 folder_item_remove_msg(folder
, tmp
->msgnum
);
5235 procmsg_msginfo_free(&tmp
);
5238 if (!discard_window
) {
5239 compose
->sending
= FALSE
;
5240 compose_allow_user_actions (compose
, TRUE
);
5241 compose_close(compose
);
5245 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5246 "the main window to retry."), errstr
);
5249 alertpanel_error_log(_("The message was queued but could not be "
5250 "sent.\nUse \"Send queued messages\" from "
5251 "the main window to retry."));
5253 if (!discard_window
) {
5262 toolbar_main_set_sensitive(mainwin
);
5263 main_window_set_menu_sensitive(mainwin
);
5269 compose_allow_user_actions (compose
, TRUE
);
5270 compose
->sending
= FALSE
;
5271 compose
->modified
= TRUE
;
5272 toolbar_main_set_sensitive(mainwin
);
5273 main_window_set_menu_sensitive(mainwin
);
5278 static gboolean
compose_use_attach(Compose
*compose
)
5280 GtkTreeModel
*model
= gtk_tree_view_get_model
5281 (GTK_TREE_VIEW(compose
->attach_clist
));
5282 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5285 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5288 gchar buf
[BUFFSIZE
];
5290 gboolean first_to_address
;
5291 gboolean first_cc_address
;
5293 ComposeHeaderEntry
*headerentry
;
5294 const gchar
*headerentryname
;
5295 const gchar
*cc_hdr
;
5296 const gchar
*to_hdr
;
5297 gboolean err
= FALSE
;
5299 debug_print("Writing redirect header\n");
5301 cc_hdr
= prefs_common_translated_header_name("Cc:");
5302 to_hdr
= prefs_common_translated_header_name("To:");
5304 first_to_address
= TRUE
;
5305 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5306 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5307 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5309 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5310 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5311 Xstrdup_a(str
, entstr
, return -1);
5313 if (str
[0] != '\0') {
5314 compose_convert_header
5315 (compose
, buf
, sizeof(buf
), str
,
5316 strlen("Resent-To") + 2, TRUE
);
5318 if (first_to_address
) {
5319 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5320 first_to_address
= FALSE
;
5322 err
|= (fprintf(fp
, ",") < 0);
5324 err
|= (fprintf(fp
, "%s", buf
) < 0);
5328 if (!first_to_address
) {
5329 err
|= (fprintf(fp
, "\n") < 0);
5332 first_cc_address
= TRUE
;
5333 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5334 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5335 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5337 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5338 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5339 Xstrdup_a(str
, strg
, return -1);
5341 if (str
[0] != '\0') {
5342 compose_convert_header
5343 (compose
, buf
, sizeof(buf
), str
,
5344 strlen("Resent-Cc") + 2, TRUE
);
5346 if (first_cc_address
) {
5347 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5348 first_cc_address
= FALSE
;
5350 err
|= (fprintf(fp
, ",") < 0);
5352 err
|= (fprintf(fp
, "%s", buf
) < 0);
5356 if (!first_cc_address
) {
5357 err
|= (fprintf(fp
, "\n") < 0);
5360 return (err
? -1:0);
5363 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5365 gchar buf
[BUFFSIZE
];
5367 const gchar
*entstr
;
5368 /* struct utsname utsbuf; */
5369 gboolean err
= FALSE
;
5371 cm_return_val_if_fail(fp
!= NULL
, -1);
5372 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5373 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5376 if (prefs_common
.hide_timezone
)
5377 get_rfc822_date_hide_tz(buf
, sizeof(buf
));
5379 get_rfc822_date(buf
, sizeof(buf
));
5380 err
|= (fprintf(fp
, "Resent-Date: %s\n", buf
) < 0);
5383 if (compose
->account
->name
&& *compose
->account
->name
) {
5384 compose_convert_header
5385 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5386 strlen("From: "), TRUE
);
5387 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5388 buf
, compose
->account
->address
) < 0);
5390 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5393 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5394 if (*entstr
!= '\0') {
5395 Xstrdup_a(str
, entstr
, return -1);
5398 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5399 strlen("Subject: "), FALSE
);
5400 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5404 /* Resent-Message-ID */
5405 if (compose
->account
->gen_msgid
) {
5406 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5407 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5409 g_free(compose
->msgid
);
5410 compose
->msgid
= addr
;
5412 compose
->msgid
= NULL
;
5415 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5418 /* separator between header and body */
5419 err
|= (fputs("\n", fp
) == EOF
);
5421 return (err
? -1:0);
5424 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5428 gchar buf
[BUFFSIZE
];
5430 gboolean skip
= FALSE
;
5431 gboolean err
= FALSE
;
5432 gchar
*not_included
[]={
5433 "Return-Path:", "Delivered-To:", "Received:",
5434 "Subject:", "X-UIDL:", "AF:",
5435 "NF:", "PS:", "SRH:",
5436 "SFN:", "DSR:", "MID:",
5437 "CFG:", "PT:", "S:",
5438 "RQ:", "SSV:", "NSV:",
5439 "SSH:", "R:", "MAID:",
5440 "NAID:", "RMID:", "FMID:",
5441 "SCF:", "RRCPT:", "NG:",
5442 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5443 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5444 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5445 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5446 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5449 if ((fp
= g_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5450 FILE_OP_ERROR(compose
->redirect_filename
, "fopen");
5454 while (procheader_get_one_field_asis(buf
, sizeof(buf
), fp
) != -1) {
5456 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5457 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5458 strlen(not_included
[i
])) == 0) {
5465 if (fputs(buf
, fdest
) == -1)
5468 if (!prefs_common
.redirect_keep_from
) {
5469 if (g_ascii_strncasecmp(buf
, "From:",
5470 strlen("From:")) == 0) {
5471 err
|= (fputs(" (by way of ", fdest
) == EOF
);
5472 if (compose
->account
->name
5473 && *compose
->account
->name
) {
5474 compose_convert_header
5475 (compose
, buf
, sizeof(buf
),
5476 compose
->account
->name
,
5479 err
|= (fprintf(fdest
, "%s <%s>",
5481 compose
->account
->address
) < 0);
5483 err
|= (fprintf(fdest
, "%s",
5484 compose
->account
->address
) < 0);
5485 err
|= (fputs(")", fdest
) == EOF
);
5489 if (fputs("\n", fdest
) == -1)
5496 if (compose_redirect_write_headers(compose
, fdest
))
5499 while ((len
= fread(buf
, sizeof(gchar
), sizeof(buf
), fp
)) > 0) {
5500 if (fwrite(buf
, sizeof(gchar
), len
, fdest
) != len
)
5513 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5515 GtkTextBuffer
*buffer
;
5516 GtkTextIter start
, end
;
5517 gchar
*chars
, *tmp_enc_file
, *content
;
5519 const gchar
*out_codeset
;
5520 EncodingType encoding
= ENC_UNKNOWN
;
5521 MimeInfo
*mimemsg
, *mimetext
;
5523 const gchar
*src_codeset
= CS_INTERNAL
;
5524 gchar
*from_addr
= NULL
;
5525 gchar
*from_name
= NULL
;
5528 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5529 attach_parts
= TRUE
;
5531 /* We're sending the message, generate a Message-ID
5533 if (compose
->msgid
== NULL
&&
5534 compose
->account
->gen_msgid
) {
5535 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5539 /* create message MimeInfo */
5540 mimemsg
= procmime_mimeinfo_new();
5541 mimemsg
->type
= MIMETYPE_MESSAGE
;
5542 mimemsg
->subtype
= g_strdup("rfc822");
5543 mimemsg
->content
= MIMECONTENT_MEM
;
5544 mimemsg
->tmp
= TRUE
; /* must free content later */
5545 mimemsg
->data
.mem
= compose_get_header(compose
);
5547 /* Create text part MimeInfo */
5548 /* get all composed text */
5549 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5550 gtk_text_buffer_get_start_iter(buffer
, &start
);
5551 gtk_text_buffer_get_end_iter(buffer
, &end
);
5552 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5554 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5556 if (!out_codeset
&& is_ascii_str(chars
)) {
5557 out_codeset
= CS_US_ASCII
;
5558 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5559 is_ascii_str(chars
)) {
5560 out_codeset
= CS_US_ASCII
;
5561 encoding
= ENC_7BIT
;
5565 gchar
*test_conv_global_out
= NULL
;
5566 gchar
*test_conv_reply
= NULL
;
5568 /* automatic mode. be automatic. */
5569 codeconv_set_strict(TRUE
);
5571 out_codeset
= conv_get_outgoing_charset_str();
5573 debug_print("trying to convert to %s\n", out_codeset
);
5574 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5577 if (!test_conv_global_out
&& compose
->orig_charset
5578 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5579 out_codeset
= compose
->orig_charset
;
5580 debug_print("failure; trying to convert to %s\n", out_codeset
);
5581 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5584 if (!test_conv_global_out
&& !test_conv_reply
) {
5586 out_codeset
= CS_INTERNAL
;
5587 debug_print("failure; finally using %s\n", out_codeset
);
5589 g_free(test_conv_global_out
);
5590 g_free(test_conv_reply
);
5591 codeconv_set_strict(FALSE
);
5594 if (encoding
== ENC_UNKNOWN
) {
5595 if (prefs_common
.encoding_method
== CTE_BASE64
)
5596 encoding
= ENC_BASE64
;
5597 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5598 encoding
= ENC_QUOTED_PRINTABLE
;
5599 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5600 encoding
= ENC_8BIT
;
5602 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5605 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5606 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5608 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5609 codeconv_set_strict(TRUE
);
5610 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5611 codeconv_set_strict(FALSE
);
5616 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5617 "to the specified %s charset.\n"
5618 "Send it as %s?"), out_codeset
, src_codeset
);
5619 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
,
5620 g_strconcat("+", _("_Send"), NULL
), NULL
, FALSE
,
5621 NULL
, ALERT_ERROR
, G_ALERTDEFAULT
);
5624 if (aval
!= G_ALERTALTERNATE
) {
5629 out_codeset
= src_codeset
;
5635 out_codeset
= src_codeset
;
5640 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5641 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5642 strstr(buf
, "\nFrom ") != NULL
) {
5643 encoding
= ENC_QUOTED_PRINTABLE
;
5647 mimetext
= procmime_mimeinfo_new();
5648 mimetext
->content
= MIMECONTENT_MEM
;
5649 mimetext
->tmp
= TRUE
; /* must free content later */
5650 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5651 * and free the data, which we need later. */
5652 mimetext
->data
.mem
= g_strdup(buf
);
5653 mimetext
->type
= MIMETYPE_TEXT
;
5654 mimetext
->subtype
= g_strdup("plain");
5655 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5656 g_strdup(out_codeset
));
5658 /* protect trailing spaces when signing message */
5659 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5660 privacy_system_can_sign(compose
->privacy_system
)) {
5661 encoding
= ENC_QUOTED_PRINTABLE
;
5665 debug_print("main text: %Id bytes encoded as %s in %d\n",
5667 debug_print("main text: %zd bytes encoded as %s in %d\n",
5669 strlen(buf
), out_codeset
, encoding
);
5671 /* check for line length limit */
5672 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5673 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5674 check_line_length(buf
, 1000, &line
) < 0) {
5677 msg
= g_strdup_printf
5678 (_("Line %d exceeds the line length limit (998 bytes).\n"
5679 "The contents of the message might be broken on the way to the delivery.\n"
5681 "Send it anyway?"), line
+ 1);
5682 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
);
5684 if (aval
!= G_ALERTALTERNATE
) {
5690 if (encoding
!= ENC_UNKNOWN
)
5691 procmime_encode_content(mimetext
, encoding
);
5693 /* append attachment parts */
5694 if (compose_use_attach(compose
) && attach_parts
) {
5695 MimeInfo
*mimempart
;
5696 gchar
*boundary
= NULL
;
5697 mimempart
= procmime_mimeinfo_new();
5698 mimempart
->content
= MIMECONTENT_EMPTY
;
5699 mimempart
->type
= MIMETYPE_MULTIPART
;
5700 mimempart
->subtype
= g_strdup("mixed");
5704 boundary
= generate_mime_boundary(NULL
);
5705 } while (strstr(buf
, boundary
) != NULL
);
5707 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5710 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5712 g_node_append(mimempart
->node
, mimetext
->node
);
5713 g_node_append(mimemsg
->node
, mimempart
->node
);
5715 if (compose_add_attachments(compose
, mimempart
) < 0)
5718 g_node_append(mimemsg
->node
, mimetext
->node
);
5722 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5723 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5724 /* extract name and address */
5725 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5726 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5727 *(strrchr(from_addr
, '>')) = '\0';
5728 from_name
= g_strdup(spec
);
5729 *(strrchr(from_name
, '<')) = '\0';
5736 /* sign message if sending */
5737 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5738 privacy_system_can_sign(compose
->privacy_system
))
5739 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5740 compose
->account
, from_addr
)) {
5748 if (compose
->use_encryption
) {
5749 if (compose
->encdata
!= NULL
&&
5750 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5752 /* First, write an unencrypted copy and save it to outbox, if
5753 * user wants that. */
5754 if (compose
->account
->save_encrypted_as_clear_text
) {
5755 debug_print("saving sent message unencrypted...\n");
5756 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5760 /* fp now points to a file with headers written,
5761 * let's make a copy. */
5763 content
= file_read_stream_to_str(fp
);
5765 str_write_to_file(content
, tmp_enc_file
);
5768 /* Now write the unencrypted body. */
5769 if ((tmpfp
= g_fopen(tmp_enc_file
, "a")) != NULL
) {
5770 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5773 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5775 outbox
= folder_get_default_outbox();
5777 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5778 claws_unlink(tmp_enc_file
);
5780 g_warning("Can't open file '%s'", tmp_enc_file
);
5783 g_warning("couldn't get tempfile");
5786 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5787 debug_print("Couldn't encrypt mime structure: %s.\n",
5788 privacy_get_error());
5789 alertpanel_error(_("Couldn't encrypt the email: %s"),
5790 privacy_get_error());
5795 procmime_write_mimeinfo(mimemsg
, fp
);
5797 procmime_mimeinfo_free_all(&mimemsg
);
5802 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5804 GtkTextBuffer
*buffer
;
5805 GtkTextIter start
, end
;
5810 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
5811 FILE_OP_ERROR(file
, "fopen");
5815 /* chmod for security */
5816 if (change_file_mode_rw(fp
, file
) < 0) {
5817 FILE_OP_ERROR(file
, "chmod");
5818 g_warning("can't change file mode");
5821 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5822 gtk_text_buffer_get_start_iter(buffer
, &start
);
5823 gtk_text_buffer_get_end_iter(buffer
, &end
);
5824 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5826 chars
= conv_codeset_strdup
5827 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
5836 len
= strlen(chars
);
5837 if (fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
5838 FILE_OP_ERROR(file
, "fwrite");
5847 if (fclose(fp
) == EOF
) {
5848 FILE_OP_ERROR(file
, "fclose");
5855 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
5858 MsgInfo
*msginfo
= compose
->targetinfo
;
5860 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
5861 if (!msginfo
) return -1;
5863 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
5866 item
= msginfo
->folder
;
5867 cm_return_val_if_fail(item
!= NULL
, -1);
5869 if (procmsg_msg_exist(msginfo
) &&
5870 (folder_has_parent_of_type(item
, F_QUEUE
) ||
5871 folder_has_parent_of_type(item
, F_DRAFT
)
5872 || msginfo
== compose
->autosaved_draft
)) {
5873 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
5874 g_warning("can't remove the old message");
5877 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
5884 static void compose_remove_draft(Compose
*compose
)
5887 MsgInfo
*msginfo
= compose
->targetinfo
;
5888 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
5890 if (procmsg_msg_exist(msginfo
)) {
5891 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
5896 gint
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
5897 gboolean remove_reedit_target
)
5899 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
5902 static gboolean
compose_warn_encryption(Compose
*compose
)
5904 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
5905 AlertValue val
= G_ALERTALTERNATE
;
5907 if (warning
== NULL
)
5910 val
= alertpanel_full(_("Encryption warning"), warning
,
5911 GTK_STOCK_CANCEL
, g_strconcat("+", _("C_ontinue"), NULL
), NULL
,
5912 TRUE
, NULL
, ALERT_WARNING
, G_ALERTALTERNATE
);
5913 if (val
& G_ALERTDISABLE
) {
5914 val
&= ~G_ALERTDISABLE
;
5915 if (val
== G_ALERTALTERNATE
)
5916 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
5920 if (val
== G_ALERTALTERNATE
) {
5927 static gint
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
5928 gchar
**msgpath
, gboolean check_subject
,
5929 gboolean remove_reedit_target
)
5936 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
5937 gboolean err
= FALSE
;
5939 debug_print("queueing message...\n");
5940 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5942 if (compose_check_entries(compose
, check_subject
) == FALSE
) {
5943 if (compose
->batch
) {
5944 gtk_widget_show_all(compose
->window
);
5949 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
5950 g_warning("can't get recipient list.");
5954 if (compose
->to_list
) {
5955 if (compose
->account
->protocol
!= A_NNTP
)
5956 mailac
= compose
->account
;
5957 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
5958 mailac
= cur_account
;
5959 else if (!(mailac
= compose_current_mail_account())) {
5960 alertpanel_error(_("No account for sending mails available!"));
5965 if (compose
->newsgroup_list
) {
5966 if (compose
->account
->protocol
== A_NNTP
)
5967 newsac
= compose
->account
;
5969 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5974 /* write queue header */
5975 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5976 G_DIR_SEPARATOR
, compose
, (guint
) rand());
5977 debug_print("queuing to %s\n", tmp
);
5978 if ((fp
= g_fopen(tmp
, "w+b")) == NULL
) {
5979 FILE_OP_ERROR(tmp
, "fopen");
5984 if (change_file_mode_rw(fp
, tmp
) < 0) {
5985 FILE_OP_ERROR(tmp
, "chmod");
5986 g_warning("can't change file mode");
5989 /* queueing variables */
5990 err
|= (fprintf(fp
, "AF:\n") < 0);
5991 err
|= (fprintf(fp
, "NF:0\n") < 0);
5992 err
|= (fprintf(fp
, "PS:10\n") < 0);
5993 err
|= (fprintf(fp
, "SRH:1\n") < 0);
5994 err
|= (fprintf(fp
, "SFN:\n") < 0);
5995 err
|= (fprintf(fp
, "DSR:\n") < 0);
5997 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
5999 err
|= (fprintf(fp
, "MID:\n") < 0);
6000 err
|= (fprintf(fp
, "CFG:\n") < 0);
6001 err
|= (fprintf(fp
, "PT:0\n") < 0);
6002 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6003 err
|= (fprintf(fp
, "RQ:\n") < 0);
6005 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6007 err
|= (fprintf(fp
, "SSV:\n") < 0);
6009 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6011 err
|= (fprintf(fp
, "NSV:\n") < 0);
6012 err
|= (fprintf(fp
, "SSH:\n") < 0);
6013 /* write recepient list */
6014 if (compose
->to_list
) {
6015 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6016 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6018 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6019 err
|= (fprintf(fp
, "\n") < 0);
6021 /* write newsgroup list */
6022 if (compose
->newsgroup_list
) {
6023 err
|= (fprintf(fp
, "NG:") < 0);
6024 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6025 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6026 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6027 err
|= (fprintf(fp
, "\n") < 0);
6029 /* Sylpheed account IDs */
6031 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6033 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6036 if (compose
->privacy_system
!= NULL
) {
6037 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6038 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6039 if (compose
->use_encryption
) {
6040 if (!compose_warn_encryption(compose
)) {
6046 if (mailac
&& mailac
->encrypt_to_self
) {
6047 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6048 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6049 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6050 g_slist_free(tmp_list
);
6052 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6054 if (compose
->encdata
!= NULL
) {
6055 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6056 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6057 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6058 compose
->encdata
) < 0);
6059 } /* else we finally dont want to encrypt */
6061 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6062 /* and if encdata was null, it means there's been a problem in
6065 g_warning("failed to write queue message");
6074 /* Save copy folder */
6075 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6076 gchar
*savefolderid
;
6078 savefolderid
= compose_get_save_to(compose
);
6079 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6080 g_free(savefolderid
);
6082 /* Save copy folder */
6083 if (compose
->return_receipt
) {
6084 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6086 /* Message-ID of message replying to */
6087 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6088 gchar
*folderid
= NULL
;
6090 if (compose
->replyinfo
->folder
)
6091 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6092 if (folderid
== NULL
)
6093 folderid
= g_strdup("NULL");
6095 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6098 /* Message-ID of message forwarding to */
6099 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6100 gchar
*folderid
= NULL
;
6102 if (compose
->fwdinfo
->folder
)
6103 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6104 if (folderid
== NULL
)
6105 folderid
= g_strdup("NULL");
6107 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6111 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6112 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6114 /* end of headers */
6115 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6117 if (compose
->redirect_filename
!= NULL
) {
6118 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6126 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6130 return result
- 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6134 g_warning("failed to write queue message");
6140 if (fclose(fp
) == EOF
) {
6141 FILE_OP_ERROR(tmp
, "fclose");
6147 if (item
&& *item
) {
6150 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6153 g_warning("can't find queue folder");
6158 folder_item_scan(queue
);
6159 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6160 g_warning("can't queue the message");
6166 if (msgpath
== NULL
) {
6172 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6173 compose_remove_reedit_target(compose
, FALSE
);
6176 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6184 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6187 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6190 gchar
*type
, *subtype
;
6191 GtkTreeModel
*model
;
6194 model
= gtk_tree_view_get_model(tree_view
);
6196 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6199 gtk_tree_model_get(model
, &iter
,
6203 if (!is_file_exist(ainfo
->file
)) {
6204 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6205 AlertValue val
= alertpanel_full(_("Warning"), msg
, _("Cancel sending"), _("Ignore attachment"),
6206 NULL
, FALSE
, NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
6208 if (val
== G_ALERTDEFAULT
) {
6213 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6216 mimepart
= procmime_mimeinfo_new();
6217 mimepart
->content
= MIMECONTENT_FILE
;
6218 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6219 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6220 mimepart
->offset
= 0;
6221 mimepart
->length
= statbuf
.st_size
;
6223 type
= g_strdup(ainfo
->content_type
);
6225 if (!strchr(type
, '/')) {
6227 type
= g_strdup("application/octet-stream");
6230 subtype
= strchr(type
, '/') + 1;
6231 *(subtype
- 1) = '\0';
6232 mimepart
->type
= procmime_get_media_type(type
);
6233 mimepart
->subtype
= g_strdup(subtype
);
6236 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6237 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6238 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6239 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6240 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6241 /* Text parts with no name come from multipart/alternative
6242 * forwards. Make sure the recipient won't look at the
6243 * original HTML part by mistake. */
6244 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6245 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6249 g_hash_table_insert(mimepart
->typeparameters
,
6250 g_strdup("charset"), g_strdup(ainfo
->charset
));
6252 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6253 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6254 !strcmp2(mimepart
->subtype
, "octet-stream"))
6255 g_hash_table_insert(mimepart
->typeparameters
,
6256 g_strdup("name"), g_strdup(ainfo
->name
));
6257 g_hash_table_insert(mimepart
->dispositionparameters
,
6258 g_strdup("filename"), g_strdup(ainfo
->name
));
6259 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6262 if (mimepart
->type
== MIMETYPE_MESSAGE
6263 || mimepart
->type
== MIMETYPE_MULTIPART
)
6264 ainfo
->encoding
= ENC_BINARY
;
6265 else if (compose
->use_signing
) {
6266 if (ainfo
->encoding
== ENC_7BIT
)
6267 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6268 else if (ainfo
->encoding
== ENC_8BIT
)
6269 ainfo
->encoding
= ENC_BASE64
;
6274 procmime_encode_content(mimepart
, ainfo
->encoding
);
6276 g_node_append(parent
->node
, mimepart
->node
);
6277 } while (gtk_tree_model_iter_next(model
, &iter
));
6282 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6284 GSList
*list
= NULL
, *item
= NULL
;
6285 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6287 list
= address_list_append_with_comments(list
, str
);
6288 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6289 gchar
*spec
= item
->data
;
6290 gchar
*endofname
= strstr(spec
, " <");
6291 if (endofname
!= NULL
) {
6294 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6295 qqname
= escape_internal_quotes(qname
, '"');
6297 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6298 gchar
*addr
= g_strdup(endofname
);
6299 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6300 faddr
= g_strconcat(name
, addr
, NULL
);
6303 debug_print("new auto-quoted address: '%s'\n", faddr
);
6307 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6309 result
= g_strconcat(result
,
6311 (faddr
!= NULL
)? faddr
: spec
,
6314 if (faddr
!= NULL
) {
6319 slist_free_strings_full(list
);
6324 #define IS_IN_CUSTOM_HEADER(header) \
6325 (compose->account->add_customhdr && \
6326 custom_header_find(compose->account->customhdr_list, header) != NULL)
6328 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6330 const gchar
*fieldname
,
6331 const gchar
*seperator
)
6333 gchar
*str
, *fieldname_w_colon
;
6334 gboolean add_field
= FALSE
;
6336 ComposeHeaderEntry
*headerentry
;
6337 const gchar
*headerentryname
;
6338 const gchar
*trans_fieldname
;
6341 if (IS_IN_CUSTOM_HEADER(fieldname
))
6344 debug_print("Adding %s-fields\n", fieldname
);
6346 fieldstr
= g_string_sized_new(64);
6348 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6349 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6351 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6352 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6353 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6355 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6356 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6358 str
= compose_quote_list_of_addresses(ustr
);
6360 if (str
!= NULL
&& str
[0] != '\0') {
6362 g_string_append(fieldstr
, seperator
);
6363 g_string_append(fieldstr
, str
);
6372 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6373 compose_convert_header
6374 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6375 strlen(fieldname
) + 2, TRUE
);
6376 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6380 g_free(fieldname_w_colon
);
6381 g_string_free(fieldstr
, TRUE
);
6386 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6388 GString
*sh_header
= g_string_new(" ");
6390 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6392 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6393 ComposeHeaderEntry
*headerentry
;
6396 gchar
*headername_wcolon
;
6397 const gchar
*headername_trans
;
6399 gboolean standard_header
= FALSE
;
6401 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6403 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6405 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6410 if (!strstr(tmp
, ":")) {
6411 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6412 headername
= g_strdup(tmp
);
6414 headername_wcolon
= g_strdup(tmp
);
6415 headername
= g_strdup(strtok(tmp
, ":"));
6419 string
= std_headers
;
6420 while (*string
!= NULL
) {
6421 headername_trans
= prefs_common_translated_header_name(*string
);
6422 if (!strcmp(headername_trans
, headername_wcolon
))
6423 standard_header
= TRUE
;
6426 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6427 g_string_append_printf(sh_header
, "%s ", headername
);
6429 g_free(headername_wcolon
);
6431 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6432 return g_string_free(sh_header
, FALSE
);
6435 static gchar
*compose_get_header(Compose
*compose
)
6437 gchar buf
[BUFFSIZE
];
6438 const gchar
*entry_str
;
6442 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6444 gchar
*from_name
= NULL
, *from_address
= NULL
;
6447 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6448 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6450 header
= g_string_sized_new(64);
6453 if (prefs_common
.hide_timezone
)
6454 get_rfc822_date_hide_tz(buf
, sizeof(buf
));
6456 get_rfc822_date(buf
, sizeof(buf
));
6457 g_string_append_printf(header
, "Date: %s\n", buf
);
6461 if (compose
->account
->name
&& *compose
->account
->name
) {
6463 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6464 tmp
= g_strdup_printf("%s <%s>",
6465 buf
, compose
->account
->address
);
6467 tmp
= g_strdup_printf("%s",
6468 compose
->account
->address
);
6470 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6471 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6473 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6474 from_address
= g_strdup(compose
->account
->address
);
6476 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6477 /* extract name and address */
6478 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6479 from_address
= g_strdup(strrchr(spec
, '<')+1);
6480 *(strrchr(from_address
, '>')) = '\0';
6481 from_name
= g_strdup(spec
);
6482 *(strrchr(from_name
, '<')) = '\0';
6485 from_address
= g_strdup(spec
);
6492 if (from_name
&& *from_name
) {
6494 compose_convert_header
6495 (compose
, buf
, sizeof(buf
), from_name
,
6496 strlen("From: "), TRUE
);
6497 QUOTE_IF_REQUIRED(name
, buf
);
6498 qname
= escape_internal_quotes(name
, '"');
6500 g_string_append_printf(header
, "From: %s <%s>\n",
6501 qname
, from_address
);
6502 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6503 compose
->return_receipt
) {
6504 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6505 strlen("Disposition-Notification-To: "),
6507 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6512 g_string_append_printf(header
, "From: %s\n", from_address
);
6513 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6514 compose
->return_receipt
)
6515 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6519 g_free(from_address
);
6522 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6525 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6528 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6532 * If this account is a NNTP account remove Bcc header from
6533 * message body since it otherwise will be publicly shown
6535 if (compose
->account
->protocol
!= A_NNTP
)
6536 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6539 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6541 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6544 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6545 strlen("Subject: "), FALSE
);
6546 g_string_append_printf(header
, "Subject: %s\n", buf
);
6552 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6553 g_string_append_printf(header
, "Message-ID: <%s>\n",
6557 if (compose
->remove_references
== FALSE
) {
6559 if (compose
->inreplyto
&& compose
->to_list
)
6560 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6563 if (compose
->references
)
6564 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6568 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6571 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6574 if (compose
->account
->organization
&&
6575 strlen(compose
->account
->organization
) &&
6576 !IS_IN_CUSTOM_HEADER("Organization")) {
6577 compose_convert_header(compose
, buf
, sizeof(buf
),
6578 compose
->account
->organization
,
6579 strlen("Organization: "), FALSE
);
6580 g_string_append_printf(header
, "Organization: %s\n", buf
);
6583 /* Program version and system info */
6584 if (compose
->account
->gen_xmailer
&&
6585 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6586 !compose
->newsgroup_list
) {
6587 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6589 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6592 if (compose
->account
->gen_xmailer
&&
6593 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6594 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6596 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6600 /* custom headers */
6601 if (compose
->account
->add_customhdr
) {
6604 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6606 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6608 if (custom_header_is_allowed(chdr
->name
)
6609 && chdr
->value
!= NULL
6610 && *(chdr
->value
) != '\0') {
6611 compose_convert_header
6612 (compose
, buf
, sizeof(buf
),
6614 strlen(chdr
->name
) + 2, FALSE
);
6615 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6620 /* Automatic Faces and X-Faces */
6621 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6622 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6624 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6625 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6627 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6628 g_string_append_printf(header
, "Face: %s\n", buf
);
6630 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6631 g_string_append_printf(header
, "Face: %s\n", buf
);
6635 switch (compose
->priority
) {
6636 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6637 "X-Priority: 1 (Highest)\n");
6639 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6640 "X-Priority: 2 (High)\n");
6642 case PRIORITY_NORMAL
: break;
6643 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6644 "X-Priority: 4 (Low)\n");
6646 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6647 "X-Priority: 5 (Lowest)\n");
6649 default: debug_print("compose: priority unknown : %d\n",
6653 /* get special headers */
6654 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6655 ComposeHeaderEntry
*headerentry
;
6658 gchar
*headername_wcolon
;
6659 const gchar
*headername_trans
;
6662 gboolean standard_header
= FALSE
;
6664 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6666 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6668 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6673 if (!strstr(tmp
, ":")) {
6674 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6675 headername
= g_strdup(tmp
);
6677 headername_wcolon
= g_strdup(tmp
);
6678 headername
= g_strdup(strtok(tmp
, ":"));
6682 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6683 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6684 subst_char(headervalue
, '\r', ' ');
6685 subst_char(headervalue
, '\n', ' ');
6686 string
= std_headers
;
6687 while (*string
!= NULL
) {
6688 headername_trans
= prefs_common_translated_header_name(*string
);
6689 if (!strcmp(headername_trans
, headername_wcolon
))
6690 standard_header
= TRUE
;
6693 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6694 g_string_append_printf(header
, "%s %s\n", headername_wcolon
, headervalue
);
6697 g_free(headername_wcolon
);
6701 g_string_free(header
, FALSE
);
6706 #undef IS_IN_CUSTOM_HEADER
6708 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6709 gint header_len
, gboolean addr_field
)
6711 gchar
*tmpstr
= NULL
;
6712 const gchar
*out_codeset
= NULL
;
6714 cm_return_if_fail(src
!= NULL
);
6715 cm_return_if_fail(dest
!= NULL
);
6717 if (len
< 1) return;
6719 tmpstr
= g_strdup(src
);
6721 subst_char(tmpstr
, '\n', ' ');
6722 subst_char(tmpstr
, '\r', ' ');
6725 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6726 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6727 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
6732 codeconv_set_strict(TRUE
);
6733 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6734 conv_get_charset_str(compose
->out_encoding
));
6735 codeconv_set_strict(FALSE
);
6737 if (!dest
|| *dest
== '\0') {
6738 gchar
*test_conv_global_out
= NULL
;
6739 gchar
*test_conv_reply
= NULL
;
6741 /* automatic mode. be automatic. */
6742 codeconv_set_strict(TRUE
);
6744 out_codeset
= conv_get_outgoing_charset_str();
6746 debug_print("trying to convert to %s\n", out_codeset
);
6747 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
6750 if (!test_conv_global_out
&& compose
->orig_charset
6751 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
6752 out_codeset
= compose
->orig_charset
;
6753 debug_print("failure; trying to convert to %s\n", out_codeset
);
6754 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
6757 if (!test_conv_global_out
&& !test_conv_reply
) {
6759 out_codeset
= CS_INTERNAL
;
6760 debug_print("finally using %s\n", out_codeset
);
6762 g_free(test_conv_global_out
);
6763 g_free(test_conv_reply
);
6764 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6766 codeconv_set_strict(FALSE
);
6771 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
6775 cm_return_if_fail(user_data
!= NULL
);
6777 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
6778 g_strstrip(address
);
6779 if (*address
!= '\0') {
6780 gchar
*name
= procheader_get_fromname(address
);
6781 extract_address(address
);
6782 #ifndef USE_ALT_ADDRBOOK
6783 addressbook_add_contact(name
, address
, NULL
, NULL
);
6785 debug_print("%s: %s\n", name
, address
);
6786 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
6787 debug_print( "addressbook_add_contact - added\n" );
6794 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
6796 GtkWidget
*menuitem
;
6799 cm_return_if_fail(menu
!= NULL
);
6800 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
6802 menuitem
= gtk_separator_menu_item_new();
6803 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
6804 gtk_widget_show(menuitem
);
6806 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6807 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
6809 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
6810 g_strstrip(address
);
6811 if (*address
== '\0') {
6812 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
6815 g_signal_connect(G_OBJECT(menuitem
), "activate",
6816 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
6817 gtk_widget_show(menuitem
);
6820 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
6823 if (strcmp(header
, "")) {
6824 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
6828 void compose_add_extra_header_entries(GtkListStore
*model
)
6832 gchar buf
[BUFFSIZE
];
6835 if (extra_headers
== NULL
) {
6836 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
6837 if ((exh
= g_fopen(exhrc
, "rb")) == NULL
) {
6838 debug_print("extra headers file not found\n");
6839 goto extra_headers_done
;
6841 while (fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
6842 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
6843 while (lastc
>= 0 && buf
[lastc
] != ':')
6844 buf
[lastc
--] = '\0';
6845 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
6846 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
6847 if (custom_header_is_allowed(buf
)) {
6849 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
6852 g_message("disallowed extra header line: %s\n", buf
);
6856 g_message("invalid extra header line: %s\n", buf
);
6862 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
6863 extra_headers
= g_slist_reverse(extra_headers
);
6865 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
6868 static void compose_create_header_entry(Compose
*compose
)
6870 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6877 const gchar
*header
= NULL
;
6878 ComposeHeaderEntry
*headerentry
;
6879 gboolean standard_header
= FALSE
;
6880 GtkListStore
*model
;
6883 headerentry
= g_new0(ComposeHeaderEntry
, 1);
6885 /* Combo box model */
6886 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
6887 #if !GTK_CHECK_VERSION(2, 24, 0)
6888 combo
= gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model
), 0);
6890 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
6892 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
6894 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
6896 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
6897 COMPOSE_NEWSGROUPS
);
6898 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
6900 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
6901 COMPOSE_FOLLOWUPTO
);
6902 compose_add_extra_header_entries(model
);
6905 #if GTK_CHECK_VERSION(2, 24, 0)
6906 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
6907 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
6908 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
6909 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
6910 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
6912 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
6913 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
6914 G_CALLBACK(compose_grab_focus_cb
), compose
);
6915 gtk_widget_show(combo
);
6917 /* Putting only the combobox child into focus chain of its parent causes
6918 * the parent to be skipped when changing focus via Tab or Shift+Tab.
6919 * This eliminates need to pres Tab twice in order to really get from the
6920 * combobox to next widget. */
6922 l
= g_list_prepend(l
, gtk_bin_get_child(GTK_BIN(combo
)));
6923 gtk_container_set_focus_chain(GTK_CONTAINER(combo
), l
);
6926 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
6927 compose
->header_nextrow
, compose
->header_nextrow
+1,
6928 GTK_SHRINK
, GTK_FILL
, 0, 0);
6929 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
6930 const gchar
*last_header_entry
= gtk_entry_get_text(
6931 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
6933 while (*string
!= NULL
) {
6934 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
6935 standard_header
= TRUE
;
6938 if (standard_header
)
6939 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
6941 if (!compose
->header_last
|| !standard_header
) {
6942 switch(compose
->account
->protocol
) {
6944 header
= prefs_common_translated_header_name("Newsgroups:");
6947 header
= prefs_common_translated_header_name("To:");
6952 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
6954 gtk_editable_set_editable(
6955 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
6956 prefs_common
.type_any_header
);
6958 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
6959 G_CALLBACK(compose_grab_focus_cb
), compose
);
6961 /* Entry field with cleanup button */
6962 button
= gtk_button_new();
6963 gtk_button_set_image(GTK_BUTTON(button
),
6964 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
6965 gtk_widget_show(button
);
6966 CLAWS_SET_TIP(button
,
6967 _("Delete entry contents"));
6968 entry
= gtk_entry_new();
6969 gtk_widget_show(entry
);
6970 CLAWS_SET_TIP(entry
,
6971 _("Use <tab> to autocomplete from addressbook"));
6972 hbox
= gtk_hbox_new (FALSE
, 0);
6973 gtk_widget_show(hbox
);
6974 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
6975 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
6976 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
6977 compose
->header_nextrow
, compose
->header_nextrow
+1,
6978 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
6980 g_signal_connect(G_OBJECT(entry
), "key-press-event",
6981 G_CALLBACK(compose_headerentry_key_press_event_cb
),
6983 g_signal_connect(G_OBJECT(entry
), "changed",
6984 G_CALLBACK(compose_headerentry_changed_cb
),
6986 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
6987 G_CALLBACK(compose_grab_focus_cb
), compose
);
6989 g_signal_connect(G_OBJECT(button
), "clicked",
6990 G_CALLBACK(compose_headerentry_button_clicked_cb
),
6994 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
6995 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
6996 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
6997 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
6998 G_CALLBACK(compose_header_drag_received_cb
),
7000 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7001 G_CALLBACK(compose_drag_drop
),
7003 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7004 G_CALLBACK(compose_entry_popup_extend
),
7007 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7009 headerentry
->compose
= compose
;
7010 headerentry
->combo
= combo
;
7011 headerentry
->entry
= entry
;
7012 headerentry
->button
= button
;
7013 headerentry
->hbox
= hbox
;
7014 headerentry
->headernum
= compose
->header_nextrow
;
7015 headerentry
->type
= PREF_NONE
;
7017 compose
->header_nextrow
++;
7018 compose
->header_last
= headerentry
;
7019 compose
->header_list
=
7020 g_slist_append(compose
->header_list
,
7024 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7025 gchar
*text
, ComposePrefType pref_type
)
7027 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7028 gchar
*tmp
= g_strdup(text
), *email
;
7029 gboolean replyto_hdr
;
7031 replyto_hdr
= (!strcasecmp(header
,
7032 prefs_common_translated_header_name("Reply-To:")) ||
7034 prefs_common_translated_header_name("Followup-To:")) ||
7036 prefs_common_translated_header_name("In-Reply-To:")));
7038 extract_address(tmp
);
7039 email
= g_utf8_strdown(tmp
, -1);
7041 if (replyto_hdr
== FALSE
&&
7042 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7044 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7045 header
, text
, (gint
) pref_type
);
7051 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7052 gtk_entry_set_text(GTK_ENTRY(
7053 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7055 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7056 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7057 last_header
->type
= pref_type
;
7059 if (replyto_hdr
== FALSE
)
7060 g_hash_table_insert(compose
->email_hashtable
, email
,
7061 GUINT_TO_POINTER(1));
7068 static void compose_destroy_headerentry(Compose
*compose
,
7069 ComposeHeaderEntry
*headerentry
)
7071 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7074 extract_address(text
);
7075 email
= g_utf8_strdown(text
, -1);
7076 g_hash_table_remove(compose
->email_hashtable
, email
);
7080 gtk_widget_destroy(headerentry
->combo
);
7081 gtk_widget_destroy(headerentry
->entry
);
7082 gtk_widget_destroy(headerentry
->button
);
7083 gtk_widget_destroy(headerentry
->hbox
);
7084 g_free(headerentry
);
7087 static void compose_remove_header_entries(Compose
*compose
)
7090 for (list
= compose
->header_list
; list
; list
= list
->next
)
7091 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7093 compose
->header_last
= NULL
;
7094 g_slist_free(compose
->header_list
);
7095 compose
->header_list
= NULL
;
7096 compose
->header_nextrow
= 1;
7097 compose_create_header_entry(compose
);
7100 static GtkWidget
*compose_create_header(Compose
*compose
)
7102 GtkWidget
*from_optmenu_hbox
;
7103 GtkWidget
*header_table_main
;
7104 GtkWidget
*header_scrolledwin
;
7105 GtkWidget
*header_table
;
7107 /* parent with account selection and from header */
7108 header_table_main
= gtk_table_new(2, 2, FALSE
);
7109 gtk_widget_show(header_table_main
);
7110 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7112 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7113 gtk_table_attach(GTK_TABLE(header_table_main
), from_optmenu_hbox
,
7114 0, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7116 /* child with header labels and entries */
7117 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7118 gtk_widget_show(header_scrolledwin
);
7119 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7121 header_table
= gtk_table_new(2, 2, FALSE
);
7122 gtk_widget_show(header_table
);
7123 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7124 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
7125 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7126 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7127 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7129 gtk_table_attach(GTK_TABLE(header_table_main
), header_scrolledwin
,
7130 0, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 0, 2);
7132 compose
->header_table
= header_table
;
7133 compose
->header_list
= NULL
;
7134 compose
->header_nextrow
= 0;
7136 compose_create_header_entry(compose
);
7138 compose
->table
= NULL
;
7140 return header_table_main
;
7143 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7145 Compose
*compose
= (Compose
*)data
;
7146 GdkEventButton event
;
7149 event
.time
= gtk_get_current_event_time();
7151 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7154 static GtkWidget
*compose_create_attach(Compose
*compose
)
7156 GtkWidget
*attach_scrwin
;
7157 GtkWidget
*attach_clist
;
7159 GtkListStore
*store
;
7160 GtkCellRenderer
*renderer
;
7161 GtkTreeViewColumn
*column
;
7162 GtkTreeSelection
*selection
;
7164 /* attachment list */
7165 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7166 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7167 GTK_POLICY_AUTOMATIC
,
7168 GTK_POLICY_AUTOMATIC
);
7169 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7171 store
= gtk_list_store_new(N_ATTACH_COLS
,
7177 G_TYPE_AUTO_POINTER
,
7179 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7180 (GTK_TREE_MODEL(store
)));
7181 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7182 g_object_unref(store
);
7184 renderer
= gtk_cell_renderer_text_new();
7185 column
= gtk_tree_view_column_new_with_attributes
7186 (_("Mime type"), renderer
, "text",
7187 COL_MIMETYPE
, NULL
);
7188 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7190 renderer
= gtk_cell_renderer_text_new();
7191 column
= gtk_tree_view_column_new_with_attributes
7192 (_("Size"), renderer
, "text",
7194 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7196 renderer
= gtk_cell_renderer_text_new();
7197 column
= gtk_tree_view_column_new_with_attributes
7198 (_("Name"), renderer
, "text",
7200 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7202 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7203 prefs_common
.use_stripes_everywhere
);
7204 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7205 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7207 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7208 G_CALLBACK(attach_selected
), compose
);
7209 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7210 G_CALLBACK(attach_button_pressed
), compose
);
7211 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7212 G_CALLBACK(popup_attach_button_pressed
), compose
);
7213 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7214 G_CALLBACK(attach_key_pressed
), compose
);
7217 gtk_drag_dest_set(attach_clist
,
7218 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7219 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7220 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7221 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7222 G_CALLBACK(compose_attach_drag_received_cb
),
7224 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7225 G_CALLBACK(compose_drag_drop
),
7228 compose
->attach_scrwin
= attach_scrwin
;
7229 compose
->attach_clist
= attach_clist
;
7231 return attach_scrwin
;
7234 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
);
7235 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7237 static GtkWidget
*compose_create_others(Compose
*compose
)
7240 GtkWidget
*savemsg_checkbtn
;
7241 GtkWidget
*savemsg_combo
;
7242 GtkWidget
*savemsg_select
;
7245 gchar
*folderidentifier
;
7247 /* Table for settings */
7248 table
= gtk_table_new(3, 1, FALSE
);
7249 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7250 gtk_widget_show(table
);
7251 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
7254 /* Save Message to folder */
7255 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7256 gtk_widget_show(savemsg_checkbtn
);
7257 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7258 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7259 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7261 g_signal_connect(G_OBJECT(savemsg_checkbtn
), "toggled",
7262 G_CALLBACK(compose_savemsg_checkbtn_cb
), compose
);
7264 #if !GTK_CHECK_VERSION(2, 24, 0)
7265 savemsg_combo
= gtk_combo_box_entry_new_text();
7267 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7269 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7270 compose
->savemsg_combo
= savemsg_combo
;
7271 gtk_widget_show(savemsg_combo
);
7273 if (prefs_common
.compose_save_to_history
)
7274 #if !GTK_CHECK_VERSION(2, 24, 0)
7275 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo
),
7276 prefs_common
.compose_save_to_history
);
7278 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7279 prefs_common
.compose_save_to_history
);
7281 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
7282 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7283 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7284 G_CALLBACK(compose_grab_focus_cb
), compose
);
7285 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7286 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7287 (compose
->account
, F_OUTBOX
));
7288 compose_set_save_to(compose
, folderidentifier
);
7289 g_free(folderidentifier
);
7292 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7293 gtk_widget_show(savemsg_select
);
7294 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7295 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7296 G_CALLBACK(compose_savemsg_select_cb
),
7302 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
)
7304 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
),
7305 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
)));
7308 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7313 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
);
7316 path
= folder_item_get_identifier(dest
);
7318 compose_set_save_to(compose
, path
);
7322 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7323 GdkAtom clip
, GtkTextIter
*insert_place
);
7326 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7330 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7332 if (event
->button
== 3) {
7334 GtkTextIter sel_start
, sel_end
;
7335 gboolean stuff_selected
;
7337 /* move the cursor to allow GtkAspell to check the word
7338 * under the mouse */
7339 if (event
->x
&& event
->y
) {
7340 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7341 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7343 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7346 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7347 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7350 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7352 &sel_start
, &sel_end
);
7354 gtk_text_buffer_place_cursor (buffer
, &iter
);
7355 /* reselect stuff */
7357 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7358 gtk_text_buffer_select_range(buffer
,
7359 &sel_start
, &sel_end
);
7361 return FALSE
; /* pass the event so that the right-click goes through */
7364 if (event
->button
== 2) {
7369 /* get the middle-click position to paste at the correct place */
7370 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7371 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7373 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7376 entry_paste_clipboard(compose
, text
,
7377 prefs_common
.linewrap_pastes
,
7378 GDK_SELECTION_PRIMARY
, &iter
);
7386 static void compose_spell_menu_changed(void *data
)
7388 Compose
*compose
= (Compose
*)data
;
7390 GtkWidget
*menuitem
;
7391 GtkWidget
*parent_item
;
7392 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7395 if (compose
->gtkaspell
== NULL
)
7398 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7399 "/Menu/Spelling/Options");
7401 /* setting the submenu removes /Spelling/Options from the factory
7402 * so we need to save it */
7404 if (parent_item
== NULL
) {
7405 parent_item
= compose
->aspell_options_menu
;
7406 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7408 compose
->aspell_options_menu
= parent_item
;
7410 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7412 spell_menu
= g_slist_reverse(spell_menu
);
7413 for (items
= spell_menu
;
7414 items
; items
= items
->next
) {
7415 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7416 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7417 gtk_widget_show(GTK_WIDGET(menuitem
));
7419 g_slist_free(spell_menu
);
7421 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7422 gtk_widget_show(parent_item
);
7425 static void compose_dict_changed(void *data
)
7427 Compose
*compose
= (Compose
*) data
;
7429 if(!compose
->gtkaspell
)
7431 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7434 gtkaspell_highlight_all(compose
->gtkaspell
);
7435 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7439 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7441 Compose
*compose
= (Compose
*)data
;
7442 GdkEventButton event
;
7445 event
.time
= gtk_get_current_event_time();
7449 return text_clicked(compose
->text
, &event
, compose
);
7452 static gboolean compose_force_window_origin
= TRUE
;
7453 static Compose
*compose_create(PrefsAccount
*account
,
7462 GtkWidget
*handlebox
;
7464 GtkWidget
*notebook
;
7466 GtkWidget
*attach_hbox
;
7467 GtkWidget
*attach_lab1
;
7468 GtkWidget
*attach_lab2
;
7473 GtkWidget
*subject_hbox
;
7474 GtkWidget
*subject_frame
;
7475 GtkWidget
*subject_entry
;
7479 GtkWidget
*edit_vbox
;
7480 GtkWidget
*ruler_hbox
;
7482 GtkWidget
*scrolledwin
;
7484 GtkTextBuffer
*buffer
;
7485 GtkClipboard
*clipboard
;
7487 UndoMain
*undostruct
;
7489 GtkWidget
*popupmenu
;
7490 GtkWidget
*tmpl_menu
;
7491 GtkActionGroup
*action_group
= NULL
;
7494 GtkAspell
* gtkaspell
= NULL
;
7497 static GdkGeometry geometry
;
7499 cm_return_val_if_fail(account
!= NULL
, NULL
);
7501 debug_print("Creating compose window...\n");
7502 compose
= g_new0(Compose
, 1);
7504 compose
->batch
= batch
;
7505 compose
->account
= account
;
7506 compose
->folder
= folder
;
7508 compose
->mutex
= cm_mutex_new();
7509 compose
->set_cursor_pos
= -1;
7511 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7513 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7514 gtk_widget_set_size_request(window
, prefs_common
.compose_width
,
7515 prefs_common
.compose_height
);
7517 if (!geometry
.max_width
) {
7518 geometry
.max_width
= gdk_screen_width();
7519 geometry
.max_height
= gdk_screen_height();
7522 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7523 &geometry
, GDK_HINT_MAX_SIZE
);
7524 if (!geometry
.min_width
) {
7525 geometry
.min_width
= 600;
7526 geometry
.min_height
= 440;
7528 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7529 &geometry
, GDK_HINT_MIN_SIZE
);
7531 #ifndef GENERIC_UMPC
7532 if (compose_force_window_origin
)
7533 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7534 prefs_common
.compose_y
);
7536 g_signal_connect(G_OBJECT(window
), "delete_event",
7537 G_CALLBACK(compose_delete_cb
), compose
);
7538 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7539 gtk_widget_realize(window
);
7541 gtkut_widget_set_composer_icon(window
);
7543 vbox
= gtk_vbox_new(FALSE
, 0);
7544 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7546 compose
->ui_manager
= gtk_ui_manager_new();
7547 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7548 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7549 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7550 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7551 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7552 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7553 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7554 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7555 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7556 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7558 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7560 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7561 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7563 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7565 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7566 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7567 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7570 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7571 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7572 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7573 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7574 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7575 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7576 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7577 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7578 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7579 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7580 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7581 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7582 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7585 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7586 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7587 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7589 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7590 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7591 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7593 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7594 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7595 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7596 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7598 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7600 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7601 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7602 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7603 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7604 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7605 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7606 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7607 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7608 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7609 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7610 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7611 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7612 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7613 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7614 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7616 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7618 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7619 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7620 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7621 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7622 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7624 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7626 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7630 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7631 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7632 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7633 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7634 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7635 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7639 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7640 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7641 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7642 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7643 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7645 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7646 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7647 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7648 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7649 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7652 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7653 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7654 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7655 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7656 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7657 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7658 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7660 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7661 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7662 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7663 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7664 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7666 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7668 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7669 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7670 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7671 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7672 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7674 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7675 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
)
7676 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
)
7677 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7679 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7681 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7682 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
)
7683 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
)
7685 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7687 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7688 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
)
7689 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7691 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7692 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
)
7693 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7695 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7697 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7698 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
)
7699 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7700 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7701 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7702 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7704 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7705 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
)
7706 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
)
7707 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7708 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7710 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7711 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7712 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7713 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
7714 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
7715 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
7717 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
7718 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
7719 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
)
7721 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
7722 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
7723 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
7727 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
7728 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
7729 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
7730 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7731 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
7732 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7735 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
7737 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
7738 gtk_widget_show_all(menubar
);
7740 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
7741 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
7743 if (prefs_common
.toolbar_detachable
) {
7744 handlebox
= gtk_handle_box_new();
7746 handlebox
= gtk_hbox_new(FALSE
, 0);
7748 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
7750 gtk_widget_realize(handlebox
);
7751 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
7754 vbox2
= gtk_vbox_new(FALSE
, 2);
7755 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
7756 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
7759 notebook
= gtk_notebook_new();
7760 gtk_widget_show(notebook
);
7762 /* header labels and entries */
7763 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7764 compose_create_header(compose
),
7765 gtk_label_new_with_mnemonic(_("Hea_der")));
7766 /* attachment list */
7767 attach_hbox
= gtk_hbox_new(FALSE
, 0);
7768 gtk_widget_show(attach_hbox
);
7770 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
7771 gtk_widget_show(attach_lab1
);
7772 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
7774 attach_lab2
= gtk_label_new("");
7775 gtk_widget_show(attach_lab2
);
7776 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
7778 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7779 compose_create_attach(compose
),
7782 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7783 compose_create_others(compose
),
7784 gtk_label_new_with_mnemonic(_("Othe_rs")));
7787 subject_hbox
= gtk_hbox_new(FALSE
, 0);
7788 gtk_widget_show(subject_hbox
);
7790 subject_frame
= gtk_frame_new(NULL
);
7791 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
7792 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
7793 gtk_widget_show(subject_frame
);
7795 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
7796 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
7797 gtk_widget_show(subject
);
7799 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
7800 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
7801 gtk_widget_show(label
);
7804 subject_entry
= claws_spell_entry_new();
7806 subject_entry
= gtk_entry_new();
7808 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
7809 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
7810 G_CALLBACK(compose_grab_focus_cb
), compose
);
7811 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
7812 gtk_widget_show(subject_entry
);
7813 compose
->subject_entry
= subject_entry
;
7814 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
7816 edit_vbox
= gtk_vbox_new(FALSE
, 0);
7818 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
7821 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
7822 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
7824 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
7825 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
7826 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
7830 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7831 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
7832 GTK_POLICY_AUTOMATIC
,
7833 GTK_POLICY_AUTOMATIC
);
7834 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
7836 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
7838 text
= gtk_text_view_new();
7839 if (prefs_common
.show_compose_margin
) {
7840 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
7841 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
7843 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7844 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
7845 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
7846 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
7847 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
7849 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
7850 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
7851 G_CALLBACK(compose_edit_size_alloc
),
7853 g_signal_connect(G_OBJECT(buffer
), "changed",
7854 G_CALLBACK(compose_changed_cb
), compose
);
7855 g_signal_connect(G_OBJECT(text
), "grab_focus",
7856 G_CALLBACK(compose_grab_focus_cb
), compose
);
7857 g_signal_connect(G_OBJECT(buffer
), "insert_text",
7858 G_CALLBACK(text_inserted
), compose
);
7859 g_signal_connect(G_OBJECT(text
), "button_press_event",
7860 G_CALLBACK(text_clicked
), compose
);
7861 g_signal_connect(G_OBJECT(text
), "popup-menu",
7862 G_CALLBACK(compose_popup_menu
), compose
);
7863 g_signal_connect(G_OBJECT(subject_entry
), "changed",
7864 G_CALLBACK(compose_changed_cb
), compose
);
7865 g_signal_connect(G_OBJECT(subject_entry
), "activate",
7866 G_CALLBACK(compose_subject_entry_activated
), compose
);
7869 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7870 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7871 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7872 g_signal_connect(G_OBJECT(text
), "drag_data_received",
7873 G_CALLBACK(compose_insert_drag_received_cb
),
7875 g_signal_connect(G_OBJECT(text
), "drag-drop",
7876 G_CALLBACK(compose_drag_drop
),
7878 g_signal_connect(G_OBJECT(text
), "key-press-event",
7879 G_CALLBACK(completion_set_focus_to_subject
),
7881 gtk_widget_show_all(vbox
);
7883 /* pane between attach clist and text */
7884 paned
= gtk_vpaned_new();
7885 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
7886 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
7887 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
7888 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
7889 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
7890 G_CALLBACK(compose_notebook_size_alloc
), paned
);
7892 gtk_widget_show_all(paned
);
7895 if (prefs_common
.textfont
) {
7896 PangoFontDescription
*font_desc
;
7898 font_desc
= pango_font_description_from_string
7899 (prefs_common
.textfont
);
7901 gtk_widget_modify_font(text
, font_desc
);
7902 pango_font_description_free(font_desc
);
7906 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
7907 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
7908 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
7909 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
7912 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
7913 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
7915 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
7917 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
7918 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
7919 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
7921 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
7923 undostruct
= undo_init(text
);
7924 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
7927 address_completion_start(window
);
7929 compose
->window
= window
;
7930 compose
->vbox
= vbox
;
7931 compose
->menubar
= menubar
;
7932 compose
->handlebox
= handlebox
;
7934 compose
->vbox2
= vbox2
;
7936 compose
->paned
= paned
;
7938 compose
->attach_label
= attach_lab2
;
7940 compose
->notebook
= notebook
;
7941 compose
->edit_vbox
= edit_vbox
;
7942 compose
->ruler_hbox
= ruler_hbox
;
7943 compose
->ruler
= ruler
;
7944 compose
->scrolledwin
= scrolledwin
;
7945 compose
->text
= text
;
7947 compose
->focused_editable
= NULL
;
7949 compose
->popupmenu
= popupmenu
;
7951 compose
->tmpl_menu
= tmpl_menu
;
7953 compose
->mode
= mode
;
7954 compose
->rmode
= mode
;
7956 compose
->targetinfo
= NULL
;
7957 compose
->replyinfo
= NULL
;
7958 compose
->fwdinfo
= NULL
;
7960 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
7961 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
7963 compose
->replyto
= NULL
;
7965 compose
->bcc
= NULL
;
7966 compose
->followup_to
= NULL
;
7968 compose
->ml_post
= NULL
;
7970 compose
->inreplyto
= NULL
;
7971 compose
->references
= NULL
;
7972 compose
->msgid
= NULL
;
7973 compose
->boundary
= NULL
;
7975 compose
->autowrap
= prefs_common
.autowrap
;
7976 compose
->autoindent
= prefs_common
.auto_indent
;
7977 compose
->use_signing
= FALSE
;
7978 compose
->use_encryption
= FALSE
;
7979 compose
->privacy_system
= NULL
;
7980 compose
->encdata
= NULL
;
7982 compose
->modified
= FALSE
;
7984 compose
->return_receipt
= FALSE
;
7986 compose
->to_list
= NULL
;
7987 compose
->newsgroup_list
= NULL
;
7989 compose
->undostruct
= undostruct
;
7991 compose
->sig_str
= NULL
;
7993 compose
->exteditor_file
= NULL
;
7994 compose
->exteditor_pid
= -1;
7995 compose
->exteditor_tag
= -1;
7996 compose
->exteditor_socket
= NULL
;
7997 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
7999 compose
->folder_update_callback_id
=
8000 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8001 compose_update_folder_hook
,
8002 (gpointer
) compose
);
8005 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8006 if (mode
!= COMPOSE_REDIRECT
) {
8007 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8008 strcmp(prefs_common
.dictionary
, "")) {
8009 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8010 prefs_common
.alt_dictionary
,
8011 conv_get_locale_charset_str(),
8012 prefs_common
.misspelled_col
,
8013 prefs_common
.check_while_typing
,
8014 prefs_common
.recheck_when_changing_dict
,
8015 prefs_common
.use_alternate
,
8016 prefs_common
.use_both_dicts
,
8017 GTK_TEXT_VIEW(text
),
8018 GTK_WINDOW(compose
->window
),
8019 compose_dict_changed
,
8020 compose_spell_menu_changed
,
8023 alertpanel_error(_("Spell checker could not "
8025 gtkaspell_checkers_strerror());
8026 gtkaspell_checkers_reset_error();
8028 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8032 compose
->gtkaspell
= gtkaspell
;
8033 compose_spell_menu_changed(compose
);
8034 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8037 compose_select_account(compose
, account
, TRUE
);
8039 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8040 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8042 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8043 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8045 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8046 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8048 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8049 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8051 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8052 if (account
->protocol
!= A_NNTP
)
8053 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8054 prefs_common_translated_header_name("To:"));
8056 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8057 prefs_common_translated_header_name("Newsgroups:"));
8059 #ifndef USE_ALT_ADDRBOOK
8060 addressbook_set_target_compose(compose
);
8062 if (mode
!= COMPOSE_REDIRECT
)
8063 compose_set_template_menu(compose
);
8065 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8068 compose_list
= g_list_append(compose_list
, compose
);
8070 if (!prefs_common
.show_ruler
)
8071 gtk_widget_hide(ruler_hbox
);
8073 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8076 compose
->priority
= PRIORITY_NORMAL
;
8077 compose_update_priority_menu_item(compose
);
8079 compose_set_out_encoding(compose
);
8082 compose_update_actions_menu(compose
);
8084 /* Privacy Systems menu */
8085 compose_update_privacy_systems_menu(compose
);
8087 activate_privacy_system(compose
, account
, TRUE
);
8088 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8090 gtk_widget_realize(window
);
8092 gtk_widget_show(window
);
8095 gtkut_convert_int_to_gdk_color(prefs_common
.default_to_bgcolor
,
8096 &default_to_bgcolor
);
8097 gtkut_convert_int_to_gdk_color(prefs_common
.default_to_color
,
8103 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8108 GtkWidget
*optmenubox
;
8109 GtkWidget
*fromlabel
;
8112 GtkWidget
*from_name
= NULL
;
8114 gint num
= 0, def_menu
= 0;
8116 accounts
= account_get_list();
8117 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8119 optmenubox
= gtk_event_box_new();
8120 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8121 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8123 hbox
= gtk_hbox_new(FALSE
, 4);
8124 from_name
= gtk_entry_new();
8126 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8127 G_CALLBACK(compose_grab_focus_cb
), compose
);
8128 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8129 G_CALLBACK(from_name_activate_cb
), optmenu
);
8131 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8132 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8133 gchar
*name
, *from
= NULL
;
8135 if (ac
== compose
->account
) def_menu
= num
;
8137 name
= g_markup_printf_escaped("<i>%s</i>",
8140 if (ac
== compose
->account
) {
8141 if (ac
->name
&& *ac
->name
) {
8143 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8144 from
= g_strdup_printf("%s <%s>",
8146 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8148 from
= g_strdup_printf("%s",
8150 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8153 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8158 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8160 g_signal_connect(G_OBJECT(optmenu
), "changed",
8161 G_CALLBACK(account_activated
),
8163 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8164 G_CALLBACK(compose_entry_popup_extend
),
8167 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8168 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8170 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8171 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8172 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8174 /* Putting only the GtkEntry into focus chain of parent hbox causes
8175 * the account selector combobox next to it to be unreachable when
8176 * navigating widgets in GtkTable with up/down arrow keys.
8177 * Note: gtk_widget_set_can_focus() was not enough. */
8179 l
= g_list_prepend(l
, from_name
);
8180 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), l
);
8183 CLAWS_SET_TIP(optmenubox
,
8184 _("Account to use for this email"));
8185 CLAWS_SET_TIP(from_name
,
8186 _("Sender address to be used"));
8188 compose
->account_combo
= optmenu
;
8189 compose
->from_name
= from_name
;
8194 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8196 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8197 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8198 Compose
*compose
= (Compose
*) data
;
8200 compose
->priority
= value
;
8204 static void compose_reply_change_mode(Compose
*compose
,
8207 gboolean was_modified
= compose
->modified
;
8209 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8211 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8213 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8215 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8217 if (action
== COMPOSE_REPLY_TO_ALL
)
8219 if (action
== COMPOSE_REPLY_TO_SENDER
)
8221 if (action
== COMPOSE_REPLY_TO_LIST
)
8224 compose_remove_header_entries(compose
);
8225 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8226 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8227 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8229 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8230 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8232 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8233 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8234 compose_show_first_last_header(compose
, TRUE
);
8235 compose
->modified
= was_modified
;
8236 compose_set_title(compose
);
8239 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8241 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8242 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8243 Compose
*compose
= (Compose
*) data
;
8246 compose_reply_change_mode(compose
, value
);
8249 static void compose_update_priority_menu_item(Compose
* compose
)
8251 GtkWidget
*menuitem
= NULL
;
8252 switch (compose
->priority
) {
8253 case PRIORITY_HIGHEST
:
8254 menuitem
= gtk_ui_manager_get_widget
8255 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8258 menuitem
= gtk_ui_manager_get_widget
8259 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8261 case PRIORITY_NORMAL
:
8262 menuitem
= gtk_ui_manager_get_widget
8263 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8266 menuitem
= gtk_ui_manager_get_widget
8267 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8269 case PRIORITY_LOWEST
:
8270 menuitem
= gtk_ui_manager_get_widget
8271 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8274 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8277 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8279 Compose
*compose
= (Compose
*) data
;
8281 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8283 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8285 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8288 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8289 g_free(compose
->privacy_system
);
8290 compose
->privacy_system
= NULL
;
8291 g_free(compose
->encdata
);
8292 compose
->encdata
= NULL
;
8293 if (systemid
!= NULL
) {
8294 compose
->privacy_system
= g_strdup(systemid
);
8296 can_sign
= privacy_system_can_sign(systemid
);
8297 can_encrypt
= privacy_system_can_encrypt(systemid
);
8300 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8302 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8303 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8306 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8308 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8309 GtkWidget
*menuitem
= NULL
;
8310 GList
*children
, *amenu
;
8311 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8312 gboolean found
= FALSE
;
8314 if (compose
->privacy_system
!= NULL
) {
8316 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8317 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8318 cm_return_if_fail(menuitem
!= NULL
);
8320 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8323 while (amenu
!= NULL
) {
8324 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8325 if (systemid
!= NULL
) {
8326 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8327 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8328 menuitem
= GTK_WIDGET(amenu
->data
);
8330 can_sign
= privacy_system_can_sign(systemid
);
8331 can_encrypt
= privacy_system_can_encrypt(systemid
);
8335 } else if (strlen(compose
->privacy_system
) == 0 &&
8336 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8337 menuitem
= GTK_WIDGET(amenu
->data
);
8340 can_encrypt
= FALSE
;
8345 amenu
= amenu
->next
;
8347 g_list_free(children
);
8348 if (menuitem
!= NULL
)
8349 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8351 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8352 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8353 "will not be able to sign or encrypt this message."),
8354 compose
->privacy_system
);
8358 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8359 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8362 static void compose_set_out_encoding(Compose
*compose
)
8364 CharSet out_encoding
;
8365 const gchar
*branch
= NULL
;
8366 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8368 switch(out_encoding
) {
8369 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8370 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8371 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8372 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8373 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8374 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8375 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8376 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8377 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8378 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8379 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8380 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8381 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8382 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8383 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8384 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8385 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8386 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8387 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8388 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8389 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8390 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8391 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8392 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8393 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8394 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8395 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8396 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8397 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8398 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8399 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8400 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8401 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8402 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8404 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8407 static void compose_set_template_menu(Compose
*compose
)
8409 GSList
*tmpl_list
, *cur
;
8413 tmpl_list
= template_get_config();
8415 menu
= gtk_menu_new();
8417 gtk_menu_set_accel_group (GTK_MENU (menu
),
8418 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8419 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8420 Template
*tmpl
= (Template
*)cur
->data
;
8421 gchar
*accel_path
= NULL
;
8422 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8423 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8424 g_signal_connect(G_OBJECT(item
), "activate",
8425 G_CALLBACK(compose_template_activate_cb
),
8427 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8428 gtk_widget_show(item
);
8429 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8430 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8434 gtk_widget_show(menu
);
8435 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8438 void compose_update_actions_menu(Compose
*compose
)
8440 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8443 static void compose_update_privacy_systems_menu(Compose
*compose
)
8445 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8446 GSList
*systems
, *cur
;
8448 GtkWidget
*system_none
;
8450 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8451 GtkWidget
*privacy_menu
= gtk_menu_new();
8453 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8454 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8456 g_signal_connect(G_OBJECT(system_none
), "activate",
8457 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8459 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8460 gtk_widget_show(system_none
);
8462 systems
= privacy_get_system_ids();
8463 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8464 gchar
*systemid
= cur
->data
;
8466 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8467 widget
= gtk_radio_menu_item_new_with_label(group
,
8468 privacy_system_get_name(systemid
));
8469 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8470 g_strdup(systemid
), g_free
);
8471 g_signal_connect(G_OBJECT(widget
), "activate",
8472 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8474 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8475 gtk_widget_show(widget
);
8478 g_slist_free(systems
);
8479 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8480 gtk_widget_show_all(privacy_menu
);
8481 gtk_widget_show_all(privacy_menuitem
);
8484 void compose_reflect_prefs_all(void)
8489 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8490 compose
= (Compose
*)cur
->data
;
8491 compose_set_template_menu(compose
);
8495 void compose_reflect_prefs_pixmap_theme(void)
8500 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8501 compose
= (Compose
*)cur
->data
;
8502 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8506 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8508 const gchar
*qmark
= NULL
;
8510 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8512 switch (compose
->mode
) {
8513 /* use forward-specific quote char */
8514 case COMPOSE_FORWARD
:
8515 case COMPOSE_FORWARD_AS_ATTACH
:
8516 case COMPOSE_FORWARD_INLINE
:
8517 if (compose
->folder
&& compose
->folder
->prefs
&&
8518 compose
->folder
->prefs
->forward_with_format
)
8519 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8520 else if (compose
->account
->forward_with_format
)
8521 qmark
= compose
->account
->forward_quotemark
;
8523 qmark
= prefs_common
.fw_quotemark
;
8526 /* use reply-specific quote char in all other modes */
8528 if (compose
->folder
&& compose
->folder
->prefs
&&
8529 compose
->folder
->prefs
->reply_with_format
)
8530 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8531 else if (compose
->account
->reply_with_format
)
8532 qmark
= compose
->account
->reply_quotemark
;
8534 qmark
= prefs_common
.quotemark
;
8538 if (qmark
== NULL
|| *qmark
== '\0')
8544 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8548 GtkTextBuffer
*buffer
;
8552 gchar
*parsed_str
= NULL
;
8553 gint cursor_pos
= 0;
8554 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8557 /* process the body */
8559 text
= GTK_TEXT_VIEW(compose
->text
);
8560 buffer
= gtk_text_view_get_buffer(text
);
8563 qmark
= compose_quote_char_from_context(compose
);
8565 if (compose
->replyinfo
!= NULL
) {
8568 gtk_text_buffer_set_text(buffer
, "", -1);
8569 mark
= gtk_text_buffer_get_insert(buffer
);
8570 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8572 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8573 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8575 } else if (compose
->fwdinfo
!= NULL
) {
8578 gtk_text_buffer_set_text(buffer
, "", -1);
8579 mark
= gtk_text_buffer_get_insert(buffer
);
8580 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8582 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8583 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8586 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8588 GtkTextIter start
, end
;
8591 gtk_text_buffer_get_start_iter(buffer
, &start
);
8592 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8593 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8595 /* clear the buffer now */
8597 gtk_text_buffer_set_text(buffer
, "", -1);
8599 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8600 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8601 procmsg_msginfo_free( &dummyinfo
);
8607 gtk_text_buffer_set_text(buffer
, "", -1);
8608 mark
= gtk_text_buffer_get_insert(buffer
);
8609 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8612 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8613 compose_insert_sig(compose
, FALSE
);
8615 if (replace
&& parsed_str
) {
8616 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8617 gtk_text_buffer_place_cursor(buffer
, &iter
);
8621 cursor_pos
= quote_fmt_get_cursor_pos();
8622 compose
->set_cursor_pos
= cursor_pos
;
8623 if (cursor_pos
== -1)
8625 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8626 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8627 gtk_text_buffer_place_cursor(buffer
, &iter
);
8630 /* process the other fields */
8632 compose_template_apply_fields(compose
, tmpl
);
8633 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8634 quote_fmt_reset_vartable();
8635 compose_changed_cb(NULL
, compose
);
8638 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8639 gtkaspell_highlight_all(compose
->gtkaspell
);
8643 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8645 MsgInfo
* dummyinfo
= NULL
;
8646 MsgInfo
*msginfo
= NULL
;
8649 if (compose
->replyinfo
!= NULL
)
8650 msginfo
= compose
->replyinfo
;
8651 else if (compose
->fwdinfo
!= NULL
)
8652 msginfo
= compose
->fwdinfo
;
8654 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8655 msginfo
= dummyinfo
;
8658 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8660 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8661 compose
->gtkaspell
);
8663 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8665 quote_fmt_scan_string(tmpl
->from
);
8668 buf
= quote_fmt_get_buffer();
8670 alertpanel_error(_("Template From format error."));
8672 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
8676 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
8678 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8679 compose
->gtkaspell
);
8681 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8683 quote_fmt_scan_string(tmpl
->to
);
8686 buf
= quote_fmt_get_buffer();
8688 alertpanel_error(_("Template To format error."));
8690 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
8694 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
8696 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8697 compose
->gtkaspell
);
8699 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8701 quote_fmt_scan_string(tmpl
->cc
);
8704 buf
= quote_fmt_get_buffer();
8706 alertpanel_error(_("Template Cc format error."));
8708 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
8712 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
8714 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8715 compose
->gtkaspell
);
8717 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8719 quote_fmt_scan_string(tmpl
->bcc
);
8722 buf
= quote_fmt_get_buffer();
8724 alertpanel_error(_("Template Bcc format error."));
8726 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
8730 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
8732 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8733 compose
->gtkaspell
);
8735 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8737 quote_fmt_scan_string(tmpl
->replyto
);
8740 buf
= quote_fmt_get_buffer();
8742 alertpanel_error(_("Template Reply-To format error."));
8744 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
8748 /* process the subject */
8749 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
8751 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8752 compose
->gtkaspell
);
8754 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8756 quote_fmt_scan_string(tmpl
->subject
);
8759 buf
= quote_fmt_get_buffer();
8761 alertpanel_error(_("Template subject format error."));
8763 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
8767 procmsg_msginfo_free( &dummyinfo
);
8770 static void compose_destroy(Compose
*compose
)
8772 GtkAllocation allocation
;
8773 GtkTextBuffer
*buffer
;
8774 GtkClipboard
*clipboard
;
8776 compose_list
= g_list_remove(compose_list
, compose
);
8778 if (compose
->updating
) {
8779 debug_print("danger, not destroying anything now\n");
8780 compose
->deferred_destroy
= TRUE
;
8784 /* NOTE: address_completion_end() does nothing with the window
8785 * however this may change. */
8786 address_completion_end(compose
->window
);
8788 slist_free_strings_full(compose
->to_list
);
8789 slist_free_strings_full(compose
->newsgroup_list
);
8790 slist_free_strings_full(compose
->header_list
);
8792 slist_free_strings_full(extra_headers
);
8793 extra_headers
= NULL
;
8795 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
8797 g_hash_table_destroy(compose
->email_hashtable
);
8799 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
8800 compose
->folder_update_callback_id
);
8802 procmsg_msginfo_free(&(compose
->targetinfo
));
8803 procmsg_msginfo_free(&(compose
->replyinfo
));
8804 procmsg_msginfo_free(&(compose
->fwdinfo
));
8806 g_free(compose
->replyto
);
8807 g_free(compose
->cc
);
8808 g_free(compose
->bcc
);
8809 g_free(compose
->newsgroups
);
8810 g_free(compose
->followup_to
);
8812 g_free(compose
->ml_post
);
8814 g_free(compose
->inreplyto
);
8815 g_free(compose
->references
);
8816 g_free(compose
->msgid
);
8817 g_free(compose
->boundary
);
8819 g_free(compose
->redirect_filename
);
8820 if (compose
->undostruct
)
8821 undo_destroy(compose
->undostruct
);
8823 g_free(compose
->sig_str
);
8825 g_free(compose
->exteditor_file
);
8827 g_free(compose
->orig_charset
);
8829 g_free(compose
->privacy_system
);
8830 g_free(compose
->encdata
);
8832 #ifndef USE_ALT_ADDRBOOK
8833 if (addressbook_get_target_compose() == compose
)
8834 addressbook_set_target_compose(NULL
);
8837 if (compose
->gtkaspell
) {
8838 gtkaspell_delete(compose
->gtkaspell
);
8839 compose
->gtkaspell
= NULL
;
8843 if (!compose
->batch
) {
8844 gtk_widget_get_allocation(compose
->window
, &allocation
);
8845 prefs_common
.compose_width
= allocation
.width
;
8846 prefs_common
.compose_height
= allocation
.height
;
8849 if (!gtk_widget_get_parent(compose
->paned
))
8850 gtk_widget_destroy(compose
->paned
);
8851 gtk_widget_destroy(compose
->popupmenu
);
8853 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
8854 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8855 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
8857 gtk_widget_destroy(compose
->window
);
8858 toolbar_destroy(compose
->toolbar
);
8859 g_free(compose
->toolbar
);
8860 cm_mutex_free(compose
->mutex
);
8864 static void compose_attach_info_free(AttachInfo
*ainfo
)
8866 g_free(ainfo
->file
);
8867 g_free(ainfo
->content_type
);
8868 g_free(ainfo
->name
);
8869 g_free(ainfo
->charset
);
8873 static void compose_attach_update_label(Compose
*compose
)
8878 GtkTreeModel
*model
;
8883 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
8884 if(!gtk_tree_model_get_iter_first(model
, &iter
)) {
8885 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
8889 while(gtk_tree_model_iter_next(model
, &iter
))
8892 text
= g_strdup_printf("(%d)", i
);
8893 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
8897 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
8899 Compose
*compose
= (Compose
*)data
;
8900 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
8901 GtkTreeSelection
*selection
;
8903 GtkTreeModel
*model
;
8905 selection
= gtk_tree_view_get_selection(tree_view
);
8906 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
8911 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
8912 GtkTreePath
*path
= cur
->data
;
8913 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
8916 gtk_tree_path_free(path
);
8919 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
8920 GtkTreeRowReference
*ref
= cur
->data
;
8921 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
8924 if (gtk_tree_model_get_iter(model
, &iter
, path
))
8925 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
8927 gtk_tree_path_free(path
);
8928 gtk_tree_row_reference_free(ref
);
8932 compose_attach_update_label(compose
);
8935 static struct _AttachProperty
8938 GtkWidget
*mimetype_entry
;
8939 GtkWidget
*encoding_optmenu
;
8940 GtkWidget
*path_entry
;
8941 GtkWidget
*filename_entry
;
8943 GtkWidget
*cancel_btn
;
8946 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
8948 gtk_tree_path_free((GtkTreePath
*)ptr
);
8951 static void compose_attach_property(GtkAction
*action
, gpointer data
)
8953 Compose
*compose
= (Compose
*)data
;
8954 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
8956 GtkComboBox
*optmenu
;
8957 GtkTreeSelection
*selection
;
8959 GtkTreeModel
*model
;
8962 static gboolean cancelled
;
8964 /* only if one selected */
8965 selection
= gtk_tree_view_get_selection(tree_view
);
8966 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
8969 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
8973 path
= (GtkTreePath
*) sel
->data
;
8974 gtk_tree_model_get_iter(model
, &iter
, path
);
8975 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
8978 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
8984 if (!attach_prop
.window
)
8985 compose_attach_property_create(&cancelled
);
8986 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
8987 gtk_widget_grab_focus(attach_prop
.ok_btn
);
8988 gtk_widget_show(attach_prop
.window
);
8989 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
8990 GTK_WINDOW(compose
->window
));
8992 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
8993 if (ainfo
->encoding
== ENC_UNKNOWN
)
8994 combobox_select_by_data(optmenu
, ENC_BASE64
);
8996 combobox_select_by_data(optmenu
, ainfo
->encoding
);
8998 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
8999 ainfo
->content_type
? ainfo
->content_type
: "");
9000 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9001 ainfo
->file
? ainfo
->file
: "");
9002 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9003 ainfo
->name
? ainfo
->name
: "");
9006 const gchar
*entry_text
;
9008 gchar
*cnttype
= NULL
;
9015 gtk_widget_hide(attach_prop
.window
);
9016 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9021 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9022 if (*entry_text
!= '\0') {
9025 text
= g_strstrip(g_strdup(entry_text
));
9026 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9027 cnttype
= g_strdup(text
);
9030 alertpanel_error(_("Invalid MIME type."));
9036 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9038 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9039 if (*entry_text
!= '\0') {
9040 if (is_file_exist(entry_text
) &&
9041 (size
= get_file_size(entry_text
)) > 0)
9042 file
= g_strdup(entry_text
);
9045 (_("File doesn't exist or is empty."));
9051 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9052 if (*entry_text
!= '\0') {
9053 g_free(ainfo
->name
);
9054 ainfo
->name
= g_strdup(entry_text
);
9058 g_free(ainfo
->content_type
);
9059 ainfo
->content_type
= cnttype
;
9062 g_free(ainfo
->file
);
9066 ainfo
->size
= (goffset
)size
;
9068 /* update tree store */
9069 text
= to_human_readable(ainfo
->size
);
9070 gtk_tree_model_get_iter(model
, &iter
, path
);
9071 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9072 COL_MIMETYPE
, ainfo
->content_type
,
9074 COL_NAME
, ainfo
->name
,
9075 COL_CHARSET
, ainfo
->charset
,
9081 gtk_tree_path_free(path
);
9084 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9086 label = gtk_label_new(str); \
9087 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9088 GTK_FILL, 0, 0, 0); \
9089 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9091 entry = gtk_entry_new(); \
9092 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9093 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9096 static void compose_attach_property_create(gboolean
*cancelled
)
9102 GtkWidget
*mimetype_entry
;
9105 GtkListStore
*optmenu_menu
;
9106 GtkWidget
*path_entry
;
9107 GtkWidget
*filename_entry
;
9110 GtkWidget
*cancel_btn
;
9111 GList
*mime_type_list
, *strlist
;
9114 debug_print("Creating attach_property window...\n");
9116 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9117 gtk_widget_set_size_request(window
, 480, -1);
9118 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9119 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9120 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9121 g_signal_connect(G_OBJECT(window
), "delete_event",
9122 G_CALLBACK(attach_property_delete_event
),
9124 g_signal_connect(G_OBJECT(window
), "key_press_event",
9125 G_CALLBACK(attach_property_key_pressed
),
9128 vbox
= gtk_vbox_new(FALSE
, 8);
9129 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9131 table
= gtk_table_new(4, 2, FALSE
);
9132 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9133 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
9134 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
9136 label
= gtk_label_new(_("MIME type"));
9137 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
9139 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9140 #if !GTK_CHECK_VERSION(2, 24, 0)
9141 mimetype_entry
= gtk_combo_box_entry_new_text();
9143 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9145 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
9146 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9148 /* stuff with list */
9149 mime_type_list
= procmime_get_mime_type_list();
9151 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9152 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9155 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9157 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)strcmp2
))
9160 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9161 (GCompareFunc
)strcmp2
);
9164 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9165 mime_type_list
= mime_type_list
->next
) {
9166 #if !GTK_CHECK_VERSION(2, 24, 0)
9167 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry
), mime_type_list
->data
);
9169 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9171 g_free(mime_type_list
->data
);
9173 g_list_free(strlist
);
9174 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9175 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9177 label
= gtk_label_new(_("Encoding"));
9178 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
9180 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9182 hbox
= gtk_hbox_new(FALSE
, 0);
9183 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
9184 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9186 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9187 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9189 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9190 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9191 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9192 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9193 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9195 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9197 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9198 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9200 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
9201 &ok_btn
, GTK_STOCK_OK
,
9203 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9204 gtk_widget_grab_default(ok_btn
);
9206 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9207 G_CALLBACK(attach_property_ok
),
9209 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9210 G_CALLBACK(attach_property_cancel
),
9213 gtk_widget_show_all(vbox
);
9215 attach_prop
.window
= window
;
9216 attach_prop
.mimetype_entry
= mimetype_entry
;
9217 attach_prop
.encoding_optmenu
= optmenu
;
9218 attach_prop
.path_entry
= path_entry
;
9219 attach_prop
.filename_entry
= filename_entry
;
9220 attach_prop
.ok_btn
= ok_btn
;
9221 attach_prop
.cancel_btn
= cancel_btn
;
9224 #undef SET_LABEL_AND_ENTRY
9226 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9232 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9238 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9239 gboolean
*cancelled
)
9247 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9249 gboolean
*cancelled
)
9251 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9255 if (event
&& event
->keyval
== GDK_KEY_Return
) {
9263 static void compose_exec_ext_editor(Compose
*compose
)
9268 GdkNativeWindow socket_wid
= 0;
9272 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9273 G_DIR_SEPARATOR
, compose
);
9275 if (compose_get_ext_editor_uses_socket()) {
9276 /* Only allow one socket */
9277 if (compose
->exteditor_socket
!= NULL
) {
9278 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9279 /* Move the focus off of the socket */
9280 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9285 /* Create the receiving GtkSocket */
9286 socket
= gtk_socket_new ();
9287 g_signal_connect (GTK_OBJECT(socket
), "plug-removed",
9288 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9290 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9291 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9292 /* Realize the socket so that we can use its ID */
9293 gtk_widget_realize(socket
);
9294 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9295 compose
->exteditor_socket
= socket
;
9298 if (pipe(pipe_fds
) < 0) {
9304 if ((pid
= fork()) < 0) {
9311 /* close the write side of the pipe */
9314 compose
->exteditor_file
= g_strdup(tmp
);
9315 compose
->exteditor_pid
= pid
;
9317 compose_set_ext_editor_sensitive(compose
, FALSE
);
9320 compose
->exteditor_ch
= g_io_channel_unix_new(pipe_fds
[0]);
9322 compose
->exteditor_ch
= g_io_channel_win32_new_fd(pipe_fds
[0]);
9324 compose
->exteditor_tag
= g_io_add_watch(compose
->exteditor_ch
,
9328 } else { /* process-monitoring process */
9334 /* close the read side of the pipe */
9337 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9338 fd_write_all(pipe_fds
[1], "2\n", 2);
9342 pid_ed
= compose_exec_ext_editor_real(tmp
, socket_wid
);
9344 fd_write_all(pipe_fds
[1], "1\n", 2);
9348 /* wait until editor is terminated */
9349 waitpid(pid_ed
, NULL
, 0);
9351 fd_write_all(pipe_fds
[1], "0\n", 2);
9358 #endif /* G_OS_UNIX */
9362 static gboolean
compose_get_ext_editor_cmd_valid()
9364 gboolean has_s
= FALSE
;
9365 gboolean has_w
= FALSE
;
9366 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9369 while ((p
= strchr(p
, '%'))) {
9375 } else if (*p
== 'w') {
9386 static gint
compose_exec_ext_editor_real(const gchar
*file
, GdkNativeWindow socket_wid
)
9393 cm_return_val_if_fail(file
!= NULL
, -1);
9395 if ((pid
= fork()) < 0) {
9400 if (pid
!= 0) return pid
;
9402 /* grandchild process */
9404 if (setpgid(0, getppid()))
9407 if (compose_get_ext_editor_cmd_valid()) {
9408 if (compose_get_ext_editor_uses_socket()) {
9409 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9410 s
= strstr(p
, "%w");
9412 if (strstr(p
, "%s") < s
)
9413 g_snprintf(buf
, sizeof(buf
), p
, file
, socket_wid
);
9415 g_snprintf(buf
, sizeof(buf
), p
, socket_wid
, file
);
9418 g_snprintf(buf
, sizeof(buf
),
9419 prefs_common_get_ext_editor_cmd(), file
);
9422 if (prefs_common_get_ext_editor_cmd())
9423 g_warning("External editor command-line is invalid: '%s'",
9424 prefs_common_get_ext_editor_cmd());
9425 g_snprintf(buf
, sizeof(buf
), DEFAULT_EDITOR_CMD
, file
);
9428 cmdline
= strsplit_with_quote(buf
, " ", 1024);
9429 execvp(cmdline
[0], cmdline
);
9432 g_strfreev(cmdline
);
9437 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9439 pid_t pgid
= compose
->exteditor_pid
* -1;
9442 ret
= kill(pgid
, 0);
9444 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
9448 msg
= g_strdup_printf
9449 (_("The external editor is still working.\n"
9450 "Force terminating the process?\n"
9451 "process group id: %d"), -pgid
);
9452 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
, GTK_STOCK_YES
,
9453 NULL
, FALSE
, NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
9457 if (val
== G_ALERTALTERNATE
) {
9458 g_source_remove(compose
->exteditor_tag
);
9459 g_io_channel_shutdown(compose
->exteditor_ch
,
9461 g_io_channel_unref(compose
->exteditor_ch
);
9463 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
9464 waitpid(compose
->exteditor_pid
, NULL
, 0);
9466 g_warning("Terminated process group id: %d. "
9467 "Temporary file: %s", -pgid
, compose
->exteditor_file
);
9469 compose_set_ext_editor_sensitive(compose
, TRUE
);
9471 g_free(compose
->exteditor_file
);
9472 compose
->exteditor_file
= NULL
;
9473 compose
->exteditor_pid
= -1;
9474 compose
->exteditor_ch
= NULL
;
9475 compose
->exteditor_tag
= -1;
9483 static gboolean
compose_input_cb(GIOChannel
*source
, GIOCondition condition
,
9487 Compose
*compose
= (Compose
*)data
;
9490 debug_print("Compose: input from monitoring process\n");
9492 if (g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
) != G_IO_STATUS_NORMAL
) {
9497 g_io_channel_shutdown(source
, FALSE
, NULL
);
9498 g_io_channel_unref(source
);
9500 waitpid(compose
->exteditor_pid
, NULL
, 0);
9502 if (buf
[0] == '0') { /* success */
9503 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9504 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9505 GtkTextIter start
, end
;
9508 gtk_text_buffer_set_text(buffer
, "", -1);
9509 compose_insert_file(compose
, compose
->exteditor_file
);
9510 compose_changed_cb(NULL
, compose
);
9511 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9513 if (claws_unlink(compose
->exteditor_file
) < 0)
9514 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9516 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9517 gtk_text_buffer_get_start_iter(buffer
, &start
);
9518 gtk_text_buffer_get_end_iter(buffer
, &end
);
9519 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9520 if (chars
&& strlen(chars
) > 0)
9521 compose
->modified
= TRUE
;
9523 } else if (buf
[0] == '1') { /* failed */
9524 g_warning("Couldn't exec external editor");
9525 if (claws_unlink(compose
->exteditor_file
) < 0)
9526 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9527 } else if (buf
[0] == '2') {
9528 g_warning("Couldn't write to file");
9529 } else if (buf
[0] == '3') {
9530 g_warning("Pipe read failed");
9533 compose_set_ext_editor_sensitive(compose
, TRUE
);
9535 g_free(compose
->exteditor_file
);
9536 compose
->exteditor_file
= NULL
;
9537 compose
->exteditor_pid
= -1;
9538 compose
->exteditor_ch
= NULL
;
9539 compose
->exteditor_tag
= -1;
9540 if (compose
->exteditor_socket
) {
9541 gtk_widget_destroy(compose
->exteditor_socket
);
9542 compose
->exteditor_socket
= NULL
;
9549 static char *ext_editor_menu_entries
[] = {
9550 "Menu/Message/Send",
9551 "Menu/Message/SendLater",
9552 "Menu/Message/InsertFile",
9553 "Menu/Message/InsertSig",
9554 "Menu/Message/ReplaceSig",
9555 "Menu/Message/Save",
9556 "Menu/Message/Print",
9561 "Menu/Tools/ShowRuler",
9562 "Menu/Tools/Actions",
9567 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9572 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9573 cm_menu_set_sensitive_full(compose
->ui_manager
,
9574 ext_editor_menu_entries
[i
], sensitive
);
9577 if (compose_get_ext_editor_uses_socket()) {
9579 if (compose
->exteditor_socket
)
9580 gtk_widget_hide(compose
->exteditor_socket
);
9581 gtk_widget_show(compose
->scrolledwin
);
9582 if (prefs_common
.show_ruler
)
9583 gtk_widget_show(compose
->ruler_hbox
);
9584 /* Fix the focus, as it doesn't go anywhere when the
9585 * socket is hidden or destroyed */
9586 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9588 g_assert (compose
->exteditor_socket
!= NULL
);
9589 /* Fix the focus, as it doesn't go anywhere when the
9590 * edit box is hidden */
9591 if (gtk_widget_is_focus(compose
->text
))
9592 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9593 gtk_widget_hide(compose
->scrolledwin
);
9594 gtk_widget_hide(compose
->ruler_hbox
);
9595 gtk_widget_show(compose
->exteditor_socket
);
9598 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9600 if (compose
->toolbar
->send_btn
)
9601 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9602 if (compose
->toolbar
->sendl_btn
)
9603 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9604 if (compose
->toolbar
->draft_btn
)
9605 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9606 if (compose
->toolbar
->insert_btn
)
9607 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9608 if (compose
->toolbar
->sig_btn
)
9609 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9610 if (compose
->toolbar
->exteditor_btn
)
9611 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9612 if (compose
->toolbar
->linewrap_current_btn
)
9613 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9614 if (compose
->toolbar
->linewrap_all_btn
)
9615 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9618 static gboolean
compose_get_ext_editor_uses_socket()
9620 return (prefs_common_get_ext_editor_cmd() &&
9621 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9624 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9626 compose
->exteditor_socket
= NULL
;
9627 /* returning FALSE allows destruction of the socket */
9630 #endif /* G_OS_UNIX */
9633 * compose_undo_state_changed:
9635 * Change the sensivity of the menuentries undo and redo
9637 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9638 gint redo_state
, gpointer data
)
9640 Compose
*compose
= (Compose
*)data
;
9642 switch (undo_state
) {
9643 case UNDO_STATE_TRUE
:
9644 if (!undostruct
->undo_state
) {
9645 undostruct
->undo_state
= TRUE
;
9646 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9649 case UNDO_STATE_FALSE
:
9650 if (undostruct
->undo_state
) {
9651 undostruct
->undo_state
= FALSE
;
9652 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9655 case UNDO_STATE_UNCHANGED
:
9657 case UNDO_STATE_REFRESH
:
9658 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9661 g_warning("Undo state not recognized");
9665 switch (redo_state
) {
9666 case UNDO_STATE_TRUE
:
9667 if (!undostruct
->redo_state
) {
9668 undostruct
->redo_state
= TRUE
;
9669 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9672 case UNDO_STATE_FALSE
:
9673 if (undostruct
->redo_state
) {
9674 undostruct
->redo_state
= FALSE
;
9675 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
9678 case UNDO_STATE_UNCHANGED
:
9680 case UNDO_STATE_REFRESH
:
9681 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
9684 g_warning("Redo state not recognized");
9689 /* callback functions */
9691 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
9692 GtkAllocation
*allocation
,
9695 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
9698 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9699 * includes "non-client" (windows-izm) in calculation, so this calculation
9700 * may not be accurate.
9702 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
9703 GtkAllocation
*allocation
,
9704 GtkSHRuler
*shruler
)
9706 if (prefs_common
.show_ruler
) {
9707 gint char_width
= 0, char_height
= 0;
9708 gint line_width_in_chars
;
9710 gtkut_get_font_size(GTK_WIDGET(widget
),
9711 &char_width
, &char_height
);
9712 line_width_in_chars
=
9713 (allocation
->width
- allocation
->x
) / char_width
;
9715 /* got the maximum */
9716 gtk_shruler_set_range(GTK_SHRULER(shruler
),
9717 0.0, line_width_in_chars
, 0);
9726 ComposePrefType type
;
9727 gboolean entry_marked
;
9730 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
9732 Compose
*compose
= (Compose
*)data
;
9735 gchar
*folderidentifier
;
9736 gint account_id
= 0;
9739 GSList
*list
, *saved_list
= NULL
;
9740 HeaderEntryState
*state
;
9741 GtkRcStyle
*style
= NULL
;
9742 #if !GTK_CHECK_VERSION(3, 0, 0)
9743 static GdkColor yellow
;
9744 static gboolean color_set
= FALSE
;
9746 static GdkColor yellow
= { (guint32
)0, (guint32
)0xf5, (guint32
)0xf6, (guint32
)0xbe };
9749 /* Get ID of active account in the combo box */
9750 menu
= gtk_combo_box_get_model(optmenu
);
9751 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
9752 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
9754 ac
= account_find_from_id(account_id
);
9755 cm_return_if_fail(ac
!= NULL
);
9757 if (ac
!= compose
->account
) {
9758 compose_select_account(compose
, ac
, FALSE
);
9760 for (list
= compose
->header_list
; list
; list
= list
->next
) {
9761 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
9763 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
9764 compose_destroy_headerentry(compose
, hentry
);
9768 state
= g_malloc0(sizeof(HeaderEntryState
));
9769 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
9770 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
9771 state
->entry
= gtk_editable_get_chars(
9772 GTK_EDITABLE(hentry
->entry
), 0, -1);
9773 state
->type
= hentry
->type
;
9775 #if !GTK_CHECK_VERSION(3, 0, 0)
9777 gdk_color_parse("#f5f6be", &yellow
);
9778 color_set
= gdk_colormap_alloc_color(
9779 gdk_colormap_get_system(),
9780 &yellow
, FALSE
, TRUE
);
9784 style
= gtk_widget_get_modifier_style(hentry
->entry
);
9785 state
->entry_marked
= gdk_color_equal(&yellow
,
9786 &style
->base
[GTK_STATE_NORMAL
]);
9788 saved_list
= g_slist_append(saved_list
, state
);
9789 compose_destroy_headerentry(compose
, hentry
);
9792 compose
->header_last
= NULL
;
9793 g_slist_free(compose
->header_list
);
9794 compose
->header_list
= NULL
;
9795 compose
->header_nextrow
= 1;
9796 compose_create_header_entry(compose
);
9798 if (ac
->set_autocc
&& ac
->auto_cc
)
9799 compose_entry_append(compose
, ac
->auto_cc
,
9800 COMPOSE_CC
, PREF_ACCOUNT
);
9802 if (ac
->set_autobcc
&& ac
->auto_bcc
)
9803 compose_entry_append(compose
, ac
->auto_bcc
,
9804 COMPOSE_BCC
, PREF_ACCOUNT
);
9806 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
9807 compose_entry_append(compose
, ac
->auto_replyto
,
9808 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
9810 for (list
= saved_list
; list
; list
= list
->next
) {
9811 state
= (HeaderEntryState
*) list
->data
;
9813 compose_add_header_entry(compose
, state
->header
,
9814 state
->entry
, state
->type
);
9815 if (state
->entry_marked
)
9816 compose_entry_mark_default_to(compose
, state
->entry
);
9818 g_free(state
->header
);
9819 g_free(state
->entry
);
9822 g_slist_free(saved_list
);
9824 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
9825 (ac
->protocol
== A_NNTP
) ?
9826 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
9829 /* Set message save folder */
9830 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
9831 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
9833 g_signal_connect(G_OBJECT(compose
->savemsg_checkbtn
), "toggled",
9834 G_CALLBACK(compose_savemsg_checkbtn_cb
), compose
);
9836 compose_set_save_to(compose
, NULL
);
9837 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
9838 folderidentifier
= folder_item_get_identifier(account_get_special_folder
9839 (compose
->account
, F_OUTBOX
));
9840 compose_set_save_to(compose
, folderidentifier
);
9841 g_free(folderidentifier
);
9845 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
9846 GtkTreeViewColumn
*column
, Compose
*compose
)
9848 compose_attach_property(NULL
, compose
);
9851 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
9854 Compose
*compose
= (Compose
*)data
;
9855 GtkTreeSelection
*attach_selection
;
9856 gint attach_nr_selected
;
9859 if (!event
) return FALSE
;
9861 if (event
->button
== 3) {
9862 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
9863 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
9865 /* If no rows, or just one row is selected, right-click should
9866 * open menu relevant to the row being right-clicked on. We
9867 * achieve that by selecting the clicked row first. If more
9868 * than one row is selected, we shouldn't modify the selection,
9869 * as user may want to remove selected rows (attachments). */
9870 if (attach_nr_selected
< 2) {
9871 gtk_tree_selection_unselect_all(attach_selection
);
9872 attach_nr_selected
= 0;
9873 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
9874 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
9876 gtk_tree_selection_select_path(attach_selection
, path
);
9877 gtk_tree_path_free(path
);
9878 attach_nr_selected
++;
9882 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
9883 /* Properties menu item makes no sense with more than one row
9884 * selected, the properties dialog can only edit one attachment. */
9885 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
9887 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
9888 NULL
, NULL
, event
->button
, event
->time
);
9895 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
9898 Compose
*compose
= (Compose
*)data
;
9900 if (!event
) return FALSE
;
9902 switch (event
->keyval
) {
9903 case GDK_KEY_Delete
:
9904 compose_attach_remove_selected(NULL
, compose
);
9910 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
9912 toolbar_comp_set_sensitive(compose
, allow
);
9913 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
9914 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
9916 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
9918 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
9919 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
9920 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
9922 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
9926 static void compose_send_cb(GtkAction
*action
, gpointer data
)
9928 Compose
*compose
= (Compose
*)data
;
9931 if (compose
->exteditor_tag
!= -1) {
9932 debug_print("ignoring send: external editor still open\n");
9936 if (prefs_common
.work_offline
&&
9937 !inc_offline_should_override(TRUE
,
9938 _("Claws Mail needs network access in order "
9939 "to send this email.")))
9942 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
9943 g_source_remove(compose
->draft_timeout_tag
);
9944 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
9947 compose_send(compose
);
9950 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
9952 Compose
*compose
= (Compose
*)data
;
9956 compose_allow_user_actions(compose
, FALSE
);
9957 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
9958 compose_allow_user_actions(compose
, TRUE
);
9962 compose_close(compose
);
9963 } else if (val
== -1) {
9964 alertpanel_error(_("Could not queue message."));
9965 } else if (val
== -2) {
9966 alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno
));
9967 } else if (val
== -3) {
9968 if (privacy_peek_error())
9969 alertpanel_error(_("Could not queue message for sending:\n\n"
9970 "Signature failed: %s"), privacy_get_error());
9971 } else if (val
== -4) {
9972 alertpanel_error(_("Could not queue message for sending:\n\n"
9973 "Charset conversion failed."));
9974 } else if (val
== -5) {
9975 alertpanel_error(_("Could not queue message for sending:\n\n"
9976 "Couldn't get recipient encryption key."));
9977 } else if (val
== -6) {
9980 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9983 #define DRAFTED_AT_EXIT "drafted_at_exit"
9984 static void compose_register_draft(MsgInfo
*info
)
9986 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9987 DRAFTED_AT_EXIT
, NULL
);
9988 FILE *fp
= g_fopen(filepath
, "ab");
9991 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
9999 gboolean
compose_draft (gpointer data
, guint action
)
10001 Compose
*compose
= (Compose
*)data
;
10006 MsgFlags flag
= {0, 0};
10007 static gboolean lock
= FALSE
;
10008 MsgInfo
*newmsginfo
;
10010 gboolean target_locked
= FALSE
;
10011 gboolean err
= FALSE
;
10013 if (lock
) return FALSE
;
10015 if (compose
->sending
)
10018 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10019 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10021 if (!g_mutex_trylock(compose
->mutex
)) {
10022 /* we don't want to lock the mutex once it's available,
10023 * because as the only other part of compose.c locking
10024 * it is compose_close - which means once unlocked,
10025 * the compose struct will be freed */
10026 debug_print("couldn't lock mutex, probably sending\n");
10032 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10033 G_DIR_SEPARATOR
, compose
);
10034 if ((fp
= g_fopen(tmp
, "wb")) == NULL
) {
10035 FILE_OP_ERROR(tmp
, "fopen");
10039 /* chmod for security */
10040 if (change_file_mode_rw(fp
, tmp
) < 0) {
10041 FILE_OP_ERROR(tmp
, "chmod");
10042 g_warning("can't change file mode");
10045 /* Save draft infos */
10046 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10047 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10049 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10050 gchar
*savefolderid
;
10052 savefolderid
= compose_get_save_to(compose
);
10053 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10054 g_free(savefolderid
);
10056 if (compose
->return_receipt
) {
10057 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10059 if (compose
->privacy_system
) {
10060 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10061 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10062 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10065 /* Message-ID of message replying to */
10066 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10067 gchar
*folderid
= NULL
;
10069 if (compose
->replyinfo
->folder
)
10070 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10071 if (folderid
== NULL
)
10072 folderid
= g_strdup("NULL");
10074 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10077 /* Message-ID of message forwarding to */
10078 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10079 gchar
*folderid
= NULL
;
10081 if (compose
->fwdinfo
->folder
)
10082 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10083 if (folderid
== NULL
)
10084 folderid
= g_strdup("NULL");
10086 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10090 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10091 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10093 sheaders
= compose_get_manual_headers_info(compose
);
10094 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10097 /* end of headers */
10098 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10105 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10109 if (fclose(fp
) == EOF
) {
10113 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10114 if (compose
->targetinfo
) {
10115 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10117 flag
.perm_flags
|= MSG_LOCKED
;
10119 flag
.tmp_flags
= MSG_DRAFT
;
10121 folder_item_scan(draft
);
10122 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10123 MsgInfo
*tmpinfo
= NULL
;
10124 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10125 if (compose
->msgid
) {
10126 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10129 msgnum
= tmpinfo
->msgnum
;
10130 procmsg_msginfo_free(&tmpinfo
);
10131 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10133 debug_print("didn't get draft msgnum after scanning\n");
10136 debug_print("got draft msgnum %d from adding\n", msgnum
);
10142 if (action
!= COMPOSE_AUTO_SAVE
) {
10143 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10144 alertpanel_error(_("Could not save draft."));
10147 gtkut_window_popup(compose
->window
);
10148 val
= alertpanel_full(_("Could not save draft"),
10149 _("Could not save draft.\n"
10150 "Do you want to cancel exit or discard this email?"),
10151 _("_Cancel exit"), _("_Discard email"), NULL
,
10152 FALSE
, NULL
, ALERT_QUESTION
, G_ALERTDEFAULT
);
10153 if (val
== G_ALERTALTERNATE
) {
10155 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10156 compose_close(compose
);
10160 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10169 if (compose
->mode
== COMPOSE_REEDIT
) {
10170 compose_remove_reedit_target(compose
, TRUE
);
10173 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10176 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10178 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10180 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10181 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10182 procmsg_msginfo_set_flags(newmsginfo
, 0,
10183 MSG_HAS_ATTACHMENT
);
10185 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10186 compose_register_draft(newmsginfo
);
10188 procmsg_msginfo_free(&newmsginfo
);
10191 folder_item_scan(draft
);
10193 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10195 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10196 compose_close(compose
);
10202 path
= folder_item_fetch_msg(draft
, msgnum
);
10203 if (path
== NULL
) {
10204 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10207 if (g_stat(path
, &s
) < 0) {
10208 FILE_OP_ERROR(path
, "stat");
10214 procmsg_msginfo_free(&(compose
->targetinfo
));
10215 compose
->targetinfo
= procmsg_msginfo_new();
10216 compose
->targetinfo
->msgnum
= msgnum
;
10217 compose
->targetinfo
->size
= (goffset
)s
.st_size
;
10218 compose
->targetinfo
->mtime
= s
.st_mtime
;
10219 compose
->targetinfo
->folder
= draft
;
10221 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10222 compose
->mode
= COMPOSE_REEDIT
;
10224 if (action
== COMPOSE_AUTO_SAVE
) {
10225 compose
->autosaved_draft
= compose
->targetinfo
;
10227 compose
->modified
= FALSE
;
10228 compose_set_title(compose
);
10232 g_mutex_unlock(compose
->mutex
);
10236 void compose_clear_exit_drafts(void)
10238 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10239 DRAFTED_AT_EXIT
, NULL
);
10240 if (is_file_exist(filepath
))
10241 claws_unlink(filepath
);
10246 void compose_reopen_exit_drafts(void)
10248 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10249 DRAFTED_AT_EXIT
, NULL
);
10250 FILE *fp
= g_fopen(filepath
, "rb");
10254 while (fgets(buf
, sizeof(buf
), fp
)) {
10255 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10256 const gchar
*folder
= parts
[0];
10257 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10259 if (folder
&& *folder
&& msgnum
> -1) {
10260 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10261 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10263 compose_reedit(info
, FALSE
);
10270 compose_clear_exit_drafts();
10273 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10275 Compose
*compose
= (Compose
*)data
;
10276 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10277 compose
->rmode
= COMPOSE_REEDIT
;
10280 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10282 if (compose
&& file_list
) {
10285 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10286 gchar
*file
= (gchar
*) tmp
->data
;
10287 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10288 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10289 compose_changed_cb(NULL
, compose
);
10294 g_free(utf8_filename
);
10299 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10301 Compose
*compose
= (Compose
*)data
;
10304 if (compose
->redirect_filename
!= NULL
)
10307 /* Set focus_window properly, in case we were called via popup menu,
10308 * which unsets it (via focus_out_event callback on compose window). */
10309 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10311 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10314 compose_attach_from_list(compose
, file_list
, TRUE
);
10315 g_list_free(file_list
);
10319 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10321 Compose
*compose
= (Compose
*)data
;
10323 gint files_inserted
= 0;
10325 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10330 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10331 gchar
*file
= (gchar
*) tmp
->data
;
10332 gchar
*filedup
= g_strdup(file
);
10333 gchar
*shortfile
= g_path_get_basename(filedup
);
10334 ComposeInsertResult res
;
10335 /* insert the file if the file is short or if the user confirmed that
10336 he/she wants to insert the large file */
10337 res
= compose_insert_file(compose
, file
);
10338 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10339 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10340 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10341 alertpanel_error(_("File '%s' contained invalid characters\n"
10342 "for the current encoding, insertion may be incorrect."),
10344 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10351 g_list_free(file_list
);
10355 if (files_inserted
> 0 && compose
->gtkaspell
&&
10356 compose
->gtkaspell
->check_while_typing
)
10357 gtkaspell_highlight_all(compose
->gtkaspell
);
10361 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10363 Compose
*compose
= (Compose
*)data
;
10365 compose_insert_sig(compose
, FALSE
);
10368 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10370 Compose
*compose
= (Compose
*)data
;
10372 compose_insert_sig(compose
, TRUE
);
10375 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10379 Compose
*compose
= (Compose
*)data
;
10381 gtkut_widget_get_uposition(widget
, &x
, &y
);
10382 if (!compose
->batch
) {
10383 prefs_common
.compose_x
= x
;
10384 prefs_common
.compose_y
= y
;
10386 if (compose
->sending
|| compose
->updating
)
10388 compose_close_cb(NULL
, compose
);
10392 void compose_close_toolbar(Compose
*compose
)
10394 compose_close_cb(NULL
, compose
);
10397 static gboolean
compose_can_autosave(Compose
*compose
)
10399 if (compose
->privacy_system
&& compose
->use_encryption
)
10400 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
10402 return prefs_common
.autosave
;
10405 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10407 Compose
*compose
= (Compose
*)data
;
10411 if (compose
->exteditor_tag
!= -1) {
10412 if (!compose_ext_editor_kill(compose
))
10417 if (compose
->modified
) {
10418 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10419 if (!g_mutex_trylock(compose
->mutex
)) {
10420 /* we don't want to lock the mutex once it's available,
10421 * because as the only other part of compose.c locking
10422 * it is compose_close - which means once unlocked,
10423 * the compose struct will be freed */
10424 debug_print("couldn't lock mutex, probably sending\n");
10428 val
= alertpanel(_("Discard message"),
10429 _("This message has been modified. Discard it?"),
10430 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
);
10432 val
= alertpanel(_("Save changes"),
10433 _("This message has been modified. Save the latest changes?"),
10434 _("_Don't save"), g_strconcat("+", _("_Save to Drafts"), NULL
),
10437 g_mutex_unlock(compose
->mutex
);
10439 case G_ALERTDEFAULT
:
10440 if (compose_can_autosave(compose
) && !reedit
)
10441 compose_remove_draft(compose
);
10443 case G_ALERTALTERNATE
:
10444 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10451 compose_close(compose
);
10454 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10456 Compose
*compose
= (Compose
*) data
;
10458 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10459 if (compose
->targetinfo
)
10460 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10463 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10465 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10466 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10467 Compose
*compose
= (Compose
*) data
;
10470 compose
->out_encoding
= (CharSet
)value
;
10473 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10475 Compose
*compose
= (Compose
*)data
;
10477 #ifndef USE_ALT_ADDRBOOK
10478 addressbook_open(compose
);
10480 GError
* error
= NULL
;
10481 addressbook_connect_signals(compose
);
10482 addressbook_dbus_open(TRUE
, &error
);
10484 g_warning("%s", error
->message
);
10485 g_error_free(error
);
10490 static void about_show_cb(GtkAction
*action
, gpointer data
)
10495 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10497 Compose
*compose
= (Compose
*)data
;
10502 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10503 cm_return_if_fail(tmpl
!= NULL
);
10505 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10507 val
= alertpanel(_("Apply template"), msg
,
10508 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
);
10511 if (val
== G_ALERTDEFAULT
)
10512 compose_template_apply(compose
, tmpl
, TRUE
);
10513 else if (val
== G_ALERTALTERNATE
)
10514 compose_template_apply(compose
, tmpl
, FALSE
);
10517 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10519 Compose
*compose
= (Compose
*)data
;
10522 if (compose
->exteditor_tag
!= -1) {
10523 debug_print("ignoring open external editor: external editor still open\n");
10527 compose_exec_ext_editor(compose
);
10530 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10532 Compose
*compose
= (Compose
*)data
;
10533 gboolean prev_autowrap
= compose
->autowrap
;
10535 compose
->autowrap
= FALSE
;
10536 undo_undo(compose
->undostruct
);
10537 compose
->autowrap
= prev_autowrap
;
10540 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10542 Compose
*compose
= (Compose
*)data
;
10543 gboolean prev_autowrap
= compose
->autowrap
;
10545 compose
->autowrap
= FALSE
;
10546 undo_redo(compose
->undostruct
);
10547 compose
->autowrap
= prev_autowrap
;
10550 static void entry_cut_clipboard(GtkWidget
*entry
)
10552 if (GTK_IS_EDITABLE(entry
))
10553 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10554 else if (GTK_IS_TEXT_VIEW(entry
))
10555 gtk_text_buffer_cut_clipboard(
10556 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10557 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10561 static void entry_copy_clipboard(GtkWidget
*entry
)
10563 if (GTK_IS_EDITABLE(entry
))
10564 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10565 else if (GTK_IS_TEXT_VIEW(entry
))
10566 gtk_text_buffer_copy_clipboard(
10567 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10568 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10571 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10572 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10574 if (GTK_IS_TEXT_VIEW(entry
)) {
10575 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10576 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10577 GtkTextIter start_iter
, end_iter
;
10579 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10581 if (contents
== NULL
)
10584 /* we shouldn't delete the selection when middle-click-pasting, or we
10585 * can't mid-click-paste our own selection */
10586 if (clip
!= GDK_SELECTION_PRIMARY
) {
10587 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10588 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10591 if (insert_place
== NULL
) {
10592 /* if insert_place isn't specified, insert at the cursor.
10593 * used for Ctrl-V pasting */
10594 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10595 start
= gtk_text_iter_get_offset(&start_iter
);
10596 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10598 /* if insert_place is specified, paste here.
10599 * used for mid-click-pasting */
10600 start
= gtk_text_iter_get_offset(insert_place
);
10601 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10602 if (prefs_common
.primary_paste_unselects
)
10603 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10607 /* paste unwrapped: mark the paste so it's not wrapped later */
10608 end
= start
+ strlen(contents
);
10609 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10610 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10611 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10612 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10613 /* rewrap paragraph now (after a mid-click-paste) */
10614 mark_start
= gtk_text_buffer_get_insert(buffer
);
10615 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10616 gtk_text_iter_backward_char(&start_iter
);
10617 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10619 } else if (GTK_IS_EDITABLE(entry
))
10620 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10622 compose
->modified
= TRUE
;
10625 static void entry_allsel(GtkWidget
*entry
)
10627 if (GTK_IS_EDITABLE(entry
))
10628 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10629 else if (GTK_IS_TEXT_VIEW(entry
)) {
10630 GtkTextIter startiter
, enditer
;
10631 GtkTextBuffer
*textbuf
;
10633 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10634 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10635 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10637 gtk_text_buffer_move_mark_by_name(textbuf
,
10638 "selection_bound", &startiter
);
10639 gtk_text_buffer_move_mark_by_name(textbuf
,
10640 "insert", &enditer
);
10644 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10646 Compose
*compose
= (Compose
*)data
;
10647 if (compose
->focused_editable
10648 #ifndef GENERIC_UMPC
10649 && gtk_widget_has_focus(compose
->focused_editable
)
10652 entry_cut_clipboard(compose
->focused_editable
);
10655 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10657 Compose
*compose
= (Compose
*)data
;
10658 if (compose
->focused_editable
10659 #ifndef GENERIC_UMPC
10660 && gtk_widget_has_focus(compose
->focused_editable
)
10663 entry_copy_clipboard(compose
->focused_editable
);
10666 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
10668 Compose
*compose
= (Compose
*)data
;
10669 gint prev_autowrap
;
10670 GtkTextBuffer
*buffer
;
10672 if (compose
->focused_editable
&&
10673 #ifndef GENERIC_UMPC
10674 gtk_widget_has_focus(compose
->focused_editable
)
10677 entry_paste_clipboard(compose
, compose
->focused_editable
,
10678 prefs_common
.linewrap_pastes
,
10679 GDK_SELECTION_CLIPBOARD
, NULL
);
10684 #ifndef GENERIC_UMPC
10685 gtk_widget_has_focus(compose
->text
) &&
10687 compose
->gtkaspell
&&
10688 compose
->gtkaspell
->check_while_typing
)
10689 gtkaspell_highlight_all(compose
->gtkaspell
);
10693 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
10695 Compose
*compose
= (Compose
*)data
;
10696 gint wrap_quote
= prefs_common
.linewrap_quote
;
10697 if (compose
->focused_editable
10698 #ifndef GENERIC_UMPC
10699 && gtk_widget_has_focus(compose
->focused_editable
)
10702 /* let text_insert() (called directly or at a later time
10703 * after the gtk_editable_paste_clipboard) know that
10704 * text is to be inserted as a quotation. implemented
10705 * by using a simple refcount... */
10706 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
10707 G_OBJECT(compose
->focused_editable
),
10708 "paste_as_quotation"));
10709 g_object_set_data(G_OBJECT(compose
->focused_editable
),
10710 "paste_as_quotation",
10711 GINT_TO_POINTER(paste_as_quotation
+ 1));
10712 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
10713 entry_paste_clipboard(compose
, compose
->focused_editable
,
10714 prefs_common
.linewrap_pastes
,
10715 GDK_SELECTION_CLIPBOARD
, NULL
);
10716 prefs_common
.linewrap_quote
= wrap_quote
;
10720 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
10722 Compose
*compose
= (Compose
*)data
;
10723 gint prev_autowrap
;
10724 GtkTextBuffer
*buffer
;
10726 if (compose
->focused_editable
10727 #ifndef GENERIC_UMPC
10728 && gtk_widget_has_focus(compose
->focused_editable
)
10731 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
10732 GDK_SELECTION_CLIPBOARD
, NULL
);
10737 #ifndef GENERIC_UMPC
10738 gtk_widget_has_focus(compose
->text
) &&
10740 compose
->gtkaspell
&&
10741 compose
->gtkaspell
->check_while_typing
)
10742 gtkaspell_highlight_all(compose
->gtkaspell
);
10746 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
10748 Compose
*compose
= (Compose
*)data
;
10749 gint prev_autowrap
;
10750 GtkTextBuffer
*buffer
;
10752 if (compose
->focused_editable
10753 #ifndef GENERIC_UMPC
10754 && gtk_widget_has_focus(compose
->focused_editable
)
10757 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
10758 GDK_SELECTION_CLIPBOARD
, NULL
);
10763 #ifndef GENERIC_UMPC
10764 gtk_widget_has_focus(compose
->text
) &&
10766 compose
->gtkaspell
&&
10767 compose
->gtkaspell
->check_while_typing
)
10768 gtkaspell_highlight_all(compose
->gtkaspell
);
10772 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
10774 Compose
*compose
= (Compose
*)data
;
10775 if (compose
->focused_editable
10776 #ifndef GENERIC_UMPC
10777 && gtk_widget_has_focus(compose
->focused_editable
)
10780 entry_allsel(compose
->focused_editable
);
10783 static void textview_move_beginning_of_line (GtkTextView
*text
)
10785 GtkTextBuffer
*buffer
;
10789 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10791 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10792 mark
= gtk_text_buffer_get_insert(buffer
);
10793 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10794 gtk_text_iter_set_line_offset(&ins
, 0);
10795 gtk_text_buffer_place_cursor(buffer
, &ins
);
10798 static void textview_move_forward_character (GtkTextView
*text
)
10800 GtkTextBuffer
*buffer
;
10804 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10806 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10807 mark
= gtk_text_buffer_get_insert(buffer
);
10808 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10809 if (gtk_text_iter_forward_cursor_position(&ins
))
10810 gtk_text_buffer_place_cursor(buffer
, &ins
);
10813 static void textview_move_backward_character (GtkTextView
*text
)
10815 GtkTextBuffer
*buffer
;
10819 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10821 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10822 mark
= gtk_text_buffer_get_insert(buffer
);
10823 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10824 if (gtk_text_iter_backward_cursor_position(&ins
))
10825 gtk_text_buffer_place_cursor(buffer
, &ins
);
10828 static void textview_move_forward_word (GtkTextView
*text
)
10830 GtkTextBuffer
*buffer
;
10835 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10837 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10838 mark
= gtk_text_buffer_get_insert(buffer
);
10839 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10840 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
10841 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
10842 gtk_text_iter_backward_word_start(&ins
);
10843 gtk_text_buffer_place_cursor(buffer
, &ins
);
10847 static void textview_move_backward_word (GtkTextView
*text
)
10849 GtkTextBuffer
*buffer
;
10853 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10855 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10856 mark
= gtk_text_buffer_get_insert(buffer
);
10857 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10858 if (gtk_text_iter_backward_word_starts(&ins
, 1))
10859 gtk_text_buffer_place_cursor(buffer
, &ins
);
10862 static void textview_move_end_of_line (GtkTextView
*text
)
10864 GtkTextBuffer
*buffer
;
10868 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10870 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10871 mark
= gtk_text_buffer_get_insert(buffer
);
10872 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10873 if (gtk_text_iter_forward_to_line_end(&ins
))
10874 gtk_text_buffer_place_cursor(buffer
, &ins
);
10877 static void textview_move_next_line (GtkTextView
*text
)
10879 GtkTextBuffer
*buffer
;
10884 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10886 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10887 mark
= gtk_text_buffer_get_insert(buffer
);
10888 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10889 offset
= gtk_text_iter_get_line_offset(&ins
);
10890 if (gtk_text_iter_forward_line(&ins
)) {
10891 gtk_text_iter_set_line_offset(&ins
, offset
);
10892 gtk_text_buffer_place_cursor(buffer
, &ins
);
10896 static void textview_move_previous_line (GtkTextView
*text
)
10898 GtkTextBuffer
*buffer
;
10903 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10905 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10906 mark
= gtk_text_buffer_get_insert(buffer
);
10907 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10908 offset
= gtk_text_iter_get_line_offset(&ins
);
10909 if (gtk_text_iter_backward_line(&ins
)) {
10910 gtk_text_iter_set_line_offset(&ins
, offset
);
10911 gtk_text_buffer_place_cursor(buffer
, &ins
);
10915 static void textview_delete_forward_character (GtkTextView
*text
)
10917 GtkTextBuffer
*buffer
;
10919 GtkTextIter ins
, end_iter
;
10921 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10923 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10924 mark
= gtk_text_buffer_get_insert(buffer
);
10925 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10927 if (gtk_text_iter_forward_char(&end_iter
)) {
10928 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10932 static void textview_delete_backward_character (GtkTextView
*text
)
10934 GtkTextBuffer
*buffer
;
10936 GtkTextIter ins
, end_iter
;
10938 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10940 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10941 mark
= gtk_text_buffer_get_insert(buffer
);
10942 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10944 if (gtk_text_iter_backward_char(&end_iter
)) {
10945 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
10949 static void textview_delete_forward_word (GtkTextView
*text
)
10951 GtkTextBuffer
*buffer
;
10953 GtkTextIter ins
, end_iter
;
10955 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10957 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10958 mark
= gtk_text_buffer_get_insert(buffer
);
10959 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10961 if (gtk_text_iter_forward_word_end(&end_iter
)) {
10962 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10966 static void textview_delete_backward_word (GtkTextView
*text
)
10968 GtkTextBuffer
*buffer
;
10970 GtkTextIter ins
, end_iter
;
10972 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10974 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10975 mark
= gtk_text_buffer_get_insert(buffer
);
10976 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10978 if (gtk_text_iter_backward_word_start(&end_iter
)) {
10979 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
10983 static void textview_delete_line (GtkTextView
*text
)
10985 GtkTextBuffer
*buffer
;
10987 GtkTextIter ins
, start_iter
, end_iter
;
10989 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10991 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10992 mark
= gtk_text_buffer_get_insert(buffer
);
10993 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10996 gtk_text_iter_set_line_offset(&start_iter
, 0);
10999 if (gtk_text_iter_ends_line(&end_iter
)){
11000 if (!gtk_text_iter_forward_char(&end_iter
))
11001 gtk_text_iter_backward_char(&start_iter
);
11004 gtk_text_iter_forward_to_line_end(&end_iter
);
11005 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11008 static void textview_delete_to_line_end (GtkTextView
*text
)
11010 GtkTextBuffer
*buffer
;
11012 GtkTextIter ins
, end_iter
;
11014 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11016 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11017 mark
= gtk_text_buffer_get_insert(buffer
);
11018 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11020 if (gtk_text_iter_ends_line(&end_iter
))
11021 gtk_text_iter_forward_char(&end_iter
);
11023 gtk_text_iter_forward_to_line_end(&end_iter
);
11024 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11027 #define DO_ACTION(name, act) { \
11028 if(!strcmp(name, a_name)) { \
11032 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11034 const gchar
*a_name
= gtk_action_get_name(action
);
11035 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11036 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11037 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11038 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11039 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11040 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11041 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11042 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11043 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11044 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11045 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11046 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11047 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11048 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11052 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11054 Compose
*compose
= (Compose
*)data
;
11055 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11056 ComposeCallAdvancedAction action
= -1;
11058 action
= compose_call_advanced_action_from_path(gaction
);
11061 void (*do_action
) (GtkTextView
*text
);
11062 } action_table
[] = {
11063 {textview_move_beginning_of_line
},
11064 {textview_move_forward_character
},
11065 {textview_move_backward_character
},
11066 {textview_move_forward_word
},
11067 {textview_move_backward_word
},
11068 {textview_move_end_of_line
},
11069 {textview_move_next_line
},
11070 {textview_move_previous_line
},
11071 {textview_delete_forward_character
},
11072 {textview_delete_backward_character
},
11073 {textview_delete_forward_word
},
11074 {textview_delete_backward_word
},
11075 {textview_delete_line
},
11076 {textview_delete_to_line_end
}
11079 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11081 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11082 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11083 if (action_table
[action
].do_action
)
11084 action_table
[action
].do_action(text
);
11086 g_warning("Not implemented yet.");
11090 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11092 GtkAllocation allocation
;
11096 if (GTK_IS_EDITABLE(widget
)) {
11097 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11098 gtk_editable_set_position(GTK_EDITABLE(widget
),
11101 if ((parent
= gtk_widget_get_parent(widget
))
11102 && (parent
= gtk_widget_get_parent(parent
))
11103 && (parent
= gtk_widget_get_parent(parent
))) {
11104 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11105 gtk_widget_get_allocation(widget
, &allocation
);
11106 gint y
= allocation
.y
;
11107 gint height
= allocation
.height
;
11108 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11109 (GTK_SCROLLED_WINDOW(parent
));
11111 gfloat value
= gtk_adjustment_get_value(shown
);
11112 gfloat upper
= gtk_adjustment_get_upper(shown
);
11113 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11114 if (y
< (int)value
) {
11115 gtk_adjustment_set_value(shown
, y
- 1);
11117 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11118 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11119 gtk_adjustment_set_value(shown
,
11120 y
+ height
- (int)page_size
- 1);
11122 gtk_adjustment_set_value(shown
,
11123 (int)upper
- (int)page_size
- 1);
11130 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11131 compose
->focused_editable
= widget
;
11133 #ifdef GENERIC_UMPC
11134 if (GTK_IS_TEXT_VIEW(widget
)
11135 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11136 g_object_ref(compose
->notebook
);
11137 g_object_ref(compose
->edit_vbox
);
11138 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11139 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11140 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11141 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11142 g_object_unref(compose
->notebook
);
11143 g_object_unref(compose
->edit_vbox
);
11144 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11145 G_CALLBACK(compose_grab_focus_cb
),
11147 gtk_widget_grab_focus(widget
);
11148 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11149 G_CALLBACK(compose_grab_focus_cb
),
11151 } else if (!GTK_IS_TEXT_VIEW(widget
)
11152 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11153 g_object_ref(compose
->notebook
);
11154 g_object_ref(compose
->edit_vbox
);
11155 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11156 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11157 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11158 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11159 g_object_unref(compose
->notebook
);
11160 g_object_unref(compose
->edit_vbox
);
11161 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11162 G_CALLBACK(compose_grab_focus_cb
),
11164 gtk_widget_grab_focus(widget
);
11165 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11166 G_CALLBACK(compose_grab_focus_cb
),
11172 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11174 compose
->modified
= TRUE
;
11175 // compose_beautify_paragraph(compose, NULL, TRUE);
11176 #ifndef GENERIC_UMPC
11177 compose_set_title(compose
);
11181 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11183 Compose
*compose
= (Compose
*)data
;
11184 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11187 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11189 Compose
*compose
= (Compose
*)data
;
11190 compose_wrap_all_full(compose
, TRUE
);
11193 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11195 Compose
*compose
= (Compose
*)data
;
11197 message_search_compose(compose
);
11200 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11203 Compose
*compose
= (Compose
*)data
;
11204 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11205 if (compose
->autowrap
)
11206 compose_wrap_all_full(compose
, TRUE
);
11207 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11210 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11213 Compose
*compose
= (Compose
*)data
;
11214 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11217 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11219 Compose
*compose
= (Compose
*)data
;
11221 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11224 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11226 Compose
*compose
= (Compose
*)data
;
11228 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11231 static void activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11233 g_free(compose
->privacy_system
);
11234 g_free(compose
->encdata
);
11236 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11237 compose_update_privacy_system_menu_item(compose
, warn
);
11240 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11242 Compose
*compose
= (Compose
*)data
;
11244 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11245 gtk_widget_show(compose
->ruler_hbox
);
11246 prefs_common
.show_ruler
= TRUE
;
11248 gtk_widget_hide(compose
->ruler_hbox
);
11249 gtk_widget_queue_resize(compose
->edit_vbox
);
11250 prefs_common
.show_ruler
= FALSE
;
11254 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11255 GdkDragContext
*context
,
11258 GtkSelectionData
*data
,
11261 gpointer user_data
)
11263 Compose
*compose
= (Compose
*)user_data
;
11267 type
= gtk_selection_data_get_data_type(data
);
11268 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11269 && gtk_drag_get_source_widget(context
) !=
11270 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11271 list
= uri_list_extract_filenames(
11272 (const gchar
*)gtk_selection_data_get_data(data
));
11273 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11274 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11275 compose_attach_append
11276 (compose
, (const gchar
*)tmp
->data
,
11277 utf8_filename
, NULL
, NULL
);
11278 g_free(utf8_filename
);
11280 if (list
) compose_changed_cb(NULL
, compose
);
11281 list_free_strings(list
);
11283 } else if (gtk_drag_get_source_widget(context
)
11284 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11285 /* comes from our summaryview */
11286 SummaryView
* summaryview
= NULL
;
11287 GSList
* list
= NULL
, *cur
= NULL
;
11289 if (mainwindow_get_mainwindow())
11290 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11293 list
= summary_get_selected_msg_list(summaryview
);
11295 for (cur
= list
; cur
; cur
= cur
->next
) {
11296 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11297 gchar
*file
= NULL
;
11299 file
= procmsg_get_message_file_full(msginfo
,
11302 compose_attach_append(compose
, (const gchar
*)file
,
11303 (const gchar
*)file
, "message/rfc822", NULL
);
11307 g_slist_free(list
);
11311 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11312 GdkDragContext
*drag_context
,
11314 guint time
, gpointer user_data
)
11316 /* not handling this signal makes compose_insert_drag_received_cb
11321 static gboolean completion_set_focus_to_subject
11322 (GtkWidget
*widget
,
11323 GdkEventKey
*event
,
11326 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11328 /* make backtab move to subject field */
11329 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11330 gtk_widget_grab_focus(compose
->subject_entry
);
11336 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11337 GdkDragContext
*drag_context
,
11340 GtkSelectionData
*data
,
11343 gpointer user_data
)
11345 Compose
*compose
= (Compose
*)user_data
;
11351 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11353 type
= gtk_selection_data_get_data_type(data
);
11354 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11355 AlertValue val
= G_ALERTDEFAULT
;
11356 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11358 list
= uri_list_extract_filenames(ddata
);
11359 num_files
= g_list_length(list
);
11360 if (list
== NULL
&& strstr(ddata
, "://")) {
11361 /* Assume a list of no files, and data has ://, is a remote link */
11362 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11363 gchar
*tmpfile
= get_tmp_file();
11364 str_write_to_file(tmpdata
, tmpfile
);
11366 compose_insert_file(compose
, tmpfile
);
11367 claws_unlink(tmpfile
);
11369 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11370 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11373 switch (prefs_common
.compose_dnd_mode
) {
11374 case COMPOSE_DND_ASK
:
11375 msg
= g_strdup_printf(
11377 "Do you want to insert the contents of the file "
11378 "into the message body, or attach it to the email?",
11379 "Do you want to insert the contents of the %d files "
11380 "into the message body, or attach them to the email?",
11383 val
= alertpanel_full(_("Insert or attach?"), msg
,
11384 GTK_STOCK_CANCEL
, g_strconcat("+", _("_Insert"), NULL
), _("_Attach"),
11385 TRUE
, NULL
, ALERT_QUESTION
, G_ALERTALTERNATE
);
11388 case COMPOSE_DND_INSERT
:
11389 val
= G_ALERTALTERNATE
;
11391 case COMPOSE_DND_ATTACH
:
11392 val
= G_ALERTOTHER
;
11395 /* unexpected case */
11396 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11399 if (val
& G_ALERTDISABLE
) {
11400 val
&= ~G_ALERTDISABLE
;
11401 /* remember what action to perform by default, only if we don't click Cancel */
11402 if (val
== G_ALERTALTERNATE
)
11403 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11404 else if (val
== G_ALERTOTHER
)
11405 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11408 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11409 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11410 list_free_strings(list
);
11413 } else if (val
== G_ALERTOTHER
) {
11414 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11415 list_free_strings(list
);
11420 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11421 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11423 list_free_strings(list
);
11425 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11430 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11431 GdkDragContext
*drag_context
,
11434 GtkSelectionData
*data
,
11437 gpointer user_data
)
11439 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11440 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11442 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11445 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11446 gchar
*decoded
=g_new(gchar
, strlen(email
));
11449 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11450 gtk_editable_delete_text(entry
, 0, -1);
11451 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11452 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11456 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11459 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11461 Compose
*compose
= (Compose
*)data
;
11463 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11464 compose
->return_receipt
= TRUE
;
11466 compose
->return_receipt
= FALSE
;
11469 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11471 Compose
*compose
= (Compose
*)data
;
11473 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11474 compose
->remove_references
= TRUE
;
11476 compose
->remove_references
= FALSE
;
11479 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11480 ComposeHeaderEntry
*headerentry
)
11482 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11486 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11487 GdkEventKey
*event
,
11488 ComposeHeaderEntry
*headerentry
)
11490 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11491 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11492 !(event
->state
& GDK_MODIFIER_MASK
) &&
11493 (event
->keyval
== GDK_KEY_BackSpace
) &&
11494 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11495 gtk_container_remove
11496 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11497 headerentry
->combo
);
11498 gtk_container_remove
11499 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11500 headerentry
->entry
);
11501 headerentry
->compose
->header_list
=
11502 g_slist_remove(headerentry
->compose
->header_list
,
11504 g_free(headerentry
);
11505 } else if (event
->keyval
== GDK_KEY_Tab
) {
11506 if (headerentry
->compose
->header_last
== headerentry
) {
11507 /* Override default next focus, and give it to subject_entry
11508 * instead of notebook tabs
11510 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11511 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11518 static gboolean
scroll_postpone(gpointer data
)
11520 Compose
*compose
= (Compose
*)data
;
11522 if (compose
->batch
)
11525 GTK_EVENTS_FLUSH();
11526 compose_show_first_last_header(compose
, FALSE
);
11530 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11531 ComposeHeaderEntry
*headerentry
)
11533 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11534 compose_create_header_entry(headerentry
->compose
);
11535 g_signal_handlers_disconnect_matched
11536 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11537 0, 0, NULL
, NULL
, headerentry
);
11539 if (!headerentry
->compose
->batch
)
11540 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11544 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11546 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11547 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11551 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11553 GtkAdjustment
*vadj
;
11555 cm_return_if_fail(compose
);
11560 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11561 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11562 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11563 gtk_widget_get_parent(compose
->header_table
)));
11564 gtk_adjustment_set_value(vadj
, (show_first
?
11565 gtk_adjustment_get_lower(vadj
) :
11566 (gtk_adjustment_get_upper(vadj
) -
11567 gtk_adjustment_get_page_size(vadj
))));
11568 gtk_adjustment_changed(vadj
);
11571 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11572 const gchar
*text
, gint len
, Compose
*compose
)
11574 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11575 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11578 cm_return_if_fail(text
!= NULL
);
11580 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11581 G_CALLBACK(text_inserted
),
11583 if (paste_as_quotation
) {
11585 const gchar
*qmark
;
11587 GtkTextIter start_iter
;
11590 len
= strlen(text
);
11592 new_text
= g_strndup(text
, len
);
11594 qmark
= compose_quote_char_from_context(compose
);
11596 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11597 gtk_text_buffer_place_cursor(buffer
, iter
);
11599 pos
= gtk_text_iter_get_offset(iter
);
11601 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11602 _("Quote format error at line %d."));
11603 quote_fmt_reset_vartable();
11605 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11606 GINT_TO_POINTER(paste_as_quotation
- 1));
11608 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11609 gtk_text_buffer_place_cursor(buffer
, iter
);
11610 gtk_text_buffer_delete_mark(buffer
, mark
);
11612 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11613 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11614 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11615 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11616 gtk_text_buffer_delete_mark(buffer
, mark
);
11618 if (strcmp(text
, "\n") || compose
->automatic_break
11619 || gtk_text_iter_starts_line(iter
)) {
11620 GtkTextIter before_ins
;
11621 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11622 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11623 before_ins
= *iter
;
11624 gtk_text_iter_backward_chars(&before_ins
, len
);
11625 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11628 /* check if the preceding is just whitespace or quote */
11629 GtkTextIter start_line
;
11630 gchar
*tmp
= NULL
, *quote
= NULL
;
11631 gint quote_len
= 0, is_normal
= 0;
11632 start_line
= *iter
;
11633 gtk_text_iter_set_line_offset(&start_line
, 0);
11634 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11637 if (*tmp
== '\0') {
11640 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
11648 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11650 gtk_text_buffer_insert_with_tags_by_name(buffer
,
11651 iter
, text
, len
, "no_join", NULL
);
11656 if (!paste_as_quotation
) {
11657 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11658 compose_beautify_paragraph(compose
, iter
, FALSE
);
11659 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11660 gtk_text_buffer_delete_mark(buffer
, mark
);
11663 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
11664 G_CALLBACK(text_inserted
),
11666 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
11668 if (compose_can_autosave(compose
) &&
11669 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
11670 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
11671 compose
->draft_timeout_tag
= g_timeout_add
11672 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
11676 static void compose_check_all(GtkAction
*action
, gpointer data
)
11678 Compose
*compose
= (Compose
*)data
;
11679 if (!compose
->gtkaspell
)
11682 if (gtk_widget_has_focus(compose
->subject_entry
))
11683 claws_spell_entry_check_all(
11684 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11686 gtkaspell_check_all(compose
->gtkaspell
);
11689 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
11691 Compose
*compose
= (Compose
*)data
;
11692 if (compose
->gtkaspell
) {
11693 claws_spell_entry_recheck_all(
11694 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11695 gtkaspell_highlight_all(compose
->gtkaspell
);
11699 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
11701 Compose
*compose
= (Compose
*)data
;
11702 if (!compose
->gtkaspell
) {
11703 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
11707 if (gtk_widget_has_focus(compose
->subject_entry
))
11708 claws_spell_entry_check_backwards(
11709 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11711 gtkaspell_check_backwards(compose
->gtkaspell
);
11714 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
11716 Compose
*compose
= (Compose
*)data
;
11717 if (!compose
->gtkaspell
) {
11718 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
11722 if (gtk_widget_has_focus(compose
->subject_entry
))
11723 claws_spell_entry_check_forwards_go(
11724 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11726 gtkaspell_check_forwards_go(compose
->gtkaspell
);
11731 *\brief Guess originating forward account from MsgInfo and several
11732 * "common preference" settings. Return NULL if no guess.
11734 static PrefsAccount
*compose_guess_forward_account_from_msginfo(MsgInfo
*msginfo
)
11736 PrefsAccount
*account
= NULL
;
11738 cm_return_val_if_fail(msginfo
, NULL
);
11739 cm_return_val_if_fail(msginfo
->folder
, NULL
);
11740 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
11742 if (msginfo
->folder
->prefs
->enable_default_account
)
11743 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
11745 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
11747 Xstrdup_a(to
, msginfo
->to
, return NULL
);
11748 extract_address(to
);
11749 account
= account_find_from_address(to
, FALSE
);
11752 if (!account
&& prefs_common
.forward_account_autosel
) {
11753 gchar cc
[BUFFSIZE
];
11754 if (!procheader_get_header_from_msginfo
11755 (msginfo
, cc
,sizeof cc
, "Cc:")) {
11756 gchar
*buf
= cc
+ strlen("Cc:");
11757 extract_address(buf
);
11758 account
= account_find_from_address(buf
, FALSE
);
11762 if (!account
&& prefs_common
.forward_account_autosel
) {
11763 gchar deliveredto
[BUFFSIZE
];
11764 if (!procheader_get_header_from_msginfo
11765 (msginfo
, deliveredto
,sizeof deliveredto
, "Delivered-To:")) {
11766 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
11767 extract_address(buf
);
11768 account
= account_find_from_address(buf
, FALSE
);
11773 account
= msginfo
->folder
->folder
->account
;
11778 gboolean
compose_close(Compose
*compose
)
11782 cm_return_val_if_fail(compose
, FALSE
);
11784 if (!g_mutex_trylock(compose
->mutex
)) {
11785 /* we have to wait for the (possibly deferred by auto-save)
11786 * drafting to be done, before destroying the compose under
11788 debug_print("waiting for drafting to finish...\n");
11789 compose_allow_user_actions(compose
, FALSE
);
11790 if (compose
->close_timeout_tag
== 0) {
11791 compose
->close_timeout_tag
=
11792 g_timeout_add (500, (GSourceFunc
) compose_close
,
11798 if (compose
->draft_timeout_tag
>= 0) {
11799 g_source_remove(compose
->draft_timeout_tag
);
11800 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
11803 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
11804 if (!compose
->batch
) {
11805 prefs_common
.compose_x
= x
;
11806 prefs_common
.compose_y
= y
;
11808 g_mutex_unlock(compose
->mutex
);
11809 compose_destroy(compose
);
11814 * Add entry field for each address in list.
11815 * \param compose E-Mail composition object.
11816 * \param listAddress List of (formatted) E-Mail addresses.
11818 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
11821 node
= listAddress
;
11823 addr
= ( gchar
* ) node
->data
;
11824 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
11825 node
= g_list_next( node
);
11829 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
11830 guint action
, gboolean opening_multiple
)
11832 gchar
*body
= NULL
;
11833 GSList
*new_msglist
= NULL
;
11834 MsgInfo
*tmp_msginfo
= NULL
;
11835 gboolean originally_enc
= FALSE
;
11836 gboolean originally_sig
= FALSE
;
11837 Compose
*compose
= NULL
;
11838 gchar
*s_system
= NULL
;
11840 cm_return_if_fail(msgview
!= NULL
);
11842 cm_return_if_fail(msginfo_list
!= NULL
);
11844 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
) {
11845 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
11846 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
11848 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
11849 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
11850 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
11851 orig_msginfo
, mimeinfo
);
11852 if (tmp_msginfo
!= NULL
) {
11853 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
11855 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
11856 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
11857 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
11859 tmp_msginfo
->folder
= orig_msginfo
->folder
;
11860 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
11861 if (orig_msginfo
->tags
) {
11862 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
11863 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
11869 if (!opening_multiple
)
11870 body
= messageview_get_selection(msgview
);
11873 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
11874 procmsg_msginfo_free(&tmp_msginfo
);
11875 g_slist_free(new_msglist
);
11877 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
11879 if (compose
&& originally_enc
) {
11880 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
11883 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
11884 compose_force_signing(compose
, compose
->account
, s_system
);
11888 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
11891 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
11894 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
11895 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
11896 GSList
*cur
= msginfo_list
;
11897 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
11898 "messages. Opening the windows "
11899 "could take some time. Do you "
11900 "want to continue?"),
11901 g_slist_length(msginfo_list
));
11902 if (g_slist_length(msginfo_list
) > 9
11903 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, "+" GTK_STOCK_YES
, NULL
)
11904 != G_ALERTALTERNATE
) {
11909 /* We'll open multiple compose windows */
11910 /* let the WM place the next windows */
11911 compose_force_window_origin
= FALSE
;
11912 for (; cur
; cur
= cur
->next
) {
11914 tmplist
.data
= cur
->data
;
11915 tmplist
.next
= NULL
;
11916 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
11918 compose_force_window_origin
= TRUE
;
11920 /* forwarding multiple mails as attachments is done via a
11921 * single compose window */
11922 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
11926 void compose_check_for_email_account(Compose
*compose
)
11928 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
11934 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
11935 ac
= account_get_cur_account();
11936 if (ac
->protocol
== A_NNTP
) {
11937 list
= account_get_list();
11939 for( ; list
!= NULL
; list
= g_list_next(list
)) {
11940 curr
= (PrefsAccount
*) list
->data
;
11941 if (curr
->protocol
!= A_NNTP
) {
11947 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
11952 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
11953 const gchar
*address
)
11955 GSList
*msginfo_list
= NULL
;
11956 gchar
*body
= messageview_get_selection(msgview
);
11959 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
11961 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
11962 compose_check_for_email_account(compose
);
11963 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
11964 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
11965 compose_reply_set_subject(compose
, msginfo
);
11968 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
11971 void compose_set_position(Compose
*compose
, gint pos
)
11973 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11975 gtkut_text_view_set_position(text
, pos
);
11978 gboolean
compose_search_string(Compose
*compose
,
11979 const gchar
*str
, gboolean case_sens
)
11981 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11983 return gtkut_text_view_search_string(text
, str
, case_sens
);
11986 gboolean
compose_search_string_backward(Compose
*compose
,
11987 const gchar
*str
, gboolean case_sens
)
11989 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11991 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
11994 /* allocate a msginfo structure and populate its data from a compose data structure */
11995 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
11997 MsgInfo
*newmsginfo
;
11999 gchar buf
[BUFFSIZE
];
12001 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12003 newmsginfo
= procmsg_msginfo_new();
12006 get_rfc822_date(buf
, sizeof(buf
));
12007 newmsginfo
->date
= g_strdup(buf
);
12010 if (compose
->from_name
) {
12011 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12012 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12016 if (compose
->subject_entry
)
12017 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12019 /* to, cc, reply-to, newsgroups */
12020 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12021 gchar
*header
= gtk_editable_get_chars(
12023 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12024 gchar
*entry
= gtk_editable_get_chars(
12025 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12027 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12028 if ( newmsginfo
->to
== NULL
) {
12029 newmsginfo
->to
= g_strdup(entry
);
12030 } else if (entry
&& *entry
) {
12031 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12032 g_free(newmsginfo
->to
);
12033 newmsginfo
->to
= tmp
;
12036 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12037 if ( newmsginfo
->cc
== NULL
) {
12038 newmsginfo
->cc
= g_strdup(entry
);
12039 } else if (entry
&& *entry
) {
12040 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12041 g_free(newmsginfo
->cc
);
12042 newmsginfo
->cc
= tmp
;
12045 if ( strcasecmp(header
,
12046 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12047 if ( newmsginfo
->newsgroups
== NULL
) {
12048 newmsginfo
->newsgroups
= g_strdup(entry
);
12049 } else if (entry
&& *entry
) {
12050 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12051 g_free(newmsginfo
->newsgroups
);
12052 newmsginfo
->newsgroups
= tmp
;
12060 /* other data is unset */
12066 /* update compose's dictionaries from folder dict settings */
12067 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12068 FolderItem
*folder_item
)
12070 cm_return_if_fail(compose
!= NULL
);
12072 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12073 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12075 if (prefs
->enable_default_dictionary
)
12076 gtkaspell_change_dict(compose
->gtkaspell
,
12077 prefs
->default_dictionary
, FALSE
);
12078 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12079 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12080 prefs
->default_alt_dictionary
);
12081 if (prefs
->enable_default_dictionary
12082 || prefs
->enable_default_alt_dictionary
)
12083 compose_spell_menu_changed(compose
);
12088 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12090 Compose
*compose
= (Compose
*)data
;
12092 cm_return_if_fail(compose
!= NULL
);
12094 gtk_widget_grab_focus(compose
->text
);
12097 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12099 gtk_combo_box_popup(GTK_COMBO_BOX(data
));