2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2023 the Claws Mail team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
34 #ifdef GDK_WINDOWING_X11
38 #include <pango/pango-break.h>
43 #include <sys/types.h>
48 # include <sys/wait.h>
52 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
59 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
66 #include "mainwindow.h"
68 #ifndef USE_ALT_ADDRBOOK
69 #include "addressbook.h"
71 #include "addressbook-dbus.h"
72 #include "addressadd.h"
74 #include "folderview.h"
77 #include "stock_pixmap.h"
78 #include "send_message.h"
81 #include "customheader.h"
82 #include "prefs_common.h"
83 #include "prefs_account.h"
87 #include "procheader.h"
89 #include "statusbar.h"
91 #include "quoted-printable.h"
95 #include "gtkshruler.h"
97 #include "alertpanel.h"
98 #include "manage_window.h"
100 #include "folder_item_prefs.h"
101 #include "addr_compl.h"
102 #include "quote_fmt.h"
104 #include "foldersel.h"
107 #include "message_search.h"
108 #include "combobox.h"
112 #include "autofaces.h"
113 #include "spell_entry.h"
115 #include "file-utils.h"
118 #include "password.h"
119 #include "ldapserver.h"
133 #define N_ATTACH_COLS (N_COL_COLUMNS)
137 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
= -1,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
= 0,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
141 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
144 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
145 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
147 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
148 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
149 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
150 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
151 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
152 } ComposeCallAdvancedAction
;
156 PRIORITY_HIGHEST
= 1,
165 COMPOSE_INSERT_SUCCESS
,
166 COMPOSE_INSERT_READ_ERROR
,
167 COMPOSE_INSERT_INVALID_CHARACTER
,
168 COMPOSE_INSERT_NO_FILE
169 } ComposeInsertResult
;
173 COMPOSE_WRITE_FOR_SEND
,
174 COMPOSE_WRITE_FOR_STORE
179 COMPOSE_QUOTE_FORCED
,
186 SUBJECT_FIELD_PRESENT
,
191 #define B64_LINE_SIZE 57
192 #define B64_BUFFSIZE 77
194 #define MAX_REFERENCES_LEN 999
196 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
197 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
199 #define COMPOSE_PRIVACY_WARNING() { \
200 alertpanel_error(_("You have opted to sign and/or encrypt this " \
201 "message but have not selected a privacy system.\n\n" \
202 "Signing and encrypting have been disabled for this " \
207 #define INVALID_PID INVALID_HANDLE_VALUE
209 #define INVALID_PID -1
212 static GdkRGBA default_header_bgcolor
=
215 static GdkRGBA default_header_color
=
218 static GList
*compose_list
= NULL
;
219 static GSList
*extra_headers
= NULL
;
221 static Compose
*compose_generic_new (PrefsAccount
*account
,
225 GList
*listAddress
);
227 static Compose
*compose_create (PrefsAccount
*account
,
232 static void compose_entry_indicate (Compose
*compose
,
233 const gchar
*address
);
234 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
235 ComposeQuoteMode quote_mode
,
239 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
240 GSList
*msginfo_list
);
241 static Compose
*compose_reply (MsgInfo
*msginfo
,
242 ComposeQuoteMode quote_mode
,
247 static Compose
*compose_reply_mode (ComposeMode mode
,
248 GSList
*msginfo_list
,
250 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
251 static void compose_update_privacy_systems_menu(Compose
*compose
);
253 static GtkWidget
*compose_account_option_menu_create
255 static void compose_set_out_encoding (Compose
*compose
);
256 static void compose_set_template_menu (Compose
*compose
);
257 static void compose_destroy (Compose
*compose
);
259 static MailField
compose_entries_set (Compose
*compose
,
261 ComposeEntryType to_type
);
262 static gint
compose_parse_header (Compose
*compose
,
264 static gint
compose_parse_manual_headers (Compose
*compose
,
266 HeaderEntry
*entries
);
267 static gchar
*compose_parse_references (const gchar
*ref
,
270 static gchar
*compose_quote_fmt (Compose
*compose
,
276 gboolean need_unescape
,
277 const gchar
*err_msg
);
279 static void compose_reply_set_entry (Compose
*compose
,
285 followup_and_reply_to
);
286 static void compose_reedit_set_entry (Compose
*compose
,
289 static void compose_insert_sig (Compose
*compose
,
291 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
294 static gboolean
compose_attach_append (Compose
*compose
,
297 const gchar
*content_type
,
298 const gchar
*charset
);
299 static void compose_attach_parts (Compose
*compose
,
302 static gboolean
compose_beautify_paragraph (Compose
*compose
,
303 GtkTextIter
*par_iter
,
305 static void compose_wrap_all (Compose
*compose
);
306 static void compose_wrap_all_full (Compose
*compose
,
309 static void compose_set_title (Compose
*compose
);
310 static void compose_select_account (Compose
*compose
,
311 PrefsAccount
*account
,
314 static PrefsAccount
*compose_current_mail_account(void);
315 /* static gint compose_send (Compose *compose); */
316 static gboolean compose_check_for_valid_recipient
318 static gboolean
compose_check_entries (Compose
*compose
,
319 gboolean check_everything
);
320 static gint
compose_write_to_file (Compose
*compose
,
323 gboolean attach_parts
);
324 static gint
compose_write_body_to_file (Compose
*compose
,
326 static gint
compose_remove_reedit_target (Compose
*compose
,
328 static void compose_remove_draft (Compose
*compose
);
329 static ComposeQueueResult
compose_queue_sub (Compose
*compose
,
333 gboolean perform_checks
,
334 gboolean remove_reedit_target
);
335 static int compose_add_attachments (Compose
*compose
,
338 static gchar
*compose_get_header (Compose
*compose
);
339 static gchar
*compose_get_manual_headers_info (Compose
*compose
);
341 static void compose_convert_header (Compose
*compose
,
346 gboolean addr_field
);
348 static void compose_attach_info_free (AttachInfo
*ainfo
);
349 static void compose_attach_remove_selected (GtkAction
*action
,
352 static void compose_template_apply (Compose
*compose
,
355 static void compose_attach_property (GtkAction
*action
,
357 static void compose_attach_property_create (gboolean
*cancelled
);
358 static void attach_property_ok (GtkWidget
*widget
,
359 gboolean
*cancelled
);
360 static void attach_property_cancel (GtkWidget
*widget
,
361 gboolean
*cancelled
);
362 static gint
attach_property_delete_event (GtkWidget
*widget
,
364 gboolean
*cancelled
);
365 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
367 gboolean
*cancelled
);
369 static void compose_exec_ext_editor (Compose
*compose
);
370 static gboolean
compose_ext_editor_kill (Compose
*compose
);
371 static void compose_ext_editor_closed_cb (GPid pid
,
374 static void compose_set_ext_editor_sensitive (Compose
*compose
,
376 static gboolean
compose_get_ext_editor_cmd_valid();
377 static gboolean
compose_get_ext_editor_uses_socket();
378 #ifdef GDK_WINDOWING_X11
379 static gboolean compose_ext_editor_plug_removed_cb
382 #endif /* GDK_WINDOWING_X11 */
384 static void compose_undo_state_changed (UndoMain
*undostruct
,
389 static void compose_create_header_entry (Compose
*compose
);
390 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
391 gchar
*text
, ComposePrefType pref_type
);
392 static void compose_remove_header_entries(Compose
*compose
);
394 static void compose_update_priority_menu_item(Compose
* compose
);
396 static void compose_spell_menu_changed (void *data
);
397 static void compose_dict_changed (void *data
);
399 static void compose_add_field_list ( Compose
*compose
,
400 GList
*listAddress
);
402 /* callback functions */
404 static void compose_notebook_size_alloc (GtkNotebook
*notebook
,
405 GtkAllocation
*allocation
,
407 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
408 GtkAllocation
*allocation
,
409 GtkSHRuler
*shruler
);
410 static void account_activated (GtkComboBox
*optmenu
,
412 static void attach_selected (GtkTreeView
*tree_view
,
413 GtkTreePath
*tree_path
,
414 GtkTreeViewColumn
*column
,
416 static gboolean
attach_button_pressed (GtkWidget
*widget
,
417 GdkEventButton
*event
,
419 static gboolean
attach_key_pressed (GtkWidget
*widget
,
422 static void compose_send_cb (GtkAction
*action
, gpointer data
);
423 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
425 static void compose_save_cb (GtkAction
*action
,
428 static void compose_attach_cb (GtkAction
*action
,
430 static void compose_insert_file_cb (GtkAction
*action
,
432 static void compose_insert_sig_cb (GtkAction
*action
,
434 static void compose_replace_sig_cb (GtkAction
*action
,
437 static void compose_close_cb (GtkAction
*action
,
439 static void compose_print_cb (GtkAction
*action
,
442 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
444 static void compose_address_cb (GtkAction
*action
,
446 static void about_show_cb (GtkAction
*action
,
448 static void compose_template_activate_cb(GtkWidget
*widget
,
451 static void compose_ext_editor_cb (GtkAction
*action
,
454 static gint
compose_delete_cb (GtkWidget
*widget
,
458 static void compose_undo_cb (GtkAction
*action
,
460 static void compose_redo_cb (GtkAction
*action
,
462 static void compose_cut_cb (GtkAction
*action
,
464 static void compose_copy_cb (GtkAction
*action
,
466 static void compose_paste_cb (GtkAction
*action
,
468 static void compose_paste_as_quote_cb (GtkAction
*action
,
470 static void compose_paste_no_wrap_cb (GtkAction
*action
,
472 static void compose_paste_wrap_cb (GtkAction
*action
,
474 static void compose_allsel_cb (GtkAction
*action
,
477 static void compose_advanced_action_cb (GtkAction
*action
,
480 static void compose_grab_focus_cb (GtkWidget
*widget
,
483 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
486 static void compose_wrap_cb (GtkAction
*action
,
488 static void compose_wrap_all_cb (GtkAction
*action
,
490 static void compose_find_cb (GtkAction
*action
,
492 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
494 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
497 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
499 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
501 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
503 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
504 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
505 static void compose_activate_privacy_system (Compose
*compose
,
506 PrefsAccount
*account
,
508 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
);
509 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
511 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
513 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
514 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
515 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
517 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
518 GdkDragContext
*drag_context
,
521 GtkSelectionData
*data
,
525 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
526 GdkDragContext
*drag_context
,
529 GtkSelectionData
*data
,
533 static void compose_header_drag_received_cb (GtkWidget
*widget
,
534 GdkDragContext
*drag_context
,
537 GtkSelectionData
*data
,
542 static gboolean
compose_drag_drop (GtkWidget
*widget
,
543 GdkDragContext
*drag_context
,
545 guint time
, gpointer user_data
);
546 static gboolean completion_set_focus_to_subject
551 static void text_inserted (GtkTextBuffer
*buffer
,
556 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
557 ComposeQuoteMode quote_mode
,
561 gboolean followup_and_reply_to
,
564 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
565 ComposeHeaderEntry
*headerentry
);
566 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
568 ComposeHeaderEntry
*headerentry
);
569 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
570 ComposeHeaderEntry
*headerentry
);
572 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
574 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
576 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
582 static void compose_check_all (GtkAction
*action
, gpointer data
);
583 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
584 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
585 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
588 static PrefsAccount
*compose_find_account (MsgInfo
*msginfo
);
590 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
593 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
594 FolderItem
*folder_item
);
596 static void compose_attach_update_label(Compose
*compose
);
597 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
598 gboolean respect_default_to
);
599 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
);
600 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
);
602 static GtkActionEntry compose_popup_entries
[] =
604 {"Compose", NULL
, "Compose", NULL
, NULL
, NULL
},
605 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
606 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
607 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
608 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
611 static GtkActionEntry compose_entries
[] =
613 {"Menu", NULL
, "Menu", NULL
, NULL
, NULL
},
615 {"Message", NULL
, N_("_Message"), NULL
, NULL
, NULL
},
616 {"Edit", NULL
, N_("_Edit"), NULL
, NULL
, NULL
},
618 {"Spelling", NULL
, N_("_Spelling"), NULL
, NULL
, NULL
},
620 {"Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
621 {"Tools", NULL
, N_("_Tools"), NULL
, NULL
, NULL
},
622 {"Help", NULL
, N_("_Help"), NULL
, NULL
, NULL
},
624 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
625 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
626 {"Message/---", NULL
, "---", NULL
, NULL
, NULL
},
628 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
629 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
630 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
631 {"Message/ReplaceSig", NULL
, N_("_Replace signature"), NULL
, NULL
, G_CALLBACK(compose_replace_sig_cb
) },
632 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
633 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
634 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
635 {"Message/Print", NULL
, N_("_Print"), NULL
, NULL
, G_CALLBACK(compose_print_cb
) },
636 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
637 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
640 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
641 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
642 {"Edit/---", NULL
, "---", NULL
, NULL
, NULL
},
644 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
645 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
646 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
648 {"Edit/SpecialPaste", NULL
, N_("_Special paste"), NULL
, NULL
, NULL
},
649 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("As _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
650 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_Wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
651 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_Unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
653 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
655 {"Edit/Advanced", NULL
, N_("A_dvanced"), NULL
, NULL
, NULL
},
656 {"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*/
657 {"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*/
658 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
659 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
660 {"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*/
661 {"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*/
662 {"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*/
663 {"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*/
664 {"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*/
665 {"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*/
666 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
667 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
668 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
669 {"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*/
671 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
672 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
674 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
675 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
676 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
677 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
678 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
681 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
682 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
683 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
684 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
686 {"Spelling/---", NULL
, "---", NULL
, NULL
, NULL
},
687 {"Spelling/Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
691 {"Options/ReplyMode", NULL
, N_("Reply _mode"), NULL
, NULL
, NULL
},
692 {"Options/---", NULL
, "---", NULL
, NULL
, NULL
},
693 {"Options/PrivacySystem", NULL
, N_("Privacy _System"), NULL
, NULL
, NULL
},
694 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
696 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
697 {"Options/Priority", NULL
, N_("_Priority"), NULL
, NULL
, NULL
},
699 {"Options/Encoding", NULL
, N_("Character _encoding"), NULL
, NULL
, NULL
},
700 {"Options/Encoding/---", NULL
, "---", NULL
, NULL
, NULL
},
701 #define ENC_ACTION(cs_char,c_char,string) \
702 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
704 {"Options/Encoding/Western", NULL
, N_("Western European"), NULL
, NULL
, NULL
},
705 {"Options/Encoding/Baltic", NULL
, N_("Baltic"), NULL
, NULL
, NULL
},
706 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew"), NULL
, NULL
, NULL
},
707 {"Options/Encoding/Arabic", NULL
, N_("Arabic"), NULL
, NULL
, NULL
},
708 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic"), NULL
, NULL
, NULL
},
709 {"Options/Encoding/Japanese", NULL
, N_("Japanese"), NULL
, NULL
, NULL
},
710 {"Options/Encoding/Chinese", NULL
, N_("Chinese"), NULL
, NULL
, NULL
},
711 {"Options/Encoding/Korean", NULL
, N_("Korean"), NULL
, NULL
, NULL
},
712 {"Options/Encoding/Thai", NULL
, N_("Thai"), NULL
, NULL
, NULL
},
715 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
717 {"Tools/Template", NULL
, N_("_Template"), NULL
, NULL
, NULL
},
718 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
719 {"Tools/Actions", NULL
, N_("Actio_ns"), NULL
, NULL
, NULL
},
720 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
723 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
726 static GtkToggleActionEntry compose_toggle_entries
[] =
728 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
), FALSE
}, /* Toggle */
729 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
), FALSE
}, /* Toggle */
730 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
), FALSE
}, /* Toggle */
731 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
), FALSE
}, /* Toggle */
732 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
), FALSE
}, /* Toggle */
733 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
), FALSE
}, /* Toggle */
734 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
), FALSE
}, /* Toggle */
737 static GtkRadioActionEntry compose_radio_rm_entries
[] =
739 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
740 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
741 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
742 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
745 static GtkRadioActionEntry compose_radio_prio_entries
[] =
747 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
748 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
749 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
750 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
751 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
754 static GtkRadioActionEntry compose_radio_enc_entries
[] =
756 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Cyrillic/"CS_MACCYR
, C_MACCYR
, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
777 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
778 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
779 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
780 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
781 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
782 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
783 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
784 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
785 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
786 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
787 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
788 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
791 static GtkTargetEntry compose_mime_types
[] =
793 {"text/uri-list", 0, 0},
794 {"UTF8_STRING", 0, 0},
798 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
800 const GList
*compose_list
= compose_get_compose_list();
801 const GList
*elem
= NULL
;
804 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
806 Compose
*c
= (Compose
*)elem
->data
;
808 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
812 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
813 gtkut_window_popup(c
->window
);
821 static GdkRGBA quote_color1
=
823 static GdkRGBA quote_color2
=
825 static GdkRGBA quote_color3
=
828 static GdkRGBA quote_bgcolor1
=
830 static GdkRGBA quote_bgcolor2
=
832 static GdkRGBA quote_bgcolor3
=
835 static GdkRGBA signature_color
=
838 static GdkRGBA uri_color
=
841 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
843 GtkTextBuffer
*buffer
;
844 GdkRGBA black
= { 0, 0, 0, 1 };
846 buffer
= gtk_text_view_get_buffer(text
);
848 if (prefs_common
.enable_color
) {
849 /* grab the quote colors, converting from an int to a GdkColor */
850 quote_color1
= prefs_common
.color
[COL_QUOTE_LEVEL1
];
851 quote_color2
= prefs_common
.color
[COL_QUOTE_LEVEL2
];
852 quote_color3
= prefs_common
.color
[COL_QUOTE_LEVEL3
];
853 quote_bgcolor1
= prefs_common
.color
[COL_QUOTE_LEVEL1_BG
];
854 quote_bgcolor2
= prefs_common
.color
[COL_QUOTE_LEVEL2_BG
];
855 quote_bgcolor3
= prefs_common
.color
[COL_QUOTE_LEVEL3_BG
];
856 signature_color
= prefs_common
.color
[COL_SIGNATURE
];
857 uri_color
= prefs_common
.color
[COL_URI
];
859 signature_color
= quote_color1
= quote_color2
= quote_color3
=
860 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
863 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
864 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
865 "foreground-rgba", "e_color1
,
866 "paragraph-background-rgba", "e_bgcolor1
,
868 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
869 "foreground-rgba", "e_color2
,
870 "paragraph-background-rgba", "e_bgcolor2
,
872 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
873 "foreground-rgba", "e_color3
,
874 "paragraph-background-rgba", "e_bgcolor3
,
877 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
878 "foreground-rgba", "e_color1
,
880 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
881 "foreground-rgba", "e_color2
,
883 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
884 "foreground-rgba", "e_color3
,
888 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
889 "foreground-rgba", &signature_color
,
892 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
893 "foreground-rgba", &uri_color
,
895 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
896 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
899 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
902 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
905 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
907 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
910 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
912 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
915 #define SCROLL_TO_CURSOR(compose) { \
916 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
917 gtk_text_view_get_buffer( \
918 GTK_TEXT_VIEW(compose->text))); \
919 gtk_text_view_scroll_mark_onscreen( \
920 GTK_TEXT_VIEW(compose->text), \
924 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
927 if (folderidentifier
) {
928 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
929 prefs_common
.compose_save_to_history
= add_history(
930 prefs_common
.compose_save_to_history
, folderidentifier
);
931 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
932 prefs_common
.compose_save_to_history
);
935 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
936 if (folderidentifier
)
937 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
939 gtk_entry_set_text(GTK_ENTRY(entry
), "");
942 static gchar
*compose_get_save_to(Compose
*compose
)
945 gchar
*result
= NULL
;
946 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
947 result
= gtk_editable_get_chars(entry
, 0, -1);
950 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
951 prefs_common
.compose_save_to_history
= add_history(
952 prefs_common
.compose_save_to_history
, result
);
953 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
954 prefs_common
.compose_save_to_history
);
959 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
960 GList
*attach_files
, GList
*listAddress
)
963 GtkTextView
*textview
;
964 GtkTextBuffer
*textbuf
;
966 const gchar
*subject_format
= NULL
;
967 const gchar
*body_format
= NULL
;
968 gchar
*mailto_from
= NULL
;
969 PrefsAccount
*mailto_account
= NULL
;
970 MsgInfo
* dummyinfo
= NULL
;
971 gint cursor_pos
= -1;
972 MailField mfield
= NO_FIELD_PRESENT
;
976 /* check if mailto defines a from */
977 if (mailto
&& *mailto
!= '\0') {
978 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
979 /* mailto defines a from, check if we can get account prefs from it,
980 if not, the account prefs will be guessed using other ways, but we'll keep
983 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
984 if (mailto_account
== NULL
) {
986 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
987 extract_address(tmp_from
);
988 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
992 account
= mailto_account
;
995 /* if no account prefs set from mailto, set if from folder prefs (if any) */
996 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
997 account
= account_find_from_id(item
->prefs
->default_account
);
999 /* if no account prefs set, fallback to the current one */
1000 if (!account
) account
= cur_account
;
1001 cm_return_val_if_fail(account
!= NULL
, NULL
);
1003 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1004 compose_apply_folder_privacy_settings(compose
, item
);
1006 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1007 (account
->default_encrypt
|| account
->default_sign
))
1008 COMPOSE_PRIVACY_WARNING();
1010 /* override from name if mailto asked for it */
1012 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1013 g_free(mailto_from
);
1015 /* override from name according to folder properties */
1016 if (item
&& item
->prefs
&&
1017 item
->prefs
->compose_with_format
&&
1018 item
->prefs
->compose_override_from_format
&&
1019 *item
->prefs
->compose_override_from_format
!= '\0') {
1024 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1026 /* decode \-escape sequences in the internal representation of the quote format */
1027 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1028 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1031 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1032 compose
->gtkaspell
);
1034 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1036 quote_fmt_scan_string(tmp
);
1039 buf
= quote_fmt_get_buffer();
1041 alertpanel_error(_("New message From format error."));
1043 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1044 quote_fmt_reset_vartable();
1045 quote_fmtlex_destroy();
1050 compose
->replyinfo
= NULL
;
1051 compose
->fwdinfo
= NULL
;
1053 textview
= GTK_TEXT_VIEW(compose
->text
);
1054 textbuf
= gtk_text_view_get_buffer(textview
);
1055 compose_create_tags(textview
, compose
);
1057 undo_block(compose
->undostruct
);
1059 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1062 if (account
->auto_sig
)
1063 compose_insert_sig(compose
, FALSE
);
1064 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1065 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1067 if (account
->protocol
!= A_NNTP
) {
1068 if (mailto
&& *mailto
!= '\0') {
1069 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1072 compose_set_folder_prefs(compose
, item
, TRUE
);
1074 if (item
&& item
->ret_rcpt
) {
1075 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1078 if (mailto
&& *mailto
!= '\0') {
1079 if (!strchr(mailto
, '@'))
1080 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1082 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1083 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1084 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1085 mfield
= TO_FIELD_PRESENT
;
1088 * CLAWS: just don't allow return receipt request, even if the user
1089 * may want to send an email. simple but foolproof.
1091 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1093 compose_add_field_list( compose
, listAddress
);
1095 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1096 subject_format
= item
->prefs
->compose_subject_format
;
1097 body_format
= item
->prefs
->compose_body_format
;
1098 } else if (account
->compose_with_format
) {
1099 subject_format
= account
->compose_subject_format
;
1100 body_format
= account
->compose_body_format
;
1101 } else if (prefs_common
.compose_with_format
) {
1102 subject_format
= prefs_common
.compose_subject_format
;
1103 body_format
= prefs_common
.compose_body_format
;
1106 if (subject_format
|| body_format
) {
1109 && *subject_format
!= '\0' )
1111 gchar
*subject
= NULL
;
1116 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1118 /* decode \-escape sequences in the internal representation of the quote format */
1119 tmp
= g_malloc(strlen(subject_format
)+1);
1120 pref_get_unescaped_pref(tmp
, subject_format
);
1122 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1124 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1125 compose
->gtkaspell
);
1127 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1129 quote_fmt_scan_string(tmp
);
1132 buf
= quote_fmt_get_buffer();
1134 alertpanel_error(_("New message subject format error."));
1136 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1137 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1138 quote_fmt_reset_vartable();
1139 quote_fmtlex_destroy();
1143 mfield
= SUBJECT_FIELD_PRESENT
;
1147 && *body_format
!= '\0' )
1150 GtkTextBuffer
*buffer
;
1151 GtkTextIter start
, end
;
1155 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1157 text
= GTK_TEXT_VIEW(compose
->text
);
1158 buffer
= gtk_text_view_get_buffer(text
);
1159 gtk_text_buffer_get_start_iter(buffer
, &start
);
1160 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1161 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1163 compose_quote_fmt(compose
, dummyinfo
,
1165 NULL
, tmp
, FALSE
, TRUE
,
1166 _("The body of the \"New message\" template has an error at line %d."));
1167 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1168 quote_fmt_reset_vartable();
1172 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1173 gtkaspell_highlight_all(compose
->gtkaspell
);
1175 mfield
= BODY_FIELD_PRESENT
;
1179 procmsg_msginfo_free( &dummyinfo
);
1185 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1186 ainfo
= (AttachInfo
*) curr
->data
;
1188 compose_insert_file(compose
, ainfo
->file
);
1190 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1191 ainfo
->content_type
, ainfo
->charset
);
1195 compose_show_first_last_header(compose
, TRUE
);
1197 /* Set save folder */
1198 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1199 gchar
*folderidentifier
;
1201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1202 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1203 folderidentifier
= folder_item_get_identifier(item
);
1204 compose_set_save_to(compose
, folderidentifier
);
1205 g_free(folderidentifier
);
1208 /* Place cursor according to provided input (mfield) */
1210 case NO_FIELD_PRESENT
:
1211 if (compose
->header_last
)
1212 gtk_widget_grab_focus(compose
->header_last
->entry
);
1214 case TO_FIELD_PRESENT
:
1215 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1217 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1220 gtk_widget_grab_focus(compose
->subject_entry
);
1222 case SUBJECT_FIELD_PRESENT
:
1223 textview
= GTK_TEXT_VIEW(compose
->text
);
1226 textbuf
= gtk_text_view_get_buffer(textview
);
1229 mark
= gtk_text_buffer_get_insert(textbuf
);
1230 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1231 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1233 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1234 * only defers where it comes to the variable body
1235 * is not null. If no body is present compose->text
1236 * will be null in which case you cannot place the
1237 * cursor inside the component so. An empty component
1238 * is therefore created before placing the cursor
1240 case BODY_FIELD_PRESENT
:
1241 cursor_pos
= quote_fmt_get_cursor_pos();
1242 if (cursor_pos
== -1)
1243 gtk_widget_grab_focus(compose
->header_last
->entry
);
1245 gtk_widget_grab_focus(compose
->text
);
1249 undo_unblock(compose
->undostruct
);
1251 if (prefs_common
.auto_exteditor
)
1252 compose_exec_ext_editor(compose
);
1254 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1256 SCROLL_TO_CURSOR(compose
);
1258 compose
->modified
= FALSE
;
1259 compose_set_title(compose
);
1261 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1266 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1267 gboolean override_pref
, const gchar
*system
)
1269 const gchar
*privacy
= NULL
;
1271 cm_return_if_fail(compose
!= NULL
);
1272 cm_return_if_fail(account
!= NULL
);
1274 if (privacy_system_can_encrypt(compose
->privacy_system
) == FALSE
||
1275 (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
))
1278 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1279 privacy
= account
->default_privacy_system
;
1283 GSList
*privacy_avail
= privacy_get_system_ids();
1284 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1285 privacy
= (gchar
*)(privacy_avail
->data
);
1287 g_slist_free_full(privacy_avail
, g_free
);
1289 if (privacy
!= NULL
) {
1291 g_free(compose
->privacy_system
);
1292 compose
->privacy_system
= NULL
;
1293 g_free(compose
->encdata
);
1294 compose
->encdata
= NULL
;
1296 if (compose
->privacy_system
== NULL
)
1297 compose
->privacy_system
= g_strdup(privacy
);
1298 else if (*(compose
->privacy_system
) == '\0') {
1299 g_free(compose
->privacy_system
);
1300 g_free(compose
->encdata
);
1301 compose
->encdata
= NULL
;
1302 compose
->privacy_system
= g_strdup(privacy
);
1304 compose_update_privacy_system_menu_item(compose
, FALSE
);
1305 compose_use_encryption(compose
, TRUE
);
1309 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1311 const gchar
*privacy
= NULL
;
1312 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
)
1315 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1316 privacy
= account
->default_privacy_system
;
1320 GSList
*privacy_avail
= privacy_get_system_ids();
1321 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1322 privacy
= (gchar
*)(privacy_avail
->data
);
1326 if (privacy
!= NULL
) {
1328 g_free(compose
->privacy_system
);
1329 compose
->privacy_system
= NULL
;
1330 g_free(compose
->encdata
);
1331 compose
->encdata
= NULL
;
1333 if (compose
->privacy_system
== NULL
)
1334 compose
->privacy_system
= g_strdup(privacy
);
1335 compose_update_privacy_system_menu_item(compose
, FALSE
);
1336 compose_use_signing(compose
, TRUE
);
1340 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1344 Compose
*compose
= NULL
;
1346 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1348 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1349 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1351 list_len
= g_slist_length(msginfo_list
);
1355 case COMPOSE_REPLY_TO_ADDRESS
:
1356 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1357 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1359 case COMPOSE_REPLY_WITH_QUOTE
:
1360 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1361 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1363 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1364 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1365 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1367 case COMPOSE_REPLY_TO_SENDER
:
1368 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1369 FALSE
, FALSE
, TRUE
, body
);
1371 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1372 compose
= compose_followup_and_reply_to(msginfo
,
1373 COMPOSE_QUOTE_CHECK
,
1374 FALSE
, FALSE
, body
);
1376 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1377 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1378 FALSE
, FALSE
, TRUE
, body
);
1380 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1381 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1382 FALSE
, FALSE
, TRUE
, NULL
);
1384 case COMPOSE_REPLY_TO_ALL
:
1385 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1386 TRUE
, FALSE
, FALSE
, body
);
1388 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1389 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1390 TRUE
, FALSE
, FALSE
, body
);
1392 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1393 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1394 TRUE
, FALSE
, FALSE
, NULL
);
1396 case COMPOSE_REPLY_TO_LIST
:
1397 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1398 FALSE
, TRUE
, FALSE
, body
);
1400 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1401 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1402 FALSE
, TRUE
, FALSE
, body
);
1404 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1405 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1406 FALSE
, TRUE
, FALSE
, NULL
);
1408 case COMPOSE_FORWARD
:
1409 if (prefs_common
.forward_as_attachment
) {
1410 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1413 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1417 case COMPOSE_FORWARD_INLINE
:
1418 /* check if we reply to more than one Message */
1419 if (list_len
== 1) {
1420 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1423 /* more messages FALL THROUGH */
1424 case COMPOSE_FORWARD_AS_ATTACH
:
1425 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1427 case COMPOSE_REDIRECT
:
1428 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1431 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode
);
1434 if (compose
== NULL
) {
1435 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1439 compose
->rmode
= mode
;
1440 switch (compose
->rmode
) {
1442 case COMPOSE_REPLY_WITH_QUOTE
:
1443 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1444 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1445 debug_print("reply mode Normal\n");
1446 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1447 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1449 case COMPOSE_REPLY_TO_SENDER
:
1450 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1451 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1452 debug_print("reply mode Sender\n");
1453 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1455 case COMPOSE_REPLY_TO_ALL
:
1456 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1457 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1458 debug_print("reply mode All\n");
1459 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1461 case COMPOSE_REPLY_TO_LIST
:
1462 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1463 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1464 debug_print("reply mode List\n");
1465 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1467 case COMPOSE_REPLY_TO_ADDRESS
:
1468 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1476 static Compose
*compose_reply(MsgInfo
*msginfo
,
1477 ComposeQuoteMode quote_mode
,
1483 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1484 to_sender
, FALSE
, body
);
1487 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1488 ComposeQuoteMode quote_mode
,
1493 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1494 to_sender
, TRUE
, body
);
1497 static void compose_extract_original_charset(Compose
*compose
)
1499 MsgInfo
*info
= NULL
;
1500 if (compose
->replyinfo
) {
1501 info
= compose
->replyinfo
;
1502 } else if (compose
->fwdinfo
) {
1503 info
= compose
->fwdinfo
;
1504 } else if (compose
->targetinfo
) {
1505 info
= compose
->targetinfo
;
1508 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1509 MimeInfo
*partinfo
= mimeinfo
;
1510 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1511 partinfo
= procmime_mimeinfo_next(partinfo
);
1513 compose
->orig_charset
=
1514 g_strdup(procmime_mimeinfo_get_parameter(
1515 partinfo
, "charset"));
1517 procmime_mimeinfo_free_all(&mimeinfo
);
1521 #define SIGNAL_BLOCK(buffer) { \
1522 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1523 G_CALLBACK(compose_changed_cb), \
1525 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1526 G_CALLBACK(text_inserted), \
1530 #define SIGNAL_UNBLOCK(buffer) { \
1531 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1532 G_CALLBACK(compose_changed_cb), \
1534 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1535 G_CALLBACK(text_inserted), \
1539 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1540 ComposeQuoteMode quote_mode
,
1541 gboolean to_all
, gboolean to_ml
,
1543 gboolean followup_and_reply_to
,
1547 PrefsAccount
*account
= NULL
;
1548 GtkTextView
*textview
;
1549 GtkTextBuffer
*textbuf
;
1550 gboolean quote
= FALSE
;
1551 const gchar
*qmark
= NULL
;
1552 const gchar
*body_fmt
= NULL
;
1553 gchar
*s_system
= NULL
;
1555 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1556 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1558 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1560 cm_return_val_if_fail(account
!= NULL
, NULL
);
1562 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1563 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1565 compose
->updating
= TRUE
;
1567 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1568 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1570 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1571 if (!compose
->replyinfo
)
1572 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1574 compose_extract_original_charset(compose
);
1576 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1577 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1579 /* Set save folder */
1580 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1581 gchar
*folderidentifier
;
1583 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1584 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1585 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1586 compose_set_save_to(compose
, folderidentifier
);
1587 g_free(folderidentifier
);
1590 if (compose_parse_header(compose
, msginfo
) < 0) {
1591 compose
->updating
= FALSE
;
1592 compose_destroy(compose
);
1596 /* override from name according to folder properties */
1597 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1598 msginfo
->folder
->prefs
->reply_with_format
&&
1599 msginfo
->folder
->prefs
->reply_override_from_format
&&
1600 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1605 /* decode \-escape sequences in the internal representation of the quote format */
1606 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1607 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1610 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1611 compose
->gtkaspell
);
1613 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1615 quote_fmt_scan_string(tmp
);
1618 buf
= quote_fmt_get_buffer();
1620 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1622 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1623 quote_fmt_reset_vartable();
1624 quote_fmtlex_destroy();
1629 textview
= (GTK_TEXT_VIEW(compose
->text
));
1630 textbuf
= gtk_text_view_get_buffer(textview
);
1631 compose_create_tags(textview
, compose
);
1633 undo_block(compose
->undostruct
);
1635 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1636 gtkaspell_block_check(compose
->gtkaspell
);
1639 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1640 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1641 /* use the reply format of folder (if enabled), or the account's one
1642 (if enabled) or fallback to the global reply format, which is always
1643 enabled (even if empty), and use the relevant quotemark */
1645 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1646 msginfo
->folder
->prefs
->reply_with_format
) {
1647 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1648 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1650 } else if (account
->reply_with_format
) {
1651 qmark
= account
->reply_quotemark
;
1652 body_fmt
= account
->reply_body_format
;
1655 qmark
= prefs_common
.quotemark
;
1656 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1657 body_fmt
= gettext(prefs_common
.quotefmt
);
1664 /* empty quotemark is not allowed */
1665 if (qmark
== NULL
|| *qmark
== '\0')
1667 compose_quote_fmt(compose
, compose
->replyinfo
,
1668 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1669 _("The body of the \"Reply\" template has an error at line %d."));
1670 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1671 quote_fmt_reset_vartable();
1674 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1675 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1678 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1679 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1680 compose_force_signing(compose
, account
, s_system
);
1684 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1685 ((account
->default_encrypt
|| account
->default_sign
) ||
1686 (account
->default_encrypt_reply
&& MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) ||
1687 (account
->default_sign_reply
&& MSG_IS_SIGNED(compose
->replyinfo
->flags
))))
1688 COMPOSE_PRIVACY_WARNING();
1690 SIGNAL_BLOCK(textbuf
);
1692 if (account
->auto_sig
)
1693 compose_insert_sig(compose
, FALSE
);
1695 compose_wrap_all(compose
);
1698 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1699 gtkaspell_highlight_all(compose
->gtkaspell
);
1700 gtkaspell_unblock_check(compose
->gtkaspell
);
1702 SIGNAL_UNBLOCK(textbuf
);
1704 gtk_widget_grab_focus(compose
->text
);
1706 undo_unblock(compose
->undostruct
);
1708 if (prefs_common
.auto_exteditor
)
1709 compose_exec_ext_editor(compose
);
1711 compose
->modified
= FALSE
;
1712 compose_set_title(compose
);
1714 compose
->updating
= FALSE
;
1715 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1716 SCROLL_TO_CURSOR(compose
);
1718 if (compose
->deferred_destroy
) {
1719 compose_destroy(compose
);
1727 #define INSERT_FW_HEADER(var, hdr) \
1728 if (msginfo->var && *msginfo->var) { \
1729 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1730 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1731 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1734 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1735 gboolean as_attach
, const gchar
*body
,
1736 gboolean no_extedit
,
1740 GtkTextView
*textview
;
1741 GtkTextBuffer
*textbuf
;
1742 gint cursor_pos
= -1;
1745 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1746 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1748 if (!account
&& !(account
= compose_find_account(msginfo
)))
1749 account
= cur_account
;
1751 if (!prefs_common
.forward_as_attachment
)
1752 mode
= COMPOSE_FORWARD_INLINE
;
1754 mode
= COMPOSE_FORWARD
;
1755 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1756 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
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();
1814 quote_fmtlex_destroy();
1817 procmsg_msginfo_free(&full_msginfo
);
1820 textview
= GTK_TEXT_VIEW(compose
->text
);
1821 textbuf
= gtk_text_view_get_buffer(textview
);
1822 compose_create_tags(textview
, compose
);
1824 undo_block(compose
->undostruct
);
1828 msgfile
= procmsg_get_message_file(msginfo
);
1829 if (!is_file_exist(msgfile
))
1830 g_warning("%s: file does not exist", msgfile
);
1832 compose_attach_append(compose
, msgfile
, msgfile
,
1833 "message/rfc822", NULL
);
1837 const gchar
*qmark
= NULL
;
1838 const gchar
*body_fmt
= NULL
;
1839 MsgInfo
*full_msginfo
;
1841 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1843 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1845 /* use the forward format of folder (if enabled), or the account's one
1846 (if enabled) or fallback to the global forward format, which is always
1847 enabled (even if empty), and use the relevant quotemark */
1848 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1849 msginfo
->folder
->prefs
->forward_with_format
) {
1850 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1851 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1853 } else if (account
->forward_with_format
) {
1854 qmark
= account
->forward_quotemark
;
1855 body_fmt
= account
->forward_body_format
;
1858 qmark
= prefs_common
.fw_quotemark
;
1859 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1860 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1865 /* empty quotemark is not allowed */
1866 if (qmark
== NULL
|| *qmark
== '\0')
1869 compose_quote_fmt(compose
, full_msginfo
,
1870 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1871 _("The body of the \"Forward\" template has an error at line %d."));
1872 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1873 quote_fmt_reset_vartable();
1874 compose_attach_parts(compose
, msginfo
);
1876 procmsg_msginfo_free(&full_msginfo
);
1879 SIGNAL_BLOCK(textbuf
);
1881 if (account
->auto_sig
)
1882 compose_insert_sig(compose
, FALSE
);
1884 compose_wrap_all(compose
);
1887 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1888 gtkaspell_highlight_all(compose
->gtkaspell
);
1889 gtkaspell_unblock_check(compose
->gtkaspell
);
1891 SIGNAL_UNBLOCK(textbuf
);
1893 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1894 (account
->default_encrypt
|| account
->default_sign
))
1895 COMPOSE_PRIVACY_WARNING();
1897 cursor_pos
= quote_fmt_get_cursor_pos();
1898 if (cursor_pos
== -1)
1899 gtk_widget_grab_focus(compose
->header_last
->entry
);
1901 gtk_widget_grab_focus(compose
->text
);
1903 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1904 compose_exec_ext_editor(compose
);
1907 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1908 gchar
*folderidentifier
;
1910 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1911 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1912 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1913 compose_set_save_to(compose
, folderidentifier
);
1914 g_free(folderidentifier
);
1917 undo_unblock(compose
->undostruct
);
1919 compose
->modified
= FALSE
;
1920 compose_set_title(compose
);
1922 compose
->updating
= FALSE
;
1923 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1924 SCROLL_TO_CURSOR(compose
);
1926 if (compose
->deferred_destroy
) {
1927 compose_destroy(compose
);
1931 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1936 #undef INSERT_FW_HEADER
1938 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1941 GtkTextView
*textview
;
1942 GtkTextBuffer
*textbuf
;
1946 gboolean single_mail
= TRUE
;
1948 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1950 if (g_slist_length(msginfo_list
) > 1)
1951 single_mail
= FALSE
;
1953 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1954 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1957 /* guess account from first selected message */
1959 !(account
= compose_find_account(msginfo_list
->data
)))
1960 account
= cur_account
;
1962 cm_return_val_if_fail(account
!= NULL
, NULL
);
1964 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1965 if (msginfo
->data
) {
1966 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1967 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1971 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1972 g_warning("no msginfo_list");
1976 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1977 compose_apply_folder_privacy_settings(compose
, ((MsgInfo
*)msginfo_list
->data
)->folder
);
1978 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1979 (account
->default_encrypt
|| account
->default_sign
))
1980 COMPOSE_PRIVACY_WARNING();
1982 compose
->updating
= TRUE
;
1984 /* override from name according to folder properties */
1985 if (msginfo_list
->data
) {
1986 MsgInfo
*msginfo
= msginfo_list
->data
;
1988 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1989 msginfo
->folder
->prefs
->forward_with_format
&&
1990 msginfo
->folder
->prefs
->forward_override_from_format
&&
1991 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1996 /* decode \-escape sequences in the internal representation of the quote format */
1997 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1998 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
2001 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2002 compose
->gtkaspell
);
2004 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2006 quote_fmt_scan_string(tmp
);
2009 buf
= quote_fmt_get_buffer();
2011 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2013 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2014 quote_fmt_reset_vartable();
2015 quote_fmtlex_destroy();
2021 textview
= GTK_TEXT_VIEW(compose
->text
);
2022 textbuf
= gtk_text_view_get_buffer(textview
);
2023 compose_create_tags(textview
, compose
);
2025 undo_block(compose
->undostruct
);
2026 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2027 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2029 if (!is_file_exist(msgfile
))
2030 g_warning("%s: file does not exist", msgfile
);
2032 compose_attach_append(compose
, msgfile
, msgfile
,
2033 "message/rfc822", NULL
);
2038 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2039 if (info
->subject
&& *info
->subject
) {
2040 gchar
*buf
, *buf2
, *p
;
2042 buf
= p
= g_strdup(info
->subject
);
2043 p
+= subject_get_prefix_length(p
);
2044 memmove(buf
, p
, strlen(p
) + 1);
2046 buf2
= g_strdup_printf("Fw: %s", buf
);
2047 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2053 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2054 _("Fw: multiple emails"));
2057 SIGNAL_BLOCK(textbuf
);
2059 if (account
->auto_sig
)
2060 compose_insert_sig(compose
, FALSE
);
2062 compose_wrap_all(compose
);
2064 SIGNAL_UNBLOCK(textbuf
);
2066 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2067 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2069 if (prefs_common
.auto_exteditor
)
2070 compose_exec_ext_editor(compose
);
2072 gtk_widget_grab_focus(compose
->header_last
->entry
);
2073 undo_unblock(compose
->undostruct
);
2074 compose
->modified
= FALSE
;
2075 compose_set_title(compose
);
2077 compose
->updating
= FALSE
;
2078 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2079 SCROLL_TO_CURSOR(compose
);
2081 if (compose
->deferred_destroy
) {
2082 compose_destroy(compose
);
2086 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2091 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2093 GtkTextIter start
= *iter
;
2094 GtkTextIter end_iter
;
2095 int start_pos
= gtk_text_iter_get_offset(&start
);
2097 if (!compose
->account
->sig_sep
)
2100 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2101 start_pos
+strlen(compose
->account
->sig_sep
));
2103 /* check sig separator */
2104 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2105 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2107 /* check end of line (\n) */
2108 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2109 start_pos
+strlen(compose
->account
->sig_sep
));
2110 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2111 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2112 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2113 if (!strcmp(tmp
,"\n")) {
2125 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2127 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2128 Compose
*compose
= (Compose
*)data
;
2129 FolderItem
*old_item
= NULL
;
2130 FolderItem
*new_item
= NULL
;
2131 gchar
*old_id
, *new_id
;
2133 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2134 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2137 old_item
= hookdata
->item
;
2138 new_item
= hookdata
->item2
;
2140 old_id
= folder_item_get_identifier(old_item
);
2141 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2143 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2144 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2145 compose
->targetinfo
->folder
= new_item
;
2148 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2149 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2150 compose
->replyinfo
->folder
= new_item
;
2153 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2154 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2155 compose
->fwdinfo
->folder
= new_item
;
2163 static void compose_colorize_signature(Compose
*compose
)
2165 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2167 GtkTextIter end_iter
;
2168 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2169 while (gtk_text_iter_forward_line(&iter
))
2170 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2171 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2172 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2176 #define BLOCK_WRAP() { \
2177 prev_autowrap = compose->autowrap; \
2178 buffer = gtk_text_view_get_buffer( \
2179 GTK_TEXT_VIEW(compose->text)); \
2180 compose->autowrap = FALSE; \
2182 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2183 G_CALLBACK(compose_changed_cb), \
2185 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2186 G_CALLBACK(text_inserted), \
2189 #define UNBLOCK_WRAP() { \
2190 compose->autowrap = prev_autowrap; \
2191 if (compose->autowrap) { \
2192 gint old = compose->draft_timeout_tag; \
2193 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2194 compose_wrap_all(compose); \
2195 compose->draft_timeout_tag = old; \
2198 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2199 G_CALLBACK(compose_changed_cb), \
2201 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2202 G_CALLBACK(text_inserted), \
2206 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2208 Compose
*compose
= NULL
;
2209 PrefsAccount
*account
= NULL
;
2210 GtkTextView
*textview
;
2211 GtkTextBuffer
*textbuf
;
2215 gboolean use_signing
= FALSE
;
2216 gboolean use_encryption
= FALSE
;
2217 gchar
*privacy_system
= NULL
;
2218 int priority
= PRIORITY_NORMAL
;
2219 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2220 gboolean autowrap
= prefs_common
.autowrap
;
2221 gboolean autoindent
= prefs_common
.auto_indent
;
2222 HeaderEntry
*manual_headers
= NULL
;
2224 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2225 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2227 if (compose_put_existing_to_front(msginfo
)) {
2231 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2232 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2233 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2234 gchar
*queueheader_buf
= NULL
;
2237 /* Select Account from queue headers */
2238 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2239 "X-Claws-Account-Id:")) {
2240 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2241 account
= account_find_from_id(id
);
2242 g_free(queueheader_buf
);
2244 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2245 "X-Sylpheed-Account-Id:")) {
2246 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2247 account
= account_find_from_id(id
);
2248 g_free(queueheader_buf
);
2250 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2252 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2253 account
= account_find_from_id(id
);
2254 g_free(queueheader_buf
);
2256 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2258 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2259 account
= account_find_from_id(id
);
2260 g_free(queueheader_buf
);
2262 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2264 account
= account_find_from_address(queueheader_buf
, FALSE
);
2265 g_free(queueheader_buf
);
2267 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2269 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2270 use_signing
= param
;
2271 g_free(queueheader_buf
);
2273 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2274 "X-Sylpheed-Sign:")) {
2275 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2276 use_signing
= param
;
2277 g_free(queueheader_buf
);
2279 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2280 "X-Claws-Encrypt:")) {
2281 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2282 use_encryption
= param
;
2283 g_free(queueheader_buf
);
2285 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2286 "X-Sylpheed-Encrypt:")) {
2287 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2288 use_encryption
= param
;
2289 g_free(queueheader_buf
);
2291 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2292 "X-Claws-Auto-Wrapping:")) {
2293 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2295 g_free(queueheader_buf
);
2297 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2298 "X-Claws-Auto-Indent:")) {
2299 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2301 g_free(queueheader_buf
);
2303 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2304 "X-Claws-Privacy-System:")) {
2305 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2306 g_free(queueheader_buf
);
2308 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2309 "X-Sylpheed-Privacy-System:")) {
2310 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2311 g_free(queueheader_buf
);
2313 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2315 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2317 g_free(queueheader_buf
);
2319 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2321 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2322 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2323 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2324 if (orig_item
!= NULL
) {
2325 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2330 g_free(queueheader_buf
);
2332 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2334 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2335 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2336 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2337 if (orig_item
!= NULL
) {
2338 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2343 g_free(queueheader_buf
);
2345 /* Get manual headers */
2346 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2347 "X-Claws-Manual-Headers:")) {
2348 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2349 if (listmh
&& *listmh
!= '\0') {
2350 debug_print("Got manual headers: %s\n", listmh
);
2351 manual_headers
= procheader_entries_from_str(listmh
);
2355 g_free(queueheader_buf
);
2358 account
= msginfo
->folder
->folder
->account
;
2361 if (!account
&& prefs_common
.reedit_account_autosel
) {
2363 if (!procheader_get_header_from_msginfo(msginfo
, &from
, "FROM:")) {
2364 extract_address(from
);
2365 account
= account_find_from_address(from
, FALSE
);
2371 account
= cur_account
;
2374 g_warning("can't select account");
2376 procheader_entries_free(manual_headers
);
2380 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2382 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
2383 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
2384 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2385 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2386 compose
->autowrap
= autowrap
;
2387 compose
->replyinfo
= replyinfo
;
2388 compose
->fwdinfo
= fwdinfo
;
2390 compose
->updating
= TRUE
;
2391 compose
->priority
= priority
;
2393 if (privacy_system
!= NULL
) {
2394 compose
->privacy_system
= privacy_system
;
2395 compose_use_signing(compose
, use_signing
);
2396 compose_use_encryption(compose
, use_encryption
);
2397 compose_update_privacy_system_menu_item(compose
, FALSE
);
2399 compose_activate_privacy_system(compose
, account
, FALSE
);
2401 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
2402 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
2403 (account
->default_encrypt
|| account
->default_sign
))
2404 COMPOSE_PRIVACY_WARNING();
2406 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2407 compose
->targetinfo
->tags
= g_slist_copy(msginfo
->tags
);
2409 compose_extract_original_charset(compose
);
2411 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2412 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2413 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2414 gchar
*queueheader_buf
= NULL
;
2416 /* Set message save folder */
2417 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "SCF:")) {
2418 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2419 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2420 compose_set_save_to(compose
, &queueheader_buf
[4]);
2421 g_free(queueheader_buf
);
2423 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "RRCPT:")) {
2424 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2426 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2428 g_free(queueheader_buf
);
2432 if (compose_parse_header(compose
, msginfo
) < 0) {
2433 compose
->updating
= FALSE
;
2434 compose_destroy(compose
);
2436 procheader_entries_free(manual_headers
);
2439 compose_reedit_set_entry(compose
, msginfo
);
2441 textview
= GTK_TEXT_VIEW(compose
->text
);
2442 textbuf
= gtk_text_view_get_buffer(textview
);
2443 compose_create_tags(textview
, compose
);
2445 mark
= gtk_text_buffer_get_insert(textbuf
);
2446 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2448 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2449 G_CALLBACK(compose_changed_cb
),
2452 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2453 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2455 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2458 fp
= procmime_get_first_text_content(msginfo
);
2461 g_warning("can't get text part");
2465 gchar buf
[BUFFSIZE
];
2466 gboolean prev_autowrap
;
2467 GtkTextBuffer
*buffer
;
2469 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2471 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2477 compose_attach_parts(compose
, msginfo
);
2479 compose_colorize_signature(compose
);
2481 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2482 G_CALLBACK(compose_changed_cb
),
2485 if (manual_headers
!= NULL
) {
2486 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2487 procheader_entries_free(manual_headers
);
2488 compose
->updating
= FALSE
;
2489 compose_destroy(compose
);
2492 procheader_entries_free(manual_headers
);
2495 gtk_widget_grab_focus(compose
->text
);
2497 if (prefs_common
.auto_exteditor
) {
2498 compose_exec_ext_editor(compose
);
2500 compose
->modified
= FALSE
;
2501 compose_set_title(compose
);
2503 compose
->updating
= FALSE
;
2504 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2505 SCROLL_TO_CURSOR(compose
);
2507 if (compose
->deferred_destroy
) {
2508 compose_destroy(compose
);
2512 compose
->sig_str
= account_get_signature_str(compose
->account
);
2514 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2519 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2526 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2529 account
= account_get_reply_account(msginfo
,
2530 prefs_common
.reply_account_autosel
);
2531 cm_return_val_if_fail(account
!= NULL
, NULL
);
2533 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2535 compose
->updating
= TRUE
;
2537 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2538 compose
->replyinfo
= NULL
;
2539 compose
->fwdinfo
= NULL
;
2541 compose_show_first_last_header(compose
, TRUE
);
2543 gtk_widget_grab_focus(compose
->header_last
->entry
);
2545 filename
= procmsg_get_message_file(msginfo
);
2547 if (filename
== NULL
) {
2548 compose
->updating
= FALSE
;
2549 compose_destroy(compose
);
2554 compose
->redirect_filename
= filename
;
2556 /* Set save folder */
2557 item
= msginfo
->folder
;
2558 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2559 gchar
*folderidentifier
;
2561 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2562 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2563 folderidentifier
= folder_item_get_identifier(item
);
2564 compose_set_save_to(compose
, folderidentifier
);
2565 g_free(folderidentifier
);
2568 compose_attach_parts(compose
, msginfo
);
2570 if (msginfo
->subject
)
2571 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2573 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2575 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2576 _("The body of the \"Redirect\" template has an error at line %d."));
2577 quote_fmt_reset_vartable();
2578 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2580 compose_colorize_signature(compose
);
2582 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2583 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2584 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2586 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/SendLater", FALSE
);
2587 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2588 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2589 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2590 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2591 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2592 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2593 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2594 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2595 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2597 if (compose
->toolbar
->sendl_btn
)
2598 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, FALSE
);
2599 if (compose
->toolbar
->draft_btn
)
2600 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2601 if (compose
->toolbar
->insert_btn
)
2602 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2603 if (compose
->toolbar
->attach_btn
)
2604 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2605 if (compose
->toolbar
->sig_btn
)
2606 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2607 if (compose
->toolbar
->exteditor_btn
)
2608 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2609 if (compose
->toolbar
->linewrap_current_btn
)
2610 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2611 if (compose
->toolbar
->linewrap_all_btn
)
2612 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2613 if (compose
->toolbar
->privacy_sign_btn
)
2614 gtk_widget_set_sensitive(compose
->toolbar
->privacy_sign_btn
, FALSE
);
2615 if (compose
->toolbar
->privacy_encrypt_btn
)
2616 gtk_widget_set_sensitive(compose
->toolbar
->privacy_encrypt_btn
, FALSE
);
2618 compose
->modified
= FALSE
;
2619 compose_set_title(compose
);
2620 compose
->updating
= FALSE
;
2621 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2622 SCROLL_TO_CURSOR(compose
);
2624 if (compose
->deferred_destroy
) {
2625 compose_destroy(compose
);
2629 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2634 const GList
*compose_get_compose_list(void)
2636 return compose_list
;
2639 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2640 ComposeEntryType type
, ComposePrefType pref_type
)
2642 const gchar
*header
;
2644 gboolean in_quote
= FALSE
;
2645 if (!address
|| *address
== '\0') return;
2652 header
= N_("Bcc:");
2654 case COMPOSE_REPLYTO
:
2655 header
= N_("Reply-To:");
2657 case COMPOSE_NEWSGROUPS
:
2658 header
= N_("Newsgroups:");
2660 case COMPOSE_FOLLOWUPTO
:
2661 header
= N_( "Followup-To:");
2663 case COMPOSE_INREPLYTO
:
2664 header
= N_( "In-Reply-To:");
2671 header
= prefs_common_translated_header_name(header
);
2673 cur
= begin
= (gchar
*)address
;
2675 /* we separate the line by commas, but not if we're inside a quoted
2677 while (*cur
!= '\0') {
2679 in_quote
= !in_quote
;
2680 if (*cur
== ',' && !in_quote
) {
2681 gchar
*tmp
= g_strdup(begin
);
2683 tmp
[cur
-begin
]='\0';
2686 while (*tmp
== ' ' || *tmp
== '\t')
2688 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2689 compose_entry_indicate(compose
, tmp
);
2696 gchar
*tmp
= g_strdup(begin
);
2698 tmp
[cur
-begin
]='\0';
2699 while (*tmp
== ' ' || *tmp
== '\t')
2701 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2702 compose_entry_indicate(compose
, tmp
);
2707 static void compose_entry_indicate(Compose
*compose
, const gchar
*mailto
)
2713 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2714 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2715 if (gtk_entry_get_text(entry
) &&
2716 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2717 /* Modify background color */
2718 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_bgcolor
, color
);
2719 gtk_widget_modify_base(
2720 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2721 GTK_STATE_NORMAL
, &color
);
2723 /* Modify foreground color */
2724 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_color
, color
);
2725 gtk_widget_modify_text(
2726 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2727 GTK_STATE_NORMAL
, &color
);
2732 void compose_toolbar_cb(gint action
, gpointer data
)
2734 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2735 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2737 cm_return_if_fail(compose
!= NULL
);
2741 compose_send_cb(NULL
, compose
);
2744 compose_send_later_cb(NULL
, compose
);
2747 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2750 compose_insert_file_cb(NULL
, compose
);
2753 compose_attach_cb(NULL
, compose
);
2756 compose_insert_sig(compose
, FALSE
);
2759 compose_insert_sig(compose
, TRUE
);
2762 compose_ext_editor_cb(NULL
, compose
);
2764 case A_LINEWRAP_CURRENT
:
2765 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2767 case A_LINEWRAP_ALL
:
2768 compose_wrap_all_full(compose
, TRUE
);
2771 compose_address_cb(NULL
, compose
);
2774 case A_CHECK_SPELLING
:
2775 compose_check_all(NULL
, compose
);
2778 case A_PRIVACY_SIGN
:
2780 case A_PRIVACY_ENCRYPT
:
2787 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2792 gchar
*subject
= NULL
;
2796 gchar
**attach
= NULL
;
2797 gchar
*inreplyto
= NULL
;
2798 MailField mfield
= NO_FIELD_PRESENT
;
2800 /* get mailto parts but skip from */
2801 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2804 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2805 mfield
= TO_FIELD_PRESENT
;
2808 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2810 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2812 if (!g_utf8_validate (subject
, -1, NULL
)) {
2813 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2814 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2817 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2819 mfield
= SUBJECT_FIELD_PRESENT
;
2822 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2823 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2826 gboolean prev_autowrap
= compose
->autowrap
;
2828 compose
->autowrap
= FALSE
;
2830 mark
= gtk_text_buffer_get_insert(buffer
);
2831 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2833 if (!g_utf8_validate (body
, -1, NULL
)) {
2834 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2835 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2838 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2840 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2842 compose
->autowrap
= prev_autowrap
;
2843 if (compose
->autowrap
)
2844 compose_wrap_all(compose
);
2845 mfield
= BODY_FIELD_PRESENT
;
2849 gint i
= 0, att
= 0;
2850 gchar
*warn_files
= NULL
;
2851 while (attach
[i
] != NULL
) {
2852 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2853 if (utf8_filename
) {
2854 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2855 gchar
*tmp
= g_strdup_printf("%s%s\n",
2856 warn_files
?warn_files
:"",
2862 g_free(utf8_filename
);
2864 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2869 alertpanel_notice(ngettext(
2870 "The following file has been attached: \n%s",
2871 "The following files have been attached: \n%s", att
), warn_files
);
2876 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2889 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2891 static HeaderEntry hentry
[] = {
2892 {"Reply-To:", NULL
, TRUE
},
2893 {"Cc:", NULL
, TRUE
},
2894 {"References:", NULL
, FALSE
},
2895 {"Bcc:", NULL
, TRUE
},
2896 {"Newsgroups:", NULL
, TRUE
},
2897 {"Followup-To:", NULL
, TRUE
},
2898 {"List-Post:", NULL
, FALSE
},
2899 {"X-Priority:", NULL
, FALSE
},
2900 {NULL
, NULL
, FALSE
}
2917 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2919 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
2920 procheader_get_header_fields(fp
, hentry
);
2923 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2924 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2926 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2929 g_free(hentry
[H_REPLY_TO
].body
);
2930 hentry
[H_REPLY_TO
].body
= NULL
;
2932 if (hentry
[H_CC
].body
!= NULL
) {
2933 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2934 g_free(hentry
[H_CC
].body
);
2935 hentry
[H_CC
].body
= NULL
;
2937 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2938 if (compose
->mode
== COMPOSE_REEDIT
)
2939 compose
->references
= hentry
[H_REFERENCES
].body
;
2941 compose
->references
= compose_parse_references
2942 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2943 g_free(hentry
[H_REFERENCES
].body
);
2945 hentry
[H_REFERENCES
].body
= NULL
;
2947 if (hentry
[H_BCC
].body
!= NULL
) {
2948 if (compose
->mode
== COMPOSE_REEDIT
)
2950 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2951 g_free(hentry
[H_BCC
].body
);
2952 hentry
[H_BCC
].body
= NULL
;
2954 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2955 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2956 hentry
[H_NEWSGROUPS
].body
= NULL
;
2958 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2959 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2960 compose
->followup_to
=
2961 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2964 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2965 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2967 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2968 gchar
*to
= NULL
, *start
= NULL
;
2970 extract_address(hentry
[H_LIST_POST
].body
);
2971 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2972 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2974 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2975 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2978 g_free(compose
->ml_post
);
2979 compose
->ml_post
= to
;
2982 g_free(hentry
[H_LIST_POST
].body
);
2983 hentry
[H_LIST_POST
].body
= NULL
;
2986 /* CLAWS - X-Priority */
2987 if (compose
->mode
== COMPOSE_REEDIT
)
2988 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2991 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2992 g_free(hentry
[H_X_PRIORITY
].body
);
2994 hentry
[H_X_PRIORITY
].body
= NULL
;
2996 if (priority
< PRIORITY_HIGHEST
||
2997 priority
> PRIORITY_LOWEST
)
2998 priority
= PRIORITY_NORMAL
;
3000 compose
->priority
= priority
;
3003 if (compose
->mode
== COMPOSE_REEDIT
) {
3004 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3005 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
3007 if (msginfo
->msgid
&& *msginfo
->msgid
&&
3008 compose
->folder
!= NULL
&&
3009 compose
->folder
->stype
== F_DRAFT
)
3010 compose
->msgid
= g_strdup(msginfo
->msgid
);
3012 if (msginfo
->msgid
&& *msginfo
->msgid
)
3013 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
3015 if (!compose
->references
) {
3016 if (msginfo
->msgid
&& *msginfo
->msgid
) {
3017 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
3018 compose
->references
=
3019 g_strdup_printf("<%s>\n\t<%s>",
3023 compose
->references
=
3024 g_strconcat("<", msginfo
->msgid
, ">",
3026 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
3027 compose
->references
=
3028 g_strconcat("<", msginfo
->inreplyto
, ">",
3037 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
3042 cm_return_val_if_fail(msginfo
!= NULL
, -1);
3044 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
3045 procheader_get_header_fields(fp
, entries
);
3049 while (he
!= NULL
&& he
->name
!= NULL
) {
3051 GtkListStore
*model
= NULL
;
3053 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3054 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3055 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3056 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3057 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3064 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3066 GSList
*ref_id_list
, *cur
;
3069 ref_id_list
= references_list_append(NULL
, ref
);
3070 if (!ref_id_list
) return NULL
;
3071 if (msgid
&& *msgid
)
3072 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3077 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3078 /* "<" + Message-ID + ">" + CR+LF+TAB */
3079 len
+= strlen((gchar
*)cur
->data
) + 5;
3081 if (len
> MAX_REFERENCES_LEN
) {
3082 /* remove second message-ID */
3083 if (ref_id_list
&& ref_id_list
->next
&&
3084 ref_id_list
->next
->next
) {
3085 g_free(ref_id_list
->next
->data
);
3086 ref_id_list
= g_slist_remove
3087 (ref_id_list
, ref_id_list
->next
->data
);
3089 slist_free_strings_full(ref_id_list
);
3096 new_ref
= g_string_new("");
3097 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3098 if (new_ref
->len
> 0)
3099 g_string_append(new_ref
, "\n\t");
3100 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3103 slist_free_strings_full(ref_id_list
);
3105 return g_string_free(new_ref
, FALSE
);
3108 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3109 const gchar
*fmt
, const gchar
*qmark
,
3110 const gchar
*body
, gboolean rewrap
,
3111 gboolean need_unescape
,
3112 const gchar
*err_msg
)
3114 MsgInfo
* dummyinfo
= NULL
;
3115 gchar
*quote_str
= NULL
;
3117 gboolean prev_autowrap
;
3118 const gchar
*trimmed_body
= body
;
3119 gint cursor_pos
= -1;
3120 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3121 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3126 SIGNAL_BLOCK(buffer
);
3129 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3130 msginfo
= dummyinfo
;
3133 if (qmark
!= NULL
) {
3135 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3136 compose
->gtkaspell
);
3138 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3140 quote_fmt_scan_string(qmark
);
3143 buf
= quote_fmt_get_buffer();
3146 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3148 Xstrdup_a(quote_str
, buf
, goto error
)
3151 if (fmt
&& *fmt
!= '\0') {
3154 while (*trimmed_body
== '\n')
3158 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3159 compose
->gtkaspell
);
3161 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3163 if (need_unescape
) {
3166 /* decode \-escape sequences in the internal representation of the quote format */
3167 tmp
= g_malloc(strlen(fmt
)+1);
3168 pref_get_unescaped_pref(tmp
, fmt
);
3169 quote_fmt_scan_string(tmp
);
3173 quote_fmt_scan_string(fmt
);
3177 buf
= quote_fmt_get_buffer();
3180 gint line
= quote_fmt_get_line();
3181 alertpanel_error(err_msg
, line
);
3189 prev_autowrap
= compose
->autowrap
;
3190 compose
->autowrap
= FALSE
;
3192 mark
= gtk_text_buffer_get_insert(buffer
);
3193 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3194 if (g_utf8_validate(buf
, -1, NULL
)) {
3195 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3197 gchar
*tmpout
= NULL
;
3198 tmpout
= conv_codeset_strdup
3199 (buf
, conv_get_locale_charset_str_no_utf8(),
3201 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3203 tmpout
= g_malloc(strlen(buf
)*2+1);
3204 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3206 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3210 cursor_pos
= quote_fmt_get_cursor_pos();
3211 if (cursor_pos
== -1)
3212 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3213 compose
->set_cursor_pos
= cursor_pos
;
3215 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3216 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3217 gtk_text_buffer_place_cursor(buffer
, &iter
);
3219 compose
->autowrap
= prev_autowrap
;
3220 if (compose
->autowrap
&& rewrap
)
3221 compose_wrap_all(compose
);
3228 SIGNAL_UNBLOCK(buffer
);
3230 procmsg_msginfo_free( &dummyinfo
);
3235 /* if ml_post is of type addr@host and from is of type
3236 * addr-anything@host, return TRUE
3238 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3240 gchar
*left_ml
= NULL
;
3241 gchar
*right_ml
= NULL
;
3242 gchar
*left_from
= NULL
;
3243 gchar
*right_from
= NULL
;
3244 gboolean result
= FALSE
;
3246 if (!ml_post
|| !from
)
3249 left_ml
= g_strdup(ml_post
);
3250 if (strstr(left_ml
, "@")) {
3251 right_ml
= strstr(left_ml
, "@")+1;
3252 *(strstr(left_ml
, "@")) = '\0';
3255 left_from
= g_strdup(from
);
3256 if (strstr(left_from
, "@")) {
3257 right_from
= strstr(left_from
, "@")+1;
3258 *(strstr(left_from
, "@")) = '\0';
3261 if (right_ml
&& right_from
3262 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3263 && !strcmp(right_from
, right_ml
)) {
3272 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3273 gboolean respect_default_to
)
3277 if (!folder
|| !folder
->prefs
)
3280 if (folder
->prefs
->enable_default_from
) {
3281 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), folder
->prefs
->default_from
);
3282 compose_entry_indicate(compose
, folder
->prefs
->default_from
);
3284 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3285 compose_entry_append(compose
, folder
->prefs
->default_to
,
3286 COMPOSE_TO
, PREF_FOLDER
);
3287 compose_entry_indicate(compose
, folder
->prefs
->default_to
);
3289 if (folder
->prefs
->enable_default_cc
) {
3290 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3291 COMPOSE_CC
, PREF_FOLDER
);
3292 compose_entry_indicate(compose
, folder
->prefs
->default_cc
);
3294 if (folder
->prefs
->enable_default_bcc
) {
3295 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3296 COMPOSE_BCC
, PREF_FOLDER
);
3297 compose_entry_indicate(compose
, folder
->prefs
->default_bcc
);
3299 if (folder
->prefs
->enable_default_replyto
) {
3300 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3301 COMPOSE_REPLYTO
, PREF_FOLDER
);
3302 compose_entry_indicate(compose
, folder
->prefs
->default_replyto
);
3306 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3311 if (!compose
|| !msginfo
)
3314 if (msginfo
->subject
&& *msginfo
->subject
) {
3315 buf
= p
= g_strdup(msginfo
->subject
);
3316 p
+= subject_get_prefix_length(p
);
3317 memmove(buf
, p
, strlen(p
) + 1);
3319 buf2
= g_strdup_printf("Re: %s", buf
);
3320 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3325 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3328 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3329 gboolean to_all
, gboolean to_ml
,
3331 gboolean followup_and_reply_to
)
3333 GSList
*cc_list
= NULL
;
3336 gchar
*replyto
= NULL
;
3337 gchar
*ac_email
= NULL
;
3339 gboolean reply_to_ml
= FALSE
;
3340 gboolean default_reply_to
= FALSE
;
3342 cm_return_if_fail(compose
->account
!= NULL
);
3343 cm_return_if_fail(msginfo
!= NULL
);
3345 reply_to_ml
= to_ml
&& compose
->ml_post
;
3347 default_reply_to
= msginfo
->folder
&&
3348 msginfo
->folder
->prefs
->enable_default_reply_to
;
3350 if (compose
->account
->protocol
!= A_NNTP
) {
3351 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3353 if (reply_to_ml
&& !default_reply_to
) {
3355 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3358 /* normal answer to ml post with a reply-to */
3359 compose_entry_append(compose
,
3361 COMPOSE_TO
, PREF_ML
);
3362 if (compose
->replyto
)
3363 compose_entry_append(compose
,
3365 COMPOSE_CC
, PREF_ML
);
3367 /* answer to subscription confirmation */
3368 if (compose
->replyto
)
3369 compose_entry_append(compose
,
3371 COMPOSE_TO
, PREF_ML
);
3372 else if (msginfo
->from
)
3373 compose_entry_append(compose
,
3375 COMPOSE_TO
, PREF_ML
);
3378 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3379 compose_entry_append(compose
,
3380 msginfo
->folder
->prefs
->default_reply_to
,
3381 COMPOSE_TO
, PREF_FOLDER
);
3382 compose_entry_indicate(compose
,
3383 msginfo
->folder
->prefs
->default_reply_to
);
3389 compose_entry_append(compose
, msginfo
->from
,
3390 COMPOSE_TO
, PREF_NONE
);
3392 Xstrdup_a(tmp1
, msginfo
->from
, return);
3393 extract_address(tmp1
);
3394 compose_entry_append(compose
,
3395 (!account_find_from_address(tmp1
, FALSE
))
3398 COMPOSE_TO
, PREF_NONE
);
3399 if (compose
->replyto
)
3400 compose_entry_append(compose
,
3402 COMPOSE_CC
, PREF_NONE
);
3404 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3405 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3406 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3407 if (compose
->replyto
) {
3408 compose_entry_append(compose
,
3410 COMPOSE_TO
, PREF_NONE
);
3412 compose_entry_append(compose
,
3413 msginfo
->from
? msginfo
->from
: "",
3414 COMPOSE_TO
, PREF_NONE
);
3417 /* replying to own mail, use original recp */
3418 compose_entry_append(compose
,
3419 msginfo
->to
? msginfo
->to
: "",
3420 COMPOSE_TO
, PREF_NONE
);
3421 compose_entry_append(compose
,
3422 msginfo
->cc
? msginfo
->cc
: "",
3423 COMPOSE_CC
, PREF_NONE
);
3428 if (to_sender
|| (compose
->followup_to
&&
3429 !strncmp(compose
->followup_to
, "poster", 6)))
3430 compose_entry_append
3432 (compose
->replyto
? compose
->replyto
:
3433 msginfo
->from
? msginfo
->from
: ""),
3434 COMPOSE_TO
, PREF_NONE
);
3436 else if (followup_and_reply_to
|| to_all
) {
3437 compose_entry_append
3439 (compose
->replyto
? compose
->replyto
:
3440 msginfo
->from
? msginfo
->from
: ""),
3441 COMPOSE_TO
, PREF_NONE
);
3443 compose_entry_append
3445 compose
->followup_to
? compose
->followup_to
:
3446 compose
->newsgroups
? compose
->newsgroups
: "",
3447 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3449 compose_entry_append
3451 msginfo
->cc
? msginfo
->cc
: "",
3452 COMPOSE_CC
, PREF_NONE
);
3455 compose_entry_append
3457 compose
->followup_to
? compose
->followup_to
:
3458 compose
->newsgroups
? compose
->newsgroups
: "",
3459 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3461 compose_reply_set_subject(compose
, msginfo
);
3463 if (to_ml
&& compose
->ml_post
) return;
3464 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3466 if (compose
->replyto
) {
3467 Xstrdup_a(replyto
, compose
->replyto
, return);
3468 extract_address(replyto
);
3470 if (msginfo
->from
) {
3471 Xstrdup_a(from
, msginfo
->from
, return);
3472 extract_address(from
);
3475 if (replyto
&& from
)
3476 cc_list
= address_list_append_with_comments(cc_list
, from
);
3477 if (to_all
&& msginfo
->folder
&&
3478 msginfo
->folder
->prefs
->enable_default_reply_to
)
3479 cc_list
= address_list_append_with_comments(cc_list
,
3480 msginfo
->folder
->prefs
->default_reply_to
);
3481 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3482 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3484 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3487 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3488 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3489 extract_address(addr
);
3491 if (strcmp(ac_email
, addr
))
3492 compose_entry_append(compose
, (gchar
*)cur
->data
,
3493 COMPOSE_CC
, PREF_NONE
);
3495 debug_print("Cc address same as compose account's, ignoring\n");
3500 slist_free_strings_full(cc_list
);
3506 #define SET_ENTRY(entry, str) \
3509 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3512 #define SET_ADDRESS(type, str) \
3515 compose_entry_append(compose, str, type, PREF_NONE); \
3518 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3520 cm_return_if_fail(msginfo
!= NULL
);
3522 SET_ENTRY(subject_entry
, msginfo
->subject
);
3523 SET_ENTRY(from_name
, msginfo
->from
);
3524 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3525 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3526 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3527 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3528 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3529 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3531 compose_update_priority_menu_item(compose
);
3532 compose_update_privacy_system_menu_item(compose
, FALSE
);
3533 compose_show_first_last_header(compose
, TRUE
);
3539 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3541 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3542 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3544 GtkTextIter iter
, iter_end
;
3545 gint cur_pos
, ins_pos
;
3546 gboolean prev_autowrap
;
3547 gboolean found
= FALSE
;
3548 gboolean exists
= FALSE
;
3550 cm_return_if_fail(compose
->account
!= NULL
);
3554 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3555 G_CALLBACK(compose_changed_cb
),
3558 mark
= gtk_text_buffer_get_insert(buffer
);
3559 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3560 cur_pos
= gtk_text_iter_get_offset (&iter
);
3563 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3565 exists
= (compose
->sig_str
!= NULL
);
3568 GtkTextIter first_iter
, start_iter
, end_iter
;
3570 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3572 if (!exists
|| compose
->sig_str
[0] == '\0')
3575 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3576 compose
->signature_tag
);
3579 /* include previous \n\n */
3580 gtk_text_iter_backward_chars(&first_iter
, 1);
3581 start_iter
= first_iter
;
3582 end_iter
= first_iter
;
3584 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3585 compose
->signature_tag
);
3586 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3587 compose
->signature_tag
);
3589 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3595 g_free(compose
->sig_str
);
3596 compose
->sig_str
= account_get_signature_str(compose
->account
);
3598 cur_pos
= gtk_text_iter_get_offset(&iter
);
3600 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3601 g_free(compose
->sig_str
);
3602 compose
->sig_str
= NULL
;
3604 if (compose
->sig_inserted
== FALSE
)
3605 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3606 compose
->sig_inserted
= TRUE
;
3608 cur_pos
= gtk_text_iter_get_offset(&iter
);
3609 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3611 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3612 gtk_text_iter_forward_chars(&iter
, 1);
3613 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3614 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3616 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3617 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3620 /* put the cursor where it should be
3621 * either where the quote_fmt says, either where it was */
3622 if (compose
->set_cursor_pos
< 0)
3623 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3625 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3626 compose
->set_cursor_pos
);
3628 compose
->set_cursor_pos
= -1;
3629 gtk_text_buffer_place_cursor(buffer
, &iter
);
3630 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3631 G_CALLBACK(compose_changed_cb
),
3637 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3640 GtkTextBuffer
*buffer
;
3643 const gchar
*cur_encoding
;
3644 gchar buf
[BUFFSIZE
];
3647 gboolean prev_autowrap
;
3651 GError
*error
= NULL
;
3657 GString
*file_contents
= NULL
;
3658 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3660 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3662 /* get the size of the file we are about to insert */
3664 f
= g_file_new_for_path(file
);
3665 fi
= g_file_query_info(f
, "standard::size",
3666 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
3668 if (error
!= NULL
) {
3669 g_warning(error
->message
);
3671 g_error_free(error
);
3675 ret
= g_stat(file
, &file_stat
);
3678 gchar
*shortfile
= g_path_get_basename(file
);
3679 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3681 return COMPOSE_INSERT_NO_FILE
;
3682 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3684 size
= g_file_info_get_size(fi
);
3688 size
= file_stat
.st_size
;
3691 /* ask user for confirmation if the file is large */
3692 if (prefs_common
.warn_large_insert_size
< 0 ||
3693 size
> ((goffset
) prefs_common
.warn_large_insert_size
* 1024)) {
3697 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3698 "in the message body. Are you sure you want to do that?"),
3699 to_human_readable(size
));
3700 aval
= alertpanel_full(_("Are you sure?"), msg
, NULL
, _("_Cancel"),
3701 NULL
, _("_Insert"), NULL
, NULL
, ALERTFOCUS_SECOND
, TRUE
,
3702 NULL
, ALERT_QUESTION
);
3705 /* do we ask for confirmation next time? */
3706 if (aval
& G_ALERTDISABLE
) {
3707 /* no confirmation next time, disable feature in preferences */
3708 aval
&= ~G_ALERTDISABLE
;
3709 prefs_common
.warn_large_insert
= FALSE
;
3712 /* abort file insertion if user canceled action */
3713 if (aval
!= G_ALERTALTERNATE
) {
3714 return COMPOSE_INSERT_NO_FILE
;
3720 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3721 FILE_OP_ERROR(file
, "claws_fopen");
3722 return COMPOSE_INSERT_READ_ERROR
;
3725 prev_autowrap
= compose
->autowrap
;
3726 compose
->autowrap
= FALSE
;
3728 text
= GTK_TEXT_VIEW(compose
->text
);
3729 buffer
= gtk_text_view_get_buffer(text
);
3730 mark
= gtk_text_buffer_get_insert(buffer
);
3731 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3733 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3734 G_CALLBACK(text_inserted
),
3737 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3739 file_contents
= g_string_new("");
3740 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3743 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3744 str
= g_strdup(buf
);
3746 codeconv_set_strict(TRUE
);
3747 str
= conv_codeset_strdup
3748 (buf
, cur_encoding
, CS_INTERNAL
);
3749 codeconv_set_strict(FALSE
);
3752 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3758 /* strip <CR> if DOS/Windows file,
3759 replace <CR> with <LF> if Macintosh file. */
3762 if (len
> 0 && str
[len
- 1] != '\n') {
3764 if (str
[len
] == '\r') str
[len
] = '\n';
3767 file_contents
= g_string_append(file_contents
, str
);
3771 if (result
== COMPOSE_INSERT_SUCCESS
) {
3772 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3774 compose_changed_cb(NULL
, compose
);
3775 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3776 G_CALLBACK(text_inserted
),
3778 compose
->autowrap
= prev_autowrap
;
3779 if (compose
->autowrap
)
3780 compose_wrap_all(compose
);
3783 g_string_free(file_contents
, TRUE
);
3789 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3790 const gchar
*filename
,
3791 const gchar
*content_type
,
3792 const gchar
*charset
)
3800 GtkListStore
*store
;
3802 gboolean has_binary
= FALSE
;
3804 if (!is_file_exist(file
)) {
3805 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3806 gboolean result
= FALSE
;
3807 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3808 result
= compose_attach_append(
3809 compose
, file_from_uri
,
3810 filename
, content_type
,
3813 g_free(file_from_uri
);
3816 alertpanel_error("File %s doesn't exist or permission denied\n", filename
);
3819 if ((size
= get_file_size(file
)) < 0) {
3820 alertpanel_error("Can't get file size of %s\n", filename
);
3824 /* In batch mode, we allow 0-length files to be attached no questions asked */
3825 if (size
== 0 && !compose
->batch
) {
3826 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3827 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3828 NULL
, _("_Cancel"), NULL
, _("_Attach anyway"),
3829 NULL
, NULL
, ALERTFOCUS_SECOND
, FALSE
, NULL
, ALERT_WARNING
);
3832 if (aval
!= G_ALERTALTERNATE
) {
3836 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3837 alertpanel_error(_("Can't read %s."), filename
);
3842 ainfo
= g_new0(AttachInfo
, 1);
3843 auto_ainfo
= g_auto_pointer_new_with_free
3844 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3845 ainfo
->file
= g_strdup(file
);
3848 ainfo
->content_type
= g_strdup(content_type
);
3849 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3851 MsgFlags flags
= {0, 0};
3853 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3854 ainfo
->encoding
= ENC_7BIT
;
3856 ainfo
->encoding
= ENC_8BIT
;
3858 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3859 if (msginfo
&& msginfo
->subject
)
3860 name
= g_strdup(msginfo
->subject
);
3862 name
= g_path_get_basename(filename
? filename
: file
);
3864 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3866 procmsg_msginfo_free(&msginfo
);
3868 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3869 ainfo
->charset
= g_strdup(charset
);
3870 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3872 ainfo
->encoding
= ENC_BASE64
;
3874 name
= g_path_get_basename(filename
? filename
: file
);
3875 ainfo
->name
= g_strdup(name
);
3879 ainfo
->content_type
= procmime_get_mime_type(file
);
3880 if (!ainfo
->content_type
) {
3881 ainfo
->content_type
=
3882 g_strdup("application/octet-stream");
3883 ainfo
->encoding
= ENC_BASE64
;
3884 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3886 procmime_get_encoding_for_text_file(file
, &has_binary
);
3888 ainfo
->encoding
= ENC_BASE64
;
3889 name
= g_path_get_basename(filename
? filename
: file
);
3890 ainfo
->name
= g_strdup(name
);
3894 if (ainfo
->name
!= NULL
3895 && !strcmp(ainfo
->name
, ".")) {
3896 g_free(ainfo
->name
);
3900 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3901 g_free(ainfo
->content_type
);
3902 ainfo
->content_type
= g_strdup("application/octet-stream");
3903 g_free(ainfo
->charset
);
3904 ainfo
->charset
= NULL
;
3907 ainfo
->size
= (goffset
)size
;
3908 size_text
= to_human_readable((goffset
)size
);
3910 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3911 (GTK_TREE_VIEW(compose
->attach_clist
)));
3913 gtk_list_store_append(store
, &iter
);
3914 gtk_list_store_set(store
, &iter
,
3915 COL_MIMETYPE
, ainfo
->content_type
,
3916 COL_SIZE
, size_text
,
3917 COL_NAME
, ainfo
->name
,
3918 COL_CHARSET
, ainfo
->charset
,
3920 COL_AUTODATA
, auto_ainfo
,
3923 g_auto_pointer_free(auto_ainfo
);
3924 compose_attach_update_label(compose
);
3928 void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3930 compose
->use_signing
= use_signing
;
3931 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3934 void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3936 compose
->use_encryption
= use_encryption
;
3937 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3940 #define NEXT_PART_NOT_CHILD(info) \
3942 node = info->node; \
3943 while (node->children) \
3944 node = g_node_last_child(node); \
3945 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3948 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3952 MimeInfo
*firsttext
= NULL
;
3953 MimeInfo
*encrypted
= NULL
;
3956 const gchar
*partname
= NULL
;
3958 mimeinfo
= procmime_scan_message(msginfo
);
3959 if (!mimeinfo
) return;
3961 if (mimeinfo
->node
->children
== NULL
) {
3962 procmime_mimeinfo_free_all(&mimeinfo
);
3966 /* find first content part */
3967 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3968 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3969 child
= (MimeInfo
*)child
->node
->children
->data
;
3972 if (child
->type
== MIMETYPE_TEXT
) {
3974 debug_print("First text part found\n");
3975 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3976 child
->type
== MIMETYPE_APPLICATION
&&
3977 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3978 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3981 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3982 while (child
!= NULL
) {
3985 if (child
== encrypted
) {
3986 /* skip this part of tree */
3987 NEXT_PART_NOT_CHILD(child
);
3991 if (child
->type
== MIMETYPE_MULTIPART
) {
3992 /* get the actual content */
3993 child
= procmime_mimeinfo_next(child
);
3997 if (child
== firsttext
) {
3998 child
= procmime_mimeinfo_next(child
);
4002 outfile
= procmime_get_tmp_file_name(child
);
4003 if ((err
= procmime_get_part(outfile
, child
)) < 0)
4004 g_warning("can't get the part of multipart message. (%s)", g_strerror(-err
));
4006 gchar
*content_type
;
4008 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
4010 /* if we meet a pgp signature, we don't attach it, but
4011 * we force signing. */
4012 if ((strcmp(content_type
, "application/pgp-signature") &&
4013 strcmp(content_type
, "application/pkcs7-signature") &&
4014 strcmp(content_type
, "application/x-pkcs7-signature"))
4015 || compose
->mode
== COMPOSE_REDIRECT
) {
4016 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
4017 if (partname
== NULL
)
4018 partname
= procmime_mimeinfo_get_parameter(child
, "name");
4019 if (partname
== NULL
)
4021 compose_attach_append(compose
, outfile
,
4022 partname
, content_type
,
4023 procmime_mimeinfo_get_parameter(child
, "charset"));
4025 compose_force_signing(compose
, compose
->account
, NULL
);
4027 g_free(content_type
);
4030 NEXT_PART_NOT_CHILD(child
);
4032 procmime_mimeinfo_free_all(&mimeinfo
);
4035 #undef NEXT_PART_NOT_CHILD
4040 WAIT_FOR_INDENT_CHAR
,
4041 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
4044 /* return indent length, we allow:
4045 indent characters followed by indent characters or spaces/tabs,
4046 alphabets and numbers immediately followed by indent characters,
4047 and the repeating sequences of the above
4048 If quote ends with multiple spaces, only the first one is included. */
4049 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
4050 const GtkTextIter
*start
, gint
*len
)
4052 GtkTextIter iter
= *start
;
4056 IndentState state
= WAIT_FOR_INDENT_CHAR
;
4059 gint alnum_count
= 0;
4060 gint space_count
= 0;
4063 if (prefs_common
.quote_chars
== NULL
) {
4067 while (!gtk_text_iter_ends_line(&iter
)) {
4068 wc
= gtk_text_iter_get_char(&iter
);
4069 if (g_unichar_iswide(wc
))
4071 clen
= g_unichar_to_utf8(wc
, ch
);
4075 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
4076 is_space
= g_unichar_isspace(wc
);
4078 if (state
== WAIT_FOR_INDENT_CHAR
) {
4079 if (!is_indent
&& !g_unichar_isalnum(wc
))
4082 quote_len
+= alnum_count
+ space_count
+ 1;
4083 alnum_count
= space_count
= 0;
4084 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4087 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4088 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4092 else if (is_indent
) {
4093 quote_len
+= alnum_count
+ space_count
+ 1;
4094 alnum_count
= space_count
= 0;
4097 state
= WAIT_FOR_INDENT_CHAR
;
4101 gtk_text_iter_forward_char(&iter
);
4104 if (quote_len
> 0 && space_count
> 0)
4110 if (quote_len
> 0) {
4112 gtk_text_iter_forward_chars(&iter
, quote_len
);
4113 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4119 /* return >0 if the line is itemized */
4120 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4121 const GtkTextIter
*start
)
4123 GtkTextIter iter
= *start
;
4128 if (gtk_text_iter_ends_line(&iter
))
4133 wc
= gtk_text_iter_get_char(&iter
);
4134 if (!g_unichar_isspace(wc
))
4136 gtk_text_iter_forward_char(&iter
);
4137 if (gtk_text_iter_ends_line(&iter
))
4141 clen
= g_unichar_to_utf8(wc
, ch
);
4142 if (!((clen
== 1 && strchr("*-+", ch
[0])) ||
4144 wc
== 0x2022 || /* BULLET */
4145 wc
== 0x2023 || /* TRIANGULAR BULLET */
4146 wc
== 0x2043 || /* HYPHEN BULLET */
4147 wc
== 0x204c || /* BLACK LEFTWARDS BULLET */
4148 wc
== 0x204d || /* BLACK RIGHTWARDS BULLET */
4149 wc
== 0x2219 || /* BULLET OPERATOR */
4150 wc
== 0x25d8 || /* INVERSE BULLET */
4151 wc
== 0x25e6 || /* WHITE BULLET */
4152 wc
== 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4153 wc
== 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4154 wc
== 0x2767 || /* ROTATED FLORAL HEART BULLET */
4155 wc
== 0x29be || /* CIRCLED WHITE BULLET */
4156 wc
== 0x29bf /* CIRCLED BULLET */
4160 gtk_text_iter_forward_char(&iter
);
4161 if (gtk_text_iter_ends_line(&iter
))
4163 wc
= gtk_text_iter_get_char(&iter
);
4164 if (g_unichar_isspace(wc
)) {
4170 /* return the string at the start of the itemization */
4171 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4172 const GtkTextIter
*start
)
4174 GtkTextIter iter
= *start
;
4177 GString
*item_chars
= g_string_new("");
4179 if (gtk_text_iter_ends_line(&iter
)) {
4180 g_string_free(item_chars
, TRUE
);
4186 wc
= gtk_text_iter_get_char(&iter
);
4187 if (!g_unichar_isspace(wc
))
4189 gtk_text_iter_forward_char(&iter
);
4190 if (gtk_text_iter_ends_line(&iter
))
4192 g_string_append_unichar(item_chars
, wc
);
4195 return g_string_free(item_chars
, FALSE
);
4198 /* return the number of spaces at a line's start */
4199 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4200 const GtkTextIter
*start
)
4202 GtkTextIter iter
= *start
;
4205 if (gtk_text_iter_ends_line(&iter
))
4209 wc
= gtk_text_iter_get_char(&iter
);
4210 if (!g_unichar_isspace(wc
))
4213 gtk_text_iter_forward_char(&iter
);
4214 if (gtk_text_iter_ends_line(&iter
))
4218 gtk_text_iter_forward_char(&iter
);
4219 if (gtk_text_iter_ends_line(&iter
))
4224 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4225 const GtkTextIter
*start
,
4226 GtkTextIter
*break_pos
,
4230 GtkTextIter iter
= *start
, line_end
= *start
;
4231 PangoLogAttr
*attrs
;
4238 gboolean can_break
= FALSE
;
4239 gboolean do_break
= FALSE
;
4240 gboolean was_white
= FALSE
;
4241 gboolean prev_dont_break
= FALSE
;
4243 gtk_text_iter_forward_to_line_end(&line_end
);
4244 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4245 len
= g_utf8_strlen(str
, -1);
4249 g_warning("compose_get_line_break_pos: len = 0!");
4253 /* g_print("breaking line: %d: %s (len = %d)\n",
4254 gtk_text_iter_get_line(&iter), str, len); */
4256 attrs
= g_new(PangoLogAttr
, len
+ 1);
4258 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4262 /* skip quote and leading spaces */
4263 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4266 wc
= g_utf8_get_char(p
);
4267 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4269 if (g_unichar_iswide(wc
))
4271 else if (*p
== '\t')
4275 p
= g_utf8_next_char(p
);
4278 for (; *p
!= '\0' && i
< len
; i
++) {
4279 PangoLogAttr
*attr
= attrs
+ i
;
4280 gunichar wc
= g_utf8_get_char(p
);
4283 /* attr->is_line_break will be false for some characters that
4284 * we want to break a line before, like '/' or ':', so we
4285 * also allow breaking on any non-wide character. The
4286 * mentioned pango attribute is still useful to decide on
4287 * line breaks when wide characters are involved. */
4288 if ((!g_unichar_iswide(wc
) || attr
->is_line_break
)
4289 && can_break
&& was_white
&& !prev_dont_break
)
4292 was_white
= attr
->is_white
;
4294 /* don't wrap URI */
4295 if ((uri_len
= get_uri_len(p
)) > 0) {
4297 if (pos
> 0 && col
> max_col
) {
4307 if (g_unichar_iswide(wc
)) {
4309 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4311 } else if (*p
== '\t')
4315 if (pos
> 0 && col
> max_col
) {
4320 if (*p
== '-' || *p
== '/')
4321 prev_dont_break
= TRUE
;
4323 prev_dont_break
= FALSE
;
4325 p
= g_utf8_next_char(p
);
4329 /* debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4334 *break_pos
= *start
;
4335 gtk_text_iter_set_line_offset(break_pos
, pos
);
4340 static gboolean
compose_join_next_line(Compose
*compose
,
4341 GtkTextBuffer
*buffer
,
4343 const gchar
*quote_str
)
4345 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4346 PangoLogAttr attrs
[3];
4348 gchar
*next_quote_str
;
4351 gboolean keep_cursor
= FALSE
;
4353 if (!gtk_text_iter_forward_line(&iter_
) ||
4354 gtk_text_iter_ends_line(&iter_
)) {
4357 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4359 if ((quote_str
|| next_quote_str
) &&
4360 g_strcmp0(quote_str
, next_quote_str
) != 0) {
4361 g_free(next_quote_str
);
4364 g_free(next_quote_str
);
4367 if (quote_len
> 0) {
4368 gtk_text_iter_forward_chars(&end
, quote_len
);
4369 if (gtk_text_iter_ends_line(&end
)) {
4374 /* don't join itemized lines */
4375 if (compose_itemized_length(buffer
, &end
) > 0) {
4379 /* don't join signature separator */
4380 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4383 /* delete quote str */
4385 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4387 /* don't join line breaks put by the user */
4389 gtk_text_iter_backward_char(&cur
);
4390 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4391 gtk_text_iter_forward_char(&cur
);
4395 gtk_text_iter_forward_char(&cur
);
4396 /* delete linebreak and extra spaces */
4397 while (gtk_text_iter_backward_char(&cur
)) {
4398 wc1
= gtk_text_iter_get_char(&cur
);
4399 if (!g_unichar_isspace(wc1
))
4404 while (!gtk_text_iter_ends_line(&cur
)) {
4405 wc1
= gtk_text_iter_get_char(&cur
);
4406 if (!g_unichar_isspace(wc1
))
4408 gtk_text_iter_forward_char(&cur
);
4411 if (!gtk_text_iter_equal(&prev
, &next
)) {
4414 mark
= gtk_text_buffer_get_insert(buffer
);
4415 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4416 if (gtk_text_iter_equal(&prev
, &cur
))
4418 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4422 /* insert space if required */
4423 gtk_text_iter_backward_char(&prev
);
4424 wc1
= gtk_text_iter_get_char(&prev
);
4425 wc2
= gtk_text_iter_get_char(&next
);
4426 gtk_text_iter_forward_char(&next
);
4427 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4428 pango_default_break(str
, -1, NULL
, attrs
, 3);
4429 if (!attrs
[1].is_line_break
||
4430 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4431 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4433 gtk_text_iter_backward_char(&iter_
);
4434 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4443 #define ADD_TXT_POS(bp_, ep_, pti_) \
4444 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4445 last = last->next; \
4446 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4447 last->next = NULL; \
4449 g_warning("alloc error scanning URIs"); \
4452 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4454 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4455 GtkTextBuffer
*buffer
;
4456 GtkTextIter iter
, break_pos
, end_of_line
;
4457 gchar
*quote_str
= NULL
;
4459 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4460 gboolean prev_autowrap
= compose
->autowrap
;
4461 gint startq_offset
= -1, noq_offset
= -1;
4462 gint uri_start
= -1, uri_stop
= -1;
4463 gint nouri_start
= -1, nouri_stop
= -1;
4464 gint num_blocks
= 0;
4465 gint quotelevel
= -1;
4466 gboolean modified
= force
;
4467 gboolean removed
= FALSE
;
4468 gboolean modified_before_remove
= FALSE
;
4470 gboolean start
= TRUE
;
4471 gint itemized_len
= 0, rem_item_len
= 0;
4472 gchar
*itemized_chars
= NULL
;
4473 gboolean item_continuation
= FALSE
;
4478 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4482 compose
->autowrap
= FALSE
;
4484 buffer
= gtk_text_view_get_buffer(text
);
4485 undo_wrapping(compose
->undostruct
, TRUE
);
4490 mark
= gtk_text_buffer_get_insert(buffer
);
4491 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4495 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4496 if (gtk_text_iter_ends_line(&iter
)) {
4497 while (gtk_text_iter_ends_line(&iter
) &&
4498 gtk_text_iter_forward_line(&iter
))
4501 while (gtk_text_iter_backward_line(&iter
)) {
4502 if (gtk_text_iter_ends_line(&iter
)) {
4503 gtk_text_iter_forward_line(&iter
);
4509 /* move to line start */
4510 gtk_text_iter_set_line_offset(&iter
, 0);
4513 itemized_len
= compose_itemized_length(buffer
, &iter
);
4515 if (!itemized_len
) {
4516 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4517 item_continuation
= TRUE
;
4521 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4523 /* go until paragraph end (empty line) */
4524 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4525 gchar
*scanpos
= NULL
;
4526 /* parse table - in order of priority */
4528 const gchar
*needle
; /* token */
4530 /* token search function */
4531 gchar
*(*search
) (const gchar
*haystack
,
4532 const gchar
*needle
);
4533 /* part parsing function */
4534 gboolean (*parse
) (const gchar
*start
,
4535 const gchar
*scanpos
,
4539 /* part to URI function */
4540 gchar
*(*build_uri
) (const gchar
*bp
,
4544 static struct table parser
[] = {
4545 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4546 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4547 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4548 {"ftps://", strcasestr
, get_uri_part
, make_uri_string
},
4549 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4550 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4551 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4552 {"webcal://",strcasestr
, get_uri_part
, make_uri_string
},
4553 {"webcals://",strcasestr
, get_uri_part
, make_uri_string
},
4554 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4555 {"@", strcasestr
, get_email_part
, make_email_string
}
4557 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4558 gint last_index
= PARSE_ELEMS
;
4560 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4564 if (!prev_autowrap
&& num_blocks
== 0) {
4566 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4567 G_CALLBACK(text_inserted
),
4570 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4573 uri_start
= uri_stop
= -1;
4575 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4578 /* debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4579 if (startq_offset
== -1)
4580 startq_offset
= gtk_text_iter_get_offset(&iter
);
4581 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4582 if (quotelevel
> 2) {
4583 /* recycle colors */
4584 if (prefs_common
.recycle_quote_colors
)
4593 if (startq_offset
== -1)
4594 noq_offset
= gtk_text_iter_get_offset(&iter
);
4598 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4601 if (gtk_text_iter_ends_line(&iter
)) {
4603 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4604 prefs_common
.linewrap_len
,
4606 GtkTextIter prev
, next
, cur
;
4607 if (prev_autowrap
!= FALSE
|| force
) {
4608 compose
->automatic_break
= TRUE
;
4610 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4611 compose
->automatic_break
= FALSE
;
4612 if (itemized_len
&& compose
->autoindent
) {
4613 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4614 if (!item_continuation
)
4615 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4617 } else if (quote_str
&& wrap_quote
) {
4618 compose
->automatic_break
= TRUE
;
4620 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4621 compose
->automatic_break
= FALSE
;
4622 if (itemized_len
&& compose
->autoindent
) {
4623 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4624 if (!item_continuation
)
4625 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4629 /* remove trailing spaces */
4631 rem_item_len
= itemized_len
;
4632 while (compose
->autoindent
&& rem_item_len
-- > 0)
4633 gtk_text_iter_backward_char(&cur
);
4634 gtk_text_iter_backward_char(&cur
);
4637 while (!gtk_text_iter_starts_line(&cur
)) {
4640 gtk_text_iter_backward_char(&cur
);
4641 wc
= gtk_text_iter_get_char(&cur
);
4642 if (!g_unichar_isspace(wc
))
4646 if (!gtk_text_iter_equal(&prev
, &next
)) {
4647 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4649 gtk_text_iter_forward_char(&break_pos
);
4653 gtk_text_buffer_insert(buffer
, &break_pos
,
4657 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4659 /* move iter to current line start */
4660 gtk_text_iter_set_line_offset(&iter
, 0);
4667 /* move iter to next line start */
4673 if (!prev_autowrap
&& num_blocks
> 0) {
4675 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4676 G_CALLBACK(text_inserted
),
4680 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4681 gtk_text_iter_forward_char(&end_of_line
);
4683 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4685 nouri_start
= gtk_text_iter_get_offset(&iter
);
4686 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4688 walk_pos
= gtk_text_iter_get_offset(&iter
);
4689 /* FIXME: this looks phony. scanning for anything in the parse table */
4690 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4693 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4695 if (scanpos
== NULL
|| tmp
< scanpos
) {
4704 /* check if URI can be parsed */
4705 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4706 (const gchar
**)&ep
, FALSE
)
4707 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4711 strlen(parser
[last_index
].needle
);
4714 uri_start
= walk_pos
+ (bp
- o_walk
);
4715 uri_stop
= walk_pos
+ (ep
- o_walk
);
4719 gtk_text_iter_forward_line(&iter
);
4722 if (startq_offset
!= -1) {
4723 GtkTextIter startquote
, endquote
;
4724 gtk_text_buffer_get_iter_at_offset(
4725 buffer
, &startquote
, startq_offset
);
4728 switch (quotelevel
) {
4730 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4731 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4732 gtk_text_buffer_apply_tag_by_name(
4733 buffer
, "quote0", &startquote
, &endquote
);
4734 gtk_text_buffer_remove_tag_by_name(
4735 buffer
, "quote1", &startquote
, &endquote
);
4736 gtk_text_buffer_remove_tag_by_name(
4737 buffer
, "quote2", &startquote
, &endquote
);
4742 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4743 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4744 gtk_text_buffer_apply_tag_by_name(
4745 buffer
, "quote1", &startquote
, &endquote
);
4746 gtk_text_buffer_remove_tag_by_name(
4747 buffer
, "quote0", &startquote
, &endquote
);
4748 gtk_text_buffer_remove_tag_by_name(
4749 buffer
, "quote2", &startquote
, &endquote
);
4754 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4755 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4756 gtk_text_buffer_apply_tag_by_name(
4757 buffer
, "quote2", &startquote
, &endquote
);
4758 gtk_text_buffer_remove_tag_by_name(
4759 buffer
, "quote0", &startquote
, &endquote
);
4760 gtk_text_buffer_remove_tag_by_name(
4761 buffer
, "quote1", &startquote
, &endquote
);
4767 } else if (noq_offset
!= -1) {
4768 GtkTextIter startnoquote
, endnoquote
;
4769 gtk_text_buffer_get_iter_at_offset(
4770 buffer
, &startnoquote
, noq_offset
);
4773 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4774 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4775 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4776 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4777 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4778 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4779 gtk_text_buffer_remove_tag_by_name(
4780 buffer
, "quote0", &startnoquote
, &endnoquote
);
4781 gtk_text_buffer_remove_tag_by_name(
4782 buffer
, "quote1", &startnoquote
, &endnoquote
);
4783 gtk_text_buffer_remove_tag_by_name(
4784 buffer
, "quote2", &startnoquote
, &endnoquote
);
4790 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4791 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4792 gtk_text_buffer_get_iter_at_offset(
4793 buffer
, &nouri_start_iter
, nouri_start
);
4794 gtk_text_buffer_get_iter_at_offset(
4795 buffer
, &nouri_end_iter
, nouri_stop
);
4796 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4797 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4798 gtk_text_buffer_remove_tag_by_name(
4799 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4800 modified_before_remove
= modified
;
4805 if (uri_start
>= 0 && uri_stop
> 0) {
4806 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4807 gtk_text_buffer_get_iter_at_offset(
4808 buffer
, &uri_start_iter
, uri_start
);
4809 gtk_text_buffer_get_iter_at_offset(
4810 buffer
, &uri_end_iter
, uri_stop
);
4811 back
= uri_end_iter
;
4812 gtk_text_iter_backward_char(&back
);
4813 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4814 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4815 gtk_text_buffer_apply_tag_by_name(
4816 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4818 if (removed
&& !modified_before_remove
) {
4824 /* debug_print("not modified, out after %d lines\n", lines); */
4828 /* debug_print("modified, out after %d lines\n", lines); */
4830 g_free(itemized_chars
);
4833 undo_wrapping(compose
->undostruct
, FALSE
);
4834 compose
->autowrap
= prev_autowrap
;
4839 void compose_action_cb(void *data
)
4841 Compose
*compose
= (Compose
*)data
;
4842 compose_wrap_all(compose
);
4845 static void compose_wrap_all(Compose
*compose
)
4847 compose_wrap_all_full(compose
, FALSE
);
4850 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4852 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4853 GtkTextBuffer
*buffer
;
4855 gboolean modified
= TRUE
;
4857 buffer
= gtk_text_view_get_buffer(text
);
4859 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4861 undo_wrapping(compose
->undostruct
, TRUE
);
4863 while (!gtk_text_iter_is_end(&iter
) && modified
)
4864 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4866 undo_wrapping(compose
->undostruct
, FALSE
);
4870 static void compose_set_title(Compose
*compose
)
4876 edited
= compose
->modified
? _(" [Edited]") : "";
4878 subject
= gtk_editable_get_chars(
4879 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4881 #ifndef GENERIC_UMPC
4882 if (subject
&& strlen(subject
))
4883 str
= g_strdup_printf(_("%s - Compose message%s"),
4886 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4888 str
= g_strdup(_("Compose message"));
4891 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4897 * compose_current_mail_account:
4899 * Find a current mail account (the currently selected account, or the
4900 * default account, if a news account is currently selected). If a
4901 * mail account cannot be found, display an error message.
4903 * Return value: Mail account, or NULL if not found.
4905 static PrefsAccount
*
4906 compose_current_mail_account(void)
4910 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4913 ac
= account_get_default();
4914 if (!ac
|| ac
->protocol
== A_NNTP
) {
4915 alertpanel_error(_("Account for sending mail is not specified.\n"
4916 "Please select a mail account before sending."));
4923 #define QUOTE_IF_REQUIRED(out, str) \
4925 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4929 len = strlen(str) + 3; \
4930 if ((__tmp = alloca(len)) == NULL) { \
4931 g_warning("can't allocate memory"); \
4932 g_string_free(header, TRUE); \
4935 g_snprintf(__tmp, len, "\"%s\"", str); \
4940 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4941 g_warning("can't allocate memory"); \
4942 g_string_free(header, TRUE); \
4945 strcpy(__tmp, str); \
4951 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4953 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4957 len = strlen(str) + 3; \
4958 if ((__tmp = alloca(len)) == NULL) { \
4959 g_warning("can't allocate memory"); \
4962 g_snprintf(__tmp, len, "\"%s\"", str); \
4967 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4968 g_warning("can't allocate memory"); \
4971 strcpy(__tmp, str); \
4977 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4980 gchar
*from
= NULL
, *header
= NULL
;
4981 ComposeHeaderEntry
*header_entry
;
4984 cm_return_if_fail(account
!= NULL
);
4986 compose
->account
= account
;
4987 if (account
->name
&& *account
->name
) {
4989 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4990 qbuf
= escape_internal_quotes(buf
, '"');
4991 from
= g_strdup_printf("%s <%s>",
4992 qbuf
, account
->address
);
4995 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4997 from
= g_strdup_printf("<%s>",
4999 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
5004 compose_set_title(compose
);
5006 compose_activate_privacy_system(compose
, account
, FALSE
);
5008 if (account
->default_sign
&& privacy_system_can_sign(compose
->privacy_system
) &&
5009 compose
->mode
!= COMPOSE_REDIRECT
)
5010 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
5012 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
5013 if (account
->default_encrypt
&& privacy_system_can_encrypt(compose
->privacy_system
) &&
5014 compose
->mode
!= COMPOSE_REDIRECT
)
5015 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
5017 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
5019 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
5020 undo_block(compose
->undostruct
);
5021 compose_insert_sig(compose
, TRUE
);
5022 undo_unblock(compose
->undostruct
);
5025 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
5026 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
5027 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
5028 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
5030 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
5031 if (account
->protocol
== A_NNTP
) {
5032 if (!strcmp(header
, _("To:")))
5033 combobox_select_by_text(
5034 GTK_COMBO_BOX(header_entry
->combo
),
5037 if (!strcmp(header
, _("Newsgroups:")))
5038 combobox_select_by_text(
5039 GTK_COMBO_BOX(header_entry
->combo
),
5047 /* use account's dict info if set */
5048 if (compose
->gtkaspell
) {
5049 if (account
->enable_default_dictionary
)
5050 gtkaspell_change_dict(compose
->gtkaspell
,
5051 account
->default_dictionary
, FALSE
);
5052 if (account
->enable_default_alt_dictionary
)
5053 gtkaspell_change_alt_dict(compose
->gtkaspell
,
5054 account
->default_alt_dictionary
);
5055 if (account
->enable_default_dictionary
5056 || account
->enable_default_alt_dictionary
)
5057 compose_spell_menu_changed(compose
);
5062 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
5063 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
5064 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
5065 gboolean recipient_found
= FALSE
;
5069 /* free to and newsgroup list */
5070 slist_free_strings_full(compose
->to_list
);
5071 compose
->to_list
= NULL
;
5073 slist_free_strings_full(compose
->newsgroup_list
);
5074 compose
->newsgroup_list
= NULL
;
5076 /* search header entries for to and newsgroup entries */
5077 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5080 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5081 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5084 if (entry
[0] != '\0') {
5085 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
5086 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5087 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
5088 recipient_found
= TRUE
;
5091 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
5092 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5093 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
5094 recipient_found
= TRUE
;
5101 return recipient_found
;
5104 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5106 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5107 gboolean found_other
= FALSE
;
5109 /* search header entries for to and newsgroup entries */
5110 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5113 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5114 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5117 if (strcmp(entry
, compose
->account
->auto_cc
)
5118 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5129 if (compose
->batch
) {
5130 gtk_widget_show_all(compose
->window
);
5132 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5133 prefs_common_translated_header_name("Cc"));
5134 aval
= alertpanel(_("Send"),
5136 NULL
, _("_Cancel"), NULL
, _("_Send"), NULL
, NULL
, ALERTFOCUS_SECOND
);
5138 if (aval
!= G_ALERTALTERNATE
)
5142 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5143 gboolean found_other
= FALSE
;
5145 /* search header entries for to and newsgroup entries */
5146 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5149 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5150 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5153 if (strcmp(entry
, compose
->account
->auto_bcc
)
5154 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5166 if (compose
->batch
) {
5167 gtk_widget_show_all(compose
->window
);
5169 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5170 prefs_common_translated_header_name("Bcc"));
5171 aval
= alertpanel(_("Send"),
5173 NULL
, _("_Cancel"), NULL
, _("_Send"), NULL
, NULL
, ALERTFOCUS_SECOND
);
5175 if (aval
!= G_ALERTALTERNATE
)
5182 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5186 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5187 if (compose
->batch
) {
5188 gtk_widget_show_all(compose
->window
);
5190 alertpanel_error(_("Recipient is not specified."));
5194 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5198 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5199 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5200 if (*str
== '\0' && check_everything
== TRUE
&&
5201 compose
->mode
!= COMPOSE_REDIRECT
) {
5205 message
= g_strdup_printf(_("Subject is empty. %s"),
5206 compose
->sending
?_("Send it anyway?"):
5207 _("Queue it anyway?"));
5209 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5210 NULL
, _("_Cancel"), NULL
, compose
->sending
?_("_Send"):_("_Queue"),
5211 NULL
, NULL
, ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5213 if (aval
& G_ALERTDISABLE
) {
5214 aval
&= ~G_ALERTDISABLE
;
5215 prefs_common
.warn_empty_subj
= FALSE
;
5217 if (aval
!= G_ALERTALTERNATE
)
5222 if (!compose
->batch
&& prefs_common
.warn_sending_many_recipients_num
> 0
5223 && check_everything
== TRUE
) {
5227 /* count To and Cc recipients */
5228 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5232 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5233 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5236 if ((entry
[0] != '\0') &&
5237 (!strcmp(header
, prefs_common_translated_header_name("To:")) ||
5238 !strcmp(header
, prefs_common_translated_header_name("Cc:")))) {
5244 if (cnt
> prefs_common
.warn_sending_many_recipients_num
) {
5248 message
= g_strdup_printf(_("Sending to %d recipients. %s"), cnt
,
5249 compose
->sending
?_("Send it anyway?"):
5250 _("Queue it anyway?"));
5252 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5253 NULL
, _("_Cancel"), NULL
, compose
->sending
?_("_Send"):_("_Queue"),
5254 NULL
, NULL
, ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5256 if (aval
& G_ALERTDISABLE
) {
5257 aval
&= ~G_ALERTDISABLE
;
5258 prefs_common
.warn_sending_many_recipients_num
= 0;
5260 if (aval
!= G_ALERTALTERNATE
)
5265 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5271 static void _display_queue_error(ComposeQueueResult val
)
5274 case COMPOSE_QUEUE_SUCCESS
:
5276 case COMPOSE_QUEUE_ERROR_NO_MSG
:
5277 alertpanel_error(_("Could not queue message."));
5279 case COMPOSE_QUEUE_ERROR_WITH_ERRNO
:
5280 alertpanel_error(_("Could not queue message:\n\n%s."),
5283 case COMPOSE_QUEUE_ERROR_SIGNING_FAILED
:
5284 alertpanel_error(_("Could not queue message for sending:\n\n"
5285 "Signature failed: %s"),
5286 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5288 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
:
5289 alertpanel_error(_("Could not queue message for sending:\n\n"
5290 "Encryption failed: %s"),
5291 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5293 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
:
5294 alertpanel_error(_("Could not queue message for sending:\n\n"
5295 "Charset conversion failed."));
5297 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
:
5298 alertpanel_error(_("Could not queue message for sending:\n\n"
5299 "Couldn't get recipient encryption key."));
5301 case COMPOSE_QUEUE_SIGNING_CANCELLED
:
5302 debug_print("signing cancelled\n");
5305 /* unhandled error */
5306 debug_print("oops, unhandled compose_queue() return value %d\n",
5312 gint
compose_send(Compose
*compose
)
5315 FolderItem
*folder
= NULL
;
5316 ComposeQueueResult val
= COMPOSE_QUEUE_ERROR_NO_MSG
;
5317 gchar
*msgpath
= NULL
;
5318 gboolean discard_window
= FALSE
;
5319 gchar
*errstr
= NULL
;
5320 gchar
*tmsgid
= NULL
;
5321 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5322 gboolean queued_removed
= FALSE
;
5324 if (prefs_common
.send_dialog_invisible
5325 || compose
->batch
== TRUE
)
5326 discard_window
= TRUE
;
5328 compose_allow_user_actions (compose
, FALSE
);
5329 compose
->sending
= TRUE
;
5331 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5332 if (compose
->batch
) {
5333 gtk_widget_show_all(compose
->window
);
5339 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5341 if (val
!= COMPOSE_QUEUE_SUCCESS
) {
5342 if (compose
->batch
) {
5343 gtk_widget_show_all(compose
->window
);
5346 _display_queue_error(val
);
5351 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5352 if (discard_window
) {
5353 compose
->sending
= FALSE
;
5354 compose_close(compose
);
5355 /* No more compose access in the normal codepath
5356 * after this point! */
5361 alertpanel_error(_("The message was queued but could not be "
5362 "sent.\nUse \"Send queued messages\" from "
5363 "the main window to retry."));
5364 if (!discard_window
) {
5371 if (msgpath
== NULL
) {
5372 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5373 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5376 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5377 claws_unlink(msgpath
);
5380 if (!discard_window
) {
5382 if (!queued_removed
)
5383 folder_item_remove_msg(folder
, msgnum
);
5384 folder_item_scan(folder
);
5386 /* make sure we delete that */
5387 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5389 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5390 folder_item_remove_msg(folder
, tmp
->msgnum
);
5391 procmsg_msginfo_free(&tmp
);
5398 if (!queued_removed
)
5399 folder_item_remove_msg(folder
, msgnum
);
5400 folder_item_scan(folder
);
5402 /* make sure we delete that */
5403 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5405 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5406 folder_item_remove_msg(folder
, tmp
->msgnum
);
5407 procmsg_msginfo_free(&tmp
);
5410 if (!discard_window
) {
5411 compose
->sending
= FALSE
;
5412 compose_allow_user_actions (compose
, TRUE
);
5413 compose_close(compose
);
5417 alertpanel_error_log(_("%s\nYou can try to \"Send\" again "
5418 "or queue the message with \"Send later\""), errstr
);
5421 alertpanel_error_log(_("The message was queued but could not be "
5422 "sent.\nUse \"Send queued messages\" from "
5423 "the main window to retry."));
5425 if (!discard_window
) {
5434 toolbar_main_set_sensitive(mainwin
);
5435 main_window_set_menu_sensitive(mainwin
);
5441 compose_allow_user_actions (compose
, TRUE
);
5442 compose
->sending
= FALSE
;
5443 compose
->modified
= TRUE
;
5444 toolbar_main_set_sensitive(mainwin
);
5445 main_window_set_menu_sensitive(mainwin
);
5450 static gboolean
compose_use_attach(Compose
*compose
)
5452 GtkTreeModel
*model
= gtk_tree_view_get_model
5453 (GTK_TREE_VIEW(compose
->attach_clist
));
5454 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5457 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5460 gchar buf
[BUFFSIZE
];
5462 gboolean first_to_address
;
5463 gboolean first_cc_address
;
5465 ComposeHeaderEntry
*headerentry
;
5466 const gchar
*headerentryname
;
5467 const gchar
*cc_hdr
;
5468 const gchar
*to_hdr
;
5469 gboolean err
= FALSE
;
5471 debug_print("Writing redirect header\n");
5473 cc_hdr
= prefs_common_translated_header_name("Cc:");
5474 to_hdr
= prefs_common_translated_header_name("To:");
5476 first_to_address
= TRUE
;
5477 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5478 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5479 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5481 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5482 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5483 Xstrdup_a(str
, entstr
, return -1);
5485 if (str
[0] != '\0') {
5486 compose_convert_header
5487 (compose
, buf
, sizeof(buf
), str
,
5488 strlen("Resent-To") + 2, TRUE
);
5490 if (first_to_address
) {
5491 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5492 first_to_address
= FALSE
;
5494 err
|= (fprintf(fp
, ",") < 0);
5496 err
|= (fprintf(fp
, "%s", buf
) < 0);
5500 if (!first_to_address
) {
5501 err
|= (fprintf(fp
, "\n") < 0);
5504 first_cc_address
= TRUE
;
5505 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5506 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5507 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5509 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5510 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5511 Xstrdup_a(str
, strg
, return -1);
5513 if (str
[0] != '\0') {
5514 compose_convert_header
5515 (compose
, buf
, sizeof(buf
), str
,
5516 strlen("Resent-Cc") + 2, TRUE
);
5518 if (first_cc_address
) {
5519 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5520 first_cc_address
= FALSE
;
5522 err
|= (fprintf(fp
, ",") < 0);
5524 err
|= (fprintf(fp
, "%s", buf
) < 0);
5528 if (!first_cc_address
) {
5529 err
|= (fprintf(fp
, "\n") < 0);
5532 return (err
? -1:0);
5535 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5537 gchar date
[RFC822_DATE_BUFFSIZE
];
5538 gchar buf
[BUFFSIZE
];
5540 const gchar
*entstr
;
5541 /* struct utsname utsbuf; */
5542 gboolean err
= FALSE
;
5544 cm_return_val_if_fail(fp
!= NULL
, -1);
5545 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5546 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5549 if (prefs_common
.hide_timezone
)
5550 get_rfc822_date_hide_tz(date
, sizeof(date
));
5552 get_rfc822_date(date
, sizeof(date
));
5553 err
|= (fprintf(fp
, "Resent-Date: %s\n", date
) < 0);
5556 if (compose
->account
->name
&& *compose
->account
->name
) {
5557 compose_convert_header
5558 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5559 strlen("From: "), TRUE
);
5560 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5561 buf
, compose
->account
->address
) < 0);
5563 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5566 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5567 if (*entstr
!= '\0') {
5568 Xstrdup_a(str
, entstr
, return -1);
5571 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5572 strlen("Subject: "), FALSE
);
5573 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5577 /* Resent-Message-ID */
5578 if (compose
->account
->gen_msgid
) {
5579 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5580 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5582 g_free(compose
->msgid
);
5583 compose
->msgid
= addr
;
5585 compose
->msgid
= NULL
;
5588 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5591 /* separator between header and body */
5592 err
|= (claws_fputs("\n", fp
) == EOF
);
5594 return (err
? -1:0);
5597 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5602 gchar rewrite_buf
[BUFFSIZE
];
5604 gboolean skip
= FALSE
;
5605 gboolean err
= FALSE
;
5606 gchar
*not_included
[]={
5607 "Return-Path:", "Delivered-To:", "Received:",
5608 "Subject:", "X-UIDL:", "AF:",
5609 "NF:", "PS:", "SRH:",
5610 "SFN:", "DSR:", "MID:",
5611 "CFG:", "PT:", "S:",
5612 "RQ:", "SSV:", "NSV:",
5613 "SSH:", "R:", "MAID:",
5614 "NAID:", "RMID:", "FMID:",
5615 "SCF:", "RRCPT:", "NG:",
5616 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5617 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5618 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5619 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5620 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5625 if ((fp
= claws_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5626 FILE_OP_ERROR(compose
->redirect_filename
, "claws_fopen");
5630 while ((ret
= procheader_get_one_field_asis(&buf
, fp
)) != -1) {
5632 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5633 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5634 strlen(not_included
[i
])) == 0) {
5644 if (claws_fputs(buf
, fdest
) == -1) {
5650 if (!prefs_common
.redirect_keep_from
) {
5651 if (g_ascii_strncasecmp(buf
, "From:",
5652 strlen("From:")) == 0) {
5653 err
|= (claws_fputs(" (by way of ", fdest
) == EOF
);
5654 if (compose
->account
->name
5655 && *compose
->account
->name
) {
5656 gchar buffer
[BUFFSIZE
];
5658 compose_convert_header
5659 (compose
, buffer
, sizeof(buffer
),
5660 compose
->account
->name
,
5663 err
|= (fprintf(fdest
, "%s <%s>",
5665 compose
->account
->address
) < 0);
5667 err
|= (fprintf(fdest
, "%s",
5668 compose
->account
->address
) < 0);
5669 err
|= (claws_fputs(")", fdest
) == EOF
);
5675 if (claws_fputs("\n", fdest
) == -1)
5682 if (compose_redirect_write_headers(compose
, fdest
))
5685 while ((len
= claws_fread(rewrite_buf
, sizeof(gchar
), sizeof(rewrite_buf
), fp
)) > 0) {
5686 if (claws_fwrite(rewrite_buf
, sizeof(gchar
), len
, fdest
) != len
)
5700 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5702 GtkTextBuffer
*buffer
;
5703 GtkTextIter start
, end
, tmp
;
5704 gchar
*chars
, *tmp_enc_file
= NULL
, *content
;
5706 const gchar
*out_codeset
;
5707 EncodingType encoding
= ENC_UNKNOWN
;
5708 MimeInfo
*mimemsg
, *mimetext
;
5710 const gchar
*src_codeset
= CS_INTERNAL
;
5711 gchar
*from_addr
= NULL
;
5712 gchar
*from_name
= NULL
;
5715 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5716 attach_parts
= TRUE
;
5718 /* We're sending the message, generate a Message-ID
5720 if (compose
->msgid
== NULL
&&
5721 compose
->account
->gen_msgid
) {
5722 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5726 /* create message MimeInfo */
5727 mimemsg
= procmime_mimeinfo_new();
5728 mimemsg
->type
= MIMETYPE_MESSAGE
;
5729 mimemsg
->subtype
= g_strdup("rfc822");
5730 mimemsg
->content
= MIMECONTENT_MEM
;
5731 mimemsg
->tmp
= TRUE
; /* must free content later */
5732 mimemsg
->data
.mem
= compose_get_header(compose
);
5734 /* Create text part MimeInfo */
5735 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5736 gtk_text_buffer_get_end_iter(buffer
, &end
);
5739 /* We make sure that there is a newline at the end. */
5740 if (action
== COMPOSE_WRITE_FOR_SEND
&& gtk_text_iter_backward_char(&tmp
)) {
5741 chars
= gtk_text_buffer_get_text(buffer
, &tmp
, &end
, FALSE
);
5742 if (*chars
!= '\n') {
5743 gtk_text_buffer_insert(buffer
, &end
, "\n", 1);
5748 /* get all composed text */
5749 gtk_text_buffer_get_start_iter(buffer
, &start
);
5750 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5752 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5754 if (!out_codeset
&& is_ascii_str(chars
)) {
5755 out_codeset
= CS_US_ASCII
;
5756 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5757 is_ascii_str(chars
)) {
5758 out_codeset
= CS_US_ASCII
;
5759 encoding
= ENC_7BIT
;
5763 gchar
*test_conv_global_out
= NULL
;
5764 gchar
*test_conv_reply
= NULL
;
5766 /* automatic mode. be automatic. */
5767 codeconv_set_strict(TRUE
);
5769 out_codeset
= conv_get_outgoing_charset_str();
5771 debug_print("trying to convert to %s\n", out_codeset
);
5772 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5775 if (!test_conv_global_out
&& compose
->orig_charset
5776 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5777 out_codeset
= compose
->orig_charset
;
5778 debug_print("failure; trying to convert to %s\n", out_codeset
);
5779 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5782 if (!test_conv_global_out
&& !test_conv_reply
) {
5784 out_codeset
= CS_INTERNAL
;
5785 debug_print("failure; finally using %s\n", out_codeset
);
5787 g_free(test_conv_global_out
);
5788 g_free(test_conv_reply
);
5789 codeconv_set_strict(FALSE
);
5792 if (encoding
== ENC_UNKNOWN
) {
5793 if (prefs_common
.encoding_method
== CTE_BASE64
)
5794 encoding
= ENC_BASE64
;
5795 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5796 encoding
= ENC_QUOTED_PRINTABLE
;
5797 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5798 encoding
= ENC_8BIT
;
5800 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5803 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5804 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5806 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5807 codeconv_set_strict(TRUE
);
5808 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5809 codeconv_set_strict(FALSE
);
5814 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5815 "to the specified %s charset.\n"
5816 "Send it as %s?"), out_codeset
, src_codeset
);
5817 aval
= alertpanel_full(_("Error"), msg
, NULL
, _("_Cancel"),
5818 NULL
, _("_Send"), NULL
, NULL
, ALERTFOCUS_SECOND
, FALSE
,
5822 if (aval
!= G_ALERTALTERNATE
) {
5824 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
;
5827 out_codeset
= src_codeset
;
5833 out_codeset
= src_codeset
;
5838 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5839 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5840 strstr(buf
, "\nFrom ") != NULL
) {
5841 encoding
= ENC_QUOTED_PRINTABLE
;
5845 mimetext
= procmime_mimeinfo_new();
5846 mimetext
->content
= MIMECONTENT_MEM
;
5847 mimetext
->tmp
= TRUE
; /* must free content later */
5848 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5849 * and free the data, which we need later. */
5850 mimetext
->data
.mem
= g_strdup(buf
);
5851 mimetext
->type
= MIMETYPE_TEXT
;
5852 mimetext
->subtype
= g_strdup("plain");
5853 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5854 g_strdup(out_codeset
));
5856 /* protect trailing spaces when signing message */
5857 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5858 privacy_system_can_sign(compose
->privacy_system
)) {
5859 encoding
= ENC_QUOTED_PRINTABLE
;
5862 debug_print("main text: %" G_GSIZE_FORMAT
" bytes encoded as %s in %d\n",
5863 strlen(buf
), out_codeset
, encoding
);
5865 /* check for line length limit */
5866 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5867 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5868 check_line_length(buf
, 1000, &line
) < 0) {
5871 msg
= g_strdup_printf
5872 (_("Line %d exceeds the line length limit (998 bytes).\n"
5873 "The contents of the message might be broken on the way to the delivery.\n"
5875 "Send it anyway?"), line
+ 1);
5876 aval
= alertpanel(_("Warning"), msg
, NULL
, _("_Cancel"), NULL
, _("_OK"),
5877 NULL
, NULL
, ALERTFOCUS_FIRST
);
5879 if (aval
!= G_ALERTALTERNATE
) {
5881 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5885 if (encoding
!= ENC_UNKNOWN
)
5886 procmime_encode_content(mimetext
, encoding
);
5888 /* append attachment parts */
5889 if (compose_use_attach(compose
) && attach_parts
) {
5890 MimeInfo
*mimempart
;
5891 gchar
*boundary
= NULL
;
5892 mimempart
= procmime_mimeinfo_new();
5893 mimempart
->content
= MIMECONTENT_EMPTY
;
5894 mimempart
->type
= MIMETYPE_MULTIPART
;
5895 mimempart
->subtype
= g_strdup("mixed");
5899 boundary
= generate_mime_boundary(NULL
);
5900 } while (strstr(buf
, boundary
) != NULL
);
5902 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5905 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5907 g_node_append(mimempart
->node
, mimetext
->node
);
5908 g_node_append(mimemsg
->node
, mimempart
->node
);
5910 if (compose_add_attachments(compose
, mimempart
, action
) < 0)
5911 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5913 g_node_append(mimemsg
->node
, mimetext
->node
);
5917 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5918 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5919 /* extract name and address */
5920 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5921 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5922 *(strrchr(from_addr
, '>')) = '\0';
5923 from_name
= g_strdup(spec
);
5924 *(strrchr(from_name
, '<')) = '\0';
5931 /* sign message if sending */
5932 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5933 privacy_system_can_sign(compose
->privacy_system
))
5934 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5935 compose
->account
, from_addr
)) {
5938 if (!privacy_peek_error())
5939 return COMPOSE_QUEUE_SIGNING_CANCELLED
;
5941 return COMPOSE_QUEUE_ERROR_SIGNING_FAILED
;
5946 if (compose
->use_encryption
) {
5947 if (compose
->encdata
!= NULL
&&
5948 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5950 /* First, write an unencrypted copy and save it to outbox, if
5951 * user wants that. */
5952 if (compose
->account
->save_encrypted_as_clear_text
) {
5953 debug_print("saving sent message unencrypted...\n");
5954 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5956 claws_fclose(tmpfp
);
5958 /* fp now points to a file with headers written,
5959 * let's make a copy. */
5961 content
= file_read_stream_to_str(fp
);
5963 str_write_to_file(content
, tmp_enc_file
, TRUE
);
5966 /* Now write the unencrypted body. */
5967 if ((tmpfp
= claws_fopen(tmp_enc_file
, "a")) != NULL
) {
5968 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5969 claws_fclose(tmpfp
);
5971 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5973 outbox
= folder_get_default_outbox();
5975 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5976 claws_unlink(tmp_enc_file
);
5978 g_warning("can't open file '%s'", tmp_enc_file
);
5981 g_warning("couldn't get tempfile");
5984 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5985 debug_print("Couldn't encrypt mime structure: %s.\n",
5986 privacy_get_error());
5988 g_free(tmp_enc_file
);
5989 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
;
5994 g_free(tmp_enc_file
);
5996 procmime_write_mimeinfo(mimemsg
, fp
);
5998 procmime_mimeinfo_free_all(&mimemsg
);
6003 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
6005 GtkTextBuffer
*buffer
;
6006 GtkTextIter start
, end
;
6011 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
6012 FILE_OP_ERROR(file
, "claws_fopen");
6016 /* chmod for security */
6017 if (change_file_mode_rw(fp
, file
) < 0) {
6018 FILE_OP_ERROR(file
, "chmod");
6019 g_warning("can't change file mode");
6022 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
6023 gtk_text_buffer_get_start_iter(buffer
, &start
);
6024 gtk_text_buffer_get_end_iter(buffer
, &end
);
6025 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
6027 chars
= conv_codeset_strdup
6028 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
6037 len
= strlen(chars
);
6038 if (claws_fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
6039 FILE_OP_ERROR(file
, "claws_fwrite");
6048 if (claws_safe_fclose(fp
) == EOF
) {
6049 FILE_OP_ERROR(file
, "claws_fclose");
6056 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
6059 MsgInfo
*msginfo
= compose
->targetinfo
;
6061 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
6062 if (!msginfo
) return -1;
6064 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
6067 item
= msginfo
->folder
;
6068 cm_return_val_if_fail(item
!= NULL
, -1);
6070 if (procmsg_msg_exist(msginfo
) &&
6071 (folder_has_parent_of_type(item
, F_QUEUE
) ||
6072 folder_has_parent_of_type(item
, F_DRAFT
)
6073 || msginfo
== compose
->autosaved_draft
)) {
6074 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
6075 g_warning("can't remove the old message");
6078 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
6085 static void compose_remove_draft(Compose
*compose
)
6088 MsgInfo
*msginfo
= compose
->targetinfo
;
6089 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
6091 if (procmsg_msg_exist(msginfo
)) {
6092 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
6097 ComposeQueueResult
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
6098 gboolean remove_reedit_target
)
6100 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
6103 static gboolean
compose_warn_encryption(Compose
*compose
)
6105 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
6106 AlertValue val
= G_ALERTALTERNATE
;
6108 if (warning
== NULL
)
6111 val
= alertpanel_full(_("Encryption warning"), warning
,
6112 NULL
, _("_Cancel"), NULL
, _("C_ontinue"), NULL
, NULL
,
6113 ALERTFOCUS_SECOND
, TRUE
, NULL
, ALERT_WARNING
);
6114 if (val
& G_ALERTDISABLE
) {
6115 val
&= ~G_ALERTDISABLE
;
6116 if (val
== G_ALERTALTERNATE
)
6117 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
6121 if (val
== G_ALERTALTERNATE
) {
6128 static ComposeQueueResult
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
6129 gchar
**msgpath
, gboolean perform_checks
,
6130 gboolean remove_reedit_target
)
6137 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
6138 gboolean err
= FALSE
;
6140 debug_print("queueing message...\n");
6141 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
6143 if (compose_check_entries(compose
, perform_checks
) == FALSE
) {
6144 if (compose
->batch
) {
6145 gtk_widget_show_all(compose
->window
);
6147 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6150 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
6151 g_warning("can't get recipient list");
6152 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6155 if (compose
->to_list
) {
6156 mailac
= compose
->account
;
6157 if (!mailac
&& cur_account
&& cur_account
->protocol
!= A_NNTP
)
6158 mailac
= cur_account
;
6159 else if (!mailac
&& !(mailac
= compose_current_mail_account())) {
6160 alertpanel_error(_("No account for sending mails available!"));
6161 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6165 if (compose
->newsgroup_list
) {
6166 if (compose
->account
->protocol
== A_NNTP
)
6167 newsac
= compose
->account
;
6169 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6170 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6174 /* write queue header */
6175 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6176 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6177 debug_print("queuing to %s\n", tmp
);
6178 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6179 FILE_OP_ERROR(tmp
, "claws_fopen");
6181 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6184 if (change_file_mode_rw(fp
, tmp
) < 0) {
6185 FILE_OP_ERROR(tmp
, "chmod");
6186 g_warning("can't change file mode");
6189 /* queueing variables */
6190 err
|= (fprintf(fp
, "AF:\n") < 0);
6191 err
|= (fprintf(fp
, "NF:0\n") < 0);
6192 err
|= (fprintf(fp
, "PS:10\n") < 0);
6193 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6194 err
|= (fprintf(fp
, "SFN:\n") < 0);
6195 err
|= (fprintf(fp
, "DSR:\n") < 0);
6197 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6199 err
|= (fprintf(fp
, "MID:\n") < 0);
6200 err
|= (fprintf(fp
, "CFG:\n") < 0);
6201 err
|= (fprintf(fp
, "PT:0\n") < 0);
6202 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6203 err
|= (fprintf(fp
, "RQ:\n") < 0);
6205 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6207 err
|= (fprintf(fp
, "SSV:\n") < 0);
6209 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6211 err
|= (fprintf(fp
, "NSV:\n") < 0);
6212 err
|= (fprintf(fp
, "SSH:\n") < 0);
6213 /* write recipient list */
6214 if (compose
->to_list
) {
6215 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6216 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6218 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6219 err
|= (fprintf(fp
, "\n") < 0);
6221 /* write newsgroup list */
6222 if (compose
->newsgroup_list
) {
6223 err
|= (fprintf(fp
, "NG:") < 0);
6224 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6225 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6226 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6227 err
|= (fprintf(fp
, "\n") < 0);
6231 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6233 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6236 if (compose
->privacy_system
!= NULL
) {
6237 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6238 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6239 if (compose
->use_encryption
) {
6240 if (!compose_warn_encryption(compose
)) {
6244 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6246 if (mailac
&& mailac
->encrypt_to_self
) {
6247 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6248 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6249 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6250 g_slist_free(tmp_list
);
6252 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6254 if (compose
->encdata
!= NULL
) {
6255 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6256 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6257 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6258 compose
->encdata
) < 0);
6259 } /* else we finally dont want to encrypt */
6261 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6262 /* and if encdata was null, it means there's been a problem in
6265 g_warning("failed to write queue message");
6269 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6274 /* Save copy folder */
6275 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6276 gchar
*savefolderid
;
6278 savefolderid
= compose_get_save_to(compose
);
6279 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6280 g_free(savefolderid
);
6282 /* Save copy folder */
6283 if (compose
->return_receipt
) {
6284 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6286 /* Message-ID of message replying to */
6287 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6288 gchar
*folderid
= NULL
;
6290 if (compose
->replyinfo
->folder
)
6291 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6292 if (folderid
== NULL
)
6293 folderid
= g_strdup("NULL");
6295 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6298 /* Message-ID of message forwarding to */
6299 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6300 gchar
*folderid
= NULL
;
6302 if (compose
->fwdinfo
->folder
)
6303 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6304 if (folderid
== NULL
)
6305 folderid
= g_strdup("NULL");
6307 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6311 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6312 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6314 /* end of headers */
6315 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6317 if (compose
->redirect_filename
!= NULL
) {
6318 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6322 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6326 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6334 g_warning("failed to write queue message");
6338 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6340 if (claws_safe_fclose(fp
) == EOF
) {
6341 FILE_OP_ERROR(tmp
, "claws_fclose");
6344 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6347 if (item
&& *item
) {
6350 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6353 g_warning("can't find queue folder");
6356 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6358 folder_item_scan(queue
);
6359 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6360 g_warning("can't queue the message");
6363 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6366 if (msgpath
== NULL
) {
6372 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6373 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6375 procmsg_msginfo_change_flags(mi
,
6376 compose
->targetinfo
->flags
.perm_flags
,
6377 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6380 g_slist_free(mi
->tags
);
6381 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6382 procmsg_msginfo_free(&mi
);
6386 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6387 compose_remove_reedit_target(compose
, FALSE
);
6390 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6395 return COMPOSE_QUEUE_SUCCESS
;
6398 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
, gint action
)
6401 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6406 GError
*error
= NULL
;
6411 gchar
*type
, *subtype
;
6412 GtkTreeModel
*model
;
6415 model
= gtk_tree_view_get_model(tree_view
);
6417 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6420 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6422 if (!is_file_exist(ainfo
->file
)) {
6423 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6424 AlertValue val
= alertpanel_full(_("Warning"), msg
, NULL
,
6425 action
== COMPOSE_WRITE_FOR_STORE
? _("Cancel drafting"): _("Cancel sending"),
6426 NULL
, _("Ignore attachment"), NULL
, NULL
,
6427 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6429 if (val
== G_ALERTDEFAULT
) {
6435 f
= g_file_new_for_path(ainfo
->file
);
6436 fi
= g_file_query_info(f
, "standard::size",
6437 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6438 if (error
!= NULL
) {
6439 g_warning(error
->message
);
6440 g_error_free(error
);
6444 size
= g_file_info_get_size(fi
);
6448 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6450 size
= statbuf
.st_size
;
6453 mimepart
= procmime_mimeinfo_new();
6454 mimepart
->content
= MIMECONTENT_FILE
;
6455 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6456 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6457 mimepart
->offset
= 0;
6458 mimepart
->length
= size
;
6460 type
= g_strdup(ainfo
->content_type
);
6462 if (!strchr(type
, '/')) {
6464 type
= g_strdup("application/octet-stream");
6467 subtype
= strchr(type
, '/') + 1;
6468 *(subtype
- 1) = '\0';
6469 mimepart
->type
= procmime_get_media_type(type
);
6470 mimepart
->subtype
= g_strdup(subtype
);
6473 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6474 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6475 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6476 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6477 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6478 /* Text parts with no name come from multipart/alternative
6479 * forwards. Make sure the recipient won't look at the
6480 * original HTML part by mistake. */
6481 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6482 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6486 g_hash_table_insert(mimepart
->typeparameters
,
6487 g_strdup("charset"), g_strdup(ainfo
->charset
));
6489 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6490 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6491 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6492 g_hash_table_insert(mimepart
->typeparameters
,
6493 g_strdup("name"), g_strdup(ainfo
->name
));
6494 g_hash_table_insert(mimepart
->dispositionparameters
,
6495 g_strdup("filename"), g_strdup(ainfo
->name
));
6496 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6499 if (mimepart
->type
== MIMETYPE_MESSAGE
6500 || mimepart
->type
== MIMETYPE_MULTIPART
)
6501 ainfo
->encoding
= ENC_BINARY
;
6502 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6503 if (ainfo
->encoding
== ENC_7BIT
)
6504 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6505 else if (ainfo
->encoding
== ENC_8BIT
)
6506 ainfo
->encoding
= ENC_BASE64
;
6509 procmime_encode_content(mimepart
, ainfo
->encoding
);
6511 g_node_append(parent
->node
, mimepart
->node
);
6512 } while (gtk_tree_model_iter_next(model
, &iter
));
6517 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6519 GSList
*list
= NULL
, *item
= NULL
;
6520 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6522 list
= address_list_append_with_comments(list
, str
);
6523 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6524 gchar
*spec
= item
->data
;
6525 gchar
*endofname
= strstr(spec
, " <");
6526 if (endofname
!= NULL
) {
6529 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6530 qqname
= escape_internal_quotes(qname
, '"');
6532 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6533 gchar
*addr
= g_strdup(endofname
);
6534 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6535 faddr
= g_strconcat(name
, addr
, NULL
);
6538 debug_print("new auto-quoted address: '%s'\n", faddr
);
6542 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6544 gchar
*tmp
= g_strconcat(result
,
6546 (faddr
!= NULL
)? faddr
: spec
,
6551 if (faddr
!= NULL
) {
6556 slist_free_strings_full(list
);
6561 #define IS_IN_CUSTOM_HEADER(header) \
6562 (compose->account->add_customhdr && \
6563 custom_header_find(compose->account->customhdr_list, header) != NULL)
6565 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6567 /* return the untranslated header name, if header_name is a known
6568 header name, in either its translated or untranslated form, with
6569 or without trailing colon. otherwise, returns header_name. */
6570 gchar
*translated_header_name
;
6571 gchar
*translated_header_name_wcolon
;
6572 const gchar
*untranslated_header_name
;
6573 const gchar
*untranslated_header_name_wcolon
;
6576 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6578 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6579 untranslated_header_name
= HEADERS
[i
].header_name
;
6580 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6582 translated_header_name
= gettext(untranslated_header_name
);
6583 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6585 if (!strcmp(header_name
, untranslated_header_name
) ||
6586 !strcmp(header_name
, translated_header_name
)) {
6587 return untranslated_header_name
;
6589 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6590 !strcmp(header_name
, translated_header_name_wcolon
)) {
6591 return untranslated_header_name_wcolon
;
6595 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6599 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6601 const gchar
*fieldname
,
6602 const gchar
*seperator
)
6604 gchar
*str
, *fieldname_w_colon
;
6605 gboolean add_field
= FALSE
;
6607 ComposeHeaderEntry
*headerentry
;
6608 const gchar
*headerentryname
;
6609 const gchar
*trans_fieldname
;
6612 if (IS_IN_CUSTOM_HEADER(fieldname
))
6615 debug_print("Adding %s-fields\n", fieldname
);
6617 fieldstr
= g_string_sized_new(64);
6619 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6620 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6622 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6623 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6624 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6626 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6627 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6629 str
= compose_quote_list_of_addresses(ustr
);
6631 if (str
!= NULL
&& str
[0] != '\0') {
6633 g_string_append(fieldstr
, seperator
);
6634 g_string_append(fieldstr
, str
);
6643 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6644 compose_convert_header
6645 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6646 strlen(fieldname
) + 2, TRUE
);
6647 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6651 g_free(fieldname_w_colon
);
6652 g_string_free(fieldstr
, TRUE
);
6657 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6659 GString
*sh_header
= g_string_new(" ");
6661 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6663 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6664 ComposeHeaderEntry
*headerentry
;
6667 gchar
*headername_wcolon
;
6668 const gchar
*headername_trans
;
6670 gboolean standard_header
= FALSE
;
6672 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6674 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6676 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6681 if (!strstr(tmp
, ":")) {
6682 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6683 headername
= g_strdup(tmp
);
6685 headername_wcolon
= g_strdup(tmp
);
6686 headername
= g_strdup(strtok(tmp
, ":"));
6690 string
= std_headers
;
6691 while (*string
!= NULL
) {
6692 headername_trans
= prefs_common_translated_header_name(*string
);
6693 if (!strcmp(headername_trans
, headername_wcolon
))
6694 standard_header
= TRUE
;
6697 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6698 g_string_append_printf(sh_header
, "%s ", headername
);
6700 g_free(headername_wcolon
);
6702 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6703 return g_string_free(sh_header
, FALSE
);
6706 static gchar
*compose_get_header(Compose
*compose
)
6708 gchar date
[RFC822_DATE_BUFFSIZE
];
6709 gchar buf
[BUFFSIZE
];
6710 const gchar
*entry_str
;
6714 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6716 gchar
*from_name
= NULL
, *from_address
= NULL
;
6719 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6720 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6722 header
= g_string_sized_new(64);
6725 if (prefs_common
.hide_timezone
)
6726 get_rfc822_date_hide_tz(date
, sizeof(date
));
6728 get_rfc822_date(date
, sizeof(date
));
6729 g_string_append_printf(header
, "Date: %s\n", date
);
6733 if (compose
->account
->name
&& *compose
->account
->name
) {
6735 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6736 tmp
= g_strdup_printf("%s <%s>",
6737 buf
, compose
->account
->address
);
6739 tmp
= g_strdup_printf("%s",
6740 compose
->account
->address
);
6742 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6743 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6745 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6746 from_address
= g_strdup(compose
->account
->address
);
6748 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6749 /* extract name and address */
6750 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6751 from_address
= g_strdup(strrchr(spec
, '<')+1);
6752 *(strrchr(from_address
, '>')) = '\0';
6753 from_name
= g_strdup(spec
);
6754 *(strrchr(from_name
, '<')) = '\0';
6757 from_address
= g_strdup(spec
);
6764 if (from_name
&& *from_name
) {
6766 compose_convert_header
6767 (compose
, buf
, sizeof(buf
), from_name
,
6768 strlen("From: "), TRUE
);
6769 QUOTE_IF_REQUIRED(name
, buf
);
6770 qname
= escape_internal_quotes(name
, '"');
6772 g_string_append_printf(header
, "From: %s <%s>\n",
6773 qname
, from_address
);
6774 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6775 compose
->return_receipt
) {
6776 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6777 strlen("Disposition-Notification-To: "),
6779 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6784 g_string_append_printf(header
, "From: %s\n", from_address
);
6785 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6786 compose
->return_receipt
)
6787 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6791 g_free(from_address
);
6794 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6797 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6800 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6804 * If this account is a NNTP account remove Bcc header from
6805 * message body since it otherwise will be publicly shown
6807 if (compose
->account
->protocol
!= A_NNTP
)
6808 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6811 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6813 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6816 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6817 strlen("Subject: "), FALSE
);
6818 g_string_append_printf(header
, "Subject: %s\n", buf
);
6824 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6825 g_string_append_printf(header
, "Message-ID: <%s>\n",
6829 if (compose
->remove_references
== FALSE
) {
6831 if (compose
->inreplyto
&& compose
->to_list
)
6832 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6835 if (compose
->references
)
6836 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6840 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6843 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6846 if (compose
->account
->organization
&&
6847 strlen(compose
->account
->organization
) &&
6848 !IS_IN_CUSTOM_HEADER("Organization")) {
6849 compose_convert_header(compose
, buf
, sizeof(buf
),
6850 compose
->account
->organization
,
6851 strlen("Organization: "), FALSE
);
6852 g_string_append_printf(header
, "Organization: %s\n", buf
);
6855 /* Program version and system info */
6856 if (compose
->account
->gen_xmailer
&&
6857 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6858 !compose
->newsgroup_list
) {
6859 g_string_append_printf(header
, "X-Mailer: %s (GTK %d.%d.%d; %s)\n",
6861 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6864 if (compose
->account
->gen_xmailer
&&
6865 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6866 g_string_append_printf(header
, "X-Newsreader: %s (GTK %d.%d.%d; %s)\n",
6868 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6872 /* custom headers */
6873 if (compose
->account
->add_customhdr
) {
6876 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6878 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6880 if (custom_header_is_allowed(chdr
->name
)
6881 && chdr
->value
!= NULL
6882 && *(chdr
->value
) != '\0') {
6883 compose_convert_header
6884 (compose
, buf
, sizeof(buf
),
6886 strlen(chdr
->name
) + 2, FALSE
);
6887 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6892 /* Automatic Faces and X-Faces */
6893 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6894 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6896 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6897 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6899 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6900 g_string_append_printf(header
, "Face: %s\n", buf
);
6902 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6903 g_string_append_printf(header
, "Face: %s\n", buf
);
6907 switch (compose
->priority
) {
6908 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6909 "X-Priority: 1 (Highest)\n");
6911 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6912 "X-Priority: 2 (High)\n");
6914 case PRIORITY_NORMAL
: break;
6915 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6916 "X-Priority: 4 (Low)\n");
6918 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6919 "X-Priority: 5 (Lowest)\n");
6921 default: debug_print("compose: priority unknown : %d\n",
6925 /* get special headers */
6926 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6927 ComposeHeaderEntry
*headerentry
;
6930 gchar
*headername_wcolon
;
6931 const gchar
*headername_trans
;
6934 gboolean standard_header
= FALSE
;
6936 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6938 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6940 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6945 if (!strstr(tmp
, ":")) {
6946 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6947 headername
= g_strdup(tmp
);
6949 headername_wcolon
= g_strdup(tmp
);
6950 headername
= g_strdup(strtok(tmp
, ":"));
6954 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6955 Xstrdup_a(headervalue
, entry_str
, {
6957 g_free(headername_wcolon
);
6958 g_string_free(header
, TRUE
);
6961 subst_char(headervalue
, '\r', ' ');
6962 subst_char(headervalue
, '\n', ' ');
6963 g_strstrip(headervalue
);
6964 if (*headervalue
!= '\0') {
6965 string
= std_headers
;
6966 while (*string
!= NULL
&& !standard_header
) {
6967 headername_trans
= prefs_common_translated_header_name(*string
);
6968 /* support mixed translated and untranslated headers */
6969 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6970 standard_header
= TRUE
;
6973 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6974 /* store untranslated header name */
6975 g_string_append_printf(header
, "%s %s\n",
6976 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6980 g_free(headername_wcolon
);
6983 return g_string_free(header
, FALSE
);
6986 #undef IS_IN_CUSTOM_HEADER
6988 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6989 gint header_len
, gboolean addr_field
)
6991 gchar
*tmpstr
= NULL
;
6992 const gchar
*out_codeset
= NULL
;
6994 cm_return_if_fail(src
!= NULL
);
6995 cm_return_if_fail(dest
!= NULL
);
6997 if (len
< 1) return;
6999 tmpstr
= g_strdup(src
);
7001 subst_char(tmpstr
, '\n', ' ');
7002 subst_char(tmpstr
, '\r', ' ');
7005 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
7006 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
7007 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
7012 codeconv_set_strict(TRUE
);
7013 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7014 conv_get_charset_str(compose
->out_encoding
));
7015 codeconv_set_strict(FALSE
);
7017 if (!dest
|| *dest
== '\0') {
7018 gchar
*test_conv_global_out
= NULL
;
7019 gchar
*test_conv_reply
= NULL
;
7021 /* automatic mode. be automatic. */
7022 codeconv_set_strict(TRUE
);
7024 out_codeset
= conv_get_outgoing_charset_str();
7026 debug_print("trying to convert to %s\n", out_codeset
);
7027 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7030 if (!test_conv_global_out
&& compose
->orig_charset
7031 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7032 out_codeset
= compose
->orig_charset
;
7033 debug_print("failure; trying to convert to %s\n", out_codeset
);
7034 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7037 if (!test_conv_global_out
&& !test_conv_reply
) {
7039 out_codeset
= CS_INTERNAL
;
7040 debug_print("finally using %s\n", out_codeset
);
7042 g_free(test_conv_global_out
);
7043 g_free(test_conv_reply
);
7044 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7046 codeconv_set_strict(FALSE
);
7051 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7055 cm_return_if_fail(user_data
!= NULL
);
7057 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7058 g_strstrip(address
);
7059 if (*address
!= '\0') {
7060 gchar
*name
= procheader_get_fromname(address
);
7061 extract_address(address
);
7062 #ifndef USE_ALT_ADDRBOOK
7063 addressbook_add_contact(name
, address
, NULL
, NULL
);
7065 debug_print("%s: %s\n", name
, address
);
7066 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7067 debug_print( "addressbook_add_contact - added\n" );
7074 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7076 GtkWidget
*menuitem
;
7079 cm_return_if_fail(menu
!= NULL
);
7080 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7082 menuitem
= gtk_separator_menu_item_new();
7083 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7084 gtk_widget_show(menuitem
);
7086 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7087 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7089 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7090 g_strstrip(address
);
7091 if (*address
== '\0') {
7092 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7095 g_signal_connect(G_OBJECT(menuitem
), "activate",
7096 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7097 gtk_widget_show(menuitem
);
7100 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7103 if (strcmp(header
, "")) {
7104 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7108 void compose_add_extra_header_entries(GtkListStore
*model
)
7112 gchar buf
[BUFFSIZE
];
7115 if (extra_headers
== NULL
) {
7116 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7117 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7118 debug_print("extra headers file not found\n");
7119 goto extra_headers_done
;
7121 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7122 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7123 while (lastc
>= 0 && buf
[lastc
] != ':')
7124 buf
[lastc
--] = '\0';
7125 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7126 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7127 if (custom_header_is_allowed(buf
)) {
7129 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7132 g_message("disallowed extra header line: %s\n", buf
);
7136 g_message("invalid extra header line: %s\n", buf
);
7142 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7143 extra_headers
= g_slist_reverse(extra_headers
);
7145 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7149 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7151 LdapServer
*server
= (LdapServer
*)data
;
7152 gboolean
*enable
= (gboolean
*)user_data
;
7154 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7155 server
->searchFlag
= *enable
;
7159 static void compose_create_header_entry(Compose
*compose
)
7161 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7168 const gchar
*header
= NULL
;
7169 ComposeHeaderEntry
*headerentry
;
7170 gboolean standard_header
= FALSE
;
7171 GtkListStore
*model
;
7174 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7176 /* Combo box model */
7177 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7178 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7180 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7182 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7184 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7185 COMPOSE_NEWSGROUPS
);
7186 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7188 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7189 COMPOSE_FOLLOWUPTO
);
7190 compose_add_extra_header_entries(model
);
7193 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7194 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7195 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7196 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7197 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7198 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7199 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7200 G_CALLBACK(compose_grab_focus_cb
), compose
);
7201 gtk_widget_show(combo
);
7203 gtk_grid_attach(GTK_GRID(compose
->header_table
), combo
, 0, compose
->header_nextrow
,
7205 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7206 const gchar
*last_header_entry
= gtk_entry_get_text(
7207 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7209 while (*string
!= NULL
) {
7210 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7211 standard_header
= TRUE
;
7214 if (standard_header
)
7215 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7217 if (!compose
->header_last
|| !standard_header
) {
7218 switch(compose
->account
->protocol
) {
7220 header
= prefs_common_translated_header_name("Newsgroups:");
7223 header
= prefs_common_translated_header_name("To:");
7228 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7230 gtk_editable_set_editable(
7231 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7232 prefs_common
.type_any_header
);
7234 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7235 G_CALLBACK(compose_grab_focus_cb
), compose
);
7237 /* Entry field with cleanup button */
7238 button
= gtk_button_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU
);
7239 gtk_widget_show(button
);
7240 CLAWS_SET_TIP(button
,
7241 _("Delete entry contents"));
7242 entry
= gtk_entry_new();
7243 gtk_widget_show(entry
);
7244 CLAWS_SET_TIP(entry
,
7245 _("Use <tab> to autocomplete from addressbook"));
7246 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
7247 gtk_widget_show(hbox
);
7248 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7249 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7250 gtk_grid_attach(GTK_GRID(compose
->header_table
), hbox
, 1, compose
->header_nextrow
,
7252 gtk_widget_set_hexpand(hbox
, TRUE
);
7253 gtk_widget_set_halign(hbox
, GTK_ALIGN_FILL
);
7255 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7256 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7258 g_signal_connect(G_OBJECT(entry
), "changed",
7259 G_CALLBACK(compose_headerentry_changed_cb
),
7261 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7262 G_CALLBACK(compose_grab_focus_cb
), compose
);
7264 g_signal_connect(G_OBJECT(button
), "clicked",
7265 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7269 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7270 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7271 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7272 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7273 G_CALLBACK(compose_header_drag_received_cb
),
7275 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7276 G_CALLBACK(compose_drag_drop
),
7278 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7279 G_CALLBACK(compose_entry_popup_extend
),
7283 #ifndef PASSWORD_CRYPTO_OLD
7284 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7285 if (pwd_servers
!= NULL
&& primary_passphrase() == NULL
) {
7286 gboolean enable
= FALSE
;
7287 debug_print("Primary passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7288 /* Temporarily disable password-protected LDAP servers,
7289 * because user did not provide a primary passphrase.
7290 * We can safely enable searchFlag on all servers in this list
7291 * later, since addrindex_get_password_protected_ldap_servers()
7292 * includes servers which have it enabled initially. */
7293 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7294 compose
->passworded_ldap_servers
= pwd_servers
;
7296 #endif /* PASSWORD_CRYPTO_OLD */
7297 #endif /* USE_LDAP */
7299 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7301 headerentry
->compose
= compose
;
7302 headerentry
->combo
= combo
;
7303 headerentry
->entry
= entry
;
7304 headerentry
->button
= button
;
7305 headerentry
->hbox
= hbox
;
7306 headerentry
->headernum
= compose
->header_nextrow
;
7307 headerentry
->type
= PREF_NONE
;
7309 compose
->header_nextrow
++;
7310 compose
->header_last
= headerentry
;
7311 compose
->header_list
=
7312 g_slist_append(compose
->header_list
,
7316 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7317 gchar
*text
, ComposePrefType pref_type
)
7319 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7320 gchar
*tmp
= g_strdup(text
), *email
;
7321 gboolean replyto_hdr
;
7323 replyto_hdr
= (!strcasecmp(header
,
7324 prefs_common_translated_header_name("Reply-To:")) ||
7326 prefs_common_translated_header_name("Followup-To:")) ||
7328 prefs_common_translated_header_name("In-Reply-To:")));
7330 extract_address(tmp
);
7331 email
= g_utf8_strdown(tmp
, -1);
7333 if (replyto_hdr
== FALSE
&&
7334 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7336 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7337 header
, text
, (gint
) pref_type
);
7343 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7344 gtk_entry_set_text(GTK_ENTRY(
7345 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7347 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7348 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7349 last_header
->type
= pref_type
;
7351 if (replyto_hdr
== FALSE
)
7352 g_hash_table_insert(compose
->email_hashtable
, email
,
7353 GUINT_TO_POINTER(1));
7360 static void compose_destroy_headerentry(Compose
*compose
,
7361 ComposeHeaderEntry
*headerentry
)
7363 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7366 extract_address(text
);
7367 email
= g_utf8_strdown(text
, -1);
7368 g_hash_table_remove(compose
->email_hashtable
, email
);
7372 gtk_widget_destroy(headerentry
->combo
);
7373 gtk_widget_destroy(headerentry
->entry
);
7374 gtk_widget_destroy(headerentry
->button
);
7375 gtk_widget_destroy(headerentry
->hbox
);
7376 g_free(headerentry
);
7379 static void compose_remove_header_entries(Compose
*compose
)
7382 for (list
= compose
->header_list
; list
; list
= list
->next
)
7383 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7385 compose
->header_last
= NULL
;
7386 g_slist_free(compose
->header_list
);
7387 compose
->header_list
= NULL
;
7388 compose
->header_nextrow
= 1;
7389 compose_create_header_entry(compose
);
7392 static GtkWidget
*compose_create_header(Compose
*compose
)
7394 GtkWidget
*from_optmenu_hbox
;
7395 GtkWidget
*header_table_main
;
7396 GtkWidget
*header_scrolledwin
;
7397 GtkWidget
*header_table
;
7399 /* parent with account selection and from header */
7400 header_table_main
= gtk_grid_new();
7401 gtk_widget_show(header_table_main
);
7402 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7404 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7405 gtk_grid_attach(GTK_GRID(header_table_main
),from_optmenu_hbox
, 0, 0, 1, 1);
7406 gtk_widget_set_hexpand(from_optmenu_hbox
, TRUE
);
7407 gtk_widget_set_halign(from_optmenu_hbox
, GTK_ALIGN_FILL
);
7409 /* child with header labels and entries */
7410 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7411 gtk_widget_show(header_scrolledwin
);
7412 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7414 header_table
= gtk_grid_new();
7415 gtk_widget_show(header_table
);
7416 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7417 gtk_container_add(GTK_CONTAINER(header_scrolledwin
), header_table
);
7418 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7419 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7420 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7422 gtk_grid_attach(GTK_GRID(header_table_main
), header_scrolledwin
, 0, 1, 1, 1);
7423 gtk_widget_set_vexpand(header_scrolledwin
, TRUE
);
7424 gtk_widget_set_valign(header_scrolledwin
, GTK_ALIGN_FILL
);
7426 compose
->header_table
= header_table
;
7427 compose
->header_list
= NULL
;
7428 compose
->header_nextrow
= 0;
7430 compose_create_header_entry(compose
);
7432 compose
->table
= NULL
;
7434 return header_table_main
;
7437 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7439 Compose
*compose
= (Compose
*)data
;
7440 GdkEventButton event
;
7443 event
.time
= gtk_get_current_event_time();
7445 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7448 static GtkWidget
*compose_create_attach(Compose
*compose
)
7450 GtkWidget
*attach_scrwin
;
7451 GtkWidget
*attach_clist
;
7453 GtkListStore
*store
;
7454 GtkCellRenderer
*renderer
;
7455 GtkTreeViewColumn
*column
;
7456 GtkTreeSelection
*selection
;
7458 /* attachment list */
7459 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7460 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7461 GTK_POLICY_AUTOMATIC
,
7462 GTK_POLICY_AUTOMATIC
);
7463 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7465 store
= gtk_list_store_new(N_ATTACH_COLS
,
7471 G_TYPE_AUTO_POINTER
,
7473 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7474 (GTK_TREE_MODEL(store
)));
7475 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7476 g_object_unref(store
);
7478 renderer
= gtk_cell_renderer_text_new();
7479 column
= gtk_tree_view_column_new_with_attributes
7480 (_("Mime type"), renderer
, "text",
7481 COL_MIMETYPE
, NULL
);
7482 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7484 renderer
= gtk_cell_renderer_text_new();
7485 column
= gtk_tree_view_column_new_with_attributes
7486 (_("Size"), renderer
, "text",
7488 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7490 renderer
= gtk_cell_renderer_text_new();
7491 column
= gtk_tree_view_column_new_with_attributes
7492 (_("Name"), renderer
, "text",
7494 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7496 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7497 prefs_common
.use_stripes_everywhere
);
7498 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7499 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7501 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7502 G_CALLBACK(attach_selected
), compose
);
7503 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7504 G_CALLBACK(attach_button_pressed
), compose
);
7505 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7506 G_CALLBACK(popup_attach_button_pressed
), compose
);
7507 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7508 G_CALLBACK(attach_key_pressed
), compose
);
7511 gtk_drag_dest_set(attach_clist
,
7512 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7513 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7514 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7515 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7516 G_CALLBACK(compose_attach_drag_received_cb
),
7518 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7519 G_CALLBACK(compose_drag_drop
),
7522 compose
->attach_scrwin
= attach_scrwin
;
7523 compose
->attach_clist
= attach_clist
;
7525 return attach_scrwin
;
7528 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7530 static GtkWidget
*compose_create_others(Compose
*compose
)
7533 GtkWidget
*savemsg_checkbtn
;
7534 GtkWidget
*savemsg_combo
;
7535 GtkWidget
*savemsg_select
;
7538 gchar
*folderidentifier
;
7540 /* Table for settings */
7541 table
= gtk_grid_new();
7542 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7543 gtk_widget_show(table
);
7544 gtk_grid_set_row_spacing(GTK_GRID(table
), VSPACING_NARROW
);
7547 /* Save Message to folder */
7548 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7549 gtk_widget_show(savemsg_checkbtn
);
7550 gtk_grid_attach(GTK_GRID(table
), savemsg_checkbtn
, 0, rowcount
, 1, 1);
7551 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7552 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7555 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7556 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7557 compose
->savemsg_combo
= savemsg_combo
;
7558 gtk_widget_show(savemsg_combo
);
7560 if (prefs_common
.compose_save_to_history
)
7561 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7562 prefs_common
.compose_save_to_history
);
7563 gtk_grid_attach(GTK_GRID(table
), savemsg_combo
, 1, rowcount
, 1, 1);
7564 gtk_widget_set_hexpand(savemsg_combo
, TRUE
);
7565 gtk_widget_set_halign(savemsg_combo
, GTK_ALIGN_FILL
);
7566 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7567 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7568 G_CALLBACK(compose_grab_focus_cb
), compose
);
7569 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7570 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7571 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7573 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7574 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7575 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7576 (compose
->account
, F_OUTBOX
));
7577 compose_set_save_to(compose
, folderidentifier
);
7578 g_free(folderidentifier
);
7581 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7582 gtk_widget_show(savemsg_select
);
7583 gtk_grid_attach(GTK_GRID(table
), savemsg_select
, 2, rowcount
, 1, 1);
7584 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7585 G_CALLBACK(compose_savemsg_select_cb
),
7591 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7596 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7597 _("Select folder to save message to"));
7600 path
= folder_item_get_identifier(dest
);
7602 compose_set_save_to(compose
, path
);
7606 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7607 GdkAtom clip
, GtkTextIter
*insert_place
);
7610 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7614 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7616 if (event
->button
== 3) {
7618 GtkTextIter sel_start
, sel_end
;
7619 gboolean stuff_selected
;
7621 /* move the cursor to allow GtkAspell to check the word
7622 * under the mouse */
7623 if (event
->x
&& event
->y
) {
7624 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7625 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7627 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7630 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7631 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7634 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7636 &sel_start
, &sel_end
);
7638 gtk_text_buffer_place_cursor (buffer
, &iter
);
7639 /* reselect stuff */
7641 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7642 gtk_text_buffer_select_range(buffer
,
7643 &sel_start
, &sel_end
);
7645 return FALSE
; /* pass the event so that the right-click goes through */
7648 if (event
->button
== 2) {
7653 /* get the middle-click position to paste at the correct place */
7654 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7655 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7657 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7660 entry_paste_clipboard(compose
, text
,
7661 prefs_common
.linewrap_pastes
,
7662 GDK_SELECTION_PRIMARY
, &iter
);
7670 static void compose_spell_menu_changed(void *data
)
7672 Compose
*compose
= (Compose
*)data
;
7674 GtkWidget
*menuitem
;
7675 GtkWidget
*parent_item
;
7676 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7679 if (compose
->gtkaspell
== NULL
)
7682 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7683 "/Menu/Spelling/Options");
7685 /* setting the submenu removes /Spelling/Options from the factory
7686 * so we need to save it */
7688 if (parent_item
== NULL
) {
7689 parent_item
= compose
->aspell_options_menu
;
7690 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7692 compose
->aspell_options_menu
= parent_item
;
7694 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7696 spell_menu
= g_slist_reverse(spell_menu
);
7697 for (items
= spell_menu
;
7698 items
; items
= items
->next
) {
7699 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7700 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7701 gtk_widget_show(GTK_WIDGET(menuitem
));
7703 g_slist_free(spell_menu
);
7705 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7706 gtk_widget_show(parent_item
);
7709 static void compose_dict_changed(void *data
)
7711 Compose
*compose
= (Compose
*) data
;
7713 if(!compose
->gtkaspell
)
7715 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7718 gtkaspell_highlight_all(compose
->gtkaspell
);
7719 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7723 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7725 Compose
*compose
= (Compose
*)data
;
7726 GdkEventButton event
;
7729 event
.time
= gtk_get_current_event_time();
7733 return text_clicked(compose
->text
, &event
, compose
);
7736 static gboolean compose_force_window_origin
= TRUE
;
7737 static Compose
*compose_create(PrefsAccount
*account
,
7746 GtkWidget
*handlebox
;
7748 GtkWidget
*notebook
;
7750 GtkWidget
*attach_hbox
;
7751 GtkWidget
*attach_lab1
;
7752 GtkWidget
*attach_lab2
;
7757 GtkWidget
*subject_hbox
;
7758 GtkWidget
*subject_frame
;
7759 GtkWidget
*subject_entry
;
7763 GtkWidget
*edit_vbox
;
7764 GtkWidget
*ruler_hbox
;
7766 GtkWidget
*scrolledwin
;
7768 GtkTextBuffer
*buffer
;
7769 GtkClipboard
*clipboard
;
7771 UndoMain
*undostruct
;
7773 GtkWidget
*popupmenu
;
7774 GtkWidget
*tmpl_menu
;
7775 GtkActionGroup
*action_group
= NULL
;
7778 GtkAspell
* gtkaspell
= NULL
;
7781 static GdkGeometry geometry
;
7782 GdkRectangle workarea
= {0};
7784 cm_return_val_if_fail(account
!= NULL
, NULL
);
7786 default_header_bgcolor
= prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7787 default_header_color
= prefs_common
.color
[COL_DEFAULT_HEADER
],
7789 debug_print("Creating compose window...\n");
7790 compose
= g_new0(Compose
, 1);
7792 compose
->batch
= batch
;
7793 compose
->account
= account
;
7794 compose
->folder
= folder
;
7796 g_mutex_init(&compose
->mutex
);
7797 compose
->set_cursor_pos
= -1;
7799 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7801 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7802 gtk_window_set_default_size(GTK_WINDOW(window
), prefs_common
.compose_width
,
7803 prefs_common
.compose_height
);
7805 gdk_monitor_get_workarea(gdk_display_get_primary_monitor(gdk_display_get_default()),
7808 if (!geometry
.max_width
) {
7809 geometry
.max_width
= workarea
.width
;
7810 geometry
.max_height
= workarea
.height
;
7813 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7814 &geometry
, GDK_HINT_MAX_SIZE
);
7815 if (!geometry
.min_width
) {
7816 geometry
.min_width
= 600;
7817 geometry
.min_height
= 440;
7819 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7820 &geometry
, GDK_HINT_MIN_SIZE
);
7822 #ifndef GENERIC_UMPC
7823 if (compose_force_window_origin
)
7824 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7825 prefs_common
.compose_y
);
7827 g_signal_connect(G_OBJECT(window
), "delete_event",
7828 G_CALLBACK(compose_delete_cb
), compose
);
7829 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7830 gtk_widget_realize(window
);
7832 gtkut_widget_set_composer_icon(window
);
7834 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
7835 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7837 compose
->ui_manager
= gtk_ui_manager_new();
7838 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7839 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7840 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7841 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7842 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7843 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7844 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7845 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7846 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7847 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7849 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7851 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7852 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7854 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7856 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7861 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7862 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7863 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7865 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7866 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7867 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7868 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7869 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7870 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7873 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7877 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7878 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7881 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7882 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7884 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7885 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7886 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7887 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7891 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7893 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7894 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7895 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7896 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7897 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7898 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7899 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7900 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7901 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7902 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7903 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7904 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7905 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7907 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7909 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7912 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7913 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7915 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7916 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7921 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7922 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7923 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7924 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7925 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7929 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7930 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7933 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7935 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7936 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7937 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7938 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7942 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7943 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7944 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7945 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7946 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7947 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7948 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7950 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7951 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7952 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7953 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7954 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7956 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7958 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7959 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7960 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7961 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7962 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7964 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7965 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
)
7966 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
)
7967 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7969 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7971 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7972 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
)
7973 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
)
7975 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7977 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7978 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
)
7979 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7981 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7982 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
)
7983 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7985 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7987 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7988 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
)
7989 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7990 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7991 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7992 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7994 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7995 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
)
7996 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
)
7997 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
8000 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
8001 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
8002 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
8003 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
8004 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
8005 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
8007 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
8008 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
8009 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
)
8011 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
8012 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
8013 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
8017 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
8018 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
8019 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
8020 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8021 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8022 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8025 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8027 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8028 gtk_widget_show_all(menubar
);
8030 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8031 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8033 handlebox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8034 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8036 gtk_widget_realize(handlebox
);
8037 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8040 vbox2
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 2);
8041 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8042 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8045 notebook
= gtk_notebook_new();
8046 gtk_widget_show(notebook
);
8048 /* header labels and entries */
8049 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8050 compose_create_header(compose
),
8051 gtk_label_new_with_mnemonic(_("Hea_der")));
8052 /* attachment list */
8053 attach_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8054 gtk_widget_show(attach_hbox
);
8056 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8057 gtk_widget_show(attach_lab1
);
8058 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8060 attach_lab2
= gtk_label_new("");
8061 gtk_widget_show(attach_lab2
);
8062 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8064 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8065 compose_create_attach(compose
),
8068 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8069 compose_create_others(compose
),
8070 gtk_label_new_with_mnemonic(_("Othe_rs")));
8073 subject_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8074 gtk_widget_show(subject_hbox
);
8076 subject_frame
= gtk_frame_new(NULL
);
8077 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8078 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8079 gtk_widget_show(subject_frame
);
8081 subject
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, HSPACING_NARROW
);
8082 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8083 gtk_widget_show(subject
);
8085 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8086 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8087 gtk_widget_show(label
);
8090 subject_entry
= claws_spell_entry_new();
8092 subject_entry
= gtk_entry_new();
8094 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8095 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8096 G_CALLBACK(compose_grab_focus_cb
), compose
);
8097 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8098 gtk_widget_show(subject_entry
);
8099 compose
->subject_entry
= subject_entry
;
8100 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8102 edit_vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
8104 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8107 ruler_hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
8108 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8110 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8111 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8112 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8116 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8117 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8118 GTK_POLICY_AUTOMATIC
,
8119 GTK_POLICY_AUTOMATIC
);
8120 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8122 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8124 text
= gtk_text_view_new();
8125 if (prefs_common
.show_compose_margin
) {
8126 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8127 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8129 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8130 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8131 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8132 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8133 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8135 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8136 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8137 G_CALLBACK(compose_edit_size_alloc
),
8139 g_signal_connect(G_OBJECT(buffer
), "changed",
8140 G_CALLBACK(compose_changed_cb
), compose
);
8141 g_signal_connect(G_OBJECT(text
), "grab_focus",
8142 G_CALLBACK(compose_grab_focus_cb
), compose
);
8143 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8144 G_CALLBACK(text_inserted
), compose
);
8145 g_signal_connect(G_OBJECT(text
), "button_press_event",
8146 G_CALLBACK(text_clicked
), compose
);
8147 g_signal_connect(G_OBJECT(text
), "popup-menu",
8148 G_CALLBACK(compose_popup_menu
), compose
);
8149 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8150 G_CALLBACK(compose_changed_cb
), compose
);
8151 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8152 G_CALLBACK(compose_subject_entry_activated
), compose
);
8155 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8156 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8157 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8158 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8159 G_CALLBACK(compose_insert_drag_received_cb
),
8161 g_signal_connect(G_OBJECT(text
), "drag-drop",
8162 G_CALLBACK(compose_drag_drop
),
8164 g_signal_connect(G_OBJECT(text
), "key-press-event",
8165 G_CALLBACK(completion_set_focus_to_subject
),
8167 gtk_widget_show_all(vbox
);
8169 /* pane between attach clist and text */
8170 paned
= gtk_paned_new(GTK_ORIENTATION_VERTICAL
);
8171 gtk_box_pack_start(GTK_BOX(vbox2
), paned
, TRUE
, TRUE
, 0);
8172 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8173 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8174 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8175 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8176 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8178 gtk_widget_show_all(paned
);
8181 if (prefs_common
.textfont
) {
8182 PangoFontDescription
*font_desc
;
8184 font_desc
= pango_font_description_from_string
8185 (prefs_common
.textfont
);
8187 gtk_widget_override_font(text
, font_desc
);
8188 pango_font_description_free(font_desc
);
8192 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8193 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8194 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8195 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8196 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8197 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8198 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8199 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8201 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8203 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8204 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8205 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8207 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8209 undostruct
= undo_init(text
);
8210 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8213 address_completion_start(window
);
8215 compose
->window
= window
;
8216 compose
->vbox
= vbox
;
8217 compose
->menubar
= menubar
;
8218 compose
->handlebox
= handlebox
;
8220 compose
->vbox2
= vbox2
;
8222 compose
->paned
= paned
;
8224 compose
->attach_label
= attach_lab2
;
8226 compose
->notebook
= notebook
;
8227 compose
->edit_vbox
= edit_vbox
;
8228 compose
->ruler_hbox
= ruler_hbox
;
8229 compose
->ruler
= ruler
;
8230 compose
->scrolledwin
= scrolledwin
;
8231 compose
->text
= text
;
8233 compose
->focused_editable
= NULL
;
8235 compose
->popupmenu
= popupmenu
;
8237 compose
->tmpl_menu
= tmpl_menu
;
8239 compose
->mode
= mode
;
8240 compose
->rmode
= mode
;
8242 compose
->targetinfo
= NULL
;
8243 compose
->replyinfo
= NULL
;
8244 compose
->fwdinfo
= NULL
;
8246 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8247 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8249 compose
->replyto
= NULL
;
8251 compose
->bcc
= NULL
;
8252 compose
->followup_to
= NULL
;
8254 compose
->ml_post
= NULL
;
8256 compose
->inreplyto
= NULL
;
8257 compose
->references
= NULL
;
8258 compose
->msgid
= NULL
;
8259 compose
->boundary
= NULL
;
8261 compose
->autowrap
= prefs_common
.autowrap
;
8262 compose
->autoindent
= prefs_common
.auto_indent
;
8263 compose
->use_signing
= FALSE
;
8264 compose
->use_encryption
= FALSE
;
8265 compose
->privacy_system
= NULL
;
8266 compose
->encdata
= NULL
;
8268 compose
->modified
= FALSE
;
8270 compose
->return_receipt
= FALSE
;
8272 compose
->to_list
= NULL
;
8273 compose
->newsgroup_list
= NULL
;
8275 compose
->undostruct
= undostruct
;
8277 compose
->sig_str
= NULL
;
8279 compose
->exteditor_file
= NULL
;
8280 compose
->exteditor_pid
= INVALID_PID
;
8281 compose
->exteditor_tag
= -1;
8282 compose
->exteditor_socket
= NULL
;
8283 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8285 compose
->folder_update_callback_id
=
8286 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8287 compose_update_folder_hook
,
8288 (gpointer
) compose
);
8291 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8292 if (mode
!= COMPOSE_REDIRECT
) {
8293 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8294 strcmp(prefs_common
.dictionary
, "")) {
8295 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8296 prefs_common
.alt_dictionary
,
8297 conv_get_locale_charset_str(),
8298 prefs_common
.color
[COL_MISSPELLED
],
8299 prefs_common
.check_while_typing
,
8300 prefs_common
.recheck_when_changing_dict
,
8301 prefs_common
.use_alternate
,
8302 prefs_common
.use_both_dicts
,
8303 GTK_TEXT_VIEW(text
),
8304 GTK_WINDOW(compose
->window
),
8305 compose_dict_changed
,
8306 compose_spell_menu_changed
,
8309 alertpanel_error(_("Spell checker could not "
8311 gtkaspell_checkers_strerror());
8312 gtkaspell_checkers_reset_error();
8314 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8318 compose
->gtkaspell
= gtkaspell
;
8319 compose_spell_menu_changed(compose
);
8320 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8323 compose_select_account(compose
, account
, TRUE
);
8325 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8326 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8328 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8329 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8331 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8332 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8334 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8335 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8337 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8338 if (account
->protocol
!= A_NNTP
)
8339 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8340 prefs_common_translated_header_name("To:"));
8342 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8343 prefs_common_translated_header_name("Newsgroups:"));
8345 #ifndef USE_ALT_ADDRBOOK
8346 addressbook_set_target_compose(compose
);
8348 if (mode
!= COMPOSE_REDIRECT
)
8349 compose_set_template_menu(compose
);
8351 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8354 compose_list
= g_list_append(compose_list
, compose
);
8356 if (!prefs_common
.show_ruler
)
8357 gtk_widget_hide(ruler_hbox
);
8359 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8362 compose
->priority
= PRIORITY_NORMAL
;
8363 compose_update_priority_menu_item(compose
);
8365 compose_set_out_encoding(compose
);
8368 compose_update_actions_menu(compose
);
8370 /* Privacy Systems menu */
8371 compose_update_privacy_systems_menu(compose
);
8372 compose_activate_privacy_system(compose
, account
, TRUE
);
8374 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8376 gtk_widget_realize(window
);
8378 gtk_widget_show(window
);
8384 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8389 GtkWidget
*optmenubox
;
8390 GtkWidget
*fromlabel
;
8393 GtkWidget
*from_name
= NULL
;
8395 gint num
= 0, def_menu
= 0;
8397 accounts
= account_get_list();
8398 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8400 optmenubox
= gtk_event_box_new();
8401 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8402 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8404 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 4);
8405 from_name
= gtk_entry_new();
8407 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8408 G_CALLBACK(compose_grab_focus_cb
), compose
);
8409 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8410 G_CALLBACK(from_name_activate_cb
), optmenu
);
8412 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8413 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8414 gchar
*name
, *from
= NULL
;
8416 if (ac
== compose
->account
) def_menu
= num
;
8418 name
= g_markup_printf_escaped("<i>%s</i>",
8421 if (ac
== compose
->account
) {
8422 if (ac
->name
&& *ac
->name
) {
8424 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8425 from
= g_strdup_printf("%s <%s>",
8427 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8429 from
= g_strdup_printf("%s",
8431 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8433 if (cur_account
!= compose
->account
) {
8436 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_bgcolor
, color
);
8437 gtk_widget_modify_base(
8438 GTK_WIDGET(from_name
),
8439 GTK_STATE_NORMAL
, &color
);
8440 GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_color
, color
);
8441 gtk_widget_modify_text(
8442 GTK_WIDGET(from_name
),
8443 GTK_STATE_NORMAL
, &color
);
8446 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8451 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8453 g_signal_connect(G_OBJECT(optmenu
), "changed",
8454 G_CALLBACK(account_activated
),
8456 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8457 G_CALLBACK(compose_entry_popup_extend
),
8460 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8461 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8463 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8464 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8465 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8467 CLAWS_SET_TIP(optmenubox
,
8468 _("Account to use for this email"));
8469 CLAWS_SET_TIP(from_name
,
8470 _("Sender address to be used"));
8472 compose
->account_combo
= optmenu
;
8473 compose
->from_name
= from_name
;
8478 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8480 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8481 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8482 Compose
*compose
= (Compose
*) data
;
8484 compose
->priority
= value
;
8488 static void compose_reply_change_mode(Compose
*compose
,
8491 gboolean was_modified
= compose
->modified
;
8493 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8495 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8497 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8499 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8501 if (action
== COMPOSE_REPLY_TO_ALL
)
8503 if (action
== COMPOSE_REPLY_TO_SENDER
)
8505 if (action
== COMPOSE_REPLY_TO_LIST
)
8508 compose_remove_header_entries(compose
);
8509 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8510 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8511 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8513 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8514 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8516 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8517 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8518 compose_show_first_last_header(compose
, TRUE
);
8519 compose
->modified
= was_modified
;
8520 compose_set_title(compose
);
8523 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8525 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8526 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8527 Compose
*compose
= (Compose
*) data
;
8530 compose_reply_change_mode(compose
, value
);
8533 static void compose_update_priority_menu_item(Compose
* compose
)
8535 GtkWidget
*menuitem
= NULL
;
8536 switch (compose
->priority
) {
8537 case PRIORITY_HIGHEST
:
8538 menuitem
= gtk_ui_manager_get_widget
8539 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8542 menuitem
= gtk_ui_manager_get_widget
8543 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8545 case PRIORITY_NORMAL
:
8546 menuitem
= gtk_ui_manager_get_widget
8547 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8550 menuitem
= gtk_ui_manager_get_widget
8551 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8553 case PRIORITY_LOWEST
:
8554 menuitem
= gtk_ui_manager_get_widget
8555 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8558 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8561 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8563 Compose
*compose
= (Compose
*) data
;
8565 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8567 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8569 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8572 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8573 g_free(compose
->privacy_system
);
8574 compose
->privacy_system
= NULL
;
8575 g_free(compose
->encdata
);
8576 compose
->encdata
= NULL
;
8577 if (systemid
!= NULL
) {
8578 compose
->privacy_system
= g_strdup(systemid
);
8580 can_sign
= privacy_system_can_sign(systemid
);
8581 can_encrypt
= privacy_system_can_encrypt(systemid
);
8584 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8586 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8587 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8588 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8589 gtk_widget_set_sensitive(
8590 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8592 gtk_toggle_tool_button_set_active(
8593 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8594 can_sign
? compose
->use_signing
: FALSE
);
8596 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8597 gtk_widget_set_sensitive(
8598 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8600 gtk_toggle_tool_button_set_active(
8601 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8602 can_encrypt
? compose
->use_encryption
: FALSE
);
8606 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8608 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8609 GtkWidget
*menuitem
= NULL
;
8610 GList
*children
, *amenu
;
8611 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8612 gboolean found
= FALSE
;
8614 if (compose
->privacy_system
!= NULL
) {
8616 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8617 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8618 cm_return_if_fail(menuitem
!= NULL
);
8620 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8623 while (amenu
!= NULL
) {
8624 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8625 if (systemid
!= NULL
) {
8626 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8627 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8628 menuitem
= GTK_WIDGET(amenu
->data
);
8630 can_sign
= privacy_system_can_sign(systemid
);
8631 can_encrypt
= privacy_system_can_encrypt(systemid
);
8635 } else if (strlen(compose
->privacy_system
) == 0 &&
8636 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8637 menuitem
= GTK_WIDGET(amenu
->data
);
8640 can_encrypt
= FALSE
;
8645 amenu
= amenu
->next
;
8647 g_list_free(children
);
8648 if (menuitem
!= NULL
)
8649 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8651 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8652 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8653 "will not be able to sign or encrypt this message."),
8654 compose
->privacy_system
);
8658 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8659 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8660 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8661 gtk_widget_set_sensitive(
8662 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8665 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8666 gtk_widget_set_sensitive(
8667 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8672 static void compose_set_out_encoding(Compose
*compose
)
8674 CharSet out_encoding
;
8675 const gchar
*branch
= NULL
;
8676 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8678 switch(out_encoding
) {
8679 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8680 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8681 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8682 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8683 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8684 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8685 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8686 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8687 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8688 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8689 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8690 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8691 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8692 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8693 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8694 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8695 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8696 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8697 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8698 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8699 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8700 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8701 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8702 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8703 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8704 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8705 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8706 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8707 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8708 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8709 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8710 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8711 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8712 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8714 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8717 static void compose_set_template_menu(Compose
*compose
)
8719 GSList
*tmpl_list
, *cur
;
8723 tmpl_list
= template_get_config();
8725 menu
= gtk_menu_new();
8727 gtk_menu_set_accel_group (GTK_MENU (menu
),
8728 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8729 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8730 Template
*tmpl
= (Template
*)cur
->data
;
8731 gchar
*accel_path
= NULL
;
8732 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8733 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8734 g_signal_connect(G_OBJECT(item
), "activate",
8735 G_CALLBACK(compose_template_activate_cb
),
8737 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8738 gtk_widget_show(item
);
8739 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8740 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8744 gtk_widget_show(menu
);
8745 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8748 void compose_update_actions_menu(Compose
*compose
)
8750 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8753 static void compose_update_privacy_systems_menu(Compose
*compose
)
8755 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8756 GSList
*systems
, *cur
;
8758 GtkWidget
*system_none
;
8760 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8761 GtkWidget
*privacy_menu
= gtk_menu_new();
8763 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8764 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8766 g_signal_connect(G_OBJECT(system_none
), "activate",
8767 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8769 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8770 gtk_widget_show(system_none
);
8772 systems
= privacy_get_system_ids();
8773 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8774 gchar
*systemid
= cur
->data
;
8776 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8777 widget
= gtk_radio_menu_item_new_with_label(group
,
8778 privacy_system_get_name(systemid
));
8779 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8780 g_strdup(systemid
), g_free
);
8781 g_signal_connect(G_OBJECT(widget
), "activate",
8782 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8784 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8785 gtk_widget_show(widget
);
8788 g_slist_free(systems
);
8789 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8790 gtk_widget_show_all(privacy_menu
);
8791 gtk_widget_show_all(privacy_menuitem
);
8794 void compose_reflect_prefs_all(void)
8799 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8800 compose
= (Compose
*)cur
->data
;
8801 compose_set_template_menu(compose
);
8805 void compose_reflect_prefs_pixmap_theme(void)
8810 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8811 compose
= (Compose
*)cur
->data
;
8812 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8816 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8818 const gchar
*qmark
= NULL
;
8820 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8822 switch (compose
->mode
) {
8823 /* use forward-specific quote char */
8824 case COMPOSE_FORWARD
:
8825 case COMPOSE_FORWARD_AS_ATTACH
:
8826 case COMPOSE_FORWARD_INLINE
:
8827 if (compose
->folder
&& compose
->folder
->prefs
&&
8828 compose
->folder
->prefs
->forward_with_format
)
8829 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8830 else if (compose
->account
->forward_with_format
)
8831 qmark
= compose
->account
->forward_quotemark
;
8833 qmark
= prefs_common
.fw_quotemark
;
8836 /* use reply-specific quote char in all other modes */
8838 if (compose
->folder
&& compose
->folder
->prefs
&&
8839 compose
->folder
->prefs
->reply_with_format
)
8840 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8841 else if (compose
->account
->reply_with_format
)
8842 qmark
= compose
->account
->reply_quotemark
;
8844 qmark
= prefs_common
.quotemark
;
8848 if (qmark
== NULL
|| *qmark
== '\0')
8854 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8858 GtkTextBuffer
*buffer
;
8862 gchar
*parsed_str
= NULL
;
8863 gint cursor_pos
= 0;
8864 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8867 /* process the body */
8869 text
= GTK_TEXT_VIEW(compose
->text
);
8870 buffer
= gtk_text_view_get_buffer(text
);
8873 qmark
= compose_quote_char_from_context(compose
);
8875 if (compose
->replyinfo
!= NULL
) {
8878 gtk_text_buffer_set_text(buffer
, "", -1);
8879 mark
= gtk_text_buffer_get_insert(buffer
);
8880 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8882 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8883 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8885 } else if (compose
->fwdinfo
!= NULL
) {
8888 gtk_text_buffer_set_text(buffer
, "", -1);
8889 mark
= gtk_text_buffer_get_insert(buffer
);
8890 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8892 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8893 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8896 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8898 GtkTextIter start
, end
;
8901 gtk_text_buffer_get_start_iter(buffer
, &start
);
8902 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8903 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8905 /* clear the buffer now */
8907 gtk_text_buffer_set_text(buffer
, "", -1);
8909 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8910 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8911 procmsg_msginfo_free( &dummyinfo
);
8917 gtk_text_buffer_set_text(buffer
, "", -1);
8918 mark
= gtk_text_buffer_get_insert(buffer
);
8919 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8922 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8923 compose_insert_sig(compose
, FALSE
);
8925 if (replace
&& parsed_str
) {
8926 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8927 gtk_text_buffer_place_cursor(buffer
, &iter
);
8931 cursor_pos
= quote_fmt_get_cursor_pos();
8932 compose
->set_cursor_pos
= cursor_pos
;
8933 if (cursor_pos
== -1)
8935 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8936 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8937 gtk_text_buffer_place_cursor(buffer
, &iter
);
8940 /* process the other fields */
8942 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8943 compose_template_apply_fields(compose
, tmpl
);
8944 quote_fmt_reset_vartable();
8945 quote_fmtlex_destroy();
8947 compose_changed_cb(NULL
, compose
);
8950 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8951 gtkaspell_highlight_all(compose
->gtkaspell
);
8955 static void compose_template_apply_fields_error(const gchar
*header
)
8960 tr
= g_strdup(C_("'%s' stands for a header name",
8961 "Template '%s' format error."));
8962 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8963 alertpanel_error("%s", text
);
8969 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8971 MsgInfo
* dummyinfo
= NULL
;
8972 MsgInfo
*msginfo
= NULL
;
8975 if (compose
->replyinfo
!= NULL
)
8976 msginfo
= compose
->replyinfo
;
8977 else if (compose
->fwdinfo
!= NULL
)
8978 msginfo
= compose
->fwdinfo
;
8980 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8981 msginfo
= dummyinfo
;
8984 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8986 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8987 compose
->gtkaspell
);
8989 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8991 quote_fmt_scan_string(tmpl
->from
);
8994 buf
= quote_fmt_get_buffer();
8996 compose_template_apply_fields_error("From");
8998 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
9001 quote_fmt_reset_vartable();
9002 quote_fmtlex_destroy();
9005 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
9007 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9008 compose
->gtkaspell
);
9010 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9012 quote_fmt_scan_string(tmpl
->to
);
9015 buf
= quote_fmt_get_buffer();
9017 compose_template_apply_fields_error("To");
9019 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9022 quote_fmt_reset_vartable();
9023 quote_fmtlex_destroy();
9026 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9028 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9029 compose
->gtkaspell
);
9031 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9033 quote_fmt_scan_string(tmpl
->cc
);
9036 buf
= quote_fmt_get_buffer();
9038 compose_template_apply_fields_error("Cc");
9040 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9043 quote_fmt_reset_vartable();
9044 quote_fmtlex_destroy();
9047 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9049 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9050 compose
->gtkaspell
);
9052 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9054 quote_fmt_scan_string(tmpl
->bcc
);
9057 buf
= quote_fmt_get_buffer();
9059 compose_template_apply_fields_error("Bcc");
9061 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9064 quote_fmt_reset_vartable();
9065 quote_fmtlex_destroy();
9068 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9070 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9071 compose
->gtkaspell
);
9073 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9075 quote_fmt_scan_string(tmpl
->replyto
);
9078 buf
= quote_fmt_get_buffer();
9080 compose_template_apply_fields_error("Reply-To");
9082 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9085 quote_fmt_reset_vartable();
9086 quote_fmtlex_destroy();
9089 /* process the subject */
9090 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9092 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9093 compose
->gtkaspell
);
9095 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9097 quote_fmt_scan_string(tmpl
->subject
);
9100 buf
= quote_fmt_get_buffer();
9102 compose_template_apply_fields_error("Subject");
9104 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9107 quote_fmt_reset_vartable();
9108 quote_fmtlex_destroy();
9111 procmsg_msginfo_free( &dummyinfo
);
9114 static void compose_destroy(Compose
*compose
)
9116 GtkAllocation allocation
;
9117 GtkTextBuffer
*buffer
;
9118 GtkClipboard
*clipboard
;
9120 compose_list
= g_list_remove(compose_list
, compose
);
9123 gboolean enable
= TRUE
;
9124 g_slist_foreach(compose
->passworded_ldap_servers
,
9125 _ldap_srv_func
, &enable
);
9126 g_slist_free(compose
->passworded_ldap_servers
);
9129 if (compose
->updating
) {
9130 debug_print("danger, not destroying anything now\n");
9131 compose
->deferred_destroy
= TRUE
;
9135 /* NOTE: address_completion_end() does nothing with the window
9136 * however this may change. */
9137 address_completion_end(compose
->window
);
9139 slist_free_strings_full(compose
->to_list
);
9140 slist_free_strings_full(compose
->newsgroup_list
);
9141 slist_free_strings_full(compose
->header_list
);
9143 slist_free_strings_full(extra_headers
);
9144 extra_headers
= NULL
;
9146 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9148 g_hash_table_destroy(compose
->email_hashtable
);
9150 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9151 compose
->folder_update_callback_id
);
9153 procmsg_msginfo_free(&(compose
->targetinfo
));
9154 procmsg_msginfo_free(&(compose
->replyinfo
));
9155 procmsg_msginfo_free(&(compose
->fwdinfo
));
9157 g_free(compose
->replyto
);
9158 g_free(compose
->cc
);
9159 g_free(compose
->bcc
);
9160 g_free(compose
->newsgroups
);
9161 g_free(compose
->followup_to
);
9163 g_free(compose
->ml_post
);
9165 g_free(compose
->inreplyto
);
9166 g_free(compose
->references
);
9167 g_free(compose
->msgid
);
9168 g_free(compose
->boundary
);
9170 g_free(compose
->redirect_filename
);
9171 if (compose
->undostruct
)
9172 undo_destroy(compose
->undostruct
);
9174 g_free(compose
->sig_str
);
9176 g_free(compose
->exteditor_file
);
9178 g_free(compose
->orig_charset
);
9180 g_free(compose
->privacy_system
);
9181 g_free(compose
->encdata
);
9183 #ifndef USE_ALT_ADDRBOOK
9184 if (addressbook_get_target_compose() == compose
)
9185 addressbook_set_target_compose(NULL
);
9188 if (compose
->gtkaspell
) {
9189 gtkaspell_delete(compose
->gtkaspell
);
9190 compose
->gtkaspell
= NULL
;
9194 if (!compose
->batch
) {
9195 gtk_window_get_size(GTK_WINDOW(compose
->window
),
9196 &allocation
.width
, &allocation
.height
);
9197 prefs_common
.compose_width
= allocation
.width
;
9198 prefs_common
.compose_height
= allocation
.height
;
9201 if (!gtk_widget_get_parent(compose
->paned
))
9202 gtk_widget_destroy(compose
->paned
);
9203 gtk_widget_destroy(compose
->popupmenu
);
9205 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9206 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9207 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9209 message_search_close(compose
);
9210 gtk_widget_destroy(compose
->window
);
9211 toolbar_destroy(compose
->toolbar
);
9212 g_free(compose
->toolbar
);
9213 g_mutex_clear(&compose
->mutex
);
9217 static void compose_attach_info_free(AttachInfo
*ainfo
)
9219 g_free(ainfo
->file
);
9220 g_free(ainfo
->content_type
);
9221 g_free(ainfo
->name
);
9222 g_free(ainfo
->charset
);
9226 static void compose_attach_update_label(Compose
*compose
)
9231 GtkTreeModel
*model
;
9235 if (compose
== NULL
)
9238 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9239 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9240 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9244 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9245 total_size
= ainfo
->size
;
9246 while(gtk_tree_model_iter_next(model
, &iter
)) {
9247 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9248 total_size
+= ainfo
->size
;
9251 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9252 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9256 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9258 Compose
*compose
= (Compose
*)data
;
9259 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9260 GtkTreeSelection
*selection
;
9262 GtkTreeModel
*model
;
9264 selection
= gtk_tree_view_get_selection(tree_view
);
9265 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9266 cm_return_if_fail(sel
);
9268 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9269 GtkTreePath
*path
= cur
->data
;
9270 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9273 gtk_tree_path_free(path
);
9276 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9277 GtkTreeRowReference
*ref
= cur
->data
;
9278 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9281 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9282 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9284 gtk_tree_path_free(path
);
9285 gtk_tree_row_reference_free(ref
);
9289 compose_attach_update_label(compose
);
9292 static struct _AttachProperty
9295 GtkWidget
*mimetype_entry
;
9296 GtkWidget
*encoding_optmenu
;
9297 GtkWidget
*path_entry
;
9298 GtkWidget
*filename_entry
;
9300 GtkWidget
*cancel_btn
;
9303 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9305 gtk_tree_path_free((GtkTreePath
*)ptr
);
9308 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9310 Compose
*compose
= (Compose
*)data
;
9311 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9313 GtkComboBox
*optmenu
;
9314 GtkTreeSelection
*selection
;
9316 GtkTreeModel
*model
;
9319 static gboolean cancelled
;
9321 /* only if one selected */
9322 selection
= gtk_tree_view_get_selection(tree_view
);
9323 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9326 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9327 cm_return_if_fail(sel
);
9329 path
= (GtkTreePath
*) sel
->data
;
9330 gtk_tree_model_get_iter(model
, &iter
, path
);
9331 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9334 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9340 if (!attach_prop
.window
)
9341 compose_attach_property_create(&cancelled
);
9342 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9343 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9344 gtk_widget_show(attach_prop
.window
);
9345 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9346 GTK_WINDOW(compose
->window
));
9348 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9349 if (ainfo
->encoding
== ENC_UNKNOWN
)
9350 combobox_select_by_data(optmenu
, ENC_BASE64
);
9352 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9354 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9355 ainfo
->content_type
? ainfo
->content_type
: "");
9356 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9357 ainfo
->file
? ainfo
->file
: "");
9358 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9359 ainfo
->name
? ainfo
->name
: "");
9362 const gchar
*entry_text
;
9364 gchar
*cnttype
= NULL
;
9371 gtk_widget_hide(attach_prop
.window
);
9372 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9377 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9378 if (*entry_text
!= '\0') {
9381 text
= g_strstrip(g_strdup(entry_text
));
9382 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9383 cnttype
= g_strdup(text
);
9386 alertpanel_error(_("Invalid MIME type."));
9392 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9394 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9395 if (*entry_text
!= '\0') {
9396 if (is_file_exist(entry_text
) &&
9397 (size
= get_file_size(entry_text
)) > 0)
9398 file
= g_strdup(entry_text
);
9401 (_("File doesn't exist or is empty."));
9407 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9408 if (*entry_text
!= '\0') {
9409 g_free(ainfo
->name
);
9410 ainfo
->name
= g_strdup(entry_text
);
9414 g_free(ainfo
->content_type
);
9415 ainfo
->content_type
= cnttype
;
9418 g_free(ainfo
->file
);
9422 ainfo
->size
= (goffset
)size
;
9424 /* update tree store */
9425 text
= to_human_readable(ainfo
->size
);
9426 gtk_tree_model_get_iter(model
, &iter
, path
);
9427 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9428 COL_MIMETYPE
, ainfo
->content_type
,
9430 COL_NAME
, ainfo
->name
,
9431 COL_CHARSET
, ainfo
->charset
,
9437 gtk_tree_path_free(path
);
9440 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9442 label = gtk_label_new(str); \
9443 gtk_grid_attach(GTK_GRID(table), label, 0, top, 1, 1); \
9444 gtk_label_set_xalign(GTK_LABEL(label), 0.0); \
9445 entry = gtk_entry_new(); \
9446 gtk_grid_attach(GTK_GRID(table), entry, 1, top, 1, 1); \
9449 static void compose_attach_property_create(gboolean
*cancelled
)
9455 GtkWidget
*mimetype_entry
;
9458 GtkListStore
*optmenu_menu
;
9459 GtkWidget
*path_entry
;
9460 GtkWidget
*filename_entry
;
9463 GtkWidget
*cancel_btn
;
9464 GList
*mime_type_list
, *strlist
;
9467 debug_print("Creating attach_property window...\n");
9469 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9470 gtk_widget_set_size_request(window
, 480, -1);
9471 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9472 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9473 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9474 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9475 g_signal_connect(G_OBJECT(window
), "delete_event",
9476 G_CALLBACK(attach_property_delete_event
),
9478 g_signal_connect(G_OBJECT(window
), "key_press_event",
9479 G_CALLBACK(attach_property_key_pressed
),
9482 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8);
9483 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9485 table
= gtk_grid_new();
9486 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9487 gtk_grid_set_row_spacing(GTK_GRID(table
), 8);
9488 gtk_grid_set_column_spacing(GTK_GRID(table
), 8);
9490 label
= gtk_label_new(_("MIME type"));
9491 gtk_grid_attach(GTK_GRID(table
), label
, 0, 0, 1, 1);
9492 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
9493 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9494 gtk_grid_attach(GTK_GRID(table
), mimetype_entry
, 1, 0, 1, 1);
9495 gtk_widget_set_hexpand(mimetype_entry
, TRUE
);
9496 gtk_widget_set_halign(mimetype_entry
, GTK_ALIGN_FILL
);
9498 /* stuff with list */
9499 mime_type_list
= procmime_get_mime_type_list();
9501 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9502 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9505 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9507 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9510 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9511 (GCompareFunc
)g_strcmp0
);
9514 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9515 mime_type_list
= mime_type_list
->next
) {
9516 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9517 g_free(mime_type_list
->data
);
9519 g_list_free(strlist
);
9520 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9521 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9523 label
= gtk_label_new(_("Encoding"));
9524 gtk_grid_attach(GTK_GRID(table
), label
, 0, 1, 1, 1);
9525 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
9527 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
9528 gtk_grid_attach(GTK_GRID(table
), hbox
, 1, 1, 1, 1);
9529 gtk_widget_set_hexpand(hbox
, TRUE
);
9530 gtk_widget_set_halign(hbox
, GTK_ALIGN_FILL
);
9532 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9533 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9535 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9536 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9537 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9538 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9539 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9541 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9543 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9544 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9546 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, NULL
, _("_Cancel"),
9547 &ok_btn
, NULL
, _("_OK"),
9549 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9550 gtk_widget_grab_default(ok_btn
);
9552 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9553 G_CALLBACK(attach_property_ok
),
9555 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9556 G_CALLBACK(attach_property_cancel
),
9559 gtk_widget_show_all(vbox
);
9561 attach_prop
.window
= window
;
9562 attach_prop
.mimetype_entry
= mimetype_entry
;
9563 attach_prop
.encoding_optmenu
= optmenu
;
9564 attach_prop
.path_entry
= path_entry
;
9565 attach_prop
.filename_entry
= filename_entry
;
9566 attach_prop
.ok_btn
= ok_btn
;
9567 attach_prop
.cancel_btn
= cancel_btn
;
9570 #undef SET_LABEL_AND_ENTRY
9572 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9578 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9584 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9585 gboolean
*cancelled
)
9593 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9595 gboolean
*cancelled
)
9597 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9601 if (event
&& (event
->keyval
== GDK_KEY_KP_Enter
||
9602 event
->keyval
== GDK_KEY_Return
)) {
9610 static gboolean
compose_can_autosave(Compose
*compose
)
9612 if (compose
->privacy_system
&& compose
->use_encryption
)
9613 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9615 return prefs_common
.autosave
;
9619 * compose_exec_ext_editor:
9621 * Open (and optionally embed) external editor
9623 static void compose_exec_ext_editor(Compose
*compose
)
9626 #ifdef GDK_WINDOWING_X11
9628 Window socket_wid
= 0;
9630 #endif /* GDK_WINDOWING_X11 */
9632 GError
*error
= NULL
;
9636 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9637 G_DIR_SEPARATOR
, compose
);
9639 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9640 alertpanel_error(_("Could not write the body to file:\n%s"),
9646 #ifdef GDK_WINDOWING_X11
9647 if (compose_get_ext_editor_uses_socket()) {
9648 if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
9649 /* Only allow one socket */
9650 if (compose
->exteditor_socket
!= NULL
) {
9651 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9652 /* Move the focus off of the socket */
9653 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9658 /* Create the receiving GtkSocket */
9659 socket
= gtk_socket_new ();
9660 g_signal_connect (G_OBJECT(socket
), "plug-removed",
9661 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9663 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9664 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9665 /* Realize the socket so that we can use its ID */
9666 gtk_widget_realize(socket
);
9667 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9668 compose
->exteditor_socket
= socket
;
9670 debug_print("Socket communication with an external editor is only available on X11.\n");
9673 if (compose_get_ext_editor_uses_socket()) {
9674 alertpanel_error(_("Socket communication with an external editor is only available on X11."));
9678 #endif /* GDK_WINDOWING_X11 */
9680 if (compose_get_ext_editor_cmd_valid()) {
9681 #ifdef GDK_WINDOWING_X11
9682 if (compose_get_ext_editor_uses_socket() && GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
9683 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9684 s
= strstr(p
, "%w");
9686 if (strstr(p
, "%s") < s
)
9687 cmd
= g_strdup_printf(p
, tmp
, socket_wid
);
9689 cmd
= g_strdup_printf(p
, socket_wid
, tmp
);
9692 cmd
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), tmp
);
9695 cmd
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), tmp
);
9696 #endif /* GDK_WINDOWING_X11 */
9698 if (prefs_common_get_ext_editor_cmd())
9699 g_warning("external editor command-line is invalid: '%s'",
9700 prefs_common_get_ext_editor_cmd());
9701 cmd
= g_strdup_printf(DEFAULT_EDITOR_CMD
, tmp
);
9704 argv
= strsplit_with_quote(cmd
, " ", 0);
9706 if (!g_spawn_async(NULL
, argv
, NULL
,
9707 G_SPAWN_DO_NOT_REAP_CHILD
| G_SPAWN_SEARCH_PATH
,
9708 NULL
, NULL
, &pid
, &error
)) {
9709 alertpanel_error(_("Could not spawn the following "
9710 "external editor command:\n%s\n%s"),
9711 cmd
, error
? error
->message
: _("Unknown error"));
9713 g_error_free(error
);
9722 compose
->exteditor_file
= g_strdup(tmp
);
9723 compose
->exteditor_pid
= pid
;
9724 compose
->exteditor_tag
= g_child_watch_add(pid
,
9725 compose_ext_editor_closed_cb
,
9728 compose_set_ext_editor_sensitive(compose
, FALSE
);
9734 * compose_ext_editor_cb:
9736 * External editor has closed (called by g_child_watch)
9738 static void compose_ext_editor_closed_cb(GPid pid
, gint exit_status
, gpointer data
)
9740 Compose
*compose
= (Compose
*)data
;
9741 GError
*error
= NULL
;
9742 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9743 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9744 GtkTextIter start
, end
;
9747 #if GLIB_CHECK_VERSION(2,70,0)
9748 if (!g_spawn_check_wait_status(exit_status
, &error
)) {
9750 if (!g_spawn_check_exit_status(exit_status
, &error
)) {
9753 _("External editor stopped with an error: %s"),
9754 error
? error
->message
: _("Unknown error"));
9756 g_error_free(error
);
9758 g_spawn_close_pid(compose
->exteditor_pid
);
9760 gtk_text_buffer_set_text(buffer
, "", -1);
9761 compose_insert_file(compose
, compose
->exteditor_file
);
9762 compose_changed_cb(NULL
, compose
);
9764 /* Check if we should save the draft or not */
9765 if (compose_can_autosave(compose
))
9766 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9768 if (claws_unlink(compose
->exteditor_file
) < 0)
9769 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9771 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9772 gtk_text_buffer_get_start_iter(buffer
, &start
);
9773 gtk_text_buffer_get_end_iter(buffer
, &end
);
9774 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9775 if (chars
&& strlen(chars
) > 0)
9776 compose
->modified
= TRUE
;
9779 compose_set_ext_editor_sensitive(compose
, TRUE
);
9781 g_free(compose
->exteditor_file
);
9782 compose
->exteditor_file
= NULL
;
9783 compose
->exteditor_pid
= INVALID_PID
;
9784 compose
->exteditor_tag
= -1;
9785 #ifdef GDK_WINDOWING_X11
9786 if (compose
->exteditor_socket
&& GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
9787 gtk_widget_destroy(compose
->exteditor_socket
);
9788 compose
->exteditor_socket
= NULL
;
9790 #endif /* GDK_WINDOWING_X11 */
9794 static gboolean
compose_get_ext_editor_cmd_valid()
9796 gboolean has_s
= FALSE
;
9797 gboolean has_w
= FALSE
;
9798 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9801 while ((p
= strchr(p
, '%'))) {
9807 } else if (*p
== 'w') {
9818 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9820 GPid pid
= compose
->exteditor_pid
;
9821 gchar
*pidmsg
= NULL
;
9827 pidmsg
= g_strdup_printf(_("process id: %" G_PID_FORMAT
), pid
);
9829 msg
= g_strdup_printf
9830 (_("The external editor is still working.\n"
9831 "Force terminating the process?\n"
9833 val
= alertpanel_full(_("Notice"), msg
, NULL
, _("_No"), NULL
, _("_Yes"),
9834 NULL
, NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
,
9838 if (val
== G_ALERTALTERNATE
) {
9839 g_source_remove(compose
->exteditor_tag
);
9842 if (!TerminateProcess(compose
->exteditor_pid
, 0))
9843 perror("TerminateProcess");
9845 if (kill(pid
, SIGTERM
) < 0) perror("kill");
9846 waitpid(compose
->exteditor_pid
, NULL
, 0);
9847 #endif /* G_OS_WIN32 */
9849 g_warning("terminated %s, temporary file: %s",
9850 pidmsg
, compose
->exteditor_file
);
9851 g_spawn_close_pid(compose
->exteditor_pid
);
9853 compose_set_ext_editor_sensitive(compose
, TRUE
);
9855 g_free(compose
->exteditor_file
);
9856 compose
->exteditor_file
= NULL
;
9857 compose
->exteditor_pid
= INVALID_PID
;
9858 compose
->exteditor_tag
= -1;
9870 static char *ext_editor_menu_entries
[] = {
9871 "Menu/Message/Send",
9872 "Menu/Message/SendLater",
9873 "Menu/Message/InsertFile",
9874 "Menu/Message/InsertSig",
9875 "Menu/Message/ReplaceSig",
9876 "Menu/Message/Save",
9877 "Menu/Message/Print",
9882 "Menu/Tools/ShowRuler",
9883 "Menu/Tools/Actions",
9888 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9893 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9894 cm_menu_set_sensitive_full(compose
->ui_manager
,
9895 ext_editor_menu_entries
[i
], sensitive
);
9898 #ifdef GDK_WINDOWING_X11
9899 if (compose_get_ext_editor_uses_socket() && GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
9901 if (compose
->exteditor_socket
)
9902 gtk_widget_hide(compose
->exteditor_socket
);
9903 gtk_widget_show(compose
->scrolledwin
);
9904 if (prefs_common
.show_ruler
)
9905 gtk_widget_show(compose
->ruler_hbox
);
9906 /* Fix the focus, as it doesn't go anywhere when the
9907 * socket is hidden or destroyed */
9908 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9910 g_assert (compose
->exteditor_socket
!= NULL
);
9911 /* Fix the focus, as it doesn't go anywhere when the
9912 * edit box is hidden */
9913 if (gtk_widget_is_focus(compose
->text
))
9914 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9915 gtk_widget_hide(compose
->scrolledwin
);
9916 gtk_widget_hide(compose
->ruler_hbox
);
9917 gtk_widget_show(compose
->exteditor_socket
);
9920 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9923 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9924 #endif /* GDK_WINDOWING_X11 */
9925 if (compose
->toolbar
->send_btn
)
9926 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9927 if (compose
->toolbar
->sendl_btn
)
9928 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9929 if (compose
->toolbar
->draft_btn
)
9930 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9931 if (compose
->toolbar
->insert_btn
)
9932 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9933 if (compose
->toolbar
->sig_btn
)
9934 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9935 if (compose
->toolbar
->exteditor_btn
)
9936 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9937 if (compose
->toolbar
->linewrap_current_btn
)
9938 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9939 if (compose
->toolbar
->linewrap_all_btn
)
9940 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9943 static gboolean
compose_get_ext_editor_uses_socket()
9945 return (prefs_common_get_ext_editor_cmd() &&
9946 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9949 #ifdef GDK_WINDOWING_X11
9950 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9952 compose
->exteditor_socket
= NULL
;
9953 /* returning FALSE allows destruction of the socket */
9956 #endif /* GDK_WINDOWING_X11 */
9959 * compose_undo_state_changed:
9961 * Change the sensivity of the menuentries undo and redo
9963 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9964 gint redo_state
, gpointer data
)
9966 Compose
*compose
= (Compose
*)data
;
9968 switch (undo_state
) {
9969 case UNDO_STATE_TRUE
:
9970 if (!undostruct
->undo_state
) {
9971 undostruct
->undo_state
= TRUE
;
9972 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9975 case UNDO_STATE_FALSE
:
9976 if (undostruct
->undo_state
) {
9977 undostruct
->undo_state
= FALSE
;
9978 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
9981 case UNDO_STATE_UNCHANGED
:
9983 case UNDO_STATE_REFRESH
:
9984 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
9987 g_warning("undo state not recognized");
9991 switch (redo_state
) {
9992 case UNDO_STATE_TRUE
:
9993 if (!undostruct
->redo_state
) {
9994 undostruct
->redo_state
= TRUE
;
9995 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
9998 case UNDO_STATE_FALSE
:
9999 if (undostruct
->redo_state
) {
10000 undostruct
->redo_state
= FALSE
;
10001 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
10004 case UNDO_STATE_UNCHANGED
:
10006 case UNDO_STATE_REFRESH
:
10007 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
10010 g_warning("redo state not recognized");
10015 /* callback functions */
10017 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
10018 GtkAllocation
*allocation
,
10021 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10024 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10025 * includes "non-client" (windows-izm) in calculation, so this calculation
10026 * may not be accurate.
10028 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10029 GtkAllocation
*allocation
,
10030 GtkSHRuler
*shruler
)
10032 if (prefs_common
.show_ruler
) {
10033 gint char_width
= 0, char_height
= 0;
10034 gint line_width_in_chars
;
10036 gtkut_get_font_size(GTK_WIDGET(widget
),
10037 &char_width
, &char_height
);
10038 line_width_in_chars
=
10039 (allocation
->width
- allocation
->x
) / char_width
;
10041 /* got the maximum */
10042 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10043 0.0, line_width_in_chars
, 0);
10052 ComposePrefType type
;
10053 gboolean entry_marked
;
10054 } HeaderEntryState
;
10056 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10058 Compose
*compose
= (Compose
*)data
;
10061 gchar
*folderidentifier
;
10062 gint account_id
= 0;
10063 GtkTreeModel
*menu
;
10065 GSList
*list
, *saved_list
= NULL
;
10066 HeaderEntryState
*state
;
10068 /* Get ID of active account in the combo box */
10069 menu
= gtk_combo_box_get_model(optmenu
);
10070 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10071 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10073 ac
= account_find_from_id(account_id
);
10074 cm_return_if_fail(ac
!= NULL
);
10076 if (ac
!= compose
->account
) {
10077 compose_select_account(compose
, ac
, FALSE
);
10079 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10080 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10082 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10083 compose_destroy_headerentry(compose
, hentry
);
10086 state
= g_malloc0(sizeof(HeaderEntryState
));
10087 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10088 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10089 state
->entry
= gtk_editable_get_chars(
10090 GTK_EDITABLE(hentry
->entry
), 0, -1);
10091 state
->type
= hentry
->type
;
10093 saved_list
= g_slist_append(saved_list
, state
);
10094 compose_destroy_headerentry(compose
, hentry
);
10097 compose
->header_last
= NULL
;
10098 g_slist_free(compose
->header_list
);
10099 compose
->header_list
= NULL
;
10100 compose
->header_nextrow
= 1;
10101 compose_create_header_entry(compose
);
10103 if (ac
->set_autocc
&& ac
->auto_cc
)
10104 compose_entry_append(compose
, ac
->auto_cc
,
10105 COMPOSE_CC
, PREF_ACCOUNT
);
10106 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10107 compose_entry_append(compose
, ac
->auto_bcc
,
10108 COMPOSE_BCC
, PREF_ACCOUNT
);
10109 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10110 compose_entry_append(compose
, ac
->auto_replyto
,
10111 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10113 for (list
= saved_list
; list
; list
= list
->next
) {
10114 state
= (HeaderEntryState
*) list
->data
;
10116 compose_add_header_entry(compose
, state
->header
,
10117 state
->entry
, state
->type
);
10119 g_free(state
->header
);
10120 g_free(state
->entry
);
10123 g_slist_free(saved_list
);
10125 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10126 (ac
->protocol
== A_NNTP
) ?
10127 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10130 /* Set message save folder */
10131 compose_set_save_to(compose
, NULL
);
10132 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10133 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10134 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10135 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10136 compose_set_save_to(compose
, folderidentifier
);
10137 g_free(folderidentifier
);
10138 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10139 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10140 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10142 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10143 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10144 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10145 (compose
->account
, F_OUTBOX
));
10146 compose_set_save_to(compose
, folderidentifier
);
10147 g_free(folderidentifier
);
10151 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10152 GtkTreeViewColumn
*column
, Compose
*compose
)
10154 compose_attach_property(NULL
, compose
);
10157 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10160 Compose
*compose
= (Compose
*)data
;
10161 GtkTreeSelection
*attach_selection
;
10162 gint attach_nr_selected
;
10165 if (!event
|| compose
->redirect_filename
!= NULL
)
10168 if (event
->button
== 3) {
10169 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10170 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10172 /* If no rows, or just one row is selected, right-click should
10173 * open menu relevant to the row being right-clicked on. We
10174 * achieve that by selecting the clicked row first. If more
10175 * than one row is selected, we shouldn't modify the selection,
10176 * as user may want to remove selected rows (attachments). */
10177 if (attach_nr_selected
< 2) {
10178 gtk_tree_selection_unselect_all(attach_selection
);
10179 attach_nr_selected
= 0;
10180 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10181 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10182 if (path
!= NULL
) {
10183 gtk_tree_selection_select_path(attach_selection
, path
);
10184 gtk_tree_path_free(path
);
10185 attach_nr_selected
++;
10189 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10190 /* Properties menu item makes no sense with more than one row
10191 * selected, the properties dialog can only edit one attachment. */
10192 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10194 gtk_menu_popup_at_pointer(GTK_MENU(compose
->popupmenu
), NULL
);
10202 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10205 Compose
*compose
= (Compose
*)data
;
10207 if (!event
) return FALSE
;
10209 switch (event
->keyval
) {
10210 case GDK_KEY_Delete
:
10211 compose_attach_remove_selected(NULL
, compose
);
10217 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10219 toolbar_comp_set_sensitive(compose
, allow
);
10220 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10221 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10223 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10225 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10226 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10227 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10229 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10233 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10235 Compose
*compose
= (Compose
*)data
;
10238 if (compose
->exteditor_tag
!= -1) {
10239 debug_print("ignoring send: external editor still open\n");
10243 if (prefs_common
.work_offline
&&
10244 !inc_offline_should_override(TRUE
,
10245 _("Claws Mail needs network access in order "
10246 "to send this email.")))
10249 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10250 g_source_remove(compose
->draft_timeout_tag
);
10251 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10254 compose_send(compose
);
10257 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10259 Compose
*compose
= (Compose
*)data
;
10260 ComposeQueueResult val
;
10263 compose_allow_user_actions(compose
, FALSE
);
10264 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10265 compose_allow_user_actions(compose
, TRUE
);
10268 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10269 compose_close(compose
);
10271 _display_queue_error(val
);
10274 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10277 #define DRAFTED_AT_EXIT "drafted_at_exit"
10278 static void compose_register_draft(MsgInfo
*info
)
10280 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10281 DRAFTED_AT_EXIT
, NULL
);
10282 FILE *fp
= claws_fopen(filepath
, "ab");
10285 gchar
*name
= folder_item_get_identifier(info
->folder
);
10286 fprintf(fp
, "%s\t%d\n", name
, info
->msgnum
);
10294 gboolean
compose_draft (gpointer data
, guint action
)
10296 Compose
*compose
= (Compose
*)data
;
10298 FolderItemPrefs
*prefs
;
10302 MsgFlags flag
= {0, 0};
10303 static gboolean lock
= FALSE
;
10304 MsgInfo
*newmsginfo
;
10306 gboolean target_locked
= FALSE
;
10307 gboolean err
= FALSE
;
10310 if (lock
) return FALSE
;
10312 if (compose
->sending
)
10315 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10316 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10318 if (!g_mutex_trylock(&compose
->mutex
)) {
10319 /* we don't want to lock the mutex once it's available,
10320 * because as the only other part of compose.c locking
10321 * it is compose_close - which means once unlocked,
10322 * the compose struct will be freed */
10323 debug_print("couldn't lock mutex, probably sending\n");
10329 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10330 G_DIR_SEPARATOR
, compose
);
10331 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10332 FILE_OP_ERROR(tmp
, "claws_fopen");
10336 /* chmod for security unless folder chmod is set */
10337 prefs
= draft
->prefs
;
10338 if (prefs
&& prefs
->enable_folder_chmod
&& prefs
->folder_chmod
) {
10339 filemode
= prefs
->folder_chmod
;
10340 if (filemode
& S_IRGRP
) filemode
|= S_IWGRP
;
10341 if (filemode
& S_IROTH
) filemode
|= S_IWOTH
;
10342 if (chmod(tmp
, filemode
) < 0)
10343 FILE_OP_ERROR(tmp
, "chmod");
10344 } else if (change_file_mode_rw(fp
, tmp
) < 0) {
10345 FILE_OP_ERROR(tmp
, "chmod");
10346 g_warning("can't change file mode");
10349 /* Save draft infos */
10350 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10351 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10353 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10354 gchar
*savefolderid
;
10356 savefolderid
= compose_get_save_to(compose
);
10357 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10358 g_free(savefolderid
);
10360 if (compose
->return_receipt
) {
10361 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10363 if (compose
->privacy_system
) {
10364 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10365 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10366 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10369 /* Message-ID of message replying to */
10370 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10371 gchar
*folderid
= NULL
;
10373 if (compose
->replyinfo
->folder
)
10374 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10375 if (folderid
== NULL
)
10376 folderid
= g_strdup("NULL");
10378 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10381 /* Message-ID of message forwarding to */
10382 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10383 gchar
*folderid
= NULL
;
10385 if (compose
->fwdinfo
->folder
)
10386 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10387 if (folderid
== NULL
)
10388 folderid
= g_strdup("NULL");
10390 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10394 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10395 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10397 sheaders
= compose_get_manual_headers_info(compose
);
10398 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10401 /* end of headers */
10402 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10409 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10413 if (claws_safe_fclose(fp
) == EOF
) {
10417 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10418 if (compose
->targetinfo
) {
10419 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10421 flag
.perm_flags
|= MSG_LOCKED
;
10423 flag
.tmp_flags
= MSG_DRAFT
;
10425 folder_item_scan(draft
);
10426 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10427 MsgInfo
*tmpinfo
= NULL
;
10428 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10429 if (compose
->msgid
) {
10430 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10433 msgnum
= tmpinfo
->msgnum
;
10434 procmsg_msginfo_free(&tmpinfo
);
10435 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10437 debug_print("didn't get draft msgnum after scanning\n");
10440 debug_print("got draft msgnum %d from adding\n", msgnum
);
10446 if (action
!= COMPOSE_AUTO_SAVE
) {
10447 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10448 alertpanel_error(_("Could not save draft."));
10451 gtkut_window_popup(compose
->window
);
10452 val
= alertpanel_full(_("Could not save draft"),
10453 _("Could not save draft.\n"
10454 "Do you want to cancel exit or discard this email?"),
10455 NULL
, _("_Cancel exit"), NULL
, _("_Discard email"),
10456 NULL
, NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_QUESTION
);
10457 if (val
== G_ALERTALTERNATE
) {
10459 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10460 compose_close(compose
);
10464 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10473 if (compose
->mode
== COMPOSE_REEDIT
) {
10474 compose_remove_reedit_target(compose
, TRUE
);
10477 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10480 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10482 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10484 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10485 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10486 procmsg_msginfo_set_flags(newmsginfo
, 0,
10487 MSG_HAS_ATTACHMENT
);
10489 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10490 compose_register_draft(newmsginfo
);
10492 procmsg_msginfo_free(&newmsginfo
);
10495 folder_item_scan(draft
);
10497 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10499 g_mutex_unlock(&compose
->mutex
); /* must be done before closing */
10500 compose_close(compose
);
10507 GError
*error
= NULL
;
10512 goffset size
, mtime
;
10514 path
= folder_item_fetch_msg(draft
, msgnum
);
10515 if (path
== NULL
) {
10516 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10520 f
= g_file_new_for_path(path
);
10521 fi
= g_file_query_info(f
, "standard::size,time::modified",
10522 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10523 if (error
!= NULL
) {
10524 debug_print("couldn't query file info for '%s': %s\n",
10525 path
, error
->message
);
10526 g_error_free(error
);
10531 size
= g_file_info_get_size(fi
);
10532 g_file_info_get_modification_time(fi
, &tv
);
10534 g_object_unref(fi
);
10537 if (g_stat(path
, &s
) < 0) {
10538 FILE_OP_ERROR(path
, "stat");
10543 mtime
= s
.st_mtime
;
10547 procmsg_msginfo_free(&(compose
->targetinfo
));
10548 compose
->targetinfo
= procmsg_msginfo_new();
10549 compose
->targetinfo
->msgnum
= msgnum
;
10550 compose
->targetinfo
->size
= size
;
10551 compose
->targetinfo
->mtime
= mtime
;
10552 compose
->targetinfo
->folder
= draft
;
10554 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10555 compose
->mode
= COMPOSE_REEDIT
;
10557 if (action
== COMPOSE_AUTO_SAVE
) {
10558 compose
->modified
= FALSE
;
10559 compose
->autosaved_draft
= compose
->targetinfo
;
10561 compose_set_title(compose
);
10565 g_mutex_unlock(&compose
->mutex
);
10569 void compose_clear_exit_drafts(void)
10571 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10572 DRAFTED_AT_EXIT
, NULL
);
10573 if (is_file_exist(filepath
))
10574 claws_unlink(filepath
);
10579 void compose_reopen_exit_drafts(void)
10581 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10582 DRAFTED_AT_EXIT
, NULL
);
10583 FILE *fp
= claws_fopen(filepath
, "rb");
10587 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10588 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10589 const gchar
*folder
= parts
[0];
10590 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10592 if (folder
&& *folder
&& msgnum
> -1) {
10593 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10594 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10596 compose_reedit(info
, FALSE
);
10603 compose_clear_exit_drafts();
10606 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10608 Compose
*compose
= (Compose
*)data
;
10609 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10610 compose
->rmode
= COMPOSE_REEDIT
;
10611 compose
->modified
= FALSE
;
10612 compose_set_title(compose
);
10615 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10617 if (compose
&& file_list
) {
10620 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10621 gchar
*file
= (gchar
*) tmp
->data
;
10622 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10623 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10624 compose_changed_cb(NULL
, compose
);
10629 g_free(utf8_filename
);
10634 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10636 Compose
*compose
= (Compose
*)data
;
10639 if (compose
->redirect_filename
!= NULL
)
10642 /* Set focus_window properly, in case we were called via popup menu,
10643 * which unsets it (via focus_out_event callback on compose window). */
10644 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10646 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10649 compose_attach_from_list(compose
, file_list
, TRUE
);
10650 g_list_free(file_list
);
10654 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10656 Compose
*compose
= (Compose
*)data
;
10658 gint files_inserted
= 0;
10660 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10665 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10666 gchar
*file
= (gchar
*) tmp
->data
;
10667 gchar
*filedup
= g_strdup(file
);
10668 gchar
*shortfile
= g_path_get_basename(filedup
);
10669 ComposeInsertResult res
;
10670 /* insert the file if the file is short or if the user confirmed that
10671 he/she wants to insert the large file */
10672 res
= compose_insert_file(compose
, file
);
10673 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10674 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10675 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10676 alertpanel_error(_("File '%s' contained invalid characters\n"
10677 "for the current encoding, insertion may be incorrect."),
10679 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10686 g_list_free(file_list
);
10690 if (files_inserted
> 0 && compose
->gtkaspell
&&
10691 compose
->gtkaspell
->check_while_typing
)
10692 gtkaspell_highlight_all(compose
->gtkaspell
);
10696 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10698 Compose
*compose
= (Compose
*)data
;
10700 compose_insert_sig(compose
, FALSE
);
10703 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10705 Compose
*compose
= (Compose
*)data
;
10707 compose_insert_sig(compose
, TRUE
);
10710 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10714 Compose
*compose
= (Compose
*)data
;
10716 gtk_window_get_position(GTK_WINDOW(widget
), &x
, &y
);
10717 if (!compose
->batch
) {
10718 prefs_common
.compose_x
= x
;
10719 prefs_common
.compose_y
= y
;
10721 if (compose
->sending
|| compose
->updating
)
10723 compose_close_cb(NULL
, compose
);
10727 void compose_close_toolbar(Compose
*compose
)
10729 compose_close_cb(NULL
, compose
);
10732 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10734 Compose
*compose
= (Compose
*)data
;
10737 if (compose
->exteditor_tag
!= -1) {
10738 if (!compose_ext_editor_kill(compose
))
10742 if (compose
->modified
) {
10743 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10744 if (!g_mutex_trylock(&compose
->mutex
)) {
10745 /* we don't want to lock the mutex once it's available,
10746 * because as the only other part of compose.c locking
10747 * it is compose_close - which means once unlocked,
10748 * the compose struct will be freed */
10749 debug_print("couldn't lock mutex, probably sending\n");
10752 if (!reedit
|| (compose
->folder
!= NULL
&& compose
->folder
->stype
== F_DRAFT
)) {
10753 val
= alertpanel(_("Discard message"),
10754 _("This message has been modified. Discard it?"),
10755 NULL
, _("_Discard"), NULL
, _("_Save to Drafts"), NULL
, _("_Cancel"),
10758 val
= alertpanel(_("Save changes"),
10759 _("This message has been modified. Save the latest changes?"),
10760 NULL
, _("_Don't save"), NULL
, _("_Save to Drafts"), NULL
, _("_Cancel"),
10761 ALERTFOCUS_SECOND
);
10763 g_mutex_unlock(&compose
->mutex
);
10765 case G_ALERTDEFAULT
:
10766 if (compose_can_autosave(compose
) && !reedit
)
10767 compose_remove_draft(compose
);
10769 case G_ALERTALTERNATE
:
10770 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10777 compose_close(compose
);
10780 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10782 Compose
*compose
= (Compose
*) data
;
10784 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10785 if (compose
->targetinfo
)
10786 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10789 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10791 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10792 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10793 Compose
*compose
= (Compose
*) data
;
10796 compose
->out_encoding
= (CharSet
)value
;
10799 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10801 Compose
*compose
= (Compose
*)data
;
10803 #ifndef USE_ALT_ADDRBOOK
10804 addressbook_open(compose
);
10806 GError
* error
= NULL
;
10807 addressbook_connect_signals(compose
);
10808 addressbook_dbus_open(TRUE
, &error
);
10810 g_warning("%s", error
->message
);
10811 g_error_free(error
);
10816 static void about_show_cb(GtkAction
*action
, gpointer data
)
10821 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10823 Compose
*compose
= (Compose
*)data
;
10828 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10829 cm_return_if_fail(tmpl
!= NULL
);
10831 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10833 val
= alertpanel(_("Apply template"), msg
,
10834 NULL
, _("_Replace"), NULL
, _("_Insert"), NULL
, _("_Cancel"),
10838 if (val
== G_ALERTDEFAULT
)
10839 compose_template_apply(compose
, tmpl
, TRUE
);
10840 else if (val
== G_ALERTALTERNATE
)
10841 compose_template_apply(compose
, tmpl
, FALSE
);
10844 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10846 Compose
*compose
= (Compose
*)data
;
10848 if (compose
->exteditor_tag
!= -1) {
10849 debug_print("ignoring open external editor: external editor still open\n");
10852 compose_exec_ext_editor(compose
);
10855 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10857 Compose
*compose
= (Compose
*)data
;
10858 gboolean prev_autowrap
= compose
->autowrap
;
10860 compose
->autowrap
= FALSE
;
10861 undo_undo(compose
->undostruct
);
10862 compose
->autowrap
= prev_autowrap
;
10865 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10867 Compose
*compose
= (Compose
*)data
;
10868 gboolean prev_autowrap
= compose
->autowrap
;
10870 compose
->autowrap
= FALSE
;
10871 undo_redo(compose
->undostruct
);
10872 compose
->autowrap
= prev_autowrap
;
10875 static void entry_cut_clipboard(GtkWidget
*entry
)
10877 if (GTK_IS_EDITABLE(entry
))
10878 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10879 else if (GTK_IS_TEXT_VIEW(entry
))
10880 gtk_text_buffer_cut_clipboard(
10881 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10882 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10886 static void entry_copy_clipboard(GtkWidget
*entry
)
10888 if (GTK_IS_EDITABLE(entry
))
10889 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10890 else if (GTK_IS_TEXT_VIEW(entry
))
10891 gtk_text_buffer_copy_clipboard(
10892 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10893 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10896 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10897 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10899 if (GTK_IS_TEXT_VIEW(entry
)) {
10900 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10901 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10902 GtkTextIter start_iter
, end_iter
;
10904 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10906 if (contents
== NULL
)
10909 /* we shouldn't delete the selection when middle-click-pasting, or we
10910 * can't mid-click-paste our own selection */
10911 if (clip
!= GDK_SELECTION_PRIMARY
) {
10912 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10913 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10916 if (insert_place
== NULL
) {
10917 /* if insert_place isn't specified, insert at the cursor.
10918 * used for Ctrl-V pasting */
10919 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10920 start
= gtk_text_iter_get_offset(&start_iter
);
10921 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10923 /* if insert_place is specified, paste here.
10924 * used for mid-click-pasting */
10925 start
= gtk_text_iter_get_offset(insert_place
);
10926 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10927 if (prefs_common
.primary_paste_unselects
)
10928 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10932 /* paste unwrapped: mark the paste so it's not wrapped later */
10933 end
= start
+ strlen(contents
);
10934 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10935 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10936 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10937 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10938 /* rewrap paragraph now (after a mid-click-paste) */
10939 mark_start
= gtk_text_buffer_get_insert(buffer
);
10940 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10941 gtk_text_iter_backward_char(&start_iter
);
10942 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10944 } else if (GTK_IS_EDITABLE(entry
))
10945 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10947 compose
->modified
= TRUE
;
10950 static void entry_allsel(GtkWidget
*entry
)
10952 if (GTK_IS_EDITABLE(entry
))
10953 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10954 else if (GTK_IS_TEXT_VIEW(entry
)) {
10955 GtkTextIter startiter
, enditer
;
10956 GtkTextBuffer
*textbuf
;
10958 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10959 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10960 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10962 gtk_text_buffer_move_mark_by_name(textbuf
,
10963 "selection_bound", &startiter
);
10964 gtk_text_buffer_move_mark_by_name(textbuf
,
10965 "insert", &enditer
);
10969 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10971 Compose
*compose
= (Compose
*)data
;
10972 if (compose
->focused_editable
10973 #ifndef GENERIC_UMPC
10974 && gtk_widget_has_focus(compose
->focused_editable
)
10977 entry_cut_clipboard(compose
->focused_editable
);
10980 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10982 Compose
*compose
= (Compose
*)data
;
10983 if (compose
->focused_editable
10984 #ifndef GENERIC_UMPC
10985 && gtk_widget_has_focus(compose
->focused_editable
)
10988 entry_copy_clipboard(compose
->focused_editable
);
10991 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
10993 Compose
*compose
= (Compose
*)data
;
10994 gint prev_autowrap
;
10995 GtkTextBuffer
*buffer
;
10997 if (compose
->focused_editable
10998 #ifndef GENERIC_UMPC
10999 && gtk_widget_has_focus(compose
->focused_editable
)
11002 entry_paste_clipboard(compose
, compose
->focused_editable
,
11003 prefs_common
.linewrap_pastes
,
11004 GDK_SELECTION_CLIPBOARD
, NULL
);
11009 #ifndef GENERIC_UMPC
11010 gtk_widget_has_focus(compose
->text
) &&
11012 compose
->gtkaspell
&&
11013 compose
->gtkaspell
->check_while_typing
)
11014 gtkaspell_highlight_all(compose
->gtkaspell
);
11018 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
11020 Compose
*compose
= (Compose
*)data
;
11021 gint wrap_quote
= prefs_common
.linewrap_quote
;
11022 if (compose
->focused_editable
11023 #ifndef GENERIC_UMPC
11024 && gtk_widget_has_focus(compose
->focused_editable
)
11027 /* let text_insert() (called directly or at a later time
11028 * after the gtk_editable_paste_clipboard) know that
11029 * text is to be inserted as a quotation. implemented
11030 * by using a simple refcount... */
11031 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11032 G_OBJECT(compose
->focused_editable
),
11033 "paste_as_quotation"));
11034 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11035 "paste_as_quotation",
11036 GINT_TO_POINTER(paste_as_quotation
+ 1));
11037 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11038 entry_paste_clipboard(compose
, compose
->focused_editable
,
11039 prefs_common
.linewrap_pastes
,
11040 GDK_SELECTION_CLIPBOARD
, NULL
);
11041 prefs_common
.linewrap_quote
= wrap_quote
;
11045 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11047 Compose
*compose
= (Compose
*)data
;
11048 gint prev_autowrap
;
11049 GtkTextBuffer
*buffer
;
11051 if (compose
->focused_editable
11052 #ifndef GENERIC_UMPC
11053 && gtk_widget_has_focus(compose
->focused_editable
)
11056 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11057 GDK_SELECTION_CLIPBOARD
, NULL
);
11062 #ifndef GENERIC_UMPC
11063 gtk_widget_has_focus(compose
->text
) &&
11065 compose
->gtkaspell
&&
11066 compose
->gtkaspell
->check_while_typing
)
11067 gtkaspell_highlight_all(compose
->gtkaspell
);
11071 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11073 Compose
*compose
= (Compose
*)data
;
11074 gint prev_autowrap
;
11075 GtkTextBuffer
*buffer
;
11077 if (compose
->focused_editable
11078 #ifndef GENERIC_UMPC
11079 && gtk_widget_has_focus(compose
->focused_editable
)
11082 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11083 GDK_SELECTION_CLIPBOARD
, NULL
);
11088 #ifndef GENERIC_UMPC
11089 gtk_widget_has_focus(compose
->text
) &&
11091 compose
->gtkaspell
&&
11092 compose
->gtkaspell
->check_while_typing
)
11093 gtkaspell_highlight_all(compose
->gtkaspell
);
11097 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11099 Compose
*compose
= (Compose
*)data
;
11100 if (compose
->focused_editable
11101 #ifndef GENERIC_UMPC
11102 && gtk_widget_has_focus(compose
->focused_editable
)
11105 entry_allsel(compose
->focused_editable
);
11108 static void textview_move_beginning_of_line (GtkTextView
*text
)
11110 GtkTextBuffer
*buffer
;
11114 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11116 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11117 mark
= gtk_text_buffer_get_insert(buffer
);
11118 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11119 gtk_text_iter_set_line_offset(&ins
, 0);
11120 gtk_text_buffer_place_cursor(buffer
, &ins
);
11123 static void textview_move_forward_character (GtkTextView
*text
)
11125 GtkTextBuffer
*buffer
;
11129 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11131 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11132 mark
= gtk_text_buffer_get_insert(buffer
);
11133 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11134 if (gtk_text_iter_forward_cursor_position(&ins
))
11135 gtk_text_buffer_place_cursor(buffer
, &ins
);
11138 static void textview_move_backward_character (GtkTextView
*text
)
11140 GtkTextBuffer
*buffer
;
11144 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11146 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11147 mark
= gtk_text_buffer_get_insert(buffer
);
11148 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11149 if (gtk_text_iter_backward_cursor_position(&ins
))
11150 gtk_text_buffer_place_cursor(buffer
, &ins
);
11153 static void textview_move_forward_word (GtkTextView
*text
)
11155 GtkTextBuffer
*buffer
;
11160 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11162 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11163 mark
= gtk_text_buffer_get_insert(buffer
);
11164 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11165 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11166 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11167 gtk_text_iter_backward_word_start(&ins
);
11168 gtk_text_buffer_place_cursor(buffer
, &ins
);
11172 static void textview_move_backward_word (GtkTextView
*text
)
11174 GtkTextBuffer
*buffer
;
11178 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11180 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11181 mark
= gtk_text_buffer_get_insert(buffer
);
11182 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11183 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11184 gtk_text_buffer_place_cursor(buffer
, &ins
);
11187 static void textview_move_end_of_line (GtkTextView
*text
)
11189 GtkTextBuffer
*buffer
;
11193 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11195 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11196 mark
= gtk_text_buffer_get_insert(buffer
);
11197 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11198 if (gtk_text_iter_forward_to_line_end(&ins
))
11199 gtk_text_buffer_place_cursor(buffer
, &ins
);
11202 static void textview_move_next_line (GtkTextView
*text
)
11204 GtkTextBuffer
*buffer
;
11209 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11211 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11212 mark
= gtk_text_buffer_get_insert(buffer
);
11213 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11214 offset
= gtk_text_iter_get_line_offset(&ins
);
11215 if (gtk_text_iter_forward_line(&ins
)) {
11216 gtk_text_iter_set_line_offset(&ins
, offset
);
11217 gtk_text_buffer_place_cursor(buffer
, &ins
);
11221 static void textview_move_previous_line (GtkTextView
*text
)
11223 GtkTextBuffer
*buffer
;
11228 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11230 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11231 mark
= gtk_text_buffer_get_insert(buffer
);
11232 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11233 offset
= gtk_text_iter_get_line_offset(&ins
);
11234 if (gtk_text_iter_backward_line(&ins
)) {
11235 gtk_text_iter_set_line_offset(&ins
, offset
);
11236 gtk_text_buffer_place_cursor(buffer
, &ins
);
11240 static void textview_delete_forward_character (GtkTextView
*text
)
11242 GtkTextBuffer
*buffer
;
11244 GtkTextIter ins
, end_iter
;
11246 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11248 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11249 mark
= gtk_text_buffer_get_insert(buffer
);
11250 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11252 if (gtk_text_iter_forward_char(&end_iter
)) {
11253 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11257 static void textview_delete_backward_character (GtkTextView
*text
)
11259 GtkTextBuffer
*buffer
;
11261 GtkTextIter ins
, end_iter
;
11263 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11265 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11266 mark
= gtk_text_buffer_get_insert(buffer
);
11267 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11269 if (gtk_text_iter_backward_char(&end_iter
)) {
11270 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11274 static void textview_delete_forward_word (GtkTextView
*text
)
11276 GtkTextBuffer
*buffer
;
11278 GtkTextIter ins
, end_iter
;
11280 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11282 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11283 mark
= gtk_text_buffer_get_insert(buffer
);
11284 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11286 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11287 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11291 static void textview_delete_backward_word (GtkTextView
*text
)
11293 GtkTextBuffer
*buffer
;
11295 GtkTextIter ins
, end_iter
;
11297 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11299 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11300 mark
= gtk_text_buffer_get_insert(buffer
);
11301 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11303 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11304 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11308 static void textview_delete_line (GtkTextView
*text
)
11310 GtkTextBuffer
*buffer
;
11312 GtkTextIter ins
, start_iter
, end_iter
;
11314 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11316 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11317 mark
= gtk_text_buffer_get_insert(buffer
);
11318 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11321 gtk_text_iter_set_line_offset(&start_iter
, 0);
11324 if (gtk_text_iter_ends_line(&end_iter
)){
11325 if (!gtk_text_iter_forward_char(&end_iter
))
11326 gtk_text_iter_backward_char(&start_iter
);
11329 gtk_text_iter_forward_to_line_end(&end_iter
);
11330 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11333 static void textview_delete_to_line_end (GtkTextView
*text
)
11335 GtkTextBuffer
*buffer
;
11337 GtkTextIter ins
, end_iter
;
11339 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11341 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11342 mark
= gtk_text_buffer_get_insert(buffer
);
11343 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11345 if (gtk_text_iter_ends_line(&end_iter
))
11346 gtk_text_iter_forward_char(&end_iter
);
11348 gtk_text_iter_forward_to_line_end(&end_iter
);
11349 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11352 #define DO_ACTION(name, act) { \
11353 if(!strcmp(name, a_name)) { \
11357 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11359 const gchar
*a_name
= gtk_action_get_name(action
);
11360 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11361 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11362 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11363 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11364 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11365 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11366 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11367 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11368 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11369 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11370 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11371 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11372 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11373 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11374 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11377 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11379 Compose
*compose
= (Compose
*)data
;
11380 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11381 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11383 action
= compose_call_advanced_action_from_path(gaction
);
11386 void (*do_action
) (GtkTextView
*text
);
11387 } action_table
[] = {
11388 {textview_move_beginning_of_line
},
11389 {textview_move_forward_character
},
11390 {textview_move_backward_character
},
11391 {textview_move_forward_word
},
11392 {textview_move_backward_word
},
11393 {textview_move_end_of_line
},
11394 {textview_move_next_line
},
11395 {textview_move_previous_line
},
11396 {textview_delete_forward_character
},
11397 {textview_delete_backward_character
},
11398 {textview_delete_forward_word
},
11399 {textview_delete_backward_word
},
11400 {textview_delete_line
},
11401 {textview_delete_to_line_end
}
11404 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11406 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11407 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11408 if (action_table
[action
].do_action
)
11409 action_table
[action
].do_action(text
);
11411 g_warning("not implemented yet");
11415 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11417 GtkAllocation allocation
;
11421 if (GTK_IS_EDITABLE(widget
)) {
11422 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11423 gtk_editable_set_position(GTK_EDITABLE(widget
),
11426 if ((parent
= gtk_widget_get_parent(widget
))
11427 && (parent
= gtk_widget_get_parent(parent
))
11428 && (parent
= gtk_widget_get_parent(parent
))) {
11429 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11430 gtk_widget_get_allocation(widget
, &allocation
);
11431 gint y
= allocation
.y
;
11432 gint height
= allocation
.height
;
11433 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11434 (GTK_SCROLLED_WINDOW(parent
));
11436 gfloat value
= gtk_adjustment_get_value(shown
);
11437 gfloat upper
= gtk_adjustment_get_upper(shown
);
11438 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11439 if (y
< (int)value
) {
11440 gtk_adjustment_set_value(shown
, y
- 1);
11442 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11443 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11444 gtk_adjustment_set_value(shown
,
11445 y
+ height
- (int)page_size
- 1);
11447 gtk_adjustment_set_value(shown
,
11448 (int)upper
- (int)page_size
- 1);
11455 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11456 compose
->focused_editable
= widget
;
11458 #ifdef GENERIC_UMPC
11459 if (GTK_IS_TEXT_VIEW(widget
)
11460 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11461 g_object_ref(compose
->notebook
);
11462 g_object_ref(compose
->edit_vbox
);
11463 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11464 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11465 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11466 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11467 g_object_unref(compose
->notebook
);
11468 g_object_unref(compose
->edit_vbox
);
11469 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11470 G_CALLBACK(compose_grab_focus_cb
),
11472 gtk_widget_grab_focus(widget
);
11473 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11474 G_CALLBACK(compose_grab_focus_cb
),
11476 } else if (!GTK_IS_TEXT_VIEW(widget
)
11477 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11478 g_object_ref(compose
->notebook
);
11479 g_object_ref(compose
->edit_vbox
);
11480 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11481 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11482 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11483 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11484 g_object_unref(compose
->notebook
);
11485 g_object_unref(compose
->edit_vbox
);
11486 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11487 G_CALLBACK(compose_grab_focus_cb
),
11489 gtk_widget_grab_focus(widget
);
11490 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11491 G_CALLBACK(compose_grab_focus_cb
),
11497 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11499 compose
->modified
= TRUE
;
11500 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11501 #ifndef GENERIC_UMPC
11502 compose_set_title(compose
);
11506 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11508 Compose
*compose
= (Compose
*)data
;
11509 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11512 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11514 Compose
*compose
= (Compose
*)data
;
11515 compose_wrap_all_full(compose
, TRUE
);
11518 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11520 Compose
*compose
= (Compose
*)data
;
11522 message_search_compose(compose
);
11525 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11528 Compose
*compose
= (Compose
*)data
;
11529 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11530 if (compose
->autowrap
)
11531 compose_wrap_all_full(compose
, TRUE
);
11532 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11535 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11538 Compose
*compose
= (Compose
*)data
;
11539 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11542 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11544 Compose
*compose
= (Compose
*)data
;
11546 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11547 if (compose
->toolbar
->privacy_sign_btn
!= NULL
)
11548 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11551 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11553 Compose
*compose
= (Compose
*)data
;
11555 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11556 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
)
11557 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11560 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11562 g_free(compose
->privacy_system
);
11563 g_free(compose
->encdata
);
11565 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11566 compose_update_privacy_system_menu_item(compose
, warn
);
11569 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11571 if (folder_item
!= NULL
) {
11572 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11573 privacy_system_can_sign(compose
->privacy_system
)) {
11574 compose_use_signing(compose
,
11575 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11577 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11578 privacy_system_can_encrypt(compose
->privacy_system
)) {
11579 compose_use_encryption(compose
,
11580 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11585 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11587 Compose
*compose
= (Compose
*)data
;
11589 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11590 gtk_widget_show(compose
->ruler_hbox
);
11591 prefs_common
.show_ruler
= TRUE
;
11593 gtk_widget_hide(compose
->ruler_hbox
);
11594 gtk_widget_queue_resize(compose
->edit_vbox
);
11595 prefs_common
.show_ruler
= FALSE
;
11599 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11600 GdkDragContext
*context
,
11603 GtkSelectionData
*data
,
11606 gpointer user_data
)
11608 Compose
*compose
= (Compose
*)user_data
;
11612 type
= gtk_selection_data_get_data_type(data
);
11613 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11614 && gtk_drag_get_source_widget(context
) !=
11615 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11616 list
= uri_list_extract_filenames(
11617 (const gchar
*)gtk_selection_data_get_data(data
));
11618 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11619 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11620 compose_attach_append
11621 (compose
, (const gchar
*)tmp
->data
,
11622 utf8_filename
, NULL
, NULL
);
11623 g_free(utf8_filename
);
11626 compose_changed_cb(NULL
, compose
);
11627 list_free_strings_full(list
);
11628 } else if (gtk_drag_get_source_widget(context
)
11629 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11630 /* comes from our summaryview */
11631 SummaryView
* summaryview
= NULL
;
11632 GSList
* list
= NULL
, *cur
= NULL
;
11634 if (mainwindow_get_mainwindow())
11635 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11638 list
= summary_get_selected_msg_list(summaryview
);
11640 for (cur
= list
; cur
; cur
= cur
->next
) {
11641 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11642 gchar
*file
= NULL
;
11644 file
= procmsg_get_message_file_full(msginfo
,
11647 compose_attach_append(compose
, (const gchar
*)file
,
11648 (const gchar
*)file
, "message/rfc822", NULL
);
11652 g_slist_free(list
);
11656 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11657 GdkDragContext
*drag_context
,
11659 guint time
, gpointer user_data
)
11661 /* not handling this signal makes compose_insert_drag_received_cb
11666 static gboolean completion_set_focus_to_subject
11667 (GtkWidget
*widget
,
11668 GdkEventKey
*event
,
11671 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11673 /* make backtab move to subject field */
11674 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11675 gtk_widget_grab_focus(compose
->subject_entry
);
11681 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11682 GdkDragContext
*drag_context
,
11685 GtkSelectionData
*data
,
11688 gpointer user_data
)
11690 Compose
*compose
= (Compose
*)user_data
;
11696 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11698 type
= gtk_selection_data_get_data_type(data
);
11699 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11700 AlertValue val
= G_ALERTDEFAULT
;
11701 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11703 list
= uri_list_extract_filenames(ddata
);
11704 num_files
= g_list_length(list
);
11705 if (list
== NULL
&& strstr(ddata
, "://")) {
11706 /* Assume a list of no files, and data has ://, is a remote link */
11707 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11708 gchar
*tmpfile
= get_tmp_file();
11709 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11711 compose_insert_file(compose
, tmpfile
);
11712 claws_unlink(tmpfile
);
11714 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11715 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11718 switch (prefs_common
.compose_dnd_mode
) {
11719 case COMPOSE_DND_ASK
:
11720 msg
= g_strdup_printf(
11722 "Do you want to insert the contents of the file "
11723 "into the message body, or attach it to the email?",
11724 "Do you want to insert the contents of the %d files "
11725 "into the message body, or attach them to the email?",
11728 val
= alertpanel_full(_("Insert or attach?"), msg
,
11729 NULL
, _("_Cancel"), NULL
, _("_Insert"), NULL
, _("_Attach"),
11730 ALERTFOCUS_SECOND
, TRUE
, NULL
, ALERT_QUESTION
);
11733 case COMPOSE_DND_INSERT
:
11734 val
= G_ALERTALTERNATE
;
11736 case COMPOSE_DND_ATTACH
:
11737 val
= G_ALERTOTHER
;
11740 /* unexpected case */
11741 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11744 if (val
& G_ALERTDISABLE
) {
11745 val
&= ~G_ALERTDISABLE
;
11746 /* remember what action to perform by default, only if we don't click Cancel */
11747 if (val
== G_ALERTALTERNATE
)
11748 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11749 else if (val
== G_ALERTOTHER
)
11750 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11753 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11754 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11755 list_free_strings_full(list
);
11757 } else if (val
== G_ALERTOTHER
) {
11758 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11759 list_free_strings_full(list
);
11763 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11764 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11766 list_free_strings_full(list
);
11767 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11772 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11773 GdkDragContext
*drag_context
,
11776 GtkSelectionData
*data
,
11779 gpointer user_data
)
11781 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11782 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11784 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11787 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11788 gchar
*decoded
=g_new(gchar
, strlen(email
));
11791 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11792 gtk_editable_delete_text(entry
, 0, -1);
11793 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11794 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11798 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11801 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11803 Compose
*compose
= (Compose
*)data
;
11805 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11806 compose
->return_receipt
= TRUE
;
11808 compose
->return_receipt
= FALSE
;
11811 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11813 Compose
*compose
= (Compose
*)data
;
11815 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11816 compose
->remove_references
= TRUE
;
11818 compose
->remove_references
= FALSE
;
11821 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11822 ComposeHeaderEntry
*headerentry
)
11824 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11825 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11826 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11830 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11831 GdkEventKey
*event
,
11832 ComposeHeaderEntry
*headerentry
)
11834 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11835 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11836 !(event
->state
& GDK_MODIFIER_MASK
) &&
11837 (event
->keyval
== GDK_KEY_BackSpace
) &&
11838 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11839 gtk_container_remove
11840 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11841 headerentry
->combo
);
11842 gtk_container_remove
11843 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11844 headerentry
->entry
);
11845 headerentry
->compose
->header_list
=
11846 g_slist_remove(headerentry
->compose
->header_list
,
11848 g_free(headerentry
);
11849 } else if (event
->keyval
== GDK_KEY_Tab
) {
11850 if (headerentry
->compose
->header_last
== headerentry
) {
11851 /* Override default next focus, and give it to subject_entry
11852 * instead of notebook tabs
11854 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11855 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11862 static gboolean
scroll_postpone(gpointer data
)
11864 Compose
*compose
= (Compose
*)data
;
11866 if (compose
->batch
)
11869 GTK_EVENTS_FLUSH();
11870 compose_show_first_last_header(compose
, FALSE
);
11874 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11875 ComposeHeaderEntry
*headerentry
)
11877 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11878 compose_create_header_entry(headerentry
->compose
);
11879 g_signal_handlers_disconnect_matched
11880 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11881 0, 0, NULL
, NULL
, headerentry
);
11883 if (!headerentry
->compose
->batch
)
11884 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11888 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11890 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11891 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11895 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11897 GtkAdjustment
*vadj
;
11899 cm_return_if_fail(compose
);
11904 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11905 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11906 vadj
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(gtk_widget_get_parent(compose
->header_table
)));
11907 gtk_adjustment_set_value(vadj
, (show_first
?
11908 gtk_adjustment_get_lower(vadj
) :
11909 (gtk_adjustment_get_upper(vadj
) -
11910 gtk_adjustment_get_page_size(vadj
))));
11913 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11914 const gchar
*text
, gint len
, Compose
*compose
)
11916 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11917 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11920 cm_return_if_fail(text
!= NULL
);
11922 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11923 G_CALLBACK(text_inserted
),
11925 if (paste_as_quotation
) {
11927 const gchar
*qmark
;
11929 GtkTextIter start_iter
;
11932 len
= strlen(text
);
11934 new_text
= g_strndup(text
, len
);
11936 qmark
= compose_quote_char_from_context(compose
);
11938 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11939 gtk_text_buffer_place_cursor(buffer
, iter
);
11941 pos
= gtk_text_iter_get_offset(iter
);
11943 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11944 _("Quote format error at line %d."));
11945 quote_fmt_reset_vartable();
11947 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11948 GINT_TO_POINTER(paste_as_quotation
- 1));
11950 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11951 gtk_text_buffer_place_cursor(buffer
, iter
);
11952 gtk_text_buffer_delete_mark(buffer
, mark
);
11954 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11955 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11956 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11957 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11958 gtk_text_buffer_delete_mark(buffer
, mark
);
11960 if (strcmp(text
, "\n") || compose
->automatic_break
11961 || gtk_text_iter_starts_line(iter
)) {
11962 GtkTextIter before_ins
;
11963 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11964 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11965 before_ins
= *iter
;
11966 gtk_text_iter_backward_chars(&before_ins
, len
);
11967 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11970 /* check if the preceding is just whitespace or quote */
11971 GtkTextIter start_line
;
11972 gchar
*tmp
= NULL
, *quote
= NULL
;
11973 gint quote_len
= 0, is_normal
= 0;
11974 start_line
= *iter
;
11975 gtk_text_iter_set_line_offset(&start_line
, 0);
11976 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11979 if (*tmp
== '\0') {
11982 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
11990 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11992 gtk_text_buffer_insert_with_tags_by_name(buffer
,
11993 iter
, text
, len
, "no_join", NULL
);
11998 if (!paste_as_quotation
) {
11999 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
12000 compose_beautify_paragraph(compose
, iter
, FALSE
);
12001 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12002 gtk_text_buffer_delete_mark(buffer
, mark
);
12005 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
12006 G_CALLBACK(text_inserted
),
12008 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
12010 if (compose_can_autosave(compose
) &&
12011 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
12012 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
12013 compose
->draft_timeout_tag
= g_timeout_add
12014 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
12018 static void compose_check_all(GtkAction
*action
, gpointer data
)
12020 Compose
*compose
= (Compose
*)data
;
12021 if (!compose
->gtkaspell
)
12024 if (gtk_widget_has_focus(compose
->subject_entry
))
12025 claws_spell_entry_check_all(
12026 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12028 gtkaspell_check_all(compose
->gtkaspell
);
12031 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12033 Compose
*compose
= (Compose
*)data
;
12034 if (compose
->gtkaspell
) {
12035 claws_spell_entry_recheck_all(
12036 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12037 gtkaspell_highlight_all(compose
->gtkaspell
);
12041 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12043 Compose
*compose
= (Compose
*)data
;
12044 if (!compose
->gtkaspell
) {
12045 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12049 if (gtk_widget_has_focus(compose
->subject_entry
))
12050 claws_spell_entry_check_backwards(
12051 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12053 gtkaspell_check_backwards(compose
->gtkaspell
);
12056 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12058 Compose
*compose
= (Compose
*)data
;
12059 if (!compose
->gtkaspell
) {
12060 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12064 if (gtk_widget_has_focus(compose
->subject_entry
))
12065 claws_spell_entry_check_forwards_go(
12066 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12068 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12073 *\brief Guess originating forward account from MsgInfo and several
12074 * "common preference" settings. Return NULL if no guess.
12076 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12078 PrefsAccount
*account
= NULL
;
12080 cm_return_val_if_fail(msginfo
, NULL
);
12081 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12082 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12084 if (msginfo
->folder
->prefs
->enable_default_account
)
12085 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12087 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12089 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12090 extract_address(to
);
12091 account
= account_find_from_address(to
, FALSE
);
12094 if (!account
&& prefs_common
.forward_account_autosel
) {
12096 if (!procheader_get_header_from_msginfo
12097 (msginfo
, &cc
, "Cc:")) {
12098 gchar
*buf
= cc
+ strlen("Cc:");
12099 extract_address(buf
);
12100 account
= account_find_from_address(buf
, FALSE
);
12105 if (!account
&& prefs_common
.forward_account_autosel
) {
12106 gchar
*deliveredto
= NULL
;
12107 if (!procheader_get_header_from_msginfo
12108 (msginfo
, &deliveredto
, "Delivered-To:")) {
12109 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12110 extract_address(buf
);
12111 account
= account_find_from_address(buf
, FALSE
);
12112 g_free(deliveredto
);
12117 account
= msginfo
->folder
->folder
->account
;
12122 gboolean
compose_close(Compose
*compose
)
12126 cm_return_val_if_fail(compose
, FALSE
);
12128 if (!g_mutex_trylock(&compose
->mutex
)) {
12129 /* we have to wait for the (possibly deferred by auto-save)
12130 * drafting to be done, before destroying the compose under
12132 debug_print("waiting for drafting to finish...\n");
12133 compose_allow_user_actions(compose
, FALSE
);
12134 if (compose
->close_timeout_tag
== 0) {
12135 compose
->close_timeout_tag
=
12136 g_timeout_add (500, (GSourceFunc
) compose_close
,
12142 if (compose
->draft_timeout_tag
>= 0) {
12143 g_source_remove(compose
->draft_timeout_tag
);
12144 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12147 gtk_window_get_position(GTK_WINDOW(compose
->window
), &x
, &y
);
12148 if (!compose
->batch
) {
12149 prefs_common
.compose_x
= x
;
12150 prefs_common
.compose_y
= y
;
12152 g_mutex_unlock(&compose
->mutex
);
12153 compose_destroy(compose
);
12158 * Add entry field for each address in list.
12159 * \param compose E-Mail composition object.
12160 * \param listAddress List of (formatted) E-Mail addresses.
12162 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12165 node
= listAddress
;
12167 addr
= ( gchar
* ) node
->data
;
12168 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12169 node
= g_list_next( node
);
12173 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12174 guint action
, gboolean opening_multiple
)
12176 gchar
*body
= NULL
;
12177 GSList
*new_msglist
= NULL
;
12178 MsgInfo
*tmp_msginfo
= NULL
;
12179 gboolean originally_enc
= FALSE
;
12180 gboolean originally_sig
= FALSE
;
12181 Compose
*compose
= NULL
;
12182 gchar
*s_system
= NULL
;
12184 cm_return_if_fail(msginfo_list
!= NULL
);
12186 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12187 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12188 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12190 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12191 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12192 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12193 orig_msginfo
, mimeinfo
);
12194 if (tmp_msginfo
!= NULL
) {
12195 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12197 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12198 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12199 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12201 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12202 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12203 if (orig_msginfo
->tags
) {
12204 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12205 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12211 if (!opening_multiple
&& msgview
!= NULL
)
12212 body
= messageview_get_selection(msgview
);
12215 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12216 procmsg_msginfo_free(&tmp_msginfo
);
12217 g_slist_free(new_msglist
);
12219 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12221 if (compose
&& originally_enc
) {
12222 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12225 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12226 compose_force_signing(compose
, compose
->account
, s_system
);
12230 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12233 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12236 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12237 && msginfo_list
!= NULL
12238 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12239 GSList
*cur
= msginfo_list
;
12240 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12241 "messages. Opening the windows "
12242 "could take some time. Do you "
12243 "want to continue?"),
12244 g_slist_length(msginfo_list
));
12245 if (g_slist_length(msginfo_list
) > 9
12246 && alertpanel(_("Warning"), msg
, NULL
, _("_Cancel"), NULL
, _("_Yes"),
12247 NULL
, NULL
, ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12252 /* We'll open multiple compose windows */
12253 /* let the WM place the next windows */
12254 compose_force_window_origin
= FALSE
;
12255 for (; cur
; cur
= cur
->next
) {
12257 tmplist
.data
= cur
->data
;
12258 tmplist
.next
= NULL
;
12259 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12261 compose_force_window_origin
= TRUE
;
12263 /* forwarding multiple mails as attachments is done via a
12264 * single compose window */
12265 if (msginfo_list
!= NULL
) {
12266 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12267 } else if (msgview
!= NULL
) {
12269 tmplist
.data
= msgview
->msginfo
;
12270 tmplist
.next
= NULL
;
12271 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12273 debug_print("Nothing to reply to\n");
12278 void compose_check_for_email_account(Compose
*compose
)
12280 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12286 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12287 ac
= account_get_cur_account();
12288 if (ac
->protocol
== A_NNTP
) {
12289 list
= account_get_list();
12291 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12292 curr
= (PrefsAccount
*) list
->data
;
12293 if (curr
->protocol
!= A_NNTP
) {
12299 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12304 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12305 const gchar
*address
)
12307 GSList
*msginfo_list
= NULL
;
12308 gchar
*body
= messageview_get_selection(msgview
);
12311 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12313 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12314 compose_check_for_email_account(compose
);
12315 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12316 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12317 compose_reply_set_subject(compose
, msginfo
);
12320 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12323 void compose_set_position(Compose
*compose
, gint pos
)
12325 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12327 gtkut_text_view_set_position(text
, pos
);
12330 gboolean
compose_search_string(Compose
*compose
,
12331 const gchar
*str
, gboolean case_sens
)
12333 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12335 return gtkut_text_view_search_string(text
, str
, case_sens
);
12338 gboolean
compose_search_string_backward(Compose
*compose
,
12339 const gchar
*str
, gboolean case_sens
)
12341 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12343 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12346 /* allocate a msginfo structure and populate its data from a compose data structure */
12347 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12349 MsgInfo
*newmsginfo
;
12351 gchar date
[RFC822_DATE_BUFFSIZE
];
12353 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12355 newmsginfo
= procmsg_msginfo_new();
12358 get_rfc822_date(date
, sizeof(date
));
12359 newmsginfo
->date
= g_strdup(date
);
12362 if (compose
->from_name
) {
12363 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12364 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12368 if (compose
->subject_entry
)
12369 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12371 /* to, cc, reply-to, newsgroups */
12372 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12373 gchar
*header
= gtk_editable_get_chars(
12375 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12376 gchar
*entry
= gtk_editable_get_chars(
12377 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12379 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12380 if ( newmsginfo
->to
== NULL
) {
12381 newmsginfo
->to
= g_strdup(entry
);
12382 } else if (entry
&& *entry
) {
12383 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12384 g_free(newmsginfo
->to
);
12385 newmsginfo
->to
= tmp
;
12388 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12389 if ( newmsginfo
->cc
== NULL
) {
12390 newmsginfo
->cc
= g_strdup(entry
);
12391 } else if (entry
&& *entry
) {
12392 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12393 g_free(newmsginfo
->cc
);
12394 newmsginfo
->cc
= tmp
;
12397 if ( strcasecmp(header
,
12398 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12399 if ( newmsginfo
->newsgroups
== NULL
) {
12400 newmsginfo
->newsgroups
= g_strdup(entry
);
12401 } else if (entry
&& *entry
) {
12402 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12403 g_free(newmsginfo
->newsgroups
);
12404 newmsginfo
->newsgroups
= tmp
;
12412 /* other data is unset */
12418 /* update compose's dictionaries from folder dict settings */
12419 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12420 FolderItem
*folder_item
)
12422 cm_return_if_fail(compose
!= NULL
);
12424 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12425 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12427 if (prefs
->enable_default_dictionary
)
12428 gtkaspell_change_dict(compose
->gtkaspell
,
12429 prefs
->default_dictionary
, FALSE
);
12430 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12431 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12432 prefs
->default_alt_dictionary
);
12433 if (prefs
->enable_default_dictionary
12434 || prefs
->enable_default_alt_dictionary
)
12435 compose_spell_menu_changed(compose
);
12440 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12442 Compose
*compose
= (Compose
*)data
;
12444 cm_return_if_fail(compose
!= NULL
);
12446 gtk_widget_grab_focus(compose
->text
);
12449 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12451 gtk_combo_box_popup(GTK_COMBO_BOX(data
));