2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2019 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>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
110 #include "file-utils.h"
113 #include "password.h"
114 #include "ldapserver.h"
128 #define N_ATTACH_COLS (N_COL_COLUMNS)
132 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
= -1,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
= 0,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
,
135 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
,
136 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
,
137 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
,
141 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
,
142 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
,
143 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
,
144 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
,
145 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
147 } ComposeCallAdvancedAction
;
151 PRIORITY_HIGHEST
= 1,
160 COMPOSE_INSERT_SUCCESS
,
161 COMPOSE_INSERT_READ_ERROR
,
162 COMPOSE_INSERT_INVALID_CHARACTER
,
163 COMPOSE_INSERT_NO_FILE
164 } ComposeInsertResult
;
168 COMPOSE_WRITE_FOR_SEND
,
169 COMPOSE_WRITE_FOR_STORE
174 COMPOSE_QUOTE_FORCED
,
181 SUBJECT_FIELD_PRESENT
,
186 #define B64_LINE_SIZE 57
187 #define B64_BUFFSIZE 77
189 #define MAX_REFERENCES_LEN 999
191 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
192 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
194 static GdkColor default_header_bgcolor
= {
201 static GdkColor default_header_color
= {
208 static GList
*compose_list
= NULL
;
209 static GSList
*extra_headers
= NULL
;
211 static Compose
*compose_generic_new (PrefsAccount
*account
,
215 GList
*listAddress
);
217 static Compose
*compose_create (PrefsAccount
*account
,
222 static void compose_entry_indicate (Compose
*compose
,
223 const gchar
*address
);
224 static Compose
*compose_followup_and_reply_to (MsgInfo
*msginfo
,
225 ComposeQuoteMode quote_mode
,
229 static Compose
*compose_forward_multiple (PrefsAccount
*account
,
230 GSList
*msginfo_list
);
231 static Compose
*compose_reply (MsgInfo
*msginfo
,
232 ComposeQuoteMode quote_mode
,
237 static Compose
*compose_reply_mode (ComposeMode mode
,
238 GSList
*msginfo_list
,
240 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
);
241 static void compose_update_privacy_systems_menu(Compose
*compose
);
243 static GtkWidget
*compose_account_option_menu_create
245 static void compose_set_out_encoding (Compose
*compose
);
246 static void compose_set_template_menu (Compose
*compose
);
247 static void compose_destroy (Compose
*compose
);
249 static MailField
compose_entries_set (Compose
*compose
,
251 ComposeEntryType to_type
);
252 static gint
compose_parse_header (Compose
*compose
,
254 static gint
compose_parse_manual_headers (Compose
*compose
,
256 HeaderEntry
*entries
);
257 static gchar
*compose_parse_references (const gchar
*ref
,
260 static gchar
*compose_quote_fmt (Compose
*compose
,
266 gboolean need_unescape
,
267 const gchar
*err_msg
);
269 static void compose_reply_set_entry (Compose
*compose
,
275 followup_and_reply_to
);
276 static void compose_reedit_set_entry (Compose
*compose
,
279 static void compose_insert_sig (Compose
*compose
,
281 static ComposeInsertResult
compose_insert_file (Compose
*compose
,
284 static gboolean
compose_attach_append (Compose
*compose
,
287 const gchar
*content_type
,
288 const gchar
*charset
);
289 static void compose_attach_parts (Compose
*compose
,
292 static gboolean
compose_beautify_paragraph (Compose
*compose
,
293 GtkTextIter
*par_iter
,
295 static void compose_wrap_all (Compose
*compose
);
296 static void compose_wrap_all_full (Compose
*compose
,
299 static void compose_set_title (Compose
*compose
);
300 static void compose_select_account (Compose
*compose
,
301 PrefsAccount
*account
,
304 static PrefsAccount
*compose_current_mail_account(void);
305 /* static gint compose_send (Compose *compose); */
306 static gboolean compose_check_for_valid_recipient
308 static gboolean
compose_check_entries (Compose
*compose
,
309 gboolean check_everything
);
310 static gint
compose_write_to_file (Compose
*compose
,
313 gboolean attach_parts
);
314 static gint
compose_write_body_to_file (Compose
*compose
,
316 static gint
compose_remove_reedit_target (Compose
*compose
,
318 static void compose_remove_draft (Compose
*compose
);
319 static ComposeQueueResult
compose_queue_sub (Compose
*compose
,
323 gboolean perform_checks
,
324 gboolean remove_reedit_target
);
325 static int compose_add_attachments (Compose
*compose
,
327 static gchar
*compose_get_header (Compose
*compose
);
328 static gchar
*compose_get_manual_headers_info (Compose
*compose
);
330 static void compose_convert_header (Compose
*compose
,
335 gboolean addr_field
);
337 static void compose_attach_info_free (AttachInfo
*ainfo
);
338 static void compose_attach_remove_selected (GtkAction
*action
,
341 static void compose_template_apply (Compose
*compose
,
344 static void compose_attach_property (GtkAction
*action
,
346 static void compose_attach_property_create (gboolean
*cancelled
);
347 static void attach_property_ok (GtkWidget
*widget
,
348 gboolean
*cancelled
);
349 static void attach_property_cancel (GtkWidget
*widget
,
350 gboolean
*cancelled
);
351 static gint
attach_property_delete_event (GtkWidget
*widget
,
353 gboolean
*cancelled
);
354 static gboolean
attach_property_key_pressed (GtkWidget
*widget
,
356 gboolean
*cancelled
);
358 static void compose_exec_ext_editor (Compose
*compose
);
360 static gint
compose_exec_ext_editor_real (const gchar
*file
,
361 GdkNativeWindow socket_wid
);
362 static gboolean
compose_ext_editor_kill (Compose
*compose
);
363 static gboolean
compose_input_cb (GIOChannel
*source
,
364 GIOCondition condition
,
366 static void compose_set_ext_editor_sensitive (Compose
*compose
,
368 static gboolean
compose_get_ext_editor_cmd_valid();
369 static gboolean
compose_get_ext_editor_uses_socket();
370 static gboolean compose_ext_editor_plug_removed_cb
373 #endif /* G_OS_UNIX */
375 static void compose_undo_state_changed (UndoMain
*undostruct
,
380 static void compose_create_header_entry (Compose
*compose
);
381 static void compose_add_header_entry (Compose
*compose
, const gchar
*header
,
382 gchar
*text
, ComposePrefType pref_type
);
383 static void compose_remove_header_entries(Compose
*compose
);
385 static void compose_update_priority_menu_item(Compose
* compose
);
387 static void compose_spell_menu_changed (void *data
);
388 static void compose_dict_changed (void *data
);
390 static void compose_add_field_list ( Compose
*compose
,
391 GList
*listAddress
);
393 /* callback functions */
395 static void compose_notebook_size_alloc (GtkNotebook
*notebook
,
396 GtkAllocation
*allocation
,
398 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
399 GtkAllocation
*allocation
,
400 GtkSHRuler
*shruler
);
401 static void account_activated (GtkComboBox
*optmenu
,
403 static void attach_selected (GtkTreeView
*tree_view
,
404 GtkTreePath
*tree_path
,
405 GtkTreeViewColumn
*column
,
407 static gboolean
attach_button_pressed (GtkWidget
*widget
,
408 GdkEventButton
*event
,
410 static gboolean
attach_key_pressed (GtkWidget
*widget
,
413 static void compose_send_cb (GtkAction
*action
, gpointer data
);
414 static void compose_send_later_cb (GtkAction
*action
, gpointer data
);
416 static void compose_save_cb (GtkAction
*action
,
419 static void compose_attach_cb (GtkAction
*action
,
421 static void compose_insert_file_cb (GtkAction
*action
,
423 static void compose_insert_sig_cb (GtkAction
*action
,
425 static void compose_replace_sig_cb (GtkAction
*action
,
428 static void compose_close_cb (GtkAction
*action
,
430 static void compose_print_cb (GtkAction
*action
,
433 static void compose_set_encoding_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
435 static void compose_address_cb (GtkAction
*action
,
437 static void about_show_cb (GtkAction
*action
,
439 static void compose_template_activate_cb(GtkWidget
*widget
,
442 static void compose_ext_editor_cb (GtkAction
*action
,
445 static gint
compose_delete_cb (GtkWidget
*widget
,
449 static void compose_undo_cb (GtkAction
*action
,
451 static void compose_redo_cb (GtkAction
*action
,
453 static void compose_cut_cb (GtkAction
*action
,
455 static void compose_copy_cb (GtkAction
*action
,
457 static void compose_paste_cb (GtkAction
*action
,
459 static void compose_paste_as_quote_cb (GtkAction
*action
,
461 static void compose_paste_no_wrap_cb (GtkAction
*action
,
463 static void compose_paste_wrap_cb (GtkAction
*action
,
465 static void compose_allsel_cb (GtkAction
*action
,
468 static void compose_advanced_action_cb (GtkAction
*action
,
471 static void compose_grab_focus_cb (GtkWidget
*widget
,
474 static void compose_changed_cb (GtkTextBuffer
*textbuf
,
477 static void compose_wrap_cb (GtkAction
*action
,
479 static void compose_wrap_all_cb (GtkAction
*action
,
481 static void compose_find_cb (GtkAction
*action
,
483 static void compose_toggle_autowrap_cb (GtkToggleAction
*action
,
485 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
488 static void compose_toggle_ruler_cb (GtkToggleAction
*action
,
490 static void compose_toggle_sign_cb (GtkToggleAction
*action
,
492 static void compose_toggle_encrypt_cb (GtkToggleAction
*action
,
494 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
);
495 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
);
496 static void compose_activate_privacy_system (Compose
*compose
,
497 PrefsAccount
*account
,
499 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
);
500 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
,
502 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
,
504 static void compose_set_priority_cb (GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
505 static void compose_reply_change_mode (Compose
*compose
, ComposeMode action
);
506 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
508 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
509 GdkDragContext
*drag_context
,
512 GtkSelectionData
*data
,
516 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
517 GdkDragContext
*drag_context
,
520 GtkSelectionData
*data
,
524 static void compose_header_drag_received_cb (GtkWidget
*widget
,
525 GdkDragContext
*drag_context
,
528 GtkSelectionData
*data
,
533 static gboolean
compose_drag_drop (GtkWidget
*widget
,
534 GdkDragContext
*drag_context
,
536 guint time
, gpointer user_data
);
537 static gboolean completion_set_focus_to_subject
542 static void text_inserted (GtkTextBuffer
*buffer
,
547 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
548 ComposeQuoteMode quote_mode
,
552 gboolean followup_and_reply_to
,
555 static void compose_headerentry_changed_cb (GtkWidget
*entry
,
556 ComposeHeaderEntry
*headerentry
);
557 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
559 ComposeHeaderEntry
*headerentry
);
560 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
561 ComposeHeaderEntry
*headerentry
);
563 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
565 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
);
567 static void compose_nothing_cb (GtkAction
*action
, gpointer data
)
573 static void compose_check_all (GtkAction
*action
, gpointer data
);
574 static void compose_highlight_all (GtkAction
*action
, gpointer data
);
575 static void compose_check_backwards (GtkAction
*action
, gpointer data
);
576 static void compose_check_forwards_go (GtkAction
*action
, gpointer data
);
579 static PrefsAccount
*compose_find_account (MsgInfo
*msginfo
);
581 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
);
584 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
585 FolderItem
*folder_item
);
587 static void compose_attach_update_label(Compose
*compose
);
588 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
589 gboolean respect_default_to
);
590 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
);
591 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
);
593 static GtkActionEntry compose_popup_entries
[] =
595 {"Compose", NULL
, "Compose", NULL
, NULL
, NULL
},
596 {"Compose/Add", NULL
, N_("_Add..."), NULL
, NULL
, G_CALLBACK(compose_attach_cb
) },
597 {"Compose/Remove", NULL
, N_("_Remove"), NULL
, NULL
, G_CALLBACK(compose_attach_remove_selected
) },
598 {"Compose/---", NULL
, "---", NULL
, NULL
, NULL
},
599 {"Compose/Properties", NULL
, N_("_Properties..."), NULL
, NULL
, G_CALLBACK(compose_attach_property
) },
602 static GtkActionEntry compose_entries
[] =
604 {"Menu", NULL
, "Menu", NULL
, NULL
, NULL
},
606 {"Message", NULL
, N_("_Message"), NULL
, NULL
, NULL
},
607 {"Edit", NULL
, N_("_Edit"), NULL
, NULL
, NULL
},
609 {"Spelling", NULL
, N_("_Spelling"), NULL
, NULL
, NULL
},
611 {"Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
612 {"Tools", NULL
, N_("_Tools"), NULL
, NULL
, NULL
},
613 {"Help", NULL
, N_("_Help"), NULL
, NULL
, NULL
},
615 {"Message/Send", NULL
, N_("S_end"), "<control>Return", NULL
, G_CALLBACK(compose_send_cb
) },
616 {"Message/SendLater", NULL
, N_("Send _later"), "<shift><control>S", NULL
, G_CALLBACK(compose_send_later_cb
) },
617 {"Message/---", NULL
, "---", NULL
, NULL
, NULL
},
619 {"Message/AttachFile", NULL
, N_("_Attach file"), "<control>M", NULL
, G_CALLBACK(compose_attach_cb
) },
620 {"Message/InsertFile", NULL
, N_("_Insert file"), "<control>I", NULL
, G_CALLBACK(compose_insert_file_cb
) },
621 {"Message/InsertSig", NULL
, N_("Insert si_gnature"), "<control>G", NULL
, G_CALLBACK(compose_insert_sig_cb
) },
622 {"Message/ReplaceSig", NULL
, N_("_Replace signature"), NULL
, NULL
, G_CALLBACK(compose_replace_sig_cb
) },
623 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
624 {"Message/Save", NULL
, N_("_Save"), "<control>S", NULL
, G_CALLBACK(compose_save_cb
) }, /*COMPOSE_KEEP_EDITING*/
625 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
626 {"Message/Print", NULL
, N_("_Print"), NULL
, NULL
, G_CALLBACK(compose_print_cb
) },
627 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
628 {"Message/Close", NULL
, N_("_Close"), "<control>W", NULL
, G_CALLBACK(compose_close_cb
) },
631 {"Edit/Undo", NULL
, N_("_Undo"), "<control>Z", NULL
, G_CALLBACK(compose_undo_cb
) },
632 {"Edit/Redo", NULL
, N_("_Redo"), "<control>Y", NULL
, G_CALLBACK(compose_redo_cb
) },
633 {"Edit/---", NULL
, "---", NULL
, NULL
, NULL
},
635 {"Edit/Cut", NULL
, N_("Cu_t"), "<control>X", NULL
, G_CALLBACK(compose_cut_cb
) },
636 {"Edit/Copy", NULL
, N_("_Copy"), "<control>C", NULL
, G_CALLBACK(compose_copy_cb
) },
637 {"Edit/Paste", NULL
, N_("_Paste"), "<control>V", NULL
, G_CALLBACK(compose_paste_cb
) },
639 {"Edit/SpecialPaste", NULL
, N_("_Special paste"), NULL
, NULL
, NULL
},
640 {"Edit/SpecialPaste/AsQuotation", NULL
, N_("As _quotation"), NULL
, NULL
, G_CALLBACK(compose_paste_as_quote_cb
) },
641 {"Edit/SpecialPaste/Wrapped", NULL
, N_("_Wrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_wrap_cb
) },
642 {"Edit/SpecialPaste/Unwrapped", NULL
, N_("_Unwrapped"), NULL
, NULL
, G_CALLBACK(compose_paste_no_wrap_cb
) },
644 {"Edit/SelectAll", NULL
, N_("Select _all"), "<control>A", NULL
, G_CALLBACK(compose_allsel_cb
) },
646 {"Edit/Advanced", NULL
, N_("A_dvanced"), NULL
, NULL
, NULL
},
647 {"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*/
648 {"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*/
649 {"Edit/Advanced/BackWord", NULL
, N_("Move a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
650 {"Edit/Advanced/ForwWord", NULL
, N_("Move a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
651 {"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*/
652 {"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*/
653 {"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*/
654 {"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*/
655 {"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*/
656 {"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*/
657 {"Edit/Advanced/DelBackWord", NULL
, N_("Delete a word backward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
658 {"Edit/Advanced/DelForwWord", NULL
, N_("Delete a word forward"), NULL
, NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
659 {"Edit/Advanced/DelLine", NULL
, N_("Delete line"), "<control>U", NULL
, G_CALLBACK(compose_advanced_action_cb
) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
660 {"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*/
662 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
663 {"Edit/Find", NULL
, N_("_Find"), "<control>F", NULL
, G_CALLBACK(compose_find_cb
) },
665 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
666 {"Edit/WrapPara", NULL
, N_("_Wrap current paragraph"), "<control>L", NULL
, G_CALLBACK(compose_wrap_cb
) }, /* 0 */
667 {"Edit/WrapAllLines", NULL
, N_("Wrap all long _lines"), "<control><alt>L", NULL
, G_CALLBACK(compose_wrap_all_cb
) }, /* 1 */
668 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
669 {"Edit/ExtEditor", NULL
, N_("Edit with e_xternal editor"), "<shift><control>X", NULL
, G_CALLBACK(compose_ext_editor_cb
) },
672 {"Spelling/CheckAllSel", NULL
, N_("_Check all or check selection"), NULL
, NULL
, G_CALLBACK(compose_check_all
) },
673 {"Spelling/HighlightAll", NULL
, N_("_Highlight all misspelled words"), NULL
, NULL
, G_CALLBACK(compose_highlight_all
) },
674 {"Spelling/CheckBackwards", NULL
, N_("Check _backwards misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_backwards
) },
675 {"Spelling/ForwardNext", NULL
, N_("_Forward to next misspelled word"), NULL
, NULL
, G_CALLBACK(compose_check_forwards_go
) },
677 {"Spelling/---", NULL
, "---", NULL
, NULL
, NULL
},
678 {"Spelling/Options", NULL
, N_("_Options"), NULL
, NULL
, NULL
},
682 {"Options/ReplyMode", NULL
, N_("Reply _mode"), NULL
, NULL
, NULL
},
683 {"Options/---", NULL
, "---", NULL
, NULL
, NULL
},
684 {"Options/PrivacySystem", NULL
, N_("Privacy _System"), NULL
, NULL
, NULL
},
685 {"Options/PrivacySystem/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
687 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
688 {"Options/Priority", NULL
, N_("_Priority"), NULL
, NULL
, NULL
},
690 {"Options/Encoding", NULL
, N_("Character _encoding"), NULL
, NULL
, NULL
},
691 {"Options/Encoding/---", NULL
, "---", NULL
, NULL
, NULL
},
692 #define ENC_ACTION(cs_char,c_char,string) \
693 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
695 {"Options/Encoding/Western", NULL
, N_("Western European"), NULL
, NULL
, NULL
},
696 {"Options/Encoding/Baltic", NULL
, N_("Baltic"), NULL
, NULL
, NULL
},
697 {"Options/Encoding/Hebrew", NULL
, N_("Hebrew"), NULL
, NULL
, NULL
},
698 {"Options/Encoding/Arabic", NULL
, N_("Arabic"), NULL
, NULL
, NULL
},
699 {"Options/Encoding/Cyrillic", NULL
, N_("Cyrillic"), NULL
, NULL
, NULL
},
700 {"Options/Encoding/Japanese", NULL
, N_("Japanese"), NULL
, NULL
, NULL
},
701 {"Options/Encoding/Chinese", NULL
, N_("Chinese"), NULL
, NULL
, NULL
},
702 {"Options/Encoding/Korean", NULL
, N_("Korean"), NULL
, NULL
, NULL
},
703 {"Options/Encoding/Thai", NULL
, N_("Thai"), NULL
, NULL
, NULL
},
706 {"Tools/AddressBook", NULL
, N_("_Address book"), NULL
, NULL
, G_CALLBACK(compose_address_cb
) },
708 {"Tools/Template", NULL
, N_("_Template"), NULL
, NULL
, NULL
},
709 {"Tools/Template/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
710 {"Tools/Actions", NULL
, N_("Actio_ns"), NULL
, NULL
, NULL
},
711 {"Tools/Actions/PlaceHolder", NULL
, "Placeholder", NULL
, NULL
, G_CALLBACK(compose_nothing_cb
) },
714 {"Help/About", NULL
, N_("_About"), NULL
, NULL
, G_CALLBACK(about_show_cb
) },
717 static GtkToggleActionEntry compose_toggle_entries
[] =
719 {"Edit/AutoWrap", NULL
, N_("Aut_o wrapping"), "<shift><control>L", NULL
, G_CALLBACK(compose_toggle_autowrap_cb
), FALSE
}, /* Toggle */
720 {"Edit/AutoIndent", NULL
, N_("Auto _indent"), NULL
, NULL
, G_CALLBACK(compose_toggle_autoindent_cb
), FALSE
}, /* Toggle */
721 {"Options/Sign", NULL
, N_("Si_gn"), NULL
, NULL
, G_CALLBACK(compose_toggle_sign_cb
), FALSE
}, /* Toggle */
722 {"Options/Encrypt", NULL
, N_("_Encrypt"), NULL
, NULL
, G_CALLBACK(compose_toggle_encrypt_cb
), FALSE
}, /* Toggle */
723 {"Options/RequestRetRcpt", NULL
, N_("_Request Return Receipt"), NULL
, NULL
, G_CALLBACK(compose_toggle_return_receipt_cb
), FALSE
}, /* Toggle */
724 {"Options/RemoveReferences", NULL
, N_("Remo_ve references"), NULL
, NULL
, G_CALLBACK(compose_toggle_remove_refs_cb
), FALSE
}, /* Toggle */
725 {"Tools/ShowRuler", NULL
, N_("Show _ruler"), NULL
, NULL
, G_CALLBACK(compose_toggle_ruler_cb
), FALSE
}, /* Toggle */
728 static GtkRadioActionEntry compose_radio_rm_entries
[] =
730 {"Options/ReplyMode/Normal", NULL
, N_("_Normal"), NULL
, NULL
, COMPOSE_REPLY
}, /* RADIO compose_reply_change_mode_cb */
731 {"Options/ReplyMode/All", NULL
, N_("_All"), NULL
, NULL
, COMPOSE_REPLY_TO_ALL
}, /* RADIO compose_reply_change_mode_cb */
732 {"Options/ReplyMode/Sender", NULL
, N_("_Sender"), NULL
, NULL
, COMPOSE_REPLY_TO_SENDER
}, /* RADIO compose_reply_change_mode_cb */
733 {"Options/ReplyMode/List", NULL
, N_("_Mailing-list"), NULL
, NULL
, COMPOSE_REPLY_TO_LIST
}, /* RADIO compose_reply_change_mode_cb */
736 static GtkRadioActionEntry compose_radio_prio_entries
[] =
738 {"Options/Priority/Highest", NULL
, N_("_Highest"), NULL
, NULL
, PRIORITY_HIGHEST
}, /* RADIO compose_set_priority_cb */
739 {"Options/Priority/High", NULL
, N_("Hi_gh"), NULL
, NULL
, PRIORITY_HIGH
}, /* RADIO compose_set_priority_cb */
740 {"Options/Priority/Normal", NULL
, N_("_Normal"), NULL
, NULL
, PRIORITY_NORMAL
}, /* RADIO compose_set_priority_cb */
741 {"Options/Priority/Low", NULL
, N_("Lo_w"), NULL
, NULL
, PRIORITY_LOW
}, /* RADIO compose_set_priority_cb */
742 {"Options/Priority/Lowest", NULL
, N_("_Lowest"), NULL
, NULL
, PRIORITY_LOWEST
}, /* RADIO compose_set_priority_cb */
745 static GtkRadioActionEntry compose_radio_enc_entries
[] =
747 ENC_ACTION(CS_AUTO
, C_AUTO
, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION(CS_US_ASCII
, C_US_ASCII
, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION(CS_UTF_8
, C_UTF_8
, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Western/"CS_ISO_8859_1
, C_ISO_8859_1
, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Western/"CS_ISO_8859_15
, C_ISO_8859_15
, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Western/"CS_WINDOWS_1252
, C_WINDOWS_1252
, "Windows-1252"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION(CS_ISO_8859_2
, C_ISO_8859_2
, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Baltic/"CS_ISO_8859_13
, C_ISO_8859_13
, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Baltic/"CS_ISO_8859_4
, C_ISO_8859_14
, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_ISO_8859_7
, C_ISO_8859_7
, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Hebrew/"CS_ISO_8859_8
, C_ISO_8859_8
, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Hebrew/"CS_WINDOWS_1255
, C_WINDOWS_1255
, "Windows-1255"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Arabic/"CS_ISO_8859_6
, C_ISO_8859_6
, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Arabic/"CS_WINDOWS_1256
, C_WINDOWS_1256
, "Windows-1256"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION(CS_ISO_8859_9
, C_ISO_8859_9
, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Cyrillic/"CS_ISO_8859_5
, C_ISO_8859_5
, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Cyrillic/"CS_KOI8_R
, C_KOI8_R
, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Cyrillic/"CS_MACCYR
, C_MACCYR
, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Cyrillic/"CS_KOI8_U
, C_KOI8_U
, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251
, C_WINDOWS_1251
, "Windows-1251"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Japanese/"CS_ISO_2022_JP
, C_ISO_2022_JP
, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2
, C_ISO_2022_JP_2
, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Japanese/"CS_EUC_JP
, C_EUC_JP
, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Japanese/"CS_SHIFT_JIS
, C_SHIFT_JIS
, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Chinese/"CS_GB18030
, C_GB18030
, "_GB18030"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Chinese/"CS_GB2312
, C_GB2312
, "_GB2312"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Chinese/"CS_GBK
, C_GBK
, "GB_K"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Chinese/"CS_BIG5
, C_BIG5
, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Chinese/"CS_EUC_TW
, C_EUC_TW
, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Korean/"CS_EUC_KR
, C_EUC_KR
, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
777 ENC_ACTION("Korean/"CS_ISO_2022_KR
, C_ISO_2022_KR
, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
778 ENC_ACTION("Thai/"CS_TIS_620
, C_TIS_620
, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
779 ENC_ACTION("Thai/"CS_WINDOWS_874
, C_WINDOWS_874
, "_Windows-874"), /* RADIO compose_set_encoding_cb */
782 static GtkTargetEntry compose_mime_types
[] =
784 {"text/uri-list", 0, 0},
785 {"UTF8_STRING", 0, 0},
789 static gboolean
compose_put_existing_to_front(MsgInfo
*info
)
791 const GList
*compose_list
= compose_get_compose_list();
792 const GList
*elem
= NULL
;
795 for (elem
= compose_list
; elem
!= NULL
&& elem
->data
!= NULL
;
797 Compose
*c
= (Compose
*)elem
->data
;
799 if (!c
->targetinfo
|| !c
->targetinfo
->msgid
||
803 if (!strcmp(c
->targetinfo
->msgid
, info
->msgid
)) {
804 gtkut_window_popup(c
->window
);
812 static GdkColor quote_color1
=
813 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
814 static GdkColor quote_color2
=
815 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
816 static GdkColor quote_color3
=
817 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
819 static GdkColor quote_bgcolor1
=
820 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
821 static GdkColor quote_bgcolor2
=
822 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
823 static GdkColor quote_bgcolor3
=
824 {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
826 static GdkColor signature_color
= {
833 static GdkColor uri_color
= {
840 static void compose_create_tags(GtkTextView
*text
, Compose
*compose
)
842 GtkTextBuffer
*buffer
;
843 GdkColor black
= {(gulong
)0, (gushort
)0, (gushort
)0, (gushort
)0};
845 buffer
= gtk_text_view_get_buffer(text
);
847 if (prefs_common
.enable_color
) {
848 /* grab the quote colors, converting from an int to a GdkColor */
849 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL1
],
851 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL2
],
853 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL3
],
855 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL1_BG
],
857 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL2_BG
],
859 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_QUOTE_LEVEL3_BG
],
861 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_SIGNATURE
],
863 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_URI
],
866 signature_color
= quote_color1
= quote_color2
= quote_color3
=
867 quote_bgcolor1
= quote_bgcolor2
= quote_bgcolor3
= uri_color
= black
;
870 if (prefs_common
.enable_color
&& prefs_common
.enable_bgcolor
) {
871 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
872 "foreground-gdk", "e_color1
,
873 "paragraph-background-gdk", "e_bgcolor1
,
875 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
876 "foreground-gdk", "e_color2
,
877 "paragraph-background-gdk", "e_bgcolor2
,
879 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
880 "foreground-gdk", "e_color3
,
881 "paragraph-background-gdk", "e_bgcolor3
,
884 compose
->quote0_tag
= gtk_text_buffer_create_tag(buffer
, "quote0",
885 "foreground-gdk", "e_color1
,
887 compose
->quote1_tag
= gtk_text_buffer_create_tag(buffer
, "quote1",
888 "foreground-gdk", "e_color2
,
890 compose
->quote2_tag
= gtk_text_buffer_create_tag(buffer
, "quote2",
891 "foreground-gdk", "e_color3
,
895 compose
->signature_tag
= gtk_text_buffer_create_tag(buffer
, "signature",
896 "foreground-gdk", &signature_color
,
899 compose
->uri_tag
= gtk_text_buffer_create_tag(buffer
, "link",
900 "foreground-gdk", &uri_color
,
902 compose
->no_wrap_tag
= gtk_text_buffer_create_tag(buffer
, "no_wrap", NULL
);
903 compose
->no_join_tag
= gtk_text_buffer_create_tag(buffer
, "no_join", NULL
);
906 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
909 return compose_generic_new(account
, mailto
, NULL
, attach_files
, NULL
);
912 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
, const gchar
*mailto
)
914 return compose_generic_new(account
, mailto
, item
, NULL
, NULL
);
917 Compose
*compose_new_with_list( PrefsAccount
*account
, GList
*listAddress
)
919 return compose_generic_new( account
, NULL
, NULL
, NULL
, listAddress
);
922 #define SCROLL_TO_CURSOR(compose) { \
923 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
924 gtk_text_view_get_buffer( \
925 GTK_TEXT_VIEW(compose->text))); \
926 gtk_text_view_scroll_mark_onscreen( \
927 GTK_TEXT_VIEW(compose->text), \
931 static void compose_set_save_to(Compose
*compose
, const gchar
*folderidentifier
)
934 if (folderidentifier
) {
935 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
936 prefs_common
.compose_save_to_history
= add_history(
937 prefs_common
.compose_save_to_history
, folderidentifier
);
938 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
939 prefs_common
.compose_save_to_history
);
942 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
943 if (folderidentifier
)
944 gtk_entry_set_text(GTK_ENTRY(entry
), folderidentifier
);
946 gtk_entry_set_text(GTK_ENTRY(entry
), "");
949 static gchar
*compose_get_save_to(Compose
*compose
)
952 gchar
*result
= NULL
;
953 entry
= GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose
->savemsg_combo
)));
954 result
= gtk_editable_get_chars(entry
, 0, -1);
957 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
));
958 prefs_common
.compose_save_to_history
= add_history(
959 prefs_common
.compose_save_to_history
, result
);
960 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose
->savemsg_combo
),
961 prefs_common
.compose_save_to_history
);
966 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
967 GList
*attach_files
, GList
*listAddress
)
970 GtkTextView
*textview
;
971 GtkTextBuffer
*textbuf
;
973 const gchar
*subject_format
= NULL
;
974 const gchar
*body_format
= NULL
;
975 gchar
*mailto_from
= NULL
;
976 PrefsAccount
*mailto_account
= NULL
;
977 MsgInfo
* dummyinfo
= NULL
;
978 gint cursor_pos
= -1;
979 MailField mfield
= NO_FIELD_PRESENT
;
983 /* check if mailto defines a from */
984 if (mailto
&& *mailto
!= '\0') {
985 scan_mailto_url(mailto
, &mailto_from
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
986 /* mailto defines a from, check if we can get account prefs from it,
987 if not, the account prefs will be guessed using other ways, but we'll keep
990 mailto_account
= account_find_from_address(mailto_from
, TRUE
);
991 if (mailto_account
== NULL
) {
993 Xstrdup_a(tmp_from
, mailto_from
, return NULL
);
994 extract_address(tmp_from
);
995 mailto_account
= account_find_from_address(tmp_from
, TRUE
);
999 account
= mailto_account
;
1002 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1003 if (!mailto_account
&& item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
1004 account
= account_find_from_id(item
->prefs
->default_account
);
1006 /* if no account prefs set, fallback to the current one */
1007 if (!account
) account
= cur_account
;
1008 cm_return_val_if_fail(account
!= NULL
, NULL
);
1010 compose
= compose_create(account
, item
, COMPOSE_NEW
, FALSE
);
1011 compose_apply_folder_privacy_settings(compose
, item
);
1013 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1014 (account
->default_encrypt
|| account
->default_sign
))
1015 alertpanel_error(_("You have opted to sign and/or encrypt this "
1016 "message but have not selected a privacy system.\n\n"
1017 "Signing and encrypting have been disabled for this "
1020 /* override from name if mailto asked for it */
1022 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), mailto_from
);
1023 g_free(mailto_from
);
1025 /* override from name according to folder properties */
1026 if (item
&& item
->prefs
&&
1027 item
->prefs
->compose_with_format
&&
1028 item
->prefs
->compose_override_from_format
&&
1029 *item
->prefs
->compose_override_from_format
!= '\0') {
1034 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1036 /* decode \-escape sequences in the internal representation of the quote format */
1037 tmp
= g_malloc(strlen(item
->prefs
->compose_override_from_format
)+1);
1038 pref_get_unescaped_pref(tmp
, item
->prefs
->compose_override_from_format
);
1041 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1042 compose
->gtkaspell
);
1044 quote_fmt_init(dummyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1046 quote_fmt_scan_string(tmp
);
1049 buf
= quote_fmt_get_buffer();
1051 alertpanel_error(_("New message From format error."));
1053 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1054 quote_fmt_reset_vartable();
1055 quote_fmtlex_destroy();
1060 compose
->replyinfo
= NULL
;
1061 compose
->fwdinfo
= NULL
;
1063 textview
= GTK_TEXT_VIEW(compose
->text
);
1064 textbuf
= gtk_text_view_get_buffer(textview
);
1065 compose_create_tags(textview
, compose
);
1067 undo_block(compose
->undostruct
);
1069 compose_set_dictionaries_from_folder_prefs(compose
, item
);
1072 if (account
->auto_sig
)
1073 compose_insert_sig(compose
, FALSE
);
1074 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
1075 gtk_text_buffer_place_cursor(textbuf
, &iter
);
1077 if (account
->protocol
!= A_NNTP
) {
1078 if (mailto
&& *mailto
!= '\0') {
1079 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1082 compose_set_folder_prefs(compose
, item
, TRUE
);
1084 if (item
&& item
->ret_rcpt
) {
1085 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1088 if (mailto
&& *mailto
!= '\0') {
1089 if (!strchr(mailto
, '@'))
1090 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_NEWSGROUPS
);
1092 mfield
= compose_entries_set(compose
, mailto
, COMPOSE_TO
);
1093 } else if (item
&& FOLDER_CLASS(item
->folder
) == news_get_class()) {
1094 compose_entry_append(compose
, item
->path
, COMPOSE_NEWSGROUPS
, PREF_FOLDER
);
1095 mfield
= TO_FIELD_PRESENT
;
1098 * CLAWS: just don't allow return receipt request, even if the user
1099 * may want to send an email. simple but foolproof.
1101 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", FALSE
);
1103 compose_add_field_list( compose
, listAddress
);
1105 if (item
&& item
->prefs
&& item
->prefs
->compose_with_format
) {
1106 subject_format
= item
->prefs
->compose_subject_format
;
1107 body_format
= item
->prefs
->compose_body_format
;
1108 } else if (account
->compose_with_format
) {
1109 subject_format
= account
->compose_subject_format
;
1110 body_format
= account
->compose_body_format
;
1111 } else if (prefs_common
.compose_with_format
) {
1112 subject_format
= prefs_common
.compose_subject_format
;
1113 body_format
= prefs_common
.compose_body_format
;
1116 if (subject_format
|| body_format
) {
1119 && *subject_format
!= '\0' )
1121 gchar
*subject
= NULL
;
1126 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1128 /* decode \-escape sequences in the internal representation of the quote format */
1129 tmp
= g_malloc(strlen(subject_format
)+1);
1130 pref_get_unescaped_pref(tmp
, subject_format
);
1132 subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1134 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
,
1135 compose
->gtkaspell
);
1137 quote_fmt_init(dummyinfo
, NULL
, subject
, FALSE
, compose
->account
, FALSE
);
1139 quote_fmt_scan_string(tmp
);
1142 buf
= quote_fmt_get_buffer();
1144 alertpanel_error(_("New message subject format error."));
1146 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1147 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1148 quote_fmt_reset_vartable();
1149 quote_fmtlex_destroy();
1153 mfield
= SUBJECT_FIELD_PRESENT
;
1157 && *body_format
!= '\0' )
1160 GtkTextBuffer
*buffer
;
1161 GtkTextIter start
, end
;
1165 dummyinfo
= compose_msginfo_new_from_compose(compose
);
1167 text
= GTK_TEXT_VIEW(compose
->text
);
1168 buffer
= gtk_text_view_get_buffer(text
);
1169 gtk_text_buffer_get_start_iter(buffer
, &start
);
1170 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
1171 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
1173 compose_quote_fmt(compose
, dummyinfo
,
1175 NULL
, tmp
, FALSE
, TRUE
,
1176 _("The body of the \"New message\" template has an error at line %d."));
1177 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1178 quote_fmt_reset_vartable();
1182 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1183 gtkaspell_highlight_all(compose
->gtkaspell
);
1185 mfield
= BODY_FIELD_PRESENT
;
1189 procmsg_msginfo_free( &dummyinfo
);
1195 for (curr
= attach_files
; curr
!= NULL
; curr
= curr
->next
) {
1196 ainfo
= (AttachInfo
*) curr
->data
;
1198 compose_insert_file(compose
, ainfo
->file
);
1200 compose_attach_append(compose
, ainfo
->file
, ainfo
->file
,
1201 ainfo
->content_type
, ainfo
->charset
);
1205 compose_show_first_last_header(compose
, TRUE
);
1207 /* Set save folder */
1208 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
1209 gchar
*folderidentifier
;
1211 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1212 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1213 folderidentifier
= folder_item_get_identifier(item
);
1214 compose_set_save_to(compose
, folderidentifier
);
1215 g_free(folderidentifier
);
1218 /* Place cursor according to provided input (mfield) */
1220 case NO_FIELD_PRESENT
:
1221 if (compose
->header_last
)
1222 gtk_widget_grab_focus(compose
->header_last
->entry
);
1224 case TO_FIELD_PRESENT
:
1225 buf
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
1227 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
1230 gtk_widget_grab_focus(compose
->subject_entry
);
1232 case SUBJECT_FIELD_PRESENT
:
1233 textview
= GTK_TEXT_VIEW(compose
->text
);
1236 textbuf
= gtk_text_view_get_buffer(textview
);
1239 mark
= gtk_text_buffer_get_insert(textbuf
);
1240 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
1241 gtk_text_buffer_insert(textbuf
, &iter
, "", -1);
1243 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1244 * only defers where it comes to the variable body
1245 * is not null. If no body is present compose->text
1246 * will be null in which case you cannot place the
1247 * cursor inside the component so. An empty component
1248 * is therefore created before placing the cursor
1250 case BODY_FIELD_PRESENT
:
1251 cursor_pos
= quote_fmt_get_cursor_pos();
1252 if (cursor_pos
== -1)
1253 gtk_widget_grab_focus(compose
->header_last
->entry
);
1255 gtk_widget_grab_focus(compose
->text
);
1259 undo_unblock(compose
->undostruct
);
1261 if (prefs_common
.auto_exteditor
)
1262 compose_exec_ext_editor(compose
);
1264 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
1266 SCROLL_TO_CURSOR(compose
);
1268 compose
->modified
= FALSE
;
1269 compose_set_title(compose
);
1271 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1276 static void compose_force_encryption(Compose
*compose
, PrefsAccount
*account
,
1277 gboolean override_pref
, const gchar
*system
)
1279 const gchar
*privacy
= NULL
;
1281 cm_return_if_fail(compose
!= NULL
);
1282 cm_return_if_fail(account
!= NULL
);
1284 if (privacy_system_can_encrypt(compose
->privacy_system
) == FALSE
||
1285 (override_pref
== FALSE
&& account
->default_encrypt_reply
== FALSE
))
1288 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1289 privacy
= account
->default_privacy_system
;
1293 GSList
*privacy_avail
= privacy_get_system_ids();
1294 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1295 privacy
= (gchar
*)(privacy_avail
->data
);
1297 g_slist_free_full(privacy_avail
, g_free
);
1299 if (privacy
!= NULL
) {
1301 g_free(compose
->privacy_system
);
1302 compose
->privacy_system
= NULL
;
1303 g_free(compose
->encdata
);
1304 compose
->encdata
= NULL
;
1306 if (compose
->privacy_system
== NULL
)
1307 compose
->privacy_system
= g_strdup(privacy
);
1308 else if (*(compose
->privacy_system
) == '\0') {
1309 g_free(compose
->privacy_system
);
1310 g_free(compose
->encdata
);
1311 compose
->encdata
= NULL
;
1312 compose
->privacy_system
= g_strdup(privacy
);
1314 compose_update_privacy_system_menu_item(compose
, FALSE
);
1315 compose_use_encryption(compose
, TRUE
);
1319 static void compose_force_signing(Compose
*compose
, PrefsAccount
*account
, const gchar
*system
)
1321 const gchar
*privacy
= NULL
;
1322 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
)
1325 if (account
->default_privacy_system
&& strlen(account
->default_privacy_system
))
1326 privacy
= account
->default_privacy_system
;
1330 GSList
*privacy_avail
= privacy_get_system_ids();
1331 if (privacy_avail
&& g_slist_length(privacy_avail
)) {
1332 privacy
= (gchar
*)(privacy_avail
->data
);
1336 if (privacy
!= NULL
) {
1338 g_free(compose
->privacy_system
);
1339 compose
->privacy_system
= NULL
;
1340 g_free(compose
->encdata
);
1341 compose
->encdata
= NULL
;
1343 if (compose
->privacy_system
== NULL
)
1344 compose
->privacy_system
= g_strdup(privacy
);
1345 compose_update_privacy_system_menu_item(compose
, FALSE
);
1346 compose_use_signing(compose
, TRUE
);
1350 static Compose
*compose_reply_mode(ComposeMode mode
, GSList
*msginfo_list
, gchar
*body
)
1354 Compose
*compose
= NULL
;
1356 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1358 msginfo
= (MsgInfo
*)g_slist_nth_data(msginfo_list
, 0);
1359 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1361 list_len
= g_slist_length(msginfo_list
);
1365 case COMPOSE_REPLY_TO_ADDRESS
:
1366 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1367 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1369 case COMPOSE_REPLY_WITH_QUOTE
:
1370 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1371 FALSE
, prefs_common
.default_reply_list
, FALSE
, body
);
1373 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1374 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1375 FALSE
, prefs_common
.default_reply_list
, FALSE
, NULL
);
1377 case COMPOSE_REPLY_TO_SENDER
:
1378 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1379 FALSE
, FALSE
, TRUE
, body
);
1381 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1382 compose
= compose_followup_and_reply_to(msginfo
,
1383 COMPOSE_QUOTE_CHECK
,
1384 FALSE
, FALSE
, body
);
1386 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1387 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1388 FALSE
, FALSE
, TRUE
, body
);
1390 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1391 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1392 FALSE
, FALSE
, TRUE
, NULL
);
1394 case COMPOSE_REPLY_TO_ALL
:
1395 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1396 TRUE
, FALSE
, FALSE
, body
);
1398 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1399 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1400 TRUE
, FALSE
, FALSE
, body
);
1402 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1403 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1404 TRUE
, FALSE
, FALSE
, NULL
);
1406 case COMPOSE_REPLY_TO_LIST
:
1407 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_CHECK
,
1408 FALSE
, TRUE
, FALSE
, body
);
1410 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1411 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_FORCED
,
1412 FALSE
, TRUE
, FALSE
, body
);
1414 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1415 compose
= compose_reply(msginfo
, COMPOSE_QUOTE_SKIP
,
1416 FALSE
, TRUE
, FALSE
, NULL
);
1418 case COMPOSE_FORWARD
:
1419 if (prefs_common
.forward_as_attachment
) {
1420 compose
= compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH
, msginfo_list
, body
);
1423 compose
= compose_reply_mode(COMPOSE_FORWARD_INLINE
, msginfo_list
, body
);
1427 case COMPOSE_FORWARD_INLINE
:
1428 /* check if we reply to more than one Message */
1429 if (list_len
== 1) {
1430 compose
= compose_forward(NULL
, msginfo
, FALSE
, body
, FALSE
, FALSE
);
1433 /* more messages FALL THROUGH */
1434 case COMPOSE_FORWARD_AS_ATTACH
:
1435 compose
= compose_forward_multiple(NULL
, msginfo_list
);
1437 case COMPOSE_REDIRECT
:
1438 compose
= compose_redirect(NULL
, msginfo
, FALSE
);
1441 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode
);
1444 if (compose
== NULL
) {
1445 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1449 compose
->rmode
= mode
;
1450 switch (compose
->rmode
) {
1452 case COMPOSE_REPLY_WITH_QUOTE
:
1453 case COMPOSE_REPLY_WITHOUT_QUOTE
:
1454 case COMPOSE_FOLLOWUP_AND_REPLY_TO
:
1455 debug_print("reply mode Normal\n");
1456 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Normal", TRUE
);
1457 compose_reply_change_mode(compose
, COMPOSE_REPLY
); /* force update */
1459 case COMPOSE_REPLY_TO_SENDER
:
1460 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE
:
1461 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE
:
1462 debug_print("reply mode Sender\n");
1463 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/Sender", TRUE
);
1465 case COMPOSE_REPLY_TO_ALL
:
1466 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE
:
1467 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE
:
1468 debug_print("reply mode All\n");
1469 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/All", TRUE
);
1471 case COMPOSE_REPLY_TO_LIST
:
1472 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE
:
1473 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE
:
1474 debug_print("reply mode List\n");
1475 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/ReplyMode/List", TRUE
);
1477 case COMPOSE_REPLY_TO_ADDRESS
:
1478 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", FALSE
);
1486 static Compose
*compose_reply(MsgInfo
*msginfo
,
1487 ComposeQuoteMode quote_mode
,
1493 return compose_generic_reply(msginfo
, quote_mode
, to_all
, to_ml
,
1494 to_sender
, FALSE
, body
);
1497 static Compose
*compose_followup_and_reply_to(MsgInfo
*msginfo
,
1498 ComposeQuoteMode quote_mode
,
1503 return compose_generic_reply(msginfo
, quote_mode
, to_all
, FALSE
,
1504 to_sender
, TRUE
, body
);
1507 static void compose_extract_original_charset(Compose
*compose
)
1509 MsgInfo
*info
= NULL
;
1510 if (compose
->replyinfo
) {
1511 info
= compose
->replyinfo
;
1512 } else if (compose
->fwdinfo
) {
1513 info
= compose
->fwdinfo
;
1514 } else if (compose
->targetinfo
) {
1515 info
= compose
->targetinfo
;
1518 MimeInfo
*mimeinfo
= procmime_scan_message_short(info
);
1519 MimeInfo
*partinfo
= mimeinfo
;
1520 while (partinfo
&& partinfo
->type
!= MIMETYPE_TEXT
)
1521 partinfo
= procmime_mimeinfo_next(partinfo
);
1523 compose
->orig_charset
=
1524 g_strdup(procmime_mimeinfo_get_parameter(
1525 partinfo
, "charset"));
1527 procmime_mimeinfo_free_all(&mimeinfo
);
1531 #define SIGNAL_BLOCK(buffer) { \
1532 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1533 G_CALLBACK(compose_changed_cb), \
1535 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1536 G_CALLBACK(text_inserted), \
1540 #define SIGNAL_UNBLOCK(buffer) { \
1541 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1542 G_CALLBACK(compose_changed_cb), \
1544 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1545 G_CALLBACK(text_inserted), \
1549 static Compose
*compose_generic_reply(MsgInfo
*msginfo
,
1550 ComposeQuoteMode quote_mode
,
1551 gboolean to_all
, gboolean to_ml
,
1553 gboolean followup_and_reply_to
,
1557 PrefsAccount
*account
= NULL
;
1558 GtkTextView
*textview
;
1559 GtkTextBuffer
*textbuf
;
1560 gboolean quote
= FALSE
;
1561 const gchar
*qmark
= NULL
;
1562 const gchar
*body_fmt
= NULL
;
1563 gchar
*s_system
= NULL
;
1565 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1566 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1568 account
= account_get_reply_account(msginfo
, prefs_common
.reply_account_autosel
);
1570 cm_return_val_if_fail(account
!= NULL
, NULL
);
1572 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REPLY
, FALSE
);
1573 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1575 compose
->updating
= TRUE
;
1577 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
1578 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", TRUE
);
1580 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
1581 if (!compose
->replyinfo
)
1582 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
1584 compose_extract_original_charset(compose
);
1586 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
)
1587 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
1589 /* Set save folder */
1590 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1591 gchar
*folderidentifier
;
1593 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1594 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1595 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1596 compose_set_save_to(compose
, folderidentifier
);
1597 g_free(folderidentifier
);
1600 if (compose_parse_header(compose
, msginfo
) < 0) {
1601 compose
->updating
= FALSE
;
1602 compose_destroy(compose
);
1606 /* override from name according to folder properties */
1607 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1608 msginfo
->folder
->prefs
->reply_with_format
&&
1609 msginfo
->folder
->prefs
->reply_override_from_format
&&
1610 *msginfo
->folder
->prefs
->reply_override_from_format
!= '\0') {
1615 /* decode \-escape sequences in the internal representation of the quote format */
1616 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->reply_override_from_format
)+1);
1617 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->reply_override_from_format
);
1620 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1621 compose
->gtkaspell
);
1623 quote_fmt_init(compose
->replyinfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1625 quote_fmt_scan_string(tmp
);
1628 buf
= quote_fmt_get_buffer();
1630 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1632 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1633 quote_fmt_reset_vartable();
1634 quote_fmtlex_destroy();
1639 textview
= (GTK_TEXT_VIEW(compose
->text
));
1640 textbuf
= gtk_text_view_get_buffer(textview
);
1641 compose_create_tags(textview
, compose
);
1643 undo_block(compose
->undostruct
);
1645 compose_set_dictionaries_from_folder_prefs(compose
, msginfo
->folder
);
1646 gtkaspell_block_check(compose
->gtkaspell
);
1649 if (quote_mode
== COMPOSE_QUOTE_FORCED
||
1650 (quote_mode
== COMPOSE_QUOTE_CHECK
&& prefs_common
.reply_with_quote
)) {
1651 /* use the reply format of folder (if enabled), or the account's one
1652 (if enabled) or fallback to the global reply format, which is always
1653 enabled (even if empty), and use the relevant quotemark */
1655 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1656 msginfo
->folder
->prefs
->reply_with_format
) {
1657 qmark
= msginfo
->folder
->prefs
->reply_quotemark
;
1658 body_fmt
= msginfo
->folder
->prefs
->reply_body_format
;
1660 } else if (account
->reply_with_format
) {
1661 qmark
= account
->reply_quotemark
;
1662 body_fmt
= account
->reply_body_format
;
1665 qmark
= prefs_common
.quotemark
;
1666 if (prefs_common
.quotefmt
&& *prefs_common
.quotefmt
)
1667 body_fmt
= gettext(prefs_common
.quotefmt
);
1674 /* empty quotemark is not allowed */
1675 if (qmark
== NULL
|| *qmark
== '\0')
1677 compose_quote_fmt(compose
, compose
->replyinfo
,
1678 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1679 _("The body of the \"Reply\" template has an error at line %d."));
1680 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1681 quote_fmt_reset_vartable();
1684 if (MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) {
1685 compose_force_encryption(compose
, account
, FALSE
, s_system
);
1688 privacy_msginfo_get_signed_state(compose
->replyinfo
, &s_system
);
1689 if (MSG_IS_SIGNED(compose
->replyinfo
->flags
) && account
->default_sign_reply
) {
1690 compose_force_signing(compose
, account
, s_system
);
1694 if (privacy_system_can_sign(compose
->privacy_system
) == FALSE
&&
1695 ((account
->default_encrypt_reply
&& MSG_IS_ENCRYPTED(compose
->replyinfo
->flags
)) ||
1696 (account
->default_sign_reply
&& MSG_IS_SIGNED(compose
->replyinfo
->flags
))))
1697 alertpanel_error(_("You have opted to sign and/or encrypt this "
1698 "message but have not selected a privacy system.\n\n"
1699 "Signing and encrypting have been disabled for this "
1702 SIGNAL_BLOCK(textbuf
);
1704 if (account
->auto_sig
)
1705 compose_insert_sig(compose
, FALSE
);
1707 compose_wrap_all(compose
);
1710 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1711 gtkaspell_highlight_all(compose
->gtkaspell
);
1712 gtkaspell_unblock_check(compose
->gtkaspell
);
1714 SIGNAL_UNBLOCK(textbuf
);
1716 gtk_widget_grab_focus(compose
->text
);
1718 undo_unblock(compose
->undostruct
);
1720 if (prefs_common
.auto_exteditor
)
1721 compose_exec_ext_editor(compose
);
1723 compose
->modified
= FALSE
;
1724 compose_set_title(compose
);
1726 compose
->updating
= FALSE
;
1727 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1728 SCROLL_TO_CURSOR(compose
);
1730 if (compose
->deferred_destroy
) {
1731 compose_destroy(compose
);
1739 #define INSERT_FW_HEADER(var, hdr) \
1740 if (msginfo->var && *msginfo->var) { \
1741 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1742 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1743 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1746 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
1747 gboolean as_attach
, const gchar
*body
,
1748 gboolean no_extedit
,
1752 GtkTextView
*textview
;
1753 GtkTextBuffer
*textbuf
;
1754 gint cursor_pos
= -1;
1757 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
1758 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
1760 if (!account
&& !(account
= compose_find_account(msginfo
)))
1761 account
= cur_account
;
1763 if (!prefs_common
.forward_as_attachment
)
1764 mode
= COMPOSE_FORWARD_INLINE
;
1766 mode
= COMPOSE_FORWARD
;
1767 compose
= compose_create(account
, msginfo
->folder
, mode
, batch
);
1768 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
1770 compose
->updating
= TRUE
;
1771 compose
->fwdinfo
= procmsg_msginfo_get_full_info(msginfo
);
1772 if (!compose
->fwdinfo
)
1773 compose
->fwdinfo
= procmsg_msginfo_copy(msginfo
);
1775 compose_extract_original_charset(compose
);
1777 if (msginfo
->subject
&& *msginfo
->subject
) {
1778 gchar
*buf
, *buf2
, *p
;
1780 buf
= p
= g_strdup(msginfo
->subject
);
1781 p
+= subject_get_prefix_length(p
);
1782 memmove(buf
, p
, strlen(p
) + 1);
1784 buf2
= g_strdup_printf("Fw: %s", buf
);
1785 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1791 /* override from name according to folder properties */
1792 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1793 msginfo
->folder
->prefs
->forward_with_format
&&
1794 msginfo
->folder
->prefs
->forward_override_from_format
&&
1795 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
1799 MsgInfo
*full_msginfo
= NULL
;
1802 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1804 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1806 /* decode \-escape sequences in the internal representation of the quote format */
1807 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
1808 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
1811 gtkaspell_block_check(compose
->gtkaspell
);
1812 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
1813 compose
->gtkaspell
);
1815 quote_fmt_init(full_msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
1817 quote_fmt_scan_string(tmp
);
1820 buf
= quote_fmt_get_buffer();
1822 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1824 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
1825 quote_fmt_reset_vartable();
1826 quote_fmtlex_destroy();
1829 procmsg_msginfo_free(&full_msginfo
);
1832 textview
= GTK_TEXT_VIEW(compose
->text
);
1833 textbuf
= gtk_text_view_get_buffer(textview
);
1834 compose_create_tags(textview
, compose
);
1836 undo_block(compose
->undostruct
);
1840 msgfile
= procmsg_get_message_file(msginfo
);
1841 if (!is_file_exist(msgfile
))
1842 g_warning("%s: file does not exist", msgfile
);
1844 compose_attach_append(compose
, msgfile
, msgfile
,
1845 "message/rfc822", NULL
);
1849 const gchar
*qmark
= NULL
;
1850 const gchar
*body_fmt
= NULL
;
1851 MsgInfo
*full_msginfo
;
1853 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1855 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1857 /* use the forward format of folder (if enabled), or the account's one
1858 (if enabled) or fallback to the global forward format, which is always
1859 enabled (even if empty), and use the relevant quotemark */
1860 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1861 msginfo
->folder
->prefs
->forward_with_format
) {
1862 qmark
= msginfo
->folder
->prefs
->forward_quotemark
;
1863 body_fmt
= msginfo
->folder
->prefs
->forward_body_format
;
1865 } else if (account
->forward_with_format
) {
1866 qmark
= account
->forward_quotemark
;
1867 body_fmt
= account
->forward_body_format
;
1870 qmark
= prefs_common
.fw_quotemark
;
1871 if (prefs_common
.fw_quotefmt
&& *prefs_common
.fw_quotefmt
)
1872 body_fmt
= gettext(prefs_common
.fw_quotefmt
);
1877 /* empty quotemark is not allowed */
1878 if (qmark
== NULL
|| *qmark
== '\0')
1881 compose_quote_fmt(compose
, full_msginfo
,
1882 body_fmt
, qmark
, body
, FALSE
, TRUE
,
1883 _("The body of the \"Forward\" template has an error at line %d."));
1884 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
1885 quote_fmt_reset_vartable();
1886 compose_attach_parts(compose
, msginfo
);
1888 procmsg_msginfo_free(&full_msginfo
);
1891 SIGNAL_BLOCK(textbuf
);
1893 if (account
->auto_sig
)
1894 compose_insert_sig(compose
, FALSE
);
1896 compose_wrap_all(compose
);
1899 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
1900 gtkaspell_highlight_all(compose
->gtkaspell
);
1901 gtkaspell_unblock_check(compose
->gtkaspell
);
1903 SIGNAL_UNBLOCK(textbuf
);
1905 cursor_pos
= quote_fmt_get_cursor_pos();
1906 if (cursor_pos
== -1)
1907 gtk_widget_grab_focus(compose
->header_last
->entry
);
1909 gtk_widget_grab_focus(compose
->text
);
1911 if (!no_extedit
&& prefs_common
.auto_exteditor
)
1912 compose_exec_ext_editor(compose
);
1915 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1916 gchar
*folderidentifier
;
1918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1919 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
1920 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1921 compose_set_save_to(compose
, folderidentifier
);
1922 g_free(folderidentifier
);
1925 undo_unblock(compose
->undostruct
);
1927 compose
->modified
= FALSE
;
1928 compose_set_title(compose
);
1930 compose
->updating
= FALSE
;
1931 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
1932 SCROLL_TO_CURSOR(compose
);
1934 if (compose
->deferred_destroy
) {
1935 compose_destroy(compose
);
1939 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
1944 #undef INSERT_FW_HEADER
1946 static Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1949 GtkTextView
*textview
;
1950 GtkTextBuffer
*textbuf
;
1954 gboolean single_mail
= TRUE
;
1956 cm_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1958 if (g_slist_length(msginfo_list
) > 1)
1959 single_mail
= FALSE
;
1961 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
)
1962 if (((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1965 /* guess account from first selected message */
1967 !(account
= compose_find_account(msginfo_list
->data
)))
1968 account
= cur_account
;
1970 cm_return_val_if_fail(account
!= NULL
, NULL
);
1972 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1973 if (msginfo
->data
) {
1974 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1975 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1979 if (msginfo_list
== NULL
|| msginfo_list
->data
== NULL
) {
1980 g_warning("no msginfo_list");
1984 compose
= compose_create(account
, ((MsgInfo
*)msginfo_list
->data
)->folder
, COMPOSE_FORWARD
, FALSE
);
1985 compose_apply_folder_privacy_settings(compose
, ((MsgInfo
*)msginfo_list
->data
)->folder
);
1987 compose
->updating
= TRUE
;
1989 /* override from name according to folder properties */
1990 if (msginfo_list
->data
) {
1991 MsgInfo
*msginfo
= msginfo_list
->data
;
1993 if (msginfo
->folder
&& msginfo
->folder
->prefs
&&
1994 msginfo
->folder
->prefs
->forward_with_format
&&
1995 msginfo
->folder
->prefs
->forward_override_from_format
&&
1996 *msginfo
->folder
->prefs
->forward_override_from_format
!= '\0') {
2001 /* decode \-escape sequences in the internal representation of the quote format */
2002 tmp
= g_malloc(strlen(msginfo
->folder
->prefs
->forward_override_from_format
)+1);
2003 pref_get_unescaped_pref(tmp
, msginfo
->folder
->prefs
->forward_override_from_format
);
2006 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
2007 compose
->gtkaspell
);
2009 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
2011 quote_fmt_scan_string(tmp
);
2014 buf
= quote_fmt_get_buffer();
2016 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2018 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
2019 quote_fmt_reset_vartable();
2020 quote_fmtlex_destroy();
2026 textview
= GTK_TEXT_VIEW(compose
->text
);
2027 textbuf
= gtk_text_view_get_buffer(textview
);
2028 compose_create_tags(textview
, compose
);
2030 undo_block(compose
->undostruct
);
2031 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
2032 msgfile
= procmsg_get_message_file((MsgInfo
*)msginfo
->data
);
2034 if (!is_file_exist(msgfile
))
2035 g_warning("%s: file does not exist", msgfile
);
2037 compose_attach_append(compose
, msgfile
, msgfile
,
2038 "message/rfc822", NULL
);
2043 MsgInfo
*info
= (MsgInfo
*)msginfo_list
->data
;
2044 if (info
->subject
&& *info
->subject
) {
2045 gchar
*buf
, *buf2
, *p
;
2047 buf
= p
= g_strdup(info
->subject
);
2048 p
+= subject_get_prefix_length(p
);
2049 memmove(buf
, p
, strlen(p
) + 1);
2051 buf2
= g_strdup_printf("Fw: %s", buf
);
2052 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
2058 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2059 _("Fw: multiple emails"));
2062 SIGNAL_BLOCK(textbuf
);
2064 if (account
->auto_sig
)
2065 compose_insert_sig(compose
, FALSE
);
2067 compose_wrap_all(compose
);
2069 SIGNAL_UNBLOCK(textbuf
);
2071 gtk_text_buffer_get_start_iter(textbuf
, &iter
);
2072 gtk_text_buffer_place_cursor(textbuf
, &iter
);
2074 if (prefs_common
.auto_exteditor
)
2075 compose_exec_ext_editor(compose
);
2077 gtk_widget_grab_focus(compose
->header_last
->entry
);
2078 undo_unblock(compose
->undostruct
);
2079 compose
->modified
= FALSE
;
2080 compose_set_title(compose
);
2082 compose
->updating
= FALSE
;
2083 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2084 SCROLL_TO_CURSOR(compose
);
2086 if (compose
->deferred_destroy
) {
2087 compose_destroy(compose
);
2091 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2096 static gboolean
compose_is_sig_separator(Compose
*compose
, GtkTextBuffer
*textbuf
, GtkTextIter
*iter
)
2098 GtkTextIter start
= *iter
;
2099 GtkTextIter end_iter
;
2100 int start_pos
= gtk_text_iter_get_offset(&start
);
2102 if (!compose
->account
->sig_sep
)
2105 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2106 start_pos
+strlen(compose
->account
->sig_sep
));
2108 /* check sig separator */
2109 str
= gtk_text_iter_get_text(&start
, &end_iter
);
2110 if (!strcmp(str
, compose
->account
->sig_sep
)) {
2112 /* check end of line (\n) */
2113 gtk_text_buffer_get_iter_at_offset(textbuf
, &start
,
2114 start_pos
+strlen(compose
->account
->sig_sep
));
2115 gtk_text_buffer_get_iter_at_offset(textbuf
, &end_iter
,
2116 start_pos
+strlen(compose
->account
->sig_sep
)+1);
2117 tmp
= gtk_text_iter_get_text(&start
, &end_iter
);
2118 if (!strcmp(tmp
,"\n")) {
2130 static gboolean
compose_update_folder_hook(gpointer source
, gpointer data
)
2132 FolderUpdateData
*hookdata
= (FolderUpdateData
*)source
;
2133 Compose
*compose
= (Compose
*)data
;
2134 FolderItem
*old_item
= NULL
;
2135 FolderItem
*new_item
= NULL
;
2136 gchar
*old_id
, *new_id
;
2138 if (!(hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
2139 && !(hookdata
->update_flags
& FOLDER_MOVE_FOLDERITEM
))
2142 old_item
= hookdata
->item
;
2143 new_item
= hookdata
->item2
;
2145 old_id
= folder_item_get_identifier(old_item
);
2146 new_id
= new_item
? folder_item_get_identifier(new_item
) : g_strdup("NULL");
2148 if (compose
->targetinfo
&& compose
->targetinfo
->folder
== old_item
) {
2149 debug_print("updating targetinfo folder: %s -> %s\n", old_id
, new_id
);
2150 compose
->targetinfo
->folder
= new_item
;
2153 if (compose
->replyinfo
&& compose
->replyinfo
->folder
== old_item
) {
2154 debug_print("updating replyinfo folder: %s -> %s\n", old_id
, new_id
);
2155 compose
->replyinfo
->folder
= new_item
;
2158 if (compose
->fwdinfo
&& compose
->fwdinfo
->folder
== old_item
) {
2159 debug_print("updating fwdinfo folder: %s -> %s\n", old_id
, new_id
);
2160 compose
->fwdinfo
->folder
= new_item
;
2168 static void compose_colorize_signature(Compose
*compose
)
2170 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
2172 GtkTextIter end_iter
;
2173 gtk_text_buffer_get_start_iter(buffer
, &iter
);
2174 while (gtk_text_iter_forward_line(&iter
))
2175 if (compose_is_sig_separator(compose
, buffer
, &iter
)) {
2176 gtk_text_buffer_get_end_iter(buffer
, &end_iter
);
2177 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &end_iter
);
2181 #define BLOCK_WRAP() { \
2182 prev_autowrap = compose->autowrap; \
2183 buffer = gtk_text_view_get_buffer( \
2184 GTK_TEXT_VIEW(compose->text)); \
2185 compose->autowrap = FALSE; \
2187 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2188 G_CALLBACK(compose_changed_cb), \
2190 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2191 G_CALLBACK(text_inserted), \
2194 #define UNBLOCK_WRAP() { \
2195 compose->autowrap = prev_autowrap; \
2196 if (compose->autowrap) { \
2197 gint old = compose->draft_timeout_tag; \
2198 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2199 compose_wrap_all(compose); \
2200 compose->draft_timeout_tag = old; \
2203 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2204 G_CALLBACK(compose_changed_cb), \
2206 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2207 G_CALLBACK(text_inserted), \
2211 Compose
*compose_reedit(MsgInfo
*msginfo
, gboolean batch
)
2213 Compose
*compose
= NULL
;
2214 PrefsAccount
*account
= NULL
;
2215 GtkTextView
*textview
;
2216 GtkTextBuffer
*textbuf
;
2220 gboolean use_signing
= FALSE
;
2221 gboolean use_encryption
= FALSE
;
2222 gchar
*privacy_system
= NULL
;
2223 int priority
= PRIORITY_NORMAL
;
2224 MsgInfo
*replyinfo
= NULL
, *fwdinfo
= NULL
;
2225 gboolean autowrap
= prefs_common
.autowrap
;
2226 gboolean autoindent
= prefs_common
.auto_indent
;
2227 HeaderEntry
*manual_headers
= NULL
;
2229 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2230 cm_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
2232 if (compose_put_existing_to_front(msginfo
)) {
2236 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2237 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2238 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2239 gchar
*queueheader_buf
= NULL
;
2242 /* Select Account from queue headers */
2243 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2244 "X-Claws-Account-Id:")) {
2245 id
= atoi(&queueheader_buf
[strlen("X-Claws-Account-Id:")]);
2246 account
= account_find_from_id(id
);
2247 g_free(queueheader_buf
);
2249 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2250 "X-Sylpheed-Account-Id:")) {
2251 id
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Account-Id:")]);
2252 account
= account_find_from_id(id
);
2253 g_free(queueheader_buf
);
2255 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2257 id
= atoi(&queueheader_buf
[strlen("NAID:")]);
2258 account
= account_find_from_id(id
);
2259 g_free(queueheader_buf
);
2261 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2263 id
= atoi(&queueheader_buf
[strlen("MAID:")]);
2264 account
= account_find_from_id(id
);
2265 g_free(queueheader_buf
);
2267 if (!account
&& !procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2269 account
= account_find_from_address(queueheader_buf
, FALSE
);
2270 g_free(queueheader_buf
);
2272 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2274 param
= atoi(&queueheader_buf
[strlen("X-Claws-Sign:")]);
2275 use_signing
= param
;
2276 g_free(queueheader_buf
);
2278 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2279 "X-Sylpheed-Sign:")) {
2280 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Sign:")]);
2281 use_signing
= param
;
2282 g_free(queueheader_buf
);
2284 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2285 "X-Claws-Encrypt:")) {
2286 param
= atoi(&queueheader_buf
[strlen("X-Claws-Encrypt:")]);
2287 use_encryption
= param
;
2288 g_free(queueheader_buf
);
2290 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2291 "X-Sylpheed-Encrypt:")) {
2292 param
= atoi(&queueheader_buf
[strlen("X-Sylpheed-Encrypt:")]);
2293 use_encryption
= param
;
2294 g_free(queueheader_buf
);
2296 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2297 "X-Claws-Auto-Wrapping:")) {
2298 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Wrapping:")]);
2300 g_free(queueheader_buf
);
2302 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2303 "X-Claws-Auto-Indent:")) {
2304 param
= atoi(&queueheader_buf
[strlen("X-Claws-Auto-Indent:")]);
2306 g_free(queueheader_buf
);
2308 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2309 "X-Claws-Privacy-System:")) {
2310 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Claws-Privacy-System:")]);
2311 g_free(queueheader_buf
);
2313 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2314 "X-Sylpheed-Privacy-System:")) {
2315 privacy_system
= g_strdup(&queueheader_buf
[strlen("X-Sylpheed-Privacy-System:")]);
2316 g_free(queueheader_buf
);
2318 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2320 param
= atoi(&queueheader_buf
[strlen("X-Priority: ")]); /* mind the space */
2322 g_free(queueheader_buf
);
2324 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2326 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("RMID:")], "\t", 0);
2327 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2328 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2329 if (orig_item
!= NULL
) {
2330 replyinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2334 g_free(queueheader_buf
);
2336 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2338 gchar
**tokens
= g_strsplit(&queueheader_buf
[strlen("FMID:")], "\t", 0);
2339 if (tokens
&& tokens
[0] && tokens
[1] && tokens
[2]) {
2340 FolderItem
*orig_item
= folder_find_item_from_identifier(tokens
[0]);
2341 if (orig_item
!= NULL
) {
2342 fwdinfo
= folder_item_get_msginfo_by_msgid(orig_item
, tokens
[2]);
2346 g_free(queueheader_buf
);
2348 /* Get manual headers */
2349 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
,
2350 "X-Claws-Manual-Headers:")) {
2351 gchar
*listmh
= g_strdup(&queueheader_buf
[strlen("X-Claws-Manual-Headers:")]);
2352 if (listmh
&& *listmh
!= '\0') {
2353 debug_print("Got manual headers: %s\n", listmh
);
2354 manual_headers
= procheader_entries_from_str(listmh
);
2357 g_free(queueheader_buf
);
2360 account
= msginfo
->folder
->folder
->account
;
2363 if (!account
&& prefs_common
.reedit_account_autosel
) {
2365 if (!procheader_get_header_from_msginfo(msginfo
, &from
, "FROM:")) {
2366 extract_address(from
);
2367 account
= account_find_from_address(from
, FALSE
);
2372 account
= cur_account
;
2374 cm_return_val_if_fail(account
!= NULL
, NULL
);
2376 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REEDIT
, batch
);
2378 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", autowrap
);
2379 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", autoindent
);
2380 compose
->autowrap
= autowrap
;
2381 compose
->replyinfo
= replyinfo
;
2382 compose
->fwdinfo
= fwdinfo
;
2384 compose
->updating
= TRUE
;
2385 compose
->priority
= priority
;
2387 if (privacy_system
!= NULL
) {
2388 compose
->privacy_system
= privacy_system
;
2389 compose_use_signing(compose
, use_signing
);
2390 compose_use_encryption(compose
, use_encryption
);
2391 compose_update_privacy_system_menu_item(compose
, FALSE
);
2393 compose_activate_privacy_system(compose
, account
, FALSE
);
2395 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
2397 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
2398 compose
->targetinfo
->tags
= g_slist_copy(msginfo
->tags
);
2400 compose_extract_original_charset(compose
);
2402 if (folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) ||
2403 folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
) ||
2404 folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
)) {
2405 gchar
*queueheader_buf
= NULL
;
2407 /* Set message save folder */
2408 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "SCF:")) {
2409 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2410 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2411 compose_set_save_to(compose
, &queueheader_buf
[4]);
2412 g_free(queueheader_buf
);
2414 if (!procheader_get_header_from_msginfo(msginfo
, &queueheader_buf
, "RRCPT:")) {
2415 gint active
= atoi(&queueheader_buf
[strlen("RRCPT:")]);
2417 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/RequestRetRcpt", TRUE
);
2419 g_free(queueheader_buf
);
2423 if (compose_parse_header(compose
, msginfo
) < 0) {
2424 compose
->updating
= FALSE
;
2425 compose_destroy(compose
);
2428 compose_reedit_set_entry(compose
, msginfo
);
2430 textview
= GTK_TEXT_VIEW(compose
->text
);
2431 textbuf
= gtk_text_view_get_buffer(textview
);
2432 compose_create_tags(textview
, compose
);
2434 mark
= gtk_text_buffer_get_insert(textbuf
);
2435 gtk_text_buffer_get_iter_at_mark(textbuf
, &iter
, mark
);
2437 g_signal_handlers_block_by_func(G_OBJECT(textbuf
),
2438 G_CALLBACK(compose_changed_cb
),
2441 if (MSG_IS_ENCRYPTED(msginfo
->flags
)) {
2442 fp
= procmime_get_first_encrypted_text_content(msginfo
);
2444 compose_force_encryption(compose
, account
, TRUE
, NULL
);
2447 fp
= procmime_get_first_text_content(msginfo
);
2450 g_warning("Can't get text part");
2454 gchar buf
[BUFFSIZE
];
2455 gboolean prev_autowrap
;
2456 GtkTextBuffer
*buffer
;
2458 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
2460 gtk_text_buffer_insert(textbuf
, &iter
, buf
, -1);
2466 compose_attach_parts(compose
, msginfo
);
2468 compose_colorize_signature(compose
);
2470 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf
),
2471 G_CALLBACK(compose_changed_cb
),
2474 if (manual_headers
!= NULL
) {
2475 if (compose_parse_manual_headers(compose
, msginfo
, manual_headers
) < 0) {
2476 procheader_entries_free(manual_headers
);
2477 compose
->updating
= FALSE
;
2478 compose_destroy(compose
);
2481 procheader_entries_free(manual_headers
);
2484 gtk_widget_grab_focus(compose
->text
);
2486 if (prefs_common
.auto_exteditor
) {
2487 compose_exec_ext_editor(compose
);
2489 compose
->modified
= FALSE
;
2490 compose_set_title(compose
);
2492 compose
->updating
= FALSE
;
2493 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2494 SCROLL_TO_CURSOR(compose
);
2496 if (compose
->deferred_destroy
) {
2497 compose_destroy(compose
);
2501 compose
->sig_str
= account_get_signature_str(compose
->account
);
2503 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2508 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
,
2515 cm_return_val_if_fail(msginfo
!= NULL
, NULL
);
2518 account
= account_get_reply_account(msginfo
,
2519 prefs_common
.reply_account_autosel
);
2520 cm_return_val_if_fail(account
!= NULL
, NULL
);
2522 compose
= compose_create(account
, msginfo
->folder
, COMPOSE_REDIRECT
, batch
);
2523 compose_apply_folder_privacy_settings(compose
, msginfo
->folder
);
2525 compose
->updating
= TRUE
;
2527 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2528 compose
->replyinfo
= NULL
;
2529 compose
->fwdinfo
= NULL
;
2531 compose_show_first_last_header(compose
, TRUE
);
2533 gtk_widget_grab_focus(compose
->header_last
->entry
);
2535 filename
= procmsg_get_message_file(msginfo
);
2537 if (filename
== NULL
) {
2538 compose
->updating
= FALSE
;
2539 compose_destroy(compose
);
2544 compose
->redirect_filename
= filename
;
2546 /* Set save folder */
2547 item
= msginfo
->folder
;
2548 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2549 gchar
*folderidentifier
;
2551 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2552 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2553 folderidentifier
= folder_item_get_identifier(item
);
2554 compose_set_save_to(compose
, folderidentifier
);
2555 g_free(folderidentifier
);
2558 compose_attach_parts(compose
, msginfo
);
2560 if (msginfo
->subject
)
2561 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2563 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2565 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2566 _("The body of the \"Redirect\" template has an error at line %d."));
2567 quote_fmt_reset_vartable();
2568 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2570 compose_colorize_signature(compose
);
2573 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2574 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2575 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2577 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2578 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2579 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2580 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2581 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2582 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2583 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2584 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2585 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2587 if (compose
->toolbar
->draft_btn
)
2588 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2589 if (compose
->toolbar
->insert_btn
)
2590 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2591 if (compose
->toolbar
->attach_btn
)
2592 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2593 if (compose
->toolbar
->sig_btn
)
2594 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2595 if (compose
->toolbar
->exteditor_btn
)
2596 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2597 if (compose
->toolbar
->linewrap_current_btn
)
2598 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2599 if (compose
->toolbar
->linewrap_all_btn
)
2600 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2602 compose
->modified
= FALSE
;
2603 compose_set_title(compose
);
2604 compose
->updating
= FALSE
;
2605 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2606 SCROLL_TO_CURSOR(compose
);
2608 if (compose
->deferred_destroy
) {
2609 compose_destroy(compose
);
2613 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2618 const GList
*compose_get_compose_list(void)
2620 return compose_list
;
2623 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2624 ComposeEntryType type
, ComposePrefType pref_type
)
2626 const gchar
*header
;
2628 gboolean in_quote
= FALSE
;
2629 if (!address
|| *address
== '\0') return;
2636 header
= N_("Bcc:");
2638 case COMPOSE_REPLYTO
:
2639 header
= N_("Reply-To:");
2641 case COMPOSE_NEWSGROUPS
:
2642 header
= N_("Newsgroups:");
2644 case COMPOSE_FOLLOWUPTO
:
2645 header
= N_( "Followup-To:");
2647 case COMPOSE_INREPLYTO
:
2648 header
= N_( "In-Reply-To:");
2655 header
= prefs_common_translated_header_name(header
);
2657 cur
= begin
= (gchar
*)address
;
2659 /* we separate the line by commas, but not if we're inside a quoted
2661 while (*cur
!= '\0') {
2663 in_quote
= !in_quote
;
2664 if (*cur
== ',' && !in_quote
) {
2665 gchar
*tmp
= g_strdup(begin
);
2667 tmp
[cur
-begin
]='\0';
2670 while (*tmp
== ' ' || *tmp
== '\t')
2672 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2673 compose_entry_indicate(compose
, tmp
);
2680 gchar
*tmp
= g_strdup(begin
);
2682 tmp
[cur
-begin
]='\0';
2683 while (*tmp
== ' ' || *tmp
== '\t')
2685 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2686 compose_entry_indicate(compose
, tmp
);
2691 static void compose_entry_indicate(Compose
*compose
, const gchar
*mailto
)
2696 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2697 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2698 if (gtk_entry_get_text(entry
) &&
2699 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2700 gtk_widget_modify_base(
2701 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2702 GTK_STATE_NORMAL
, &default_header_bgcolor
);
2703 gtk_widget_modify_text(
2704 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2705 GTK_STATE_NORMAL
, &default_header_color
);
2710 void compose_toolbar_cb(gint action
, gpointer data
)
2712 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2713 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2715 cm_return_if_fail(compose
!= NULL
);
2719 compose_send_cb(NULL
, compose
);
2722 compose_send_later_cb(NULL
, compose
);
2725 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2728 compose_insert_file_cb(NULL
, compose
);
2731 compose_attach_cb(NULL
, compose
);
2734 compose_insert_sig(compose
, FALSE
);
2737 compose_insert_sig(compose
, TRUE
);
2740 compose_ext_editor_cb(NULL
, compose
);
2742 case A_LINEWRAP_CURRENT
:
2743 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2745 case A_LINEWRAP_ALL
:
2746 compose_wrap_all_full(compose
, TRUE
);
2749 compose_address_cb(NULL
, compose
);
2752 case A_CHECK_SPELLING
:
2753 compose_check_all(NULL
, compose
);
2756 case A_PRIVACY_SIGN
:
2758 case A_PRIVACY_ENCRYPT
:
2765 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2770 gchar
*subject
= NULL
;
2774 gchar
**attach
= NULL
;
2775 gchar
*inreplyto
= NULL
;
2776 MailField mfield
= NO_FIELD_PRESENT
;
2778 /* get mailto parts but skip from */
2779 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2782 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2783 mfield
= TO_FIELD_PRESENT
;
2786 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2788 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2790 if (!g_utf8_validate (subject
, -1, NULL
)) {
2791 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2792 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2795 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2797 mfield
= SUBJECT_FIELD_PRESENT
;
2800 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2801 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2804 gboolean prev_autowrap
= compose
->autowrap
;
2806 compose
->autowrap
= FALSE
;
2808 mark
= gtk_text_buffer_get_insert(buffer
);
2809 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2811 if (!g_utf8_validate (body
, -1, NULL
)) {
2812 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2813 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2816 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2818 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2820 compose
->autowrap
= prev_autowrap
;
2821 if (compose
->autowrap
)
2822 compose_wrap_all(compose
);
2823 mfield
= BODY_FIELD_PRESENT
;
2827 gint i
= 0, att
= 0;
2828 gchar
*warn_files
= NULL
;
2829 while (attach
[i
] != NULL
) {
2830 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2831 if (utf8_filename
) {
2832 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2833 gchar
*tmp
= g_strdup_printf("%s%s\n",
2834 warn_files
?warn_files
:"",
2840 g_free(utf8_filename
);
2842 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2847 alertpanel_notice(ngettext(
2848 "The following file has been attached: \n%s",
2849 "The following files have been attached: \n%s", att
), warn_files
);
2854 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2867 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2869 static HeaderEntry hentry
[] = {
2870 {"Reply-To:", NULL
, TRUE
},
2871 {"Cc:", NULL
, TRUE
},
2872 {"References:", NULL
, FALSE
},
2873 {"Bcc:", NULL
, TRUE
},
2874 {"Newsgroups:", NULL
, TRUE
},
2875 {"Followup-To:", NULL
, TRUE
},
2876 {"List-Post:", NULL
, FALSE
},
2877 {"X-Priority:", NULL
, FALSE
},
2878 {NULL
, NULL
, FALSE
}
2895 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2897 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
2898 procheader_get_header_fields(fp
, hentry
);
2901 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2902 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2904 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2907 g_free(hentry
[H_REPLY_TO
].body
);
2908 hentry
[H_REPLY_TO
].body
= NULL
;
2910 if (hentry
[H_CC
].body
!= NULL
) {
2911 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2912 g_free(hentry
[H_CC
].body
);
2913 hentry
[H_CC
].body
= NULL
;
2915 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2916 if (compose
->mode
== COMPOSE_REEDIT
)
2917 compose
->references
= hentry
[H_REFERENCES
].body
;
2919 compose
->references
= compose_parse_references
2920 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2921 g_free(hentry
[H_REFERENCES
].body
);
2923 hentry
[H_REFERENCES
].body
= NULL
;
2925 if (hentry
[H_BCC
].body
!= NULL
) {
2926 if (compose
->mode
== COMPOSE_REEDIT
)
2928 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2929 g_free(hentry
[H_BCC
].body
);
2930 hentry
[H_BCC
].body
= NULL
;
2932 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2933 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2934 hentry
[H_NEWSGROUPS
].body
= NULL
;
2936 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2937 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2938 compose
->followup_to
=
2939 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2942 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2943 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2945 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2946 gchar
*to
= NULL
, *start
= NULL
;
2948 extract_address(hentry
[H_LIST_POST
].body
);
2949 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2950 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2952 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2953 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2956 g_free(compose
->ml_post
);
2957 compose
->ml_post
= to
;
2960 g_free(hentry
[H_LIST_POST
].body
);
2961 hentry
[H_LIST_POST
].body
= NULL
;
2964 /* CLAWS - X-Priority */
2965 if (compose
->mode
== COMPOSE_REEDIT
)
2966 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2969 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2970 g_free(hentry
[H_X_PRIORITY
].body
);
2972 hentry
[H_X_PRIORITY
].body
= NULL
;
2974 if (priority
< PRIORITY_HIGHEST
||
2975 priority
> PRIORITY_LOWEST
)
2976 priority
= PRIORITY_NORMAL
;
2978 compose
->priority
= priority
;
2981 if (compose
->mode
== COMPOSE_REEDIT
) {
2982 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2983 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
2985 if (msginfo
->msgid
&& *msginfo
->msgid
&&
2986 compose
->folder
!= NULL
&&
2987 compose
->folder
->stype
== F_DRAFT
)
2988 compose
->msgid
= g_strdup(msginfo
->msgid
);
2990 if (msginfo
->msgid
&& *msginfo
->msgid
)
2991 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
2993 if (!compose
->references
) {
2994 if (msginfo
->msgid
&& *msginfo
->msgid
) {
2995 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2996 compose
->references
=
2997 g_strdup_printf("<%s>\n\t<%s>",
3001 compose
->references
=
3002 g_strconcat("<", msginfo
->msgid
, ">",
3004 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
3005 compose
->references
=
3006 g_strconcat("<", msginfo
->inreplyto
, ">",
3015 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
3020 cm_return_val_if_fail(msginfo
!= NULL
, -1);
3022 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
3023 procheader_get_header_fields(fp
, entries
);
3027 while (he
!= NULL
&& he
->name
!= NULL
) {
3029 GtkListStore
*model
= NULL
;
3031 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3032 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3033 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3034 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3035 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3042 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3044 GSList
*ref_id_list
, *cur
;
3048 ref_id_list
= references_list_append(NULL
, ref
);
3049 if (!ref_id_list
) return NULL
;
3050 if (msgid
&& *msgid
)
3051 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3056 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3057 /* "<" + Message-ID + ">" + CR+LF+TAB */
3058 len
+= strlen((gchar
*)cur
->data
) + 5;
3060 if (len
> MAX_REFERENCES_LEN
) {
3061 /* remove second message-ID */
3062 if (ref_id_list
&& ref_id_list
->next
&&
3063 ref_id_list
->next
->next
) {
3064 g_free(ref_id_list
->next
->data
);
3065 ref_id_list
= g_slist_remove
3066 (ref_id_list
, ref_id_list
->next
->data
);
3068 slist_free_strings_full(ref_id_list
);
3075 new_ref
= g_string_new("");
3076 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3077 if (new_ref
->len
> 0)
3078 g_string_append(new_ref
, "\n\t");
3079 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3082 slist_free_strings_full(ref_id_list
);
3084 new_ref_str
= new_ref
->str
;
3085 g_string_free(new_ref
, FALSE
);
3090 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3091 const gchar
*fmt
, const gchar
*qmark
,
3092 const gchar
*body
, gboolean rewrap
,
3093 gboolean need_unescape
,
3094 const gchar
*err_msg
)
3096 MsgInfo
* dummyinfo
= NULL
;
3097 gchar
*quote_str
= NULL
;
3099 gboolean prev_autowrap
;
3100 const gchar
*trimmed_body
= body
;
3101 gint cursor_pos
= -1;
3102 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3103 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3108 SIGNAL_BLOCK(buffer
);
3111 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3112 msginfo
= dummyinfo
;
3115 if (qmark
!= NULL
) {
3117 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3118 compose
->gtkaspell
);
3120 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3122 quote_fmt_scan_string(qmark
);
3125 buf
= quote_fmt_get_buffer();
3128 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3130 Xstrdup_a(quote_str
, buf
, goto error
)
3133 if (fmt
&& *fmt
!= '\0') {
3136 while (*trimmed_body
== '\n')
3140 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3141 compose
->gtkaspell
);
3143 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3145 if (need_unescape
) {
3148 /* decode \-escape sequences in the internal representation of the quote format */
3149 tmp
= g_malloc(strlen(fmt
)+1);
3150 pref_get_unescaped_pref(tmp
, fmt
);
3151 quote_fmt_scan_string(tmp
);
3155 quote_fmt_scan_string(fmt
);
3159 buf
= quote_fmt_get_buffer();
3162 gint line
= quote_fmt_get_line();
3163 alertpanel_error(err_msg
, line
);
3171 prev_autowrap
= compose
->autowrap
;
3172 compose
->autowrap
= FALSE
;
3174 mark
= gtk_text_buffer_get_insert(buffer
);
3175 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3176 if (g_utf8_validate(buf
, -1, NULL
)) {
3177 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3179 gchar
*tmpout
= NULL
;
3180 tmpout
= conv_codeset_strdup
3181 (buf
, conv_get_locale_charset_str_no_utf8(),
3183 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3185 tmpout
= g_malloc(strlen(buf
)*2+1);
3186 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3188 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3192 cursor_pos
= quote_fmt_get_cursor_pos();
3193 if (cursor_pos
== -1)
3194 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3195 compose
->set_cursor_pos
= cursor_pos
;
3197 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3198 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3199 gtk_text_buffer_place_cursor(buffer
, &iter
);
3201 compose
->autowrap
= prev_autowrap
;
3202 if (compose
->autowrap
&& rewrap
)
3203 compose_wrap_all(compose
);
3210 SIGNAL_UNBLOCK(buffer
);
3212 procmsg_msginfo_free( &dummyinfo
);
3217 /* if ml_post is of type addr@host and from is of type
3218 * addr-anything@host, return TRUE
3220 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3222 gchar
*left_ml
= NULL
;
3223 gchar
*right_ml
= NULL
;
3224 gchar
*left_from
= NULL
;
3225 gchar
*right_from
= NULL
;
3226 gboolean result
= FALSE
;
3228 if (!ml_post
|| !from
)
3231 left_ml
= g_strdup(ml_post
);
3232 if (strstr(left_ml
, "@")) {
3233 right_ml
= strstr(left_ml
, "@")+1;
3234 *(strstr(left_ml
, "@")) = '\0';
3237 left_from
= g_strdup(from
);
3238 if (strstr(left_from
, "@")) {
3239 right_from
= strstr(left_from
, "@")+1;
3240 *(strstr(left_from
, "@")) = '\0';
3243 if (right_ml
&& right_from
3244 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3245 && !strcmp(right_from
, right_ml
)) {
3254 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3255 gboolean respect_default_to
)
3259 if (!folder
|| !folder
->prefs
)
3262 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3263 compose_entry_append(compose
, folder
->prefs
->default_to
,
3264 COMPOSE_TO
, PREF_FOLDER
);
3265 compose_entry_indicate(compose
, folder
->prefs
->default_to
);
3267 if (folder
->prefs
->enable_default_cc
) {
3268 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3269 COMPOSE_CC
, PREF_FOLDER
);
3270 compose_entry_indicate(compose
, folder
->prefs
->default_cc
);
3272 if (folder
->prefs
->enable_default_bcc
) {
3273 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3274 COMPOSE_BCC
, PREF_FOLDER
);
3275 compose_entry_indicate(compose
, folder
->prefs
->default_bcc
);
3277 if (folder
->prefs
->enable_default_replyto
) {
3278 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3279 COMPOSE_REPLYTO
, PREF_FOLDER
);
3280 compose_entry_indicate(compose
, folder
->prefs
->default_replyto
);
3284 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3289 if (!compose
|| !msginfo
)
3292 if (msginfo
->subject
&& *msginfo
->subject
) {
3293 buf
= p
= g_strdup(msginfo
->subject
);
3294 p
+= subject_get_prefix_length(p
);
3295 memmove(buf
, p
, strlen(p
) + 1);
3297 buf2
= g_strdup_printf("Re: %s", buf
);
3298 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3303 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3306 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3307 gboolean to_all
, gboolean to_ml
,
3309 gboolean followup_and_reply_to
)
3311 GSList
*cc_list
= NULL
;
3314 gchar
*replyto
= NULL
;
3315 gchar
*ac_email
= NULL
;
3317 gboolean reply_to_ml
= FALSE
;
3318 gboolean default_reply_to
= FALSE
;
3320 cm_return_if_fail(compose
->account
!= NULL
);
3321 cm_return_if_fail(msginfo
!= NULL
);
3323 reply_to_ml
= to_ml
&& compose
->ml_post
;
3325 default_reply_to
= msginfo
->folder
&&
3326 msginfo
->folder
->prefs
->enable_default_reply_to
;
3328 if (compose
->account
->protocol
!= A_NNTP
) {
3329 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3331 if (reply_to_ml
&& !default_reply_to
) {
3333 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3336 /* normal answer to ml post with a reply-to */
3337 compose_entry_append(compose
,
3339 COMPOSE_TO
, PREF_ML
);
3340 if (compose
->replyto
)
3341 compose_entry_append(compose
,
3343 COMPOSE_CC
, PREF_ML
);
3345 /* answer to subscription confirmation */
3346 if (compose
->replyto
)
3347 compose_entry_append(compose
,
3349 COMPOSE_TO
, PREF_ML
);
3350 else if (msginfo
->from
)
3351 compose_entry_append(compose
,
3353 COMPOSE_TO
, PREF_ML
);
3356 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3357 compose_entry_append(compose
,
3358 msginfo
->folder
->prefs
->default_reply_to
,
3359 COMPOSE_TO
, PREF_FOLDER
);
3360 compose_entry_indicate(compose
,
3361 msginfo
->folder
->prefs
->default_reply_to
);
3367 compose_entry_append(compose
, msginfo
->from
,
3368 COMPOSE_TO
, PREF_NONE
);
3370 Xstrdup_a(tmp1
, msginfo
->from
, return);
3371 extract_address(tmp1
);
3372 compose_entry_append(compose
,
3373 (!account_find_from_address(tmp1
, FALSE
))
3376 COMPOSE_TO
, PREF_NONE
);
3377 if (compose
->replyto
)
3378 compose_entry_append(compose
,
3380 COMPOSE_CC
, PREF_NONE
);
3382 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3383 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3384 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3385 if (compose
->replyto
) {
3386 compose_entry_append(compose
,
3388 COMPOSE_TO
, PREF_NONE
);
3390 compose_entry_append(compose
,
3391 msginfo
->from
? msginfo
->from
: "",
3392 COMPOSE_TO
, PREF_NONE
);
3395 /* replying to own mail, use original recp */
3396 compose_entry_append(compose
,
3397 msginfo
->to
? msginfo
->to
: "",
3398 COMPOSE_TO
, PREF_NONE
);
3399 compose_entry_append(compose
,
3400 msginfo
->cc
? msginfo
->cc
: "",
3401 COMPOSE_CC
, PREF_NONE
);
3406 if (to_sender
|| (compose
->followup_to
&&
3407 !strncmp(compose
->followup_to
, "poster", 6)))
3408 compose_entry_append
3410 (compose
->replyto
? compose
->replyto
:
3411 msginfo
->from
? msginfo
->from
: ""),
3412 COMPOSE_TO
, PREF_NONE
);
3414 else if (followup_and_reply_to
|| to_all
) {
3415 compose_entry_append
3417 (compose
->replyto
? compose
->replyto
:
3418 msginfo
->from
? msginfo
->from
: ""),
3419 COMPOSE_TO
, PREF_NONE
);
3421 compose_entry_append
3423 compose
->followup_to
? compose
->followup_to
:
3424 compose
->newsgroups
? compose
->newsgroups
: "",
3425 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3427 compose_entry_append
3429 msginfo
->cc
? msginfo
->cc
: "",
3430 COMPOSE_CC
, PREF_NONE
);
3433 compose_entry_append
3435 compose
->followup_to
? compose
->followup_to
:
3436 compose
->newsgroups
? compose
->newsgroups
: "",
3437 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3439 compose_reply_set_subject(compose
, msginfo
);
3441 if (to_ml
&& compose
->ml_post
) return;
3442 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3444 if (compose
->replyto
) {
3445 Xstrdup_a(replyto
, compose
->replyto
, return);
3446 extract_address(replyto
);
3448 if (msginfo
->from
) {
3449 Xstrdup_a(from
, msginfo
->from
, return);
3450 extract_address(from
);
3453 if (replyto
&& from
)
3454 cc_list
= address_list_append_with_comments(cc_list
, from
);
3455 if (to_all
&& msginfo
->folder
&&
3456 msginfo
->folder
->prefs
->enable_default_reply_to
)
3457 cc_list
= address_list_append_with_comments(cc_list
,
3458 msginfo
->folder
->prefs
->default_reply_to
);
3459 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3460 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3462 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3465 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3466 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3467 extract_address(addr
);
3469 if (strcmp(ac_email
, addr
))
3470 compose_entry_append(compose
, (gchar
*)cur
->data
,
3471 COMPOSE_CC
, PREF_NONE
);
3473 debug_print("Cc address same as compose account's, ignoring\n");
3478 slist_free_strings_full(cc_list
);
3484 #define SET_ENTRY(entry, str) \
3487 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3490 #define SET_ADDRESS(type, str) \
3493 compose_entry_append(compose, str, type, PREF_NONE); \
3496 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3498 cm_return_if_fail(msginfo
!= NULL
);
3500 SET_ENTRY(subject_entry
, msginfo
->subject
);
3501 SET_ENTRY(from_name
, msginfo
->from
);
3502 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3503 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3504 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3505 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3506 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3507 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3509 compose_update_priority_menu_item(compose
);
3510 compose_update_privacy_system_menu_item(compose
, FALSE
);
3511 compose_show_first_last_header(compose
, TRUE
);
3517 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3519 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3520 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3522 GtkTextIter iter
, iter_end
;
3523 gint cur_pos
, ins_pos
;
3524 gboolean prev_autowrap
;
3525 gboolean found
= FALSE
;
3526 gboolean exists
= FALSE
;
3528 cm_return_if_fail(compose
->account
!= NULL
);
3532 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3533 G_CALLBACK(compose_changed_cb
),
3536 mark
= gtk_text_buffer_get_insert(buffer
);
3537 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3538 cur_pos
= gtk_text_iter_get_offset (&iter
);
3541 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3543 exists
= (compose
->sig_str
!= NULL
);
3546 GtkTextIter first_iter
, start_iter
, end_iter
;
3548 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3550 if (!exists
|| compose
->sig_str
[0] == '\0')
3553 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3554 compose
->signature_tag
);
3557 /* include previous \n\n */
3558 gtk_text_iter_backward_chars(&first_iter
, 1);
3559 start_iter
= first_iter
;
3560 end_iter
= first_iter
;
3562 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3563 compose
->signature_tag
);
3564 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3565 compose
->signature_tag
);
3567 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3573 g_free(compose
->sig_str
);
3574 compose
->sig_str
= account_get_signature_str(compose
->account
);
3576 cur_pos
= gtk_text_iter_get_offset(&iter
);
3578 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3579 g_free(compose
->sig_str
);
3580 compose
->sig_str
= NULL
;
3582 if (compose
->sig_inserted
== FALSE
)
3583 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3584 compose
->sig_inserted
= TRUE
;
3586 cur_pos
= gtk_text_iter_get_offset(&iter
);
3587 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3589 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3590 gtk_text_iter_forward_chars(&iter
, 1);
3591 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3592 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3594 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3595 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3598 /* put the cursor where it should be
3599 * either where the quote_fmt says, either where it was */
3600 if (compose
->set_cursor_pos
< 0)
3601 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3603 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3604 compose
->set_cursor_pos
);
3606 compose
->set_cursor_pos
= -1;
3607 gtk_text_buffer_place_cursor(buffer
, &iter
);
3608 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3609 G_CALLBACK(compose_changed_cb
),
3615 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3618 GtkTextBuffer
*buffer
;
3621 const gchar
*cur_encoding
;
3622 gchar buf
[BUFFSIZE
];
3625 gboolean prev_autowrap
;
3629 GError
*error
= NULL
;
3635 GString
*file_contents
= NULL
;
3636 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3638 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3640 /* get the size of the file we are about to insert */
3642 f
= g_file_new_for_path(file
);
3643 fi
= g_file_query_info(f
, "standard::size",
3644 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
3646 if (error
!= NULL
) {
3647 g_warning(error
->message
);
3649 g_error_free(error
);
3653 ret
= g_stat(file
, &file_stat
);
3656 gchar
*shortfile
= g_path_get_basename(file
);
3657 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3659 return COMPOSE_INSERT_NO_FILE
;
3660 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3662 size
= g_file_info_get_size(fi
);
3666 size
= file_stat
.st_size
;
3669 /* ask user for confirmation if the file is large */
3670 if (prefs_common
.warn_large_insert_size
< 0 ||
3671 size
> ((goffset
) prefs_common
.warn_large_insert_size
* 1024)) {
3675 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3676 "in the message body. Are you sure you want to do that?"),
3677 to_human_readable(size
));
3678 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3679 _("_Insert"), NULL
, ALERTFOCUS_SECOND
, TRUE
,
3680 NULL
, ALERT_QUESTION
);
3683 /* do we ask for confirmation next time? */
3684 if (aval
& G_ALERTDISABLE
) {
3685 /* no confirmation next time, disable feature in preferences */
3686 aval
&= ~G_ALERTDISABLE
;
3687 prefs_common
.warn_large_insert
= FALSE
;
3690 /* abort file insertion if user canceled action */
3691 if (aval
!= G_ALERTALTERNATE
) {
3692 return COMPOSE_INSERT_NO_FILE
;
3698 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3699 FILE_OP_ERROR(file
, "claws_fopen");
3700 return COMPOSE_INSERT_READ_ERROR
;
3703 prev_autowrap
= compose
->autowrap
;
3704 compose
->autowrap
= FALSE
;
3706 text
= GTK_TEXT_VIEW(compose
->text
);
3707 buffer
= gtk_text_view_get_buffer(text
);
3708 mark
= gtk_text_buffer_get_insert(buffer
);
3709 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3711 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3712 G_CALLBACK(text_inserted
),
3715 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3717 file_contents
= g_string_new("");
3718 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3721 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3722 str
= g_strdup(buf
);
3724 codeconv_set_strict(TRUE
);
3725 str
= conv_codeset_strdup
3726 (buf
, cur_encoding
, CS_INTERNAL
);
3727 codeconv_set_strict(FALSE
);
3730 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3736 /* strip <CR> if DOS/Windows file,
3737 replace <CR> with <LF> if Macintosh file. */
3740 if (len
> 0 && str
[len
- 1] != '\n') {
3742 if (str
[len
] == '\r') str
[len
] = '\n';
3745 file_contents
= g_string_append(file_contents
, str
);
3749 if (result
== COMPOSE_INSERT_SUCCESS
) {
3750 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3752 compose_changed_cb(NULL
, compose
);
3753 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3754 G_CALLBACK(text_inserted
),
3756 compose
->autowrap
= prev_autowrap
;
3757 if (compose
->autowrap
)
3758 compose_wrap_all(compose
);
3761 g_string_free(file_contents
, TRUE
);
3767 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3768 const gchar
*filename
,
3769 const gchar
*content_type
,
3770 const gchar
*charset
)
3778 GtkListStore
*store
;
3780 gboolean has_binary
= FALSE
;
3782 if (!is_file_exist(file
)) {
3783 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3784 gboolean result
= FALSE
;
3785 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3786 result
= compose_attach_append(
3787 compose
, file_from_uri
,
3788 filename
, content_type
,
3791 g_free(file_from_uri
);
3794 alertpanel_error("File %s doesn't exist or permission denied\n", filename
);
3797 if ((size
= get_file_size(file
)) < 0) {
3798 alertpanel_error("Can't get file size of %s\n", filename
);
3802 /* In batch mode, we allow 0-length files to be attached no questions asked */
3803 if (size
== 0 && !compose
->batch
) {
3804 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3805 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3806 GTK_STOCK_CANCEL
, _("_Attach anyway"), NULL
,
3807 ALERTFOCUS_SECOND
, FALSE
, NULL
, ALERT_WARNING
);
3810 if (aval
!= G_ALERTALTERNATE
) {
3814 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3815 alertpanel_error(_("Can't read %s."), filename
);
3820 ainfo
= g_new0(AttachInfo
, 1);
3821 auto_ainfo
= g_auto_pointer_new_with_free
3822 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3823 ainfo
->file
= g_strdup(file
);
3826 ainfo
->content_type
= g_strdup(content_type
);
3827 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3829 MsgFlags flags
= {0, 0};
3831 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3832 ainfo
->encoding
= ENC_7BIT
;
3834 ainfo
->encoding
= ENC_8BIT
;
3836 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3837 if (msginfo
&& msginfo
->subject
)
3838 name
= g_strdup(msginfo
->subject
);
3840 name
= g_path_get_basename(filename
? filename
: file
);
3842 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3844 procmsg_msginfo_free(&msginfo
);
3846 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3847 ainfo
->charset
= g_strdup(charset
);
3848 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3850 ainfo
->encoding
= ENC_BASE64
;
3852 name
= g_path_get_basename(filename
? filename
: file
);
3853 ainfo
->name
= g_strdup(name
);
3857 ainfo
->content_type
= procmime_get_mime_type(file
);
3858 if (!ainfo
->content_type
) {
3859 ainfo
->content_type
=
3860 g_strdup("application/octet-stream");
3861 ainfo
->encoding
= ENC_BASE64
;
3862 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3864 procmime_get_encoding_for_text_file(file
, &has_binary
);
3866 ainfo
->encoding
= ENC_BASE64
;
3867 name
= g_path_get_basename(filename
? filename
: file
);
3868 ainfo
->name
= g_strdup(name
);
3872 if (ainfo
->name
!= NULL
3873 && !strcmp(ainfo
->name
, ".")) {
3874 g_free(ainfo
->name
);
3878 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3879 g_free(ainfo
->content_type
);
3880 ainfo
->content_type
= g_strdup("application/octet-stream");
3881 g_free(ainfo
->charset
);
3882 ainfo
->charset
= NULL
;
3885 ainfo
->size
= (goffset
)size
;
3886 size_text
= to_human_readable((goffset
)size
);
3888 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3889 (GTK_TREE_VIEW(compose
->attach_clist
)));
3891 gtk_list_store_append(store
, &iter
);
3892 gtk_list_store_set(store
, &iter
,
3893 COL_MIMETYPE
, ainfo
->content_type
,
3894 COL_SIZE
, size_text
,
3895 COL_NAME
, ainfo
->name
,
3896 COL_CHARSET
, ainfo
->charset
,
3898 COL_AUTODATA
, auto_ainfo
,
3901 g_auto_pointer_free(auto_ainfo
);
3902 compose_attach_update_label(compose
);
3906 void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3908 compose
->use_signing
= use_signing
;
3909 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3912 void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3914 compose
->use_encryption
= use_encryption
;
3915 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3918 #define NEXT_PART_NOT_CHILD(info) \
3920 node = info->node; \
3921 while (node->children) \
3922 node = g_node_last_child(node); \
3923 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3926 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3930 MimeInfo
*firsttext
= NULL
;
3931 MimeInfo
*encrypted
= NULL
;
3934 const gchar
*partname
= NULL
;
3936 mimeinfo
= procmime_scan_message(msginfo
);
3937 if (!mimeinfo
) return;
3939 if (mimeinfo
->node
->children
== NULL
) {
3940 procmime_mimeinfo_free_all(&mimeinfo
);
3944 /* find first content part */
3945 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3946 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3947 child
= (MimeInfo
*)child
->node
->children
->data
;
3950 if (child
->type
== MIMETYPE_TEXT
) {
3952 debug_print("First text part found\n");
3953 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3954 child
->type
== MIMETYPE_APPLICATION
&&
3955 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3956 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3959 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3960 while (child
!= NULL
) {
3963 if (child
== encrypted
) {
3964 /* skip this part of tree */
3965 NEXT_PART_NOT_CHILD(child
);
3969 if (child
->type
== MIMETYPE_MULTIPART
) {
3970 /* get the actual content */
3971 child
= procmime_mimeinfo_next(child
);
3975 if (child
== firsttext
) {
3976 child
= procmime_mimeinfo_next(child
);
3980 outfile
= procmime_get_tmp_file_name(child
);
3981 if ((err
= procmime_get_part(outfile
, child
)) < 0)
3982 g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err
));
3984 gchar
*content_type
;
3986 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
3988 /* if we meet a pgp signature, we don't attach it, but
3989 * we force signing. */
3990 if ((strcmp(content_type
, "application/pgp-signature") &&
3991 strcmp(content_type
, "application/pkcs7-signature") &&
3992 strcmp(content_type
, "application/x-pkcs7-signature"))
3993 || compose
->mode
== COMPOSE_REDIRECT
) {
3994 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
3995 if (partname
== NULL
)
3996 partname
= procmime_mimeinfo_get_parameter(child
, "name");
3997 if (partname
== NULL
)
3999 compose_attach_append(compose
, outfile
,
4000 partname
, content_type
,
4001 procmime_mimeinfo_get_parameter(child
, "charset"));
4003 compose_force_signing(compose
, compose
->account
, NULL
);
4005 g_free(content_type
);
4008 NEXT_PART_NOT_CHILD(child
);
4010 procmime_mimeinfo_free_all(&mimeinfo
);
4013 #undef NEXT_PART_NOT_CHILD
4018 WAIT_FOR_INDENT_CHAR
,
4019 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
4022 /* return indent length, we allow:
4023 indent characters followed by indent characters or spaces/tabs,
4024 alphabets and numbers immediately followed by indent characters,
4025 and the repeating sequences of the above
4026 If quote ends with multiple spaces, only the first one is included. */
4027 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
4028 const GtkTextIter
*start
, gint
*len
)
4030 GtkTextIter iter
= *start
;
4034 IndentState state
= WAIT_FOR_INDENT_CHAR
;
4037 gint alnum_count
= 0;
4038 gint space_count
= 0;
4041 if (prefs_common
.quote_chars
== NULL
) {
4045 while (!gtk_text_iter_ends_line(&iter
)) {
4046 wc
= gtk_text_iter_get_char(&iter
);
4047 if (g_unichar_iswide(wc
))
4049 clen
= g_unichar_to_utf8(wc
, ch
);
4053 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
4054 is_space
= g_unichar_isspace(wc
);
4056 if (state
== WAIT_FOR_INDENT_CHAR
) {
4057 if (!is_indent
&& !g_unichar_isalnum(wc
))
4060 quote_len
+= alnum_count
+ space_count
+ 1;
4061 alnum_count
= space_count
= 0;
4062 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4065 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4066 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4070 else if (is_indent
) {
4071 quote_len
+= alnum_count
+ space_count
+ 1;
4072 alnum_count
= space_count
= 0;
4075 state
= WAIT_FOR_INDENT_CHAR
;
4079 gtk_text_iter_forward_char(&iter
);
4082 if (quote_len
> 0 && space_count
> 0)
4088 if (quote_len
> 0) {
4090 gtk_text_iter_forward_chars(&iter
, quote_len
);
4091 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4097 /* return >0 if the line is itemized */
4098 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4099 const GtkTextIter
*start
)
4101 GtkTextIter iter
= *start
;
4106 if (gtk_text_iter_ends_line(&iter
))
4111 wc
= gtk_text_iter_get_char(&iter
);
4112 if (!g_unichar_isspace(wc
))
4114 gtk_text_iter_forward_char(&iter
);
4115 if (gtk_text_iter_ends_line(&iter
))
4119 clen
= g_unichar_to_utf8(wc
, ch
);
4120 if (!((clen
== 1 && strchr("*-+", ch
[0])) ||
4122 wc
== 0x2022 || /* BULLET */
4123 wc
== 0x2023 || /* TRIANGULAR BULLET */
4124 wc
== 0x2043 || /* HYPHEN BULLET */
4125 wc
== 0x204c || /* BLACK LEFTWARDS BULLET */
4126 wc
== 0x204d || /* BLACK RIGHTWARDS BULLET */
4127 wc
== 0x2219 || /* BULLET OPERATOR */
4128 wc
== 0x25d8 || /* INVERSE BULLET */
4129 wc
== 0x25e6 || /* WHITE BULLET */
4130 wc
== 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4131 wc
== 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4132 wc
== 0x2767 || /* ROTATED FLORAL HEART BULLET */
4133 wc
== 0x29be || /* CIRCLED WHITE BULLET */
4134 wc
== 0x29bf /* CIRCLED BULLET */
4138 gtk_text_iter_forward_char(&iter
);
4139 if (gtk_text_iter_ends_line(&iter
))
4141 wc
= gtk_text_iter_get_char(&iter
);
4142 if (g_unichar_isspace(wc
)) {
4148 /* return the string at the start of the itemization */
4149 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4150 const GtkTextIter
*start
)
4152 GtkTextIter iter
= *start
;
4155 GString
*item_chars
= g_string_new("");
4158 if (gtk_text_iter_ends_line(&iter
))
4163 wc
= gtk_text_iter_get_char(&iter
);
4164 if (!g_unichar_isspace(wc
))
4166 gtk_text_iter_forward_char(&iter
);
4167 if (gtk_text_iter_ends_line(&iter
))
4169 g_string_append_unichar(item_chars
, wc
);
4172 str
= item_chars
->str
;
4173 g_string_free(item_chars
, FALSE
);
4177 /* return the number of spaces at a line's start */
4178 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4179 const GtkTextIter
*start
)
4181 GtkTextIter iter
= *start
;
4184 if (gtk_text_iter_ends_line(&iter
))
4188 wc
= gtk_text_iter_get_char(&iter
);
4189 if (!g_unichar_isspace(wc
))
4192 gtk_text_iter_forward_char(&iter
);
4193 if (gtk_text_iter_ends_line(&iter
))
4197 gtk_text_iter_forward_char(&iter
);
4198 if (gtk_text_iter_ends_line(&iter
))
4203 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4204 const GtkTextIter
*start
,
4205 GtkTextIter
*break_pos
,
4209 GtkTextIter iter
= *start
, line_end
= *start
;
4210 PangoLogAttr
*attrs
;
4217 gboolean can_break
= FALSE
;
4218 gboolean do_break
= FALSE
;
4219 gboolean was_white
= FALSE
;
4220 gboolean prev_dont_break
= FALSE
;
4222 gtk_text_iter_forward_to_line_end(&line_end
);
4223 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4224 len
= g_utf8_strlen(str
, -1);
4228 g_warning("compose_get_line_break_pos: len = 0!");
4232 /* g_print("breaking line: %d: %s (len = %d)\n",
4233 gtk_text_iter_get_line(&iter), str, len); */
4235 attrs
= g_new(PangoLogAttr
, len
+ 1);
4237 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4241 /* skip quote and leading spaces */
4242 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4245 wc
= g_utf8_get_char(p
);
4246 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4248 if (g_unichar_iswide(wc
))
4250 else if (*p
== '\t')
4254 p
= g_utf8_next_char(p
);
4257 for (; *p
!= '\0' && i
< len
; i
++) {
4258 PangoLogAttr
*attr
= attrs
+ i
;
4259 gunichar wc
= g_utf8_get_char(p
);
4262 /* attr->is_line_break will be false for some characters that
4263 * we want to break a line before, like '/' or ':', so we
4264 * also allow breaking on any non-wide character. The
4265 * mentioned pango attribute is still useful to decide on
4266 * line breaks when wide characters are involved. */
4267 if ((!g_unichar_iswide(wc
) || attr
->is_line_break
)
4268 && can_break
&& was_white
&& !prev_dont_break
)
4271 was_white
= attr
->is_white
;
4273 /* don't wrap URI */
4274 if ((uri_len
= get_uri_len(p
)) > 0) {
4276 if (pos
> 0 && col
> max_col
) {
4286 if (g_unichar_iswide(wc
)) {
4288 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4290 } else if (*p
== '\t')
4294 if (pos
> 0 && col
> max_col
) {
4299 if (*p
== '-' || *p
== '/')
4300 prev_dont_break
= TRUE
;
4302 prev_dont_break
= FALSE
;
4304 p
= g_utf8_next_char(p
);
4308 /* debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4313 *break_pos
= *start
;
4314 gtk_text_iter_set_line_offset(break_pos
, pos
);
4319 static gboolean
compose_join_next_line(Compose
*compose
,
4320 GtkTextBuffer
*buffer
,
4322 const gchar
*quote_str
)
4324 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4325 PangoLogAttr attrs
[3];
4327 gchar
*next_quote_str
;
4330 gboolean keep_cursor
= FALSE
;
4332 if (!gtk_text_iter_forward_line(&iter_
) ||
4333 gtk_text_iter_ends_line(&iter_
)) {
4336 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4338 if ((quote_str
|| next_quote_str
) &&
4339 g_strcmp0(quote_str
, next_quote_str
) != 0) {
4340 g_free(next_quote_str
);
4343 g_free(next_quote_str
);
4346 if (quote_len
> 0) {
4347 gtk_text_iter_forward_chars(&end
, quote_len
);
4348 if (gtk_text_iter_ends_line(&end
)) {
4353 /* don't join itemized lines */
4354 if (compose_itemized_length(buffer
, &end
) > 0) {
4358 /* don't join signature separator */
4359 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4362 /* delete quote str */
4364 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4366 /* don't join line breaks put by the user */
4368 gtk_text_iter_backward_char(&cur
);
4369 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4370 gtk_text_iter_forward_char(&cur
);
4374 gtk_text_iter_forward_char(&cur
);
4375 /* delete linebreak and extra spaces */
4376 while (gtk_text_iter_backward_char(&cur
)) {
4377 wc1
= gtk_text_iter_get_char(&cur
);
4378 if (!g_unichar_isspace(wc1
))
4383 while (!gtk_text_iter_ends_line(&cur
)) {
4384 wc1
= gtk_text_iter_get_char(&cur
);
4385 if (!g_unichar_isspace(wc1
))
4387 gtk_text_iter_forward_char(&cur
);
4390 if (!gtk_text_iter_equal(&prev
, &next
)) {
4393 mark
= gtk_text_buffer_get_insert(buffer
);
4394 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4395 if (gtk_text_iter_equal(&prev
, &cur
))
4397 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4401 /* insert space if required */
4402 gtk_text_iter_backward_char(&prev
);
4403 wc1
= gtk_text_iter_get_char(&prev
);
4404 wc2
= gtk_text_iter_get_char(&next
);
4405 gtk_text_iter_forward_char(&next
);
4406 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4407 pango_default_break(str
, -1, NULL
, attrs
, 3);
4408 if (!attrs
[1].is_line_break
||
4409 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4410 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4412 gtk_text_iter_backward_char(&iter_
);
4413 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4422 #define ADD_TXT_POS(bp_, ep_, pti_) \
4423 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4424 last = last->next; \
4425 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4426 last->next = NULL; \
4428 g_warning("alloc error scanning URIs"); \
4431 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4433 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4434 GtkTextBuffer
*buffer
;
4435 GtkTextIter iter
, break_pos
, end_of_line
;
4436 gchar
*quote_str
= NULL
;
4438 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4439 gboolean prev_autowrap
= compose
->autowrap
;
4440 gint startq_offset
= -1, noq_offset
= -1;
4441 gint uri_start
= -1, uri_stop
= -1;
4442 gint nouri_start
= -1, nouri_stop
= -1;
4443 gint num_blocks
= 0;
4444 gint quotelevel
= -1;
4445 gboolean modified
= force
;
4446 gboolean removed
= FALSE
;
4447 gboolean modified_before_remove
= FALSE
;
4449 gboolean start
= TRUE
;
4450 gint itemized_len
= 0, rem_item_len
= 0;
4451 gchar
*itemized_chars
= NULL
;
4452 gboolean item_continuation
= FALSE
;
4457 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4461 compose
->autowrap
= FALSE
;
4463 buffer
= gtk_text_view_get_buffer(text
);
4464 undo_wrapping(compose
->undostruct
, TRUE
);
4469 mark
= gtk_text_buffer_get_insert(buffer
);
4470 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4474 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4475 if (gtk_text_iter_ends_line(&iter
)) {
4476 while (gtk_text_iter_ends_line(&iter
) &&
4477 gtk_text_iter_forward_line(&iter
))
4480 while (gtk_text_iter_backward_line(&iter
)) {
4481 if (gtk_text_iter_ends_line(&iter
)) {
4482 gtk_text_iter_forward_line(&iter
);
4488 /* move to line start */
4489 gtk_text_iter_set_line_offset(&iter
, 0);
4492 itemized_len
= compose_itemized_length(buffer
, &iter
);
4494 if (!itemized_len
) {
4495 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4496 item_continuation
= TRUE
;
4500 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4502 /* go until paragraph end (empty line) */
4503 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4504 gchar
*scanpos
= NULL
;
4505 /* parse table - in order of priority */
4507 const gchar
*needle
; /* token */
4509 /* token search function */
4510 gchar
*(*search
) (const gchar
*haystack
,
4511 const gchar
*needle
);
4512 /* part parsing function */
4513 gboolean (*parse
) (const gchar
*start
,
4514 const gchar
*scanpos
,
4518 /* part to URI function */
4519 gchar
*(*build_uri
) (const gchar
*bp
,
4523 static struct table parser
[] = {
4524 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4525 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4526 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4527 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4528 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4529 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4530 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4531 {"@", strcasestr
, get_email_part
, make_email_string
}
4533 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4534 gint last_index
= PARSE_ELEMS
;
4536 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4540 if (!prev_autowrap
&& num_blocks
== 0) {
4542 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4543 G_CALLBACK(text_inserted
),
4546 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4549 uri_start
= uri_stop
= -1;
4551 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4554 /* debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4555 if (startq_offset
== -1)
4556 startq_offset
= gtk_text_iter_get_offset(&iter
);
4557 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4558 if (quotelevel
> 2) {
4559 /* recycle colors */
4560 if (prefs_common
.recycle_quote_colors
)
4569 if (startq_offset
== -1)
4570 noq_offset
= gtk_text_iter_get_offset(&iter
);
4574 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4577 if (gtk_text_iter_ends_line(&iter
)) {
4579 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4580 prefs_common
.linewrap_len
,
4582 GtkTextIter prev
, next
, cur
;
4583 if (prev_autowrap
!= FALSE
|| force
) {
4584 compose
->automatic_break
= TRUE
;
4586 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4587 compose
->automatic_break
= FALSE
;
4588 if (itemized_len
&& compose
->autoindent
) {
4589 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4590 if (!item_continuation
)
4591 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4593 } else if (quote_str
&& wrap_quote
) {
4594 compose
->automatic_break
= TRUE
;
4596 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4597 compose
->automatic_break
= FALSE
;
4598 if (itemized_len
&& compose
->autoindent
) {
4599 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4600 if (!item_continuation
)
4601 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4605 /* remove trailing spaces */
4607 rem_item_len
= itemized_len
;
4608 while (compose
->autoindent
&& rem_item_len
-- > 0)
4609 gtk_text_iter_backward_char(&cur
);
4610 gtk_text_iter_backward_char(&cur
);
4613 while (!gtk_text_iter_starts_line(&cur
)) {
4616 gtk_text_iter_backward_char(&cur
);
4617 wc
= gtk_text_iter_get_char(&cur
);
4618 if (!g_unichar_isspace(wc
))
4622 if (!gtk_text_iter_equal(&prev
, &next
)) {
4623 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4625 gtk_text_iter_forward_char(&break_pos
);
4629 gtk_text_buffer_insert(buffer
, &break_pos
,
4633 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4635 /* move iter to current line start */
4636 gtk_text_iter_set_line_offset(&iter
, 0);
4643 /* move iter to next line start */
4649 if (!prev_autowrap
&& num_blocks
> 0) {
4651 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4652 G_CALLBACK(text_inserted
),
4656 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4657 gtk_text_iter_forward_char(&end_of_line
);
4659 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4661 nouri_start
= gtk_text_iter_get_offset(&iter
);
4662 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4664 walk_pos
= gtk_text_iter_get_offset(&iter
);
4665 /* FIXME: this looks phony. scanning for anything in the parse table */
4666 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4669 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4671 if (scanpos
== NULL
|| tmp
< scanpos
) {
4680 /* check if URI can be parsed */
4681 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4682 (const gchar
**)&ep
, FALSE
)
4683 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4687 strlen(parser
[last_index
].needle
);
4690 uri_start
= walk_pos
+ (bp
- o_walk
);
4691 uri_stop
= walk_pos
+ (ep
- o_walk
);
4695 gtk_text_iter_forward_line(&iter
);
4698 if (startq_offset
!= -1) {
4699 GtkTextIter startquote
, endquote
;
4700 gtk_text_buffer_get_iter_at_offset(
4701 buffer
, &startquote
, startq_offset
);
4704 switch (quotelevel
) {
4706 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4707 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4708 gtk_text_buffer_apply_tag_by_name(
4709 buffer
, "quote0", &startquote
, &endquote
);
4710 gtk_text_buffer_remove_tag_by_name(
4711 buffer
, "quote1", &startquote
, &endquote
);
4712 gtk_text_buffer_remove_tag_by_name(
4713 buffer
, "quote2", &startquote
, &endquote
);
4718 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4719 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4720 gtk_text_buffer_apply_tag_by_name(
4721 buffer
, "quote1", &startquote
, &endquote
);
4722 gtk_text_buffer_remove_tag_by_name(
4723 buffer
, "quote0", &startquote
, &endquote
);
4724 gtk_text_buffer_remove_tag_by_name(
4725 buffer
, "quote2", &startquote
, &endquote
);
4730 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4731 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4732 gtk_text_buffer_apply_tag_by_name(
4733 buffer
, "quote2", &startquote
, &endquote
);
4734 gtk_text_buffer_remove_tag_by_name(
4735 buffer
, "quote0", &startquote
, &endquote
);
4736 gtk_text_buffer_remove_tag_by_name(
4737 buffer
, "quote1", &startquote
, &endquote
);
4743 } else if (noq_offset
!= -1) {
4744 GtkTextIter startnoquote
, endnoquote
;
4745 gtk_text_buffer_get_iter_at_offset(
4746 buffer
, &startnoquote
, noq_offset
);
4749 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4750 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4751 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4752 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4753 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4754 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4755 gtk_text_buffer_remove_tag_by_name(
4756 buffer
, "quote0", &startnoquote
, &endnoquote
);
4757 gtk_text_buffer_remove_tag_by_name(
4758 buffer
, "quote1", &startnoquote
, &endnoquote
);
4759 gtk_text_buffer_remove_tag_by_name(
4760 buffer
, "quote2", &startnoquote
, &endnoquote
);
4766 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4767 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4768 gtk_text_buffer_get_iter_at_offset(
4769 buffer
, &nouri_start_iter
, nouri_start
);
4770 gtk_text_buffer_get_iter_at_offset(
4771 buffer
, &nouri_end_iter
, nouri_stop
);
4772 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4773 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4774 gtk_text_buffer_remove_tag_by_name(
4775 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4776 modified_before_remove
= modified
;
4781 if (uri_start
>= 0 && uri_stop
> 0) {
4782 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4783 gtk_text_buffer_get_iter_at_offset(
4784 buffer
, &uri_start_iter
, uri_start
);
4785 gtk_text_buffer_get_iter_at_offset(
4786 buffer
, &uri_end_iter
, uri_stop
);
4787 back
= uri_end_iter
;
4788 gtk_text_iter_backward_char(&back
);
4789 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4790 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4791 gtk_text_buffer_apply_tag_by_name(
4792 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4794 if (removed
&& !modified_before_remove
) {
4800 /* debug_print("not modified, out after %d lines\n", lines); */
4804 /* debug_print("modified, out after %d lines\n", lines); */
4806 g_free(itemized_chars
);
4809 undo_wrapping(compose
->undostruct
, FALSE
);
4810 compose
->autowrap
= prev_autowrap
;
4815 void compose_action_cb(void *data
)
4817 Compose
*compose
= (Compose
*)data
;
4818 compose_wrap_all(compose
);
4821 static void compose_wrap_all(Compose
*compose
)
4823 compose_wrap_all_full(compose
, FALSE
);
4826 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4828 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4829 GtkTextBuffer
*buffer
;
4831 gboolean modified
= TRUE
;
4833 buffer
= gtk_text_view_get_buffer(text
);
4835 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4837 undo_wrapping(compose
->undostruct
, TRUE
);
4839 while (!gtk_text_iter_is_end(&iter
) && modified
)
4840 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4842 undo_wrapping(compose
->undostruct
, FALSE
);
4846 static void compose_set_title(Compose
*compose
)
4852 edited
= compose
->modified
? _(" [Edited]") : "";
4854 subject
= gtk_editable_get_chars(
4855 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4857 #ifndef GENERIC_UMPC
4858 if (subject
&& strlen(subject
))
4859 str
= g_strdup_printf(_("%s - Compose message%s"),
4862 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4864 str
= g_strdup(_("Compose message"));
4867 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4873 * compose_current_mail_account:
4875 * Find a current mail account (the currently selected account, or the
4876 * default account, if a news account is currently selected). If a
4877 * mail account cannot be found, display an error message.
4879 * Return value: Mail account, or NULL if not found.
4881 static PrefsAccount
*
4882 compose_current_mail_account(void)
4886 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4889 ac
= account_get_default();
4890 if (!ac
|| ac
->protocol
== A_NNTP
) {
4891 alertpanel_error(_("Account for sending mail is not specified.\n"
4892 "Please select a mail account before sending."));
4899 #define QUOTE_IF_REQUIRED(out, str) \
4901 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4905 len = strlen(str) + 3; \
4906 if ((__tmp = alloca(len)) == NULL) { \
4907 g_warning("can't allocate memory"); \
4908 g_string_free(header, TRUE); \
4911 g_snprintf(__tmp, len, "\"%s\"", str); \
4916 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4917 g_warning("can't allocate memory"); \
4918 g_string_free(header, TRUE); \
4921 strcpy(__tmp, str); \
4927 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4929 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4933 len = strlen(str) + 3; \
4934 if ((__tmp = alloca(len)) == NULL) { \
4935 g_warning("can't allocate memory"); \
4938 g_snprintf(__tmp, len, "\"%s\"", str); \
4943 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4944 g_warning("can't allocate memory"); \
4947 strcpy(__tmp, str); \
4953 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4956 gchar
*from
= NULL
, *header
= NULL
;
4957 ComposeHeaderEntry
*header_entry
;
4960 cm_return_if_fail(account
!= NULL
);
4962 compose
->account
= account
;
4963 if (account
->name
&& *account
->name
) {
4965 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4966 qbuf
= escape_internal_quotes(buf
, '"');
4967 from
= g_strdup_printf("%s <%s>",
4968 qbuf
, account
->address
);
4971 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4973 from
= g_strdup_printf("<%s>",
4975 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4980 compose_set_title(compose
);
4982 compose_activate_privacy_system(compose
, account
, FALSE
);
4984 if (account
->default_sign
&& privacy_system_can_sign(compose
->privacy_system
) &&
4985 compose
->mode
!= COMPOSE_REDIRECT
)
4986 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
4988 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
4989 if (account
->default_encrypt
&& privacy_system_can_encrypt(compose
->privacy_system
) &&
4990 compose
->mode
!= COMPOSE_REDIRECT
)
4991 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
4993 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
4995 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
4996 undo_block(compose
->undostruct
);
4997 compose_insert_sig(compose
, TRUE
);
4998 undo_unblock(compose
->undostruct
);
5001 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
5002 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
5003 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
5004 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
5006 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
5007 if (account
->protocol
== A_NNTP
) {
5008 if (!strcmp(header
, _("To:")))
5009 combobox_select_by_text(
5010 GTK_COMBO_BOX(header_entry
->combo
),
5013 if (!strcmp(header
, _("Newsgroups:")))
5014 combobox_select_by_text(
5015 GTK_COMBO_BOX(header_entry
->combo
),
5023 /* use account's dict info if set */
5024 if (compose
->gtkaspell
) {
5025 if (account
->enable_default_dictionary
)
5026 gtkaspell_change_dict(compose
->gtkaspell
,
5027 account
->default_dictionary
, FALSE
);
5028 if (account
->enable_default_alt_dictionary
)
5029 gtkaspell_change_alt_dict(compose
->gtkaspell
,
5030 account
->default_alt_dictionary
);
5031 if (account
->enable_default_dictionary
5032 || account
->enable_default_alt_dictionary
)
5033 compose_spell_menu_changed(compose
);
5038 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
5039 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
5040 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
5041 gboolean recipient_found
= FALSE
;
5045 /* free to and newsgroup list */
5046 slist_free_strings_full(compose
->to_list
);
5047 compose
->to_list
= NULL
;
5049 slist_free_strings_full(compose
->newsgroup_list
);
5050 compose
->newsgroup_list
= NULL
;
5052 /* search header entries for to and newsgroup entries */
5053 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5056 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5057 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5060 if (entry
[0] != '\0') {
5061 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
5062 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5063 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
5064 recipient_found
= TRUE
;
5067 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
5068 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5069 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
5070 recipient_found
= TRUE
;
5077 return recipient_found
;
5080 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5082 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5083 gboolean found_other
= FALSE
;
5085 /* search header entries for to and newsgroup entries */
5086 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5089 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5090 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5093 if (strcmp(entry
, compose
->account
->auto_cc
)
5094 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5105 if (compose
->batch
) {
5106 gtk_widget_show_all(compose
->window
);
5108 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5109 prefs_common_translated_header_name("Cc"));
5110 aval
= alertpanel(_("Send"),
5112 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5114 if (aval
!= G_ALERTALTERNATE
)
5118 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5119 gboolean found_other
= FALSE
;
5121 /* search header entries for to and newsgroup entries */
5122 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5125 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5126 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5129 if (strcmp(entry
, compose
->account
->auto_bcc
)
5130 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5142 if (compose
->batch
) {
5143 gtk_widget_show_all(compose
->window
);
5145 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5146 prefs_common_translated_header_name("Bcc"));
5147 aval
= alertpanel(_("Send"),
5149 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5151 if (aval
!= G_ALERTALTERNATE
)
5158 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5162 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5163 if (compose
->batch
) {
5164 gtk_widget_show_all(compose
->window
);
5166 alertpanel_error(_("Recipient is not specified."));
5170 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5174 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5175 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5176 if (*str
== '\0' && check_everything
== TRUE
&&
5177 compose
->mode
!= COMPOSE_REDIRECT
) {
5181 message
= g_strdup_printf(_("Subject is empty. %s"),
5182 compose
->sending
?_("Send it anyway?"):
5183 _("Queue it anyway?"));
5185 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5186 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5187 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5189 if (aval
& G_ALERTDISABLE
) {
5190 aval
&= ~G_ALERTDISABLE
;
5191 prefs_common
.warn_empty_subj
= FALSE
;
5193 if (aval
!= G_ALERTALTERNATE
)
5198 if (!compose
->batch
&& prefs_common
.warn_sending_many_recipients_num
> 0
5199 && check_everything
== TRUE
) {
5203 /* count To and Cc recipients */
5204 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5208 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5209 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5212 if ((entry
[0] != '\0') &&
5213 (!strcmp(header
, prefs_common_translated_header_name("To:")) ||
5214 !strcmp(header
, prefs_common_translated_header_name("Cc:")))) {
5220 if (cnt
> prefs_common
.warn_sending_many_recipients_num
) {
5224 message
= g_strdup_printf(_("Sending to %d recipients. %s"), cnt
,
5225 compose
->sending
?_("Send it anyway?"):
5226 _("Queue it anyway?"));
5228 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5229 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5230 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5232 if (aval
& G_ALERTDISABLE
) {
5233 aval
&= ~G_ALERTDISABLE
;
5234 prefs_common
.warn_sending_many_recipients_num
= 0;
5236 if (aval
!= G_ALERTALTERNATE
)
5241 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5247 static void _display_queue_error(ComposeQueueResult val
)
5250 case COMPOSE_QUEUE_SUCCESS
:
5252 case COMPOSE_QUEUE_ERROR_NO_MSG
:
5253 alertpanel_error(_("Could not queue message."));
5255 case COMPOSE_QUEUE_ERROR_WITH_ERRNO
:
5256 alertpanel_error(_("Could not queue message:\n\n%s."),
5259 case COMPOSE_QUEUE_ERROR_SIGNING_FAILED
:
5260 alertpanel_error(_("Could not queue message for sending:\n\n"
5261 "Signature failed: %s"),
5262 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5264 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
:
5265 alertpanel_error(_("Could not queue message for sending:\n\n"
5266 "Encryption failed: %s"),
5267 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5269 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
:
5270 alertpanel_error(_("Could not queue message for sending:\n\n"
5271 "Charset conversion failed."));
5273 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
:
5274 alertpanel_error(_("Could not queue message for sending:\n\n"
5275 "Couldn't get recipient encryption key."));
5277 case COMPOSE_QUEUE_SIGNING_CANCELLED
:
5278 debug_print("signing cancelled\n");
5281 /* unhandled error */
5282 debug_print("oops, unhandled compose_queue() return value %d\n",
5288 gint
compose_send(Compose
*compose
)
5291 FolderItem
*folder
= NULL
;
5292 ComposeQueueResult val
= COMPOSE_QUEUE_ERROR_NO_MSG
;
5293 gchar
*msgpath
= NULL
;
5294 gboolean discard_window
= FALSE
;
5295 gchar
*errstr
= NULL
;
5296 gchar
*tmsgid
= NULL
;
5297 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5298 gboolean queued_removed
= FALSE
;
5300 if (prefs_common
.send_dialog_invisible
5301 || compose
->batch
== TRUE
)
5302 discard_window
= TRUE
;
5304 compose_allow_user_actions (compose
, FALSE
);
5305 compose
->sending
= TRUE
;
5307 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5308 if (compose
->batch
) {
5309 gtk_widget_show_all(compose
->window
);
5315 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5317 if (val
!= COMPOSE_QUEUE_SUCCESS
) {
5318 if (compose
->batch
) {
5319 gtk_widget_show_all(compose
->window
);
5322 _display_queue_error(val
);
5327 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5328 if (discard_window
) {
5329 compose
->sending
= FALSE
;
5330 compose_close(compose
);
5331 /* No more compose access in the normal codepath
5332 * after this point! */
5337 alertpanel_error(_("The message was queued but could not be "
5338 "sent.\nUse \"Send queued messages\" from "
5339 "the main window to retry."));
5340 if (!discard_window
) {
5347 if (msgpath
== NULL
) {
5348 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5349 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5352 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5353 claws_unlink(msgpath
);
5356 if (!discard_window
) {
5358 if (!queued_removed
)
5359 folder_item_remove_msg(folder
, msgnum
);
5360 folder_item_scan(folder
);
5362 /* make sure we delete that */
5363 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5365 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5366 folder_item_remove_msg(folder
, tmp
->msgnum
);
5367 procmsg_msginfo_free(&tmp
);
5374 if (!queued_removed
)
5375 folder_item_remove_msg(folder
, msgnum
);
5376 folder_item_scan(folder
);
5378 /* make sure we delete that */
5379 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5381 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5382 folder_item_remove_msg(folder
, tmp
->msgnum
);
5383 procmsg_msginfo_free(&tmp
);
5386 if (!discard_window
) {
5387 compose
->sending
= FALSE
;
5388 compose_allow_user_actions (compose
, TRUE
);
5389 compose_close(compose
);
5393 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5394 "the main window to retry."), errstr
);
5397 alertpanel_error_log(_("The message was queued but could not be "
5398 "sent.\nUse \"Send queued messages\" from "
5399 "the main window to retry."));
5401 if (!discard_window
) {
5410 toolbar_main_set_sensitive(mainwin
);
5411 main_window_set_menu_sensitive(mainwin
);
5417 compose_allow_user_actions (compose
, TRUE
);
5418 compose
->sending
= FALSE
;
5419 compose
->modified
= TRUE
;
5420 toolbar_main_set_sensitive(mainwin
);
5421 main_window_set_menu_sensitive(mainwin
);
5426 static gboolean
compose_use_attach(Compose
*compose
)
5428 GtkTreeModel
*model
= gtk_tree_view_get_model
5429 (GTK_TREE_VIEW(compose
->attach_clist
));
5430 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5433 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5436 gchar buf
[BUFFSIZE
];
5438 gboolean first_to_address
;
5439 gboolean first_cc_address
;
5441 ComposeHeaderEntry
*headerentry
;
5442 const gchar
*headerentryname
;
5443 const gchar
*cc_hdr
;
5444 const gchar
*to_hdr
;
5445 gboolean err
= FALSE
;
5447 debug_print("Writing redirect header\n");
5449 cc_hdr
= prefs_common_translated_header_name("Cc:");
5450 to_hdr
= prefs_common_translated_header_name("To:");
5452 first_to_address
= TRUE
;
5453 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5454 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5455 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5457 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5458 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5459 Xstrdup_a(str
, entstr
, return -1);
5461 if (str
[0] != '\0') {
5462 compose_convert_header
5463 (compose
, buf
, sizeof(buf
), str
,
5464 strlen("Resent-To") + 2, TRUE
);
5466 if (first_to_address
) {
5467 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5468 first_to_address
= FALSE
;
5470 err
|= (fprintf(fp
, ",") < 0);
5472 err
|= (fprintf(fp
, "%s", buf
) < 0);
5476 if (!first_to_address
) {
5477 err
|= (fprintf(fp
, "\n") < 0);
5480 first_cc_address
= TRUE
;
5481 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5482 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5483 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5485 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5486 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5487 Xstrdup_a(str
, strg
, return -1);
5489 if (str
[0] != '\0') {
5490 compose_convert_header
5491 (compose
, buf
, sizeof(buf
), str
,
5492 strlen("Resent-Cc") + 2, TRUE
);
5494 if (first_cc_address
) {
5495 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5496 first_cc_address
= FALSE
;
5498 err
|= (fprintf(fp
, ",") < 0);
5500 err
|= (fprintf(fp
, "%s", buf
) < 0);
5504 if (!first_cc_address
) {
5505 err
|= (fprintf(fp
, "\n") < 0);
5508 return (err
? -1:0);
5511 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5513 gchar date
[RFC822_DATE_BUFFSIZE
];
5514 gchar buf
[BUFFSIZE
];
5516 const gchar
*entstr
;
5517 /* struct utsname utsbuf; */
5518 gboolean err
= FALSE
;
5520 cm_return_val_if_fail(fp
!= NULL
, -1);
5521 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5522 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5525 if (prefs_common
.hide_timezone
)
5526 get_rfc822_date_hide_tz(date
, sizeof(date
));
5528 get_rfc822_date(date
, sizeof(date
));
5529 err
|= (fprintf(fp
, "Resent-Date: %s\n", date
) < 0);
5532 if (compose
->account
->name
&& *compose
->account
->name
) {
5533 compose_convert_header
5534 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5535 strlen("From: "), TRUE
);
5536 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5537 buf
, compose
->account
->address
) < 0);
5539 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5542 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5543 if (*entstr
!= '\0') {
5544 Xstrdup_a(str
, entstr
, return -1);
5547 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5548 strlen("Subject: "), FALSE
);
5549 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5553 /* Resent-Message-ID */
5554 if (compose
->account
->gen_msgid
) {
5555 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5556 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5558 g_free(compose
->msgid
);
5559 compose
->msgid
= addr
;
5561 compose
->msgid
= NULL
;
5564 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5567 /* separator between header and body */
5568 err
|= (claws_fputs("\n", fp
) == EOF
);
5570 return (err
? -1:0);
5573 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5578 gchar rewrite_buf
[BUFFSIZE
];
5580 gboolean skip
= FALSE
;
5581 gboolean err
= FALSE
;
5582 gchar
*not_included
[]={
5583 "Return-Path:", "Delivered-To:", "Received:",
5584 "Subject:", "X-UIDL:", "AF:",
5585 "NF:", "PS:", "SRH:",
5586 "SFN:", "DSR:", "MID:",
5587 "CFG:", "PT:", "S:",
5588 "RQ:", "SSV:", "NSV:",
5589 "SSH:", "R:", "MAID:",
5590 "NAID:", "RMID:", "FMID:",
5591 "SCF:", "RRCPT:", "NG:",
5592 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5593 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5594 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5595 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5596 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5601 if ((fp
= claws_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5602 FILE_OP_ERROR(compose
->redirect_filename
, "claws_fopen");
5606 while ((ret
= procheader_get_one_field_asis(&buf
, fp
)) != -1) {
5608 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5609 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5610 strlen(not_included
[i
])) == 0) {
5620 if (claws_fputs(buf
, fdest
) == -1) {
5626 if (!prefs_common
.redirect_keep_from
) {
5627 if (g_ascii_strncasecmp(buf
, "From:",
5628 strlen("From:")) == 0) {
5629 err
|= (claws_fputs(" (by way of ", fdest
) == EOF
);
5630 if (compose
->account
->name
5631 && *compose
->account
->name
) {
5632 gchar buffer
[BUFFSIZE
];
5634 compose_convert_header
5635 (compose
, buffer
, sizeof(buffer
),
5636 compose
->account
->name
,
5639 err
|= (fprintf(fdest
, "%s <%s>",
5641 compose
->account
->address
) < 0);
5643 err
|= (fprintf(fdest
, "%s",
5644 compose
->account
->address
) < 0);
5645 err
|= (claws_fputs(")", fdest
) == EOF
);
5651 if (claws_fputs("\n", fdest
) == -1)
5658 if (compose_redirect_write_headers(compose
, fdest
))
5661 while ((len
= claws_fread(rewrite_buf
, sizeof(gchar
), sizeof(rewrite_buf
), fp
)) > 0) {
5662 if (claws_fwrite(rewrite_buf
, sizeof(gchar
), len
, fdest
) != len
)
5676 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5678 GtkTextBuffer
*buffer
;
5679 GtkTextIter start
, end
, tmp
;
5680 gchar
*chars
, *tmp_enc_file
, *content
;
5682 const gchar
*out_codeset
;
5683 EncodingType encoding
= ENC_UNKNOWN
;
5684 MimeInfo
*mimemsg
, *mimetext
;
5686 const gchar
*src_codeset
= CS_INTERNAL
;
5687 gchar
*from_addr
= NULL
;
5688 gchar
*from_name
= NULL
;
5691 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5692 attach_parts
= TRUE
;
5694 /* We're sending the message, generate a Message-ID
5696 if (compose
->msgid
== NULL
&&
5697 compose
->account
->gen_msgid
) {
5698 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5702 /* create message MimeInfo */
5703 mimemsg
= procmime_mimeinfo_new();
5704 mimemsg
->type
= MIMETYPE_MESSAGE
;
5705 mimemsg
->subtype
= g_strdup("rfc822");
5706 mimemsg
->content
= MIMECONTENT_MEM
;
5707 mimemsg
->tmp
= TRUE
; /* must free content later */
5708 mimemsg
->data
.mem
= compose_get_header(compose
);
5710 /* Create text part MimeInfo */
5711 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5712 gtk_text_buffer_get_end_iter(buffer
, &end
);
5715 /* We make sure that there is a newline at the end. */
5716 if (action
== COMPOSE_WRITE_FOR_SEND
&& gtk_text_iter_backward_char(&tmp
)) {
5717 chars
= gtk_text_buffer_get_text(buffer
, &tmp
, &end
, FALSE
);
5718 if (*chars
!= '\n') {
5719 gtk_text_buffer_insert(buffer
, &end
, "\n", 1);
5724 /* get all composed text */
5725 gtk_text_buffer_get_start_iter(buffer
, &start
);
5726 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5728 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5730 if (!out_codeset
&& is_ascii_str(chars
)) {
5731 out_codeset
= CS_US_ASCII
;
5732 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5733 is_ascii_str(chars
)) {
5734 out_codeset
= CS_US_ASCII
;
5735 encoding
= ENC_7BIT
;
5739 gchar
*test_conv_global_out
= NULL
;
5740 gchar
*test_conv_reply
= NULL
;
5742 /* automatic mode. be automatic. */
5743 codeconv_set_strict(TRUE
);
5745 out_codeset
= conv_get_outgoing_charset_str();
5747 debug_print("trying to convert to %s\n", out_codeset
);
5748 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5751 if (!test_conv_global_out
&& compose
->orig_charset
5752 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5753 out_codeset
= compose
->orig_charset
;
5754 debug_print("failure; trying to convert to %s\n", out_codeset
);
5755 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5758 if (!test_conv_global_out
&& !test_conv_reply
) {
5760 out_codeset
= CS_INTERNAL
;
5761 debug_print("failure; finally using %s\n", out_codeset
);
5763 g_free(test_conv_global_out
);
5764 g_free(test_conv_reply
);
5765 codeconv_set_strict(FALSE
);
5768 if (encoding
== ENC_UNKNOWN
) {
5769 if (prefs_common
.encoding_method
== CTE_BASE64
)
5770 encoding
= ENC_BASE64
;
5771 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5772 encoding
= ENC_QUOTED_PRINTABLE
;
5773 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5774 encoding
= ENC_8BIT
;
5776 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5779 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5780 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5782 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5783 codeconv_set_strict(TRUE
);
5784 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5785 codeconv_set_strict(FALSE
);
5790 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5791 "to the specified %s charset.\n"
5792 "Send it as %s?"), out_codeset
, src_codeset
);
5793 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
,
5794 _("_Send"), NULL
, ALERTFOCUS_SECOND
, FALSE
,
5798 if (aval
!= G_ALERTALTERNATE
) {
5800 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
;
5803 out_codeset
= src_codeset
;
5809 out_codeset
= src_codeset
;
5814 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5815 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5816 strstr(buf
, "\nFrom ") != NULL
) {
5817 encoding
= ENC_QUOTED_PRINTABLE
;
5821 mimetext
= procmime_mimeinfo_new();
5822 mimetext
->content
= MIMECONTENT_MEM
;
5823 mimetext
->tmp
= TRUE
; /* must free content later */
5824 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5825 * and free the data, which we need later. */
5826 mimetext
->data
.mem
= g_strdup(buf
);
5827 mimetext
->type
= MIMETYPE_TEXT
;
5828 mimetext
->subtype
= g_strdup("plain");
5829 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5830 g_strdup(out_codeset
));
5832 /* protect trailing spaces when signing message */
5833 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5834 privacy_system_can_sign(compose
->privacy_system
)) {
5835 encoding
= ENC_QUOTED_PRINTABLE
;
5839 debug_print("main text: %Id bytes encoded as %s in %d\n",
5841 debug_print("main text: %zd bytes encoded as %s in %d\n",
5843 strlen(buf
), out_codeset
, encoding
);
5845 /* check for line length limit */
5846 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5847 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5848 check_line_length(buf
, 1000, &line
) < 0) {
5851 msg
= g_strdup_printf
5852 (_("Line %d exceeds the line length limit (998 bytes).\n"
5853 "The contents of the message might be broken on the way to the delivery.\n"
5855 "Send it anyway?"), line
+ 1);
5856 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
,
5859 if (aval
!= G_ALERTALTERNATE
) {
5861 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5865 if (encoding
!= ENC_UNKNOWN
)
5866 procmime_encode_content(mimetext
, encoding
);
5868 /* append attachment parts */
5869 if (compose_use_attach(compose
) && attach_parts
) {
5870 MimeInfo
*mimempart
;
5871 gchar
*boundary
= NULL
;
5872 mimempart
= procmime_mimeinfo_new();
5873 mimempart
->content
= MIMECONTENT_EMPTY
;
5874 mimempart
->type
= MIMETYPE_MULTIPART
;
5875 mimempart
->subtype
= g_strdup("mixed");
5879 boundary
= generate_mime_boundary(NULL
);
5880 } while (strstr(buf
, boundary
) != NULL
);
5882 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5885 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5887 g_node_append(mimempart
->node
, mimetext
->node
);
5888 g_node_append(mimemsg
->node
, mimempart
->node
);
5890 if (compose_add_attachments(compose
, mimempart
) < 0)
5891 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5893 g_node_append(mimemsg
->node
, mimetext
->node
);
5897 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5898 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5899 /* extract name and address */
5900 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5901 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5902 *(strrchr(from_addr
, '>')) = '\0';
5903 from_name
= g_strdup(spec
);
5904 *(strrchr(from_name
, '<')) = '\0';
5911 /* sign message if sending */
5912 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5913 privacy_system_can_sign(compose
->privacy_system
))
5914 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5915 compose
->account
, from_addr
)) {
5918 if (!privacy_peek_error())
5919 return COMPOSE_QUEUE_SIGNING_CANCELLED
;
5921 return COMPOSE_QUEUE_ERROR_SIGNING_FAILED
;
5926 if (compose
->use_encryption
) {
5927 if (compose
->encdata
!= NULL
&&
5928 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5930 /* First, write an unencrypted copy and save it to outbox, if
5931 * user wants that. */
5932 if (compose
->account
->save_encrypted_as_clear_text
) {
5933 debug_print("saving sent message unencrypted...\n");
5934 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5936 claws_fclose(tmpfp
);
5938 /* fp now points to a file with headers written,
5939 * let's make a copy. */
5941 content
= file_read_stream_to_str(fp
);
5943 str_write_to_file(content
, tmp_enc_file
, TRUE
);
5946 /* Now write the unencrypted body. */
5947 if ((tmpfp
= claws_fopen(tmp_enc_file
, "a")) != NULL
) {
5948 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5949 claws_fclose(tmpfp
);
5951 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5953 outbox
= folder_get_default_outbox();
5955 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5956 claws_unlink(tmp_enc_file
);
5958 g_warning("Can't open file '%s'", tmp_enc_file
);
5961 g_warning("couldn't get tempfile");
5964 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5965 debug_print("Couldn't encrypt mime structure: %s.\n",
5966 privacy_get_error());
5967 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
;
5972 procmime_write_mimeinfo(mimemsg
, fp
);
5974 procmime_mimeinfo_free_all(&mimemsg
);
5979 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5981 GtkTextBuffer
*buffer
;
5982 GtkTextIter start
, end
;
5987 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
5988 FILE_OP_ERROR(file
, "claws_fopen");
5992 /* chmod for security */
5993 if (change_file_mode_rw(fp
, file
) < 0) {
5994 FILE_OP_ERROR(file
, "chmod");
5995 g_warning("can't change file mode");
5998 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5999 gtk_text_buffer_get_start_iter(buffer
, &start
);
6000 gtk_text_buffer_get_end_iter(buffer
, &end
);
6001 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
6003 chars
= conv_codeset_strdup
6004 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
6013 len
= strlen(chars
);
6014 if (claws_fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
6015 FILE_OP_ERROR(file
, "claws_fwrite");
6024 if (claws_safe_fclose(fp
) == EOF
) {
6025 FILE_OP_ERROR(file
, "claws_fclose");
6032 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
6035 MsgInfo
*msginfo
= compose
->targetinfo
;
6037 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
6038 if (!msginfo
) return -1;
6040 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
6043 item
= msginfo
->folder
;
6044 cm_return_val_if_fail(item
!= NULL
, -1);
6046 if (procmsg_msg_exist(msginfo
) &&
6047 (folder_has_parent_of_type(item
, F_QUEUE
) ||
6048 folder_has_parent_of_type(item
, F_DRAFT
)
6049 || msginfo
== compose
->autosaved_draft
)) {
6050 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
6051 g_warning("can't remove the old message");
6054 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
6061 static void compose_remove_draft(Compose
*compose
)
6064 MsgInfo
*msginfo
= compose
->targetinfo
;
6065 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
6067 if (procmsg_msg_exist(msginfo
)) {
6068 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
6073 ComposeQueueResult
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
6074 gboolean remove_reedit_target
)
6076 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
6079 static gboolean
compose_warn_encryption(Compose
*compose
)
6081 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
6082 AlertValue val
= G_ALERTALTERNATE
;
6084 if (warning
== NULL
)
6087 val
= alertpanel_full(_("Encryption warning"), warning
,
6088 GTK_STOCK_CANCEL
, _("C_ontinue"), NULL
, ALERTFOCUS_SECOND
,
6089 TRUE
, NULL
, ALERT_WARNING
);
6090 if (val
& G_ALERTDISABLE
) {
6091 val
&= ~G_ALERTDISABLE
;
6092 if (val
== G_ALERTALTERNATE
)
6093 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
6097 if (val
== G_ALERTALTERNATE
) {
6104 static ComposeQueueResult
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
6105 gchar
**msgpath
, gboolean perform_checks
,
6106 gboolean remove_reedit_target
)
6113 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
6114 gboolean err
= FALSE
;
6116 debug_print("queueing message...\n");
6117 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
6119 if (compose_check_entries(compose
, perform_checks
) == FALSE
) {
6120 if (compose
->batch
) {
6121 gtk_widget_show_all(compose
->window
);
6123 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6126 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
6127 g_warning("can't get recipient list.");
6128 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6131 if (compose
->to_list
) {
6132 if (compose
->account
->protocol
!= A_NNTP
)
6133 mailac
= compose
->account
;
6134 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
6135 mailac
= cur_account
;
6136 else if (!(mailac
= compose_current_mail_account())) {
6137 alertpanel_error(_("No account for sending mails available!"));
6138 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6142 if (compose
->newsgroup_list
) {
6143 if (compose
->account
->protocol
== A_NNTP
)
6144 newsac
= compose
->account
;
6146 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6147 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6151 /* write queue header */
6152 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6153 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6154 debug_print("queuing to %s\n", tmp
);
6155 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6156 FILE_OP_ERROR(tmp
, "claws_fopen");
6158 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6161 if (change_file_mode_rw(fp
, tmp
) < 0) {
6162 FILE_OP_ERROR(tmp
, "chmod");
6163 g_warning("can't change file mode");
6166 /* queueing variables */
6167 err
|= (fprintf(fp
, "AF:\n") < 0);
6168 err
|= (fprintf(fp
, "NF:0\n") < 0);
6169 err
|= (fprintf(fp
, "PS:10\n") < 0);
6170 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6171 err
|= (fprintf(fp
, "SFN:\n") < 0);
6172 err
|= (fprintf(fp
, "DSR:\n") < 0);
6174 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6176 err
|= (fprintf(fp
, "MID:\n") < 0);
6177 err
|= (fprintf(fp
, "CFG:\n") < 0);
6178 err
|= (fprintf(fp
, "PT:0\n") < 0);
6179 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6180 err
|= (fprintf(fp
, "RQ:\n") < 0);
6182 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6184 err
|= (fprintf(fp
, "SSV:\n") < 0);
6186 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6188 err
|= (fprintf(fp
, "NSV:\n") < 0);
6189 err
|= (fprintf(fp
, "SSH:\n") < 0);
6190 /* write recipient list */
6191 if (compose
->to_list
) {
6192 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6193 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6195 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6196 err
|= (fprintf(fp
, "\n") < 0);
6198 /* write newsgroup list */
6199 if (compose
->newsgroup_list
) {
6200 err
|= (fprintf(fp
, "NG:") < 0);
6201 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6202 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6203 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6204 err
|= (fprintf(fp
, "\n") < 0);
6208 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6210 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6213 if (compose
->privacy_system
!= NULL
) {
6214 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6215 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6216 if (compose
->use_encryption
) {
6217 if (!compose_warn_encryption(compose
)) {
6221 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6223 if (mailac
&& mailac
->encrypt_to_self
) {
6224 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6225 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6226 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6227 g_slist_free(tmp_list
);
6229 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6231 if (compose
->encdata
!= NULL
) {
6232 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6233 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6234 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6235 compose
->encdata
) < 0);
6236 } /* else we finally dont want to encrypt */
6238 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6239 /* and if encdata was null, it means there's been a problem in
6242 g_warning("failed to write queue message");
6246 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6251 /* Save copy folder */
6252 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6253 gchar
*savefolderid
;
6255 savefolderid
= compose_get_save_to(compose
);
6256 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6257 g_free(savefolderid
);
6259 /* Save copy folder */
6260 if (compose
->return_receipt
) {
6261 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6263 /* Message-ID of message replying to */
6264 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6265 gchar
*folderid
= NULL
;
6267 if (compose
->replyinfo
->folder
)
6268 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6269 if (folderid
== NULL
)
6270 folderid
= g_strdup("NULL");
6272 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6275 /* Message-ID of message forwarding to */
6276 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6277 gchar
*folderid
= NULL
;
6279 if (compose
->fwdinfo
->folder
)
6280 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6281 if (folderid
== NULL
)
6282 folderid
= g_strdup("NULL");
6284 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6288 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6289 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6291 /* end of headers */
6292 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6294 if (compose
->redirect_filename
!= NULL
) {
6295 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6299 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6303 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6311 g_warning("failed to write queue message");
6315 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6317 if (claws_safe_fclose(fp
) == EOF
) {
6318 FILE_OP_ERROR(tmp
, "claws_fclose");
6321 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6324 if (item
&& *item
) {
6327 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6330 g_warning("can't find queue folder");
6333 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6335 folder_item_scan(queue
);
6336 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6337 g_warning("can't queue the message");
6340 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6343 if (msgpath
== NULL
) {
6349 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6350 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6352 procmsg_msginfo_change_flags(mi
,
6353 compose
->targetinfo
->flags
.perm_flags
,
6354 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6357 g_slist_free(mi
->tags
);
6358 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6359 procmsg_msginfo_free(&mi
);
6363 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6364 compose_remove_reedit_target(compose
, FALSE
);
6367 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6372 return COMPOSE_QUEUE_SUCCESS
;
6375 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6378 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6383 GError
*error
= NULL
;
6388 gchar
*type
, *subtype
;
6389 GtkTreeModel
*model
;
6392 model
= gtk_tree_view_get_model(tree_view
);
6394 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6397 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6399 if (!is_file_exist(ainfo
->file
)) {
6400 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6401 AlertValue val
= alertpanel_full(_("Warning"), msg
,
6402 _("Cancel sending"), _("Ignore attachment"), NULL
,
6403 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6405 if (val
== G_ALERTDEFAULT
) {
6411 f
= g_file_new_for_path(ainfo
->file
);
6412 fi
= g_file_query_info(f
, "standard::size",
6413 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6414 if (error
!= NULL
) {
6415 g_warning(error
->message
);
6416 g_error_free(error
);
6420 size
= g_file_info_get_size(fi
);
6424 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6426 size
= statbuf
.st_size
;
6429 mimepart
= procmime_mimeinfo_new();
6430 mimepart
->content
= MIMECONTENT_FILE
;
6431 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6432 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6433 mimepart
->offset
= 0;
6434 mimepart
->length
= size
;
6436 type
= g_strdup(ainfo
->content_type
);
6438 if (!strchr(type
, '/')) {
6440 type
= g_strdup("application/octet-stream");
6443 subtype
= strchr(type
, '/') + 1;
6444 *(subtype
- 1) = '\0';
6445 mimepart
->type
= procmime_get_media_type(type
);
6446 mimepart
->subtype
= g_strdup(subtype
);
6449 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6450 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6451 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6452 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6453 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6454 /* Text parts with no name come from multipart/alternative
6455 * forwards. Make sure the recipient won't look at the
6456 * original HTML part by mistake. */
6457 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6458 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6462 g_hash_table_insert(mimepart
->typeparameters
,
6463 g_strdup("charset"), g_strdup(ainfo
->charset
));
6465 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6466 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6467 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6468 g_hash_table_insert(mimepart
->typeparameters
,
6469 g_strdup("name"), g_strdup(ainfo
->name
));
6470 g_hash_table_insert(mimepart
->dispositionparameters
,
6471 g_strdup("filename"), g_strdup(ainfo
->name
));
6472 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6475 if (mimepart
->type
== MIMETYPE_MESSAGE
6476 || mimepart
->type
== MIMETYPE_MULTIPART
)
6477 ainfo
->encoding
= ENC_BINARY
;
6478 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6479 if (ainfo
->encoding
== ENC_7BIT
)
6480 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6481 else if (ainfo
->encoding
== ENC_8BIT
)
6482 ainfo
->encoding
= ENC_BASE64
;
6485 procmime_encode_content(mimepart
, ainfo
->encoding
);
6487 g_node_append(parent
->node
, mimepart
->node
);
6488 } while (gtk_tree_model_iter_next(model
, &iter
));
6493 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6495 GSList
*list
= NULL
, *item
= NULL
;
6496 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6498 list
= address_list_append_with_comments(list
, str
);
6499 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6500 gchar
*spec
= item
->data
;
6501 gchar
*endofname
= strstr(spec
, " <");
6502 if (endofname
!= NULL
) {
6505 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6506 qqname
= escape_internal_quotes(qname
, '"');
6508 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6509 gchar
*addr
= g_strdup(endofname
);
6510 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6511 faddr
= g_strconcat(name
, addr
, NULL
);
6514 debug_print("new auto-quoted address: '%s'\n", faddr
);
6518 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6520 result
= g_strconcat(result
,
6522 (faddr
!= NULL
)? faddr
: spec
,
6525 if (faddr
!= NULL
) {
6530 slist_free_strings_full(list
);
6535 #define IS_IN_CUSTOM_HEADER(header) \
6536 (compose->account->add_customhdr && \
6537 custom_header_find(compose->account->customhdr_list, header) != NULL)
6539 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6541 /* return the untranslated header name, if header_name is a known
6542 header name, in either its translated or untranslated form, with
6543 or without trailing colon. otherwise, returns header_name. */
6544 gchar
*translated_header_name
;
6545 gchar
*translated_header_name_wcolon
;
6546 const gchar
*untranslated_header_name
;
6547 const gchar
*untranslated_header_name_wcolon
;
6550 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6552 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6553 untranslated_header_name
= HEADERS
[i
].header_name
;
6554 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6556 translated_header_name
= gettext(untranslated_header_name
);
6557 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6559 if (!strcmp(header_name
, untranslated_header_name
) ||
6560 !strcmp(header_name
, translated_header_name
)) {
6561 return untranslated_header_name
;
6563 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6564 !strcmp(header_name
, translated_header_name_wcolon
)) {
6565 return untranslated_header_name_wcolon
;
6569 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6573 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6575 const gchar
*fieldname
,
6576 const gchar
*seperator
)
6578 gchar
*str
, *fieldname_w_colon
;
6579 gboolean add_field
= FALSE
;
6581 ComposeHeaderEntry
*headerentry
;
6582 const gchar
*headerentryname
;
6583 const gchar
*trans_fieldname
;
6586 if (IS_IN_CUSTOM_HEADER(fieldname
))
6589 debug_print("Adding %s-fields\n", fieldname
);
6591 fieldstr
= g_string_sized_new(64);
6593 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6594 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6596 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6597 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6598 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6600 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6601 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6603 str
= compose_quote_list_of_addresses(ustr
);
6605 if (str
!= NULL
&& str
[0] != '\0') {
6607 g_string_append(fieldstr
, seperator
);
6608 g_string_append(fieldstr
, str
);
6617 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6618 compose_convert_header
6619 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6620 strlen(fieldname
) + 2, TRUE
);
6621 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6625 g_free(fieldname_w_colon
);
6626 g_string_free(fieldstr
, TRUE
);
6631 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6633 GString
*sh_header
= g_string_new(" ");
6635 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6637 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6638 ComposeHeaderEntry
*headerentry
;
6641 gchar
*headername_wcolon
;
6642 const gchar
*headername_trans
;
6644 gboolean standard_header
= FALSE
;
6646 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6648 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6650 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6655 if (!strstr(tmp
, ":")) {
6656 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6657 headername
= g_strdup(tmp
);
6659 headername_wcolon
= g_strdup(tmp
);
6660 headername
= g_strdup(strtok(tmp
, ":"));
6664 string
= std_headers
;
6665 while (*string
!= NULL
) {
6666 headername_trans
= prefs_common_translated_header_name(*string
);
6667 if (!strcmp(headername_trans
, headername_wcolon
))
6668 standard_header
= TRUE
;
6671 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6672 g_string_append_printf(sh_header
, "%s ", headername
);
6674 g_free(headername_wcolon
);
6676 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6677 return g_string_free(sh_header
, FALSE
);
6680 static gchar
*compose_get_header(Compose
*compose
)
6682 gchar date
[RFC822_DATE_BUFFSIZE
];
6683 gchar buf
[BUFFSIZE
];
6684 const gchar
*entry_str
;
6688 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6690 gchar
*from_name
= NULL
, *from_address
= NULL
;
6693 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6694 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6696 header
= g_string_sized_new(64);
6699 if (prefs_common
.hide_timezone
)
6700 get_rfc822_date_hide_tz(date
, sizeof(date
));
6702 get_rfc822_date(date
, sizeof(date
));
6703 g_string_append_printf(header
, "Date: %s\n", date
);
6707 if (compose
->account
->name
&& *compose
->account
->name
) {
6709 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6710 tmp
= g_strdup_printf("%s <%s>",
6711 buf
, compose
->account
->address
);
6713 tmp
= g_strdup_printf("%s",
6714 compose
->account
->address
);
6716 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6717 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6719 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6720 from_address
= g_strdup(compose
->account
->address
);
6722 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6723 /* extract name and address */
6724 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6725 from_address
= g_strdup(strrchr(spec
, '<')+1);
6726 *(strrchr(from_address
, '>')) = '\0';
6727 from_name
= g_strdup(spec
);
6728 *(strrchr(from_name
, '<')) = '\0';
6731 from_address
= g_strdup(spec
);
6738 if (from_name
&& *from_name
) {
6740 compose_convert_header
6741 (compose
, buf
, sizeof(buf
), from_name
,
6742 strlen("From: "), TRUE
);
6743 QUOTE_IF_REQUIRED(name
, buf
);
6744 qname
= escape_internal_quotes(name
, '"');
6746 g_string_append_printf(header
, "From: %s <%s>\n",
6747 qname
, from_address
);
6748 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6749 compose
->return_receipt
) {
6750 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6751 strlen("Disposition-Notification-To: "),
6753 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6758 g_string_append_printf(header
, "From: %s\n", from_address
);
6759 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6760 compose
->return_receipt
)
6761 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6765 g_free(from_address
);
6768 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6771 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6774 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6778 * If this account is a NNTP account remove Bcc header from
6779 * message body since it otherwise will be publicly shown
6781 if (compose
->account
->protocol
!= A_NNTP
)
6782 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6785 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6787 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6790 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6791 strlen("Subject: "), FALSE
);
6792 g_string_append_printf(header
, "Subject: %s\n", buf
);
6798 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6799 g_string_append_printf(header
, "Message-ID: <%s>\n",
6803 if (compose
->remove_references
== FALSE
) {
6805 if (compose
->inreplyto
&& compose
->to_list
)
6806 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6809 if (compose
->references
)
6810 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6814 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6817 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6820 if (compose
->account
->organization
&&
6821 strlen(compose
->account
->organization
) &&
6822 !IS_IN_CUSTOM_HEADER("Organization")) {
6823 compose_convert_header(compose
, buf
, sizeof(buf
),
6824 compose
->account
->organization
,
6825 strlen("Organization: "), FALSE
);
6826 g_string_append_printf(header
, "Organization: %s\n", buf
);
6829 /* Program version and system info */
6830 if (compose
->account
->gen_xmailer
&&
6831 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6832 !compose
->newsgroup_list
) {
6833 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6835 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6838 if (compose
->account
->gen_xmailer
&&
6839 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6840 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6842 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6846 /* custom headers */
6847 if (compose
->account
->add_customhdr
) {
6850 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6852 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6854 if (custom_header_is_allowed(chdr
->name
)
6855 && chdr
->value
!= NULL
6856 && *(chdr
->value
) != '\0') {
6857 compose_convert_header
6858 (compose
, buf
, sizeof(buf
),
6860 strlen(chdr
->name
) + 2, FALSE
);
6861 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6866 /* Automatic Faces and X-Faces */
6867 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6868 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6870 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6871 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6873 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6874 g_string_append_printf(header
, "Face: %s\n", buf
);
6876 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6877 g_string_append_printf(header
, "Face: %s\n", buf
);
6881 switch (compose
->priority
) {
6882 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6883 "X-Priority: 1 (Highest)\n");
6885 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6886 "X-Priority: 2 (High)\n");
6888 case PRIORITY_NORMAL
: break;
6889 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6890 "X-Priority: 4 (Low)\n");
6892 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6893 "X-Priority: 5 (Lowest)\n");
6895 default: debug_print("compose: priority unknown : %d\n",
6899 /* get special headers */
6900 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6901 ComposeHeaderEntry
*headerentry
;
6904 gchar
*headername_wcolon
;
6905 const gchar
*headername_trans
;
6908 gboolean standard_header
= FALSE
;
6910 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6912 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6914 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6919 if (!strstr(tmp
, ":")) {
6920 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6921 headername
= g_strdup(tmp
);
6923 headername_wcolon
= g_strdup(tmp
);
6924 headername
= g_strdup(strtok(tmp
, ":"));
6928 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6929 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6930 subst_char(headervalue
, '\r', ' ');
6931 subst_char(headervalue
, '\n', ' ');
6932 g_strstrip(headervalue
);
6933 if (*headervalue
!= '\0') {
6934 string
= std_headers
;
6935 while (*string
!= NULL
&& !standard_header
) {
6936 headername_trans
= prefs_common_translated_header_name(*string
);
6937 /* support mixed translated and untranslated headers */
6938 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6939 standard_header
= TRUE
;
6942 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6943 /* store untranslated header name */
6944 g_string_append_printf(header
, "%s %s\n",
6945 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6949 g_free(headername_wcolon
);
6953 g_string_free(header
, FALSE
);
6958 #undef IS_IN_CUSTOM_HEADER
6960 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6961 gint header_len
, gboolean addr_field
)
6963 gchar
*tmpstr
= NULL
;
6964 const gchar
*out_codeset
= NULL
;
6966 cm_return_if_fail(src
!= NULL
);
6967 cm_return_if_fail(dest
!= NULL
);
6969 if (len
< 1) return;
6971 tmpstr
= g_strdup(src
);
6973 subst_char(tmpstr
, '\n', ' ');
6974 subst_char(tmpstr
, '\r', ' ');
6977 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6978 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6979 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
6984 codeconv_set_strict(TRUE
);
6985 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6986 conv_get_charset_str(compose
->out_encoding
));
6987 codeconv_set_strict(FALSE
);
6989 if (!dest
|| *dest
== '\0') {
6990 gchar
*test_conv_global_out
= NULL
;
6991 gchar
*test_conv_reply
= NULL
;
6993 /* automatic mode. be automatic. */
6994 codeconv_set_strict(TRUE
);
6996 out_codeset
= conv_get_outgoing_charset_str();
6998 debug_print("trying to convert to %s\n", out_codeset
);
6999 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7002 if (!test_conv_global_out
&& compose
->orig_charset
7003 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7004 out_codeset
= compose
->orig_charset
;
7005 debug_print("failure; trying to convert to %s\n", out_codeset
);
7006 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7009 if (!test_conv_global_out
&& !test_conv_reply
) {
7011 out_codeset
= CS_INTERNAL
;
7012 debug_print("finally using %s\n", out_codeset
);
7014 g_free(test_conv_global_out
);
7015 g_free(test_conv_reply
);
7016 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7018 codeconv_set_strict(FALSE
);
7023 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7027 cm_return_if_fail(user_data
!= NULL
);
7029 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7030 g_strstrip(address
);
7031 if (*address
!= '\0') {
7032 gchar
*name
= procheader_get_fromname(address
);
7033 extract_address(address
);
7034 #ifndef USE_ALT_ADDRBOOK
7035 addressbook_add_contact(name
, address
, NULL
, NULL
);
7037 debug_print("%s: %s\n", name
, address
);
7038 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7039 debug_print( "addressbook_add_contact - added\n" );
7046 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7048 GtkWidget
*menuitem
;
7051 cm_return_if_fail(menu
!= NULL
);
7052 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7054 menuitem
= gtk_separator_menu_item_new();
7055 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7056 gtk_widget_show(menuitem
);
7058 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7059 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7061 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7062 g_strstrip(address
);
7063 if (*address
== '\0') {
7064 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7067 g_signal_connect(G_OBJECT(menuitem
), "activate",
7068 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7069 gtk_widget_show(menuitem
);
7072 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7075 if (strcmp(header
, "")) {
7076 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7080 void compose_add_extra_header_entries(GtkListStore
*model
)
7084 gchar buf
[BUFFSIZE
];
7087 if (extra_headers
== NULL
) {
7088 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7089 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7090 debug_print("extra headers file not found\n");
7091 goto extra_headers_done
;
7093 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7094 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7095 while (lastc
>= 0 && buf
[lastc
] != ':')
7096 buf
[lastc
--] = '\0';
7097 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7098 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7099 if (custom_header_is_allowed(buf
)) {
7101 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7104 g_message("disallowed extra header line: %s\n", buf
);
7108 g_message("invalid extra header line: %s\n", buf
);
7114 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7115 extra_headers
= g_slist_reverse(extra_headers
);
7117 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7121 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7123 LdapServer
*server
= (LdapServer
*)data
;
7124 gboolean
*enable
= (gboolean
*)user_data
;
7126 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7127 server
->searchFlag
= *enable
;
7131 static void compose_create_header_entry(Compose
*compose
)
7133 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7140 const gchar
*header
= NULL
;
7141 ComposeHeaderEntry
*headerentry
;
7142 gboolean standard_header
= FALSE
;
7143 GtkListStore
*model
;
7146 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7148 /* Combo box model */
7149 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7150 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7152 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7154 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7156 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7157 COMPOSE_NEWSGROUPS
);
7158 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7160 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7161 COMPOSE_FOLLOWUPTO
);
7162 compose_add_extra_header_entries(model
);
7165 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7166 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7167 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7168 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7169 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7170 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7171 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7172 G_CALLBACK(compose_grab_focus_cb
), compose
);
7173 gtk_widget_show(combo
);
7175 /* Putting only the combobox child into focus chain of its parent causes
7176 * the parent to be skipped when changing focus via Tab or Shift+Tab.
7177 * This eliminates need to pres Tab twice in order to really get from the
7178 * combobox to next widget. */
7180 l
= g_list_prepend(l
, gtk_bin_get_child(GTK_BIN(combo
)));
7181 gtk_container_set_focus_chain(GTK_CONTAINER(combo
), l
);
7184 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
7185 compose
->header_nextrow
, compose
->header_nextrow
+1,
7186 GTK_SHRINK
, GTK_FILL
, 0, 0);
7187 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7188 const gchar
*last_header_entry
= gtk_entry_get_text(
7189 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7191 while (*string
!= NULL
) {
7192 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7193 standard_header
= TRUE
;
7196 if (standard_header
)
7197 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7199 if (!compose
->header_last
|| !standard_header
) {
7200 switch(compose
->account
->protocol
) {
7202 header
= prefs_common_translated_header_name("Newsgroups:");
7205 header
= prefs_common_translated_header_name("To:");
7210 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7212 gtk_editable_set_editable(
7213 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7214 prefs_common
.type_any_header
);
7216 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7217 G_CALLBACK(compose_grab_focus_cb
), compose
);
7219 /* Entry field with cleanup button */
7220 button
= gtk_button_new();
7221 gtk_button_set_image(GTK_BUTTON(button
),
7222 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
7223 gtk_widget_show(button
);
7224 CLAWS_SET_TIP(button
,
7225 _("Delete entry contents"));
7226 entry
= gtk_entry_new();
7227 gtk_widget_show(entry
);
7228 CLAWS_SET_TIP(entry
,
7229 _("Use <tab> to autocomplete from addressbook"));
7230 hbox
= gtk_hbox_new (FALSE
, 0);
7231 gtk_widget_show(hbox
);
7232 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7233 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7234 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
7235 compose
->header_nextrow
, compose
->header_nextrow
+1,
7236 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
7238 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7239 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7241 g_signal_connect(G_OBJECT(entry
), "changed",
7242 G_CALLBACK(compose_headerentry_changed_cb
),
7244 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7245 G_CALLBACK(compose_grab_focus_cb
), compose
);
7247 g_signal_connect(G_OBJECT(button
), "clicked",
7248 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7252 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7253 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7254 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7255 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7256 G_CALLBACK(compose_header_drag_received_cb
),
7258 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7259 G_CALLBACK(compose_drag_drop
),
7261 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7262 G_CALLBACK(compose_entry_popup_extend
),
7266 #ifndef PASSWORD_CRYPTO_OLD
7267 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7268 if (pwd_servers
!= NULL
&& master_passphrase() == NULL
) {
7269 gboolean enable
= FALSE
;
7270 debug_print("Master passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7271 /* Temporarily disable password-protected LDAP servers,
7272 * because user did not provide a master passphrase.
7273 * We can safely enable searchFlag on all servers in this list
7274 * later, since addrindex_get_password_protected_ldap_servers()
7275 * includes servers which have it enabled initially. */
7276 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7277 compose
->passworded_ldap_servers
= pwd_servers
;
7279 #endif /* PASSWORD_CRYPTO_OLD */
7280 #endif /* USE_LDAP */
7282 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7284 headerentry
->compose
= compose
;
7285 headerentry
->combo
= combo
;
7286 headerentry
->entry
= entry
;
7287 headerentry
->button
= button
;
7288 headerentry
->hbox
= hbox
;
7289 headerentry
->headernum
= compose
->header_nextrow
;
7290 headerentry
->type
= PREF_NONE
;
7292 compose
->header_nextrow
++;
7293 compose
->header_last
= headerentry
;
7294 compose
->header_list
=
7295 g_slist_append(compose
->header_list
,
7299 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7300 gchar
*text
, ComposePrefType pref_type
)
7302 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7303 gchar
*tmp
= g_strdup(text
), *email
;
7304 gboolean replyto_hdr
;
7306 replyto_hdr
= (!strcasecmp(header
,
7307 prefs_common_translated_header_name("Reply-To:")) ||
7309 prefs_common_translated_header_name("Followup-To:")) ||
7311 prefs_common_translated_header_name("In-Reply-To:")));
7313 extract_address(tmp
);
7314 email
= g_utf8_strdown(tmp
, -1);
7316 if (replyto_hdr
== FALSE
&&
7317 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7319 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7320 header
, text
, (gint
) pref_type
);
7326 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7327 gtk_entry_set_text(GTK_ENTRY(
7328 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7330 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7331 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7332 last_header
->type
= pref_type
;
7334 if (replyto_hdr
== FALSE
)
7335 g_hash_table_insert(compose
->email_hashtable
, email
,
7336 GUINT_TO_POINTER(1));
7343 static void compose_destroy_headerentry(Compose
*compose
,
7344 ComposeHeaderEntry
*headerentry
)
7346 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7349 extract_address(text
);
7350 email
= g_utf8_strdown(text
, -1);
7351 g_hash_table_remove(compose
->email_hashtable
, email
);
7355 gtk_widget_destroy(headerentry
->combo
);
7356 gtk_widget_destroy(headerentry
->entry
);
7357 gtk_widget_destroy(headerentry
->button
);
7358 gtk_widget_destroy(headerentry
->hbox
);
7359 g_free(headerentry
);
7362 static void compose_remove_header_entries(Compose
*compose
)
7365 for (list
= compose
->header_list
; list
; list
= list
->next
)
7366 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7368 compose
->header_last
= NULL
;
7369 g_slist_free(compose
->header_list
);
7370 compose
->header_list
= NULL
;
7371 compose
->header_nextrow
= 1;
7372 compose_create_header_entry(compose
);
7375 static GtkWidget
*compose_create_header(Compose
*compose
)
7377 GtkWidget
*from_optmenu_hbox
;
7378 GtkWidget
*header_table_main
;
7379 GtkWidget
*header_scrolledwin
;
7380 GtkWidget
*header_table
;
7382 /* parent with account selection and from header */
7383 header_table_main
= gtk_table_new(2, 2, FALSE
);
7384 gtk_widget_show(header_table_main
);
7385 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7387 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7388 gtk_table_attach(GTK_TABLE(header_table_main
), from_optmenu_hbox
,
7389 0, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7391 /* child with header labels and entries */
7392 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7393 gtk_widget_show(header_scrolledwin
);
7394 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7396 header_table
= gtk_table_new(2, 2, FALSE
);
7397 gtk_widget_show(header_table
);
7398 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7399 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
7400 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7401 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7402 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7404 gtk_table_attach(GTK_TABLE(header_table_main
), header_scrolledwin
,
7405 0, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 0, 2);
7407 compose
->header_table
= header_table
;
7408 compose
->header_list
= NULL
;
7409 compose
->header_nextrow
= 0;
7411 compose_create_header_entry(compose
);
7413 compose
->table
= NULL
;
7415 return header_table_main
;
7418 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7420 Compose
*compose
= (Compose
*)data
;
7421 GdkEventButton event
;
7424 event
.time
= gtk_get_current_event_time();
7426 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7429 static GtkWidget
*compose_create_attach(Compose
*compose
)
7431 GtkWidget
*attach_scrwin
;
7432 GtkWidget
*attach_clist
;
7434 GtkListStore
*store
;
7435 GtkCellRenderer
*renderer
;
7436 GtkTreeViewColumn
*column
;
7437 GtkTreeSelection
*selection
;
7439 /* attachment list */
7440 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7441 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7442 GTK_POLICY_AUTOMATIC
,
7443 GTK_POLICY_AUTOMATIC
);
7444 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7446 store
= gtk_list_store_new(N_ATTACH_COLS
,
7452 G_TYPE_AUTO_POINTER
,
7454 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7455 (GTK_TREE_MODEL(store
)));
7456 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7457 g_object_unref(store
);
7459 renderer
= gtk_cell_renderer_text_new();
7460 column
= gtk_tree_view_column_new_with_attributes
7461 (_("Mime type"), renderer
, "text",
7462 COL_MIMETYPE
, NULL
);
7463 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7465 renderer
= gtk_cell_renderer_text_new();
7466 column
= gtk_tree_view_column_new_with_attributes
7467 (_("Size"), renderer
, "text",
7469 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7471 renderer
= gtk_cell_renderer_text_new();
7472 column
= gtk_tree_view_column_new_with_attributes
7473 (_("Name"), renderer
, "text",
7475 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7477 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7478 prefs_common
.use_stripes_everywhere
);
7479 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7480 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7482 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7483 G_CALLBACK(attach_selected
), compose
);
7484 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7485 G_CALLBACK(attach_button_pressed
), compose
);
7486 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7487 G_CALLBACK(popup_attach_button_pressed
), compose
);
7488 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7489 G_CALLBACK(attach_key_pressed
), compose
);
7492 gtk_drag_dest_set(attach_clist
,
7493 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7494 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7495 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7496 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7497 G_CALLBACK(compose_attach_drag_received_cb
),
7499 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7500 G_CALLBACK(compose_drag_drop
),
7503 compose
->attach_scrwin
= attach_scrwin
;
7504 compose
->attach_clist
= attach_clist
;
7506 return attach_scrwin
;
7509 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7511 static GtkWidget
*compose_create_others(Compose
*compose
)
7514 GtkWidget
*savemsg_checkbtn
;
7515 GtkWidget
*savemsg_combo
;
7516 GtkWidget
*savemsg_select
;
7519 gchar
*folderidentifier
;
7521 /* Table for settings */
7522 table
= gtk_table_new(3, 1, FALSE
);
7523 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7524 gtk_widget_show(table
);
7525 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
7528 /* Save Message to folder */
7529 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7530 gtk_widget_show(savemsg_checkbtn
);
7531 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7532 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7533 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7536 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7537 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7538 compose
->savemsg_combo
= savemsg_combo
;
7539 gtk_widget_show(savemsg_combo
);
7541 if (prefs_common
.compose_save_to_history
)
7542 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7543 prefs_common
.compose_save_to_history
);
7544 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
7545 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7546 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7547 G_CALLBACK(compose_grab_focus_cb
), compose
);
7548 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7549 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7550 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7552 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7553 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7554 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7555 (compose
->account
, F_OUTBOX
));
7556 compose_set_save_to(compose
, folderidentifier
);
7557 g_free(folderidentifier
);
7560 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7561 gtk_widget_show(savemsg_select
);
7562 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7563 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7564 G_CALLBACK(compose_savemsg_select_cb
),
7570 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7575 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7576 _("Select folder to save message to"));
7579 path
= folder_item_get_identifier(dest
);
7581 compose_set_save_to(compose
, path
);
7585 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7586 GdkAtom clip
, GtkTextIter
*insert_place
);
7589 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7593 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7595 if (event
->button
== 3) {
7597 GtkTextIter sel_start
, sel_end
;
7598 gboolean stuff_selected
;
7600 /* move the cursor to allow GtkAspell to check the word
7601 * under the mouse */
7602 if (event
->x
&& event
->y
) {
7603 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7604 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7606 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7609 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7610 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7613 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7615 &sel_start
, &sel_end
);
7617 gtk_text_buffer_place_cursor (buffer
, &iter
);
7618 /* reselect stuff */
7620 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7621 gtk_text_buffer_select_range(buffer
,
7622 &sel_start
, &sel_end
);
7624 return FALSE
; /* pass the event so that the right-click goes through */
7627 if (event
->button
== 2) {
7632 /* get the middle-click position to paste at the correct place */
7633 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7634 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7636 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7639 entry_paste_clipboard(compose
, text
,
7640 prefs_common
.linewrap_pastes
,
7641 GDK_SELECTION_PRIMARY
, &iter
);
7649 static void compose_spell_menu_changed(void *data
)
7651 Compose
*compose
= (Compose
*)data
;
7653 GtkWidget
*menuitem
;
7654 GtkWidget
*parent_item
;
7655 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7658 if (compose
->gtkaspell
== NULL
)
7661 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7662 "/Menu/Spelling/Options");
7664 /* setting the submenu removes /Spelling/Options from the factory
7665 * so we need to save it */
7667 if (parent_item
== NULL
) {
7668 parent_item
= compose
->aspell_options_menu
;
7669 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7671 compose
->aspell_options_menu
= parent_item
;
7673 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7675 spell_menu
= g_slist_reverse(spell_menu
);
7676 for (items
= spell_menu
;
7677 items
; items
= items
->next
) {
7678 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7679 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7680 gtk_widget_show(GTK_WIDGET(menuitem
));
7682 g_slist_free(spell_menu
);
7684 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7685 gtk_widget_show(parent_item
);
7688 static void compose_dict_changed(void *data
)
7690 Compose
*compose
= (Compose
*) data
;
7692 if(!compose
->gtkaspell
)
7694 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7697 gtkaspell_highlight_all(compose
->gtkaspell
);
7698 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7702 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7704 Compose
*compose
= (Compose
*)data
;
7705 GdkEventButton event
;
7708 event
.time
= gtk_get_current_event_time();
7712 return text_clicked(compose
->text
, &event
, compose
);
7715 static gboolean compose_force_window_origin
= TRUE
;
7716 static Compose
*compose_create(PrefsAccount
*account
,
7725 GtkWidget
*handlebox
;
7727 GtkWidget
*notebook
;
7729 GtkWidget
*attach_hbox
;
7730 GtkWidget
*attach_lab1
;
7731 GtkWidget
*attach_lab2
;
7736 GtkWidget
*subject_hbox
;
7737 GtkWidget
*subject_frame
;
7738 GtkWidget
*subject_entry
;
7742 GtkWidget
*edit_vbox
;
7743 GtkWidget
*ruler_hbox
;
7745 GtkWidget
*scrolledwin
;
7747 GtkTextBuffer
*buffer
;
7748 GtkClipboard
*clipboard
;
7750 UndoMain
*undostruct
;
7752 GtkWidget
*popupmenu
;
7753 GtkWidget
*tmpl_menu
;
7754 GtkActionGroup
*action_group
= NULL
;
7757 GtkAspell
* gtkaspell
= NULL
;
7760 static GdkGeometry geometry
;
7762 cm_return_val_if_fail(account
!= NULL
, NULL
);
7764 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7765 &default_header_bgcolor
);
7766 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER
],
7767 &default_header_color
);
7769 debug_print("Creating compose window...\n");
7770 compose
= g_new0(Compose
, 1);
7772 compose
->batch
= batch
;
7773 compose
->account
= account
;
7774 compose
->folder
= folder
;
7776 compose
->mutex
= cm_mutex_new();
7777 compose
->set_cursor_pos
= -1;
7779 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7781 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7782 gtk_widget_set_size_request(window
, prefs_common
.compose_width
,
7783 prefs_common
.compose_height
);
7785 if (!geometry
.max_width
) {
7786 geometry
.max_width
= gdk_screen_width();
7787 geometry
.max_height
= gdk_screen_height();
7790 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7791 &geometry
, GDK_HINT_MAX_SIZE
);
7792 if (!geometry
.min_width
) {
7793 geometry
.min_width
= 600;
7794 geometry
.min_height
= 440;
7796 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7797 &geometry
, GDK_HINT_MIN_SIZE
);
7799 #ifndef GENERIC_UMPC
7800 if (compose_force_window_origin
)
7801 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7802 prefs_common
.compose_y
);
7804 g_signal_connect(G_OBJECT(window
), "delete_event",
7805 G_CALLBACK(compose_delete_cb
), compose
);
7806 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7807 gtk_widget_realize(window
);
7809 gtkut_widget_set_composer_icon(window
);
7811 vbox
= gtk_vbox_new(FALSE
, 0);
7812 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7814 compose
->ui_manager
= gtk_ui_manager_new();
7815 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7816 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7817 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7818 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7819 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7820 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7821 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7822 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7823 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7824 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7826 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7828 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7829 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7831 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7833 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7834 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7835 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7838 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7839 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7840 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7841 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7842 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7843 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7844 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7845 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7846 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7847 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7848 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7849 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7850 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7853 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7854 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7855 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7859 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7861 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7862 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7863 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7866 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7868 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7869 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7870 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7873 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7874 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7875 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7877 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7878 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7879 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7881 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7882 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7884 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7886 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7887 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7888 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7890 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7894 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7898 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7899 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7900 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7901 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7902 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7903 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7907 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7908 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7909 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7913 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7914 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7915 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7916 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7917 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7921 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7922 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7923 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7924 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7925 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7926 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7928 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7929 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7930 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7934 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7936 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7937 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7938 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7940 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7942 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7943 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
)
7944 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
)
7945 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7947 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7949 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7950 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
)
7951 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
)
7953 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7955 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7956 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
)
7957 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7959 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7960 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
)
7961 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7963 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7965 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7966 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
)
7967 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7968 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7969 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7970 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7972 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7973 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
)
7974 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
)
7975 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7976 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7978 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7979 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7980 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7981 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
7982 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
7983 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
7985 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
7986 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
7987 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
)
7989 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
7990 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
7991 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
7995 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
7996 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
7997 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7999 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8000 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8003 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8005 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8006 gtk_widget_show_all(menubar
);
8008 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8009 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8011 if (prefs_common
.toolbar_detachable
) {
8012 handlebox
= gtk_handle_box_new();
8014 handlebox
= gtk_hbox_new(FALSE
, 0);
8016 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8018 gtk_widget_realize(handlebox
);
8019 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8022 vbox2
= gtk_vbox_new(FALSE
, 2);
8023 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8024 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8027 notebook
= gtk_notebook_new();
8028 gtk_widget_show(notebook
);
8030 /* header labels and entries */
8031 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8032 compose_create_header(compose
),
8033 gtk_label_new_with_mnemonic(_("Hea_der")));
8034 /* attachment list */
8035 attach_hbox
= gtk_hbox_new(FALSE
, 0);
8036 gtk_widget_show(attach_hbox
);
8038 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8039 gtk_widget_show(attach_lab1
);
8040 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8042 attach_lab2
= gtk_label_new("");
8043 gtk_widget_show(attach_lab2
);
8044 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8046 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8047 compose_create_attach(compose
),
8050 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8051 compose_create_others(compose
),
8052 gtk_label_new_with_mnemonic(_("Othe_rs")));
8055 subject_hbox
= gtk_hbox_new(FALSE
, 0);
8056 gtk_widget_show(subject_hbox
);
8058 subject_frame
= gtk_frame_new(NULL
);
8059 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8060 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8061 gtk_widget_show(subject_frame
);
8063 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
8064 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8065 gtk_widget_show(subject
);
8067 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8068 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8069 gtk_widget_show(label
);
8072 subject_entry
= claws_spell_entry_new();
8074 subject_entry
= gtk_entry_new();
8076 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8077 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8078 G_CALLBACK(compose_grab_focus_cb
), compose
);
8079 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8080 gtk_widget_show(subject_entry
);
8081 compose
->subject_entry
= subject_entry
;
8082 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8084 edit_vbox
= gtk_vbox_new(FALSE
, 0);
8086 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8089 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
8090 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8092 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8093 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8094 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8098 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8099 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8100 GTK_POLICY_AUTOMATIC
,
8101 GTK_POLICY_AUTOMATIC
);
8102 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8104 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8106 text
= gtk_text_view_new();
8107 if (prefs_common
.show_compose_margin
) {
8108 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8109 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8111 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8112 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8113 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8114 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8115 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8117 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8118 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8119 G_CALLBACK(compose_edit_size_alloc
),
8121 g_signal_connect(G_OBJECT(buffer
), "changed",
8122 G_CALLBACK(compose_changed_cb
), compose
);
8123 g_signal_connect(G_OBJECT(text
), "grab_focus",
8124 G_CALLBACK(compose_grab_focus_cb
), compose
);
8125 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8126 G_CALLBACK(text_inserted
), compose
);
8127 g_signal_connect(G_OBJECT(text
), "button_press_event",
8128 G_CALLBACK(text_clicked
), compose
);
8129 g_signal_connect(G_OBJECT(text
), "popup-menu",
8130 G_CALLBACK(compose_popup_menu
), compose
);
8131 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8132 G_CALLBACK(compose_changed_cb
), compose
);
8133 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8134 G_CALLBACK(compose_subject_entry_activated
), compose
);
8137 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8138 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8139 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8140 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8141 G_CALLBACK(compose_insert_drag_received_cb
),
8143 g_signal_connect(G_OBJECT(text
), "drag-drop",
8144 G_CALLBACK(compose_drag_drop
),
8146 g_signal_connect(G_OBJECT(text
), "key-press-event",
8147 G_CALLBACK(completion_set_focus_to_subject
),
8149 gtk_widget_show_all(vbox
);
8151 /* pane between attach clist and text */
8152 paned
= gtk_vpaned_new();
8153 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
8154 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8155 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8156 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8157 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8158 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8160 gtk_widget_show_all(paned
);
8163 if (prefs_common
.textfont
) {
8164 PangoFontDescription
*font_desc
;
8166 font_desc
= pango_font_description_from_string
8167 (prefs_common
.textfont
);
8169 gtk_widget_modify_font(text
, font_desc
);
8170 pango_font_description_free(font_desc
);
8174 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8175 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8176 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8177 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8178 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8179 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8180 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8181 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8183 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8185 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8186 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8187 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8189 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8191 undostruct
= undo_init(text
);
8192 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8195 address_completion_start(window
);
8197 compose
->window
= window
;
8198 compose
->vbox
= vbox
;
8199 compose
->menubar
= menubar
;
8200 compose
->handlebox
= handlebox
;
8202 compose
->vbox2
= vbox2
;
8204 compose
->paned
= paned
;
8206 compose
->attach_label
= attach_lab2
;
8208 compose
->notebook
= notebook
;
8209 compose
->edit_vbox
= edit_vbox
;
8210 compose
->ruler_hbox
= ruler_hbox
;
8211 compose
->ruler
= ruler
;
8212 compose
->scrolledwin
= scrolledwin
;
8213 compose
->text
= text
;
8215 compose
->focused_editable
= NULL
;
8217 compose
->popupmenu
= popupmenu
;
8219 compose
->tmpl_menu
= tmpl_menu
;
8221 compose
->mode
= mode
;
8222 compose
->rmode
= mode
;
8224 compose
->targetinfo
= NULL
;
8225 compose
->replyinfo
= NULL
;
8226 compose
->fwdinfo
= NULL
;
8228 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8229 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8231 compose
->replyto
= NULL
;
8233 compose
->bcc
= NULL
;
8234 compose
->followup_to
= NULL
;
8236 compose
->ml_post
= NULL
;
8238 compose
->inreplyto
= NULL
;
8239 compose
->references
= NULL
;
8240 compose
->msgid
= NULL
;
8241 compose
->boundary
= NULL
;
8243 compose
->autowrap
= prefs_common
.autowrap
;
8244 compose
->autoindent
= prefs_common
.auto_indent
;
8245 compose
->use_signing
= FALSE
;
8246 compose
->use_encryption
= FALSE
;
8247 compose
->privacy_system
= NULL
;
8248 compose
->encdata
= NULL
;
8250 compose
->modified
= FALSE
;
8252 compose
->return_receipt
= FALSE
;
8254 compose
->to_list
= NULL
;
8255 compose
->newsgroup_list
= NULL
;
8257 compose
->undostruct
= undostruct
;
8259 compose
->sig_str
= NULL
;
8261 compose
->exteditor_file
= NULL
;
8262 compose
->exteditor_pid
= -1;
8263 compose
->exteditor_tag
= -1;
8264 compose
->exteditor_socket
= NULL
;
8265 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8267 compose
->folder_update_callback_id
=
8268 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8269 compose_update_folder_hook
,
8270 (gpointer
) compose
);
8273 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8274 if (mode
!= COMPOSE_REDIRECT
) {
8275 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8276 strcmp(prefs_common
.dictionary
, "")) {
8277 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8278 prefs_common
.alt_dictionary
,
8279 conv_get_locale_charset_str(),
8280 prefs_common
.color
[COL_MISSPELLED
],
8281 prefs_common
.check_while_typing
,
8282 prefs_common
.recheck_when_changing_dict
,
8283 prefs_common
.use_alternate
,
8284 prefs_common
.use_both_dicts
,
8285 GTK_TEXT_VIEW(text
),
8286 GTK_WINDOW(compose
->window
),
8287 compose_dict_changed
,
8288 compose_spell_menu_changed
,
8291 alertpanel_error(_("Spell checker could not "
8293 gtkaspell_checkers_strerror());
8294 gtkaspell_checkers_reset_error();
8296 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8300 compose
->gtkaspell
= gtkaspell
;
8301 compose_spell_menu_changed(compose
);
8302 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8305 compose_select_account(compose
, account
, TRUE
);
8307 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8308 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8310 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8311 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8313 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8314 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8316 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8317 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8319 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8320 if (account
->protocol
!= A_NNTP
)
8321 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8322 prefs_common_translated_header_name("To:"));
8324 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8325 prefs_common_translated_header_name("Newsgroups:"));
8327 #ifndef USE_ALT_ADDRBOOK
8328 addressbook_set_target_compose(compose
);
8330 if (mode
!= COMPOSE_REDIRECT
)
8331 compose_set_template_menu(compose
);
8333 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8336 compose_list
= g_list_append(compose_list
, compose
);
8338 if (!prefs_common
.show_ruler
)
8339 gtk_widget_hide(ruler_hbox
);
8341 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8344 compose
->priority
= PRIORITY_NORMAL
;
8345 compose_update_priority_menu_item(compose
);
8347 compose_set_out_encoding(compose
);
8350 compose_update_actions_menu(compose
);
8352 /* Privacy Systems menu */
8353 compose_update_privacy_systems_menu(compose
);
8354 compose_activate_privacy_system(compose
, account
, TRUE
);
8356 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8358 gtk_widget_realize(window
);
8360 gtk_widget_show(window
);
8366 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8371 GtkWidget
*optmenubox
;
8372 GtkWidget
*fromlabel
;
8375 GtkWidget
*from_name
= NULL
;
8377 gint num
= 0, def_menu
= 0;
8379 accounts
= account_get_list();
8380 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8382 optmenubox
= gtk_event_box_new();
8383 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8384 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8386 hbox
= gtk_hbox_new(FALSE
, 4);
8387 from_name
= gtk_entry_new();
8389 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8390 G_CALLBACK(compose_grab_focus_cb
), compose
);
8391 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8392 G_CALLBACK(from_name_activate_cb
), optmenu
);
8394 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8395 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8396 gchar
*name
, *from
= NULL
;
8398 if (ac
== compose
->account
) def_menu
= num
;
8400 name
= g_markup_printf_escaped("<i>%s</i>",
8403 if (ac
== compose
->account
) {
8404 if (ac
->name
&& *ac
->name
) {
8406 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8407 from
= g_strdup_printf("%s <%s>",
8409 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8411 from
= g_strdup_printf("%s",
8413 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8415 if (cur_account
!= compose
->account
) {
8416 gtk_widget_modify_base(
8417 GTK_WIDGET(from_name
),
8418 GTK_STATE_NORMAL
, &default_header_bgcolor
);
8419 gtk_widget_modify_text(
8420 GTK_WIDGET(from_name
),
8421 GTK_STATE_NORMAL
, &default_header_color
);
8424 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8429 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8431 g_signal_connect(G_OBJECT(optmenu
), "changed",
8432 G_CALLBACK(account_activated
),
8434 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8435 G_CALLBACK(compose_entry_popup_extend
),
8438 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8439 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8441 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8442 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8443 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8445 /* Putting only the GtkEntry into focus chain of parent hbox causes
8446 * the account selector combobox next to it to be unreachable when
8447 * navigating widgets in GtkTable with up/down arrow keys.
8448 * Note: gtk_widget_set_can_focus() was not enough. */
8450 l
= g_list_prepend(l
, from_name
);
8451 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), l
);
8454 CLAWS_SET_TIP(optmenubox
,
8455 _("Account to use for this email"));
8456 CLAWS_SET_TIP(from_name
,
8457 _("Sender address to be used"));
8459 compose
->account_combo
= optmenu
;
8460 compose
->from_name
= from_name
;
8465 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8467 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8468 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8469 Compose
*compose
= (Compose
*) data
;
8471 compose
->priority
= value
;
8475 static void compose_reply_change_mode(Compose
*compose
,
8478 gboolean was_modified
= compose
->modified
;
8480 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8482 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8484 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8486 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8488 if (action
== COMPOSE_REPLY_TO_ALL
)
8490 if (action
== COMPOSE_REPLY_TO_SENDER
)
8492 if (action
== COMPOSE_REPLY_TO_LIST
)
8495 compose_remove_header_entries(compose
);
8496 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8497 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8498 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8500 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8501 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8503 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8504 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8505 compose_show_first_last_header(compose
, TRUE
);
8506 compose
->modified
= was_modified
;
8507 compose_set_title(compose
);
8510 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8512 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8513 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8514 Compose
*compose
= (Compose
*) data
;
8517 compose_reply_change_mode(compose
, value
);
8520 static void compose_update_priority_menu_item(Compose
* compose
)
8522 GtkWidget
*menuitem
= NULL
;
8523 switch (compose
->priority
) {
8524 case PRIORITY_HIGHEST
:
8525 menuitem
= gtk_ui_manager_get_widget
8526 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8529 menuitem
= gtk_ui_manager_get_widget
8530 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8532 case PRIORITY_NORMAL
:
8533 menuitem
= gtk_ui_manager_get_widget
8534 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8537 menuitem
= gtk_ui_manager_get_widget
8538 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8540 case PRIORITY_LOWEST
:
8541 menuitem
= gtk_ui_manager_get_widget
8542 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8545 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8548 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8550 Compose
*compose
= (Compose
*) data
;
8552 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8554 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8556 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8559 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8560 g_free(compose
->privacy_system
);
8561 compose
->privacy_system
= NULL
;
8562 g_free(compose
->encdata
);
8563 compose
->encdata
= NULL
;
8564 if (systemid
!= NULL
) {
8565 compose
->privacy_system
= g_strdup(systemid
);
8567 can_sign
= privacy_system_can_sign(systemid
);
8568 can_encrypt
= privacy_system_can_encrypt(systemid
);
8571 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8573 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8574 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8575 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8576 gtk_widget_set_sensitive(
8577 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8579 gtk_toggle_tool_button_set_active(
8580 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8581 can_sign
? compose
->use_signing
: FALSE
);
8583 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8584 gtk_widget_set_sensitive(
8585 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8587 gtk_toggle_tool_button_set_active(
8588 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8589 can_encrypt
? compose
->use_encryption
: FALSE
);
8593 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8595 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8596 GtkWidget
*menuitem
= NULL
;
8597 GList
*children
, *amenu
;
8598 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8599 gboolean found
= FALSE
;
8601 if (compose
->privacy_system
!= NULL
) {
8603 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8604 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8605 cm_return_if_fail(menuitem
!= NULL
);
8607 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8610 while (amenu
!= NULL
) {
8611 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8612 if (systemid
!= NULL
) {
8613 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8614 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8615 menuitem
= GTK_WIDGET(amenu
->data
);
8617 can_sign
= privacy_system_can_sign(systemid
);
8618 can_encrypt
= privacy_system_can_encrypt(systemid
);
8622 } else if (strlen(compose
->privacy_system
) == 0 &&
8623 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8624 menuitem
= GTK_WIDGET(amenu
->data
);
8627 can_encrypt
= FALSE
;
8632 amenu
= amenu
->next
;
8634 g_list_free(children
);
8635 if (menuitem
!= NULL
)
8636 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8638 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8639 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8640 "will not be able to sign or encrypt this message."),
8641 compose
->privacy_system
);
8645 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8646 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8647 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8648 gtk_widget_set_sensitive(
8649 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8652 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8653 gtk_widget_set_sensitive(
8654 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8659 static void compose_set_out_encoding(Compose
*compose
)
8661 CharSet out_encoding
;
8662 const gchar
*branch
= NULL
;
8663 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8665 switch(out_encoding
) {
8666 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8667 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8668 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8669 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8670 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8671 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8672 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8673 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8674 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8675 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8676 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8677 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8678 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8679 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8680 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8681 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8682 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8683 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8684 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8685 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8686 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8687 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8688 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8689 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8690 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8691 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8692 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8693 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8694 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8695 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8696 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8697 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8698 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8699 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8701 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8704 static void compose_set_template_menu(Compose
*compose
)
8706 GSList
*tmpl_list
, *cur
;
8710 tmpl_list
= template_get_config();
8712 menu
= gtk_menu_new();
8714 gtk_menu_set_accel_group (GTK_MENU (menu
),
8715 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8716 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8717 Template
*tmpl
= (Template
*)cur
->data
;
8718 gchar
*accel_path
= NULL
;
8719 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8720 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8721 g_signal_connect(G_OBJECT(item
), "activate",
8722 G_CALLBACK(compose_template_activate_cb
),
8724 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8725 gtk_widget_show(item
);
8726 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8727 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8731 gtk_widget_show(menu
);
8732 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8735 void compose_update_actions_menu(Compose
*compose
)
8737 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8740 static void compose_update_privacy_systems_menu(Compose
*compose
)
8742 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8743 GSList
*systems
, *cur
;
8745 GtkWidget
*system_none
;
8747 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8748 GtkWidget
*privacy_menu
= gtk_menu_new();
8750 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8751 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8753 g_signal_connect(G_OBJECT(system_none
), "activate",
8754 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8756 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8757 gtk_widget_show(system_none
);
8759 systems
= privacy_get_system_ids();
8760 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8761 gchar
*systemid
= cur
->data
;
8763 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8764 widget
= gtk_radio_menu_item_new_with_label(group
,
8765 privacy_system_get_name(systemid
));
8766 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8767 g_strdup(systemid
), g_free
);
8768 g_signal_connect(G_OBJECT(widget
), "activate",
8769 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8771 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8772 gtk_widget_show(widget
);
8775 g_slist_free(systems
);
8776 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8777 gtk_widget_show_all(privacy_menu
);
8778 gtk_widget_show_all(privacy_menuitem
);
8781 void compose_reflect_prefs_all(void)
8786 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8787 compose
= (Compose
*)cur
->data
;
8788 compose_set_template_menu(compose
);
8792 void compose_reflect_prefs_pixmap_theme(void)
8797 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8798 compose
= (Compose
*)cur
->data
;
8799 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8803 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8805 const gchar
*qmark
= NULL
;
8807 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8809 switch (compose
->mode
) {
8810 /* use forward-specific quote char */
8811 case COMPOSE_FORWARD
:
8812 case COMPOSE_FORWARD_AS_ATTACH
:
8813 case COMPOSE_FORWARD_INLINE
:
8814 if (compose
->folder
&& compose
->folder
->prefs
&&
8815 compose
->folder
->prefs
->forward_with_format
)
8816 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8817 else if (compose
->account
->forward_with_format
)
8818 qmark
= compose
->account
->forward_quotemark
;
8820 qmark
= prefs_common
.fw_quotemark
;
8823 /* use reply-specific quote char in all other modes */
8825 if (compose
->folder
&& compose
->folder
->prefs
&&
8826 compose
->folder
->prefs
->reply_with_format
)
8827 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8828 else if (compose
->account
->reply_with_format
)
8829 qmark
= compose
->account
->reply_quotemark
;
8831 qmark
= prefs_common
.quotemark
;
8835 if (qmark
== NULL
|| *qmark
== '\0')
8841 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8845 GtkTextBuffer
*buffer
;
8849 gchar
*parsed_str
= NULL
;
8850 gint cursor_pos
= 0;
8851 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8854 /* process the body */
8856 text
= GTK_TEXT_VIEW(compose
->text
);
8857 buffer
= gtk_text_view_get_buffer(text
);
8860 qmark
= compose_quote_char_from_context(compose
);
8862 if (compose
->replyinfo
!= NULL
) {
8865 gtk_text_buffer_set_text(buffer
, "", -1);
8866 mark
= gtk_text_buffer_get_insert(buffer
);
8867 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8869 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8870 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8872 } else if (compose
->fwdinfo
!= NULL
) {
8875 gtk_text_buffer_set_text(buffer
, "", -1);
8876 mark
= gtk_text_buffer_get_insert(buffer
);
8877 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8879 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8880 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8883 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8885 GtkTextIter start
, end
;
8888 gtk_text_buffer_get_start_iter(buffer
, &start
);
8889 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8890 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8892 /* clear the buffer now */
8894 gtk_text_buffer_set_text(buffer
, "", -1);
8896 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8897 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8898 procmsg_msginfo_free( &dummyinfo
);
8904 gtk_text_buffer_set_text(buffer
, "", -1);
8905 mark
= gtk_text_buffer_get_insert(buffer
);
8906 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8909 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8910 compose_insert_sig(compose
, FALSE
);
8912 if (replace
&& parsed_str
) {
8913 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8914 gtk_text_buffer_place_cursor(buffer
, &iter
);
8918 cursor_pos
= quote_fmt_get_cursor_pos();
8919 compose
->set_cursor_pos
= cursor_pos
;
8920 if (cursor_pos
== -1)
8922 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8923 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8924 gtk_text_buffer_place_cursor(buffer
, &iter
);
8927 /* process the other fields */
8929 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8930 compose_template_apply_fields(compose
, tmpl
);
8931 quote_fmt_reset_vartable();
8932 quote_fmtlex_destroy();
8934 compose_changed_cb(NULL
, compose
);
8937 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8938 gtkaspell_highlight_all(compose
->gtkaspell
);
8942 static void compose_template_apply_fields_error(const gchar
*header
)
8947 tr
= g_strdup(C_("'%s' stands for a header name",
8948 "Template '%s' format error."));
8949 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8950 alertpanel_error("%s", text
);
8956 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8958 MsgInfo
* dummyinfo
= NULL
;
8959 MsgInfo
*msginfo
= NULL
;
8962 if (compose
->replyinfo
!= NULL
)
8963 msginfo
= compose
->replyinfo
;
8964 else if (compose
->fwdinfo
!= NULL
)
8965 msginfo
= compose
->fwdinfo
;
8967 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8968 msginfo
= dummyinfo
;
8971 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8973 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8974 compose
->gtkaspell
);
8976 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8978 quote_fmt_scan_string(tmpl
->from
);
8981 buf
= quote_fmt_get_buffer();
8983 compose_template_apply_fields_error("From");
8985 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
8988 quote_fmt_reset_vartable();
8989 quote_fmtlex_destroy();
8992 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
8994 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8995 compose
->gtkaspell
);
8997 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8999 quote_fmt_scan_string(tmpl
->to
);
9002 buf
= quote_fmt_get_buffer();
9004 compose_template_apply_fields_error("To");
9006 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9009 quote_fmt_reset_vartable();
9010 quote_fmtlex_destroy();
9013 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9015 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9016 compose
->gtkaspell
);
9018 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9020 quote_fmt_scan_string(tmpl
->cc
);
9023 buf
= quote_fmt_get_buffer();
9025 compose_template_apply_fields_error("Cc");
9027 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9030 quote_fmt_reset_vartable();
9031 quote_fmtlex_destroy();
9034 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9036 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9037 compose
->gtkaspell
);
9039 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9041 quote_fmt_scan_string(tmpl
->bcc
);
9044 buf
= quote_fmt_get_buffer();
9046 compose_template_apply_fields_error("Bcc");
9048 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9051 quote_fmt_reset_vartable();
9052 quote_fmtlex_destroy();
9055 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9057 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9058 compose
->gtkaspell
);
9060 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9062 quote_fmt_scan_string(tmpl
->replyto
);
9065 buf
= quote_fmt_get_buffer();
9067 compose_template_apply_fields_error("Reply-To");
9069 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9072 quote_fmt_reset_vartable();
9073 quote_fmtlex_destroy();
9076 /* process the subject */
9077 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9079 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9080 compose
->gtkaspell
);
9082 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9084 quote_fmt_scan_string(tmpl
->subject
);
9087 buf
= quote_fmt_get_buffer();
9089 compose_template_apply_fields_error("Subject");
9091 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9094 quote_fmt_reset_vartable();
9095 quote_fmtlex_destroy();
9098 procmsg_msginfo_free( &dummyinfo
);
9101 static void compose_destroy(Compose
*compose
)
9103 GtkAllocation allocation
;
9104 GtkTextBuffer
*buffer
;
9105 GtkClipboard
*clipboard
;
9107 compose_list
= g_list_remove(compose_list
, compose
);
9110 gboolean enable
= TRUE
;
9111 g_slist_foreach(compose
->passworded_ldap_servers
,
9112 _ldap_srv_func
, &enable
);
9113 g_slist_free(compose
->passworded_ldap_servers
);
9116 if (compose
->updating
) {
9117 debug_print("danger, not destroying anything now\n");
9118 compose
->deferred_destroy
= TRUE
;
9122 /* NOTE: address_completion_end() does nothing with the window
9123 * however this may change. */
9124 address_completion_end(compose
->window
);
9126 slist_free_strings_full(compose
->to_list
);
9127 slist_free_strings_full(compose
->newsgroup_list
);
9128 slist_free_strings_full(compose
->header_list
);
9130 slist_free_strings_full(extra_headers
);
9131 extra_headers
= NULL
;
9133 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9135 g_hash_table_destroy(compose
->email_hashtable
);
9137 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9138 compose
->folder_update_callback_id
);
9140 procmsg_msginfo_free(&(compose
->targetinfo
));
9141 procmsg_msginfo_free(&(compose
->replyinfo
));
9142 procmsg_msginfo_free(&(compose
->fwdinfo
));
9144 g_free(compose
->replyto
);
9145 g_free(compose
->cc
);
9146 g_free(compose
->bcc
);
9147 g_free(compose
->newsgroups
);
9148 g_free(compose
->followup_to
);
9150 g_free(compose
->ml_post
);
9152 g_free(compose
->inreplyto
);
9153 g_free(compose
->references
);
9154 g_free(compose
->msgid
);
9155 g_free(compose
->boundary
);
9157 g_free(compose
->redirect_filename
);
9158 if (compose
->undostruct
)
9159 undo_destroy(compose
->undostruct
);
9161 g_free(compose
->sig_str
);
9163 g_free(compose
->exteditor_file
);
9165 g_free(compose
->orig_charset
);
9167 g_free(compose
->privacy_system
);
9168 g_free(compose
->encdata
);
9170 #ifndef USE_ALT_ADDRBOOK
9171 if (addressbook_get_target_compose() == compose
)
9172 addressbook_set_target_compose(NULL
);
9175 if (compose
->gtkaspell
) {
9176 gtkaspell_delete(compose
->gtkaspell
);
9177 compose
->gtkaspell
= NULL
;
9181 if (!compose
->batch
) {
9182 gtk_widget_get_allocation(compose
->window
, &allocation
);
9183 prefs_common
.compose_width
= allocation
.width
;
9184 prefs_common
.compose_height
= allocation
.height
;
9187 if (!gtk_widget_get_parent(compose
->paned
))
9188 gtk_widget_destroy(compose
->paned
);
9189 gtk_widget_destroy(compose
->popupmenu
);
9191 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9192 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9193 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9195 message_search_close(compose
);
9196 gtk_widget_destroy(compose
->window
);
9197 toolbar_destroy(compose
->toolbar
);
9198 g_free(compose
->toolbar
);
9199 cm_mutex_free(compose
->mutex
);
9203 static void compose_attach_info_free(AttachInfo
*ainfo
)
9205 g_free(ainfo
->file
);
9206 g_free(ainfo
->content_type
);
9207 g_free(ainfo
->name
);
9208 g_free(ainfo
->charset
);
9212 static void compose_attach_update_label(Compose
*compose
)
9217 GtkTreeModel
*model
;
9221 if (compose
== NULL
)
9224 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9225 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9226 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9230 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9231 total_size
= ainfo
->size
;
9232 while(gtk_tree_model_iter_next(model
, &iter
)) {
9233 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9234 total_size
+= ainfo
->size
;
9237 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9238 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9242 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9244 Compose
*compose
= (Compose
*)data
;
9245 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9246 GtkTreeSelection
*selection
;
9248 GtkTreeModel
*model
;
9250 selection
= gtk_tree_view_get_selection(tree_view
);
9251 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9252 cm_return_if_fail(sel
);
9254 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9255 GtkTreePath
*path
= cur
->data
;
9256 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9259 gtk_tree_path_free(path
);
9262 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9263 GtkTreeRowReference
*ref
= cur
->data
;
9264 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9267 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9268 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9270 gtk_tree_path_free(path
);
9271 gtk_tree_row_reference_free(ref
);
9275 compose_attach_update_label(compose
);
9278 static struct _AttachProperty
9281 GtkWidget
*mimetype_entry
;
9282 GtkWidget
*encoding_optmenu
;
9283 GtkWidget
*path_entry
;
9284 GtkWidget
*filename_entry
;
9286 GtkWidget
*cancel_btn
;
9289 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9291 gtk_tree_path_free((GtkTreePath
*)ptr
);
9294 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9296 Compose
*compose
= (Compose
*)data
;
9297 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9299 GtkComboBox
*optmenu
;
9300 GtkTreeSelection
*selection
;
9302 GtkTreeModel
*model
;
9305 static gboolean cancelled
;
9307 /* only if one selected */
9308 selection
= gtk_tree_view_get_selection(tree_view
);
9309 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9312 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9313 cm_return_if_fail(sel
);
9315 path
= (GtkTreePath
*) sel
->data
;
9316 gtk_tree_model_get_iter(model
, &iter
, path
);
9317 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9320 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9326 if (!attach_prop
.window
)
9327 compose_attach_property_create(&cancelled
);
9328 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9329 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9330 gtk_widget_show(attach_prop
.window
);
9331 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9332 GTK_WINDOW(compose
->window
));
9334 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9335 if (ainfo
->encoding
== ENC_UNKNOWN
)
9336 combobox_select_by_data(optmenu
, ENC_BASE64
);
9338 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9340 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9341 ainfo
->content_type
? ainfo
->content_type
: "");
9342 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9343 ainfo
->file
? ainfo
->file
: "");
9344 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9345 ainfo
->name
? ainfo
->name
: "");
9348 const gchar
*entry_text
;
9350 gchar
*cnttype
= NULL
;
9357 gtk_widget_hide(attach_prop
.window
);
9358 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9363 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9364 if (*entry_text
!= '\0') {
9367 text
= g_strstrip(g_strdup(entry_text
));
9368 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9369 cnttype
= g_strdup(text
);
9372 alertpanel_error(_("Invalid MIME type."));
9378 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9380 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9381 if (*entry_text
!= '\0') {
9382 if (is_file_exist(entry_text
) &&
9383 (size
= get_file_size(entry_text
)) > 0)
9384 file
= g_strdup(entry_text
);
9387 (_("File doesn't exist or is empty."));
9393 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9394 if (*entry_text
!= '\0') {
9395 g_free(ainfo
->name
);
9396 ainfo
->name
= g_strdup(entry_text
);
9400 g_free(ainfo
->content_type
);
9401 ainfo
->content_type
= cnttype
;
9404 g_free(ainfo
->file
);
9408 ainfo
->size
= (goffset
)size
;
9410 /* update tree store */
9411 text
= to_human_readable(ainfo
->size
);
9412 gtk_tree_model_get_iter(model
, &iter
, path
);
9413 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9414 COL_MIMETYPE
, ainfo
->content_type
,
9416 COL_NAME
, ainfo
->name
,
9417 COL_CHARSET
, ainfo
->charset
,
9423 gtk_tree_path_free(path
);
9426 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9428 label = gtk_label_new(str); \
9429 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9430 GTK_FILL, 0, 0, 0); \
9431 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9433 entry = gtk_entry_new(); \
9434 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9435 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9438 static void compose_attach_property_create(gboolean
*cancelled
)
9444 GtkWidget
*mimetype_entry
;
9447 GtkListStore
*optmenu_menu
;
9448 GtkWidget
*path_entry
;
9449 GtkWidget
*filename_entry
;
9452 GtkWidget
*cancel_btn
;
9453 GList
*mime_type_list
, *strlist
;
9456 debug_print("Creating attach_property window...\n");
9458 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9459 gtk_widget_set_size_request(window
, 480, -1);
9460 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9461 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9462 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9463 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9464 g_signal_connect(G_OBJECT(window
), "delete_event",
9465 G_CALLBACK(attach_property_delete_event
),
9467 g_signal_connect(G_OBJECT(window
), "key_press_event",
9468 G_CALLBACK(attach_property_key_pressed
),
9471 vbox
= gtk_vbox_new(FALSE
, 8);
9472 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9474 table
= gtk_table_new(4, 2, FALSE
);
9475 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9476 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
9477 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
9479 label
= gtk_label_new(_("MIME type"));
9480 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
9482 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9483 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9484 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
9485 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9487 /* stuff with list */
9488 mime_type_list
= procmime_get_mime_type_list();
9490 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9491 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9494 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9496 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9499 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9500 (GCompareFunc
)g_strcmp0
);
9503 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9504 mime_type_list
= mime_type_list
->next
) {
9505 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9506 g_free(mime_type_list
->data
);
9508 g_list_free(strlist
);
9509 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9510 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9512 label
= gtk_label_new(_("Encoding"));
9513 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
9515 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9517 hbox
= gtk_hbox_new(FALSE
, 0);
9518 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
9519 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9521 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9522 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9524 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9525 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9526 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9527 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9528 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9530 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9532 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9533 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9535 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
9536 &ok_btn
, GTK_STOCK_OK
,
9538 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9539 gtk_widget_grab_default(ok_btn
);
9541 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9542 G_CALLBACK(attach_property_ok
),
9544 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9545 G_CALLBACK(attach_property_cancel
),
9548 gtk_widget_show_all(vbox
);
9550 attach_prop
.window
= window
;
9551 attach_prop
.mimetype_entry
= mimetype_entry
;
9552 attach_prop
.encoding_optmenu
= optmenu
;
9553 attach_prop
.path_entry
= path_entry
;
9554 attach_prop
.filename_entry
= filename_entry
;
9555 attach_prop
.ok_btn
= ok_btn
;
9556 attach_prop
.cancel_btn
= cancel_btn
;
9559 #undef SET_LABEL_AND_ENTRY
9561 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9567 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9573 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9574 gboolean
*cancelled
)
9582 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9584 gboolean
*cancelled
)
9586 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9590 if (event
&& event
->keyval
== GDK_KEY_Return
) {
9598 static void compose_exec_ext_editor(Compose
*compose
)
9603 GdkNativeWindow socket_wid
= 0;
9607 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9608 G_DIR_SEPARATOR
, compose
);
9610 if (compose_get_ext_editor_uses_socket()) {
9611 /* Only allow one socket */
9612 if (compose
->exteditor_socket
!= NULL
) {
9613 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9614 /* Move the focus off of the socket */
9615 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9620 /* Create the receiving GtkSocket */
9621 socket
= gtk_socket_new ();
9622 g_signal_connect (GTK_OBJECT(socket
), "plug-removed",
9623 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9625 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9626 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9627 /* Realize the socket so that we can use its ID */
9628 gtk_widget_realize(socket
);
9629 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9630 compose
->exteditor_socket
= socket
;
9633 if (pipe(pipe_fds
) < 0) {
9639 if ((pid
= fork()) < 0) {
9646 /* close the write side of the pipe */
9649 compose
->exteditor_file
= g_strdup(tmp
);
9650 compose
->exteditor_pid
= pid
;
9652 compose_set_ext_editor_sensitive(compose
, FALSE
);
9655 compose
->exteditor_ch
= g_io_channel_unix_new(pipe_fds
[0]);
9657 compose
->exteditor_ch
= g_io_channel_win32_new_fd(pipe_fds
[0]);
9659 compose
->exteditor_tag
= g_io_add_watch(compose
->exteditor_ch
,
9663 } else { /* process-monitoring process */
9669 /* close the read side of the pipe */
9672 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9673 fd_write_all(pipe_fds
[1], "2\n", 2);
9677 pid_ed
= compose_exec_ext_editor_real(tmp
, socket_wid
);
9679 fd_write_all(pipe_fds
[1], "1\n", 2);
9683 /* wait until editor is terminated */
9684 waitpid(pid_ed
, NULL
, 0);
9686 fd_write_all(pipe_fds
[1], "0\n", 2);
9693 #endif /* G_OS_UNIX */
9696 static gboolean
compose_can_autosave(Compose
*compose
)
9698 if (compose
->privacy_system
&& compose
->use_encryption
)
9699 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9701 return prefs_common
.autosave
;
9705 static gboolean
compose_get_ext_editor_cmd_valid()
9707 gboolean has_s
= FALSE
;
9708 gboolean has_w
= FALSE
;
9709 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9712 while ((p
= strchr(p
, '%'))) {
9718 } else if (*p
== 'w') {
9729 static gint
compose_exec_ext_editor_real(const gchar
*file
, GdkNativeWindow socket_wid
)
9736 cm_return_val_if_fail(file
!= NULL
, -1);
9738 if ((pid
= fork()) < 0) {
9743 if (pid
!= 0) return pid
;
9745 /* grandchild process */
9747 if (setpgid(0, getppid()))
9750 if (compose_get_ext_editor_cmd_valid()) {
9751 if (compose_get_ext_editor_uses_socket()) {
9752 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9753 s
= strstr(p
, "%w");
9755 if (strstr(p
, "%s") < s
)
9756 buf
= g_strdup_printf(p
, file
, socket_wid
);
9758 buf
= g_strdup_printf(p
, socket_wid
, file
);
9761 buf
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), file
);
9764 if (prefs_common_get_ext_editor_cmd())
9765 g_warning("External editor command-line is invalid: '%s'",
9766 prefs_common_get_ext_editor_cmd());
9767 buf
= g_strdup_printf(DEFAULT_EDITOR_CMD
, file
);
9770 cmdline
= strsplit_with_quote(buf
, " ", 0);
9772 execvp(cmdline
[0], cmdline
);
9775 g_strfreev(cmdline
);
9780 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9782 pid_t pgid
= compose
->exteditor_pid
* -1;
9785 ret
= kill(pgid
, 0);
9787 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
9791 msg
= g_strdup_printf
9792 (_("The external editor is still working.\n"
9793 "Force terminating the process?\n"
9794 "process group id: %d"), -pgid
);
9795 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
, GTK_STOCK_YES
,
9796 NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
,
9801 if (val
== G_ALERTALTERNATE
) {
9802 g_source_remove(compose
->exteditor_tag
);
9803 g_io_channel_shutdown(compose
->exteditor_ch
,
9805 g_io_channel_unref(compose
->exteditor_ch
);
9807 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
9808 waitpid(compose
->exteditor_pid
, NULL
, 0);
9810 g_warning("Terminated process group id: %d. "
9811 "Temporary file: %s", -pgid
, compose
->exteditor_file
);
9813 compose_set_ext_editor_sensitive(compose
, TRUE
);
9815 g_free(compose
->exteditor_file
);
9816 compose
->exteditor_file
= NULL
;
9817 compose
->exteditor_pid
= -1;
9818 compose
->exteditor_ch
= NULL
;
9819 compose
->exteditor_tag
= -1;
9827 static gboolean
compose_input_cb(GIOChannel
*source
, GIOCondition condition
,
9831 Compose
*compose
= (Compose
*)data
;
9834 debug_print("Compose: input from monitoring process\n");
9836 if (g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
) != G_IO_STATUS_NORMAL
) {
9841 g_io_channel_shutdown(source
, FALSE
, NULL
);
9842 g_io_channel_unref(source
);
9844 waitpid(compose
->exteditor_pid
, NULL
, 0);
9846 if (buf
[0] == '0') { /* success */
9847 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9848 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9849 GtkTextIter start
, end
;
9852 gtk_text_buffer_set_text(buffer
, "", -1);
9853 compose_insert_file(compose
, compose
->exteditor_file
);
9854 compose_changed_cb(NULL
, compose
);
9856 /* Check if we should save the draft or not */
9857 if (compose_can_autosave(compose
))
9858 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9860 if (claws_unlink(compose
->exteditor_file
) < 0)
9861 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9863 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9864 gtk_text_buffer_get_start_iter(buffer
, &start
);
9865 gtk_text_buffer_get_end_iter(buffer
, &end
);
9866 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9867 if (chars
&& strlen(chars
) > 0)
9868 compose
->modified
= TRUE
;
9870 } else if (buf
[0] == '1') { /* failed */
9871 g_warning("Couldn't exec external editor");
9872 if (claws_unlink(compose
->exteditor_file
) < 0)
9873 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9874 } else if (buf
[0] == '2') {
9875 g_warning("Couldn't write to file");
9876 } else if (buf
[0] == '3') {
9877 g_warning("Pipe read failed");
9880 compose_set_ext_editor_sensitive(compose
, TRUE
);
9882 g_free(compose
->exteditor_file
);
9883 compose
->exteditor_file
= NULL
;
9884 compose
->exteditor_pid
= -1;
9885 compose
->exteditor_ch
= NULL
;
9886 compose
->exteditor_tag
= -1;
9887 if (compose
->exteditor_socket
) {
9888 gtk_widget_destroy(compose
->exteditor_socket
);
9889 compose
->exteditor_socket
= NULL
;
9896 static char *ext_editor_menu_entries
[] = {
9897 "Menu/Message/Send",
9898 "Menu/Message/SendLater",
9899 "Menu/Message/InsertFile",
9900 "Menu/Message/InsertSig",
9901 "Menu/Message/ReplaceSig",
9902 "Menu/Message/Save",
9903 "Menu/Message/Print",
9908 "Menu/Tools/ShowRuler",
9909 "Menu/Tools/Actions",
9914 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9919 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9920 cm_menu_set_sensitive_full(compose
->ui_manager
,
9921 ext_editor_menu_entries
[i
], sensitive
);
9924 if (compose_get_ext_editor_uses_socket()) {
9926 if (compose
->exteditor_socket
)
9927 gtk_widget_hide(compose
->exteditor_socket
);
9928 gtk_widget_show(compose
->scrolledwin
);
9929 if (prefs_common
.show_ruler
)
9930 gtk_widget_show(compose
->ruler_hbox
);
9931 /* Fix the focus, as it doesn't go anywhere when the
9932 * socket is hidden or destroyed */
9933 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9935 g_assert (compose
->exteditor_socket
!= NULL
);
9936 /* Fix the focus, as it doesn't go anywhere when the
9937 * edit box is hidden */
9938 if (gtk_widget_is_focus(compose
->text
))
9939 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9940 gtk_widget_hide(compose
->scrolledwin
);
9941 gtk_widget_hide(compose
->ruler_hbox
);
9942 gtk_widget_show(compose
->exteditor_socket
);
9945 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9947 if (compose
->toolbar
->send_btn
)
9948 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9949 if (compose
->toolbar
->sendl_btn
)
9950 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9951 if (compose
->toolbar
->draft_btn
)
9952 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9953 if (compose
->toolbar
->insert_btn
)
9954 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9955 if (compose
->toolbar
->sig_btn
)
9956 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9957 if (compose
->toolbar
->exteditor_btn
)
9958 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9959 if (compose
->toolbar
->linewrap_current_btn
)
9960 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9961 if (compose
->toolbar
->linewrap_all_btn
)
9962 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9965 static gboolean
compose_get_ext_editor_uses_socket()
9967 return (prefs_common_get_ext_editor_cmd() &&
9968 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9971 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9973 compose
->exteditor_socket
= NULL
;
9974 /* returning FALSE allows destruction of the socket */
9977 #endif /* G_OS_UNIX */
9980 * compose_undo_state_changed:
9982 * Change the sensivity of the menuentries undo and redo
9984 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9985 gint redo_state
, gpointer data
)
9987 Compose
*compose
= (Compose
*)data
;
9989 switch (undo_state
) {
9990 case UNDO_STATE_TRUE
:
9991 if (!undostruct
->undo_state
) {
9992 undostruct
->undo_state
= TRUE
;
9993 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9996 case UNDO_STATE_FALSE
:
9997 if (undostruct
->undo_state
) {
9998 undostruct
->undo_state
= FALSE
;
9999 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
10002 case UNDO_STATE_UNCHANGED
:
10004 case UNDO_STATE_REFRESH
:
10005 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
10008 g_warning("Undo state not recognized");
10012 switch (redo_state
) {
10013 case UNDO_STATE_TRUE
:
10014 if (!undostruct
->redo_state
) {
10015 undostruct
->redo_state
= TRUE
;
10016 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
10019 case UNDO_STATE_FALSE
:
10020 if (undostruct
->redo_state
) {
10021 undostruct
->redo_state
= FALSE
;
10022 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
10025 case UNDO_STATE_UNCHANGED
:
10027 case UNDO_STATE_REFRESH
:
10028 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
10031 g_warning("Redo state not recognized");
10036 /* callback functions */
10038 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
10039 GtkAllocation
*allocation
,
10042 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10045 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10046 * includes "non-client" (windows-izm) in calculation, so this calculation
10047 * may not be accurate.
10049 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10050 GtkAllocation
*allocation
,
10051 GtkSHRuler
*shruler
)
10053 if (prefs_common
.show_ruler
) {
10054 gint char_width
= 0, char_height
= 0;
10055 gint line_width_in_chars
;
10057 gtkut_get_font_size(GTK_WIDGET(widget
),
10058 &char_width
, &char_height
);
10059 line_width_in_chars
=
10060 (allocation
->width
- allocation
->x
) / char_width
;
10062 /* got the maximum */
10063 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10064 0.0, line_width_in_chars
, 0);
10073 ComposePrefType type
;
10074 gboolean entry_marked
;
10075 } HeaderEntryState
;
10077 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10079 Compose
*compose
= (Compose
*)data
;
10082 gchar
*folderidentifier
;
10083 gint account_id
= 0;
10084 GtkTreeModel
*menu
;
10086 GSList
*list
, *saved_list
= NULL
;
10087 HeaderEntryState
*state
;
10089 /* Get ID of active account in the combo box */
10090 menu
= gtk_combo_box_get_model(optmenu
);
10091 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10092 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10094 ac
= account_find_from_id(account_id
);
10095 cm_return_if_fail(ac
!= NULL
);
10097 if (ac
!= compose
->account
) {
10098 compose_select_account(compose
, ac
, FALSE
);
10100 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10101 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10103 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10104 compose_destroy_headerentry(compose
, hentry
);
10107 state
= g_malloc0(sizeof(HeaderEntryState
));
10108 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10109 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10110 state
->entry
= gtk_editable_get_chars(
10111 GTK_EDITABLE(hentry
->entry
), 0, -1);
10112 state
->type
= hentry
->type
;
10114 saved_list
= g_slist_append(saved_list
, state
);
10115 compose_destroy_headerentry(compose
, hentry
);
10118 compose
->header_last
= NULL
;
10119 g_slist_free(compose
->header_list
);
10120 compose
->header_list
= NULL
;
10121 compose
->header_nextrow
= 1;
10122 compose_create_header_entry(compose
);
10124 if (ac
->set_autocc
&& ac
->auto_cc
)
10125 compose_entry_append(compose
, ac
->auto_cc
,
10126 COMPOSE_CC
, PREF_ACCOUNT
);
10127 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10128 compose_entry_append(compose
, ac
->auto_bcc
,
10129 COMPOSE_BCC
, PREF_ACCOUNT
);
10130 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10131 compose_entry_append(compose
, ac
->auto_replyto
,
10132 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10134 for (list
= saved_list
; list
; list
= list
->next
) {
10135 state
= (HeaderEntryState
*) list
->data
;
10137 compose_add_header_entry(compose
, state
->header
,
10138 state
->entry
, state
->type
);
10140 g_free(state
->header
);
10141 g_free(state
->entry
);
10144 g_slist_free(saved_list
);
10146 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10147 (ac
->protocol
== A_NNTP
) ?
10148 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10151 /* Set message save folder */
10152 compose_set_save_to(compose
, NULL
);
10153 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10154 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10155 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10156 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10157 compose_set_save_to(compose
, folderidentifier
);
10158 g_free(folderidentifier
);
10159 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10160 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10161 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10163 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10164 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10165 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10166 (compose
->account
, F_OUTBOX
));
10167 compose_set_save_to(compose
, folderidentifier
);
10168 g_free(folderidentifier
);
10172 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10173 GtkTreeViewColumn
*column
, Compose
*compose
)
10175 compose_attach_property(NULL
, compose
);
10178 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10181 Compose
*compose
= (Compose
*)data
;
10182 GtkTreeSelection
*attach_selection
;
10183 gint attach_nr_selected
;
10186 if (!event
) return FALSE
;
10188 if (event
->button
== 3) {
10189 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10190 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10192 /* If no rows, or just one row is selected, right-click should
10193 * open menu relevant to the row being right-clicked on. We
10194 * achieve that by selecting the clicked row first. If more
10195 * than one row is selected, we shouldn't modify the selection,
10196 * as user may want to remove selected rows (attachments). */
10197 if (attach_nr_selected
< 2) {
10198 gtk_tree_selection_unselect_all(attach_selection
);
10199 attach_nr_selected
= 0;
10200 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10201 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10202 if (path
!= NULL
) {
10203 gtk_tree_selection_select_path(attach_selection
, path
);
10204 gtk_tree_path_free(path
);
10205 attach_nr_selected
++;
10209 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10210 /* Properties menu item makes no sense with more than one row
10211 * selected, the properties dialog can only edit one attachment. */
10212 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10214 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
10215 NULL
, NULL
, event
->button
, event
->time
);
10222 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10225 Compose
*compose
= (Compose
*)data
;
10227 if (!event
) return FALSE
;
10229 switch (event
->keyval
) {
10230 case GDK_KEY_Delete
:
10231 compose_attach_remove_selected(NULL
, compose
);
10237 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10239 toolbar_comp_set_sensitive(compose
, allow
);
10240 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10241 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10243 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10245 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10246 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10247 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10249 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10253 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10255 Compose
*compose
= (Compose
*)data
;
10258 if (compose
->exteditor_tag
!= -1) {
10259 debug_print("ignoring send: external editor still open\n");
10263 if (prefs_common
.work_offline
&&
10264 !inc_offline_should_override(TRUE
,
10265 _("Claws Mail needs network access in order "
10266 "to send this email.")))
10269 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10270 g_source_remove(compose
->draft_timeout_tag
);
10271 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10274 compose_send(compose
);
10277 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10279 Compose
*compose
= (Compose
*)data
;
10280 ComposeQueueResult val
;
10283 compose_allow_user_actions(compose
, FALSE
);
10284 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10285 compose_allow_user_actions(compose
, TRUE
);
10288 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10289 compose_close(compose
);
10291 _display_queue_error(val
);
10294 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10297 #define DRAFTED_AT_EXIT "drafted_at_exit"
10298 static void compose_register_draft(MsgInfo
*info
)
10300 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10301 DRAFTED_AT_EXIT
, NULL
);
10302 FILE *fp
= claws_fopen(filepath
, "ab");
10305 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
10313 gboolean
compose_draft (gpointer data
, guint action
)
10315 Compose
*compose
= (Compose
*)data
;
10320 MsgFlags flag
= {0, 0};
10321 static gboolean lock
= FALSE
;
10322 MsgInfo
*newmsginfo
;
10324 gboolean target_locked
= FALSE
;
10325 gboolean err
= FALSE
;
10327 if (lock
) return FALSE
;
10329 if (compose
->sending
)
10332 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10333 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10335 if (!g_mutex_trylock(compose
->mutex
)) {
10336 /* we don't want to lock the mutex once it's available,
10337 * because as the only other part of compose.c locking
10338 * it is compose_close - which means once unlocked,
10339 * the compose struct will be freed */
10340 debug_print("couldn't lock mutex, probably sending\n");
10346 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10347 G_DIR_SEPARATOR
, compose
);
10348 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10349 FILE_OP_ERROR(tmp
, "claws_fopen");
10353 /* chmod for security */
10354 if (change_file_mode_rw(fp
, tmp
) < 0) {
10355 FILE_OP_ERROR(tmp
, "chmod");
10356 g_warning("can't change file mode");
10359 /* Save draft infos */
10360 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10361 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10363 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10364 gchar
*savefolderid
;
10366 savefolderid
= compose_get_save_to(compose
);
10367 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10368 g_free(savefolderid
);
10370 if (compose
->return_receipt
) {
10371 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10373 if (compose
->privacy_system
) {
10374 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10375 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10376 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10379 /* Message-ID of message replying to */
10380 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10381 gchar
*folderid
= NULL
;
10383 if (compose
->replyinfo
->folder
)
10384 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10385 if (folderid
== NULL
)
10386 folderid
= g_strdup("NULL");
10388 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10391 /* Message-ID of message forwarding to */
10392 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10393 gchar
*folderid
= NULL
;
10395 if (compose
->fwdinfo
->folder
)
10396 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10397 if (folderid
== NULL
)
10398 folderid
= g_strdup("NULL");
10400 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10404 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10405 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10407 sheaders
= compose_get_manual_headers_info(compose
);
10408 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10411 /* end of headers */
10412 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10419 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10423 if (claws_safe_fclose(fp
) == EOF
) {
10427 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10428 if (compose
->targetinfo
) {
10429 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10431 flag
.perm_flags
|= MSG_LOCKED
;
10433 flag
.tmp_flags
= MSG_DRAFT
;
10435 folder_item_scan(draft
);
10436 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10437 MsgInfo
*tmpinfo
= NULL
;
10438 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10439 if (compose
->msgid
) {
10440 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10443 msgnum
= tmpinfo
->msgnum
;
10444 procmsg_msginfo_free(&tmpinfo
);
10445 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10447 debug_print("didn't get draft msgnum after scanning\n");
10450 debug_print("got draft msgnum %d from adding\n", msgnum
);
10456 if (action
!= COMPOSE_AUTO_SAVE
) {
10457 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10458 alertpanel_error(_("Could not save draft."));
10461 gtkut_window_popup(compose
->window
);
10462 val
= alertpanel_full(_("Could not save draft"),
10463 _("Could not save draft.\n"
10464 "Do you want to cancel exit or discard this email?"),
10465 _("_Cancel exit"), _("_Discard email"), NULL
, ALERTFOCUS_FIRST
,
10466 FALSE
, NULL
, ALERT_QUESTION
);
10467 if (val
== G_ALERTALTERNATE
) {
10469 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10470 compose_close(compose
);
10474 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10483 if (compose
->mode
== COMPOSE_REEDIT
) {
10484 compose_remove_reedit_target(compose
, TRUE
);
10487 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10490 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10492 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10494 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10495 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10496 procmsg_msginfo_set_flags(newmsginfo
, 0,
10497 MSG_HAS_ATTACHMENT
);
10499 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10500 compose_register_draft(newmsginfo
);
10502 procmsg_msginfo_free(&newmsginfo
);
10505 folder_item_scan(draft
);
10507 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10509 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10510 compose_close(compose
);
10517 GError
*error
= NULL
;
10522 goffset size
, mtime
;
10524 path
= folder_item_fetch_msg(draft
, msgnum
);
10525 if (path
== NULL
) {
10526 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10530 f
= g_file_new_for_path(path
);
10531 fi
= g_file_query_info(f
, "standard::size,time::modified",
10532 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10533 if (error
!= NULL
) {
10534 debug_print("couldn't query file info for '%s': %s\n",
10535 path
, error
->message
);
10536 g_error_free(error
);
10541 size
= g_file_info_get_size(fi
);
10542 g_file_info_get_modification_time(fi
, &tv
);
10544 g_object_unref(fi
);
10547 if (g_stat(path
, &s
) < 0) {
10548 FILE_OP_ERROR(path
, "stat");
10553 mtime
= s
.st_mtime
;
10557 procmsg_msginfo_free(&(compose
->targetinfo
));
10558 compose
->targetinfo
= procmsg_msginfo_new();
10559 compose
->targetinfo
->msgnum
= msgnum
;
10560 compose
->targetinfo
->size
= size
;
10561 compose
->targetinfo
->mtime
= mtime
;
10562 compose
->targetinfo
->folder
= draft
;
10564 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10565 compose
->mode
= COMPOSE_REEDIT
;
10567 if (action
== COMPOSE_AUTO_SAVE
) {
10568 compose
->autosaved_draft
= compose
->targetinfo
;
10570 compose
->modified
= FALSE
;
10571 compose_set_title(compose
);
10575 g_mutex_unlock(compose
->mutex
);
10579 void compose_clear_exit_drafts(void)
10581 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10582 DRAFTED_AT_EXIT
, NULL
);
10583 if (is_file_exist(filepath
))
10584 claws_unlink(filepath
);
10589 void compose_reopen_exit_drafts(void)
10591 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10592 DRAFTED_AT_EXIT
, NULL
);
10593 FILE *fp
= claws_fopen(filepath
, "rb");
10597 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10598 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10599 const gchar
*folder
= parts
[0];
10600 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10602 if (folder
&& *folder
&& msgnum
> -1) {
10603 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10604 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10606 compose_reedit(info
, FALSE
);
10613 compose_clear_exit_drafts();
10616 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10618 Compose
*compose
= (Compose
*)data
;
10619 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10620 compose
->rmode
= COMPOSE_REEDIT
;
10623 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10625 if (compose
&& file_list
) {
10628 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10629 gchar
*file
= (gchar
*) tmp
->data
;
10630 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10631 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10632 compose_changed_cb(NULL
, compose
);
10637 g_free(utf8_filename
);
10642 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10644 Compose
*compose
= (Compose
*)data
;
10647 if (compose
->redirect_filename
!= NULL
)
10650 /* Set focus_window properly, in case we were called via popup menu,
10651 * which unsets it (via focus_out_event callback on compose window). */
10652 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10654 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10657 compose_attach_from_list(compose
, file_list
, TRUE
);
10658 g_list_free(file_list
);
10662 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10664 Compose
*compose
= (Compose
*)data
;
10666 gint files_inserted
= 0;
10668 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10673 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10674 gchar
*file
= (gchar
*) tmp
->data
;
10675 gchar
*filedup
= g_strdup(file
);
10676 gchar
*shortfile
= g_path_get_basename(filedup
);
10677 ComposeInsertResult res
;
10678 /* insert the file if the file is short or if the user confirmed that
10679 he/she wants to insert the large file */
10680 res
= compose_insert_file(compose
, file
);
10681 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10682 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10683 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10684 alertpanel_error(_("File '%s' contained invalid characters\n"
10685 "for the current encoding, insertion may be incorrect."),
10687 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10694 g_list_free(file_list
);
10698 if (files_inserted
> 0 && compose
->gtkaspell
&&
10699 compose
->gtkaspell
->check_while_typing
)
10700 gtkaspell_highlight_all(compose
->gtkaspell
);
10704 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10706 Compose
*compose
= (Compose
*)data
;
10708 compose_insert_sig(compose
, FALSE
);
10711 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10713 Compose
*compose
= (Compose
*)data
;
10715 compose_insert_sig(compose
, TRUE
);
10718 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10722 Compose
*compose
= (Compose
*)data
;
10724 gtkut_widget_get_uposition(widget
, &x
, &y
);
10725 if (!compose
->batch
) {
10726 prefs_common
.compose_x
= x
;
10727 prefs_common
.compose_y
= y
;
10729 if (compose
->sending
|| compose
->updating
)
10731 compose_close_cb(NULL
, compose
);
10735 void compose_close_toolbar(Compose
*compose
)
10737 compose_close_cb(NULL
, compose
);
10740 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10742 Compose
*compose
= (Compose
*)data
;
10746 if (compose
->exteditor_tag
!= -1) {
10747 if (!compose_ext_editor_kill(compose
))
10752 if (compose
->modified
) {
10753 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10754 if (!g_mutex_trylock(compose
->mutex
)) {
10755 /* we don't want to lock the mutex once it's available,
10756 * because as the only other part of compose.c locking
10757 * it is compose_close - which means once unlocked,
10758 * the compose struct will be freed */
10759 debug_print("couldn't lock mutex, probably sending\n");
10762 if (!reedit
|| compose
->folder
->stype
== F_DRAFT
) {
10763 val
= alertpanel(_("Discard message"),
10764 _("This message has been modified. Discard it?"),
10765 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10768 val
= alertpanel(_("Save changes"),
10769 _("This message has been modified. Save the latest changes?"),
10770 _("_Don't save"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10771 ALERTFOCUS_SECOND
);
10773 g_mutex_unlock(compose
->mutex
);
10775 case G_ALERTDEFAULT
:
10776 if (compose_can_autosave(compose
) && !reedit
)
10777 compose_remove_draft(compose
);
10779 case G_ALERTALTERNATE
:
10780 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10787 compose_close(compose
);
10790 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10792 Compose
*compose
= (Compose
*) data
;
10794 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10795 if (compose
->targetinfo
)
10796 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10799 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10801 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10802 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10803 Compose
*compose
= (Compose
*) data
;
10806 compose
->out_encoding
= (CharSet
)value
;
10809 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10811 Compose
*compose
= (Compose
*)data
;
10813 #ifndef USE_ALT_ADDRBOOK
10814 addressbook_open(compose
);
10816 GError
* error
= NULL
;
10817 addressbook_connect_signals(compose
);
10818 addressbook_dbus_open(TRUE
, &error
);
10820 g_warning("%s", error
->message
);
10821 g_error_free(error
);
10826 static void about_show_cb(GtkAction
*action
, gpointer data
)
10831 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10833 Compose
*compose
= (Compose
*)data
;
10838 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10839 cm_return_if_fail(tmpl
!= NULL
);
10841 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10843 val
= alertpanel(_("Apply template"), msg
,
10844 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
, ALERTFOCUS_FIRST
);
10847 if (val
== G_ALERTDEFAULT
)
10848 compose_template_apply(compose
, tmpl
, TRUE
);
10849 else if (val
== G_ALERTALTERNATE
)
10850 compose_template_apply(compose
, tmpl
, FALSE
);
10853 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10855 Compose
*compose
= (Compose
*)data
;
10858 if (compose
->exteditor_tag
!= -1) {
10859 debug_print("ignoring open external editor: external editor still open\n");
10863 compose_exec_ext_editor(compose
);
10866 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10868 Compose
*compose
= (Compose
*)data
;
10869 gboolean prev_autowrap
= compose
->autowrap
;
10871 compose
->autowrap
= FALSE
;
10872 undo_undo(compose
->undostruct
);
10873 compose
->autowrap
= prev_autowrap
;
10876 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10878 Compose
*compose
= (Compose
*)data
;
10879 gboolean prev_autowrap
= compose
->autowrap
;
10881 compose
->autowrap
= FALSE
;
10882 undo_redo(compose
->undostruct
);
10883 compose
->autowrap
= prev_autowrap
;
10886 static void entry_cut_clipboard(GtkWidget
*entry
)
10888 if (GTK_IS_EDITABLE(entry
))
10889 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10890 else if (GTK_IS_TEXT_VIEW(entry
))
10891 gtk_text_buffer_cut_clipboard(
10892 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10893 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10897 static void entry_copy_clipboard(GtkWidget
*entry
)
10899 if (GTK_IS_EDITABLE(entry
))
10900 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10901 else if (GTK_IS_TEXT_VIEW(entry
))
10902 gtk_text_buffer_copy_clipboard(
10903 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10904 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10907 static void paste_text(Compose
*compose
, GtkWidget
*entry
,
10908 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
,
10909 const gchar
*contents
)
10911 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10912 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10913 GtkTextIter start_iter
, end_iter
;
10916 if (contents
== NULL
)
10919 /* we shouldn't delete the selection when middle-click-pasting, or we
10920 * can't mid-click-paste our own selection */
10921 if (clip
!= GDK_SELECTION_PRIMARY
) {
10922 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10923 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10926 if (insert_place
== NULL
) {
10927 /* if insert_place isn't specified, insert at the cursor.
10928 * used for Ctrl-V pasting */
10929 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10930 start
= gtk_text_iter_get_offset(&start_iter
);
10931 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10933 /* if insert_place is specified, paste here.
10934 * used for mid-click-pasting */
10935 start
= gtk_text_iter_get_offset(insert_place
);
10936 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10937 if (prefs_common
.primary_paste_unselects
)
10938 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10942 /* paste unwrapped: mark the paste so it's not wrapped later */
10943 end
= start
+ strlen(contents
);
10944 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10945 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10946 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10947 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10948 /* rewrap paragraph now (after a mid-click-paste) */
10949 mark_start
= gtk_text_buffer_get_insert(buffer
);
10950 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10951 gtk_text_iter_backward_char(&start_iter
);
10952 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10954 compose
->modified
= TRUE
;
10957 static void attach_uri_list(Compose
*compose
, GtkSelectionData
*data
)
10961 list
= uri_list_extract_filenames(
10962 (const gchar
*)gtk_selection_data_get_data(data
));
10963 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
10964 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
10965 compose_attach_append
10966 (compose
, (const gchar
*)tmp
->data
,
10967 utf8_filename
, NULL
, NULL
);
10968 g_free(utf8_filename
);
10971 compose_changed_cb(NULL
, compose
);
10973 list_free_strings_full(list
);
10976 int attach_image(Compose
*compose
, GtkSelectionData
*data
, const gchar
*subtype
)
10979 const guchar
*contents
;
10985 cm_return_val_if_fail(data
!= NULL
, -1);
10987 contents
= gtk_selection_data_get_data(data
);
10988 len
= gtk_selection_data_get_length(data
);
10990 file
= g_strconcat(get_tmp_file(), "-image.", subtype
, NULL
);
10992 debug_print("writing image to %s\n", file
);
10994 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
10995 FILE_OP_ERROR(file
, "claws_fopen");
10999 if (claws_fwrite(contents
, 1, len
, fp
) != len
) {
11000 FILE_OP_ERROR(file
, "claws_fwrite");
11002 claws_unlink(file
);
11006 r
= claws_safe_fclose(fp
);
11009 FILE_OP_ERROR(file
, "claws_fclose");
11010 claws_unlink(file
);
11014 type
= g_strconcat("image/", subtype
, NULL
);
11016 compose_attach_append(compose
, (const gchar
*)file
,
11017 (const gchar
*)file
, type
, NULL
);
11019 alertpanel_notice(_("The pasted image has been attached as: \n%s"), file
);
11027 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
11028 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
11030 if (GTK_IS_TEXT_VIEW(entry
)) {
11031 GdkAtom types
= gdk_atom_intern ("MULTIPLE", FALSE
);
11032 GdkAtom
*targets
= NULL
;
11033 int n_targets
= 0, i
;
11034 gboolean paste_done
= FALSE
;
11035 GtkClipboard
*clipboard
= gtk_clipboard_get(clip
);
11037 GtkSelectionData
*contents
= gtk_clipboard_wait_for_contents(
11040 if (contents
!= NULL
) {
11041 gtk_selection_data_get_targets(contents
, &targets
, &n_targets
);
11042 gtk_selection_data_free(contents
);
11045 for (i
= 0; i
< n_targets
; i
++) {
11046 GdkAtom atom
= targets
[i
];
11047 gchar
*atom_type
= gdk_atom_name(atom
);
11049 if (atom_type
!= NULL
) {
11050 GtkSelectionData
*data
= gtk_clipboard_wait_for_contents(
11052 debug_print("got contents of type %s\n", atom_type
);
11053 if (!strcmp(atom_type
, "text/plain")) {
11054 /* let the default text handler handle it */
11056 } else if (!strcmp(atom_type
, "text/uri-list")) {
11057 attach_uri_list(compose
, data
);
11061 } else if (!strncmp(atom_type
, "image/", strlen("image/"))) {
11062 gchar
*subtype
= g_strdup((gchar
*)(strstr(atom_type
, "/")+1));
11063 debug_print("image of type %s\n", subtype
);
11065 attach_image(compose
, data
, subtype
);
11074 gchar
*def_text
= gtk_clipboard_wait_for_text(clipboard
);
11075 paste_text(compose
, entry
, wrap
, clip
,
11076 insert_place
, def_text
);
11081 } else if (GTK_IS_EDITABLE(entry
)) {
11082 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
11083 compose
->modified
= TRUE
;
11087 static void entry_allsel(GtkWidget
*entry
)
11089 if (GTK_IS_EDITABLE(entry
))
11090 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
11091 else if (GTK_IS_TEXT_VIEW(entry
)) {
11092 GtkTextIter startiter
, enditer
;
11093 GtkTextBuffer
*textbuf
;
11095 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
11096 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
11097 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
11099 gtk_text_buffer_move_mark_by_name(textbuf
,
11100 "selection_bound", &startiter
);
11101 gtk_text_buffer_move_mark_by_name(textbuf
,
11102 "insert", &enditer
);
11106 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
11108 Compose
*compose
= (Compose
*)data
;
11109 if (compose
->focused_editable
11110 #ifndef GENERIC_UMPC
11111 && gtk_widget_has_focus(compose
->focused_editable
)
11114 entry_cut_clipboard(compose
->focused_editable
);
11117 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
11119 Compose
*compose
= (Compose
*)data
;
11120 if (compose
->focused_editable
11121 #ifndef GENERIC_UMPC
11122 && gtk_widget_has_focus(compose
->focused_editable
)
11125 entry_copy_clipboard(compose
->focused_editable
);
11128 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
11130 Compose
*compose
= (Compose
*)data
;
11131 gint prev_autowrap
;
11132 GtkTextBuffer
*buffer
;
11134 if (compose
->focused_editable
11135 #ifndef GENERIC_UMPC
11136 && gtk_widget_has_focus(compose
->focused_editable
)
11139 entry_paste_clipboard(compose
, compose
->focused_editable
,
11140 prefs_common
.linewrap_pastes
,
11141 GDK_SELECTION_CLIPBOARD
, NULL
);
11146 #ifndef GENERIC_UMPC
11147 gtk_widget_has_focus(compose
->text
) &&
11149 compose
->gtkaspell
&&
11150 compose
->gtkaspell
->check_while_typing
)
11151 gtkaspell_highlight_all(compose
->gtkaspell
);
11155 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
11157 Compose
*compose
= (Compose
*)data
;
11158 gint wrap_quote
= prefs_common
.linewrap_quote
;
11159 if (compose
->focused_editable
11160 #ifndef GENERIC_UMPC
11161 && gtk_widget_has_focus(compose
->focused_editable
)
11164 /* let text_insert() (called directly or at a later time
11165 * after the gtk_editable_paste_clipboard) know that
11166 * text is to be inserted as a quotation. implemented
11167 * by using a simple refcount... */
11168 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11169 G_OBJECT(compose
->focused_editable
),
11170 "paste_as_quotation"));
11171 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11172 "paste_as_quotation",
11173 GINT_TO_POINTER(paste_as_quotation
+ 1));
11174 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11175 entry_paste_clipboard(compose
, compose
->focused_editable
,
11176 prefs_common
.linewrap_pastes
,
11177 GDK_SELECTION_CLIPBOARD
, NULL
);
11178 prefs_common
.linewrap_quote
= wrap_quote
;
11182 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11184 Compose
*compose
= (Compose
*)data
;
11185 gint prev_autowrap
;
11186 GtkTextBuffer
*buffer
;
11188 if (compose
->focused_editable
11189 #ifndef GENERIC_UMPC
11190 && gtk_widget_has_focus(compose
->focused_editable
)
11193 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11194 GDK_SELECTION_CLIPBOARD
, NULL
);
11199 #ifndef GENERIC_UMPC
11200 gtk_widget_has_focus(compose
->text
) &&
11202 compose
->gtkaspell
&&
11203 compose
->gtkaspell
->check_while_typing
)
11204 gtkaspell_highlight_all(compose
->gtkaspell
);
11208 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11210 Compose
*compose
= (Compose
*)data
;
11211 gint prev_autowrap
;
11212 GtkTextBuffer
*buffer
;
11214 if (compose
->focused_editable
11215 #ifndef GENERIC_UMPC
11216 && gtk_widget_has_focus(compose
->focused_editable
)
11219 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11220 GDK_SELECTION_CLIPBOARD
, NULL
);
11225 #ifndef GENERIC_UMPC
11226 gtk_widget_has_focus(compose
->text
) &&
11228 compose
->gtkaspell
&&
11229 compose
->gtkaspell
->check_while_typing
)
11230 gtkaspell_highlight_all(compose
->gtkaspell
);
11234 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11236 Compose
*compose
= (Compose
*)data
;
11237 if (compose
->focused_editable
11238 #ifndef GENERIC_UMPC
11239 && gtk_widget_has_focus(compose
->focused_editable
)
11242 entry_allsel(compose
->focused_editable
);
11245 static void textview_move_beginning_of_line (GtkTextView
*text
)
11247 GtkTextBuffer
*buffer
;
11251 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11253 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11254 mark
= gtk_text_buffer_get_insert(buffer
);
11255 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11256 gtk_text_iter_set_line_offset(&ins
, 0);
11257 gtk_text_buffer_place_cursor(buffer
, &ins
);
11260 static void textview_move_forward_character (GtkTextView
*text
)
11262 GtkTextBuffer
*buffer
;
11266 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11268 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11269 mark
= gtk_text_buffer_get_insert(buffer
);
11270 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11271 if (gtk_text_iter_forward_cursor_position(&ins
))
11272 gtk_text_buffer_place_cursor(buffer
, &ins
);
11275 static void textview_move_backward_character (GtkTextView
*text
)
11277 GtkTextBuffer
*buffer
;
11281 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11283 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11284 mark
= gtk_text_buffer_get_insert(buffer
);
11285 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11286 if (gtk_text_iter_backward_cursor_position(&ins
))
11287 gtk_text_buffer_place_cursor(buffer
, &ins
);
11290 static void textview_move_forward_word (GtkTextView
*text
)
11292 GtkTextBuffer
*buffer
;
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
);
11302 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11303 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11304 gtk_text_iter_backward_word_start(&ins
);
11305 gtk_text_buffer_place_cursor(buffer
, &ins
);
11309 static void textview_move_backward_word (GtkTextView
*text
)
11311 GtkTextBuffer
*buffer
;
11315 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11317 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11318 mark
= gtk_text_buffer_get_insert(buffer
);
11319 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11320 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11321 gtk_text_buffer_place_cursor(buffer
, &ins
);
11324 static void textview_move_end_of_line (GtkTextView
*text
)
11326 GtkTextBuffer
*buffer
;
11330 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11332 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11333 mark
= gtk_text_buffer_get_insert(buffer
);
11334 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11335 if (gtk_text_iter_forward_to_line_end(&ins
))
11336 gtk_text_buffer_place_cursor(buffer
, &ins
);
11339 static void textview_move_next_line (GtkTextView
*text
)
11341 GtkTextBuffer
*buffer
;
11346 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11348 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11349 mark
= gtk_text_buffer_get_insert(buffer
);
11350 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11351 offset
= gtk_text_iter_get_line_offset(&ins
);
11352 if (gtk_text_iter_forward_line(&ins
)) {
11353 gtk_text_iter_set_line_offset(&ins
, offset
);
11354 gtk_text_buffer_place_cursor(buffer
, &ins
);
11358 static void textview_move_previous_line (GtkTextView
*text
)
11360 GtkTextBuffer
*buffer
;
11365 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11367 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11368 mark
= gtk_text_buffer_get_insert(buffer
);
11369 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11370 offset
= gtk_text_iter_get_line_offset(&ins
);
11371 if (gtk_text_iter_backward_line(&ins
)) {
11372 gtk_text_iter_set_line_offset(&ins
, offset
);
11373 gtk_text_buffer_place_cursor(buffer
, &ins
);
11377 static void textview_delete_forward_character (GtkTextView
*text
)
11379 GtkTextBuffer
*buffer
;
11381 GtkTextIter ins
, end_iter
;
11383 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11385 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11386 mark
= gtk_text_buffer_get_insert(buffer
);
11387 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11389 if (gtk_text_iter_forward_char(&end_iter
)) {
11390 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11394 static void textview_delete_backward_character (GtkTextView
*text
)
11396 GtkTextBuffer
*buffer
;
11398 GtkTextIter ins
, end_iter
;
11400 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11402 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11403 mark
= gtk_text_buffer_get_insert(buffer
);
11404 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11406 if (gtk_text_iter_backward_char(&end_iter
)) {
11407 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11411 static void textview_delete_forward_word (GtkTextView
*text
)
11413 GtkTextBuffer
*buffer
;
11415 GtkTextIter ins
, end_iter
;
11417 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11419 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11420 mark
= gtk_text_buffer_get_insert(buffer
);
11421 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11423 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11424 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11428 static void textview_delete_backward_word (GtkTextView
*text
)
11430 GtkTextBuffer
*buffer
;
11432 GtkTextIter ins
, end_iter
;
11434 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11436 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11437 mark
= gtk_text_buffer_get_insert(buffer
);
11438 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11440 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11441 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11445 static void textview_delete_line (GtkTextView
*text
)
11447 GtkTextBuffer
*buffer
;
11449 GtkTextIter ins
, start_iter
, end_iter
;
11451 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11453 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11454 mark
= gtk_text_buffer_get_insert(buffer
);
11455 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11458 gtk_text_iter_set_line_offset(&start_iter
, 0);
11461 if (gtk_text_iter_ends_line(&end_iter
)){
11462 if (!gtk_text_iter_forward_char(&end_iter
))
11463 gtk_text_iter_backward_char(&start_iter
);
11466 gtk_text_iter_forward_to_line_end(&end_iter
);
11467 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11470 static void textview_delete_to_line_end (GtkTextView
*text
)
11472 GtkTextBuffer
*buffer
;
11474 GtkTextIter ins
, end_iter
;
11476 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11478 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11479 mark
= gtk_text_buffer_get_insert(buffer
);
11480 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11482 if (gtk_text_iter_ends_line(&end_iter
))
11483 gtk_text_iter_forward_char(&end_iter
);
11485 gtk_text_iter_forward_to_line_end(&end_iter
);
11486 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11489 #define DO_ACTION(name, act) { \
11490 if(!strcmp(name, a_name)) { \
11494 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11496 const gchar
*a_name
= gtk_action_get_name(action
);
11497 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11498 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11499 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11500 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11501 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11502 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11503 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11504 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11505 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11506 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11507 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11508 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11509 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11510 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11511 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11514 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11516 Compose
*compose
= (Compose
*)data
;
11517 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11518 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11520 action
= compose_call_advanced_action_from_path(gaction
);
11523 void (*do_action
) (GtkTextView
*text
);
11524 } action_table
[] = {
11525 {textview_move_beginning_of_line
},
11526 {textview_move_forward_character
},
11527 {textview_move_backward_character
},
11528 {textview_move_forward_word
},
11529 {textview_move_backward_word
},
11530 {textview_move_end_of_line
},
11531 {textview_move_next_line
},
11532 {textview_move_previous_line
},
11533 {textview_delete_forward_character
},
11534 {textview_delete_backward_character
},
11535 {textview_delete_forward_word
},
11536 {textview_delete_backward_word
},
11537 {textview_delete_line
},
11538 {textview_delete_to_line_end
}
11541 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11543 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11544 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11545 if (action_table
[action
].do_action
)
11546 action_table
[action
].do_action(text
);
11548 g_warning("Not implemented yet.");
11552 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11554 GtkAllocation allocation
;
11558 if (GTK_IS_EDITABLE(widget
)) {
11559 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11560 gtk_editable_set_position(GTK_EDITABLE(widget
),
11563 if ((parent
= gtk_widget_get_parent(widget
))
11564 && (parent
= gtk_widget_get_parent(parent
))
11565 && (parent
= gtk_widget_get_parent(parent
))) {
11566 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11567 gtk_widget_get_allocation(widget
, &allocation
);
11568 gint y
= allocation
.y
;
11569 gint height
= allocation
.height
;
11570 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11571 (GTK_SCROLLED_WINDOW(parent
));
11573 gfloat value
= gtk_adjustment_get_value(shown
);
11574 gfloat upper
= gtk_adjustment_get_upper(shown
);
11575 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11576 if (y
< (int)value
) {
11577 gtk_adjustment_set_value(shown
, y
- 1);
11579 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11580 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11581 gtk_adjustment_set_value(shown
,
11582 y
+ height
- (int)page_size
- 1);
11584 gtk_adjustment_set_value(shown
,
11585 (int)upper
- (int)page_size
- 1);
11592 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11593 compose
->focused_editable
= widget
;
11595 #ifdef GENERIC_UMPC
11596 if (GTK_IS_TEXT_VIEW(widget
)
11597 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11598 g_object_ref(compose
->notebook
);
11599 g_object_ref(compose
->edit_vbox
);
11600 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11601 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11602 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11603 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11604 g_object_unref(compose
->notebook
);
11605 g_object_unref(compose
->edit_vbox
);
11606 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11607 G_CALLBACK(compose_grab_focus_cb
),
11609 gtk_widget_grab_focus(widget
);
11610 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11611 G_CALLBACK(compose_grab_focus_cb
),
11613 } else if (!GTK_IS_TEXT_VIEW(widget
)
11614 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11615 g_object_ref(compose
->notebook
);
11616 g_object_ref(compose
->edit_vbox
);
11617 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11618 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11619 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11620 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11621 g_object_unref(compose
->notebook
);
11622 g_object_unref(compose
->edit_vbox
);
11623 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11624 G_CALLBACK(compose_grab_focus_cb
),
11626 gtk_widget_grab_focus(widget
);
11627 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11628 G_CALLBACK(compose_grab_focus_cb
),
11634 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11636 compose
->modified
= TRUE
;
11637 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11638 #ifndef GENERIC_UMPC
11639 compose_set_title(compose
);
11643 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11645 Compose
*compose
= (Compose
*)data
;
11646 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11649 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11651 Compose
*compose
= (Compose
*)data
;
11652 compose_wrap_all_full(compose
, TRUE
);
11655 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11657 Compose
*compose
= (Compose
*)data
;
11659 message_search_compose(compose
);
11662 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11665 Compose
*compose
= (Compose
*)data
;
11666 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11667 if (compose
->autowrap
)
11668 compose_wrap_all_full(compose
, TRUE
);
11669 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11672 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11675 Compose
*compose
= (Compose
*)data
;
11676 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11679 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11681 Compose
*compose
= (Compose
*)data
;
11683 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11684 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11687 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11689 Compose
*compose
= (Compose
*)data
;
11691 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11692 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11695 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11697 g_free(compose
->privacy_system
);
11698 g_free(compose
->encdata
);
11700 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11701 compose_update_privacy_system_menu_item(compose
, warn
);
11704 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11706 if (folder_item
!= NULL
) {
11707 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11708 privacy_system_can_sign(compose
->privacy_system
)) {
11709 compose_use_signing(compose
,
11710 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11712 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11713 privacy_system_can_encrypt(compose
->privacy_system
)) {
11714 compose_use_encryption(compose
,
11715 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11720 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11722 Compose
*compose
= (Compose
*)data
;
11724 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11725 gtk_widget_show(compose
->ruler_hbox
);
11726 prefs_common
.show_ruler
= TRUE
;
11728 gtk_widget_hide(compose
->ruler_hbox
);
11729 gtk_widget_queue_resize(compose
->edit_vbox
);
11730 prefs_common
.show_ruler
= FALSE
;
11734 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11735 GdkDragContext
*context
,
11738 GtkSelectionData
*data
,
11741 gpointer user_data
)
11743 Compose
*compose
= (Compose
*)user_data
;
11746 type
= gtk_selection_data_get_data_type(data
);
11747 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11748 && gtk_drag_get_source_widget(context
) !=
11749 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11750 attach_uri_list(compose
, data
);
11751 } else if (gtk_drag_get_source_widget(context
)
11752 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11753 /* comes from our summaryview */
11754 SummaryView
* summaryview
= NULL
;
11755 GSList
* list
= NULL
, *cur
= NULL
;
11757 if (mainwindow_get_mainwindow())
11758 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11761 list
= summary_get_selected_msg_list(summaryview
);
11763 for (cur
= list
; cur
; cur
= cur
->next
) {
11764 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11765 gchar
*file
= NULL
;
11767 file
= procmsg_get_message_file_full(msginfo
,
11770 compose_attach_append(compose
, (const gchar
*)file
,
11771 (const gchar
*)file
, "message/rfc822", NULL
);
11775 g_slist_free(list
);
11779 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11780 GdkDragContext
*drag_context
,
11782 guint time
, gpointer user_data
)
11784 /* not handling this signal makes compose_insert_drag_received_cb
11789 static gboolean completion_set_focus_to_subject
11790 (GtkWidget
*widget
,
11791 GdkEventKey
*event
,
11794 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11796 /* make backtab move to subject field */
11797 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11798 gtk_widget_grab_focus(compose
->subject_entry
);
11804 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11805 GdkDragContext
*drag_context
,
11808 GtkSelectionData
*data
,
11811 gpointer user_data
)
11813 Compose
*compose
= (Compose
*)user_data
;
11819 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11821 type
= gtk_selection_data_get_data_type(data
);
11822 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11823 AlertValue val
= G_ALERTDEFAULT
;
11824 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11826 list
= uri_list_extract_filenames(ddata
);
11827 num_files
= g_list_length(list
);
11828 if (list
== NULL
&& strstr(ddata
, "://")) {
11829 /* Assume a list of no files, and data has ://, is a remote link */
11830 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11831 gchar
*tmpfile
= get_tmp_file();
11832 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11834 compose_insert_file(compose
, tmpfile
);
11835 claws_unlink(tmpfile
);
11837 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11838 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11841 switch (prefs_common
.compose_dnd_mode
) {
11842 case COMPOSE_DND_ASK
:
11843 msg
= g_strdup_printf(
11845 "Do you want to insert the contents of the file "
11846 "into the message body, or attach it to the email?",
11847 "Do you want to insert the contents of the %d files "
11848 "into the message body, or attach them to the email?",
11851 val
= alertpanel_full(_("Insert or attach?"), msg
,
11852 GTK_STOCK_CANCEL
, _("_Insert"), _("_Attach"),
11854 TRUE
, NULL
, ALERT_QUESTION
);
11857 case COMPOSE_DND_INSERT
:
11858 val
= G_ALERTALTERNATE
;
11860 case COMPOSE_DND_ATTACH
:
11861 val
= G_ALERTOTHER
;
11864 /* unexpected case */
11865 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11868 if (val
& G_ALERTDISABLE
) {
11869 val
&= ~G_ALERTDISABLE
;
11870 /* remember what action to perform by default, only if we don't click Cancel */
11871 if (val
== G_ALERTALTERNATE
)
11872 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11873 else if (val
== G_ALERTOTHER
)
11874 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11877 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11878 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11879 list_free_strings_full(list
);
11881 } else if (val
== G_ALERTOTHER
) {
11882 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11883 list_free_strings_full(list
);
11887 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11888 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11890 list_free_strings_full(list
);
11891 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11896 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11897 GdkDragContext
*drag_context
,
11900 GtkSelectionData
*data
,
11903 gpointer user_data
)
11905 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11906 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11908 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11911 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11912 gchar
*decoded
=g_new(gchar
, strlen(email
));
11915 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11916 gtk_editable_delete_text(entry
, 0, -1);
11917 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11918 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11922 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11925 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11927 Compose
*compose
= (Compose
*)data
;
11929 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11930 compose
->return_receipt
= TRUE
;
11932 compose
->return_receipt
= FALSE
;
11935 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11937 Compose
*compose
= (Compose
*)data
;
11939 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11940 compose
->remove_references
= TRUE
;
11942 compose
->remove_references
= FALSE
;
11945 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11946 ComposeHeaderEntry
*headerentry
)
11948 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11949 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11950 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11954 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11955 GdkEventKey
*event
,
11956 ComposeHeaderEntry
*headerentry
)
11958 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11959 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11960 !(event
->state
& GDK_MODIFIER_MASK
) &&
11961 (event
->keyval
== GDK_KEY_BackSpace
) &&
11962 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11963 gtk_container_remove
11964 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11965 headerentry
->combo
);
11966 gtk_container_remove
11967 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11968 headerentry
->entry
);
11969 headerentry
->compose
->header_list
=
11970 g_slist_remove(headerentry
->compose
->header_list
,
11972 g_free(headerentry
);
11973 } else if (event
->keyval
== GDK_KEY_Tab
) {
11974 if (headerentry
->compose
->header_last
== headerentry
) {
11975 /* Override default next focus, and give it to subject_entry
11976 * instead of notebook tabs
11978 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11979 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11986 static gboolean
scroll_postpone(gpointer data
)
11988 Compose
*compose
= (Compose
*)data
;
11990 if (compose
->batch
)
11993 GTK_EVENTS_FLUSH();
11994 compose_show_first_last_header(compose
, FALSE
);
11998 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11999 ComposeHeaderEntry
*headerentry
)
12001 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
12002 compose_create_header_entry(headerentry
->compose
);
12003 g_signal_handlers_disconnect_matched
12004 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
12005 0, 0, NULL
, NULL
, headerentry
);
12007 if (!headerentry
->compose
->batch
)
12008 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
12012 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
12014 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
12015 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
12019 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
12021 GtkAdjustment
*vadj
;
12023 cm_return_if_fail(compose
);
12028 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
12029 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
12030 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
12031 gtk_widget_get_parent(compose
->header_table
)));
12032 gtk_adjustment_set_value(vadj
, (show_first
?
12033 gtk_adjustment_get_lower(vadj
) :
12034 (gtk_adjustment_get_upper(vadj
) -
12035 gtk_adjustment_get_page_size(vadj
))));
12036 gtk_adjustment_changed(vadj
);
12039 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
12040 const gchar
*text
, gint len
, Compose
*compose
)
12042 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
12043 (G_OBJECT(compose
->text
), "paste_as_quotation"));
12046 cm_return_if_fail(text
!= NULL
);
12048 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
12049 G_CALLBACK(text_inserted
),
12051 if (paste_as_quotation
) {
12053 const gchar
*qmark
;
12055 GtkTextIter start_iter
;
12058 len
= strlen(text
);
12060 new_text
= g_strndup(text
, len
);
12062 qmark
= compose_quote_char_from_context(compose
);
12064 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
12065 gtk_text_buffer_place_cursor(buffer
, iter
);
12067 pos
= gtk_text_iter_get_offset(iter
);
12069 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
12070 _("Quote format error at line %d."));
12071 quote_fmt_reset_vartable();
12073 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
12074 GINT_TO_POINTER(paste_as_quotation
- 1));
12076 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12077 gtk_text_buffer_place_cursor(buffer
, iter
);
12078 gtk_text_buffer_delete_mark(buffer
, mark
);
12080 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
12081 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
12082 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
12083 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
12084 gtk_text_buffer_delete_mark(buffer
, mark
);
12086 if (strcmp(text
, "\n") || compose
->automatic_break
12087 || gtk_text_iter_starts_line(iter
)) {
12088 GtkTextIter before_ins
;
12089 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
12090 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
12091 before_ins
= *iter
;
12092 gtk_text_iter_backward_chars(&before_ins
, len
);
12093 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
12096 /* check if the preceding is just whitespace or quote */
12097 GtkTextIter start_line
;
12098 gchar
*tmp
= NULL
, *quote
= NULL
;
12099 gint quote_len
= 0, is_normal
= 0;
12100 start_line
= *iter
;
12101 gtk_text_iter_set_line_offset(&start_line
, 0);
12102 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
12105 if (*tmp
== '\0') {
12108 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
12116 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
12118 gtk_text_buffer_insert_with_tags_by_name(buffer
,
12119 iter
, text
, len
, "no_join", NULL
);
12124 if (!paste_as_quotation
) {
12125 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
12126 compose_beautify_paragraph(compose
, iter
, FALSE
);
12127 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12128 gtk_text_buffer_delete_mark(buffer
, mark
);
12131 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
12132 G_CALLBACK(text_inserted
),
12134 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
12136 if (compose_can_autosave(compose
) &&
12137 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
12138 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
12139 compose
->draft_timeout_tag
= g_timeout_add
12140 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
12144 static void compose_check_all(GtkAction
*action
, gpointer data
)
12146 Compose
*compose
= (Compose
*)data
;
12147 if (!compose
->gtkaspell
)
12150 if (gtk_widget_has_focus(compose
->subject_entry
))
12151 claws_spell_entry_check_all(
12152 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12154 gtkaspell_check_all(compose
->gtkaspell
);
12157 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12159 Compose
*compose
= (Compose
*)data
;
12160 if (compose
->gtkaspell
) {
12161 claws_spell_entry_recheck_all(
12162 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12163 gtkaspell_highlight_all(compose
->gtkaspell
);
12167 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12169 Compose
*compose
= (Compose
*)data
;
12170 if (!compose
->gtkaspell
) {
12171 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12175 if (gtk_widget_has_focus(compose
->subject_entry
))
12176 claws_spell_entry_check_backwards(
12177 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12179 gtkaspell_check_backwards(compose
->gtkaspell
);
12182 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12184 Compose
*compose
= (Compose
*)data
;
12185 if (!compose
->gtkaspell
) {
12186 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12190 if (gtk_widget_has_focus(compose
->subject_entry
))
12191 claws_spell_entry_check_forwards_go(
12192 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12194 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12199 *\brief Guess originating forward account from MsgInfo and several
12200 * "common preference" settings. Return NULL if no guess.
12202 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12204 PrefsAccount
*account
= NULL
;
12206 cm_return_val_if_fail(msginfo
, NULL
);
12207 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12208 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12210 if (msginfo
->folder
->prefs
->enable_default_account
)
12211 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12213 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12215 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12216 extract_address(to
);
12217 account
= account_find_from_address(to
, FALSE
);
12220 if (!account
&& prefs_common
.forward_account_autosel
) {
12222 if (!procheader_get_header_from_msginfo
12223 (msginfo
, &cc
, "Cc:")) {
12224 gchar
*buf
= cc
+ strlen("Cc:");
12225 extract_address(buf
);
12226 account
= account_find_from_address(buf
, FALSE
);
12231 if (!account
&& prefs_common
.forward_account_autosel
) {
12232 gchar
*deliveredto
= NULL
;
12233 if (!procheader_get_header_from_msginfo
12234 (msginfo
, &deliveredto
, "Delivered-To:")) {
12235 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12236 extract_address(buf
);
12237 account
= account_find_from_address(buf
, FALSE
);
12238 g_free(deliveredto
);
12243 account
= msginfo
->folder
->folder
->account
;
12248 gboolean
compose_close(Compose
*compose
)
12252 cm_return_val_if_fail(compose
, FALSE
);
12254 if (!g_mutex_trylock(compose
->mutex
)) {
12255 /* we have to wait for the (possibly deferred by auto-save)
12256 * drafting to be done, before destroying the compose under
12258 debug_print("waiting for drafting to finish...\n");
12259 compose_allow_user_actions(compose
, FALSE
);
12260 if (compose
->close_timeout_tag
== 0) {
12261 compose
->close_timeout_tag
=
12262 g_timeout_add (500, (GSourceFunc
) compose_close
,
12268 if (compose
->draft_timeout_tag
>= 0) {
12269 g_source_remove(compose
->draft_timeout_tag
);
12270 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12273 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
12274 if (!compose
->batch
) {
12275 prefs_common
.compose_x
= x
;
12276 prefs_common
.compose_y
= y
;
12278 g_mutex_unlock(compose
->mutex
);
12279 compose_destroy(compose
);
12284 * Add entry field for each address in list.
12285 * \param compose E-Mail composition object.
12286 * \param listAddress List of (formatted) E-Mail addresses.
12288 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12291 node
= listAddress
;
12293 addr
= ( gchar
* ) node
->data
;
12294 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12295 node
= g_list_next( node
);
12299 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12300 guint action
, gboolean opening_multiple
)
12302 gchar
*body
= NULL
;
12303 GSList
*new_msglist
= NULL
;
12304 MsgInfo
*tmp_msginfo
= NULL
;
12305 gboolean originally_enc
= FALSE
;
12306 gboolean originally_sig
= FALSE
;
12307 Compose
*compose
= NULL
;
12308 gchar
*s_system
= NULL
;
12310 cm_return_if_fail(msginfo_list
!= NULL
);
12312 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12313 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12314 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12316 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12317 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12318 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12319 orig_msginfo
, mimeinfo
);
12320 if (tmp_msginfo
!= NULL
) {
12321 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12323 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12324 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12325 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12327 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12328 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12329 if (orig_msginfo
->tags
) {
12330 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12331 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12337 if (!opening_multiple
&& msgview
!= NULL
)
12338 body
= messageview_get_selection(msgview
);
12341 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12342 procmsg_msginfo_free(&tmp_msginfo
);
12343 g_slist_free(new_msglist
);
12345 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12347 if (compose
&& originally_enc
) {
12348 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12351 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12352 compose_force_signing(compose
, compose
->account
, s_system
);
12356 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12359 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12362 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12363 && msginfo_list
!= NULL
12364 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12365 GSList
*cur
= msginfo_list
;
12366 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12367 "messages. Opening the windows "
12368 "could take some time. Do you "
12369 "want to continue?"),
12370 g_slist_length(msginfo_list
));
12371 if (g_slist_length(msginfo_list
) > 9
12372 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_YES
, NULL
,
12373 ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12378 /* We'll open multiple compose windows */
12379 /* let the WM place the next windows */
12380 compose_force_window_origin
= FALSE
;
12381 for (; cur
; cur
= cur
->next
) {
12383 tmplist
.data
= cur
->data
;
12384 tmplist
.next
= NULL
;
12385 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12387 compose_force_window_origin
= TRUE
;
12389 /* forwarding multiple mails as attachments is done via a
12390 * single compose window */
12391 if (msginfo_list
!= NULL
) {
12392 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12393 } else if (msgview
!= NULL
) {
12395 tmplist
.data
= msgview
->msginfo
;
12396 tmplist
.next
= NULL
;
12397 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12399 debug_print("Nothing to reply to\n");
12404 void compose_check_for_email_account(Compose
*compose
)
12406 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12412 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12413 ac
= account_get_cur_account();
12414 if (ac
->protocol
== A_NNTP
) {
12415 list
= account_get_list();
12417 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12418 curr
= (PrefsAccount
*) list
->data
;
12419 if (curr
->protocol
!= A_NNTP
) {
12425 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12430 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12431 const gchar
*address
)
12433 GSList
*msginfo_list
= NULL
;
12434 gchar
*body
= messageview_get_selection(msgview
);
12437 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12439 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12440 compose_check_for_email_account(compose
);
12441 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12442 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12443 compose_reply_set_subject(compose
, msginfo
);
12446 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12449 void compose_set_position(Compose
*compose
, gint pos
)
12451 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12453 gtkut_text_view_set_position(text
, pos
);
12456 gboolean
compose_search_string(Compose
*compose
,
12457 const gchar
*str
, gboolean case_sens
)
12459 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12461 return gtkut_text_view_search_string(text
, str
, case_sens
);
12464 gboolean
compose_search_string_backward(Compose
*compose
,
12465 const gchar
*str
, gboolean case_sens
)
12467 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12469 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12472 /* allocate a msginfo structure and populate its data from a compose data structure */
12473 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12475 MsgInfo
*newmsginfo
;
12477 gchar date
[RFC822_DATE_BUFFSIZE
];
12479 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12481 newmsginfo
= procmsg_msginfo_new();
12484 get_rfc822_date(date
, sizeof(date
));
12485 newmsginfo
->date
= g_strdup(date
);
12488 if (compose
->from_name
) {
12489 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12490 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12494 if (compose
->subject_entry
)
12495 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12497 /* to, cc, reply-to, newsgroups */
12498 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12499 gchar
*header
= gtk_editable_get_chars(
12501 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12502 gchar
*entry
= gtk_editable_get_chars(
12503 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12505 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12506 if ( newmsginfo
->to
== NULL
) {
12507 newmsginfo
->to
= g_strdup(entry
);
12508 } else if (entry
&& *entry
) {
12509 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12510 g_free(newmsginfo
->to
);
12511 newmsginfo
->to
= tmp
;
12514 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12515 if ( newmsginfo
->cc
== NULL
) {
12516 newmsginfo
->cc
= g_strdup(entry
);
12517 } else if (entry
&& *entry
) {
12518 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12519 g_free(newmsginfo
->cc
);
12520 newmsginfo
->cc
= tmp
;
12523 if ( strcasecmp(header
,
12524 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12525 if ( newmsginfo
->newsgroups
== NULL
) {
12526 newmsginfo
->newsgroups
= g_strdup(entry
);
12527 } else if (entry
&& *entry
) {
12528 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12529 g_free(newmsginfo
->newsgroups
);
12530 newmsginfo
->newsgroups
= tmp
;
12538 /* other data is unset */
12544 /* update compose's dictionaries from folder dict settings */
12545 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12546 FolderItem
*folder_item
)
12548 cm_return_if_fail(compose
!= NULL
);
12550 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12551 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12553 if (prefs
->enable_default_dictionary
)
12554 gtkaspell_change_dict(compose
->gtkaspell
,
12555 prefs
->default_dictionary
, FALSE
);
12556 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12557 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12558 prefs
->default_alt_dictionary
);
12559 if (prefs
->enable_default_dictionary
12560 || prefs
->enable_default_alt_dictionary
)
12561 compose_spell_menu_changed(compose
);
12566 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12568 Compose
*compose
= (Compose
*)data
;
12570 cm_return_if_fail(compose
!= NULL
);
12572 gtk_widget_grab_focus(compose
->text
);
12575 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12577 gtk_combo_box_popup(GTK_COMBO_BOX(data
));