2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 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/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction
;
145 PRIORITY_HIGHEST
= 1,
154 COMPOSE_INSERT_SUCCESS
,
155 COMPOSE_INSERT_READ_ERROR
,
156 COMPOSE_INSERT_INVALID_CHARACTER
,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult
;
162 COMPOSE_WRITE_FOR_SEND
,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED
,
175 SUBJECT_FIELD_PRESENT
,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
186 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
188 static GList
*compose_list
= NULL
;
189 static GSList
*extra_headers
= NULL
;
191 static Compose
*compose_generic_new (PrefsAccount
*account
,
195 GList
*listAddress
);
197 static Compose
*compose_create (PrefsAccount
*account
,
202 static void compose_entry_mark_default_to (Compose
*compose
,
203 const gchar
*address
);
204 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
205 ComposeQuoteMode quote_mode
,
209 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
210 GSList
*msginfo_list
);
211 static Compose
*compose_reply (MsgInfo
*msginfo
,
212 ComposeQuoteMode quote_mode
,
217 static Compose
*compose_reply_mode (ComposeMode mode
,
218 GSList
*msginfo_list
,
220 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
221 static void compose_update_privacy_systems_menu(Compose
*compose
);
223 static GtkWidget
*compose_account_option_menu_create
225 static void compose_set_out_encoding (Compose
*compose
);
226 static void compose_set_template_menu (Compose
*compose
);
227 static void compose_destroy (Compose
*compose
);
229 static MailField
compose_entries_set (Compose
*compose
,
231 ComposeEntryType to_type
);
232 static gint
compose_parse_header (Compose
*compose
,
234 static gint
compose_parse_manual_headers (Compose
*compose
,
236 HeaderEntry
*entries
);
237 static gchar
*compose_parse_references (const gchar
*ref
,
240 static gchar
*compose_quote_fmt (Compose
*compose
,
246 gboolean need_unescape
,
247 const gchar
*err_msg
);
249 static void compose_reply_set_entry (Compose
*compose
,
255 followup_and_reply_to
);
256 static void compose_reedit_set_entry (Compose
*compose
,
259 static void compose_insert_sig (Compose
*compose
,
261 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
264 static gboolean
compose_attach_append (Compose
*compose
,
267 const gchar
*content_type
,
268 const gchar
*charset
);
269 static void compose_attach_parts (Compose
*compose
,
272 static gboolean
compose_beautify_paragraph (Compose
*compose
,
273 GtkTextIter
*par_iter
,
275 static void compose_wrap_all (Compose
*compose
);
276 static void compose_wrap_all_full (Compose
*compose
,
279 static void compose_set_title (Compose
*compose
);
280 static void compose_select_account (Compose
*compose
,
281 PrefsAccount
*account
,
284 static PrefsAccount
*compose_current_mail_account(void);
285 /* static gint compose_send (Compose *compose); */
286 static gboolean compose_check_for_valid_recipient
288 static gboolean
compose_check_entries (Compose
*compose
,
289 gboolean check_everything
);
290 static gint
compose_write_to_file (Compose
*compose
,
293 gboolean attach_parts
);
294 static gint
compose_write_body_to_file (Compose
*compose
,
296 static gint
compose_remove_reedit_target (Compose
*compose
,
298 static void compose_remove_draft (Compose
*compose
);
299 static gint
compose_queue_sub (Compose
*compose
,
303 gboolean check_subject
,
304 gboolean remove_reedit_target
);
305 static int compose_add_attachments (Compose
*compose
,
307 static gchar
*compose_get_header (Compose
*compose
);
308 static gchar
*compose_get_manual_headers_info (Compose
*compose
);
310 static void compose_convert_header (Compose
*compose
,
315 gboolean addr_field
);
317 static void compose_attach_info_free (AttachInfo
*ainfo
);
318 static void compose_attach_remove_selected (GtkAction
*action
,
321 static void compose_template_apply (Compose
*compose
,
324 static void compose_attach_property (GtkAction
*action
,
326 static void compose_attach_property_create (gboolean
*cancelled
);
327 static void attach_property_ok (GtkWidget
*widget
,
328 gboolean
*cancelled
);
329 static void attach_property_cancel (GtkWidget
*widget
,
330 gboolean
*cancelled
);
331 static gint
attach_property_delete_event (GtkWidget
*widget
,
333 gboolean
*cancelled
);
334 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
336 gboolean
*cancelled
);
338 static void compose_exec_ext_editor (Compose
*compose
);
340 static gint
compose_exec_ext_editor_real (const gchar
*file
);
341 static gboolean
compose_ext_editor_kill (Compose
*compose
);
342 static gboolean
compose_input_cb (GIOChannel
*source
,
343 GIOCondition condition
,
345 static void compose_set_ext_editor_sensitive (Compose
*compose
,
347 #endif /* G_OS_UNIX */
349 static void compose_undo_state_changed (UndoMain
*undostruct
,
354 static void compose_create_header_entry (Compose
*compose
);
355 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
356 gchar
*text
, ComposePrefType pref_type
);
357 static void compose_remove_header_entries(Compose
*compose
);
359 static void compose_update_priority_menu_item(Compose
* compose
);
361 static void compose_spell_menu_changed (void *data
);
362 static void compose_dict_changed (void *data
);
364 static void compose_add_field_list ( Compose
*compose
,
365 GList
*listAddress
);
367 /* callback functions */
369 static void compose_notebook_size_alloc (GtkNotebook
*notebook
,
370 GtkAllocation
*allocation
,
372 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
373 GtkAllocation
*allocation
,
374 GtkSHRuler
*shruler
);
375 static void account_activated (GtkComboBox
*optmenu
,
377 static void attach_selected (GtkTreeView
*tree_view
,
378 GtkTreePath
*tree_path
,
379 GtkTreeViewColumn
*column
,
381 static gboolean
attach_button_pressed (GtkWidget
*widget
,
382 GdkEventButton
*event
,
384 static gboolean
attach_key_pressed (GtkWidget
*widget
,
387 static void compose_send_cb (GtkAction
*action
, gpointer data
);
388 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
390 static void compose_save_cb (GtkAction
*action
,
393 static void compose_attach_cb (GtkAction
*action
,
395 static void compose_insert_file_cb (GtkAction
*action
,
397 static void compose_insert_sig_cb (GtkAction
*action
,
399 static void compose_replace_sig_cb (GtkAction
*action
,
402 static void compose_close_cb (GtkAction
*action
,
404 static void compose_print_cb (GtkAction
*action
,
407 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
409 static void compose_address_cb (GtkAction
*action
,
411 static void about_show_cb (GtkAction
*action
,
413 static void compose_template_activate_cb(GtkWidget
*widget
,
416 static void compose_ext_editor_cb (GtkAction
*action
,
419 static gint
compose_delete_cb (GtkWidget
*widget
,
423 static void compose_undo_cb (GtkAction
*action
,
425 static void compose_redo_cb (GtkAction
*action
,
427 static void compose_cut_cb (GtkAction
*action
,
429 static void compose_copy_cb (GtkAction
*action
,
431 static void compose_paste_cb (GtkAction
*action
,
433 static void compose_paste_as_quote_cb (GtkAction
*action
,
435 static void compose_paste_no_wrap_cb (GtkAction
*action
,
437 static void compose_paste_wrap_cb (GtkAction
*action
,
439 static void compose_allsel_cb (GtkAction
*action
,
442 static void compose_advanced_action_cb (GtkAction
*action
,
445 static void compose_grab_focus_cb (GtkWidget
*widget
,
448 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
451 static void compose_wrap_cb (GtkAction
*action
,
453 static void compose_wrap_all_cb (GtkAction
*action
,
455 static void compose_find_cb (GtkAction
*action
,
457 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
459 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
462 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
464 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
466 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
468 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
469 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
470 static void activate_privacy_system (Compose
*compose
,
471 PrefsAccount
*account
,
473 static void compose_use_signing(Compose
*compose
, gboolean use_signing
);
474 static void compose_use_encryption(Compose
*compose
, gboolean use_encryption
);
475 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
477 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
479 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
480 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
481 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
483 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
484 GdkDragContext
*drag_context
,
487 GtkSelectionData
*data
,
491 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
492 GdkDragContext
*drag_context
,
495 GtkSelectionData
*data
,
499 static void compose_header_drag_received_cb (GtkWidget
*widget
,
500 GdkDragContext
*drag_context
,
503 GtkSelectionData
*data
,
508 static gboolean
compose_drag_drop (GtkWidget
*widget
,
509 GdkDragContext
*drag_context
,
511 guint time
, gpointer user_data
);
512 static gboolean completion_set_focus_to_subject
517 static void text_inserted (GtkTextBuffer
*buffer
,
522 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
523 ComposeQuoteMode quote_mode
,
527 gboolean followup_and_reply_to
,
530 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
531 ComposeHeaderEntry
*headerentry
);
532 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
534 ComposeHeaderEntry
*headerentry
);
535 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
536 ComposeHeaderEntry
*headerentry
);
538 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
540 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
542 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
548 static void compose_check_all (GtkAction
*action
, gpointer data
);
549 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
550 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
551 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
554 static PrefsAccount
*compose_guess_forward_account_from_msginfo (MsgInfo
*msginfo
);
556 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
559 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
560 FolderItem
*folder_item
);
562 static void compose_attach_update_label(Compose
*compose
);
563 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
564 gboolean respect_default_to
);
565 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
);
567 static GtkActionEntry compose_popup_entries
[] =
569 {"Compose", NULL
, "Compose" },
570 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
571 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
572 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
573 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
576 static GtkActionEntry compose_entries
[] =
578 {"Menu", NULL
, "Menu" },
580 {"Message", NULL
, N_("_Message") },
581 {"Edit", NULL
, N_("_Edit") },
583 {"Spelling", NULL
, N_("_Spelling") },
585 {"Options", NULL
, N_("_Options") },
586 {"Tools", NULL
, N_("_Tools") },
587 {"Help", NULL
, N_("_Help") },
589 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
590 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
591 {"Message/---", NULL
, "---" },
593 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
594 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
595 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
596 {"Message/ReplaceSig", NULL
, N_("_Replace signature"), NULL
, NULL
, G_CALLBACK(compose_replace_sig_cb
) },
597 /* {"Message/---", NULL, "---" }, */
598 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
599 /* {"Message/---", NULL, "---" }, */
600 {"Message/Print", NULL
, N_("_Print"), NULL
, NULL
, G_CALLBACK(compose_print_cb
) },
601 /* {"Message/---", NULL, "---" }, */
602 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
605 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
606 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
607 {"Edit/---", NULL
, "---" },
609 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
610 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
611 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
613 {"Edit/SpecialPaste", NULL
, N_("_Special paste") },
614 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("As _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
615 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_Wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
616 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_Unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
618 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
620 {"Edit/Advanced", NULL
, N_("A_dvanced") },
621 {"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*/
622 {"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*/
623 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
624 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
625 {"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*/
626 {"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*/
627 {"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*/
628 {"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*/
629 {"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*/
630 {"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*/
631 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
632 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
633 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
634 {"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*/
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
639 /* {"Edit/---", NULL, "---" }, */
640 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
641 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
642 /* {"Edit/---", NULL, "---" }, */
643 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
646 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
647 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
648 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
649 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
651 {"Spelling/---", NULL
, "---" },
652 {"Spelling/Options", NULL
, N_("_Options") },
657 {"Options/ReplyMode", NULL
, N_("Reply _mode") },
658 {"Options/---", NULL
, "---" },
659 {"Options/PrivacySystem", NULL
, N_("Privacy _System") },
660 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
662 /* {"Options/---", NULL, "---" }, */
664 {"Options/Priority", NULL
, N_("_Priority") },
666 {"Options/Encoding", NULL
, N_("Character _encoding") },
667 {"Options/Encoding/---", NULL
, "---" },
668 #define ENC_ACTION(cs_char,c_char,string) \
669 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
671 {"Options/Encoding/Western", NULL
, N_("Western European") },
672 {"Options/Encoding/Baltic", NULL
, N_("Baltic") },
673 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew") },
674 {"Options/Encoding/Arabic", NULL
, N_("Arabic") },
675 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic") },
676 {"Options/Encoding/Japanese", NULL
, N_("Japanese") },
677 {"Options/Encoding/Chinese", NULL
, N_("Chinese") },
678 {"Options/Encoding/Korean", NULL
, N_("Korean") },
679 {"Options/Encoding/Thai", NULL
, N_("Thai") },
682 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
684 {"Tools/Template", NULL
, N_("_Template") },
685 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
686 {"Tools/Actions", NULL
, N_("Actio_ns") },
687 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
690 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
693 static GtkToggleActionEntry compose_toggle_entries
[] =
695 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
) }, /* TOGGLE */
696 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
) }, /* TOGGLE */
697 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
) }, /* Toggle */
698 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
) }, /* Toggle */
699 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
) }, /* TOGGLE */
700 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
) }, /* TOGGLE */
701 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
) }, /* Toggle */
704 static GtkRadioActionEntry compose_radio_rm_entries
[] =
706 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
707 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
708 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
709 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
712 static GtkRadioActionEntry compose_radio_prio_entries
[] =
714 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
715 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
716 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
717 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
718 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
721 static GtkRadioActionEntry compose_radio_enc_entries
[] =
723 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
757 static GtkTargetEntry compose_mime_types
[] =
759 {"text/uri-list", 0, 0},
760 {"UTF8_STRING", 0, 0},
764 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
766 const GList
*compose_list
= compose_get_compose_list();
767 const GList
*elem
= NULL
;
770 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
772 Compose
*c
= (Compose
*)elem
->data
;
774 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
778 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
779 gtkut_window_popup(c
->window
);
787 static GdkColor quote_color1
=
788 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
789 static GdkColor quote_color2
=
790 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
791 static GdkColor quote_color3
=
792 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
794 static GdkColor quote_bgcolor1
=
795 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
796 static GdkColor quote_bgcolor2
=
797 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
798 static GdkColor quote_bgcolor3
=
799 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
801 static GdkColor signature_color
= {
808 static GdkColor uri_color
= {
815 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
817 GtkTextBuffer
*buffer
;
818 GdkColor black
= {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
819 #if !GTK_CHECK_VERSION(2, 24, 0)
826 buffer
= gtk_text_view_get_buffer(text
);
828 if (prefs_common
.enable_color
) {
829 /* grab the quote colors, converting from an int to a GdkColor */
830 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level1_col
,
832 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level2_col
,
834 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level3_col
,
836 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level1_bgcol
,
838 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level2_bgcol
,
840 gtkut_convert_int_to_gdk_color(prefs_common
.quote_level3_bgcol
,
842 gtkut_convert_int_to_gdk_color(prefs_common
.signature_col
,
844 gtkut_convert_int_to_gdk_color(prefs_common
.uri_col
,
847 signature_color
= quote_color1
= quote_color2
= quote_color3
=
848 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
851 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
852 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
853 "foreground-gdk", "e_color1
,
854 "paragraph-background-gdk", "e_bgcolor1
,
856 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
857 "foreground-gdk", "e_color2
,
858 "paragraph-background-gdk", "e_bgcolor2
,
860 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
861 "foreground-gdk", "e_color3
,
862 "paragraph-background-gdk", "e_bgcolor3
,
865 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
866 "foreground-gdk", "e_color1
,
868 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
869 "foreground-gdk", "e_color2
,
871 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
872 "foreground-gdk", "e_color3
,
876 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
877 "foreground-gdk", &signature_color
,
880 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
881 "foreground-gdk", &uri_color
,
883 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
884 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
886 #if !GTK_CHECK_VERSION(2, 24, 0)
887 color
[0] = quote_color1
;
888 color
[1] = quote_color2
;
889 color
[2] = quote_color3
;
890 color
[3] = quote_bgcolor1
;
891 color
[4] = quote_bgcolor2
;
892 color
[5] = quote_bgcolor3
;
893 color
[6] = signature_color
;
894 color
[7] = uri_color
;
896 cmap
= gdk_drawable_get_colormap(gtk_widget_get_window(compose
->window
));
897 gdk_colormap_alloc_colors(cmap
, color
, 8, FALSE
, TRUE
, success
);
899 for (i
= 0; i
< 8; i
++) {
900 if (success
[i
] == FALSE
) {
901 g_warning("Compose: color allocation failed.\n");
902 quote_color1
= quote_color2
= quote_color3
=
903 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
=
904 signature_color
= uri_color
= black
;
910 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
913 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
916 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
918 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
921 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
923 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
926 #define SCROLL_TO_CURSOR(compose) { \
927 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
928 gtk_text_view_get_buffer( \
929 GTK_TEXT_VIEW(compose->text))); \
930 gtk_text_view_scroll_mark_onscreen( \
931 GTK_TEXT_VIEW(compose->text), \
935 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
938 if (folderidentifier
) {
939 #if !GTK_CHECK_VERSION(2, 24, 0)
940 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
));
942 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
944 prefs_common
.compose_save_to_history
= add_history(
945 prefs_common
.compose_save_to_history
, folderidentifier
);
946 #if !GTK_CHECK_VERSION(2, 24, 0)
947 combobox_set_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
),
948 prefs_common
.compose_save_to_history
);
950 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
951 prefs_common
.compose_save_to_history
);
955 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
956 if (folderidentifier
)
957 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
959 gtk_entry_set_text(GTK_ENTRY(entry
), "");
962 static gchar
*compose_get_save_to(Compose
*compose
)
965 gchar
*result
= NULL
;
966 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
967 result
= gtk_editable_get_chars(entry
, 0, -1);
970 #if !GTK_CHECK_VERSION(2, 24, 0)
971 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
));
973 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
975 prefs_common
.compose_save_to_history
= add_history(
976 prefs_common
.compose_save_to_history
, result
);
977 #if !GTK_CHECK_VERSION(2, 24, 0)
978 combobox_set_popdown_strings(GTK_COMBO_BOX(compose
->savemsg_combo
),
979 prefs_common
.compose_save_to_history
);
981 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
982 prefs_common
.compose_save_to_history
);
988 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
989 GList
*attach_files
, GList
*listAddress
)
992 GtkTextView
*textview
;
993 GtkTextBuffer
*textbuf
;
995 const gchar
*subject_format
= NULL
;
996 const gchar
*body_format
= NULL
;
997 gchar
*mailto_from
= NULL
;
998 PrefsAccount
*mailto_account
= NULL
;
999 MsgInfo
* dummyinfo
= NULL
;
1000 gint cursor_pos
= -1;
1001 MailField mfield
= NO_FIELD_PRESENT
;
1005 /* check if mailto defines a from */
1006 if (mailto
&& *mailto
!= '\0') {
1007 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
1008 /* mailto defines a from, check if we can get account prefs from it,
1009 if not, the account prefs will be guessed using other ways, but we'll keep
1012 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
1013 if (mailto_account
== NULL
) {
1015 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
1016 extract_address(tmp_from
);
1017 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
1021 account
= mailto_account
;
1024 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1025 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
1026 account
= account_find_from_id(item
->prefs
->default_account
);
1028 /* if no account prefs set, fallback to the current one */
1029 if (!account
) account
= cur_account
;
1030 cm_return_val_if_fail(account
!= NULL
, NULL
);
1032 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1034 /* override from name if mailto asked for it */
1036 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1037 g_free(mailto_from
);
1039 /* override from name according to folder properties */
1040 if (item
&& item
->prefs
&&
1041 item
->prefs
->compose_with_format
&&
1042 item
->prefs
->compose_override_from_format
&&
1043 *item
->prefs
->compose_override_from_format
!= '\0') {
1048 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1050 /* decode \-escape sequences in the internal representation of the quote format */
1051 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1052 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1055 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1056 compose
->gtkaspell
);
1058 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1060 quote_fmt_scan_string(tmp
);
1063 buf
= quote_fmt_get_buffer();
1065 alertpanel_error(_("New message From format error."));
1067 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1068 quote_fmt_reset_vartable();
1073 compose
->replyinfo
= NULL
;
1074 compose
->fwdinfo
= NULL
;
1076 textview
= GTK_TEXT_VIEW(compose
->text
);
1077 textbuf
= gtk_text_view_get_buffer(textview
);
1078 compose_create_tags(textview
, compose
);
1080 undo_block(compose
->undostruct
);
1082 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1085 if (account
->auto_sig
)
1086 compose_insert_sig(compose
, FALSE
);
1087 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1088 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1090 if (account
->protocol
!= A_NNTP
) {
1091 if (mailto
&& *mailto
!= '\0') {
1092 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1095 compose_set_folder_prefs(compose
, item
, TRUE
);
1097 if (item
&& item
->ret_rcpt
) {
1098 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1101 if (mailto
&& *mailto
!= '\0') {
1102 if (!strchr(mailto
, '@'))
1103 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1105 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1106 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1107 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1108 mfield
= TO_FIELD_PRESENT
;
1111 * CLAWS: just don't allow return receipt request, even if the user
1112 * may want to send an email. simple but foolproof.
1114 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1116 compose_add_field_list( compose
, listAddress
);
1118 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1119 subject_format
= item
->prefs
->compose_subject_format
;
1120 body_format
= item
->prefs
->compose_body_format
;
1121 } else if (account
->compose_with_format
) {
1122 subject_format
= account
->compose_subject_format
;
1123 body_format
= account
->compose_body_format
;
1124 } else if (prefs_common
.compose_with_format
) {
1125 subject_format
= prefs_common
.compose_subject_format
;
1126 body_format
= prefs_common
.compose_body_format
;
1129 if (subject_format
|| body_format
) {
1132 && *subject_format
!= '\0' )
1134 gchar
*subject
= NULL
;
1139 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1141 /* decode \-escape sequences in the internal representation of the quote format */
1142 tmp
= g_malloc(strlen(subject_format
)+1);
1143 pref_get_unescaped_pref(tmp
, subject_format
);
1145 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1147 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1148 compose
->gtkaspell
);
1150 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1152 quote_fmt_scan_string(tmp
);
1155 buf
= quote_fmt_get_buffer();
1157 alertpanel_error(_("New message subject format error."));
1159 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1160 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1161 quote_fmt_reset_vartable();
1165 mfield
= SUBJECT_FIELD_PRESENT
;
1169 && *body_format
!= '\0' )
1172 GtkTextBuffer
*buffer
;
1173 GtkTextIter start
, end
;
1177 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1179 text
= GTK_TEXT_VIEW(compose
->text
);
1180 buffer
= gtk_text_view_get_buffer(text
);
1181 gtk_text_buffer_get_start_iter(buffer
, &start
);
1182 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1183 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1185 compose_quote_fmt(compose
, dummyinfo
,
1187 NULL
, tmp
, FALSE
, TRUE
,
1188 _("The body of the \"New message\" template has an error at line %d."));
1189 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1190 quote_fmt_reset_vartable();
1194 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1195 gtkaspell_highlight_all(compose
->gtkaspell
);
1197 mfield
= BODY_FIELD_PRESENT
;
1201 procmsg_msginfo_free( dummyinfo
);
1207 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1208 ainfo
= (AttachInfo
*) curr
->data
;
1209 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1210 ainfo
->content_type
, ainfo
->charset
);
1214 compose_show_first_last_header(compose
, TRUE
);
1216 /* Set save folder */
1217 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1218 gchar
*folderidentifier
;
1220 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
1221 folderidentifier
= folder_item_get_identifier(item
);
1222 compose_set_save_to(compose
, folderidentifier
);
1223 g_free(folderidentifier
);
1226 /* Place cursor according to provided input (mfield) */
1228 case NO_FIELD_PRESENT
:
1229 if (compose
->header_last
)
1230 gtk_widget_grab_focus(compose
->header_last
->entry
);
1232 case TO_FIELD_PRESENT
:
1233 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1235 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1238 gtk_widget_grab_focus(compose
->subject_entry
);
1240 case SUBJECT_FIELD_PRESENT
:
1241 textview
= GTK_TEXT_VIEW(compose
->text
);
1244 textbuf
= gtk_text_view_get_buffer(textview
);
1247 mark
= gtk_text_buffer_get_insert(textbuf
);
1248 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1249 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1251 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1252 * only defers where it comes to the variable body
1253 * is not null. If no body is present compose->text
1254 * will be null in which case you cannot place the
1255 * cursor inside the component so. An empty component
1256 * is therefore created before placing the cursor
1258 case BODY_FIELD_PRESENT
:
1259 cursor_pos
= quote_fmt_get_cursor_pos();
1260 if (cursor_pos
== -1)
1261 gtk_widget_grab_focus(compose
->header_last
->entry
);
1263 gtk_widget_grab_focus(compose
->text
);
1267 undo_unblock(compose
->undostruct
);
1269 if (prefs_common
.auto_exteditor
)
1270 compose_exec_ext_editor(compose
);
1272 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1274 SCROLL_TO_CURSOR(compose
);
1276 compose
->modified
= FALSE
;
1277 compose_set_title(compose
);
1279 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1284 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1285 gboolean override_pref
, const gchar
*system
)
1287 const gchar
*privacy
= NULL
;
1289 cm_return_if_fail(compose
!= NULL
);
1290 cm_return_if_fail(account
!= NULL
);
1292 if (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
)
1295 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1296 privacy
= account
->default_privacy_system
;
1300 GSList
*privacy_avail
= privacy_get_system_ids();
1301 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1302 privacy
= (gchar
*)(privacy_avail
->data
);
1305 if (privacy
!= NULL
) {
1307 g_free(compose
->privacy_system
);
1308 compose
->privacy_system
= NULL
;
1310 if (compose
->privacy_system
== NULL
)
1311 compose
->privacy_system
= g_strdup(privacy
);
1312 else if (*(compose
->privacy_system
) == '\0') {
1313 g_free(compose
->privacy_system
);
1314 compose
->privacy_system
= g_strdup(privacy
);
1316 compose_update_privacy_system_menu_item(compose
, FALSE
);
1317 compose_use_encryption(compose
, TRUE
);
1321 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1323 const gchar
*privacy
= NULL
;
1325 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1326 privacy
= account
->default_privacy_system
;
1330 GSList
*privacy_avail
= privacy_get_system_ids();
1331 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1332 privacy
= (gchar
*)(privacy_avail
->data
);
1336 if (privacy
!= NULL
) {
1338 g_free(compose
->privacy_system
);
1339 compose
->privacy_system
= NULL
;
1341 if (compose
->privacy_system
== NULL
)
1342 compose
->privacy_system
= g_strdup(privacy
);
1343 compose_update_privacy_system_menu_item(compose
, FALSE
);
1344 compose_use_signing(compose
, TRUE
);
1348 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1352 Compose
*compose
= NULL
;
1354 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1356 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1357 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1359 list_len
= g_slist_length(msginfo_list
);
1363 case COMPOSE_REPLY_TO_ADDRESS
:
1364 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1365 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1367 case COMPOSE_REPLY_WITH_QUOTE
:
1368 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1369 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1371 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1372 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1373 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1375 case COMPOSE_REPLY_TO_SENDER
:
1376 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1377 FALSE
, FALSE
, TRUE
, body
);
1379 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1380 compose
= compose_followup_and_reply_to(msginfo
,
1381 COMPOSE_QUOTE_CHECK
,
1382 FALSE
, FALSE
, body
);
1384 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1385 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1386 FALSE
, FALSE
, TRUE
, body
);
1388 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1389 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1390 FALSE
, FALSE
, TRUE
, NULL
);
1392 case COMPOSE_REPLY_TO_ALL
:
1393 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1394 TRUE
, FALSE
, FALSE
, body
);
1396 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1397 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1398 TRUE
, FALSE
, FALSE
, body
);
1400 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1401 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1402 TRUE
, FALSE
, FALSE
, NULL
);
1404 case COMPOSE_REPLY_TO_LIST
:
1405 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1406 FALSE
, TRUE
, FALSE
, body
);
1408 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1409 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1410 FALSE
, TRUE
, FALSE
, body
);
1412 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1413 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1414 FALSE
, TRUE
, FALSE
, NULL
);
1416 case COMPOSE_FORWARD
:
1417 if (prefs_common
.forward_as_attachment
) {
1418 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1421 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1425 case COMPOSE_FORWARD_INLINE
:
1426 /* check if we reply to more than one Message */
1427 if (list_len
== 1) {
1428 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1431 /* more messages FALL THROUGH */
1432 case COMPOSE_FORWARD_AS_ATTACH
:
1433 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1435 case COMPOSE_REDIRECT
:
1436 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1439 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode
);
1442 if (compose
== NULL
) {
1443 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1447 compose
->rmode
= mode
;
1448 switch (compose
->rmode
) {
1450 case COMPOSE_REPLY_WITH_QUOTE
:
1451 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1452 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1453 debug_print("reply mode Normal\n");
1454 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1455 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1457 case COMPOSE_REPLY_TO_SENDER
:
1458 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1459 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1460 debug_print("reply mode Sender\n");
1461 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1463 case COMPOSE_REPLY_TO_ALL
:
1464 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1465 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1466 debug_print("reply mode All\n");
1467 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1469 case COMPOSE_REPLY_TO_LIST
:
1470 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1471 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1472 debug_print("reply mode List\n");
1473 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1475 case COMPOSE_REPLY_TO_ADDRESS
:
1476 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1484 static Compose
*compose_reply(MsgInfo
*msginfo
,
1485 ComposeQuoteMode quote_mode
,
1491 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1492 to_sender
, FALSE
, body
);
1495 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1496 ComposeQuoteMode quote_mode
,
1501 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1502 to_sender
, TRUE
, body
);
1505 static void compose_extract_original_charset(Compose
*compose
)
1507 MsgInfo
*info
= NULL
;
1508 if (compose
->replyinfo
) {
1509 info
= compose
->replyinfo
;
1510 } else if (compose
->fwdinfo
) {
1511 info
= compose
->fwdinfo
;
1512 } else if (compose
->targetinfo
) {
1513 info
= compose
->targetinfo
;
1516 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1517 MimeInfo
*partinfo
= mimeinfo
;
1518 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1519 partinfo
= procmime_mimeinfo_next(partinfo
);
1521 compose
->orig_charset
=
1522 g_strdup(procmime_mimeinfo_get_parameter(
1523 partinfo
, "charset"));
1525 procmime_mimeinfo_free_all(mimeinfo
);
1529 #define SIGNAL_BLOCK(buffer) { \
1530 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1531 G_CALLBACK(compose_changed_cb), \
1533 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1534 G_CALLBACK(text_inserted), \
1538 #define SIGNAL_UNBLOCK(buffer) { \
1539 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1540 G_CALLBACK(compose_changed_cb), \
1542 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1543 G_CALLBACK(text_inserted), \
1547 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1548 ComposeQuoteMode quote_mode
,
1549 gboolean to_all
, gboolean to_ml
,
1551 gboolean followup_and_reply_to
,
1555 PrefsAccount
*account
= NULL
;
1556 GtkTextView
*textview
;
1557 GtkTextBuffer
*textbuf
;
1558 gboolean quote
= FALSE
;
1559 const gchar
*qmark
= NULL
;
1560 const gchar
*body_fmt
= NULL
;
1561 gchar
*s_system
= NULL
;
1563 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1564 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1566 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1568 cm_return_val_if_fail(account
!= NULL
, NULL
);
1570 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1572 compose
->updating
= TRUE
;
1574 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1575 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1577 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1578 if (!compose
->replyinfo
)
1579 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1581 compose_extract_original_charset(compose
);
1583 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1584 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1586 /* Set save folder */
1587 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1588 gchar
*folderidentifier
;
1590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1591 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1592 compose_set_save_to(compose
, folderidentifier
);
1593 g_free(folderidentifier
);
1596 if (compose_parse_header(compose
, msginfo
) < 0) {
1597 compose
->updating
= FALSE
;
1598 compose_destroy(compose
);
1602 /* override from name according to folder properties */
1603 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1604 msginfo
->folder
->prefs
->reply_with_format
&&
1605 msginfo
->folder
->prefs
->reply_override_from_format
&&
1606 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1611 /* decode \-escape sequences in the internal representation of the quote format */
1612 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1613 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1616 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1617 compose
->gtkaspell
);
1619 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1621 quote_fmt_scan_string(tmp
);
1624 buf
= quote_fmt_get_buffer();
1626 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1628 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1629 quote_fmt_reset_vartable();
1634 textview
= (GTK_TEXT_VIEW(compose
->text
));
1635 textbuf
= gtk_text_view_get_buffer(textview
);
1636 compose_create_tags(textview
, compose
);
1638 undo_block(compose
->undostruct
);
1640 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1641 gtkaspell_block_check(compose
->gtkaspell
);
1644 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1645 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1646 /* use the reply format of folder (if enabled), or the account's one
1647 (if enabled) or fallback to the global reply format, which is always
1648 enabled (even if empty), and use the relevant quotemark */
1650 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1651 msginfo
->folder
->prefs
->reply_with_format
) {
1652 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1653 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1655 } else if (account
->reply_with_format
) {
1656 qmark
= account
->reply_quotemark
;
1657 body_fmt
= account
->reply_body_format
;
1660 qmark
= prefs_common
.quotemark
;
1661 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1662 body_fmt
= gettext(prefs_common
.quotefmt
);
1669 /* empty quotemark is not allowed */
1670 if (qmark
== NULL
|| *qmark
== '\0')
1672 compose_quote_fmt(compose
, compose
->replyinfo
,
1673 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1674 _("The body of the \"Reply\" template has an error at line %d."));
1675 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1676 quote_fmt_reset_vartable();
1679 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1680 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1683 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1684 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1685 compose_force_signing(compose
, account
, s_system
);
1689 SIGNAL_BLOCK(textbuf
);
1691 if (account
->auto_sig
)
1692 compose_insert_sig(compose
, FALSE
);
1694 compose_wrap_all(compose
);
1697 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1698 gtkaspell_highlight_all(compose
->gtkaspell
);
1699 gtkaspell_unblock_check(compose
->gtkaspell
);
1701 SIGNAL_UNBLOCK(textbuf
);
1703 gtk_widget_grab_focus(compose
->text
);
1705 undo_unblock(compose
->undostruct
);
1707 if (prefs_common
.auto_exteditor
)
1708 compose_exec_ext_editor(compose
);
1710 compose
->modified
= FALSE
;
1711 compose_set_title(compose
);
1713 compose
->updating
= FALSE
;
1714 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1715 SCROLL_TO_CURSOR(compose
);
1717 if (compose
->deferred_destroy
) {
1718 compose_destroy(compose
);
1726 #define INSERT_FW_HEADER(var, hdr) \
1727 if (msginfo->var && *msginfo->var) { \
1728 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1729 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1730 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1733 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1734 gboolean as_attach
, const gchar
*body
,
1735 gboolean no_extedit
,
1739 GtkTextView
*textview
;
1740 GtkTextBuffer
*textbuf
;
1741 gint cursor_pos
= -1;
1744 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1745 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1748 !(account
= compose_guess_forward_account_from_msginfo
1750 account
= cur_account
;
1752 if (!prefs_common
.forward_as_attachment
)
1753 mode
= COMPOSE_FORWARD_INLINE
;
1755 mode
= COMPOSE_FORWARD
;
1756 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1758 compose
->updating
= TRUE
;
1759 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1760 if (!compose
->fwdinfo
)
1761 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1763 compose_extract_original_charset(compose
);
1765 if (msginfo
->subject
&& *msginfo
->subject
) {
1766 gchar
*buf
, *buf2
, *p
;
1768 buf
= p
= g_strdup(msginfo
->subject
);
1769 p
+= subject_get_prefix_length(p
);
1770 memmove(buf
, p
, strlen(p
) + 1);
1772 buf2
= g_strdup_printf("Fw: %s", buf
);
1773 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1779 /* override from name according to folder properties */
1780 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1781 msginfo
->folder
->prefs
->forward_with_format
&&
1782 msginfo
->folder
->prefs
->forward_override_from_format
&&
1783 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1787 MsgInfo
*full_msginfo
= NULL
;
1790 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1792 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1794 /* decode \-escape sequences in the internal representation of the quote format */
1795 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1796 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1799 gtkaspell_block_check(compose
->gtkaspell
);
1800 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1801 compose
->gtkaspell
);
1803 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1805 quote_fmt_scan_string(tmp
);
1808 buf
= quote_fmt_get_buffer();
1810 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1812 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1813 quote_fmt_reset_vartable();
1816 procmsg_msginfo_free(full_msginfo
);
1819 textview
= GTK_TEXT_VIEW(compose
->text
);
1820 textbuf
= gtk_text_view_get_buffer(textview
);
1821 compose_create_tags(textview
, compose
);
1823 undo_block(compose
->undostruct
);
1827 msgfile
= procmsg_get_message_file(msginfo
);
1828 if (!is_file_exist(msgfile
))
1829 g_warning("%s: file not exist\n", msgfile
);
1831 compose_attach_append(compose
, msgfile
, msgfile
,
1832 "message/rfc822", NULL
);
1836 const gchar
*qmark
= NULL
;
1837 const gchar
*body_fmt
= NULL
;
1838 MsgInfo
*full_msginfo
;
1840 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1842 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1844 /* use the forward format of folder (if enabled), or the account's one
1845 (if enabled) or fallback to the global forward format, which is always
1846 enabled (even if empty), and use the relevant quotemark */
1847 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1848 msginfo
->folder
->prefs
->forward_with_format
) {
1849 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1850 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1852 } else if (account
->forward_with_format
) {
1853 qmark
= account
->forward_quotemark
;
1854 body_fmt
= account
->forward_body_format
;
1857 qmark
= prefs_common
.fw_quotemark
;
1858 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1859 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1864 /* empty quotemark is not allowed */
1865 if (qmark
== NULL
|| *qmark
== '\0')
1868 compose_quote_fmt(compose
, full_msginfo
,
1869 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1870 _("The body of the \"Forward\" template has an error at line %d."));
1871 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1872 quote_fmt_reset_vartable();
1873 compose_attach_parts(compose
, msginfo
);
1875 procmsg_msginfo_free(full_msginfo
);
1878 SIGNAL_BLOCK(textbuf
);
1880 if (account
->auto_sig
)
1881 compose_insert_sig(compose
, FALSE
);
1883 compose_wrap_all(compose
);
1886 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1887 gtkaspell_highlight_all(compose
->gtkaspell
);
1888 gtkaspell_unblock_check(compose
->gtkaspell
);
1890 SIGNAL_UNBLOCK(textbuf
);
1892 cursor_pos
= quote_fmt_get_cursor_pos();
1893 if (cursor_pos
== -1)
1894 gtk_widget_grab_focus(compose
->header_last
->entry
);
1896 gtk_widget_grab_focus(compose
->text
);
1898 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1899 compose_exec_ext_editor(compose
);
1902 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1903 gchar
*folderidentifier
;
1905 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1906 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1907 compose_set_save_to(compose
, folderidentifier
);
1908 g_free(folderidentifier
);
1911 undo_unblock(compose
->undostruct
);
1913 compose
->modified
= FALSE
;
1914 compose_set_title(compose
);
1916 compose
->updating
= FALSE
;
1917 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1918 SCROLL_TO_CURSOR(compose
);
1920 if (compose
->deferred_destroy
) {
1921 compose_destroy(compose
);
1925 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1930 #undef INSERT_FW_HEADER
1932 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1935 GtkTextView
*textview
;
1936 GtkTextBuffer
*textbuf
;
1940 gboolean single_mail
= TRUE
;
1942 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1944 if (g_slist_length(msginfo_list
) > 1)
1945 single_mail
= FALSE
;
1947 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1948 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1951 /* guess account from first selected message */
1953 !(account
= compose_guess_forward_account_from_msginfo
1954 (msginfo_list
->data
)))
1955 account
= cur_account
;
1957 cm_return_val_if_fail(account
!= NULL
, NULL
);
1959 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1960 if (msginfo
->data
) {
1961 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1962 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1966 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1967 g_warning("no msginfo_list");
1971 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1973 compose
->updating
= TRUE
;
1975 /* override from name according to folder properties */
1976 if (msginfo_list
->data
) {
1977 MsgInfo
*msginfo
= msginfo_list
->data
;
1979 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1980 msginfo
->folder
->prefs
->forward_with_format
&&
1981 msginfo
->folder
->prefs
->forward_override_from_format
&&
1982 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1987 /* decode \-escape sequences in the internal representation of the quote format */
1988 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1989 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1992 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1993 compose
->gtkaspell
);
1995 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1997 quote_fmt_scan_string(tmp
);
2000 buf
= quote_fmt_get_buffer();
2002 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2004 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2005 quote_fmt_reset_vartable();
2011 textview
= GTK_TEXT_VIEW(compose
->text
);
2012 textbuf
= gtk_text_view_get_buffer(textview
);
2013 compose_create_tags(textview
, compose
);
2015 undo_block(compose
->undostruct
);
2016 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2017 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2019 if (!is_file_exist(msgfile
))
2020 g_warning("%s: file not exist\n", msgfile
);
2022 compose_attach_append(compose
, msgfile
, msgfile
,
2023 "message/rfc822", NULL
);
2028 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2029 if (info
->subject
&& *info
->subject
) {
2030 gchar
*buf
, *buf2
, *p
;
2032 buf
= p
= g_strdup(info
->subject
);
2033 p
+= subject_get_prefix_length(p
);
2034 memmove(buf
, p
, strlen(p
) + 1);
2036 buf2
= g_strdup_printf("Fw: %s", buf
);
2037 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2043 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2044 _("Fw: multiple emails"));
2047 SIGNAL_BLOCK(textbuf
);
2049 if (account
->auto_sig
)
2050 compose_insert_sig(compose
, FALSE
);
2052 compose_wrap_all(compose
);
2054 SIGNAL_UNBLOCK(textbuf
);
2056 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2057 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2059 gtk_widget_grab_focus(compose
->header_last
->entry
);
2060 undo_unblock(compose
->undostruct
);
2061 compose
->modified
= FALSE
;
2062 compose_set_title(compose
);
2064 compose
->updating
= FALSE
;
2065 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2066 SCROLL_TO_CURSOR(compose
);
2068 if (compose
->deferred_destroy
) {
2069 compose_destroy(compose
);
2073 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2078 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2080 GtkTextIter start
= *iter
;
2081 GtkTextIter end_iter
;
2082 int start_pos
= gtk_text_iter_get_offset(&start
);
2084 if (!compose
->account
->sig_sep
)
2087 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2088 start_pos
+strlen(compose
->account
->sig_sep
));
2090 /* check sig separator */
2091 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2092 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2094 /* check end of line (\n) */
2095 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2096 start_pos
+strlen(compose
->account
->sig_sep
));
2097 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2098 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2099 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2100 if (!strcmp(tmp
,"\n")) {
2112 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2114 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2115 Compose
*compose
= (Compose
*)data
;
2116 FolderItem
*old_item
= NULL
;
2117 FolderItem
*new_item
= NULL
;
2118 gchar
*old_id
, *new_id
;
2120 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2121 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2124 old_item
= hookdata
->item
;
2125 new_item
= hookdata
->item2
;
2127 old_id
= folder_item_get_identifier(old_item
);
2128 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2130 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2131 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2132 compose
->targetinfo
->folder
= new_item
;
2135 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2136 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2137 compose
->replyinfo
->folder
= new_item
;
2140 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2141 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2142 compose
->fwdinfo
->folder
= new_item
;
2150 static void compose_colorize_signature(Compose
*compose
)
2152 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2154 GtkTextIter end_iter
;
2155 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2156 while (gtk_text_iter_forward_line(&iter
))
2157 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2158 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2159 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2163 #define BLOCK_WRAP() { \
2164 prev_autowrap = compose->autowrap; \
2165 buffer = gtk_text_view_get_buffer( \
2166 GTK_TEXT_VIEW(compose->text)); \
2167 compose->autowrap = FALSE; \
2169 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2170 G_CALLBACK(compose_changed_cb), \
2172 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2173 G_CALLBACK(text_inserted), \
2176 #define UNBLOCK_WRAP() { \
2177 compose->autowrap = prev_autowrap; \
2178 if (compose->autowrap) { \
2179 gint old = compose->draft_timeout_tag; \
2180 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2181 compose_wrap_all(compose); \
2182 compose->draft_timeout_tag = old; \
2185 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2186 G_CALLBACK(compose_changed_cb), \
2188 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2189 G_CALLBACK(text_inserted), \
2193 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2195 Compose
*compose
= NULL
;
2196 PrefsAccount
*account
= NULL
;
2197 GtkTextView
*textview
;
2198 GtkTextBuffer
*textbuf
;
2202 gchar buf
[BUFFSIZE
];
2203 gboolean use_signing
= FALSE
;
2204 gboolean use_encryption
= FALSE
;
2205 gchar
*privacy_system
= NULL
;
2206 int priority
= PRIORITY_NORMAL
;
2207 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2208 gboolean autowrap
= prefs_common
.autowrap
;
2209 gboolean autoindent
= prefs_common
.auto_indent
;
2210 HeaderEntry
*manual_headers
= NULL
;
2212 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2213 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2215 if (compose_put_existing_to_front(msginfo
)) {
2219 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2220 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2221 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2222 gchar queueheader_buf
[BUFFSIZE
];
2225 /* Select Account from queue headers */
2226 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2227 sizeof(queueheader_buf
), "X-Claws-Account-Id:")) {
2228 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2229 account
= account_find_from_id(id
);
2231 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2232 sizeof(queueheader_buf
), "X-Sylpheed-Account-Id:")) {
2233 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2234 account
= account_find_from_id(id
);
2236 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2237 sizeof(queueheader_buf
), "NAID:")) {
2238 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2239 account
= account_find_from_id(id
);
2241 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2242 sizeof(queueheader_buf
), "MAID:")) {
2243 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2244 account
= account_find_from_id(id
);
2246 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2247 sizeof(queueheader_buf
), "S:")) {
2248 account
= account_find_from_address(queueheader_buf
, FALSE
);
2250 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2251 sizeof(queueheader_buf
), "X-Claws-Sign:")) {
2252 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2253 use_signing
= param
;
2256 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2257 sizeof(queueheader_buf
), "X-Sylpheed-Sign:")) {
2258 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2259 use_signing
= param
;
2262 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2263 sizeof(queueheader_buf
), "X-Claws-Encrypt:")) {
2264 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2265 use_encryption
= param
;
2267 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2268 sizeof(queueheader_buf
), "X-Sylpheed-Encrypt:")) {
2269 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2270 use_encryption
= param
;
2272 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2273 sizeof(queueheader_buf
), "X-Claws-Auto-Wrapping:")) {
2274 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2277 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2278 sizeof(queueheader_buf
), "X-Claws-Auto-Indent:")) {
2279 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2282 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2283 sizeof(queueheader_buf
), "X-Claws-Privacy-System:")) {
2284 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2286 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2287 sizeof(queueheader_buf
), "X-Sylpheed-Privacy-System:")) {
2288 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2290 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2291 sizeof(queueheader_buf
), "X-Priority: ")) {
2292 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2295 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2296 sizeof(queueheader_buf
), "RMID:")) {
2297 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2298 if (tokens
[0] && tokens
[1] && tokens
[2]) {
2299 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2300 if (orig_item
!= NULL
) {
2301 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2306 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
,
2307 sizeof(queueheader_buf
), "FMID:")) {
2308 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2309 if (tokens
[0] && tokens
[1] && tokens
[2]) {
2310 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2311 if (orig_item
!= NULL
) {
2312 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2317 /* Get manual headers */
2318 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "X-Claws-Manual-Headers:")) {
2319 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2320 if (*listmh
!= '\0') {
2321 debug_print("Got manual headers: %s\n", listmh
);
2322 manual_headers
= procheader_entries_from_str(listmh
);
2327 account
= msginfo
->folder
->folder
->account
;
2330 if (!account
&& prefs_common
.reedit_account_autosel
) {
2331 gchar from
[BUFFSIZE
];
2332 if (!procheader_get_header_from_msginfo(msginfo
, from
, sizeof(from
), "FROM:")) {
2333 extract_address(from
);
2334 account
= account_find_from_address(from
, FALSE
);
2338 account
= cur_account
;
2340 cm_return_val_if_fail(account
!= NULL
, NULL
);
2342 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2344 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2345 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2346 compose
->autowrap
= autowrap
;
2347 compose
->replyinfo
= replyinfo
;
2348 compose
->fwdinfo
= fwdinfo
;
2350 compose
->updating
= TRUE
;
2351 compose
->priority
= priority
;
2353 if (privacy_system
!= NULL
) {
2354 compose
->privacy_system
= privacy_system
;
2355 compose_use_signing(compose
, use_signing
);
2356 compose_use_encryption(compose
, use_encryption
);
2357 compose_update_privacy_system_menu_item(compose
, FALSE
);
2359 activate_privacy_system(compose
, account
, FALSE
);
2362 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2364 compose_extract_original_charset(compose
);
2366 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2367 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2368 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2369 gchar queueheader_buf
[BUFFSIZE
];
2371 /* Set message save folder */
2372 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "SCF:")) {
2373 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2374 compose_set_save_to(compose
, &queueheader_buf
[4]);
2376 if (!procheader_get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "RRCPT:")) {
2377 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2379 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2384 if (compose_parse_header(compose
, msginfo
) < 0) {
2385 compose
->updating
= FALSE
;
2386 compose_destroy(compose
);
2389 compose_reedit_set_entry(compose
, msginfo
);
2391 textview
= GTK_TEXT_VIEW(compose
->text
);
2392 textbuf
= gtk_text_view_get_buffer(textview
);
2393 compose_create_tags(textview
, compose
);
2395 mark
= gtk_text_buffer_get_insert(textbuf
);
2396 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2398 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2399 G_CALLBACK(compose_changed_cb
),
2402 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2403 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2405 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2408 fp
= procmime_get_first_text_content(msginfo
);
2411 g_warning("Can't get text part\n");
2415 gboolean prev_autowrap
;
2416 GtkTextBuffer
*buffer
;
2418 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2420 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2426 compose_attach_parts(compose
, msginfo
);
2428 compose_colorize_signature(compose
);
2430 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2431 G_CALLBACK(compose_changed_cb
),
2434 if (manual_headers
!= NULL
) {
2435 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2436 procheader_entries_free(manual_headers
);
2437 compose
->updating
= FALSE
;
2438 compose_destroy(compose
);
2441 procheader_entries_free(manual_headers
);
2444 gtk_widget_grab_focus(compose
->text
);
2446 if (prefs_common
.auto_exteditor
) {
2447 compose_exec_ext_editor(compose
);
2449 compose
->modified
= FALSE
;
2450 compose_set_title(compose
);
2452 compose
->updating
= FALSE
;
2453 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2454 SCROLL_TO_CURSOR(compose
);
2456 if (compose
->deferred_destroy
) {
2457 compose_destroy(compose
);
2461 compose
->sig_str
= account_get_signature_str(compose
->account
);
2463 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2468 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2475 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2478 account
= account_get_reply_account(msginfo
,
2479 prefs_common
.reply_account_autosel
);
2480 cm_return_val_if_fail(account
!= NULL
, NULL
);
2482 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2484 compose
->updating
= TRUE
;
2486 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2487 compose
->replyinfo
= NULL
;
2488 compose
->fwdinfo
= NULL
;
2490 compose_show_first_last_header(compose
, TRUE
);
2492 gtk_widget_grab_focus(compose
->header_last
->entry
);
2494 filename
= procmsg_get_message_file(msginfo
);
2496 if (filename
== NULL
) {
2497 compose
->updating
= FALSE
;
2498 compose_destroy(compose
);
2503 compose
->redirect_filename
= filename
;
2505 /* Set save folder */
2506 item
= msginfo
->folder
;
2507 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2508 gchar
*folderidentifier
;
2510 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
2511 folderidentifier
= folder_item_get_identifier(item
);
2512 compose_set_save_to(compose
, folderidentifier
);
2513 g_free(folderidentifier
);
2516 compose_attach_parts(compose
, msginfo
);
2518 if (msginfo
->subject
)
2519 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2521 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2523 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2524 _("The body of the \"Redirect\" template has an error at line %d."));
2525 quote_fmt_reset_vartable();
2526 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2528 compose_colorize_signature(compose
);
2531 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2532 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2533 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2535 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2536 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2537 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2538 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2539 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2540 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2541 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2542 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2543 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2545 if (compose
->toolbar
->draft_btn
)
2546 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2547 if (compose
->toolbar
->insert_btn
)
2548 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2549 if (compose
->toolbar
->attach_btn
)
2550 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2551 if (compose
->toolbar
->sig_btn
)
2552 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2553 if (compose
->toolbar
->exteditor_btn
)
2554 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2555 if (compose
->toolbar
->linewrap_current_btn
)
2556 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2557 if (compose
->toolbar
->linewrap_all_btn
)
2558 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2560 compose
->modified
= FALSE
;
2561 compose_set_title(compose
);
2562 compose
->updating
= FALSE
;
2563 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2564 SCROLL_TO_CURSOR(compose
);
2566 if (compose
->deferred_destroy
) {
2567 compose_destroy(compose
);
2571 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2576 const GList
*compose_get_compose_list(void)
2578 return compose_list
;
2581 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2582 ComposeEntryType type
, ComposePrefType pref_type
)
2584 const gchar
*header
;
2586 gboolean in_quote
= FALSE
;
2587 if (!address
|| *address
== '\0') return;
2594 header
= N_("Bcc:");
2596 case COMPOSE_REPLYTO
:
2597 header
= N_("Reply-To:");
2599 case COMPOSE_NEWSGROUPS
:
2600 header
= N_("Newsgroups:");
2602 case COMPOSE_FOLLOWUPTO
:
2603 header
= N_( "Followup-To:");
2605 case COMPOSE_INREPLYTO
:
2606 header
= N_( "In-Reply-To:");
2613 header
= prefs_common_translated_header_name(header
);
2615 cur
= begin
= (gchar
*)address
;
2617 /* we separate the line by commas, but not if we're inside a quoted
2619 while (*cur
!= '\0') {
2621 in_quote
= !in_quote
;
2622 if (*cur
== ',' && !in_quote
) {
2623 gchar
*tmp
= g_strdup(begin
);
2625 tmp
[cur
-begin
]='\0';
2628 while (*tmp
== ' ' || *tmp
== '\t')
2630 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2637 gchar
*tmp
= g_strdup(begin
);
2639 tmp
[cur
-begin
]='\0';
2640 while (*tmp
== ' ' || *tmp
== '\t')
2642 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2647 static void compose_entry_mark_default_to(Compose
*compose
, const gchar
*mailto
)
2649 #if !GTK_CHECK_VERSION(3, 0, 0)
2650 static GdkColor yellow
;
2651 static GdkColor black
;
2652 static gboolean yellow_initialised
= FALSE
;
2654 static GdkColor yellow
= { (guint32
)0, (guint16
)0xf5, (guint16
)0xf6, (guint16
)0xbe };
2655 static GdkColor black
= { (guint32
)0, (guint16
)0x0, (guint16
)0x0, (guint16
)0x0 };
2660 #if !GTK_CHECK_VERSION(3, 0, 0)
2661 if (!yellow_initialised
) {
2662 gdk_color_parse("#f5f6be", &yellow
);
2663 gdk_color_parse("#000000", &black
);
2664 yellow_initialised
= gdk_colormap_alloc_color(
2665 gdk_colormap_get_system(), &yellow
, FALSE
, TRUE
);
2666 yellow_initialised
&= gdk_colormap_alloc_color(
2667 gdk_colormap_get_system(), &black
, FALSE
, TRUE
);
2671 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2672 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2673 if (gtk_entry_get_text(entry
) &&
2674 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2675 #if !GTK_CHECK_VERSION(3, 0, 0)
2676 if (yellow_initialised
) {
2678 gtk_widget_modify_base(
2679 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2680 GTK_STATE_NORMAL
, &yellow
);
2681 gtk_widget_modify_text(
2682 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2683 GTK_STATE_NORMAL
, &black
);
2684 #if !GTK_CHECK_VERSION(3, 0, 0)
2691 void compose_toolbar_cb(gint action
, gpointer data
)
2693 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2694 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2696 cm_return_if_fail(compose
!= NULL
);
2700 compose_send_cb(NULL
, compose
);
2703 compose_send_later_cb(NULL
, compose
);
2706 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2709 compose_insert_file_cb(NULL
, compose
);
2712 compose_attach_cb(NULL
, compose
);
2715 compose_insert_sig(compose
, FALSE
);
2718 compose_insert_sig(compose
, TRUE
);
2721 compose_ext_editor_cb(NULL
, compose
);
2723 case A_LINEWRAP_CURRENT
:
2724 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2726 case A_LINEWRAP_ALL
:
2727 compose_wrap_all_full(compose
, TRUE
);
2730 compose_address_cb(NULL
, compose
);
2733 case A_CHECK_SPELLING
:
2734 compose_check_all(NULL
, compose
);
2742 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2747 gchar
*subject
= NULL
;
2751 gchar
**attach
= NULL
;
2752 gchar
*inreplyto
= NULL
;
2753 MailField mfield
= NO_FIELD_PRESENT
;
2755 /* get mailto parts but skip from */
2756 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2759 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2760 mfield
= TO_FIELD_PRESENT
;
2763 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2765 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2767 if (!g_utf8_validate (subject
, -1, NULL
)) {
2768 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2769 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2772 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2774 mfield
= SUBJECT_FIELD_PRESENT
;
2777 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2778 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2781 gboolean prev_autowrap
= compose
->autowrap
;
2783 compose
->autowrap
= FALSE
;
2785 mark
= gtk_text_buffer_get_insert(buffer
);
2786 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2788 if (!g_utf8_validate (body
, -1, NULL
)) {
2789 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2790 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2793 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2795 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2797 compose
->autowrap
= prev_autowrap
;
2798 if (compose
->autowrap
)
2799 compose_wrap_all(compose
);
2800 mfield
= BODY_FIELD_PRESENT
;
2804 gint i
= 0, att
= 0;
2805 gchar
*warn_files
= NULL
;
2806 while (attach
[i
] != NULL
) {
2807 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2808 if (utf8_filename
) {
2809 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2810 gchar
*tmp
= g_strdup_printf("%s%s\n",
2811 warn_files
?warn_files
:"",
2817 g_free(utf8_filename
);
2819 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2824 alertpanel_notice(ngettext(
2825 "The following file has been attached: \n%s",
2826 "The following files have been attached: \n%s", att
), warn_files
);
2831 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2844 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2846 static HeaderEntry hentry
[] = {{"Reply-To:", NULL
, TRUE
},
2847 {"Cc:", NULL
, TRUE
},
2848 {"References:", NULL
, FALSE
},
2849 {"Bcc:", NULL
, TRUE
},
2850 {"Newsgroups:", NULL
, TRUE
},
2851 {"Followup-To:", NULL
, TRUE
},
2852 {"List-Post:", NULL
, FALSE
},
2853 {"X-Priority:", NULL
, FALSE
},
2854 {NULL
, NULL
, FALSE
}};
2870 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2872 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
2873 procheader_get_header_fields(fp
, hentry
);
2876 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2877 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2879 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2882 g_free(hentry
[H_REPLY_TO
].body
);
2883 hentry
[H_REPLY_TO
].body
= NULL
;
2885 if (hentry
[H_CC
].body
!= NULL
) {
2886 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2887 g_free(hentry
[H_CC
].body
);
2888 hentry
[H_CC
].body
= NULL
;
2890 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2891 if (compose
->mode
== COMPOSE_REEDIT
)
2892 compose
->references
= hentry
[H_REFERENCES
].body
;
2894 compose
->references
= compose_parse_references
2895 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2896 g_free(hentry
[H_REFERENCES
].body
);
2898 hentry
[H_REFERENCES
].body
= NULL
;
2900 if (hentry
[H_BCC
].body
!= NULL
) {
2901 if (compose
->mode
== COMPOSE_REEDIT
)
2903 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2904 g_free(hentry
[H_BCC
].body
);
2905 hentry
[H_BCC
].body
= NULL
;
2907 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2908 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2909 hentry
[H_NEWSGROUPS
].body
= NULL
;
2911 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2912 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2913 compose
->followup_to
=
2914 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2917 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2918 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2920 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2921 gchar
*to
= NULL
, *start
= NULL
;
2923 extract_address(hentry
[H_LIST_POST
].body
);
2924 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2925 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2927 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2928 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2931 g_free(compose
->ml_post
);
2932 compose
->ml_post
= to
;
2935 g_free(hentry
[H_LIST_POST
].body
);
2936 hentry
[H_LIST_POST
].body
= NULL
;
2939 /* CLAWS - X-Priority */
2940 if (compose
->mode
== COMPOSE_REEDIT
)
2941 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2944 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2945 g_free(hentry
[H_X_PRIORITY
].body
);
2947 hentry
[H_X_PRIORITY
].body
= NULL
;
2949 if (priority
< PRIORITY_HIGHEST
||
2950 priority
> PRIORITY_LOWEST
)
2951 priority
= PRIORITY_NORMAL
;
2953 compose
->priority
= priority
;
2956 if (compose
->mode
== COMPOSE_REEDIT
) {
2957 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2958 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
2962 if (msginfo
->msgid
&& *msginfo
->msgid
)
2963 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
2965 if (!compose
->references
) {
2966 if (msginfo
->msgid
&& *msginfo
->msgid
) {
2967 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2968 compose
->references
=
2969 g_strdup_printf("<%s>\n\t<%s>",
2973 compose
->references
=
2974 g_strconcat("<", msginfo
->msgid
, ">",
2976 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
2977 compose
->references
=
2978 g_strconcat("<", msginfo
->inreplyto
, ">",
2986 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
2991 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2993 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
2994 procheader_get_header_fields(fp
, entries
);
2998 while (he
!= NULL
&& he
->name
!= NULL
) {
3000 GtkListStore
*model
= NULL
;
3002 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3003 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3004 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3005 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3006 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3013 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3015 GSList
*ref_id_list
, *cur
;
3019 ref_id_list
= references_list_append(NULL
, ref
);
3020 if (!ref_id_list
) return NULL
;
3021 if (msgid
&& *msgid
)
3022 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3027 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3028 /* "<" + Message-ID + ">" + CR+LF+TAB */
3029 len
+= strlen((gchar
*)cur
->data
) + 5;
3031 if (len
> MAX_REFERENCES_LEN
) {
3032 /* remove second message-ID */
3033 if (ref_id_list
&& ref_id_list
->next
&&
3034 ref_id_list
->next
->next
) {
3035 g_free(ref_id_list
->next
->data
);
3036 ref_id_list
= g_slist_remove
3037 (ref_id_list
, ref_id_list
->next
->data
);
3039 slist_free_strings_full(ref_id_list
);
3046 new_ref
= g_string_new("");
3047 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3048 if (new_ref
->len
> 0)
3049 g_string_append(new_ref
, "\n\t");
3050 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3053 slist_free_strings_full(ref_id_list
);
3055 new_ref_str
= new_ref
->str
;
3056 g_string_free(new_ref
, FALSE
);
3061 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3062 const gchar
*fmt
, const gchar
*qmark
,
3063 const gchar
*body
, gboolean rewrap
,
3064 gboolean need_unescape
,
3065 const gchar
*err_msg
)
3067 MsgInfo
* dummyinfo
= NULL
;
3068 gchar
*quote_str
= NULL
;
3070 gboolean prev_autowrap
;
3071 const gchar
*trimmed_body
= body
;
3072 gint cursor_pos
= -1;
3073 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3074 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3079 SIGNAL_BLOCK(buffer
);
3082 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3083 msginfo
= dummyinfo
;
3086 if (qmark
!= NULL
) {
3088 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3089 compose
->gtkaspell
);
3091 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3093 quote_fmt_scan_string(qmark
);
3096 buf
= quote_fmt_get_buffer();
3098 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3100 Xstrdup_a(quote_str
, buf
, goto error
)
3103 if (fmt
&& *fmt
!= '\0') {
3106 while (*trimmed_body
== '\n')
3110 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3111 compose
->gtkaspell
);
3113 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3115 if (need_unescape
) {
3118 /* decode \-escape sequences in the internal representation of the quote format */
3119 tmp
= g_malloc(strlen(fmt
)+1);
3120 pref_get_unescaped_pref(tmp
, fmt
);
3121 quote_fmt_scan_string(tmp
);
3125 quote_fmt_scan_string(fmt
);
3129 buf
= quote_fmt_get_buffer();
3131 gint line
= quote_fmt_get_line();
3132 alertpanel_error(err_msg
, line
);
3138 prev_autowrap
= compose
->autowrap
;
3139 compose
->autowrap
= FALSE
;
3141 mark
= gtk_text_buffer_get_insert(buffer
);
3142 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3143 if (g_utf8_validate(buf
, -1, NULL
)) {
3144 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3146 gchar
*tmpout
= NULL
;
3147 tmpout
= conv_codeset_strdup
3148 (buf
, conv_get_locale_charset_str_no_utf8(),
3150 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3152 tmpout
= g_malloc(strlen(buf
)*2+1);
3153 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3155 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3159 cursor_pos
= quote_fmt_get_cursor_pos();
3160 if (cursor_pos
== -1)
3161 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3162 compose
->set_cursor_pos
= cursor_pos
;
3164 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3165 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3166 gtk_text_buffer_place_cursor(buffer
, &iter
);
3168 compose
->autowrap
= prev_autowrap
;
3169 if (compose
->autowrap
&& rewrap
)
3170 compose_wrap_all(compose
);
3177 SIGNAL_UNBLOCK(buffer
);
3179 procmsg_msginfo_free( dummyinfo
);
3184 /* if ml_post is of type addr@host and from is of type
3185 * addr-anything@host, return TRUE
3187 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3189 gchar
*left_ml
= NULL
;
3190 gchar
*right_ml
= NULL
;
3191 gchar
*left_from
= NULL
;
3192 gchar
*right_from
= NULL
;
3193 gboolean result
= FALSE
;
3195 if (!ml_post
|| !from
)
3198 left_ml
= g_strdup(ml_post
);
3199 if (strstr(left_ml
, "@")) {
3200 right_ml
= strstr(left_ml
, "@")+1;
3201 *(strstr(left_ml
, "@")) = '\0';
3204 left_from
= g_strdup(from
);
3205 if (strstr(left_from
, "@")) {
3206 right_from
= strstr(left_from
, "@")+1;
3207 *(strstr(left_from
, "@")) = '\0';
3210 if (right_ml
&& right_from
3211 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3212 && !strcmp(right_from
, right_ml
)) {
3221 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3222 gboolean respect_default_to
)
3226 if (!folder
|| !folder
->prefs
)
3229 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3230 compose_entry_append(compose
, folder
->prefs
->default_to
,
3231 COMPOSE_TO
, PREF_FOLDER
);
3232 compose_entry_mark_default_to(compose
, folder
->prefs
->default_to
);
3234 if (folder
->prefs
->enable_default_cc
)
3235 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3236 COMPOSE_CC
, PREF_FOLDER
);
3237 if (folder
->prefs
->enable_default_bcc
)
3238 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3239 COMPOSE_BCC
, PREF_FOLDER
);
3240 if (folder
->prefs
->enable_default_replyto
)
3241 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3242 COMPOSE_REPLYTO
, PREF_FOLDER
);
3245 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3250 if (!compose
|| !msginfo
)
3253 if (msginfo
->subject
&& *msginfo
->subject
) {
3254 buf
= p
= g_strdup(msginfo
->subject
);
3255 p
+= subject_get_prefix_length(p
);
3256 memmove(buf
, p
, strlen(p
) + 1);
3258 buf2
= g_strdup_printf("Re: %s", buf
);
3259 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3264 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3267 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3268 gboolean to_all
, gboolean to_ml
,
3270 gboolean followup_and_reply_to
)
3272 GSList
*cc_list
= NULL
;
3275 gchar
*replyto
= NULL
;
3276 gchar
*ac_email
= NULL
;
3278 gboolean reply_to_ml
= FALSE
;
3279 gboolean default_reply_to
= FALSE
;
3281 cm_return_if_fail(compose
->account
!= NULL
);
3282 cm_return_if_fail(msginfo
!= NULL
);
3284 reply_to_ml
= to_ml
&& compose
->ml_post
;
3286 default_reply_to
= msginfo
->folder
&&
3287 msginfo
->folder
->prefs
->enable_default_reply_to
;
3289 if (compose
->account
->protocol
!= A_NNTP
) {
3290 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3292 if (reply_to_ml
&& !default_reply_to
) {
3294 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3297 /* normal answer to ml post with a reply-to */
3298 compose_entry_append(compose
,
3300 COMPOSE_TO
, PREF_ML
);
3301 if (compose
->replyto
)
3302 compose_entry_append(compose
,
3304 COMPOSE_CC
, PREF_ML
);
3306 /* answer to subscription confirmation */
3307 if (compose
->replyto
)
3308 compose_entry_append(compose
,
3310 COMPOSE_TO
, PREF_ML
);
3311 else if (msginfo
->from
)
3312 compose_entry_append(compose
,
3314 COMPOSE_TO
, PREF_ML
);
3317 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3318 compose_entry_append(compose
,
3319 msginfo
->folder
->prefs
->default_reply_to
,
3320 COMPOSE_TO
, PREF_FOLDER
);
3321 compose_entry_mark_default_to(compose
,
3322 msginfo
->folder
->prefs
->default_reply_to
);
3328 compose_entry_append(compose
, msginfo
->from
,
3329 COMPOSE_TO
, PREF_NONE
);
3331 Xstrdup_a(tmp1
, msginfo
->from
, return);
3332 extract_address(tmp1
);
3333 compose_entry_append(compose
,
3334 (!account_find_from_address(tmp1
, FALSE
))
3337 COMPOSE_TO
, PREF_NONE
);
3339 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3340 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3341 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3342 if (compose
->replyto
) {
3343 compose_entry_append(compose
,
3345 COMPOSE_TO
, PREF_NONE
);
3347 compose_entry_append(compose
,
3348 msginfo
->from
? msginfo
->from
: "",
3349 COMPOSE_TO
, PREF_NONE
);
3352 /* replying to own mail, use original recp */
3353 compose_entry_append(compose
,
3354 msginfo
->to
? msginfo
->to
: "",
3355 COMPOSE_TO
, PREF_NONE
);
3356 compose_entry_append(compose
,
3357 msginfo
->cc
? msginfo
->cc
: "",
3358 COMPOSE_CC
, PREF_NONE
);
3363 if (to_sender
|| (compose
->followup_to
&&
3364 !strncmp(compose
->followup_to
, "poster", 6)))
3365 compose_entry_append
3367 (compose
->replyto
? compose
->replyto
:
3368 msginfo
->from
? msginfo
->from
: ""),
3369 COMPOSE_TO
, PREF_NONE
);
3371 else if (followup_and_reply_to
|| to_all
) {
3372 compose_entry_append
3374 (compose
->replyto
? compose
->replyto
:
3375 msginfo
->from
? msginfo
->from
: ""),
3376 COMPOSE_TO
, PREF_NONE
);
3378 compose_entry_append
3380 compose
->followup_to
? compose
->followup_to
:
3381 compose
->newsgroups
? compose
->newsgroups
: "",
3382 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3385 compose_entry_append
3387 compose
->followup_to
? compose
->followup_to
:
3388 compose
->newsgroups
? compose
->newsgroups
: "",
3389 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3391 compose_reply_set_subject(compose
, msginfo
);
3393 if (to_ml
&& compose
->ml_post
) return;
3394 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3396 if (compose
->replyto
) {
3397 Xstrdup_a(replyto
, compose
->replyto
, return);
3398 extract_address(replyto
);
3400 if (msginfo
->from
) {
3401 Xstrdup_a(from
, msginfo
->from
, return);
3402 extract_address(from
);
3405 if (replyto
&& from
)
3406 cc_list
= address_list_append_with_comments(cc_list
, from
);
3407 if (to_all
&& msginfo
->folder
&&
3408 msginfo
->folder
->prefs
->enable_default_reply_to
)
3409 cc_list
= address_list_append_with_comments(cc_list
,
3410 msginfo
->folder
->prefs
->default_reply_to
);
3411 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3412 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3414 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3417 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3418 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3419 extract_address(addr
);
3421 if (strcmp(ac_email
, addr
))
3422 compose_entry_append(compose
, (gchar
*)cur
->data
,
3423 COMPOSE_CC
, PREF_NONE
);
3425 debug_print("Cc address same as compose account's, ignoring\n");
3430 slist_free_strings_full(cc_list
);
3436 #define SET_ENTRY(entry, str) \
3439 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3442 #define SET_ADDRESS(type, str) \
3445 compose_entry_append(compose, str, type, PREF_NONE); \
3448 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3450 cm_return_if_fail(msginfo
!= NULL
);
3452 SET_ENTRY(subject_entry
, msginfo
->subject
);
3453 SET_ENTRY(from_name
, msginfo
->from
);
3454 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3455 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3456 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3457 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3458 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3459 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3461 compose_update_priority_menu_item(compose
);
3462 compose_update_privacy_system_menu_item(compose
, FALSE
);
3463 compose_show_first_last_header(compose
, TRUE
);
3469 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3471 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3472 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3474 GtkTextIter iter
, iter_end
;
3475 gint cur_pos
, ins_pos
;
3476 gboolean prev_autowrap
;
3477 gboolean found
= FALSE
;
3478 gboolean exists
= FALSE
;
3480 cm_return_if_fail(compose
->account
!= NULL
);
3484 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3485 G_CALLBACK(compose_changed_cb
),
3488 mark
= gtk_text_buffer_get_insert(buffer
);
3489 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3490 cur_pos
= gtk_text_iter_get_offset (&iter
);
3493 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3495 exists
= (compose
->sig_str
!= NULL
);
3498 GtkTextIter first_iter
, start_iter
, end_iter
;
3500 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3502 if (!exists
|| compose
->sig_str
[0] == '\0')
3505 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3506 compose
->signature_tag
);
3509 /* include previous \n\n */
3510 gtk_text_iter_backward_chars(&first_iter
, 1);
3511 start_iter
= first_iter
;
3512 end_iter
= first_iter
;
3514 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3515 compose
->signature_tag
);
3516 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3517 compose
->signature_tag
);
3519 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3525 g_free(compose
->sig_str
);
3526 compose
->sig_str
= account_get_signature_str(compose
->account
);
3528 cur_pos
= gtk_text_iter_get_offset(&iter
);
3530 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3531 g_free(compose
->sig_str
);
3532 compose
->sig_str
= NULL
;
3534 if (compose
->sig_inserted
== FALSE
)
3535 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3536 compose
->sig_inserted
= TRUE
;
3538 cur_pos
= gtk_text_iter_get_offset(&iter
);
3539 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3541 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3542 gtk_text_iter_forward_chars(&iter
, 1);
3543 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3544 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3546 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3547 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3550 /* put the cursor where it should be
3551 * either where the quote_fmt says, either where it was */
3552 if (compose
->set_cursor_pos
< 0)
3553 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3555 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3556 compose
->set_cursor_pos
);
3558 compose
->set_cursor_pos
= -1;
3559 gtk_text_buffer_place_cursor(buffer
, &iter
);
3560 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3561 G_CALLBACK(compose_changed_cb
),
3567 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3570 GtkTextBuffer
*buffer
;
3573 const gchar
*cur_encoding
;
3574 gchar buf
[BUFFSIZE
];
3577 gboolean prev_autowrap
;
3578 struct stat file_stat
;
3580 GString
*file_contents
= NULL
;
3582 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3584 /* get the size of the file we are about to insert */
3585 ret
= g_stat(file
, &file_stat
);
3587 gchar
*shortfile
= g_path_get_basename(file
);
3588 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3590 return COMPOSE_INSERT_NO_FILE
;
3591 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3593 /* ask user for confirmation if the file is large */
3594 if (prefs_common
.warn_large_insert_size
< 0 ||
3595 file_stat
.st_size
> (prefs_common
.warn_large_insert_size
* 1024)) {
3599 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3600 "in the message body. Are you sure you want to do that?"),
3601 to_human_readable(file_stat
.st_size
));
3602 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3603 _("+_Insert"), NULL
, TRUE
, NULL
, ALERT_QUESTION
, G_ALERTDEFAULT
);
3606 /* do we ask for confirmation next time? */
3607 if (aval
& G_ALERTDISABLE
) {
3608 /* no confirmation next time, disable feature in preferences */
3609 aval
&= ~G_ALERTDISABLE
;
3610 prefs_common
.warn_large_insert
= FALSE
;
3613 /* abort file insertion if user canceled action */
3614 if (aval
!= G_ALERTALTERNATE
) {
3615 return COMPOSE_INSERT_NO_FILE
;
3621 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
3622 FILE_OP_ERROR(file
, "fopen");
3623 return COMPOSE_INSERT_READ_ERROR
;
3626 prev_autowrap
= compose
->autowrap
;
3627 compose
->autowrap
= FALSE
;
3629 text
= GTK_TEXT_VIEW(compose
->text
);
3630 buffer
= gtk_text_view_get_buffer(text
);
3631 mark
= gtk_text_buffer_get_insert(buffer
);
3632 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3634 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3635 G_CALLBACK(text_inserted
),
3638 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3640 file_contents
= g_string_new("");
3641 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3644 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3645 str
= g_strdup(buf
);
3647 str
= conv_codeset_strdup
3648 (buf
, cur_encoding
, CS_INTERNAL
);
3651 /* strip <CR> if DOS/Windows file,
3652 replace <CR> with <LF> if Macintosh file. */
3655 if (len
> 0 && str
[len
- 1] != '\n') {
3657 if (str
[len
] == '\r') str
[len
] = '\n';
3660 file_contents
= g_string_append(file_contents
, str
);
3664 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3665 g_string_free(file_contents
, TRUE
);
3667 compose_changed_cb(NULL
, compose
);
3668 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3669 G_CALLBACK(text_inserted
),
3671 compose
->autowrap
= prev_autowrap
;
3672 if (compose
->autowrap
)
3673 compose_wrap_all(compose
);
3677 return COMPOSE_INSERT_SUCCESS
;
3680 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3681 const gchar
*filename
,
3682 const gchar
*content_type
,
3683 const gchar
*charset
)
3691 GtkListStore
*store
;
3693 gboolean has_binary
= FALSE
;
3695 if (!is_file_exist(file
)) {
3696 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3697 gboolean result
= FALSE
;
3698 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3699 result
= compose_attach_append(
3700 compose
, file_from_uri
,
3701 filename
, content_type
,
3704 g_free(file_from_uri
);
3707 alertpanel_error("File %s doesn't exist\n", filename
);
3710 if ((size
= get_file_size(file
)) < 0) {
3711 alertpanel_error("Can't get file size of %s\n", filename
);
3715 /* In batch mode, we allow 0-length files to be attached no questions asked */
3716 if (size
== 0 && !compose
->batch
) {
3717 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3718 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3719 GTK_STOCK_CANCEL
, _("+_Attach anyway"), NULL
, FALSE
,
3720 NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
3723 if (aval
!= G_ALERTALTERNATE
) {
3727 if ((fp
= g_fopen(file
, "rb")) == NULL
) {
3728 alertpanel_error(_("Can't read %s."), filename
);
3733 ainfo
= g_new0(AttachInfo
, 1);
3734 auto_ainfo
= g_auto_pointer_new_with_free
3735 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3736 ainfo
->file
= g_strdup(file
);
3739 ainfo
->content_type
= g_strdup(content_type
);
3740 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3742 MsgFlags flags
= {0, 0};
3744 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3745 ainfo
->encoding
= ENC_7BIT
;
3747 ainfo
->encoding
= ENC_8BIT
;
3749 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3750 if (msginfo
&& msginfo
->subject
)
3751 name
= g_strdup(msginfo
->subject
);
3753 name
= g_path_get_basename(filename
? filename
: file
);
3755 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3757 procmsg_msginfo_free(msginfo
);
3759 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3760 ainfo
->charset
= g_strdup(charset
);
3761 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3763 ainfo
->encoding
= ENC_BASE64
;
3765 name
= g_path_get_basename(filename
? filename
: file
);
3766 ainfo
->name
= g_strdup(name
);
3770 ainfo
->content_type
= procmime_get_mime_type(file
);
3771 if (!ainfo
->content_type
) {
3772 ainfo
->content_type
=
3773 g_strdup("application/octet-stream");
3774 ainfo
->encoding
= ENC_BASE64
;
3775 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3777 procmime_get_encoding_for_text_file(file
, &has_binary
);
3779 ainfo
->encoding
= ENC_BASE64
;
3780 name
= g_path_get_basename(filename
? filename
: file
);
3781 ainfo
->name
= g_strdup(name
);
3785 if (ainfo
->name
!= NULL
3786 && !strcmp(ainfo
->name
, ".")) {
3787 g_free(ainfo
->name
);
3791 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3792 g_free(ainfo
->content_type
);
3793 ainfo
->content_type
= g_strdup("application/octet-stream");
3794 g_free(ainfo
->charset
);
3795 ainfo
->charset
= NULL
;
3798 ainfo
->size
= (goffset
)size
;
3799 size_text
= to_human_readable((goffset
)size
);
3801 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3802 (GTK_TREE_VIEW(compose
->attach_clist
)));
3804 gtk_list_store_append(store
, &iter
);
3805 gtk_list_store_set(store
, &iter
,
3806 COL_MIMETYPE
, ainfo
->content_type
,
3807 COL_SIZE
, size_text
,
3808 COL_NAME
, ainfo
->name
,
3809 COL_CHARSET
, ainfo
->charset
,
3811 COL_AUTODATA
, auto_ainfo
,
3814 g_auto_pointer_free(auto_ainfo
);
3815 compose_attach_update_label(compose
);
3819 static void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3821 compose
->use_signing
= use_signing
;
3822 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3825 static void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3827 compose
->use_encryption
= use_encryption
;
3828 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3831 #define NEXT_PART_NOT_CHILD(info) \
3833 node = info->node; \
3834 while (node->children) \
3835 node = g_node_last_child(node); \
3836 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3839 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3843 MimeInfo
*firsttext
= NULL
;
3844 MimeInfo
*encrypted
= NULL
;
3847 const gchar
*partname
= NULL
;
3849 mimeinfo
= procmime_scan_message(msginfo
);
3850 if (!mimeinfo
) return;
3852 if (mimeinfo
->node
->children
== NULL
) {
3853 procmime_mimeinfo_free_all(mimeinfo
);
3857 /* find first content part */
3858 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3859 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3860 child
= (MimeInfo
*)child
->node
->children
->data
;
3863 if (child
->type
== MIMETYPE_TEXT
) {
3865 debug_print("First text part found\n");
3866 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3867 child
->type
== MIMETYPE_APPLICATION
&&
3868 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3869 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3872 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3873 while (child
!= NULL
) {
3876 if (child
== encrypted
) {
3877 /* skip this part of tree */
3878 NEXT_PART_NOT_CHILD(child
);
3882 if (child
->type
== MIMETYPE_MULTIPART
) {
3883 /* get the actual content */
3884 child
= procmime_mimeinfo_next(child
);
3888 if (child
== firsttext
) {
3889 child
= procmime_mimeinfo_next(child
);
3893 outfile
= procmime_get_tmp_file_name(child
);
3894 if ((err
= procmime_get_part(outfile
, child
)) < 0)
3895 g_warning("Can't get the part of multipart message. (%s)", strerror(-err
));
3897 gchar
*content_type
;
3899 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
3901 /* if we meet a pgp signature, we don't attach it, but
3902 * we force signing. */
3903 if ((strcmp(content_type
, "application/pgp-signature") &&
3904 strcmp(content_type
, "application/pkcs7-signature") &&
3905 strcmp(content_type
, "application/x-pkcs7-signature"))
3906 || compose
->mode
== COMPOSE_REDIRECT
) {
3907 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
3908 if (partname
== NULL
)
3909 partname
= procmime_mimeinfo_get_parameter(child
, "name");
3910 if (partname
== NULL
)
3912 compose_attach_append(compose
, outfile
,
3913 partname
, content_type
,
3914 procmime_mimeinfo_get_parameter(child
, "charset"));
3916 compose_force_signing(compose
, compose
->account
, NULL
);
3918 g_free(content_type
);
3921 NEXT_PART_NOT_CHILD(child
);
3923 procmime_mimeinfo_free_all(mimeinfo
);
3926 #undef NEXT_PART_NOT_CHILD
3931 WAIT_FOR_INDENT_CHAR
,
3932 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
3935 /* return indent length, we allow:
3936 indent characters followed by indent characters or spaces/tabs,
3937 alphabets and numbers immediately followed by indent characters,
3938 and the repeating sequences of the above
3939 If quote ends with multiple spaces, only the first one is included. */
3940 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
3941 const GtkTextIter
*start
, gint
*len
)
3943 GtkTextIter iter
= *start
;
3947 IndentState state
= WAIT_FOR_INDENT_CHAR
;
3950 gint alnum_count
= 0;
3951 gint space_count
= 0;
3954 if (prefs_common
.quote_chars
== NULL
) {
3958 while (!gtk_text_iter_ends_line(&iter
)) {
3959 wc
= gtk_text_iter_get_char(&iter
);
3960 if (g_unichar_iswide(wc
))
3962 clen
= g_unichar_to_utf8(wc
, ch
);
3966 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
3967 is_space
= g_unichar_isspace(wc
);
3969 if (state
== WAIT_FOR_INDENT_CHAR
) {
3970 if (!is_indent
&& !g_unichar_isalnum(wc
))
3973 quote_len
+= alnum_count
+ space_count
+ 1;
3974 alnum_count
= space_count
= 0;
3975 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
3978 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
3979 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
3983 else if (is_indent
) {
3984 quote_len
+= alnum_count
+ space_count
+ 1;
3985 alnum_count
= space_count
= 0;
3988 state
= WAIT_FOR_INDENT_CHAR
;
3992 gtk_text_iter_forward_char(&iter
);
3995 if (quote_len
> 0 && space_count
> 0)
4001 if (quote_len
> 0) {
4003 gtk_text_iter_forward_chars(&iter
, quote_len
);
4004 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4010 /* return >0 if the line is itemized */
4011 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4012 const GtkTextIter
*start
)
4014 GtkTextIter iter
= *start
;
4019 if (gtk_text_iter_ends_line(&iter
))
4024 wc
= gtk_text_iter_get_char(&iter
);
4025 if (!g_unichar_isspace(wc
))
4027 gtk_text_iter_forward_char(&iter
);
4028 if (gtk_text_iter_ends_line(&iter
))
4032 clen
= g_unichar_to_utf8(wc
, ch
);
4036 if (!strchr("*-+", ch
[0]))
4039 gtk_text_iter_forward_char(&iter
);
4040 if (gtk_text_iter_ends_line(&iter
))
4042 wc
= gtk_text_iter_get_char(&iter
);
4043 if (g_unichar_isspace(wc
)) {
4049 /* return the string at the start of the itemization */
4050 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4051 const GtkTextIter
*start
)
4053 GtkTextIter iter
= *start
;
4056 GString
*item_chars
= g_string_new("");
4059 if (gtk_text_iter_ends_line(&iter
))
4064 wc
= gtk_text_iter_get_char(&iter
);
4065 if (!g_unichar_isspace(wc
))
4067 gtk_text_iter_forward_char(&iter
);
4068 if (gtk_text_iter_ends_line(&iter
))
4070 g_string_append_unichar(item_chars
, wc
);
4073 str
= item_chars
->str
;
4074 g_string_free(item_chars
, FALSE
);
4078 /* return the number of spaces at a line's start */
4079 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4080 const GtkTextIter
*start
)
4082 GtkTextIter iter
= *start
;
4085 if (gtk_text_iter_ends_line(&iter
))
4089 wc
= gtk_text_iter_get_char(&iter
);
4090 if (!g_unichar_isspace(wc
))
4093 gtk_text_iter_forward_char(&iter
);
4094 if (gtk_text_iter_ends_line(&iter
))
4098 gtk_text_iter_forward_char(&iter
);
4099 if (gtk_text_iter_ends_line(&iter
))
4104 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4105 const GtkTextIter
*start
,
4106 GtkTextIter
*break_pos
,
4110 GtkTextIter iter
= *start
, line_end
= *start
;
4111 PangoLogAttr
*attrs
;
4118 gboolean can_break
= FALSE
;
4119 gboolean do_break
= FALSE
;
4120 gboolean was_white
= FALSE
;
4121 gboolean prev_dont_break
= FALSE
;
4123 gtk_text_iter_forward_to_line_end(&line_end
);
4124 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4125 len
= g_utf8_strlen(str
, -1);
4129 g_warning("compose_get_line_break_pos: len = 0!\n");
4133 /* g_print("breaking line: %d: %s (len = %d)\n",
4134 gtk_text_iter_get_line(&iter), str, len); */
4136 attrs
= g_new(PangoLogAttr
, len
+ 1);
4138 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4142 /* skip quote and leading spaces */
4143 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4146 wc
= g_utf8_get_char(p
);
4147 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4149 if (g_unichar_iswide(wc
))
4151 else if (*p
== '\t')
4155 p
= g_utf8_next_char(p
);
4158 for (; *p
!= '\0' && i
< len
; i
++) {
4159 PangoLogAttr
*attr
= attrs
+ i
;
4163 if (attr
->is_line_break
&& can_break
&& was_white
&& !prev_dont_break
)
4166 was_white
= attr
->is_white
;
4168 /* don't wrap URI */
4169 if ((uri_len
= get_uri_len(p
)) > 0) {
4171 if (pos
> 0 && col
> max_col
) {
4181 wc
= g_utf8_get_char(p
);
4182 if (g_unichar_iswide(wc
)) {
4184 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4186 } else if (*p
== '\t')
4190 if (pos
> 0 && col
> max_col
) {
4195 if (*p
== '-' || *p
== '/')
4196 prev_dont_break
= TRUE
;
4198 prev_dont_break
= FALSE
;
4200 p
= g_utf8_next_char(p
);
4204 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4209 *break_pos
= *start
;
4210 gtk_text_iter_set_line_offset(break_pos
, pos
);
4215 static gboolean
compose_join_next_line(Compose
*compose
,
4216 GtkTextBuffer
*buffer
,
4218 const gchar
*quote_str
)
4220 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4221 PangoLogAttr attrs
[3];
4223 gchar
*next_quote_str
;
4226 gboolean keep_cursor
= FALSE
;
4228 if (!gtk_text_iter_forward_line(&iter_
) ||
4229 gtk_text_iter_ends_line(&iter_
)) {
4232 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4234 if ((quote_str
|| next_quote_str
) &&
4235 strcmp2(quote_str
, next_quote_str
) != 0) {
4236 g_free(next_quote_str
);
4239 g_free(next_quote_str
);
4242 if (quote_len
> 0) {
4243 gtk_text_iter_forward_chars(&end
, quote_len
);
4244 if (gtk_text_iter_ends_line(&end
)) {
4249 /* don't join itemized lines */
4250 if (compose_itemized_length(buffer
, &end
) > 0) {
4254 /* don't join signature separator */
4255 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4258 /* delete quote str */
4260 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4262 /* don't join line breaks put by the user */
4264 gtk_text_iter_backward_char(&cur
);
4265 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4266 gtk_text_iter_forward_char(&cur
);
4270 gtk_text_iter_forward_char(&cur
);
4271 /* delete linebreak and extra spaces */
4272 while (gtk_text_iter_backward_char(&cur
)) {
4273 wc1
= gtk_text_iter_get_char(&cur
);
4274 if (!g_unichar_isspace(wc1
))
4279 while (!gtk_text_iter_ends_line(&cur
)) {
4280 wc1
= gtk_text_iter_get_char(&cur
);
4281 if (!g_unichar_isspace(wc1
))
4283 gtk_text_iter_forward_char(&cur
);
4286 if (!gtk_text_iter_equal(&prev
, &next
)) {
4289 mark
= gtk_text_buffer_get_insert(buffer
);
4290 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4291 if (gtk_text_iter_equal(&prev
, &cur
))
4293 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4297 /* insert space if required */
4298 gtk_text_iter_backward_char(&prev
);
4299 wc1
= gtk_text_iter_get_char(&prev
);
4300 wc2
= gtk_text_iter_get_char(&next
);
4301 gtk_text_iter_forward_char(&next
);
4302 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4303 pango_default_break(str
, -1, NULL
, attrs
, 3);
4304 if (!attrs
[1].is_line_break
||
4305 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4306 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4308 gtk_text_iter_backward_char(&iter_
);
4309 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4318 #define ADD_TXT_POS(bp_, ep_, pti_) \
4319 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4320 last = last->next; \
4321 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4322 last->next = NULL; \
4324 g_warning("alloc error scanning URIs\n"); \
4327 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4329 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4330 GtkTextBuffer
*buffer
;
4331 GtkTextIter iter
, break_pos
, end_of_line
;
4332 gchar
*quote_str
= NULL
;
4334 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4335 gboolean prev_autowrap
= compose
->autowrap
;
4336 gint startq_offset
= -1, noq_offset
= -1;
4337 gint uri_start
= -1, uri_stop
= -1;
4338 gint nouri_start
= -1, nouri_stop
= -1;
4339 gint num_blocks
= 0;
4340 gint quotelevel
= -1;
4341 gboolean modified
= force
;
4342 gboolean removed
= FALSE
;
4343 gboolean modified_before_remove
= FALSE
;
4345 gboolean start
= TRUE
;
4346 gint itemized_len
= 0, rem_item_len
= 0;
4347 gchar
*itemized_chars
= NULL
;
4348 gboolean item_continuation
= FALSE
;
4353 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4357 compose
->autowrap
= FALSE
;
4359 buffer
= gtk_text_view_get_buffer(text
);
4360 undo_wrapping(compose
->undostruct
, TRUE
);
4365 mark
= gtk_text_buffer_get_insert(buffer
);
4366 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4370 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4371 if (gtk_text_iter_ends_line(&iter
)) {
4372 while (gtk_text_iter_ends_line(&iter
) &&
4373 gtk_text_iter_forward_line(&iter
))
4376 while (gtk_text_iter_backward_line(&iter
)) {
4377 if (gtk_text_iter_ends_line(&iter
)) {
4378 gtk_text_iter_forward_line(&iter
);
4384 /* move to line start */
4385 gtk_text_iter_set_line_offset(&iter
, 0);
4388 itemized_len
= compose_itemized_length(buffer
, &iter
);
4390 if (!itemized_len
) {
4391 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4392 item_continuation
= TRUE
;
4396 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4398 /* go until paragraph end (empty line) */
4399 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4400 gchar
*scanpos
= NULL
;
4401 /* parse table - in order of priority */
4403 const gchar
*needle
; /* token */
4405 /* token search function */
4406 gchar
*(*search
) (const gchar
*haystack
,
4407 const gchar
*needle
);
4408 /* part parsing function */
4409 gboolean (*parse
) (const gchar
*start
,
4410 const gchar
*scanpos
,
4414 /* part to URI function */
4415 gchar
*(*build_uri
) (const gchar
*bp
,
4419 static struct table parser
[] = {
4420 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4421 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4422 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4423 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4424 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4425 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4426 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4427 {"@", strcasestr
, get_email_part
, make_email_string
}
4429 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4430 gint last_index
= PARSE_ELEMS
;
4432 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4436 if (!prev_autowrap
&& num_blocks
== 0) {
4438 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4439 G_CALLBACK(text_inserted
),
4442 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4445 uri_start
= uri_stop
= -1;
4447 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4450 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4451 if (startq_offset
== -1)
4452 startq_offset
= gtk_text_iter_get_offset(&iter
);
4453 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4454 if (quotelevel
> 2) {
4455 /* recycle colors */
4456 if (prefs_common
.recycle_quote_colors
)
4465 if (startq_offset
== -1)
4466 noq_offset
= gtk_text_iter_get_offset(&iter
);
4470 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4473 if (gtk_text_iter_ends_line(&iter
)) {
4475 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4476 prefs_common
.linewrap_len
,
4478 GtkTextIter prev
, next
, cur
;
4479 if (prev_autowrap
!= FALSE
|| force
) {
4480 compose
->automatic_break
= TRUE
;
4482 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4483 compose
->automatic_break
= FALSE
;
4484 if (itemized_len
&& compose
->autoindent
) {
4485 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4486 if (!item_continuation
)
4487 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4489 } else if (quote_str
&& wrap_quote
) {
4490 compose
->automatic_break
= TRUE
;
4492 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4493 compose
->automatic_break
= FALSE
;
4494 if (itemized_len
&& compose
->autoindent
) {
4495 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4496 if (!item_continuation
)
4497 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4501 /* remove trailing spaces */
4503 rem_item_len
= itemized_len
;
4504 while (compose
->autoindent
&& rem_item_len
-- > 0)
4505 gtk_text_iter_backward_char(&cur
);
4506 gtk_text_iter_backward_char(&cur
);
4509 while (!gtk_text_iter_starts_line(&cur
)) {
4512 gtk_text_iter_backward_char(&cur
);
4513 wc
= gtk_text_iter_get_char(&cur
);
4514 if (!g_unichar_isspace(wc
))
4518 if (!gtk_text_iter_equal(&prev
, &next
)) {
4519 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4521 gtk_text_iter_forward_char(&break_pos
);
4525 gtk_text_buffer_insert(buffer
, &break_pos
,
4529 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4531 /* move iter to current line start */
4532 gtk_text_iter_set_line_offset(&iter
, 0);
4539 /* move iter to next line start */
4545 if (!prev_autowrap
&& num_blocks
> 0) {
4547 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4548 G_CALLBACK(text_inserted
),
4552 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4553 gtk_text_iter_forward_char(&end_of_line
);
4555 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4557 nouri_start
= gtk_text_iter_get_offset(&iter
);
4558 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4560 walk_pos
= gtk_text_iter_get_offset(&iter
);
4561 /* FIXME: this looks phony. scanning for anything in the parse table */
4562 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4565 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4567 if (scanpos
== NULL
|| tmp
< scanpos
) {
4576 /* check if URI can be parsed */
4577 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4578 (const gchar
**)&ep
, FALSE
)
4579 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4583 strlen(parser
[last_index
].needle
);
4586 uri_start
= walk_pos
+ (bp
- o_walk
);
4587 uri_stop
= walk_pos
+ (ep
- o_walk
);
4591 gtk_text_iter_forward_line(&iter
);
4594 if (startq_offset
!= -1) {
4595 GtkTextIter startquote
, endquote
;
4596 gtk_text_buffer_get_iter_at_offset(
4597 buffer
, &startquote
, startq_offset
);
4600 switch (quotelevel
) {
4602 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4603 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4604 gtk_text_buffer_apply_tag_by_name(
4605 buffer
, "quote0", &startquote
, &endquote
);
4606 gtk_text_buffer_remove_tag_by_name(
4607 buffer
, "quote1", &startquote
, &endquote
);
4608 gtk_text_buffer_remove_tag_by_name(
4609 buffer
, "quote2", &startquote
, &endquote
);
4614 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4615 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4616 gtk_text_buffer_apply_tag_by_name(
4617 buffer
, "quote1", &startquote
, &endquote
);
4618 gtk_text_buffer_remove_tag_by_name(
4619 buffer
, "quote0", &startquote
, &endquote
);
4620 gtk_text_buffer_remove_tag_by_name(
4621 buffer
, "quote2", &startquote
, &endquote
);
4626 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4627 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4628 gtk_text_buffer_apply_tag_by_name(
4629 buffer
, "quote2", &startquote
, &endquote
);
4630 gtk_text_buffer_remove_tag_by_name(
4631 buffer
, "quote0", &startquote
, &endquote
);
4632 gtk_text_buffer_remove_tag_by_name(
4633 buffer
, "quote1", &startquote
, &endquote
);
4639 } else if (noq_offset
!= -1) {
4640 GtkTextIter startnoquote
, endnoquote
;
4641 gtk_text_buffer_get_iter_at_offset(
4642 buffer
, &startnoquote
, noq_offset
);
4645 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4646 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4647 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4648 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4649 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4650 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4651 gtk_text_buffer_remove_tag_by_name(
4652 buffer
, "quote0", &startnoquote
, &endnoquote
);
4653 gtk_text_buffer_remove_tag_by_name(
4654 buffer
, "quote1", &startnoquote
, &endnoquote
);
4655 gtk_text_buffer_remove_tag_by_name(
4656 buffer
, "quote2", &startnoquote
, &endnoquote
);
4662 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4663 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4664 gtk_text_buffer_get_iter_at_offset(
4665 buffer
, &nouri_start_iter
, nouri_start
);
4666 gtk_text_buffer_get_iter_at_offset(
4667 buffer
, &nouri_end_iter
, nouri_stop
);
4668 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4669 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4670 gtk_text_buffer_remove_tag_by_name(
4671 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4672 modified_before_remove
= modified
;
4677 if (uri_start
>= 0 && uri_stop
> 0) {
4678 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4679 gtk_text_buffer_get_iter_at_offset(
4680 buffer
, &uri_start_iter
, uri_start
);
4681 gtk_text_buffer_get_iter_at_offset(
4682 buffer
, &uri_end_iter
, uri_stop
);
4683 back
= uri_end_iter
;
4684 gtk_text_iter_backward_char(&back
);
4685 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4686 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4687 gtk_text_buffer_apply_tag_by_name(
4688 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4690 if (removed
&& !modified_before_remove
) {
4696 // debug_print("not modified, out after %d lines\n", lines);
4700 // debug_print("modified, out after %d lines\n", lines);
4702 g_free(itemized_chars
);
4705 undo_wrapping(compose
->undostruct
, FALSE
);
4706 compose
->autowrap
= prev_autowrap
;
4711 void compose_action_cb(void *data
)
4713 Compose
*compose
= (Compose
*)data
;
4714 compose_wrap_all(compose
);
4717 static void compose_wrap_all(Compose
*compose
)
4719 compose_wrap_all_full(compose
, FALSE
);
4722 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4724 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4725 GtkTextBuffer
*buffer
;
4727 gboolean modified
= TRUE
;
4729 buffer
= gtk_text_view_get_buffer(text
);
4731 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4733 undo_wrapping(compose
->undostruct
, TRUE
);
4735 while (!gtk_text_iter_is_end(&iter
) && modified
)
4736 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4738 undo_wrapping(compose
->undostruct
, FALSE
);
4742 static void compose_set_title(Compose
*compose
)
4748 edited
= compose
->modified
? _(" [Edited]") : "";
4750 subject
= gtk_editable_get_chars(
4751 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4753 #ifndef GENERIC_UMPC
4754 if (subject
&& strlen(subject
))
4755 str
= g_strdup_printf(_("%s - Compose message%s"),
4758 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4760 str
= g_strdup(_("Compose message"));
4763 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4769 * compose_current_mail_account:
4771 * Find a current mail account (the currently selected account, or the
4772 * default account, if a news account is currently selected). If a
4773 * mail account cannot be found, display an error message.
4775 * Return value: Mail account, or NULL if not found.
4777 static PrefsAccount
*
4778 compose_current_mail_account(void)
4782 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4785 ac
= account_get_default();
4786 if (!ac
|| ac
->protocol
== A_NNTP
) {
4787 alertpanel_error(_("Account for sending mail is not specified.\n"
4788 "Please select a mail account before sending."));
4795 #define QUOTE_IF_REQUIRED(out, str) \
4797 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4801 len = strlen(str) + 3; \
4802 if ((__tmp = alloca(len)) == NULL) { \
4803 g_warning("can't allocate memory\n"); \
4804 g_string_free(header, TRUE); \
4807 g_snprintf(__tmp, len, "\"%s\"", str); \
4812 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4813 g_warning("can't allocate memory\n"); \
4814 g_string_free(header, TRUE); \
4817 strcpy(__tmp, str); \
4823 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4825 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4829 len = strlen(str) + 3; \
4830 if ((__tmp = alloca(len)) == NULL) { \
4831 g_warning("can't allocate memory\n"); \
4834 g_snprintf(__tmp, len, "\"%s\"", str); \
4839 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4840 g_warning("can't allocate memory\n"); \
4843 strcpy(__tmp, str); \
4849 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4852 gchar
*from
= NULL
, *header
= NULL
;
4853 ComposeHeaderEntry
*header_entry
;
4854 #if GTK_CHECK_VERSION(2, 24, 0)
4858 cm_return_if_fail(account
!= NULL
);
4860 compose
->account
= account
;
4861 if (account
->name
&& *account
->name
) {
4863 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4864 qbuf
= escape_internal_quotes(buf
, '"');
4865 from
= g_strdup_printf("%s <%s>",
4866 qbuf
, account
->address
);
4869 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4871 from
= g_strdup_printf("<%s>",
4873 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4878 compose_set_title(compose
);
4880 if (account
->default_sign
&& compose
->mode
!= COMPOSE_REDIRECT
)
4881 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
4883 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
4884 if (account
->default_encrypt
&& compose
->mode
!= COMPOSE_REDIRECT
)
4885 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
4887 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
4889 activate_privacy_system(compose
, account
, FALSE
);
4891 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
4892 undo_block(compose
->undostruct
);
4893 compose_insert_sig(compose
, TRUE
);
4894 undo_unblock(compose
->undostruct
);
4897 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
4898 #if !GTK_CHECK_VERSION(2, 24, 0)
4899 header
= gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry
->combo
));
4901 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
4902 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4903 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
4906 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
4907 if (account
->protocol
== A_NNTP
) {
4908 if (!strcmp(header
, _("To:")))
4909 combobox_select_by_text(
4910 GTK_COMBO_BOX(header_entry
->combo
),
4913 if (!strcmp(header
, _("Newsgroups:")))
4914 combobox_select_by_text(
4915 GTK_COMBO_BOX(header_entry
->combo
),
4923 /* use account's dict info if set */
4924 if (compose
->gtkaspell
) {
4925 if (account
->enable_default_dictionary
)
4926 gtkaspell_change_dict(compose
->gtkaspell
,
4927 account
->default_dictionary
, FALSE
);
4928 if (account
->enable_default_alt_dictionary
)
4929 gtkaspell_change_alt_dict(compose
->gtkaspell
,
4930 account
->default_alt_dictionary
);
4931 if (account
->enable_default_dictionary
4932 || account
->enable_default_alt_dictionary
)
4933 compose_spell_menu_changed(compose
);
4938 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
4939 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
4940 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
4941 gboolean recipient_found
= FALSE
;
4945 /* free to and newsgroup list */
4946 slist_free_strings_full(compose
->to_list
);
4947 compose
->to_list
= NULL
;
4949 slist_free_strings_full(compose
->newsgroup_list
);
4950 compose
->newsgroup_list
= NULL
;
4952 /* search header entries for to and newsgroup entries */
4953 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4956 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
4957 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
4960 if (entry
[0] != '\0') {
4961 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
4962 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
4963 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
4964 recipient_found
= TRUE
;
4967 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
4968 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
4969 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
4970 recipient_found
= TRUE
;
4977 return recipient_found
;
4980 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
4982 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
4983 gboolean found_other
= FALSE
;
4985 /* search header entries for to and newsgroup entries */
4986 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4989 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
4990 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
4993 if (strcmp(entry
, compose
->account
->auto_cc
)
4994 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5004 if (compose
->batch
) {
5005 gtk_widget_show_all(compose
->window
);
5007 aval
= alertpanel(_("Send"),
5008 _("The only recipient is the default CC address. Send anyway?"),
5009 GTK_STOCK_CANCEL
, _("+_Send"), NULL
);
5010 if (aval
!= G_ALERTALTERNATE
)
5014 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5015 gboolean found_other
= FALSE
;
5017 /* search header entries for to and newsgroup entries */
5018 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5021 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5022 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5025 if (strcmp(entry
, compose
->account
->auto_bcc
)
5026 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5036 if (compose
->batch
) {
5037 gtk_widget_show_all(compose
->window
);
5039 aval
= alertpanel(_("Send"),
5040 _("The only recipient is the default BCC address. Send anyway?"),
5041 GTK_STOCK_CANCEL
, _("+_Send"), NULL
);
5042 if (aval
!= G_ALERTALTERNATE
)
5049 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5053 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5054 if (compose
->batch
) {
5055 gtk_widget_show_all(compose
->window
);
5057 alertpanel_error(_("Recipient is not specified."));
5061 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5065 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5066 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5067 if (*str
== '\0' && check_everything
== TRUE
&&
5068 compose
->mode
!= COMPOSE_REDIRECT
) {
5070 gchar
*button_label
;
5073 if (compose
->sending
)
5074 button_label
= _("+_Send");
5076 button_label
= _("+_Queue");
5077 message
= g_strdup_printf(_("Subject is empty. %s"),
5078 compose
->sending
?_("Send it anyway?"):
5079 _("Queue it anyway?"));
5081 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5082 GTK_STOCK_CANCEL
, button_label
, NULL
, TRUE
, NULL
,
5083 ALERT_QUESTION
, G_ALERTDEFAULT
);
5085 if (aval
& G_ALERTDISABLE
) {
5086 aval
&= ~G_ALERTDISABLE
;
5087 prefs_common
.warn_empty_subj
= FALSE
;
5089 if (aval
!= G_ALERTALTERNATE
)
5094 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5100 gint
compose_send(Compose
*compose
)
5103 FolderItem
*folder
= NULL
;
5105 gchar
*msgpath
= NULL
;
5106 gboolean discard_window
= FALSE
;
5107 gchar
*errstr
= NULL
;
5108 gchar
*tmsgid
= NULL
;
5109 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5110 gboolean queued_removed
= FALSE
;
5112 if (prefs_common
.send_dialog_invisible
5113 || compose
->batch
== TRUE
)
5114 discard_window
= TRUE
;
5116 compose_allow_user_actions (compose
, FALSE
);
5117 compose
->sending
= TRUE
;
5119 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5120 if (compose
->batch
) {
5121 gtk_widget_show_all(compose
->window
);
5127 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5130 if (compose
->batch
) {
5131 gtk_widget_show_all(compose
->window
);
5134 alertpanel_error(_("Could not queue message for sending:\n\n"
5135 "Charset conversion failed."));
5136 } else if (val
== -5) {
5137 alertpanel_error(_("Could not queue message for sending:\n\n"
5138 "Couldn't get recipient encryption key."));
5139 } else if (val
== -6) {
5141 } else if (val
== -3) {
5142 if (privacy_peek_error())
5143 alertpanel_error(_("Could not queue message for sending:\n\n"
5144 "Signature failed: %s"), privacy_get_error());
5145 } else if (val
== -2 && errno
!= 0) {
5146 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno
));
5148 alertpanel_error(_("Could not queue message for sending."));
5153 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5154 if (discard_window
) {
5155 compose
->sending
= FALSE
;
5156 compose_close(compose
);
5157 /* No more compose access in the normal codepath
5158 * after this point! */
5163 alertpanel_error(_("The message was queued but could not be "
5164 "sent.\nUse \"Send queued messages\" from "
5165 "the main window to retry."));
5166 if (!discard_window
) {
5173 if (msgpath
== NULL
) {
5174 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5175 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5178 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5179 claws_unlink(msgpath
);
5182 if (!discard_window
) {
5184 if (!queued_removed
)
5185 folder_item_remove_msg(folder
, msgnum
);
5186 folder_item_scan(folder
);
5188 /* make sure we delete that */
5189 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5191 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5192 folder_item_remove_msg(folder
, tmp
->msgnum
);
5193 procmsg_msginfo_free(tmp
);
5200 if (!queued_removed
)
5201 folder_item_remove_msg(folder
, msgnum
);
5202 folder_item_scan(folder
);
5204 /* make sure we delete that */
5205 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5207 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5208 folder_item_remove_msg(folder
, tmp
->msgnum
);
5209 procmsg_msginfo_free(tmp
);
5212 if (!discard_window
) {
5213 compose
->sending
= FALSE
;
5214 compose_allow_user_actions (compose
, TRUE
);
5215 compose_close(compose
);
5219 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5220 "the main window to retry."), errstr
);
5223 alertpanel_error_log(_("The message was queued but could not be "
5224 "sent.\nUse \"Send queued messages\" from "
5225 "the main window to retry."));
5227 if (!discard_window
) {
5236 toolbar_main_set_sensitive(mainwin
);
5237 main_window_set_menu_sensitive(mainwin
);
5243 compose_allow_user_actions (compose
, TRUE
);
5244 compose
->sending
= FALSE
;
5245 compose
->modified
= TRUE
;
5246 toolbar_main_set_sensitive(mainwin
);
5247 main_window_set_menu_sensitive(mainwin
);
5252 static gboolean
compose_use_attach(Compose
*compose
)
5254 GtkTreeModel
*model
= gtk_tree_view_get_model
5255 (GTK_TREE_VIEW(compose
->attach_clist
));
5256 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5259 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5262 gchar buf
[BUFFSIZE
];
5264 gboolean first_to_address
;
5265 gboolean first_cc_address
;
5267 ComposeHeaderEntry
*headerentry
;
5268 const gchar
*headerentryname
;
5269 const gchar
*cc_hdr
;
5270 const gchar
*to_hdr
;
5271 gboolean err
= FALSE
;
5273 debug_print("Writing redirect header\n");
5275 cc_hdr
= prefs_common_translated_header_name("Cc:");
5276 to_hdr
= prefs_common_translated_header_name("To:");
5278 first_to_address
= TRUE
;
5279 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5280 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5281 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5283 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5284 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5285 Xstrdup_a(str
, entstr
, return -1);
5287 if (str
[0] != '\0') {
5288 compose_convert_header
5289 (compose
, buf
, sizeof(buf
), str
,
5290 strlen("Resent-To") + 2, TRUE
);
5292 if (first_to_address
) {
5293 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5294 first_to_address
= FALSE
;
5296 err
|= (fprintf(fp
, ",") < 0);
5298 err
|= (fprintf(fp
, "%s", buf
) < 0);
5302 if (!first_to_address
) {
5303 err
|= (fprintf(fp
, "\n") < 0);
5306 first_cc_address
= TRUE
;
5307 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5308 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5309 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5311 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5312 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5313 Xstrdup_a(str
, strg
, return -1);
5315 if (str
[0] != '\0') {
5316 compose_convert_header
5317 (compose
, buf
, sizeof(buf
), str
,
5318 strlen("Resent-Cc") + 2, TRUE
);
5320 if (first_cc_address
) {
5321 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5322 first_cc_address
= FALSE
;
5324 err
|= (fprintf(fp
, ",") < 0);
5326 err
|= (fprintf(fp
, "%s", buf
) < 0);
5330 if (!first_cc_address
) {
5331 err
|= (fprintf(fp
, "\n") < 0);
5334 return (err
? -1:0);
5337 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5339 gchar buf
[BUFFSIZE
];
5341 const gchar
*entstr
;
5342 /* struct utsname utsbuf; */
5343 gboolean err
= FALSE
;
5345 cm_return_val_if_fail(fp
!= NULL
, -1);
5346 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5347 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5350 get_rfc822_date(buf
, sizeof(buf
));
5351 err
|= (fprintf(fp
, "Resent-Date: %s\n", buf
) < 0);
5354 if (compose
->account
->name
&& *compose
->account
->name
) {
5355 compose_convert_header
5356 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5357 strlen("From: "), TRUE
);
5358 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5359 buf
, compose
->account
->address
) < 0);
5361 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5364 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5365 if (*entstr
!= '\0') {
5366 Xstrdup_a(str
, entstr
, return -1);
5369 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5370 strlen("Subject: "), FALSE
);
5371 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5375 /* Resent-Message-ID */
5376 if (compose
->account
->set_domain
&& compose
->account
->domain
) {
5377 g_snprintf(buf
, sizeof(buf
), "%s", compose
->account
->domain
);
5378 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5379 g_snprintf(buf
, sizeof(buf
), "%s",
5380 strchr(compose
->account
->address
, '@') ?
5381 strchr(compose
->account
->address
, '@')+1 :
5382 compose
->account
->address
);
5384 g_snprintf(buf
, sizeof(buf
), "%s", "");
5387 if (compose
->account
->gen_msgid
) {
5389 if (compose
->account
->msgid_with_addr
) {
5390 addr
= compose
->account
->address
;
5392 generate_msgid(buf
, sizeof(buf
), addr
);
5393 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", buf
) < 0);
5395 g_free(compose
->msgid
);
5396 compose
->msgid
= g_strdup(buf
);
5398 compose
->msgid
= NULL
;
5401 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5404 /* separator between header and body */
5405 err
|= (fputs("\n", fp
) == EOF
);
5407 return (err
? -1:0);
5410 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5414 gchar buf
[BUFFSIZE
];
5416 gboolean skip
= FALSE
;
5417 gboolean err
= FALSE
;
5418 gchar
*not_included
[]={
5419 "Return-Path:", "Delivered-To:", "Received:",
5420 "Subject:", "X-UIDL:", "AF:",
5421 "NF:", "PS:", "SRH:",
5422 "SFN:", "DSR:", "MID:",
5423 "CFG:", "PT:", "S:",
5424 "RQ:", "SSV:", "NSV:",
5425 "SSH:", "R:", "MAID:",
5426 "NAID:", "RMID:", "FMID:",
5427 "SCF:", "RRCPT:", "NG:",
5428 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5429 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5430 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5431 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5432 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5435 if ((fp
= g_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5436 FILE_OP_ERROR(compose
->redirect_filename
, "fopen");
5440 while (procheader_get_one_field_asis(buf
, sizeof(buf
), fp
) != -1) {
5442 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5443 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5444 strlen(not_included
[i
])) == 0) {
5451 if (fputs(buf
, fdest
) == -1)
5454 if (!prefs_common
.redirect_keep_from
) {
5455 if (g_ascii_strncasecmp(buf
, "From:",
5456 strlen("From:")) == 0) {
5457 err
|= (fputs(" (by way of ", fdest
) == EOF
);
5458 if (compose
->account
->name
5459 && *compose
->account
->name
) {
5460 compose_convert_header
5461 (compose
, buf
, sizeof(buf
),
5462 compose
->account
->name
,
5465 err
|= (fprintf(fdest
, "%s <%s>",
5467 compose
->account
->address
) < 0);
5469 err
|= (fprintf(fdest
, "%s",
5470 compose
->account
->address
) < 0);
5471 err
|= (fputs(")", fdest
) == EOF
);
5475 if (fputs("\n", fdest
) == -1)
5482 if (compose_redirect_write_headers(compose
, fdest
))
5485 while ((len
= fread(buf
, sizeof(gchar
), sizeof(buf
), fp
)) > 0) {
5486 if (fwrite(buf
, sizeof(gchar
), len
, fdest
) != len
)
5499 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5501 GtkTextBuffer
*buffer
;
5502 GtkTextIter start
, end
;
5505 const gchar
*out_codeset
;
5506 EncodingType encoding
= ENC_UNKNOWN
;
5507 MimeInfo
*mimemsg
, *mimetext
;
5509 const gchar
*src_codeset
= CS_INTERNAL
;
5510 gchar
*from_addr
= NULL
;
5511 gchar
*from_name
= NULL
;
5513 if (action
== COMPOSE_WRITE_FOR_SEND
)
5514 attach_parts
= TRUE
;
5516 /* create message MimeInfo */
5517 mimemsg
= procmime_mimeinfo_new();
5518 mimemsg
->type
= MIMETYPE_MESSAGE
;
5519 mimemsg
->subtype
= g_strdup("rfc822");
5520 mimemsg
->content
= MIMECONTENT_MEM
;
5521 mimemsg
->tmp
= TRUE
; /* must free content later */
5522 mimemsg
->data
.mem
= compose_get_header(compose
);
5524 /* Create text part MimeInfo */
5525 /* get all composed text */
5526 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5527 gtk_text_buffer_get_start_iter(buffer
, &start
);
5528 gtk_text_buffer_get_end_iter(buffer
, &end
);
5529 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5531 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5533 if (!out_codeset
&& is_ascii_str(chars
)) {
5534 out_codeset
= CS_US_ASCII
;
5535 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5536 is_ascii_str(chars
)) {
5537 out_codeset
= CS_US_ASCII
;
5538 encoding
= ENC_7BIT
;
5542 gchar
*test_conv_global_out
= NULL
;
5543 gchar
*test_conv_reply
= NULL
;
5545 /* automatic mode. be automatic. */
5546 codeconv_set_strict(TRUE
);
5548 out_codeset
= conv_get_outgoing_charset_str();
5550 debug_print("trying to convert to %s\n", out_codeset
);
5551 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5554 if (!test_conv_global_out
&& compose
->orig_charset
5555 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5556 out_codeset
= compose
->orig_charset
;
5557 debug_print("failure; trying to convert to %s\n", out_codeset
);
5558 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5561 if (!test_conv_global_out
&& !test_conv_reply
) {
5563 out_codeset
= CS_INTERNAL
;
5564 debug_print("failure; finally using %s\n", out_codeset
);
5566 g_free(test_conv_global_out
);
5567 g_free(test_conv_reply
);
5568 codeconv_set_strict(FALSE
);
5571 if (encoding
== ENC_UNKNOWN
) {
5572 if (prefs_common
.encoding_method
== CTE_BASE64
)
5573 encoding
= ENC_BASE64
;
5574 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5575 encoding
= ENC_QUOTED_PRINTABLE
;
5576 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5577 encoding
= ENC_8BIT
;
5579 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5582 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5583 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5585 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5586 codeconv_set_strict(TRUE
);
5587 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5588 codeconv_set_strict(FALSE
);
5594 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5595 "to the specified %s charset.\n"
5596 "Send it as %s?"), out_codeset
, src_codeset
);
5597 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
, _("+_Send"), NULL
, FALSE
,
5598 NULL
, ALERT_ERROR
, G_ALERTDEFAULT
);
5601 if (aval
!= G_ALERTALTERNATE
) {
5606 out_codeset
= src_codeset
;
5612 out_codeset
= src_codeset
;
5617 if (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
) {
5618 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5619 strstr(buf
, "\nFrom ") != NULL
) {
5620 encoding
= ENC_QUOTED_PRINTABLE
;
5624 mimetext
= procmime_mimeinfo_new();
5625 mimetext
->content
= MIMECONTENT_MEM
;
5626 mimetext
->tmp
= TRUE
; /* must free content later */
5627 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5628 * and free the data, which we need later. */
5629 mimetext
->data
.mem
= g_strdup(buf
);
5630 mimetext
->type
= MIMETYPE_TEXT
;
5631 mimetext
->subtype
= g_strdup("plain");
5632 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5633 g_strdup(out_codeset
));
5635 /* protect trailing spaces when signing message */
5636 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5637 privacy_system_can_sign(compose
->privacy_system
)) {
5638 encoding
= ENC_QUOTED_PRINTABLE
;
5641 debug_print("main text: %zd bytes encoded as %s in %d\n",
5642 strlen(buf
), out_codeset
, encoding
);
5644 /* check for line length limit */
5645 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5646 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5647 check_line_length(buf
, 1000, &line
) < 0) {
5651 msg
= g_strdup_printf
5652 (_("Line %d exceeds the line length limit (998 bytes).\n"
5653 "The contents of the message might be broken on the way to the delivery.\n"
5655 "Send it anyway?"), line
+ 1);
5656 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
);
5658 if (aval
!= G_ALERTALTERNATE
) {
5664 if (encoding
!= ENC_UNKNOWN
)
5665 procmime_encode_content(mimetext
, encoding
);
5667 /* append attachment parts */
5668 if (compose_use_attach(compose
) && attach_parts
) {
5669 MimeInfo
*mimempart
;
5670 gchar
*boundary
= NULL
;
5671 mimempart
= procmime_mimeinfo_new();
5672 mimempart
->content
= MIMECONTENT_EMPTY
;
5673 mimempart
->type
= MIMETYPE_MULTIPART
;
5674 mimempart
->subtype
= g_strdup("mixed");
5678 boundary
= generate_mime_boundary(NULL
);
5679 } while (strstr(buf
, boundary
) != NULL
);
5681 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5684 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5686 g_node_append(mimempart
->node
, mimetext
->node
);
5687 g_node_append(mimemsg
->node
, mimempart
->node
);
5689 if (compose_add_attachments(compose
, mimempart
) < 0)
5692 g_node_append(mimemsg
->node
, mimetext
->node
);
5696 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5697 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5698 /* extract name and address */
5699 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5700 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5701 *(strrchr(from_addr
, '>')) = '\0';
5702 from_name
= g_strdup(spec
);
5703 *(strrchr(from_name
, '<')) = '\0';
5710 /* sign message if sending */
5711 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5712 privacy_system_can_sign(compose
->privacy_system
))
5713 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5714 compose
->account
, from_addr
)) {
5721 procmime_write_mimeinfo(mimemsg
, fp
);
5723 procmime_mimeinfo_free_all(mimemsg
);
5728 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5730 GtkTextBuffer
*buffer
;
5731 GtkTextIter start
, end
;
5736 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
5737 FILE_OP_ERROR(file
, "fopen");
5741 /* chmod for security */
5742 if (change_file_mode_rw(fp
, file
) < 0) {
5743 FILE_OP_ERROR(file
, "chmod");
5744 g_warning("can't change file mode\n");
5747 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5748 gtk_text_buffer_get_start_iter(buffer
, &start
);
5749 gtk_text_buffer_get_end_iter(buffer
, &end
);
5750 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5752 chars
= conv_codeset_strdup
5753 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
5762 len
= strlen(chars
);
5763 if (fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
5764 FILE_OP_ERROR(file
, "fwrite");
5773 if (fclose(fp
) == EOF
) {
5774 FILE_OP_ERROR(file
, "fclose");
5781 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
5784 MsgInfo
*msginfo
= compose
->targetinfo
;
5786 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
5787 if (!msginfo
) return -1;
5789 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
5792 item
= msginfo
->folder
;
5793 cm_return_val_if_fail(item
!= NULL
, -1);
5795 if (procmsg_msg_exist(msginfo
) &&
5796 (folder_has_parent_of_type(item
, F_QUEUE
) ||
5797 folder_has_parent_of_type(item
, F_DRAFT
)
5798 || msginfo
== compose
->autosaved_draft
)) {
5799 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
5800 g_warning("can't remove the old message\n");
5803 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
5810 static void compose_remove_draft(Compose
*compose
)
5813 MsgInfo
*msginfo
= compose
->targetinfo
;
5814 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
5816 if (procmsg_msg_exist(msginfo
)) {
5817 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
5822 gint
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
5823 gboolean remove_reedit_target
)
5825 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
5828 static gboolean
compose_warn_encryption(Compose
*compose
)
5830 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
5831 AlertValue val
= G_ALERTALTERNATE
;
5833 if (warning
== NULL
)
5836 val
= alertpanel_full(_("Encryption warning"), warning
,
5837 GTK_STOCK_CANCEL
, _("+C_ontinue"), NULL
,
5838 TRUE
, NULL
, ALERT_WARNING
, G_ALERTALTERNATE
);
5839 if (val
& G_ALERTDISABLE
) {
5840 val
&= ~G_ALERTDISABLE
;
5841 if (val
== G_ALERTALTERNATE
)
5842 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
5846 if (val
== G_ALERTALTERNATE
) {
5853 static gint
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
5854 gchar
**msgpath
, gboolean check_subject
,
5855 gboolean remove_reedit_target
)
5862 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
5863 gboolean err
= FALSE
;
5865 debug_print("queueing message...\n");
5866 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5868 if (compose_check_entries(compose
, check_subject
) == FALSE
) {
5869 if (compose
->batch
) {
5870 gtk_widget_show_all(compose
->window
);
5875 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
5876 g_warning("can't get recipient list.");
5880 if (compose
->to_list
) {
5881 if (compose
->account
->protocol
!= A_NNTP
)
5882 mailac
= compose
->account
;
5883 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
5884 mailac
= cur_account
;
5885 else if (!(mailac
= compose_current_mail_account())) {
5886 alertpanel_error(_("No account for sending mails available!"));
5891 if (compose
->newsgroup_list
) {
5892 if (compose
->account
->protocol
== A_NNTP
)
5893 newsac
= compose
->account
;
5895 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5900 /* write queue header */
5901 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5902 G_DIR_SEPARATOR
, compose
, (guint
) rand());
5903 debug_print("queuing to %s\n", tmp
);
5904 if ((fp
= g_fopen(tmp
, "wb")) == NULL
) {
5905 FILE_OP_ERROR(tmp
, "fopen");
5910 if (change_file_mode_rw(fp
, tmp
) < 0) {
5911 FILE_OP_ERROR(tmp
, "chmod");
5912 g_warning("can't change file mode\n");
5915 /* queueing variables */
5916 err
|= (fprintf(fp
, "AF:\n") < 0);
5917 err
|= (fprintf(fp
, "NF:0\n") < 0);
5918 err
|= (fprintf(fp
, "PS:10\n") < 0);
5919 err
|= (fprintf(fp
, "SRH:1\n") < 0);
5920 err
|= (fprintf(fp
, "SFN:\n") < 0);
5921 err
|= (fprintf(fp
, "DSR:\n") < 0);
5923 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
5925 err
|= (fprintf(fp
, "MID:\n") < 0);
5926 err
|= (fprintf(fp
, "CFG:\n") < 0);
5927 err
|= (fprintf(fp
, "PT:0\n") < 0);
5928 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
5929 err
|= (fprintf(fp
, "RQ:\n") < 0);
5931 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
5933 err
|= (fprintf(fp
, "SSV:\n") < 0);
5935 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
5937 err
|= (fprintf(fp
, "NSV:\n") < 0);
5938 err
|= (fprintf(fp
, "SSH:\n") < 0);
5939 /* write recepient list */
5940 if (compose
->to_list
) {
5941 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
5942 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
5944 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
5945 err
|= (fprintf(fp
, "\n") < 0);
5947 /* write newsgroup list */
5948 if (compose
->newsgroup_list
) {
5949 err
|= (fprintf(fp
, "NG:") < 0);
5950 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
5951 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
5952 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
5953 err
|= (fprintf(fp
, "\n") < 0);
5955 /* Sylpheed account IDs */
5957 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
5959 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
5962 if (compose
->privacy_system
!= NULL
) {
5963 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
5964 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
5965 if (compose
->use_encryption
) {
5967 if (!compose_warn_encryption(compose
)) {
5973 if (mailac
&& mailac
->encrypt_to_self
) {
5974 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
5975 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
5976 encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
5977 g_slist_free(tmp_list
);
5979 encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
5981 if (encdata
!= NULL
) {
5982 if (strcmp(encdata
, "_DONT_ENCRYPT_")) {
5983 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
5984 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
5986 } /* else we finally dont want to encrypt */
5988 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
5989 /* and if encdata was null, it means there's been a problem in
5992 g_warning("failed to write queue message");
6002 /* Save copy folder */
6003 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6004 gchar
*savefolderid
;
6006 savefolderid
= compose_get_save_to(compose
);
6007 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6008 g_free(savefolderid
);
6010 /* Save copy folder */
6011 if (compose
->return_receipt
) {
6012 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6014 /* Message-ID of message replying to */
6015 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6016 gchar
*folderid
= NULL
;
6018 if (compose
->replyinfo
->folder
)
6019 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6020 if (folderid
== NULL
)
6021 folderid
= g_strdup("NULL");
6023 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6026 /* Message-ID of message forwarding to */
6027 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6028 gchar
*folderid
= NULL
;
6030 if (compose
->fwdinfo
->folder
)
6031 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6032 if (folderid
== NULL
)
6033 folderid
= g_strdup("NULL");
6035 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6039 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6040 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6042 /* end of headers */
6043 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6045 if (compose
->redirect_filename
!= NULL
) {
6046 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6054 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6058 return result
- 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6062 g_warning("failed to write queue message\n");
6068 if (fclose(fp
) == EOF
) {
6069 FILE_OP_ERROR(tmp
, "fclose");
6075 if (item
&& *item
) {
6078 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6081 g_warning("can't find queue folder\n");
6086 folder_item_scan(queue
);
6087 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6088 g_warning("can't queue the message\n");
6094 if (msgpath
== NULL
) {
6100 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6101 compose_remove_reedit_target(compose
, FALSE
);
6104 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6112 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6115 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6117 struct stat statbuf
;
6118 gchar
*type
, *subtype
;
6119 GtkTreeModel
*model
;
6122 model
= gtk_tree_view_get_model(tree_view
);
6124 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6127 gtk_tree_model_get(model
, &iter
,
6131 if (!is_file_exist(ainfo
->file
)) {
6132 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6133 AlertValue val
= alertpanel_full(_("Warning"), msg
, _("Cancel sending"), _("Ignore attachment"),
6134 NULL
, FALSE
, NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
6136 if (val
== G_ALERTDEFAULT
) {
6141 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6144 mimepart
= procmime_mimeinfo_new();
6145 mimepart
->content
= MIMECONTENT_FILE
;
6146 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6147 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6148 mimepart
->offset
= 0;
6149 mimepart
->length
= statbuf
.st_size
;
6151 type
= g_strdup(ainfo
->content_type
);
6153 if (!strchr(type
, '/')) {
6155 type
= g_strdup("application/octet-stream");
6158 subtype
= strchr(type
, '/') + 1;
6159 *(subtype
- 1) = '\0';
6160 mimepart
->type
= procmime_get_media_type(type
);
6161 mimepart
->subtype
= g_strdup(subtype
);
6164 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6165 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6166 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6167 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6168 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6169 /* Text parts with no name come from multipart/alternative
6170 * forwards. Make sure the recipient won't look at the
6171 * original HTML part by mistake. */
6172 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6173 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6177 g_hash_table_insert(mimepart
->typeparameters
,
6178 g_strdup("charset"), g_strdup(ainfo
->charset
));
6180 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6181 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6182 !strcmp2(mimepart
->subtype
, "octet-stream"))
6183 g_hash_table_insert(mimepart
->typeparameters
,
6184 g_strdup("name"), g_strdup(ainfo
->name
));
6185 g_hash_table_insert(mimepart
->dispositionparameters
,
6186 g_strdup("filename"), g_strdup(ainfo
->name
));
6187 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6190 if (mimepart
->type
== MIMETYPE_MESSAGE
6191 || mimepart
->type
== MIMETYPE_MULTIPART
)
6192 ainfo
->encoding
= ENC_BINARY
;
6193 else if (compose
->use_signing
) {
6194 if (ainfo
->encoding
== ENC_7BIT
)
6195 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6196 else if (ainfo
->encoding
== ENC_8BIT
)
6197 ainfo
->encoding
= ENC_BASE64
;
6202 procmime_encode_content(mimepart
, ainfo
->encoding
);
6204 g_node_append(parent
->node
, mimepart
->node
);
6205 } while (gtk_tree_model_iter_next(model
, &iter
));
6210 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6212 GSList
*list
= NULL
, *item
= NULL
;
6213 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6215 list
= address_list_append_with_comments(list
, str
);
6216 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6217 gchar
*spec
= item
->data
;
6218 gchar
*endofname
= strstr(spec
, " <");
6219 if (endofname
!= NULL
) {
6222 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6223 qqname
= escape_internal_quotes(qname
, '"');
6225 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6226 gchar
*addr
= g_strdup(endofname
);
6227 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6228 faddr
= g_strconcat(name
, addr
, NULL
);
6231 debug_print("new auto-quoted address: '%s'", faddr
);
6235 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6237 result
= g_strconcat(result
,
6239 (faddr
!= NULL
)? faddr
: spec
,
6242 if (faddr
!= NULL
) {
6247 slist_free_strings_full(list
);
6252 #define IS_IN_CUSTOM_HEADER(header) \
6253 (compose->account->add_customhdr && \
6254 custom_header_find(compose->account->customhdr_list, header) != NULL)
6256 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6258 const gchar
*fieldname
,
6259 const gchar
*seperator
)
6261 gchar
*str
, *fieldname_w_colon
;
6262 gboolean add_field
= FALSE
;
6264 ComposeHeaderEntry
*headerentry
;
6265 const gchar
*headerentryname
;
6266 const gchar
*trans_fieldname
;
6269 if (IS_IN_CUSTOM_HEADER(fieldname
))
6272 debug_print("Adding %s-fields\n", fieldname
);
6274 fieldstr
= g_string_sized_new(64);
6276 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6277 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6279 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6280 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6281 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6283 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6284 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6286 str
= compose_quote_list_of_addresses(ustr
);
6288 if (str
!= NULL
&& str
[0] != '\0') {
6290 g_string_append(fieldstr
, seperator
);
6291 g_string_append(fieldstr
, str
);
6300 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6301 compose_convert_header
6302 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6303 strlen(fieldname
) + 2, TRUE
);
6304 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6308 g_free(fieldname_w_colon
);
6309 g_string_free(fieldstr
, TRUE
);
6314 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6316 GString
*sh_header
= g_string_new(" ");
6318 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6320 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6321 ComposeHeaderEntry
*headerentry
;
6324 gchar
*headername_wcolon
;
6325 const gchar
*headername_trans
;
6327 gboolean standard_header
= FALSE
;
6329 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6331 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6333 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6338 if (!strstr(tmp
, ":")) {
6339 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6340 headername
= g_strdup(tmp
);
6342 headername_wcolon
= g_strdup(tmp
);
6343 headername
= g_strdup(strtok(tmp
, ":"));
6347 string
= std_headers
;
6348 while (*string
!= NULL
) {
6349 headername_trans
= prefs_common_translated_header_name(*string
);
6350 if (!strcmp(headername_trans
, headername_wcolon
))
6351 standard_header
= TRUE
;
6354 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6355 g_string_append_printf(sh_header
, "%s ", headername
);
6357 g_free(headername_wcolon
);
6359 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6360 return g_string_free(sh_header
, FALSE
);
6363 static gchar
*compose_get_header(Compose
*compose
)
6365 gchar buf
[BUFFSIZE
];
6366 const gchar
*entry_str
;
6370 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6372 gchar
*from_name
= NULL
, *from_address
= NULL
;
6375 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6376 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6378 header
= g_string_sized_new(64);
6381 get_rfc822_date(buf
, sizeof(buf
));
6382 g_string_append_printf(header
, "Date: %s\n", buf
);
6386 if (compose
->account
->name
&& *compose
->account
->name
) {
6388 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6389 tmp
= g_strdup_printf("%s <%s>",
6390 buf
, compose
->account
->address
);
6392 tmp
= g_strdup_printf("%s",
6393 compose
->account
->address
);
6395 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6396 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6398 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6399 from_address
= g_strdup(compose
->account
->address
);
6401 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6402 /* extract name and address */
6403 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6404 from_address
= g_strdup(strrchr(spec
, '<')+1);
6405 *(strrchr(from_address
, '>')) = '\0';
6406 from_name
= g_strdup(spec
);
6407 *(strrchr(from_name
, '<')) = '\0';
6410 from_address
= g_strdup(spec
);
6417 if (from_name
&& *from_name
) {
6419 compose_convert_header
6420 (compose
, buf
, sizeof(buf
), from_name
,
6421 strlen("From: "), TRUE
);
6422 QUOTE_IF_REQUIRED(name
, buf
);
6423 qname
= escape_internal_quotes(name
, '"');
6425 g_string_append_printf(header
, "From: %s <%s>\n",
6426 qname
, from_address
);
6430 g_string_append_printf(header
, "From: %s\n", from_address
);
6433 g_free(from_address
);
6436 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6439 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6442 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6446 * If this account is a NNTP account remove Bcc header from
6447 * message body since it otherwise will be publicly shown
6449 if (compose
->account
->protocol
!= A_NNTP
)
6450 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6453 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6455 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6458 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6459 strlen("Subject: "), FALSE
);
6460 g_string_append_printf(header
, "Subject: %s\n", buf
);
6466 if (compose
->account
->set_domain
&& compose
->account
->domain
) {
6467 g_snprintf(buf
, sizeof(buf
), "%s", compose
->account
->domain
);
6468 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6469 g_snprintf(buf
, sizeof(buf
), "%s",
6470 strchr(compose
->account
->address
, '@') ?
6471 strchr(compose
->account
->address
, '@')+1 :
6472 compose
->account
->address
);
6474 g_snprintf(buf
, sizeof(buf
), "%s", "");
6477 if (compose
->account
->gen_msgid
) {
6479 if (compose
->account
->msgid_with_addr
) {
6480 addr
= compose
->account
->address
;
6482 generate_msgid(buf
, sizeof(buf
), addr
);
6483 g_string_append_printf(header
, "Message-ID: <%s>\n", buf
);
6485 g_free(compose
->msgid
);
6486 compose
->msgid
= g_strdup(buf
);
6488 compose
->msgid
= NULL
;
6491 if (compose
->remove_references
== FALSE
) {
6493 if (compose
->inreplyto
&& compose
->to_list
)
6494 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6497 if (compose
->references
)
6498 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6502 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6505 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6508 if (compose
->account
->organization
&&
6509 strlen(compose
->account
->organization
) &&
6510 !IS_IN_CUSTOM_HEADER("Organization")) {
6511 compose_convert_header(compose
, buf
, sizeof(buf
),
6512 compose
->account
->organization
,
6513 strlen("Organization: "), FALSE
);
6514 g_string_append_printf(header
, "Organization: %s\n", buf
);
6517 /* Program version and system info */
6518 if (compose
->account
->gen_xmailer
&&
6519 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6520 !compose
->newsgroup_list
) {
6521 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6523 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6526 if (compose
->account
->gen_xmailer
&&
6527 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6528 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6530 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6534 /* custom headers */
6535 if (compose
->account
->add_customhdr
) {
6538 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6540 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6542 if (custom_header_is_allowed(chdr
->name
)
6543 && chdr
->value
!= NULL
6544 && *(chdr
->value
) != '\0') {
6545 compose_convert_header
6546 (compose
, buf
, sizeof(buf
),
6548 strlen(chdr
->name
) + 2, FALSE
);
6549 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6554 /* Automatic Faces and X-Faces */
6555 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6556 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6558 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6559 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6561 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6562 g_string_append_printf(header
, "Face: %s\n", buf
);
6564 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6565 g_string_append_printf(header
, "Face: %s\n", buf
);
6569 switch (compose
->priority
) {
6570 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6571 "X-Priority: 1 (Highest)\n");
6573 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6574 "X-Priority: 2 (High)\n");
6576 case PRIORITY_NORMAL
: break;
6577 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6578 "X-Priority: 4 (Low)\n");
6580 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6581 "X-Priority: 5 (Lowest)\n");
6583 default: debug_print("compose: priority unknown : %d\n",
6587 /* Request Return Receipt */
6588 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6589 if (compose
->return_receipt
) {
6590 if (compose
->account
->name
6591 && *compose
->account
->name
) {
6592 compose_convert_header(compose
, buf
, sizeof(buf
),
6593 compose
->account
->name
,
6594 strlen("Disposition-Notification-To: "),
6596 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, compose
->account
->address
);
6598 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", compose
->account
->address
);
6602 /* get special headers */
6603 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6604 ComposeHeaderEntry
*headerentry
;
6607 gchar
*headername_wcolon
;
6608 const gchar
*headername_trans
;
6611 gboolean standard_header
= FALSE
;
6613 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6615 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6617 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6622 if (!strstr(tmp
, ":")) {
6623 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6624 headername
= g_strdup(tmp
);
6626 headername_wcolon
= g_strdup(tmp
);
6627 headername
= g_strdup(strtok(tmp
, ":"));
6631 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6632 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6633 subst_char(headervalue
, '\r', ' ');
6634 subst_char(headervalue
, '\n', ' ');
6635 string
= std_headers
;
6636 while (*string
!= NULL
) {
6637 headername_trans
= prefs_common_translated_header_name(*string
);
6638 if (!strcmp(headername_trans
, headername_wcolon
))
6639 standard_header
= TRUE
;
6642 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6643 g_string_append_printf(header
, "%s %s\n", headername_wcolon
, headervalue
);
6646 g_free(headername_wcolon
);
6650 g_string_free(header
, FALSE
);
6655 #undef IS_IN_CUSTOM_HEADER
6657 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6658 gint header_len
, gboolean addr_field
)
6660 gchar
*tmpstr
= NULL
;
6661 const gchar
*out_codeset
= NULL
;
6663 cm_return_if_fail(src
!= NULL
);
6664 cm_return_if_fail(dest
!= NULL
);
6666 if (len
< 1) return;
6668 tmpstr
= g_strdup(src
);
6670 subst_char(tmpstr
, '\n', ' ');
6671 subst_char(tmpstr
, '\r', ' ');
6674 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6675 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6676 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
6681 codeconv_set_strict(TRUE
);
6682 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6683 conv_get_charset_str(compose
->out_encoding
));
6684 codeconv_set_strict(FALSE
);
6686 if (!dest
|| *dest
== '\0') {
6687 gchar
*test_conv_global_out
= NULL
;
6688 gchar
*test_conv_reply
= NULL
;
6690 /* automatic mode. be automatic. */
6691 codeconv_set_strict(TRUE
);
6693 out_codeset
= conv_get_outgoing_charset_str();
6695 debug_print("trying to convert to %s\n", out_codeset
);
6696 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
6699 if (!test_conv_global_out
&& compose
->orig_charset
6700 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
6701 out_codeset
= compose
->orig_charset
;
6702 debug_print("failure; trying to convert to %s\n", out_codeset
);
6703 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
6706 if (!test_conv_global_out
&& !test_conv_reply
) {
6708 out_codeset
= CS_INTERNAL
;
6709 debug_print("finally using %s\n", out_codeset
);
6711 g_free(test_conv_global_out
);
6712 g_free(test_conv_reply
);
6713 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6715 codeconv_set_strict(FALSE
);
6720 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
6724 cm_return_if_fail(user_data
!= NULL
);
6726 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
6727 g_strstrip(address
);
6728 if (*address
!= '\0') {
6729 gchar
*name
= procheader_get_fromname(address
);
6730 extract_address(address
);
6731 #ifndef USE_NEW_ADDRBOOK
6732 addressbook_add_contact(name
, address
, NULL
, NULL
);
6734 debug_print("%s: %s\n", name
, address
);
6735 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
6736 debug_print( "addressbook_add_contact - added\n" );
6743 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
6745 GtkWidget
*menuitem
;
6748 cm_return_if_fail(menu
!= NULL
);
6749 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
6751 menuitem
= gtk_separator_menu_item_new();
6752 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
6753 gtk_widget_show(menuitem
);
6755 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6756 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
6758 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
6759 g_strstrip(address
);
6760 if (*address
== '\0') {
6761 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
6764 g_signal_connect(G_OBJECT(menuitem
), "activate",
6765 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
6766 gtk_widget_show(menuitem
);
6769 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
6772 if (strcmp(header
, "")) {
6773 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
6777 void compose_add_extra_header_entries(GtkListStore
*model
)
6781 gchar buf
[BUFFSIZE
];
6784 if (extra_headers
== NULL
) {
6785 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
6786 if ((exh
= g_fopen(exhrc
, "rb")) == NULL
) {
6787 debug_print("extra headers file not found\n");
6788 goto extra_headers_done
;
6790 while (fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
6791 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
6792 while (lastc
>= 0 && buf
[lastc
] != ':')
6793 buf
[lastc
--] = '\0';
6794 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
6795 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
6796 if (custom_header_is_allowed(buf
)) {
6798 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
6801 g_message("disallowed extra header line: %s\n", buf
);
6805 g_message("invalid extra header line: %s\n", buf
);
6811 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
6812 extra_headers
= g_slist_reverse(extra_headers
);
6814 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
6817 static void compose_create_header_entry(Compose
*compose
)
6819 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6826 const gchar
*header
= NULL
;
6827 ComposeHeaderEntry
*headerentry
;
6828 gboolean standard_header
= FALSE
;
6829 GtkListStore
*model
;
6832 headerentry
= g_new0(ComposeHeaderEntry
, 1);
6834 /* Combo box model */
6835 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
6836 #if !GTK_CHECK_VERSION(2, 24, 0)
6837 combo
= gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model
), 0);
6839 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
6841 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
6843 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
6845 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
6846 COMPOSE_NEWSGROUPS
);
6847 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
6849 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
6850 COMPOSE_FOLLOWUPTO
);
6851 compose_add_extra_header_entries(model
);
6854 #if GTK_CHECK_VERSION(2, 24, 0)
6855 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
6856 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
6857 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
6858 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
6859 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
6861 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
6862 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
6863 G_CALLBACK(compose_grab_focus_cb
), compose
);
6864 gtk_widget_show(combo
);
6867 l
= g_list_prepend(l
, gtk_bin_get_child(GTK_BIN(combo
)));
6868 gtk_container_set_focus_chain(GTK_CONTAINER(combo
), l
);
6871 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
6872 compose
->header_nextrow
, compose
->header_nextrow
+1,
6873 GTK_SHRINK
, GTK_FILL
, 0, 0);
6874 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
6875 const gchar
*last_header_entry
= gtk_entry_get_text(
6876 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
6878 while (*string
!= NULL
) {
6879 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
6880 standard_header
= TRUE
;
6883 if (standard_header
)
6884 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
6886 if (!compose
->header_last
|| !standard_header
) {
6887 switch(compose
->account
->protocol
) {
6889 header
= prefs_common_translated_header_name("Newsgroups:");
6892 header
= prefs_common_translated_header_name("To:");
6897 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
6899 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
6900 G_CALLBACK(compose_grab_focus_cb
), compose
);
6902 /* Entry field with cleanup button */
6903 button
= gtk_button_new();
6904 gtk_button_set_image(GTK_BUTTON(button
),
6905 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
6906 gtk_widget_show(button
);
6907 CLAWS_SET_TIP(button
,
6908 _("Delete entry contents"));
6909 entry
= gtk_entry_new();
6910 gtk_widget_show(entry
);
6911 CLAWS_SET_TIP(entry
,
6912 _("Use <tab> to autocomplete from addressbook"));
6913 hbox
= gtk_hbox_new (FALSE
, 0);
6914 gtk_widget_show(hbox
);
6915 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
6916 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
6917 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
6918 compose
->header_nextrow
, compose
->header_nextrow
+1,
6919 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
6921 g_signal_connect(G_OBJECT(entry
), "key-press-event",
6922 G_CALLBACK(compose_headerentry_key_press_event_cb
),
6924 g_signal_connect(G_OBJECT(entry
), "changed",
6925 G_CALLBACK(compose_headerentry_changed_cb
),
6927 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
6928 G_CALLBACK(compose_grab_focus_cb
), compose
);
6930 g_signal_connect(G_OBJECT(button
), "clicked",
6931 G_CALLBACK(compose_headerentry_button_clicked_cb
),
6935 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
6936 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
6937 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
6938 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
6939 G_CALLBACK(compose_header_drag_received_cb
),
6941 g_signal_connect(G_OBJECT(entry
), "drag-drop",
6942 G_CALLBACK(compose_drag_drop
),
6944 g_signal_connect(G_OBJECT(entry
), "populate-popup",
6945 G_CALLBACK(compose_entry_popup_extend
),
6948 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
6950 headerentry
->compose
= compose
;
6951 headerentry
->combo
= combo
;
6952 headerentry
->entry
= entry
;
6953 headerentry
->button
= button
;
6954 headerentry
->hbox
= hbox
;
6955 headerentry
->headernum
= compose
->header_nextrow
;
6956 headerentry
->type
= PREF_NONE
;
6958 compose
->header_nextrow
++;
6959 compose
->header_last
= headerentry
;
6960 compose
->header_list
=
6961 g_slist_append(compose
->header_list
,
6965 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
6966 gchar
*text
, ComposePrefType pref_type
)
6968 ComposeHeaderEntry
*last_header
= compose
->header_last
;
6969 gchar
*tmp
= g_strdup(text
), *email
;
6970 gboolean replyto_hdr
;
6972 replyto_hdr
= (!strcasecmp(header
,
6973 prefs_common_translated_header_name("Reply-To:")) ||
6975 prefs_common_translated_header_name("Followup-To:")) ||
6977 prefs_common_translated_header_name("In-Reply-To:")));
6979 extract_address(tmp
);
6980 email
= g_utf8_strdown(tmp
, -1);
6982 if (replyto_hdr
== FALSE
&&
6983 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
6985 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6986 header
, text
, (gint
) pref_type
);
6992 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
6993 gtk_entry_set_text(GTK_ENTRY(
6994 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
6996 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
6997 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
6998 last_header
->type
= pref_type
;
7000 if (replyto_hdr
== FALSE
)
7001 g_hash_table_insert(compose
->email_hashtable
, email
,
7002 GUINT_TO_POINTER(1));
7009 static void compose_destroy_headerentry(Compose
*compose
,
7010 ComposeHeaderEntry
*headerentry
)
7012 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7015 extract_address(text
);
7016 email
= g_utf8_strdown(text
, -1);
7017 g_hash_table_remove(compose
->email_hashtable
, email
);
7021 gtk_widget_destroy(headerentry
->combo
);
7022 gtk_widget_destroy(headerentry
->entry
);
7023 gtk_widget_destroy(headerentry
->button
);
7024 gtk_widget_destroy(headerentry
->hbox
);
7025 g_free(headerentry
);
7028 static void compose_remove_header_entries(Compose
*compose
)
7031 for (list
= compose
->header_list
; list
; list
= list
->next
)
7032 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7034 compose
->header_last
= NULL
;
7035 g_slist_free(compose
->header_list
);
7036 compose
->header_list
= NULL
;
7037 compose
->header_nextrow
= 1;
7038 compose_create_header_entry(compose
);
7041 static GtkWidget
*compose_create_header(Compose
*compose
)
7043 GtkWidget
*from_optmenu_hbox
;
7044 GtkWidget
*header_scrolledwin_main
;
7045 GtkWidget
*header_table_main
;
7046 GtkWidget
*header_scrolledwin
;
7047 GtkWidget
*header_table
;
7049 /* parent with account selection and from header */
7050 header_scrolledwin_main
= gtk_scrolled_window_new(NULL
, NULL
);
7051 gtk_widget_show(header_scrolledwin_main
);
7052 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7054 header_table_main
= gtk_table_new(2, 2, FALSE
);
7055 gtk_widget_show(header_table_main
);
7056 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7057 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main
), header_table_main
);
7058 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main
)))), GTK_SHADOW_NONE
);
7060 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7061 gtk_table_attach(GTK_TABLE(header_table_main
), from_optmenu_hbox
,
7062 0, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7064 /* child with header labels and entries */
7065 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7066 gtk_widget_show(header_scrolledwin
);
7067 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7069 header_table
= gtk_table_new(2, 2, FALSE
);
7070 gtk_widget_show(header_table
);
7071 gtk_container_set_border_width(GTK_CONTAINER(header_table
), BORDER_WIDTH
);
7072 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
7073 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin
)))), GTK_SHADOW_NONE
);
7075 gtk_table_attach(GTK_TABLE(header_table_main
), header_scrolledwin
,
7076 0, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 0, 2);
7078 compose
->header_table
= header_table
;
7079 compose
->header_list
= NULL
;
7080 compose
->header_nextrow
= 0;
7082 compose_create_header_entry(compose
);
7084 compose
->table
= NULL
;
7086 return header_scrolledwin_main
;
7089 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7091 Compose
*compose
= (Compose
*)data
;
7092 GdkEventButton event
;
7095 event
.time
= gtk_get_current_event_time();
7097 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7100 static GtkWidget
*compose_create_attach(Compose
*compose
)
7102 GtkWidget
*attach_scrwin
;
7103 GtkWidget
*attach_clist
;
7105 GtkListStore
*store
;
7106 GtkCellRenderer
*renderer
;
7107 GtkTreeViewColumn
*column
;
7108 GtkTreeSelection
*selection
;
7110 /* attachment list */
7111 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7112 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7113 GTK_POLICY_AUTOMATIC
,
7114 GTK_POLICY_AUTOMATIC
);
7115 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7117 store
= gtk_list_store_new(N_ATTACH_COLS
,
7123 G_TYPE_AUTO_POINTER
,
7125 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7126 (GTK_TREE_MODEL(store
)));
7127 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7128 g_object_unref(store
);
7130 renderer
= gtk_cell_renderer_text_new();
7131 column
= gtk_tree_view_column_new_with_attributes
7132 (_("Mime type"), renderer
, "text",
7133 COL_MIMETYPE
, NULL
);
7134 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7136 renderer
= gtk_cell_renderer_text_new();
7137 column
= gtk_tree_view_column_new_with_attributes
7138 (_("Size"), renderer
, "text",
7140 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7142 renderer
= gtk_cell_renderer_text_new();
7143 column
= gtk_tree_view_column_new_with_attributes
7144 (_("Name"), renderer
, "text",
7146 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7148 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7149 prefs_common
.use_stripes_everywhere
);
7150 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7151 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7153 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7154 G_CALLBACK(attach_selected
), compose
);
7155 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7156 G_CALLBACK(attach_button_pressed
), compose
);
7157 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7158 G_CALLBACK(popup_attach_button_pressed
), compose
);
7159 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7160 G_CALLBACK(attach_key_pressed
), compose
);
7163 gtk_drag_dest_set(attach_clist
,
7164 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7165 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7166 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7167 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7168 G_CALLBACK(compose_attach_drag_received_cb
),
7170 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7171 G_CALLBACK(compose_drag_drop
),
7174 compose
->attach_scrwin
= attach_scrwin
;
7175 compose
->attach_clist
= attach_clist
;
7177 return attach_scrwin
;
7180 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
);
7181 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7183 static GtkWidget
*compose_create_others(Compose
*compose
)
7186 GtkWidget
*savemsg_checkbtn
;
7187 GtkWidget
*savemsg_combo
;
7188 GtkWidget
*savemsg_select
;
7191 gchar
*folderidentifier
;
7193 /* Table for settings */
7194 table
= gtk_table_new(3, 1, FALSE
);
7195 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7196 gtk_widget_show(table
);
7197 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
7200 /* Save Message to folder */
7201 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7202 gtk_widget_show(savemsg_checkbtn
);
7203 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7204 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7205 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7207 g_signal_connect(G_OBJECT(savemsg_checkbtn
), "toggled",
7208 G_CALLBACK(compose_savemsg_checkbtn_cb
), compose
);
7210 #if !GTK_CHECK_VERSION(2, 24, 0)
7211 savemsg_combo
= gtk_combo_box_entry_new_text();
7213 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7215 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7216 compose
->savemsg_combo
= savemsg_combo
;
7217 gtk_widget_show(savemsg_combo
);
7219 if (prefs_common
.compose_save_to_history
)
7220 #if !GTK_CHECK_VERSION(2, 24, 0)
7221 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo
),
7222 prefs_common
.compose_save_to_history
);
7224 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7225 prefs_common
.compose_save_to_history
);
7227 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
7228 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7229 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7230 G_CALLBACK(compose_grab_focus_cb
), compose
);
7231 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7232 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7233 (compose
->account
, F_OUTBOX
));
7234 compose_set_save_to(compose
, folderidentifier
);
7235 g_free(folderidentifier
);
7238 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7239 gtk_widget_show(savemsg_select
);
7240 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7241 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7242 G_CALLBACK(compose_savemsg_select_cb
),
7248 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
)
7250 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
),
7251 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
)));
7254 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7259 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
);
7262 path
= folder_item_get_identifier(dest
);
7264 compose_set_save_to(compose
, path
);
7268 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7269 GdkAtom clip
, GtkTextIter
*insert_place
);
7272 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7276 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7278 if (event
->button
== 3) {
7280 GtkTextIter sel_start
, sel_end
;
7281 gboolean stuff_selected
;
7283 /* move the cursor to allow GtkAspell to check the word
7284 * under the mouse */
7285 if (event
->x
&& event
->y
) {
7286 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7287 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7289 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7292 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7293 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7296 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7298 &sel_start
, &sel_end
);
7300 gtk_text_buffer_place_cursor (buffer
, &iter
);
7301 /* reselect stuff */
7303 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7304 gtk_text_buffer_select_range(buffer
,
7305 &sel_start
, &sel_end
);
7307 return FALSE
; /* pass the event so that the right-click goes through */
7310 if (event
->button
== 2) {
7315 /* get the middle-click position to paste at the correct place */
7316 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7317 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7319 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7322 entry_paste_clipboard(compose
, text
,
7323 prefs_common
.linewrap_pastes
,
7324 GDK_SELECTION_PRIMARY
, &iter
);
7332 static void compose_spell_menu_changed(void *data
)
7334 Compose
*compose
= (Compose
*)data
;
7336 GtkWidget
*menuitem
;
7337 GtkWidget
*parent_item
;
7338 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7341 if (compose
->gtkaspell
== NULL
)
7344 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7345 "/Menu/Spelling/Options");
7347 /* setting the submenu removes /Spelling/Options from the factory
7348 * so we need to save it */
7350 if (parent_item
== NULL
) {
7351 parent_item
= compose
->aspell_options_menu
;
7352 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7354 compose
->aspell_options_menu
= parent_item
;
7356 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7358 spell_menu
= g_slist_reverse(spell_menu
);
7359 for (items
= spell_menu
;
7360 items
; items
= items
->next
) {
7361 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7362 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7363 gtk_widget_show(GTK_WIDGET(menuitem
));
7365 g_slist_free(spell_menu
);
7367 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7368 gtk_widget_show(parent_item
);
7371 static void compose_dict_changed(void *data
)
7373 Compose
*compose
= (Compose
*) data
;
7375 if(!compose
->gtkaspell
)
7377 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7380 gtkaspell_highlight_all(compose
->gtkaspell
);
7381 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7385 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7387 Compose
*compose
= (Compose
*)data
;
7388 GdkEventButton event
;
7391 event
.time
= gtk_get_current_event_time();
7395 return text_clicked(compose
->text
, &event
, compose
);
7398 static gboolean compose_force_window_origin
= TRUE
;
7399 static Compose
*compose_create(PrefsAccount
*account
,
7408 GtkWidget
*handlebox
;
7410 GtkWidget
*notebook
;
7412 GtkWidget
*attach_hbox
;
7413 GtkWidget
*attach_lab1
;
7414 GtkWidget
*attach_lab2
;
7419 GtkWidget
*subject_hbox
;
7420 GtkWidget
*subject_frame
;
7421 GtkWidget
*subject_entry
;
7425 GtkWidget
*edit_vbox
;
7426 GtkWidget
*ruler_hbox
;
7428 GtkWidget
*scrolledwin
;
7430 GtkTextBuffer
*buffer
;
7431 GtkClipboard
*clipboard
;
7433 UndoMain
*undostruct
;
7435 GtkWidget
*popupmenu
;
7436 GtkWidget
*tmpl_menu
;
7437 GtkActionGroup
*action_group
= NULL
;
7440 GtkAspell
* gtkaspell
= NULL
;
7443 static GdkGeometry geometry
;
7445 cm_return_val_if_fail(account
!= NULL
, NULL
);
7447 debug_print("Creating compose window...\n");
7448 compose
= g_new0(Compose
, 1);
7450 compose
->batch
= batch
;
7451 compose
->account
= account
;
7452 compose
->folder
= folder
;
7454 compose
->mutex
= cm_mutex_new();
7455 compose
->set_cursor_pos
= -1;
7457 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7459 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7460 gtk_widget_set_size_request(window
, prefs_common
.compose_width
,
7461 prefs_common
.compose_height
);
7463 if (!geometry
.max_width
) {
7464 geometry
.max_width
= gdk_screen_width();
7465 geometry
.max_height
= gdk_screen_height();
7468 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7469 &geometry
, GDK_HINT_MAX_SIZE
);
7470 if (!geometry
.min_width
) {
7471 geometry
.min_width
= 600;
7472 geometry
.min_height
= 440;
7474 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7475 &geometry
, GDK_HINT_MIN_SIZE
);
7477 #ifndef GENERIC_UMPC
7478 if (compose_force_window_origin
)
7479 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7480 prefs_common
.compose_y
);
7482 g_signal_connect(G_OBJECT(window
), "delete_event",
7483 G_CALLBACK(compose_delete_cb
), compose
);
7484 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7485 gtk_widget_realize(window
);
7487 gtkut_widget_set_composer_icon(window
);
7489 vbox
= gtk_vbox_new(FALSE
, 0);
7490 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7492 compose
->ui_manager
= gtk_ui_manager_new();
7493 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7494 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7495 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7496 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7497 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7498 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7499 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7500 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7501 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7502 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7504 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7506 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7507 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7509 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7511 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7512 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7513 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7516 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7517 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7518 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7519 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7520 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7521 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7522 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7523 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7524 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7525 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7526 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7527 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7528 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7531 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7532 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7533 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7535 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7536 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7537 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7539 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7540 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7541 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7542 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7544 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7546 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7547 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7548 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7549 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7550 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7551 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7552 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7553 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7554 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7555 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7556 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7557 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7558 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7559 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7560 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7562 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7564 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7565 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7566 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7567 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7568 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7570 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7572 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7576 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7577 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7578 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7579 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7580 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7581 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7585 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7586 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7587 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7588 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7589 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7591 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7592 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7593 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7594 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7595 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7598 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7599 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7600 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7601 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7602 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7603 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7604 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7606 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7607 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7608 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7609 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7610 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7612 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7614 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7615 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7616 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7617 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7618 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7620 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7621 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
)
7622 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
)
7623 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7625 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7627 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7628 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
)
7629 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
)
7631 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7633 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7634 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
)
7635 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7637 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7638 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
)
7639 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7641 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7643 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7644 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
)
7645 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7646 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7647 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7649 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7650 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
)
7651 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
)
7652 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7653 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7655 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7656 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7657 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7658 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
7659 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
7660 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
7662 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
7663 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
7664 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
)
7666 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
7667 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
7668 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
7672 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
7673 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
7674 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
7675 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7676 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
7677 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7680 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
7682 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
7683 gtk_widget_show_all(menubar
);
7685 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
7686 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
7688 if (prefs_common
.toolbar_detachable
) {
7689 handlebox
= gtk_handle_box_new();
7691 handlebox
= gtk_hbox_new(FALSE
, 0);
7693 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
7695 gtk_widget_realize(handlebox
);
7696 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
7699 vbox2
= gtk_vbox_new(FALSE
, 2);
7700 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
7701 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
7704 notebook
= gtk_notebook_new();
7705 gtk_widget_show(notebook
);
7707 /* header labels and entries */
7708 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7709 compose_create_header(compose
),
7710 gtk_label_new_with_mnemonic(_("Hea_der")));
7711 /* attachment list */
7712 attach_hbox
= gtk_hbox_new(FALSE
, 0);
7713 gtk_widget_show(attach_hbox
);
7715 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
7716 gtk_widget_show(attach_lab1
);
7717 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
7719 attach_lab2
= gtk_label_new("");
7720 gtk_widget_show(attach_lab2
);
7721 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
7723 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7724 compose_create_attach(compose
),
7727 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
7728 compose_create_others(compose
),
7729 gtk_label_new_with_mnemonic(_("Othe_rs")));
7732 subject_hbox
= gtk_hbox_new(FALSE
, 0);
7733 gtk_widget_show(subject_hbox
);
7735 subject_frame
= gtk_frame_new(NULL
);
7736 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
7737 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
7738 gtk_widget_show(subject_frame
);
7740 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
7741 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
7742 gtk_widget_show(subject
);
7744 label
= gtk_label_new_with_mnemonic(_("Subject:"));
7745 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
7746 gtk_widget_show(label
);
7749 subject_entry
= claws_spell_entry_new();
7751 subject_entry
= gtk_entry_new();
7753 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
7754 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
7755 G_CALLBACK(compose_grab_focus_cb
), compose
);
7756 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
7757 gtk_widget_show(subject_entry
);
7758 compose
->subject_entry
= subject_entry
;
7759 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
7761 edit_vbox
= gtk_vbox_new(FALSE
, 0);
7763 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
7766 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
7767 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
7769 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
7770 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
7771 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
7775 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7776 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
7777 GTK_POLICY_AUTOMATIC
,
7778 GTK_POLICY_AUTOMATIC
);
7779 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
7781 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
7783 text
= gtk_text_view_new();
7784 if (prefs_common
.show_compose_margin
) {
7785 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
7786 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
7788 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7789 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
7790 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
7791 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
7792 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
7794 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
7795 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
7796 G_CALLBACK(compose_edit_size_alloc
),
7798 g_signal_connect(G_OBJECT(buffer
), "changed",
7799 G_CALLBACK(compose_changed_cb
), compose
);
7800 g_signal_connect(G_OBJECT(text
), "grab_focus",
7801 G_CALLBACK(compose_grab_focus_cb
), compose
);
7802 g_signal_connect(G_OBJECT(buffer
), "insert_text",
7803 G_CALLBACK(text_inserted
), compose
);
7804 g_signal_connect(G_OBJECT(text
), "button_press_event",
7805 G_CALLBACK(text_clicked
), compose
);
7806 g_signal_connect(G_OBJECT(text
), "popup-menu",
7807 G_CALLBACK(compose_popup_menu
), compose
);
7808 g_signal_connect(G_OBJECT(subject_entry
), "changed",
7809 G_CALLBACK(compose_changed_cb
), compose
);
7810 g_signal_connect(G_OBJECT(subject_entry
), "activate",
7811 G_CALLBACK(compose_subject_entry_activated
), compose
);
7814 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7815 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7816 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7817 g_signal_connect(G_OBJECT(text
), "drag_data_received",
7818 G_CALLBACK(compose_insert_drag_received_cb
),
7820 g_signal_connect(G_OBJECT(text
), "drag-drop",
7821 G_CALLBACK(compose_drag_drop
),
7823 g_signal_connect(G_OBJECT(text
), "key-press-event",
7824 G_CALLBACK(completion_set_focus_to_subject
),
7826 gtk_widget_show_all(vbox
);
7828 /* pane between attach clist and text */
7829 paned
= gtk_vpaned_new();
7830 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
7831 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
7832 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
7833 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
7834 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
7835 G_CALLBACK(compose_notebook_size_alloc
), paned
);
7837 gtk_widget_show_all(paned
);
7840 if (prefs_common
.textfont
) {
7841 PangoFontDescription
*font_desc
;
7843 font_desc
= pango_font_description_from_string
7844 (prefs_common
.textfont
);
7846 gtk_widget_modify_font(text
, font_desc
);
7847 pango_font_description_free(font_desc
);
7851 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
7852 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
7853 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
7854 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
7855 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
7856 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
7860 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
7862 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
7863 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
7864 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
7866 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
7868 undostruct
= undo_init(text
);
7869 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
7872 address_completion_start(window
);
7874 compose
->window
= window
;
7875 compose
->vbox
= vbox
;
7876 compose
->menubar
= menubar
;
7877 compose
->handlebox
= handlebox
;
7879 compose
->vbox2
= vbox2
;
7881 compose
->paned
= paned
;
7883 compose
->attach_label
= attach_lab2
;
7885 compose
->notebook
= notebook
;
7886 compose
->edit_vbox
= edit_vbox
;
7887 compose
->ruler_hbox
= ruler_hbox
;
7888 compose
->ruler
= ruler
;
7889 compose
->scrolledwin
= scrolledwin
;
7890 compose
->text
= text
;
7892 compose
->focused_editable
= NULL
;
7894 compose
->popupmenu
= popupmenu
;
7896 compose
->tmpl_menu
= tmpl_menu
;
7898 compose
->mode
= mode
;
7899 compose
->rmode
= mode
;
7901 compose
->targetinfo
= NULL
;
7902 compose
->replyinfo
= NULL
;
7903 compose
->fwdinfo
= NULL
;
7905 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
7906 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
7908 compose
->replyto
= NULL
;
7910 compose
->bcc
= NULL
;
7911 compose
->followup_to
= NULL
;
7913 compose
->ml_post
= NULL
;
7915 compose
->inreplyto
= NULL
;
7916 compose
->references
= NULL
;
7917 compose
->msgid
= NULL
;
7918 compose
->boundary
= NULL
;
7920 compose
->autowrap
= prefs_common
.autowrap
;
7921 compose
->autoindent
= prefs_common
.auto_indent
;
7922 compose
->use_signing
= FALSE
;
7923 compose
->use_encryption
= FALSE
;
7924 compose
->privacy_system
= NULL
;
7926 compose
->modified
= FALSE
;
7928 compose
->return_receipt
= FALSE
;
7930 compose
->to_list
= NULL
;
7931 compose
->newsgroup_list
= NULL
;
7933 compose
->undostruct
= undostruct
;
7935 compose
->sig_str
= NULL
;
7937 compose
->exteditor_file
= NULL
;
7938 compose
->exteditor_pid
= -1;
7939 compose
->exteditor_tag
= -1;
7940 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
7942 compose
->folder_update_callback_id
=
7943 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
7944 compose_update_folder_hook
,
7945 (gpointer
) compose
);
7948 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
7949 if (mode
!= COMPOSE_REDIRECT
) {
7950 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
7951 strcmp(prefs_common
.dictionary
, "")) {
7952 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
7953 prefs_common
.alt_dictionary
,
7954 conv_get_locale_charset_str(),
7955 prefs_common
.misspelled_col
,
7956 prefs_common
.check_while_typing
,
7957 prefs_common
.recheck_when_changing_dict
,
7958 prefs_common
.use_alternate
,
7959 prefs_common
.use_both_dicts
,
7960 GTK_TEXT_VIEW(text
),
7961 GTK_WINDOW(compose
->window
),
7962 compose_dict_changed
,
7963 compose_spell_menu_changed
,
7966 alertpanel_error(_("Spell checker could not "
7968 gtkaspell_checkers_strerror());
7969 gtkaspell_checkers_reset_error();
7971 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
7975 compose
->gtkaspell
= gtkaspell
;
7976 compose_spell_menu_changed(compose
);
7977 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
7980 compose_select_account(compose
, account
, TRUE
);
7982 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
7983 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
7985 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
7986 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
7988 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
7989 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
7991 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
7992 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
7994 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
7995 if (account
->protocol
!= A_NNTP
)
7996 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
7997 prefs_common_translated_header_name("To:"));
7999 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8000 prefs_common_translated_header_name("Newsgroups:"));
8002 #ifndef USE_NEW_ADDRBOOK
8003 addressbook_set_target_compose(compose
);
8005 if (mode
!= COMPOSE_REDIRECT
)
8006 compose_set_template_menu(compose
);
8008 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8011 compose_list
= g_list_append(compose_list
, compose
);
8013 if (!prefs_common
.show_ruler
)
8014 gtk_widget_hide(ruler_hbox
);
8016 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8019 compose
->priority
= PRIORITY_NORMAL
;
8020 compose_update_priority_menu_item(compose
);
8022 compose_set_out_encoding(compose
);
8025 compose_update_actions_menu(compose
);
8027 /* Privacy Systems menu */
8028 compose_update_privacy_systems_menu(compose
);
8030 activate_privacy_system(compose
, account
, TRUE
);
8031 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8033 gtk_widget_realize(window
);
8035 gtk_widget_show(window
);
8041 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8046 GtkWidget
*optmenubox
;
8049 GtkWidget
*from_name
= NULL
;
8051 gint num
= 0, def_menu
= 0;
8053 accounts
= account_get_list();
8054 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8056 optmenubox
= gtk_event_box_new();
8057 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8058 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8060 hbox
= gtk_hbox_new(FALSE
, 6);
8061 from_name
= gtk_entry_new();
8063 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8064 G_CALLBACK(compose_grab_focus_cb
), compose
);
8066 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8067 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8068 gchar
*name
, *from
= NULL
;
8070 if (ac
== compose
->account
) def_menu
= num
;
8072 name
= g_markup_printf_escaped(_("From: <i>%s</i>"),
8075 if (ac
== compose
->account
) {
8076 if (ac
->name
&& *ac
->name
) {
8078 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8079 from
= g_strdup_printf("%s <%s>",
8081 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8083 from
= g_strdup_printf("%s",
8085 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8088 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8093 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8095 g_signal_connect(G_OBJECT(optmenu
), "changed",
8096 G_CALLBACK(account_activated
),
8098 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8099 G_CALLBACK(compose_entry_popup_extend
),
8102 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8103 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8105 CLAWS_SET_TIP(optmenubox
,
8106 _("Account to use for this email"));
8107 CLAWS_SET_TIP(from_name
,
8108 _("Sender address to be used"));
8110 compose
->account_combo
= optmenu
;
8111 compose
->from_name
= from_name
;
8116 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8118 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8119 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8120 Compose
*compose
= (Compose
*) data
;
8122 compose
->priority
= value
;
8126 static void compose_reply_change_mode(Compose
*compose
,
8129 gboolean was_modified
= compose
->modified
;
8131 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8133 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8135 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8137 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8139 if (action
== COMPOSE_REPLY_TO_ALL
)
8141 if (action
== COMPOSE_REPLY_TO_SENDER
)
8143 if (action
== COMPOSE_REPLY_TO_LIST
)
8146 compose_remove_header_entries(compose
);
8147 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8148 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8149 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8151 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8152 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8154 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8155 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8156 compose_show_first_last_header(compose
, TRUE
);
8157 compose
->modified
= was_modified
;
8158 compose_set_title(compose
);
8161 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8163 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8164 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8165 Compose
*compose
= (Compose
*) data
;
8168 compose_reply_change_mode(compose
, value
);
8171 static void compose_update_priority_menu_item(Compose
* compose
)
8173 GtkWidget
*menuitem
= NULL
;
8174 switch (compose
->priority
) {
8175 case PRIORITY_HIGHEST
:
8176 menuitem
= gtk_ui_manager_get_widget
8177 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8180 menuitem
= gtk_ui_manager_get_widget
8181 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8183 case PRIORITY_NORMAL
:
8184 menuitem
= gtk_ui_manager_get_widget
8185 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8188 menuitem
= gtk_ui_manager_get_widget
8189 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8191 case PRIORITY_LOWEST
:
8192 menuitem
= gtk_ui_manager_get_widget
8193 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8196 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8199 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8201 Compose
*compose
= (Compose
*) data
;
8203 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8205 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8207 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8210 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8211 g_free(compose
->privacy_system
);
8212 compose
->privacy_system
= NULL
;
8213 if (systemid
!= NULL
) {
8214 compose
->privacy_system
= g_strdup(systemid
);
8216 can_sign
= privacy_system_can_sign(systemid
);
8217 can_encrypt
= privacy_system_can_encrypt(systemid
);
8220 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8222 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8223 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8226 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8228 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8229 GtkWidget
*menuitem
= NULL
;
8230 GList
*children
, *amenu
;
8231 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8232 gboolean found
= FALSE
;
8234 if (compose
->privacy_system
!= NULL
) {
8236 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8237 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8238 cm_return_if_fail(menuitem
!= NULL
);
8240 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8243 while (amenu
!= NULL
) {
8244 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8245 if (systemid
!= NULL
) {
8246 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8247 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8248 menuitem
= GTK_WIDGET(amenu
->data
);
8250 can_sign
= privacy_system_can_sign(systemid
);
8251 can_encrypt
= privacy_system_can_encrypt(systemid
);
8255 } else if (strlen(compose
->privacy_system
) == 0 &&
8256 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8257 menuitem
= GTK_WIDGET(amenu
->data
);
8260 can_encrypt
= FALSE
;
8265 amenu
= amenu
->next
;
8267 g_list_free(children
);
8268 if (menuitem
!= NULL
)
8269 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8271 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8272 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8273 "will not be able to sign or encrypt this message."),
8274 compose
->privacy_system
);
8278 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8279 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8282 static void compose_set_out_encoding(Compose
*compose
)
8284 CharSet out_encoding
;
8285 const gchar
*branch
= NULL
;
8286 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8288 switch(out_encoding
) {
8289 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8290 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8291 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8292 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8293 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8294 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8295 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8296 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8297 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8298 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8299 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8300 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8301 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8302 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8303 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8304 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8305 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8306 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8307 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8308 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8309 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8310 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8311 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8312 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8313 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8314 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8315 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8316 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8317 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8318 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8319 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8320 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8321 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8323 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8326 static void compose_set_template_menu(Compose
*compose
)
8328 GSList
*tmpl_list
, *cur
;
8332 tmpl_list
= template_get_config();
8334 menu
= gtk_menu_new();
8336 gtk_menu_set_accel_group (GTK_MENU (menu
),
8337 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8338 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8339 Template
*tmpl
= (Template
*)cur
->data
;
8340 gchar
*accel_path
= NULL
;
8341 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8342 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8343 g_signal_connect(G_OBJECT(item
), "activate",
8344 G_CALLBACK(compose_template_activate_cb
),
8346 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8347 gtk_widget_show(item
);
8348 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8349 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8353 gtk_widget_show(menu
);
8354 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8357 void compose_update_actions_menu(Compose
*compose
)
8359 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8362 static void compose_update_privacy_systems_menu(Compose
*compose
)
8364 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8365 GSList
*systems
, *cur
;
8367 GtkWidget
*system_none
;
8369 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8370 GtkWidget
*privacy_menu
= gtk_menu_new();
8372 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8373 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8375 g_signal_connect(G_OBJECT(system_none
), "activate",
8376 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8378 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8379 gtk_widget_show(system_none
);
8381 systems
= privacy_get_system_ids();
8382 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8383 gchar
*systemid
= cur
->data
;
8385 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8386 widget
= gtk_radio_menu_item_new_with_label(group
,
8387 privacy_system_get_name(systemid
));
8388 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8389 g_strdup(systemid
), g_free
);
8390 g_signal_connect(G_OBJECT(widget
), "activate",
8391 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8393 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8394 gtk_widget_show(widget
);
8397 g_slist_free(systems
);
8398 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8399 gtk_widget_show_all(privacy_menu
);
8400 gtk_widget_show_all(privacy_menuitem
);
8403 void compose_reflect_prefs_all(void)
8408 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8409 compose
= (Compose
*)cur
->data
;
8410 compose_set_template_menu(compose
);
8414 void compose_reflect_prefs_pixmap_theme(void)
8419 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8420 compose
= (Compose
*)cur
->data
;
8421 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8425 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8427 const gchar
*qmark
= NULL
;
8429 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8431 switch (compose
->mode
) {
8432 /* use forward-specific quote char */
8433 case COMPOSE_FORWARD
:
8434 case COMPOSE_FORWARD_AS_ATTACH
:
8435 case COMPOSE_FORWARD_INLINE
:
8436 if (compose
->folder
&& compose
->folder
->prefs
&&
8437 compose
->folder
->prefs
->forward_with_format
)
8438 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8439 else if (compose
->account
->forward_with_format
)
8440 qmark
= compose
->account
->forward_quotemark
;
8442 qmark
= prefs_common
.fw_quotemark
;
8445 /* use reply-specific quote char in all other modes */
8447 if (compose
->folder
&& compose
->folder
->prefs
&&
8448 compose
->folder
->prefs
->reply_with_format
)
8449 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8450 else if (compose
->account
->reply_with_format
)
8451 qmark
= compose
->account
->reply_quotemark
;
8453 qmark
= prefs_common
.quotemark
;
8457 if (qmark
== NULL
|| *qmark
== '\0')
8463 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8467 GtkTextBuffer
*buffer
;
8471 gchar
*parsed_str
= NULL
;
8472 gint cursor_pos
= 0;
8473 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8476 /* process the body */
8478 text
= GTK_TEXT_VIEW(compose
->text
);
8479 buffer
= gtk_text_view_get_buffer(text
);
8482 qmark
= compose_quote_char_from_context(compose
);
8484 if (compose
->replyinfo
!= NULL
) {
8487 gtk_text_buffer_set_text(buffer
, "", -1);
8488 mark
= gtk_text_buffer_get_insert(buffer
);
8489 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8491 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8492 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8494 } else if (compose
->fwdinfo
!= NULL
) {
8497 gtk_text_buffer_set_text(buffer
, "", -1);
8498 mark
= gtk_text_buffer_get_insert(buffer
);
8499 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8501 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8502 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8505 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8507 GtkTextIter start
, end
;
8510 gtk_text_buffer_get_start_iter(buffer
, &start
);
8511 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8512 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8514 /* clear the buffer now */
8516 gtk_text_buffer_set_text(buffer
, "", -1);
8518 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8519 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8520 procmsg_msginfo_free( dummyinfo
);
8526 gtk_text_buffer_set_text(buffer
, "", -1);
8527 mark
= gtk_text_buffer_get_insert(buffer
);
8528 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8531 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8532 compose_insert_sig(compose
, FALSE
);
8534 if (replace
&& parsed_str
) {
8535 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8536 gtk_text_buffer_place_cursor(buffer
, &iter
);
8540 cursor_pos
= quote_fmt_get_cursor_pos();
8541 compose
->set_cursor_pos
= cursor_pos
;
8542 if (cursor_pos
== -1)
8544 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8545 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8546 gtk_text_buffer_place_cursor(buffer
, &iter
);
8549 /* process the other fields */
8551 compose_template_apply_fields(compose
, tmpl
);
8552 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8553 quote_fmt_reset_vartable();
8554 compose_changed_cb(NULL
, compose
);
8557 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8558 gtkaspell_highlight_all(compose
->gtkaspell
);
8562 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8564 MsgInfo
* dummyinfo
= NULL
;
8565 MsgInfo
*msginfo
= NULL
;
8568 if (compose
->replyinfo
!= NULL
)
8569 msginfo
= compose
->replyinfo
;
8570 else if (compose
->fwdinfo
!= NULL
)
8571 msginfo
= compose
->fwdinfo
;
8573 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8574 msginfo
= dummyinfo
;
8577 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8579 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8580 compose
->gtkaspell
);
8582 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8584 quote_fmt_scan_string(tmpl
->from
);
8587 buf
= quote_fmt_get_buffer();
8589 alertpanel_error(_("Template From format error."));
8591 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
8595 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
8597 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8598 compose
->gtkaspell
);
8600 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8602 quote_fmt_scan_string(tmpl
->to
);
8605 buf
= quote_fmt_get_buffer();
8607 alertpanel_error(_("Template To format error."));
8609 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
8613 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
8615 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8616 compose
->gtkaspell
);
8618 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8620 quote_fmt_scan_string(tmpl
->cc
);
8623 buf
= quote_fmt_get_buffer();
8625 alertpanel_error(_("Template Cc format error."));
8627 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
8631 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
8633 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8634 compose
->gtkaspell
);
8636 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8638 quote_fmt_scan_string(tmpl
->bcc
);
8641 buf
= quote_fmt_get_buffer();
8643 alertpanel_error(_("Template Bcc format error."));
8645 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
8649 /* process the subject */
8650 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
8652 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8653 compose
->gtkaspell
);
8655 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8657 quote_fmt_scan_string(tmpl
->subject
);
8660 buf
= quote_fmt_get_buffer();
8662 alertpanel_error(_("Template subject format error."));
8664 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
8668 procmsg_msginfo_free( dummyinfo
);
8671 static void compose_destroy(Compose
*compose
)
8673 GtkAllocation allocation
;
8674 GtkTextBuffer
*buffer
;
8675 GtkClipboard
*clipboard
;
8677 compose_list
= g_list_remove(compose_list
, compose
);
8679 if (compose
->updating
) {
8680 debug_print("danger, not destroying anything now\n");
8681 compose
->deferred_destroy
= TRUE
;
8685 /* NOTE: address_completion_end() does nothing with the window
8686 * however this may change. */
8687 address_completion_end(compose
->window
);
8689 slist_free_strings_full(compose
->to_list
);
8690 slist_free_strings_full(compose
->newsgroup_list
);
8691 slist_free_strings_full(compose
->header_list
);
8693 slist_free_strings_full(extra_headers
);
8694 extra_headers
= NULL
;
8696 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
8698 g_hash_table_destroy(compose
->email_hashtable
);
8700 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
8701 compose
->folder_update_callback_id
);
8703 procmsg_msginfo_free(compose
->targetinfo
);
8704 procmsg_msginfo_free(compose
->replyinfo
);
8705 procmsg_msginfo_free(compose
->fwdinfo
);
8707 g_free(compose
->replyto
);
8708 g_free(compose
->cc
);
8709 g_free(compose
->bcc
);
8710 g_free(compose
->newsgroups
);
8711 g_free(compose
->followup_to
);
8713 g_free(compose
->ml_post
);
8715 g_free(compose
->inreplyto
);
8716 g_free(compose
->references
);
8717 g_free(compose
->msgid
);
8718 g_free(compose
->boundary
);
8720 g_free(compose
->redirect_filename
);
8721 if (compose
->undostruct
)
8722 undo_destroy(compose
->undostruct
);
8724 g_free(compose
->sig_str
);
8726 g_free(compose
->exteditor_file
);
8728 g_free(compose
->orig_charset
);
8730 g_free(compose
->privacy_system
);
8732 #ifndef USE_NEW_ADDRBOOK
8733 if (addressbook_get_target_compose() == compose
)
8734 addressbook_set_target_compose(NULL
);
8737 if (compose
->gtkaspell
) {
8738 gtkaspell_delete(compose
->gtkaspell
);
8739 compose
->gtkaspell
= NULL
;
8743 if (!compose
->batch
) {
8744 gtk_widget_get_allocation(compose
->window
, &allocation
);
8745 prefs_common
.compose_width
= allocation
.width
;
8746 prefs_common
.compose_height
= allocation
.height
;
8749 if (!gtk_widget_get_parent(compose
->paned
))
8750 gtk_widget_destroy(compose
->paned
);
8751 gtk_widget_destroy(compose
->popupmenu
);
8753 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
8754 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8755 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
8757 gtk_widget_destroy(compose
->window
);
8758 toolbar_destroy(compose
->toolbar
);
8759 g_free(compose
->toolbar
);
8760 cm_mutex_free(compose
->mutex
);
8764 static void compose_attach_info_free(AttachInfo
*ainfo
)
8766 g_free(ainfo
->file
);
8767 g_free(ainfo
->content_type
);
8768 g_free(ainfo
->name
);
8769 g_free(ainfo
->charset
);
8773 static void compose_attach_update_label(Compose
*compose
)
8778 GtkTreeModel
*model
;
8783 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
8784 if(!gtk_tree_model_get_iter_first(model
, &iter
)) {
8785 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
8789 while(gtk_tree_model_iter_next(model
, &iter
))
8792 text
= g_strdup_printf("(%d)", i
);
8793 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
8797 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
8799 Compose
*compose
= (Compose
*)data
;
8800 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
8801 GtkTreeSelection
*selection
;
8803 GtkTreeModel
*model
;
8805 selection
= gtk_tree_view_get_selection(tree_view
);
8806 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
8811 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
8812 GtkTreePath
*path
= cur
->data
;
8813 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
8816 gtk_tree_path_free(path
);
8819 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
8820 GtkTreeRowReference
*ref
= cur
->data
;
8821 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
8824 if (gtk_tree_model_get_iter(model
, &iter
, path
))
8825 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
8827 gtk_tree_path_free(path
);
8828 gtk_tree_row_reference_free(ref
);
8832 compose_attach_update_label(compose
);
8835 static struct _AttachProperty
8838 GtkWidget
*mimetype_entry
;
8839 GtkWidget
*encoding_optmenu
;
8840 GtkWidget
*path_entry
;
8841 GtkWidget
*filename_entry
;
8843 GtkWidget
*cancel_btn
;
8846 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
8848 gtk_tree_path_free((GtkTreePath
*)ptr
);
8851 static void compose_attach_property(GtkAction
*action
, gpointer data
)
8853 Compose
*compose
= (Compose
*)data
;
8854 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
8856 GtkComboBox
*optmenu
;
8857 GtkTreeSelection
*selection
;
8859 GtkTreeModel
*model
;
8862 static gboolean cancelled
;
8864 /* only if one selected */
8865 selection
= gtk_tree_view_get_selection(tree_view
);
8866 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
8869 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
8873 path
= (GtkTreePath
*) sel
->data
;
8874 gtk_tree_model_get_iter(model
, &iter
, path
);
8875 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
8878 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
8884 if (!attach_prop
.window
)
8885 compose_attach_property_create(&cancelled
);
8886 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
8887 gtk_widget_grab_focus(attach_prop
.ok_btn
);
8888 gtk_widget_show(attach_prop
.window
);
8889 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
8890 GTK_WINDOW(compose
->window
));
8892 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
8893 if (ainfo
->encoding
== ENC_UNKNOWN
)
8894 combobox_select_by_data(optmenu
, ENC_BASE64
);
8896 combobox_select_by_data(optmenu
, ainfo
->encoding
);
8898 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
8899 ainfo
->content_type
? ainfo
->content_type
: "");
8900 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
8901 ainfo
->file
? ainfo
->file
: "");
8902 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
8903 ainfo
->name
? ainfo
->name
: "");
8906 const gchar
*entry_text
;
8908 gchar
*cnttype
= NULL
;
8915 gtk_widget_hide(attach_prop
.window
);
8916 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
8921 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
8922 if (*entry_text
!= '\0') {
8925 text
= g_strstrip(g_strdup(entry_text
));
8926 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
8927 cnttype
= g_strdup(text
);
8930 alertpanel_error(_("Invalid MIME type."));
8936 ainfo
->encoding
= combobox_get_active_data(optmenu
);
8938 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
8939 if (*entry_text
!= '\0') {
8940 if (is_file_exist(entry_text
) &&
8941 (size
= get_file_size(entry_text
)) > 0)
8942 file
= g_strdup(entry_text
);
8945 (_("File doesn't exist or is empty."));
8951 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
8952 if (*entry_text
!= '\0') {
8953 g_free(ainfo
->name
);
8954 ainfo
->name
= g_strdup(entry_text
);
8958 g_free(ainfo
->content_type
);
8959 ainfo
->content_type
= cnttype
;
8962 g_free(ainfo
->file
);
8966 ainfo
->size
= (goffset
)size
;
8968 /* update tree store */
8969 text
= to_human_readable(ainfo
->size
);
8970 gtk_tree_model_get_iter(model
, &iter
, path
);
8971 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
8972 COL_MIMETYPE
, ainfo
->content_type
,
8974 COL_NAME
, ainfo
->name
,
8975 COL_CHARSET
, ainfo
->charset
,
8981 gtk_tree_path_free(path
);
8984 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8986 label = gtk_label_new(str); \
8987 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8988 GTK_FILL, 0, 0, 0); \
8989 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8991 entry = gtk_entry_new(); \
8992 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8993 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8996 static void compose_attach_property_create(gboolean
*cancelled
)
9002 GtkWidget
*mimetype_entry
;
9005 GtkListStore
*optmenu_menu
;
9006 GtkWidget
*path_entry
;
9007 GtkWidget
*filename_entry
;
9010 GtkWidget
*cancel_btn
;
9011 GList
*mime_type_list
, *strlist
;
9014 debug_print("Creating attach_property window...\n");
9016 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9017 gtk_widget_set_size_request(window
, 480, -1);
9018 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9019 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9020 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9021 g_signal_connect(G_OBJECT(window
), "delete_event",
9022 G_CALLBACK(attach_property_delete_event
),
9024 g_signal_connect(G_OBJECT(window
), "key_press_event",
9025 G_CALLBACK(attach_property_key_pressed
),
9028 vbox
= gtk_vbox_new(FALSE
, 8);
9029 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9031 table
= gtk_table_new(4, 2, FALSE
);
9032 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9033 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
9034 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
9036 label
= gtk_label_new(_("MIME type"));
9037 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
9039 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9040 #if !GTK_CHECK_VERSION(2, 24, 0)
9041 mimetype_entry
= gtk_combo_box_entry_new_text();
9043 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9045 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
9046 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9048 /* stuff with list */
9049 mime_type_list
= procmime_get_mime_type_list();
9051 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9052 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9055 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9057 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)strcmp2
))
9060 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9061 (GCompareFunc
)strcmp2
);
9064 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9065 mime_type_list
= mime_type_list
->next
) {
9066 #if !GTK_CHECK_VERSION(2, 24, 0)
9067 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry
), mime_type_list
->data
);
9069 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9071 g_free(mime_type_list
->data
);
9073 g_list_free(strlist
);
9074 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9075 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9077 label
= gtk_label_new(_("Encoding"));
9078 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
9080 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9082 hbox
= gtk_hbox_new(FALSE
, 0);
9083 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
9084 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9086 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9087 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9089 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9090 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9091 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9092 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9093 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9095 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9097 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9098 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9100 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
9101 &ok_btn
, GTK_STOCK_OK
,
9103 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9104 gtk_widget_grab_default(ok_btn
);
9106 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9107 G_CALLBACK(attach_property_ok
),
9109 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9110 G_CALLBACK(attach_property_cancel
),
9113 gtk_widget_show_all(vbox
);
9115 attach_prop
.window
= window
;
9116 attach_prop
.mimetype_entry
= mimetype_entry
;
9117 attach_prop
.encoding_optmenu
= optmenu
;
9118 attach_prop
.path_entry
= path_entry
;
9119 attach_prop
.filename_entry
= filename_entry
;
9120 attach_prop
.ok_btn
= ok_btn
;
9121 attach_prop
.cancel_btn
= cancel_btn
;
9124 #undef SET_LABEL_AND_ENTRY
9126 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9132 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9138 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9139 gboolean
*cancelled
)
9147 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9149 gboolean
*cancelled
)
9151 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9155 if (event
&& event
->keyval
== GDK_KEY_Return
) {
9163 static void compose_exec_ext_editor(Compose
*compose
)
9170 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9171 G_DIR_SEPARATOR
, compose
);
9173 if (pipe(pipe_fds
) < 0) {
9179 if ((pid
= fork()) < 0) {
9186 /* close the write side of the pipe */
9189 compose
->exteditor_file
= g_strdup(tmp
);
9190 compose
->exteditor_pid
= pid
;
9192 compose_set_ext_editor_sensitive(compose
, FALSE
);
9195 compose
->exteditor_ch
= g_io_channel_unix_new(pipe_fds
[0]);
9197 compose
->exteditor_ch
= g_io_channel_win32_new_fd(pipe_fds
[0]);
9199 compose
->exteditor_tag
= g_io_add_watch(compose
->exteditor_ch
,
9203 } else { /* process-monitoring process */
9209 /* close the read side of the pipe */
9212 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9213 fd_write_all(pipe_fds
[1], "2\n", 2);
9217 pid_ed
= compose_exec_ext_editor_real(tmp
);
9219 fd_write_all(pipe_fds
[1], "1\n", 2);
9223 /* wait until editor is terminated */
9224 waitpid(pid_ed
, NULL
, 0);
9226 fd_write_all(pipe_fds
[1], "0\n", 2);
9233 #endif /* G_OS_UNIX */
9237 static gint
compose_exec_ext_editor_real(const gchar
*file
)
9244 cm_return_val_if_fail(file
!= NULL
, -1);
9246 if ((pid
= fork()) < 0) {
9251 if (pid
!= 0) return pid
;
9253 /* grandchild process */
9255 if (setpgid(0, getppid()))
9258 if (prefs_common_get_ext_editor_cmd() &&
9259 (p
= strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9260 *(p
+ 1) == 's' && !strchr(p
+ 2, '%')) {
9261 g_snprintf(buf
, sizeof(buf
), prefs_common_get_ext_editor_cmd(), file
);
9263 if (prefs_common_get_ext_editor_cmd())
9264 g_warning("External editor command-line is invalid: '%s'\n",
9265 prefs_common_get_ext_editor_cmd());
9266 g_snprintf(buf
, sizeof(buf
), DEFAULT_EDITOR_CMD
, file
);
9269 cmdline
= strsplit_with_quote(buf
, " ", 1024);
9270 execvp(cmdline
[0], cmdline
);
9273 g_strfreev(cmdline
);
9278 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9280 pid_t pgid
= compose
->exteditor_pid
* -1;
9283 ret
= kill(pgid
, 0);
9285 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
9289 msg
= g_strdup_printf
9290 (_("The external editor is still working.\n"
9291 "Force terminating the process?\n"
9292 "process group id: %d"), -pgid
);
9293 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
, GTK_STOCK_YES
,
9294 NULL
, FALSE
, NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
9298 if (val
== G_ALERTALTERNATE
) {
9299 g_source_remove(compose
->exteditor_tag
);
9300 g_io_channel_shutdown(compose
->exteditor_ch
,
9302 g_io_channel_unref(compose
->exteditor_ch
);
9304 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
9305 waitpid(compose
->exteditor_pid
, NULL
, 0);
9307 g_warning("Terminated process group id: %d", -pgid
);
9308 g_warning("Temporary file: %s",
9309 compose
->exteditor_file
);
9311 compose_set_ext_editor_sensitive(compose
, TRUE
);
9313 g_free(compose
->exteditor_file
);
9314 compose
->exteditor_file
= NULL
;
9315 compose
->exteditor_pid
= -1;
9316 compose
->exteditor_ch
= NULL
;
9317 compose
->exteditor_tag
= -1;
9325 static gboolean
compose_input_cb(GIOChannel
*source
, GIOCondition condition
,
9329 Compose
*compose
= (Compose
*)data
;
9332 debug_print("Compose: input from monitoring process\n");
9334 g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
);
9336 g_io_channel_shutdown(source
, FALSE
, NULL
);
9337 g_io_channel_unref(source
);
9339 waitpid(compose
->exteditor_pid
, NULL
, 0);
9341 if (buf
[0] == '0') { /* success */
9342 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9343 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9345 gtk_text_buffer_set_text(buffer
, "", -1);
9346 compose_insert_file(compose
, compose
->exteditor_file
);
9347 compose_changed_cb(NULL
, compose
);
9348 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9350 if (claws_unlink(compose
->exteditor_file
) < 0)
9351 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9352 } else if (buf
[0] == '1') { /* failed */
9353 g_warning("Couldn't exec external editor\n");
9354 if (claws_unlink(compose
->exteditor_file
) < 0)
9355 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9356 } else if (buf
[0] == '2') {
9357 g_warning("Couldn't write to file\n");
9358 } else if (buf
[0] == '3') {
9359 g_warning("Pipe read failed\n");
9362 compose_set_ext_editor_sensitive(compose
, TRUE
);
9364 g_free(compose
->exteditor_file
);
9365 compose
->exteditor_file
= NULL
;
9366 compose
->exteditor_pid
= -1;
9367 compose
->exteditor_ch
= NULL
;
9368 compose
->exteditor_tag
= -1;
9373 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9376 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Send", sensitive
);
9377 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/SendLater", sensitive
);
9378 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", sensitive
);
9379 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", sensitive
);
9380 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", sensitive
);
9381 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/WrapPara", sensitive
);
9382 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/WrapAllLines", sensitive
);
9383 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/ExtEditor", sensitive
);
9385 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9386 if (compose
->toolbar
->send_btn
)
9387 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9388 if (compose
->toolbar
->sendl_btn
)
9389 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9390 if (compose
->toolbar
->draft_btn
)
9391 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9392 if (compose
->toolbar
->insert_btn
)
9393 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9394 if (compose
->toolbar
->sig_btn
)
9395 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9396 if (compose
->toolbar
->exteditor_btn
)
9397 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9398 if (compose
->toolbar
->linewrap_current_btn
)
9399 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9400 if (compose
->toolbar
->linewrap_all_btn
)
9401 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9403 #endif /* G_OS_UNIX */
9406 * compose_undo_state_changed:
9408 * Change the sensivity of the menuentries undo and redo
9410 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9411 gint redo_state
, gpointer data
)
9413 Compose
*compose
= (Compose
*)data
;
9415 switch (undo_state
) {
9416 case UNDO_STATE_TRUE
:
9417 if (!undostruct
->undo_state
) {
9418 undostruct
->undo_state
= TRUE
;
9419 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9422 case UNDO_STATE_FALSE
:
9423 if (undostruct
->undo_state
) {
9424 undostruct
->undo_state
= FALSE
;
9425 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9428 case UNDO_STATE_UNCHANGED
:
9430 case UNDO_STATE_REFRESH
:
9431 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9434 g_warning("Undo state not recognized");
9438 switch (redo_state
) {
9439 case UNDO_STATE_TRUE
:
9440 if (!undostruct
->redo_state
) {
9441 undostruct
->redo_state
= TRUE
;
9442 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9445 case UNDO_STATE_FALSE
:
9446 if (undostruct
->redo_state
) {
9447 undostruct
->redo_state
= FALSE
;
9448 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
9451 case UNDO_STATE_UNCHANGED
:
9453 case UNDO_STATE_REFRESH
:
9454 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
9457 g_warning("Redo state not recognized");
9462 /* callback functions */
9464 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
9465 GtkAllocation
*allocation
,
9468 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
9471 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9472 * includes "non-client" (windows-izm) in calculation, so this calculation
9473 * may not be accurate.
9475 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
9476 GtkAllocation
*allocation
,
9477 GtkSHRuler
*shruler
)
9479 if (prefs_common
.show_ruler
) {
9480 gint char_width
= 0, char_height
= 0;
9481 gint line_width_in_chars
;
9483 gtkut_get_font_size(GTK_WIDGET(widget
),
9484 &char_width
, &char_height
);
9485 line_width_in_chars
=
9486 (allocation
->width
- allocation
->x
) / char_width
;
9488 /* got the maximum */
9489 gtk_shruler_set_range(GTK_SHRULER(shruler
),
9490 0.0, line_width_in_chars
, 0);
9499 ComposePrefType type
;
9500 gboolean entry_marked
;
9503 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
9505 Compose
*compose
= (Compose
*)data
;
9508 gchar
*folderidentifier
;
9509 gint account_id
= 0;
9512 GSList
*list
, *saved_list
= NULL
;
9513 HeaderEntryState
*state
;
9514 GtkRcStyle
*style
= NULL
;
9515 #if !GTK_CHECK_VERSION(3, 0, 0)
9516 static GdkColor yellow
;
9517 static gboolean color_set
= FALSE
;
9519 static GdkColor yellow
= { (guint32
)0, (guint32
)0xf5, (guint32
)0xf6, (guint32
)0xbe };
9522 /* Get ID of active account in the combo box */
9523 menu
= gtk_combo_box_get_model(optmenu
);
9524 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
9525 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
9527 ac
= account_find_from_id(account_id
);
9528 cm_return_if_fail(ac
!= NULL
);
9530 if (ac
!= compose
->account
) {
9531 compose_select_account(compose
, ac
, FALSE
);
9533 for (list
= compose
->header_list
; list
; list
= list
->next
) {
9534 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
9536 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
9537 compose_destroy_headerentry(compose
, hentry
);
9541 state
= g_malloc0(sizeof(HeaderEntryState
));
9542 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
9543 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
9544 state
->entry
= gtk_editable_get_chars(
9545 GTK_EDITABLE(hentry
->entry
), 0, -1);
9546 state
->type
= hentry
->type
;
9548 #if !GTK_CHECK_VERSION(3, 0, 0)
9550 gdk_color_parse("#f5f6be", &yellow
);
9551 color_set
= gdk_colormap_alloc_color(
9552 gdk_colormap_get_system(),
9553 &yellow
, FALSE
, TRUE
);
9557 style
= gtk_widget_get_modifier_style(hentry
->entry
);
9558 state
->entry_marked
= gdk_color_equal(&yellow
,
9559 &style
->base
[GTK_STATE_NORMAL
]);
9561 saved_list
= g_slist_append(saved_list
, state
);
9562 compose_destroy_headerentry(compose
, hentry
);
9565 compose
->header_last
= NULL
;
9566 g_slist_free(compose
->header_list
);
9567 compose
->header_list
= NULL
;
9568 compose
->header_nextrow
= 1;
9569 compose_create_header_entry(compose
);
9571 if (ac
->set_autocc
&& ac
->auto_cc
)
9572 compose_entry_append(compose
, ac
->auto_cc
,
9573 COMPOSE_CC
, PREF_ACCOUNT
);
9575 if (ac
->set_autobcc
&& ac
->auto_bcc
)
9576 compose_entry_append(compose
, ac
->auto_bcc
,
9577 COMPOSE_BCC
, PREF_ACCOUNT
);
9579 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
9580 compose_entry_append(compose
, ac
->auto_replyto
,
9581 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
9583 for (list
= saved_list
; list
; list
= list
->next
) {
9584 state
= (HeaderEntryState
*) list
->data
;
9586 compose_add_header_entry(compose
, state
->header
,
9587 state
->entry
, state
->type
);
9588 if (state
->entry_marked
)
9589 compose_entry_mark_default_to(compose
, state
->entry
);
9591 g_free(state
->header
);
9592 g_free(state
->entry
);
9595 g_slist_free(saved_list
);
9597 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
9598 (ac
->protocol
== A_NNTP
) ?
9599 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
9602 /* Set message save folder */
9603 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
9604 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
9606 g_signal_connect(G_OBJECT(compose
->savemsg_checkbtn
), "toggled",
9607 G_CALLBACK(compose_savemsg_checkbtn_cb
), compose
);
9609 compose_set_save_to(compose
, NULL
);
9610 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
9611 folderidentifier
= folder_item_get_identifier(account_get_special_folder
9612 (compose
->account
, F_OUTBOX
));
9613 compose_set_save_to(compose
, folderidentifier
);
9614 g_free(folderidentifier
);
9618 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
9619 GtkTreeViewColumn
*column
, Compose
*compose
)
9621 compose_attach_property(NULL
, compose
);
9624 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
9627 Compose
*compose
= (Compose
*)data
;
9628 GtkTreeSelection
*attach_selection
;
9629 gint attach_nr_selected
;
9631 if (!event
) return FALSE
;
9633 if (event
->button
== 3) {
9634 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
9635 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
9637 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
9638 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
> 0));
9640 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
9641 NULL
, NULL
, event
->button
, event
->time
);
9648 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
9651 Compose
*compose
= (Compose
*)data
;
9653 if (!event
) return FALSE
;
9655 switch (event
->keyval
) {
9656 case GDK_KEY_Delete
:
9657 compose_attach_remove_selected(NULL
, compose
);
9663 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
9665 toolbar_comp_set_sensitive(compose
, allow
);
9666 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
9667 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
9669 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
9671 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
9672 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
9673 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
9675 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
9679 static void compose_send_cb(GtkAction
*action
, gpointer data
)
9681 Compose
*compose
= (Compose
*)data
;
9683 if (prefs_common
.work_offline
&&
9684 !inc_offline_should_override(TRUE
,
9685 _("Claws Mail needs network access in order "
9686 "to send this email.")))
9689 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
9690 g_source_remove(compose
->draft_timeout_tag
);
9691 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
9694 compose_send(compose
);
9697 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
9699 Compose
*compose
= (Compose
*)data
;
9703 compose_allow_user_actions(compose
, FALSE
);
9704 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
9705 compose_allow_user_actions(compose
, TRUE
);
9709 compose_close(compose
);
9710 } else if (val
== -1) {
9711 alertpanel_error(_("Could not queue message."));
9712 } else if (val
== -2) {
9713 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno
));
9714 } else if (val
== -3) {
9715 if (privacy_peek_error())
9716 alertpanel_error(_("Could not queue message for sending:\n\n"
9717 "Signature failed: %s"), privacy_get_error());
9718 } else if (val
== -4) {
9719 alertpanel_error(_("Could not queue message for sending:\n\n"
9720 "Charset conversion failed."));
9721 } else if (val
== -5) {
9722 alertpanel_error(_("Could not queue message for sending:\n\n"
9723 "Couldn't get recipient encryption key."));
9724 } else if (val
== -6) {
9727 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9730 #define DRAFTED_AT_EXIT "drafted_at_exit"
9731 static void compose_register_draft(MsgInfo
*info
)
9733 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9734 DRAFTED_AT_EXIT
, NULL
);
9735 FILE *fp
= g_fopen(filepath
, "ab");
9738 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
9746 gboolean
compose_draft (gpointer data
, guint action
)
9748 Compose
*compose
= (Compose
*)data
;
9753 MsgFlags flag
= {0, 0};
9754 static gboolean lock
= FALSE
;
9755 MsgInfo
*newmsginfo
;
9757 gboolean target_locked
= FALSE
;
9758 gboolean err
= FALSE
;
9760 if (lock
) return FALSE
;
9762 if (compose
->sending
)
9765 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
9766 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
9768 if (!g_mutex_trylock(compose
->mutex
)) {
9769 /* we don't want to lock the mutex once it's available,
9770 * because as the only other part of compose.c locking
9771 * it is compose_close - which means once unlocked,
9772 * the compose struct will be freed */
9773 debug_print("couldn't lock mutex, probably sending\n");
9779 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9780 G_DIR_SEPARATOR
, compose
);
9781 if ((fp
= g_fopen(tmp
, "wb")) == NULL
) {
9782 FILE_OP_ERROR(tmp
, "fopen");
9786 /* chmod for security */
9787 if (change_file_mode_rw(fp
, tmp
) < 0) {
9788 FILE_OP_ERROR(tmp
, "chmod");
9789 g_warning("can't change file mode\n");
9792 /* Save draft infos */
9793 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
9794 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
9796 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
9797 gchar
*savefolderid
;
9799 savefolderid
= compose_get_save_to(compose
);
9800 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
9801 g_free(savefolderid
);
9803 if (compose
->return_receipt
) {
9804 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
9806 if (compose
->privacy_system
) {
9807 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
9808 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
9809 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
9812 /* Message-ID of message replying to */
9813 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
9814 gchar
*folderid
= NULL
;
9816 if (compose
->replyinfo
->folder
)
9817 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
9818 if (folderid
== NULL
)
9819 folderid
= g_strdup("NULL");
9821 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
9824 /* Message-ID of message forwarding to */
9825 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
9826 gchar
*folderid
= NULL
;
9828 if (compose
->fwdinfo
->folder
)
9829 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
9830 if (folderid
== NULL
)
9831 folderid
= g_strdup("NULL");
9833 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
9837 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
9838 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
9840 sheaders
= compose_get_manual_headers_info(compose
);
9841 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
9844 /* end of headers */
9845 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
9852 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
9856 if (fclose(fp
) == EOF
) {
9860 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
9861 if (compose
->targetinfo
) {
9862 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
9864 flag
.perm_flags
|= MSG_LOCKED
;
9866 flag
.tmp_flags
= MSG_DRAFT
;
9868 folder_item_scan(draft
);
9869 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
9870 MsgInfo
*tmpinfo
= NULL
;
9871 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
9872 if (compose
->msgid
) {
9873 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
9876 msgnum
= tmpinfo
->msgnum
;
9877 procmsg_msginfo_free(tmpinfo
);
9878 debug_print("got draft msgnum %d from scanning\n", msgnum
);
9880 debug_print("didn't get draft msgnum after scanning\n");
9883 debug_print("got draft msgnum %d from adding\n", msgnum
);
9889 if (action
!= COMPOSE_AUTO_SAVE
) {
9890 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
9891 alertpanel_error(_("Could not save draft."));
9894 gtkut_window_popup(compose
->window
);
9895 val
= alertpanel_full(_("Could not save draft"),
9896 _("Could not save draft.\n"
9897 "Do you want to cancel exit or discard this email?"),
9898 _("_Cancel exit"), _("_Discard email"), NULL
,
9899 FALSE
, NULL
, ALERT_QUESTION
, G_ALERTDEFAULT
);
9900 if (val
== G_ALERTALTERNATE
) {
9902 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
9903 compose_close(compose
);
9907 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
9916 if (compose
->mode
== COMPOSE_REEDIT
) {
9917 compose_remove_reedit_target(compose
, TRUE
);
9920 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
9923 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
9925 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
9927 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
9928 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
9929 procmsg_msginfo_set_flags(newmsginfo
, 0,
9930 MSG_HAS_ATTACHMENT
);
9932 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
9933 compose_register_draft(newmsginfo
);
9935 procmsg_msginfo_free(newmsginfo
);
9938 folder_item_scan(draft
);
9940 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
9942 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
9943 compose_close(compose
);
9949 path
= folder_item_fetch_msg(draft
, msgnum
);
9951 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
9954 if (g_stat(path
, &s
) < 0) {
9955 FILE_OP_ERROR(path
, "stat");
9961 procmsg_msginfo_free(compose
->targetinfo
);
9962 compose
->targetinfo
= procmsg_msginfo_new();
9963 compose
->targetinfo
->msgnum
= msgnum
;
9964 compose
->targetinfo
->size
= (goffset
)s
.st_size
;
9965 compose
->targetinfo
->mtime
= s
.st_mtime
;
9966 compose
->targetinfo
->folder
= draft
;
9968 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
9969 compose
->mode
= COMPOSE_REEDIT
;
9971 if (action
== COMPOSE_AUTO_SAVE
) {
9972 compose
->autosaved_draft
= compose
->targetinfo
;
9974 compose
->modified
= FALSE
;
9975 compose_set_title(compose
);
9979 g_mutex_unlock(compose
->mutex
);
9983 void compose_clear_exit_drafts(void)
9985 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9986 DRAFTED_AT_EXIT
, NULL
);
9987 if (is_file_exist(filepath
))
9988 claws_unlink(filepath
);
9993 void compose_reopen_exit_drafts(void)
9995 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
9996 DRAFTED_AT_EXIT
, NULL
);
9997 FILE *fp
= g_fopen(filepath
, "rb");
10001 while (fgets(buf
, sizeof(buf
), fp
)) {
10002 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10003 const gchar
*folder
= parts
[0];
10004 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10006 if (folder
&& *folder
&& msgnum
> -1) {
10007 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10008 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10010 compose_reedit(info
, FALSE
);
10017 compose_clear_exit_drafts();
10020 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10022 Compose
*compose
= (Compose
*)data
;
10023 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10024 compose
->rmode
= COMPOSE_REEDIT
;
10027 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10029 if (compose
&& file_list
) {
10032 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10033 gchar
*file
= (gchar
*) tmp
->data
;
10034 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10035 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10036 compose_changed_cb(NULL
, compose
);
10041 g_free(utf8_filename
);
10046 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10048 Compose
*compose
= (Compose
*)data
;
10051 if (compose
->redirect_filename
!= NULL
)
10054 /* Set focus_window properly, in case we were called via popup menu,
10055 * which unsets it (via focus_out_event callback on compose window). */
10056 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10058 file_list
= filesel_select_multiple_files_open(_("Select file"));
10061 compose_attach_from_list(compose
, file_list
, TRUE
);
10062 g_list_free(file_list
);
10066 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10068 Compose
*compose
= (Compose
*)data
;
10070 gint files_inserted
= 0;
10072 file_list
= filesel_select_multiple_files_open(_("Select file"));
10077 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10078 gchar
*file
= (gchar
*) tmp
->data
;
10079 gchar
*filedup
= g_strdup(file
);
10080 gchar
*shortfile
= g_path_get_basename(filedup
);
10081 ComposeInsertResult res
;
10082 /* insert the file if the file is short or if the user confirmed that
10083 he/she wants to insert the large file */
10084 res
= compose_insert_file(compose
, file
);
10085 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10086 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10087 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10088 alertpanel_error(_("File '%s' contained invalid characters\n"
10089 "for the current encoding, insertion may be incorrect."),
10091 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10098 g_list_free(file_list
);
10102 if (files_inserted
> 0 && compose
->gtkaspell
&&
10103 compose
->gtkaspell
->check_while_typing
)
10104 gtkaspell_highlight_all(compose
->gtkaspell
);
10108 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10110 Compose
*compose
= (Compose
*)data
;
10112 compose_insert_sig(compose
, FALSE
);
10115 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10117 Compose
*compose
= (Compose
*)data
;
10119 compose_insert_sig(compose
, TRUE
);
10122 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10126 Compose
*compose
= (Compose
*)data
;
10128 gtkut_widget_get_uposition(widget
, &x
, &y
);
10129 if (!compose
->batch
) {
10130 prefs_common
.compose_x
= x
;
10131 prefs_common
.compose_y
= y
;
10133 if (compose
->sending
|| compose
->updating
)
10135 compose_close_cb(NULL
, compose
);
10139 void compose_close_toolbar(Compose
*compose
)
10141 compose_close_cb(NULL
, compose
);
10144 static gboolean
compose_can_autosave(Compose
*compose
)
10146 if (compose
->privacy_system
&& compose
->use_encryption
)
10147 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
10149 return prefs_common
.autosave
;
10152 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10154 Compose
*compose
= (Compose
*)data
;
10158 if (compose
->exteditor_tag
!= -1) {
10159 if (!compose_ext_editor_kill(compose
))
10164 if (compose
->modified
) {
10165 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10166 if (!g_mutex_trylock(compose
->mutex
)) {
10167 /* we don't want to lock the mutex once it's available,
10168 * because as the only other part of compose.c locking
10169 * it is compose_close - which means once unlocked,
10170 * the compose struct will be freed */
10171 debug_print("couldn't lock mutex, probably sending\n");
10175 val
= alertpanel(_("Discard message"),
10176 _("This message has been modified. Discard it?"),
10177 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
);
10179 val
= alertpanel(_("Save changes"),
10180 _("This message has been modified. Save the latest changes?"),
10181 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL
);
10183 g_mutex_unlock(compose
->mutex
);
10185 case G_ALERTDEFAULT
:
10186 if (compose_can_autosave(compose
) && !reedit
)
10187 compose_remove_draft(compose
);
10189 case G_ALERTALTERNATE
:
10190 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10197 compose_close(compose
);
10200 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10202 Compose
*compose
= (Compose
*) data
;
10204 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10205 if (compose
->targetinfo
)
10206 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10209 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10211 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10212 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10213 Compose
*compose
= (Compose
*) data
;
10216 compose
->out_encoding
= (CharSet
)value
;
10219 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10221 Compose
*compose
= (Compose
*)data
;
10223 #ifndef USE_NEW_ADDRBOOK
10224 addressbook_open(compose
);
10226 GError
* error
= NULL
;
10227 addressbook_connect_signals(compose
);
10228 addressbook_dbus_open(TRUE
, &error
);
10230 g_warning("%s", error
->message
);
10231 g_error_free(error
);
10236 static void about_show_cb(GtkAction
*action
, gpointer data
)
10241 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10243 Compose
*compose
= (Compose
*)data
;
10248 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10249 cm_return_if_fail(tmpl
!= NULL
);
10251 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10253 val
= alertpanel(_("Apply template"), msg
,
10254 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
);
10257 if (val
== G_ALERTDEFAULT
)
10258 compose_template_apply(compose
, tmpl
, TRUE
);
10259 else if (val
== G_ALERTALTERNATE
)
10260 compose_template_apply(compose
, tmpl
, FALSE
);
10263 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10265 Compose
*compose
= (Compose
*)data
;
10267 compose_exec_ext_editor(compose
);
10270 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10272 Compose
*compose
= (Compose
*)data
;
10273 gboolean prev_autowrap
= compose
->autowrap
;
10275 compose
->autowrap
= FALSE
;
10276 undo_undo(compose
->undostruct
);
10277 compose
->autowrap
= prev_autowrap
;
10280 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10282 Compose
*compose
= (Compose
*)data
;
10283 gboolean prev_autowrap
= compose
->autowrap
;
10285 compose
->autowrap
= FALSE
;
10286 undo_redo(compose
->undostruct
);
10287 compose
->autowrap
= prev_autowrap
;
10290 static void entry_cut_clipboard(GtkWidget
*entry
)
10292 if (GTK_IS_EDITABLE(entry
))
10293 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10294 else if (GTK_IS_TEXT_VIEW(entry
))
10295 gtk_text_buffer_cut_clipboard(
10296 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10297 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10301 static void entry_copy_clipboard(GtkWidget
*entry
)
10303 if (GTK_IS_EDITABLE(entry
))
10304 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10305 else if (GTK_IS_TEXT_VIEW(entry
))
10306 gtk_text_buffer_copy_clipboard(
10307 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10308 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10311 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10312 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10314 if (GTK_IS_TEXT_VIEW(entry
)) {
10315 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10316 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10317 GtkTextIter start_iter
, end_iter
;
10319 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10321 if (contents
== NULL
)
10324 /* we shouldn't delete the selection when middle-click-pasting, or we
10325 * can't mid-click-paste our own selection */
10326 if (clip
!= GDK_SELECTION_PRIMARY
) {
10327 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10328 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10331 if (insert_place
== NULL
) {
10332 /* if insert_place isn't specified, insert at the cursor.
10333 * used for Ctrl-V pasting */
10334 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10335 start
= gtk_text_iter_get_offset(&start_iter
);
10336 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10338 /* if insert_place is specified, paste here.
10339 * used for mid-click-pasting */
10340 start
= gtk_text_iter_get_offset(insert_place
);
10341 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10342 if (prefs_common
.primary_paste_unselects
)
10343 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10347 /* paste unwrapped: mark the paste so it's not wrapped later */
10348 end
= start
+ strlen(contents
);
10349 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10350 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10351 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10352 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10353 /* rewrap paragraph now (after a mid-click-paste) */
10354 mark_start
= gtk_text_buffer_get_insert(buffer
);
10355 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10356 gtk_text_iter_backward_char(&start_iter
);
10357 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10359 } else if (GTK_IS_EDITABLE(entry
))
10360 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10362 compose
->modified
= TRUE
;
10365 static void entry_allsel(GtkWidget
*entry
)
10367 if (GTK_IS_EDITABLE(entry
))
10368 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10369 else if (GTK_IS_TEXT_VIEW(entry
)) {
10370 GtkTextIter startiter
, enditer
;
10371 GtkTextBuffer
*textbuf
;
10373 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10374 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10375 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10377 gtk_text_buffer_move_mark_by_name(textbuf
,
10378 "selection_bound", &startiter
);
10379 gtk_text_buffer_move_mark_by_name(textbuf
,
10380 "insert", &enditer
);
10384 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10386 Compose
*compose
= (Compose
*)data
;
10387 if (compose
->focused_editable
10388 #ifndef GENERIC_UMPC
10389 && gtk_widget_has_focus(compose
->focused_editable
)
10392 entry_cut_clipboard(compose
->focused_editable
);
10395 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10397 Compose
*compose
= (Compose
*)data
;
10398 if (compose
->focused_editable
10399 #ifndef GENERIC_UMPC
10400 && gtk_widget_has_focus(compose
->focused_editable
)
10403 entry_copy_clipboard(compose
->focused_editable
);
10406 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
10408 Compose
*compose
= (Compose
*)data
;
10409 gint prev_autowrap
;
10410 GtkTextBuffer
*buffer
;
10412 if (compose
->focused_editable
&&
10413 #ifndef GENERIC_UMPC
10414 gtk_widget_has_focus(compose
->focused_editable
)
10417 entry_paste_clipboard(compose
, compose
->focused_editable
,
10418 prefs_common
.linewrap_pastes
,
10419 GDK_SELECTION_CLIPBOARD
, NULL
);
10424 #ifndef GENERIC_UMPC
10425 gtk_widget_has_focus(compose
->text
) &&
10427 compose
->gtkaspell
&&
10428 compose
->gtkaspell
->check_while_typing
)
10429 gtkaspell_highlight_all(compose
->gtkaspell
);
10433 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
10435 Compose
*compose
= (Compose
*)data
;
10436 gint wrap_quote
= prefs_common
.linewrap_quote
;
10437 if (compose
->focused_editable
10438 #ifndef GENERIC_UMPC
10439 && gtk_widget_has_focus(compose
->focused_editable
)
10442 /* let text_insert() (called directly or at a later time
10443 * after the gtk_editable_paste_clipboard) know that
10444 * text is to be inserted as a quotation. implemented
10445 * by using a simple refcount... */
10446 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
10447 G_OBJECT(compose
->focused_editable
),
10448 "paste_as_quotation"));
10449 g_object_set_data(G_OBJECT(compose
->focused_editable
),
10450 "paste_as_quotation",
10451 GINT_TO_POINTER(paste_as_quotation
+ 1));
10452 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
10453 entry_paste_clipboard(compose
, compose
->focused_editable
,
10454 prefs_common
.linewrap_pastes
,
10455 GDK_SELECTION_CLIPBOARD
, NULL
);
10456 prefs_common
.linewrap_quote
= wrap_quote
;
10460 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
10462 Compose
*compose
= (Compose
*)data
;
10463 gint prev_autowrap
;
10464 GtkTextBuffer
*buffer
;
10466 if (compose
->focused_editable
10467 #ifndef GENERIC_UMPC
10468 && gtk_widget_has_focus(compose
->focused_editable
)
10471 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
10472 GDK_SELECTION_CLIPBOARD
, NULL
);
10477 #ifndef GENERIC_UMPC
10478 gtk_widget_has_focus(compose
->text
) &&
10480 compose
->gtkaspell
&&
10481 compose
->gtkaspell
->check_while_typing
)
10482 gtkaspell_highlight_all(compose
->gtkaspell
);
10486 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
10488 Compose
*compose
= (Compose
*)data
;
10489 gint prev_autowrap
;
10490 GtkTextBuffer
*buffer
;
10492 if (compose
->focused_editable
10493 #ifndef GENERIC_UMPC
10494 && gtk_widget_has_focus(compose
->focused_editable
)
10497 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
10498 GDK_SELECTION_CLIPBOARD
, NULL
);
10503 #ifndef GENERIC_UMPC
10504 gtk_widget_has_focus(compose
->text
) &&
10506 compose
->gtkaspell
&&
10507 compose
->gtkaspell
->check_while_typing
)
10508 gtkaspell_highlight_all(compose
->gtkaspell
);
10512 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
10514 Compose
*compose
= (Compose
*)data
;
10515 if (compose
->focused_editable
10516 #ifndef GENERIC_UMPC
10517 && gtk_widget_has_focus(compose
->focused_editable
)
10520 entry_allsel(compose
->focused_editable
);
10523 static void textview_move_beginning_of_line (GtkTextView
*text
)
10525 GtkTextBuffer
*buffer
;
10529 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10531 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10532 mark
= gtk_text_buffer_get_insert(buffer
);
10533 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10534 gtk_text_iter_set_line_offset(&ins
, 0);
10535 gtk_text_buffer_place_cursor(buffer
, &ins
);
10538 static void textview_move_forward_character (GtkTextView
*text
)
10540 GtkTextBuffer
*buffer
;
10544 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10546 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10547 mark
= gtk_text_buffer_get_insert(buffer
);
10548 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10549 if (gtk_text_iter_forward_cursor_position(&ins
))
10550 gtk_text_buffer_place_cursor(buffer
, &ins
);
10553 static void textview_move_backward_character (GtkTextView
*text
)
10555 GtkTextBuffer
*buffer
;
10559 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10561 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10562 mark
= gtk_text_buffer_get_insert(buffer
);
10563 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10564 if (gtk_text_iter_backward_cursor_position(&ins
))
10565 gtk_text_buffer_place_cursor(buffer
, &ins
);
10568 static void textview_move_forward_word (GtkTextView
*text
)
10570 GtkTextBuffer
*buffer
;
10575 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10577 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10578 mark
= gtk_text_buffer_get_insert(buffer
);
10579 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10580 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
10581 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
10582 gtk_text_iter_backward_word_start(&ins
);
10583 gtk_text_buffer_place_cursor(buffer
, &ins
);
10587 static void textview_move_backward_word (GtkTextView
*text
)
10589 GtkTextBuffer
*buffer
;
10593 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10595 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10596 mark
= gtk_text_buffer_get_insert(buffer
);
10597 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10598 if (gtk_text_iter_backward_word_starts(&ins
, 1))
10599 gtk_text_buffer_place_cursor(buffer
, &ins
);
10602 static void textview_move_end_of_line (GtkTextView
*text
)
10604 GtkTextBuffer
*buffer
;
10608 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10610 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10611 mark
= gtk_text_buffer_get_insert(buffer
);
10612 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10613 if (gtk_text_iter_forward_to_line_end(&ins
))
10614 gtk_text_buffer_place_cursor(buffer
, &ins
);
10617 static void textview_move_next_line (GtkTextView
*text
)
10619 GtkTextBuffer
*buffer
;
10624 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10626 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10627 mark
= gtk_text_buffer_get_insert(buffer
);
10628 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10629 offset
= gtk_text_iter_get_line_offset(&ins
);
10630 if (gtk_text_iter_forward_line(&ins
)) {
10631 gtk_text_iter_set_line_offset(&ins
, offset
);
10632 gtk_text_buffer_place_cursor(buffer
, &ins
);
10636 static void textview_move_previous_line (GtkTextView
*text
)
10638 GtkTextBuffer
*buffer
;
10643 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10645 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10646 mark
= gtk_text_buffer_get_insert(buffer
);
10647 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10648 offset
= gtk_text_iter_get_line_offset(&ins
);
10649 if (gtk_text_iter_backward_line(&ins
)) {
10650 gtk_text_iter_set_line_offset(&ins
, offset
);
10651 gtk_text_buffer_place_cursor(buffer
, &ins
);
10655 static void textview_delete_forward_character (GtkTextView
*text
)
10657 GtkTextBuffer
*buffer
;
10659 GtkTextIter ins
, end_iter
;
10661 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10663 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10664 mark
= gtk_text_buffer_get_insert(buffer
);
10665 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10667 if (gtk_text_iter_forward_char(&end_iter
)) {
10668 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10672 static void textview_delete_backward_character (GtkTextView
*text
)
10674 GtkTextBuffer
*buffer
;
10676 GtkTextIter ins
, end_iter
;
10678 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10680 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10681 mark
= gtk_text_buffer_get_insert(buffer
);
10682 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10684 if (gtk_text_iter_backward_char(&end_iter
)) {
10685 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
10689 static void textview_delete_forward_word (GtkTextView
*text
)
10691 GtkTextBuffer
*buffer
;
10693 GtkTextIter ins
, end_iter
;
10695 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10697 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10698 mark
= gtk_text_buffer_get_insert(buffer
);
10699 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10701 if (gtk_text_iter_forward_word_end(&end_iter
)) {
10702 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10706 static void textview_delete_backward_word (GtkTextView
*text
)
10708 GtkTextBuffer
*buffer
;
10710 GtkTextIter ins
, end_iter
;
10712 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10714 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10715 mark
= gtk_text_buffer_get_insert(buffer
);
10716 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10718 if (gtk_text_iter_backward_word_start(&end_iter
)) {
10719 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
10723 static void textview_delete_line (GtkTextView
*text
)
10725 GtkTextBuffer
*buffer
;
10727 GtkTextIter ins
, start_iter
, end_iter
;
10729 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10731 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10732 mark
= gtk_text_buffer_get_insert(buffer
);
10733 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10736 gtk_text_iter_set_line_offset(&start_iter
, 0);
10739 if (gtk_text_iter_ends_line(&end_iter
)){
10740 if (!gtk_text_iter_forward_char(&end_iter
))
10741 gtk_text_iter_backward_char(&start_iter
);
10744 gtk_text_iter_forward_to_line_end(&end_iter
);
10745 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
10748 static void textview_delete_to_line_end (GtkTextView
*text
)
10750 GtkTextBuffer
*buffer
;
10752 GtkTextIter ins
, end_iter
;
10754 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
10756 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
10757 mark
= gtk_text_buffer_get_insert(buffer
);
10758 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
10760 if (gtk_text_iter_ends_line(&end_iter
))
10761 gtk_text_iter_forward_char(&end_iter
);
10763 gtk_text_iter_forward_to_line_end(&end_iter
);
10764 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
10767 #define DO_ACTION(name, act) { \
10768 if(!strcmp(name, a_name)) { \
10772 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
10774 const gchar
*a_name
= gtk_action_get_name(action
);
10775 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
10776 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
10777 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
10778 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
10779 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
10780 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
10781 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
10782 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
10783 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
10784 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
10785 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
10786 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
10787 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
10788 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
10792 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
10794 Compose
*compose
= (Compose
*)data
;
10795 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
10796 ComposeCallAdvancedAction action
= -1;
10798 action
= compose_call_advanced_action_from_path(gaction
);
10801 void (*do_action
) (GtkTextView
*text
);
10802 } action_table
[] = {
10803 {textview_move_beginning_of_line
},
10804 {textview_move_forward_character
},
10805 {textview_move_backward_character
},
10806 {textview_move_forward_word
},
10807 {textview_move_backward_word
},
10808 {textview_move_end_of_line
},
10809 {textview_move_next_line
},
10810 {textview_move_previous_line
},
10811 {textview_delete_forward_character
},
10812 {textview_delete_backward_character
},
10813 {textview_delete_forward_word
},
10814 {textview_delete_backward_word
},
10815 {textview_delete_line
},
10816 {textview_delete_to_line_end
}
10819 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
10821 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
10822 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
10823 if (action_table
[action
].do_action
)
10824 action_table
[action
].do_action(text
);
10826 g_warning("Not implemented yet.");
10830 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
10832 GtkAllocation allocation
;
10836 if (GTK_IS_EDITABLE(widget
)) {
10837 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
10838 gtk_editable_set_position(GTK_EDITABLE(widget
),
10841 if ((parent
= gtk_widget_get_parent(widget
))
10842 && (parent
= gtk_widget_get_parent(parent
))
10843 && (parent
= gtk_widget_get_parent(parent
))) {
10844 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
10845 gtk_widget_get_allocation(widget
, &allocation
);
10846 gint y
= allocation
.y
;
10847 gint height
= allocation
.height
;
10848 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
10849 (GTK_SCROLLED_WINDOW(parent
));
10851 gfloat value
= gtk_adjustment_get_value(shown
);
10852 gfloat upper
= gtk_adjustment_get_upper(shown
);
10853 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
10854 if (y
< (int)value
) {
10855 gtk_adjustment_set_value(shown
, y
- 1);
10857 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
10858 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
10859 gtk_adjustment_set_value(shown
,
10860 y
+ height
- (int)page_size
- 1);
10862 gtk_adjustment_set_value(shown
,
10863 (int)upper
- (int)page_size
- 1);
10870 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
10871 compose
->focused_editable
= widget
;
10873 #ifdef GENERIC_UMPC
10874 if (GTK_IS_TEXT_VIEW(widget
)
10875 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
10876 g_object_ref(compose
->notebook
);
10877 g_object_ref(compose
->edit_vbox
);
10878 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
10879 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
10880 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
10881 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
10882 g_object_unref(compose
->notebook
);
10883 g_object_unref(compose
->edit_vbox
);
10884 g_signal_handlers_block_by_func(G_OBJECT(widget
),
10885 G_CALLBACK(compose_grab_focus_cb
),
10887 gtk_widget_grab_focus(widget
);
10888 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
10889 G_CALLBACK(compose_grab_focus_cb
),
10891 } else if (!GTK_IS_TEXT_VIEW(widget
)
10892 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
10893 g_object_ref(compose
->notebook
);
10894 g_object_ref(compose
->edit_vbox
);
10895 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
10896 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
10897 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
10898 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
10899 g_object_unref(compose
->notebook
);
10900 g_object_unref(compose
->edit_vbox
);
10901 g_signal_handlers_block_by_func(G_OBJECT(widget
),
10902 G_CALLBACK(compose_grab_focus_cb
),
10904 gtk_widget_grab_focus(widget
);
10905 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
10906 G_CALLBACK(compose_grab_focus_cb
),
10912 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
10914 compose
->modified
= TRUE
;
10915 // compose_beautify_paragraph(compose, NULL, TRUE);
10916 #ifndef GENERIC_UMPC
10917 compose_set_title(compose
);
10921 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
10923 Compose
*compose
= (Compose
*)data
;
10924 compose_beautify_paragraph(compose
, NULL
, TRUE
);
10927 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
10929 Compose
*compose
= (Compose
*)data
;
10930 compose_wrap_all_full(compose
, TRUE
);
10933 static void compose_find_cb(GtkAction
*action
, gpointer data
)
10935 Compose
*compose
= (Compose
*)data
;
10937 message_search_compose(compose
);
10940 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
10943 Compose
*compose
= (Compose
*)data
;
10944 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10945 if (compose
->autowrap
)
10946 compose_wrap_all_full(compose
, TRUE
);
10947 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10950 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
10953 Compose
*compose
= (Compose
*)data
;
10954 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10957 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
10959 Compose
*compose
= (Compose
*)data
;
10961 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10964 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
10966 Compose
*compose
= (Compose
*)data
;
10968 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
10971 static void activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
10973 g_free(compose
->privacy_system
);
10975 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
10976 compose_update_privacy_system_menu_item(compose
, warn
);
10979 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
10981 Compose
*compose
= (Compose
*)data
;
10983 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
10984 gtk_widget_show(compose
->ruler_hbox
);
10985 prefs_common
.show_ruler
= TRUE
;
10987 gtk_widget_hide(compose
->ruler_hbox
);
10988 gtk_widget_queue_resize(compose
->edit_vbox
);
10989 prefs_common
.show_ruler
= FALSE
;
10993 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
10994 GdkDragContext
*context
,
10997 GtkSelectionData
*data
,
11000 gpointer user_data
)
11002 Compose
*compose
= (Compose
*)user_data
;
11006 type
= gtk_selection_data_get_data_type(data
);
11007 if (((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11009 || (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "DROPFILES_DND"))
11011 ) && gtk_drag_get_source_widget(context
) !=
11012 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11013 list
= uri_list_extract_filenames(
11014 (const gchar
*)gtk_selection_data_get_data(data
));
11015 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11016 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11017 compose_attach_append
11018 (compose
, (const gchar
*)tmp
->data
,
11019 utf8_filename
, NULL
, NULL
);
11020 g_free(utf8_filename
);
11022 if (list
) compose_changed_cb(NULL
, compose
);
11023 list_free_strings(list
);
11025 } else if (gtk_drag_get_source_widget(context
)
11026 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11027 /* comes from our summaryview */
11028 SummaryView
* summaryview
= NULL
;
11029 GSList
* list
= NULL
, *cur
= NULL
;
11031 if (mainwindow_get_mainwindow())
11032 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11035 list
= summary_get_selected_msg_list(summaryview
);
11037 for (cur
= list
; cur
; cur
= cur
->next
) {
11038 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11039 gchar
*file
= NULL
;
11041 file
= procmsg_get_message_file_full(msginfo
,
11044 compose_attach_append(compose
, (const gchar
*)file
,
11045 (const gchar
*)file
, "message/rfc822", NULL
);
11049 g_slist_free(list
);
11053 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11054 GdkDragContext
*drag_context
,
11056 guint time
, gpointer user_data
)
11058 /* not handling this signal makes compose_insert_drag_received_cb
11063 static gboolean completion_set_focus_to_subject
11064 (GtkWidget
*widget
,
11065 GdkEventKey
*event
,
11068 GtkTextBuffer
*buffer
;
11072 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11074 /* make backtab move to subject field */
11075 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11076 gtk_widget_grab_focus(compose
->subject_entry
);
11080 // Up key should also move the focus to subject field, if the cursor
11081 // is on the first line.
11082 if ((event
->keyval
== GDK_KEY_Up
|| event
->keyval
== GDK_KEY_KP_Up
)
11083 && (event
->state
& (GDK_SHIFT_MASK
|GDK_CONTROL_MASK
)) == 0) {
11084 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget
));
11085 g_return_val_if_fail(buffer
!= NULL
, FALSE
);
11087 mark
= gtk_text_buffer_get_mark(buffer
, "insert");
11088 g_return_val_if_fail(mark
!= NULL
, FALSE
);
11090 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
11092 if (gtk_text_iter_get_line(&iter
) == 0) {
11093 gtk_widget_grab_focus(compose
->subject_entry
);
11101 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11102 GdkDragContext
*drag_context
,
11105 GtkSelectionData
*data
,
11108 gpointer user_data
)
11110 Compose
*compose
= (Compose
*)user_data
;
11114 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11116 type
= gtk_selection_data_get_data_type(data
);
11118 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11120 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "DROPFILES_DND")) {
11122 AlertValue val
= G_ALERTDEFAULT
;
11123 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11125 list
= uri_list_extract_filenames(ddata
);
11126 if (list
== NULL
&& strstr(ddata
, "://")) {
11127 /* Assume a list of no files, and data has ://, is a remote link */
11128 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11129 gchar
*tmpfile
= get_tmp_file();
11130 str_write_to_file(tmpdata
, tmpfile
);
11132 compose_insert_file(compose
, tmpfile
);
11133 claws_unlink(tmpfile
);
11135 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11136 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11139 switch (prefs_common
.compose_dnd_mode
) {
11140 case COMPOSE_DND_ASK
:
11141 val
= alertpanel_full(_("Insert or attach?"),
11142 _("Do you want to insert the contents of the file(s) "
11143 "into the message body, or attach it to the email?"),
11144 GTK_STOCK_CANCEL
, _("+_Insert"), _("_Attach"),
11145 TRUE
, NULL
, ALERT_QUESTION
, G_ALERTALTERNATE
);
11147 case COMPOSE_DND_INSERT
:
11148 val
= G_ALERTALTERNATE
;
11150 case COMPOSE_DND_ATTACH
:
11151 val
= G_ALERTOTHER
;
11154 /* unexpected case */
11155 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11158 if (val
& G_ALERTDISABLE
) {
11159 val
&= ~G_ALERTDISABLE
;
11160 /* remember what action to perform by default, only if we don't click Cancel */
11161 if (val
== G_ALERTALTERNATE
)
11162 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11163 else if (val
== G_ALERTOTHER
)
11164 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11167 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11168 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11169 list_free_strings(list
);
11172 } else if (val
== G_ALERTOTHER
) {
11173 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11174 list_free_strings(list
);
11179 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11180 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11182 list_free_strings(list
);
11184 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11189 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11190 GdkDragContext
*drag_context
,
11193 GtkSelectionData
*data
,
11196 gpointer user_data
)
11198 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11199 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11201 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11204 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11205 gchar
*decoded
=g_new(gchar
, strlen(email
));
11208 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11209 gtk_editable_delete_text(entry
, 0, -1);
11210 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11211 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11215 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11218 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11220 Compose
*compose
= (Compose
*)data
;
11222 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11223 compose
->return_receipt
= TRUE
;
11225 compose
->return_receipt
= FALSE
;
11228 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11230 Compose
*compose
= (Compose
*)data
;
11232 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11233 compose
->remove_references
= TRUE
;
11235 compose
->remove_references
= FALSE
;
11238 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11239 ComposeHeaderEntry
*headerentry
)
11241 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11245 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11246 GdkEventKey
*event
,
11247 ComposeHeaderEntry
*headerentry
)
11249 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11250 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11251 !(event
->state
& GDK_MODIFIER_MASK
) &&
11252 (event
->keyval
== GDK_KEY_BackSpace
) &&
11253 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11254 gtk_container_remove
11255 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11256 headerentry
->combo
);
11257 gtk_container_remove
11258 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11259 headerentry
->entry
);
11260 headerentry
->compose
->header_list
=
11261 g_slist_remove(headerentry
->compose
->header_list
,
11263 g_free(headerentry
);
11264 } else if (event
->keyval
== GDK_KEY_Tab
) {
11265 if (headerentry
->compose
->header_last
== headerentry
) {
11266 /* Override default next focus, and give it to subject_entry
11267 * instead of notebook tabs
11269 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11270 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11277 static gboolean
scroll_postpone(gpointer data
)
11279 Compose
*compose
= (Compose
*)data
;
11281 if (compose
->batch
)
11284 GTK_EVENTS_FLUSH();
11285 compose_show_first_last_header(compose
, FALSE
);
11289 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11290 ComposeHeaderEntry
*headerentry
)
11292 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11293 compose_create_header_entry(headerentry
->compose
);
11294 g_signal_handlers_disconnect_matched
11295 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11296 0, 0, NULL
, NULL
, headerentry
);
11298 if (!headerentry
->compose
->batch
)
11299 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11303 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11305 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11306 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11310 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11312 GtkAdjustment
*vadj
;
11314 cm_return_if_fail(compose
);
11319 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11320 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11321 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11322 gtk_widget_get_parent(compose
->header_table
)));
11323 gtk_adjustment_set_value(vadj
, (show_first
?
11324 gtk_adjustment_get_lower(vadj
) :
11325 (gtk_adjustment_get_upper(vadj
) -
11326 gtk_adjustment_get_page_size(vadj
))));
11327 gtk_adjustment_changed(vadj
);
11330 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11331 const gchar
*text
, gint len
, Compose
*compose
)
11333 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11334 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11337 cm_return_if_fail(text
!= NULL
);
11339 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11340 G_CALLBACK(text_inserted
),
11342 if (paste_as_quotation
) {
11344 const gchar
*qmark
;
11346 GtkTextIter start_iter
;
11349 len
= strlen(text
);
11351 new_text
= g_strndup(text
, len
);
11353 qmark
= compose_quote_char_from_context(compose
);
11355 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11356 gtk_text_buffer_place_cursor(buffer
, iter
);
11358 pos
= gtk_text_iter_get_offset(iter
);
11360 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11361 _("Quote format error at line %d."));
11362 quote_fmt_reset_vartable();
11364 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11365 GINT_TO_POINTER(paste_as_quotation
- 1));
11367 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11368 gtk_text_buffer_place_cursor(buffer
, iter
);
11369 gtk_text_buffer_delete_mark(buffer
, mark
);
11371 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11372 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11373 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11374 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11375 gtk_text_buffer_delete_mark(buffer
, mark
);
11377 if (strcmp(text
, "\n") || compose
->automatic_break
11378 || gtk_text_iter_starts_line(iter
)) {
11379 GtkTextIter before_ins
;
11380 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11381 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11382 before_ins
= *iter
;
11383 gtk_text_iter_backward_chars(&before_ins
, len
);
11384 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11387 /* check if the preceding is just whitespace or quote */
11388 GtkTextIter start_line
;
11389 gchar
*tmp
= NULL
, *quote
= NULL
;
11390 gint quote_len
= 0, is_normal
= 0;
11391 start_line
= *iter
;
11392 gtk_text_iter_set_line_offset(&start_line
, 0);
11393 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11396 if (*tmp
== '\0') {
11399 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
11407 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11409 gtk_text_buffer_insert_with_tags_by_name(buffer
,
11410 iter
, text
, len
, "no_join", NULL
);
11415 if (!paste_as_quotation
) {
11416 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11417 compose_beautify_paragraph(compose
, iter
, FALSE
);
11418 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11419 gtk_text_buffer_delete_mark(buffer
, mark
);
11422 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
11423 G_CALLBACK(text_inserted
),
11425 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
11427 if (compose_can_autosave(compose
) &&
11428 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
11429 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
11430 compose
->draft_timeout_tag
= g_timeout_add
11431 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
11435 static void compose_check_all(GtkAction
*action
, gpointer data
)
11437 Compose
*compose
= (Compose
*)data
;
11438 if (!compose
->gtkaspell
)
11441 if (gtk_widget_has_focus(compose
->subject_entry
))
11442 claws_spell_entry_check_all(
11443 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11445 gtkaspell_check_all(compose
->gtkaspell
);
11448 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
11450 Compose
*compose
= (Compose
*)data
;
11451 if (compose
->gtkaspell
) {
11452 claws_spell_entry_recheck_all(
11453 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11454 gtkaspell_highlight_all(compose
->gtkaspell
);
11458 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
11460 Compose
*compose
= (Compose
*)data
;
11461 if (!compose
->gtkaspell
) {
11462 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
11466 if (gtk_widget_has_focus(compose
->subject_entry
))
11467 claws_spell_entry_check_backwards(
11468 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11470 gtkaspell_check_backwards(compose
->gtkaspell
);
11473 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
11475 Compose
*compose
= (Compose
*)data
;
11476 if (!compose
->gtkaspell
) {
11477 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
11481 if (gtk_widget_has_focus(compose
->subject_entry
))
11482 claws_spell_entry_check_forwards_go(
11483 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
11485 gtkaspell_check_forwards_go(compose
->gtkaspell
);
11490 *\brief Guess originating forward account from MsgInfo and several
11491 * "common preference" settings. Return NULL if no guess.
11493 static PrefsAccount
*compose_guess_forward_account_from_msginfo(MsgInfo
*msginfo
)
11495 PrefsAccount
*account
= NULL
;
11497 cm_return_val_if_fail(msginfo
, NULL
);
11498 cm_return_val_if_fail(msginfo
->folder
, NULL
);
11499 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
11501 if (msginfo
->folder
->prefs
->enable_default_account
)
11502 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
11505 account
= msginfo
->folder
->folder
->account
;
11507 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
11509 Xstrdup_a(to
, msginfo
->to
, return NULL
);
11510 extract_address(to
);
11511 account
= account_find_from_address(to
, FALSE
);
11514 if (!account
&& prefs_common
.forward_account_autosel
) {
11515 gchar cc
[BUFFSIZE
];
11516 if (!procheader_get_header_from_msginfo
11517 (msginfo
, cc
,sizeof cc
, "Cc:")) {
11518 gchar
*buf
= cc
+ strlen("Cc:");
11519 extract_address(buf
);
11520 account
= account_find_from_address(buf
, FALSE
);
11524 if (!account
&& prefs_common
.forward_account_autosel
) {
11525 gchar deliveredto
[BUFFSIZE
];
11526 if (!procheader_get_header_from_msginfo
11527 (msginfo
, deliveredto
,sizeof deliveredto
, "Delivered-To:")) {
11528 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
11529 extract_address(buf
);
11530 account
= account_find_from_address(buf
, FALSE
);
11537 gboolean
compose_close(Compose
*compose
)
11541 cm_return_val_if_fail(compose
, FALSE
);
11543 if (!g_mutex_trylock(compose
->mutex
)) {
11544 /* we have to wait for the (possibly deferred by auto-save)
11545 * drafting to be done, before destroying the compose under
11547 debug_print("waiting for drafting to finish...\n");
11548 compose_allow_user_actions(compose
, FALSE
);
11549 if (compose
->close_timeout_tag
== 0) {
11550 compose
->close_timeout_tag
=
11551 g_timeout_add (500, (GSourceFunc
) compose_close
,
11557 if (compose
->draft_timeout_tag
>= 0) {
11558 g_source_remove(compose
->draft_timeout_tag
);
11559 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
11562 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
11563 if (!compose
->batch
) {
11564 prefs_common
.compose_x
= x
;
11565 prefs_common
.compose_y
= y
;
11567 g_mutex_unlock(compose
->mutex
);
11568 compose_destroy(compose
);
11573 * Add entry field for each address in list.
11574 * \param compose E-Mail composition object.
11575 * \param listAddress List of (formatted) E-Mail addresses.
11577 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
11580 node
= listAddress
;
11582 addr
= ( gchar
* ) node
->data
;
11583 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
11584 node
= g_list_next( node
);
11588 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
11589 guint action
, gboolean opening_multiple
)
11591 gchar
*body
= NULL
;
11592 GSList
*new_msglist
= NULL
;
11593 MsgInfo
*tmp_msginfo
= NULL
;
11594 gboolean originally_enc
= FALSE
;
11595 gboolean originally_sig
= FALSE
;
11596 Compose
*compose
= NULL
;
11597 gchar
*s_system
= NULL
;
11599 cm_return_if_fail(msgview
!= NULL
);
11601 cm_return_if_fail(msginfo_list
!= NULL
);
11603 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
) {
11604 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
11605 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
11607 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
11608 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
11609 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
11610 orig_msginfo
, mimeinfo
);
11611 if (tmp_msginfo
!= NULL
) {
11612 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
11614 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
11615 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
11616 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
11618 tmp_msginfo
->folder
= orig_msginfo
->folder
;
11619 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
11620 if (orig_msginfo
->tags
) {
11621 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
11622 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
11628 if (!opening_multiple
)
11629 body
= messageview_get_selection(msgview
);
11632 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
11633 procmsg_msginfo_free(tmp_msginfo
);
11634 g_slist_free(new_msglist
);
11636 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
11638 if (compose
&& originally_enc
) {
11639 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
11642 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
11643 compose_force_signing(compose
, compose
->account
, s_system
);
11647 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
11650 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
11653 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
11654 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
11655 GSList
*cur
= msginfo_list
;
11656 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
11657 "messages. Opening the windows "
11658 "could take some time. Do you "
11659 "want to continue?"),
11660 g_slist_length(msginfo_list
));
11661 if (g_slist_length(msginfo_list
) > 9
11662 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, "+" GTK_STOCK_YES
, NULL
)
11663 != G_ALERTALTERNATE
) {
11668 /* We'll open multiple compose windows */
11669 /* let the WM place the next windows */
11670 compose_force_window_origin
= FALSE
;
11671 for (; cur
; cur
= cur
->next
) {
11673 tmplist
.data
= cur
->data
;
11674 tmplist
.next
= NULL
;
11675 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
11677 compose_force_window_origin
= TRUE
;
11679 /* forwarding multiple mails as attachments is done via a
11680 * single compose window */
11681 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
11685 void compose_check_for_email_account(Compose
*compose
)
11687 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
11693 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
11694 ac
= account_get_cur_account();
11695 if (ac
->protocol
== A_NNTP
) {
11696 list
= account_get_list();
11698 for( ; list
!= NULL
; list
= g_list_next(list
)) {
11699 curr
= (PrefsAccount
*) list
->data
;
11700 if (curr
->protocol
!= A_NNTP
) {
11706 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
11711 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
11712 const gchar
*address
)
11714 GSList
*msginfo_list
= NULL
;
11715 gchar
*body
= messageview_get_selection(msgview
);
11718 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
11720 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
11721 compose_check_for_email_account(compose
);
11722 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
11723 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
11724 compose_reply_set_subject(compose
, msginfo
);
11727 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
11730 void compose_set_position(Compose
*compose
, gint pos
)
11732 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11734 gtkut_text_view_set_position(text
, pos
);
11737 gboolean
compose_search_string(Compose
*compose
,
11738 const gchar
*str
, gboolean case_sens
)
11740 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11742 return gtkut_text_view_search_string(text
, str
, case_sens
);
11745 gboolean
compose_search_string_backward(Compose
*compose
,
11746 const gchar
*str
, gboolean case_sens
)
11748 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11750 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
11753 /* allocate a msginfo structure and populate its data from a compose data structure */
11754 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
11756 MsgInfo
*newmsginfo
;
11758 gchar buf
[BUFFSIZE
];
11760 cm_return_val_if_fail( compose
!= NULL
, NULL
);
11762 newmsginfo
= procmsg_msginfo_new();
11765 get_rfc822_date(buf
, sizeof(buf
));
11766 newmsginfo
->date
= g_strdup(buf
);
11769 if (compose
->from_name
) {
11770 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
11771 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
11775 if (compose
->subject_entry
)
11776 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
11778 /* to, cc, reply-to, newsgroups */
11779 for (list
= compose
->header_list
; list
; list
= list
->next
) {
11780 gchar
*header
= gtk_editable_get_chars(
11782 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
11783 gchar
*entry
= gtk_editable_get_chars(
11784 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
11786 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
11787 if ( newmsginfo
->to
== NULL
) {
11788 newmsginfo
->to
= g_strdup(entry
);
11789 } else if (entry
&& *entry
) {
11790 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
11791 g_free(newmsginfo
->to
);
11792 newmsginfo
->to
= tmp
;
11795 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
11796 if ( newmsginfo
->cc
== NULL
) {
11797 newmsginfo
->cc
= g_strdup(entry
);
11798 } else if (entry
&& *entry
) {
11799 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
11800 g_free(newmsginfo
->cc
);
11801 newmsginfo
->cc
= tmp
;
11804 if ( strcasecmp(header
,
11805 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11806 if ( newmsginfo
->newsgroups
== NULL
) {
11807 newmsginfo
->newsgroups
= g_strdup(entry
);
11808 } else if (entry
&& *entry
) {
11809 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
11810 g_free(newmsginfo
->newsgroups
);
11811 newmsginfo
->newsgroups
= tmp
;
11819 /* other data is unset */
11825 /* update compose's dictionaries from folder dict settings */
11826 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
11827 FolderItem
*folder_item
)
11829 cm_return_if_fail(compose
!= NULL
);
11831 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
11832 FolderItemPrefs
*prefs
= folder_item
->prefs
;
11834 if (prefs
->enable_default_dictionary
)
11835 gtkaspell_change_dict(compose
->gtkaspell
,
11836 prefs
->default_dictionary
, FALSE
);
11837 if (folder_item
->prefs
->enable_default_alt_dictionary
)
11838 gtkaspell_change_alt_dict(compose
->gtkaspell
,
11839 prefs
->default_alt_dictionary
);
11840 if (prefs
->enable_default_dictionary
11841 || prefs
->enable_default_alt_dictionary
)
11842 compose_spell_menu_changed(compose
);
11847 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
11849 Compose
*compose
= (Compose
*)data
;
11851 cm_return_if_fail(compose
!= NULL
);
11853 gtk_widget_grab_focus(compose
->text
);