2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2020 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
);
2524 compose
->updating
= TRUE
;
2526 compose_create_tags(GTK_TEXT_VIEW(compose
->text
), compose
);
2527 compose
->replyinfo
= NULL
;
2528 compose
->fwdinfo
= NULL
;
2530 compose_show_first_last_header(compose
, TRUE
);
2532 gtk_widget_grab_focus(compose
->header_last
->entry
);
2534 filename
= procmsg_get_message_file(msginfo
);
2536 if (filename
== NULL
) {
2537 compose
->updating
= FALSE
;
2538 compose_destroy(compose
);
2543 compose
->redirect_filename
= filename
;
2545 /* Set save folder */
2546 item
= msginfo
->folder
;
2547 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
2548 gchar
*folderidentifier
;
2550 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
2551 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
2552 folderidentifier
= folder_item_get_identifier(item
);
2553 compose_set_save_to(compose
, folderidentifier
);
2554 g_free(folderidentifier
);
2557 compose_attach_parts(compose
, msginfo
);
2559 if (msginfo
->subject
)
2560 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
2562 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
2564 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
, FALSE
, FALSE
,
2565 _("The body of the \"Redirect\" template has an error at line %d."));
2566 quote_fmt_reset_vartable();
2567 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), FALSE
);
2569 compose_colorize_signature(compose
);
2572 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Add", FALSE
);
2573 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", FALSE
);
2574 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", FALSE
);
2576 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/Save", FALSE
);
2577 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertFile", FALSE
);
2578 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/AttachFile", FALSE
);
2579 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/InsertSig", FALSE
);
2580 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message/ReplaceSig", FALSE
);
2581 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", FALSE
);
2582 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", FALSE
);
2583 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", FALSE
);
2584 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Actions", FALSE
);
2586 if (compose
->toolbar
->draft_btn
)
2587 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
2588 if (compose
->toolbar
->insert_btn
)
2589 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
2590 if (compose
->toolbar
->attach_btn
)
2591 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
2592 if (compose
->toolbar
->sig_btn
)
2593 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
2594 if (compose
->toolbar
->exteditor_btn
)
2595 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
2596 if (compose
->toolbar
->linewrap_current_btn
)
2597 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, FALSE
);
2598 if (compose
->toolbar
->linewrap_all_btn
)
2599 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, FALSE
);
2600 if (compose
->toolbar
->privacy_sign_btn
)
2601 gtk_widget_set_sensitive(compose
->toolbar
->privacy_sign_btn
, FALSE
);
2602 if (compose
->toolbar
->privacy_encrypt_btn
)
2603 gtk_widget_set_sensitive(compose
->toolbar
->privacy_encrypt_btn
, FALSE
);
2605 compose
->modified
= FALSE
;
2606 compose_set_title(compose
);
2607 compose
->updating
= FALSE
;
2608 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
; /* desinhibit auto-drafting after loading */
2609 SCROLL_TO_CURSOR(compose
);
2611 if (compose
->deferred_destroy
) {
2612 compose_destroy(compose
);
2616 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
2621 const GList
*compose_get_compose_list(void)
2623 return compose_list
;
2626 void compose_entry_append(Compose
*compose
, const gchar
*address
,
2627 ComposeEntryType type
, ComposePrefType pref_type
)
2629 const gchar
*header
;
2631 gboolean in_quote
= FALSE
;
2632 if (!address
|| *address
== '\0') return;
2639 header
= N_("Bcc:");
2641 case COMPOSE_REPLYTO
:
2642 header
= N_("Reply-To:");
2644 case COMPOSE_NEWSGROUPS
:
2645 header
= N_("Newsgroups:");
2647 case COMPOSE_FOLLOWUPTO
:
2648 header
= N_( "Followup-To:");
2650 case COMPOSE_INREPLYTO
:
2651 header
= N_( "In-Reply-To:");
2658 header
= prefs_common_translated_header_name(header
);
2660 cur
= begin
= (gchar
*)address
;
2662 /* we separate the line by commas, but not if we're inside a quoted
2664 while (*cur
!= '\0') {
2666 in_quote
= !in_quote
;
2667 if (*cur
== ',' && !in_quote
) {
2668 gchar
*tmp
= g_strdup(begin
);
2670 tmp
[cur
-begin
]='\0';
2673 while (*tmp
== ' ' || *tmp
== '\t')
2675 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2676 compose_entry_indicate(compose
, tmp
);
2683 gchar
*tmp
= g_strdup(begin
);
2685 tmp
[cur
-begin
]='\0';
2686 while (*tmp
== ' ' || *tmp
== '\t')
2688 compose_add_header_entry(compose
, header
, tmp
, pref_type
);
2689 compose_entry_indicate(compose
, tmp
);
2694 static void compose_entry_indicate(Compose
*compose
, const gchar
*mailto
)
2699 for (h_list
= compose
->header_list
; h_list
!= NULL
; h_list
= h_list
->next
) {
2700 entry
= GTK_ENTRY(((ComposeHeaderEntry
*)h_list
->data
)->entry
);
2701 if (gtk_entry_get_text(entry
) &&
2702 !g_utf8_collate(gtk_entry_get_text(entry
), mailto
)) {
2703 gtk_widget_modify_base(
2704 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2705 GTK_STATE_NORMAL
, &default_header_bgcolor
);
2706 gtk_widget_modify_text(
2707 GTK_WIDGET(((ComposeHeaderEntry
*)h_list
->data
)->entry
),
2708 GTK_STATE_NORMAL
, &default_header_color
);
2713 void compose_toolbar_cb(gint action
, gpointer data
)
2715 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
2716 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
2718 cm_return_if_fail(compose
!= NULL
);
2722 compose_send_cb(NULL
, compose
);
2725 compose_send_later_cb(NULL
, compose
);
2728 compose_draft(compose
, COMPOSE_QUIT_EDITING
);
2731 compose_insert_file_cb(NULL
, compose
);
2734 compose_attach_cb(NULL
, compose
);
2737 compose_insert_sig(compose
, FALSE
);
2740 compose_insert_sig(compose
, TRUE
);
2743 compose_ext_editor_cb(NULL
, compose
);
2745 case A_LINEWRAP_CURRENT
:
2746 compose_beautify_paragraph(compose
, NULL
, TRUE
);
2748 case A_LINEWRAP_ALL
:
2749 compose_wrap_all_full(compose
, TRUE
);
2752 compose_address_cb(NULL
, compose
);
2755 case A_CHECK_SPELLING
:
2756 compose_check_all(NULL
, compose
);
2759 case A_PRIVACY_SIGN
:
2761 case A_PRIVACY_ENCRYPT
:
2768 static MailField
compose_entries_set(Compose
*compose
, const gchar
*mailto
, ComposeEntryType to_type
)
2773 gchar
*subject
= NULL
;
2777 gchar
**attach
= NULL
;
2778 gchar
*inreplyto
= NULL
;
2779 MailField mfield
= NO_FIELD_PRESENT
;
2781 /* get mailto parts but skip from */
2782 scan_mailto_url(mailto
, NULL
, &to
, &cc
, &bcc
, &subject
, &body
, &attach
, &inreplyto
);
2785 compose_entry_append(compose
, to
, to_type
, PREF_MAILTO
);
2786 mfield
= TO_FIELD_PRESENT
;
2789 compose_entry_append(compose
, cc
, COMPOSE_CC
, PREF_MAILTO
);
2791 compose_entry_append(compose
, bcc
, COMPOSE_BCC
, PREF_MAILTO
);
2793 if (!g_utf8_validate (subject
, -1, NULL
)) {
2794 temp
= g_locale_to_utf8 (subject
, -1, NULL
, &len
, NULL
);
2795 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), temp
);
2798 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
2800 mfield
= SUBJECT_FIELD_PRESENT
;
2803 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
2804 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
2807 gboolean prev_autowrap
= compose
->autowrap
;
2809 compose
->autowrap
= FALSE
;
2811 mark
= gtk_text_buffer_get_insert(buffer
);
2812 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
2814 if (!g_utf8_validate (body
, -1, NULL
)) {
2815 temp
= g_locale_to_utf8 (body
, -1, NULL
, &len
, NULL
);
2816 gtk_text_buffer_insert(buffer
, &iter
, temp
, -1);
2819 gtk_text_buffer_insert(buffer
, &iter
, body
, -1);
2821 gtk_text_buffer_insert(buffer
, &iter
, "\n", 1);
2823 compose
->autowrap
= prev_autowrap
;
2824 if (compose
->autowrap
)
2825 compose_wrap_all(compose
);
2826 mfield
= BODY_FIELD_PRESENT
;
2830 gint i
= 0, att
= 0;
2831 gchar
*warn_files
= NULL
;
2832 while (attach
[i
] != NULL
) {
2833 gchar
*utf8_filename
= conv_filename_to_utf8(attach
[i
]);
2834 if (utf8_filename
) {
2835 if (compose_attach_append(compose
, attach
[i
], utf8_filename
, NULL
, NULL
)) {
2836 gchar
*tmp
= g_strdup_printf("%s%s\n",
2837 warn_files
?warn_files
:"",
2843 g_free(utf8_filename
);
2845 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2850 alertpanel_notice(ngettext(
2851 "The following file has been attached: \n%s",
2852 "The following files have been attached: \n%s", att
), warn_files
);
2857 compose_entry_append(compose
, inreplyto
, COMPOSE_INREPLYTO
, PREF_MAILTO
);
2870 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
2872 static HeaderEntry hentry
[] = {
2873 {"Reply-To:", NULL
, TRUE
},
2874 {"Cc:", NULL
, TRUE
},
2875 {"References:", NULL
, FALSE
},
2876 {"Bcc:", NULL
, TRUE
},
2877 {"Newsgroups:", NULL
, TRUE
},
2878 {"Followup-To:", NULL
, TRUE
},
2879 {"List-Post:", NULL
, FALSE
},
2880 {"X-Priority:", NULL
, FALSE
},
2881 {NULL
, NULL
, FALSE
}
2898 cm_return_val_if_fail(msginfo
!= NULL
, -1);
2900 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
2901 procheader_get_header_fields(fp
, hentry
);
2904 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
2905 if (hentry
[H_REPLY_TO
].body
[0] != '\0') {
2907 conv_unmime_header(hentry
[H_REPLY_TO
].body
,
2910 g_free(hentry
[H_REPLY_TO
].body
);
2911 hentry
[H_REPLY_TO
].body
= NULL
;
2913 if (hentry
[H_CC
].body
!= NULL
) {
2914 compose
->cc
= conv_unmime_header(hentry
[H_CC
].body
, NULL
, TRUE
);
2915 g_free(hentry
[H_CC
].body
);
2916 hentry
[H_CC
].body
= NULL
;
2918 if (hentry
[H_REFERENCES
].body
!= NULL
) {
2919 if (compose
->mode
== COMPOSE_REEDIT
)
2920 compose
->references
= hentry
[H_REFERENCES
].body
;
2922 compose
->references
= compose_parse_references
2923 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
2924 g_free(hentry
[H_REFERENCES
].body
);
2926 hentry
[H_REFERENCES
].body
= NULL
;
2928 if (hentry
[H_BCC
].body
!= NULL
) {
2929 if (compose
->mode
== COMPOSE_REEDIT
)
2931 conv_unmime_header(hentry
[H_BCC
].body
, NULL
, TRUE
);
2932 g_free(hentry
[H_BCC
].body
);
2933 hentry
[H_BCC
].body
= NULL
;
2935 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
2936 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
2937 hentry
[H_NEWSGROUPS
].body
= NULL
;
2939 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
2940 if (hentry
[H_FOLLOWUP_TO
].body
[0] != '\0') {
2941 compose
->followup_to
=
2942 conv_unmime_header(hentry
[H_FOLLOWUP_TO
].body
,
2945 g_free(hentry
[H_FOLLOWUP_TO
].body
);
2946 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
2948 if (hentry
[H_LIST_POST
].body
!= NULL
) {
2949 gchar
*to
= NULL
, *start
= NULL
;
2951 extract_address(hentry
[H_LIST_POST
].body
);
2952 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
2953 start
= strstr(hentry
[H_LIST_POST
].body
, "mailto:");
2955 scan_mailto_url(start
? start
: hentry
[H_LIST_POST
].body
,
2956 NULL
, &to
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
2959 g_free(compose
->ml_post
);
2960 compose
->ml_post
= to
;
2963 g_free(hentry
[H_LIST_POST
].body
);
2964 hentry
[H_LIST_POST
].body
= NULL
;
2967 /* CLAWS - X-Priority */
2968 if (compose
->mode
== COMPOSE_REEDIT
)
2969 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
2972 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
2973 g_free(hentry
[H_X_PRIORITY
].body
);
2975 hentry
[H_X_PRIORITY
].body
= NULL
;
2977 if (priority
< PRIORITY_HIGHEST
||
2978 priority
> PRIORITY_LOWEST
)
2979 priority
= PRIORITY_NORMAL
;
2981 compose
->priority
= priority
;
2984 if (compose
->mode
== COMPOSE_REEDIT
) {
2985 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2986 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
2988 if (msginfo
->msgid
&& *msginfo
->msgid
&&
2989 compose
->folder
!= NULL
&&
2990 compose
->folder
->stype
== F_DRAFT
)
2991 compose
->msgid
= g_strdup(msginfo
->msgid
);
2993 if (msginfo
->msgid
&& *msginfo
->msgid
)
2994 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
2996 if (!compose
->references
) {
2997 if (msginfo
->msgid
&& *msginfo
->msgid
) {
2998 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
2999 compose
->references
=
3000 g_strdup_printf("<%s>\n\t<%s>",
3004 compose
->references
=
3005 g_strconcat("<", msginfo
->msgid
, ">",
3007 } else if (msginfo
->inreplyto
&& *msginfo
->inreplyto
) {
3008 compose
->references
=
3009 g_strconcat("<", msginfo
->inreplyto
, ">",
3018 static gint
compose_parse_manual_headers(Compose
*compose
, MsgInfo
*msginfo
, HeaderEntry
*entries
)
3023 cm_return_val_if_fail(msginfo
!= NULL
, -1);
3025 if ((fp
= procmsg_open_message(msginfo
, FALSE
)) == NULL
) return -1;
3026 procheader_get_header_fields(fp
, entries
);
3030 while (he
!= NULL
&& he
->name
!= NULL
) {
3032 GtkListStore
*model
= NULL
;
3034 debug_print("Adding manual header: %s with value %s\n", he
->name
, he
->body
);
3035 model
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose
->header_last
->combo
)));
3036 COMBOBOX_ADD(model
, he
->name
, COMPOSE_TO
);
3037 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose
->header_last
->combo
), &iter
);
3038 gtk_entry_set_text(GTK_ENTRY(compose
->header_last
->entry
), he
->body
);
3045 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
3047 GSList
*ref_id_list
, *cur
;
3051 ref_id_list
= references_list_append(NULL
, ref
);
3052 if (!ref_id_list
) return NULL
;
3053 if (msgid
&& *msgid
)
3054 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
3059 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
3060 /* "<" + Message-ID + ">" + CR+LF+TAB */
3061 len
+= strlen((gchar
*)cur
->data
) + 5;
3063 if (len
> MAX_REFERENCES_LEN
) {
3064 /* remove second message-ID */
3065 if (ref_id_list
&& ref_id_list
->next
&&
3066 ref_id_list
->next
->next
) {
3067 g_free(ref_id_list
->next
->data
);
3068 ref_id_list
= g_slist_remove
3069 (ref_id_list
, ref_id_list
->next
->data
);
3071 slist_free_strings_full(ref_id_list
);
3078 new_ref
= g_string_new("");
3079 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
3080 if (new_ref
->len
> 0)
3081 g_string_append(new_ref
, "\n\t");
3082 g_string_append_printf(new_ref
, "<%s>", (gchar
*)cur
->data
);
3085 slist_free_strings_full(ref_id_list
);
3087 new_ref_str
= new_ref
->str
;
3088 g_string_free(new_ref
, FALSE
);
3093 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
3094 const gchar
*fmt
, const gchar
*qmark
,
3095 const gchar
*body
, gboolean rewrap
,
3096 gboolean need_unescape
,
3097 const gchar
*err_msg
)
3099 MsgInfo
* dummyinfo
= NULL
;
3100 gchar
*quote_str
= NULL
;
3102 gboolean prev_autowrap
;
3103 const gchar
*trimmed_body
= body
;
3104 gint cursor_pos
= -1;
3105 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3106 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3111 SIGNAL_BLOCK(buffer
);
3114 dummyinfo
= compose_msginfo_new_from_compose(compose
);
3115 msginfo
= dummyinfo
;
3118 if (qmark
!= NULL
) {
3120 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
3121 compose
->gtkaspell
);
3123 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
3125 quote_fmt_scan_string(qmark
);
3128 buf
= quote_fmt_get_buffer();
3131 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3133 Xstrdup_a(quote_str
, buf
, goto error
)
3136 if (fmt
&& *fmt
!= '\0') {
3139 while (*trimmed_body
== '\n')
3143 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
,
3144 compose
->gtkaspell
);
3146 quote_fmt_init(msginfo
, quote_str
, trimmed_body
, FALSE
, compose
->account
, FALSE
);
3148 if (need_unescape
) {
3151 /* decode \-escape sequences in the internal representation of the quote format */
3152 tmp
= g_malloc(strlen(fmt
)+1);
3153 pref_get_unescaped_pref(tmp
, fmt
);
3154 quote_fmt_scan_string(tmp
);
3158 quote_fmt_scan_string(fmt
);
3162 buf
= quote_fmt_get_buffer();
3165 gint line
= quote_fmt_get_line();
3166 alertpanel_error(err_msg
, line
);
3174 prev_autowrap
= compose
->autowrap
;
3175 compose
->autowrap
= FALSE
;
3177 mark
= gtk_text_buffer_get_insert(buffer
);
3178 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3179 if (g_utf8_validate(buf
, -1, NULL
)) {
3180 gtk_text_buffer_insert(buffer
, &iter
, buf
, -1);
3182 gchar
*tmpout
= NULL
;
3183 tmpout
= conv_codeset_strdup
3184 (buf
, conv_get_locale_charset_str_no_utf8(),
3186 if (!tmpout
|| !g_utf8_validate(tmpout
, -1, NULL
)) {
3188 tmpout
= g_malloc(strlen(buf
)*2+1);
3189 conv_localetodisp(tmpout
, strlen(buf
)*2+1, buf
);
3191 gtk_text_buffer_insert(buffer
, &iter
, tmpout
, -1);
3195 cursor_pos
= quote_fmt_get_cursor_pos();
3196 if (cursor_pos
== -1)
3197 cursor_pos
= gtk_text_iter_get_offset(&iter
);
3198 compose
->set_cursor_pos
= cursor_pos
;
3200 gtk_text_buffer_get_start_iter(buffer
, &iter
);
3201 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
3202 gtk_text_buffer_place_cursor(buffer
, &iter
);
3204 compose
->autowrap
= prev_autowrap
;
3205 if (compose
->autowrap
&& rewrap
)
3206 compose_wrap_all(compose
);
3213 SIGNAL_UNBLOCK(buffer
);
3215 procmsg_msginfo_free( &dummyinfo
);
3220 /* if ml_post is of type addr@host and from is of type
3221 * addr-anything@host, return TRUE
3223 static gboolean
is_subscription(const gchar
*ml_post
, const gchar
*from
)
3225 gchar
*left_ml
= NULL
;
3226 gchar
*right_ml
= NULL
;
3227 gchar
*left_from
= NULL
;
3228 gchar
*right_from
= NULL
;
3229 gboolean result
= FALSE
;
3231 if (!ml_post
|| !from
)
3234 left_ml
= g_strdup(ml_post
);
3235 if (strstr(left_ml
, "@")) {
3236 right_ml
= strstr(left_ml
, "@")+1;
3237 *(strstr(left_ml
, "@")) = '\0';
3240 left_from
= g_strdup(from
);
3241 if (strstr(left_from
, "@")) {
3242 right_from
= strstr(left_from
, "@")+1;
3243 *(strstr(left_from
, "@")) = '\0';
3246 if (right_ml
&& right_from
3247 && !strncmp(left_from
, left_ml
, strlen(left_ml
))
3248 && !strcmp(right_from
, right_ml
)) {
3257 static void compose_set_folder_prefs(Compose
*compose
, FolderItem
*folder
,
3258 gboolean respect_default_to
)
3262 if (!folder
|| !folder
->prefs
)
3265 if (respect_default_to
&& folder
->prefs
->enable_default_to
) {
3266 compose_entry_append(compose
, folder
->prefs
->default_to
,
3267 COMPOSE_TO
, PREF_FOLDER
);
3268 compose_entry_indicate(compose
, folder
->prefs
->default_to
);
3270 if (folder
->prefs
->enable_default_cc
) {
3271 compose_entry_append(compose
, folder
->prefs
->default_cc
,
3272 COMPOSE_CC
, PREF_FOLDER
);
3273 compose_entry_indicate(compose
, folder
->prefs
->default_cc
);
3275 if (folder
->prefs
->enable_default_bcc
) {
3276 compose_entry_append(compose
, folder
->prefs
->default_bcc
,
3277 COMPOSE_BCC
, PREF_FOLDER
);
3278 compose_entry_indicate(compose
, folder
->prefs
->default_bcc
);
3280 if (folder
->prefs
->enable_default_replyto
) {
3281 compose_entry_append(compose
, folder
->prefs
->default_replyto
,
3282 COMPOSE_REPLYTO
, PREF_FOLDER
);
3283 compose_entry_indicate(compose
, folder
->prefs
->default_replyto
);
3287 static void compose_reply_set_subject(Compose
*compose
, MsgInfo
*msginfo
)
3292 if (!compose
|| !msginfo
)
3295 if (msginfo
->subject
&& *msginfo
->subject
) {
3296 buf
= p
= g_strdup(msginfo
->subject
);
3297 p
+= subject_get_prefix_length(p
);
3298 memmove(buf
, p
, strlen(p
) + 1);
3300 buf2
= g_strdup_printf("Re: %s", buf
);
3301 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
3306 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
3309 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
3310 gboolean to_all
, gboolean to_ml
,
3312 gboolean followup_and_reply_to
)
3314 GSList
*cc_list
= NULL
;
3317 gchar
*replyto
= NULL
;
3318 gchar
*ac_email
= NULL
;
3320 gboolean reply_to_ml
= FALSE
;
3321 gboolean default_reply_to
= FALSE
;
3323 cm_return_if_fail(compose
->account
!= NULL
);
3324 cm_return_if_fail(msginfo
!= NULL
);
3326 reply_to_ml
= to_ml
&& compose
->ml_post
;
3328 default_reply_to
= msginfo
->folder
&&
3329 msginfo
->folder
->prefs
->enable_default_reply_to
;
3331 if (compose
->account
->protocol
!= A_NNTP
) {
3332 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
3334 if (reply_to_ml
&& !default_reply_to
) {
3336 gboolean is_subscr
= is_subscription(compose
->ml_post
,
3339 /* normal answer to ml post with a reply-to */
3340 compose_entry_append(compose
,
3342 COMPOSE_TO
, PREF_ML
);
3343 if (compose
->replyto
)
3344 compose_entry_append(compose
,
3346 COMPOSE_CC
, PREF_ML
);
3348 /* answer to subscription confirmation */
3349 if (compose
->replyto
)
3350 compose_entry_append(compose
,
3352 COMPOSE_TO
, PREF_ML
);
3353 else if (msginfo
->from
)
3354 compose_entry_append(compose
,
3356 COMPOSE_TO
, PREF_ML
);
3359 else if (!(to_all
|| to_sender
) && default_reply_to
) {
3360 compose_entry_append(compose
,
3361 msginfo
->folder
->prefs
->default_reply_to
,
3362 COMPOSE_TO
, PREF_FOLDER
);
3363 compose_entry_indicate(compose
,
3364 msginfo
->folder
->prefs
->default_reply_to
);
3370 compose_entry_append(compose
, msginfo
->from
,
3371 COMPOSE_TO
, PREF_NONE
);
3373 Xstrdup_a(tmp1
, msginfo
->from
, return);
3374 extract_address(tmp1
);
3375 compose_entry_append(compose
,
3376 (!account_find_from_address(tmp1
, FALSE
))
3379 COMPOSE_TO
, PREF_NONE
);
3380 if (compose
->replyto
)
3381 compose_entry_append(compose
,
3383 COMPOSE_CC
, PREF_NONE
);
3385 if (!folder_has_parent_of_type(msginfo
->folder
, F_QUEUE
) &&
3386 !folder_has_parent_of_type(msginfo
->folder
, F_OUTBOX
) &&
3387 !folder_has_parent_of_type(msginfo
->folder
, F_DRAFT
)) {
3388 if (compose
->replyto
) {
3389 compose_entry_append(compose
,
3391 COMPOSE_TO
, PREF_NONE
);
3393 compose_entry_append(compose
,
3394 msginfo
->from
? msginfo
->from
: "",
3395 COMPOSE_TO
, PREF_NONE
);
3398 /* replying to own mail, use original recp */
3399 compose_entry_append(compose
,
3400 msginfo
->to
? msginfo
->to
: "",
3401 COMPOSE_TO
, PREF_NONE
);
3402 compose_entry_append(compose
,
3403 msginfo
->cc
? msginfo
->cc
: "",
3404 COMPOSE_CC
, PREF_NONE
);
3409 if (to_sender
|| (compose
->followup_to
&&
3410 !strncmp(compose
->followup_to
, "poster", 6)))
3411 compose_entry_append
3413 (compose
->replyto
? compose
->replyto
:
3414 msginfo
->from
? msginfo
->from
: ""),
3415 COMPOSE_TO
, PREF_NONE
);
3417 else if (followup_and_reply_to
|| to_all
) {
3418 compose_entry_append
3420 (compose
->replyto
? compose
->replyto
:
3421 msginfo
->from
? msginfo
->from
: ""),
3422 COMPOSE_TO
, PREF_NONE
);
3424 compose_entry_append
3426 compose
->followup_to
? compose
->followup_to
:
3427 compose
->newsgroups
? compose
->newsgroups
: "",
3428 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3430 compose_entry_append
3432 msginfo
->cc
? msginfo
->cc
: "",
3433 COMPOSE_CC
, PREF_NONE
);
3436 compose_entry_append
3438 compose
->followup_to
? compose
->followup_to
:
3439 compose
->newsgroups
? compose
->newsgroups
: "",
3440 COMPOSE_NEWSGROUPS
, PREF_NONE
);
3442 compose_reply_set_subject(compose
, msginfo
);
3444 if (to_ml
&& compose
->ml_post
) return;
3445 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
3447 if (compose
->replyto
) {
3448 Xstrdup_a(replyto
, compose
->replyto
, return);
3449 extract_address(replyto
);
3451 if (msginfo
->from
) {
3452 Xstrdup_a(from
, msginfo
->from
, return);
3453 extract_address(from
);
3456 if (replyto
&& from
)
3457 cc_list
= address_list_append_with_comments(cc_list
, from
);
3458 if (to_all
&& msginfo
->folder
&&
3459 msginfo
->folder
->prefs
->enable_default_reply_to
)
3460 cc_list
= address_list_append_with_comments(cc_list
,
3461 msginfo
->folder
->prefs
->default_reply_to
);
3462 cc_list
= address_list_append_with_comments(cc_list
, msginfo
->to
);
3463 cc_list
= address_list_append_with_comments(cc_list
, compose
->cc
);
3465 ac_email
= g_utf8_strdown(compose
->account
->address
, -1);
3468 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
) {
3469 gchar
*addr
= g_utf8_strdown(cur
->data
, -1);
3470 extract_address(addr
);
3472 if (strcmp(ac_email
, addr
))
3473 compose_entry_append(compose
, (gchar
*)cur
->data
,
3474 COMPOSE_CC
, PREF_NONE
);
3476 debug_print("Cc address same as compose account's, ignoring\n");
3481 slist_free_strings_full(cc_list
);
3487 #define SET_ENTRY(entry, str) \
3490 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3493 #define SET_ADDRESS(type, str) \
3496 compose_entry_append(compose, str, type, PREF_NONE); \
3499 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
3501 cm_return_if_fail(msginfo
!= NULL
);
3503 SET_ENTRY(subject_entry
, msginfo
->subject
);
3504 SET_ENTRY(from_name
, msginfo
->from
);
3505 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
3506 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
3507 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
3508 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
3509 SET_ADDRESS(COMPOSE_NEWSGROUPS
, compose
->newsgroups
);
3510 SET_ADDRESS(COMPOSE_FOLLOWUPTO
, compose
->followup_to
);
3512 compose_update_priority_menu_item(compose
);
3513 compose_update_privacy_system_menu_item(compose
, FALSE
);
3514 compose_show_first_last_header(compose
, TRUE
);
3520 static void compose_insert_sig(Compose
*compose
, gboolean replace
)
3522 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
3523 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
3525 GtkTextIter iter
, iter_end
;
3526 gint cur_pos
, ins_pos
;
3527 gboolean prev_autowrap
;
3528 gboolean found
= FALSE
;
3529 gboolean exists
= FALSE
;
3531 cm_return_if_fail(compose
->account
!= NULL
);
3535 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3536 G_CALLBACK(compose_changed_cb
),
3539 mark
= gtk_text_buffer_get_insert(buffer
);
3540 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3541 cur_pos
= gtk_text_iter_get_offset (&iter
);
3544 gtk_text_buffer_get_end_iter(buffer
, &iter
);
3546 exists
= (compose
->sig_str
!= NULL
);
3549 GtkTextIter first_iter
, start_iter
, end_iter
;
3551 gtk_text_buffer_get_start_iter(buffer
, &first_iter
);
3553 if (!exists
|| compose
->sig_str
[0] == '\0')
3556 found
= gtk_text_iter_forward_to_tag_toggle(&first_iter
,
3557 compose
->signature_tag
);
3560 /* include previous \n\n */
3561 gtk_text_iter_backward_chars(&first_iter
, 1);
3562 start_iter
= first_iter
;
3563 end_iter
= first_iter
;
3565 found
= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3566 compose
->signature_tag
);
3567 found
&= gtk_text_iter_forward_to_tag_toggle(&end_iter
,
3568 compose
->signature_tag
);
3570 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
3576 g_free(compose
->sig_str
);
3577 compose
->sig_str
= account_get_signature_str(compose
->account
);
3579 cur_pos
= gtk_text_iter_get_offset(&iter
);
3581 if (!compose
->sig_str
|| (replace
&& !compose
->account
->auto_sig
)) {
3582 g_free(compose
->sig_str
);
3583 compose
->sig_str
= NULL
;
3585 if (compose
->sig_inserted
== FALSE
)
3586 gtk_text_buffer_insert(buffer
, &iter
, "\n", -1);
3587 compose
->sig_inserted
= TRUE
;
3589 cur_pos
= gtk_text_iter_get_offset(&iter
);
3590 gtk_text_buffer_insert(buffer
, &iter
, compose
->sig_str
, -1);
3592 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cur_pos
);
3593 gtk_text_iter_forward_chars(&iter
, 1);
3594 gtk_text_buffer_get_end_iter(buffer
, &iter_end
);
3595 gtk_text_buffer_apply_tag_by_name(buffer
,"signature",&iter
, &iter_end
);
3597 if (cur_pos
> gtk_text_buffer_get_char_count (buffer
))
3598 cur_pos
= gtk_text_buffer_get_char_count (buffer
);
3601 /* put the cursor where it should be
3602 * either where the quote_fmt says, either where it was */
3603 if (compose
->set_cursor_pos
< 0)
3604 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, ins_pos
);
3606 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
,
3607 compose
->set_cursor_pos
);
3609 compose
->set_cursor_pos
= -1;
3610 gtk_text_buffer_place_cursor(buffer
, &iter
);
3611 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3612 G_CALLBACK(compose_changed_cb
),
3618 static ComposeInsertResult
compose_insert_file(Compose
*compose
, const gchar
*file
)
3621 GtkTextBuffer
*buffer
;
3624 const gchar
*cur_encoding
;
3625 gchar buf
[BUFFSIZE
];
3628 gboolean prev_autowrap
;
3632 GError
*error
= NULL
;
3638 GString
*file_contents
= NULL
;
3639 ComposeInsertResult result
= COMPOSE_INSERT_SUCCESS
;
3641 cm_return_val_if_fail(file
!= NULL
, COMPOSE_INSERT_NO_FILE
);
3643 /* get the size of the file we are about to insert */
3645 f
= g_file_new_for_path(file
);
3646 fi
= g_file_query_info(f
, "standard::size",
3647 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
3649 if (error
!= NULL
) {
3650 g_warning(error
->message
);
3652 g_error_free(error
);
3656 ret
= g_stat(file
, &file_stat
);
3659 gchar
*shortfile
= g_path_get_basename(file
);
3660 alertpanel_error(_("Could not get size of file '%s'."), shortfile
);
3662 return COMPOSE_INSERT_NO_FILE
;
3663 } else if (prefs_common
.warn_large_insert
== TRUE
) {
3665 size
= g_file_info_get_size(fi
);
3669 size
= file_stat
.st_size
;
3672 /* ask user for confirmation if the file is large */
3673 if (prefs_common
.warn_large_insert_size
< 0 ||
3674 size
> ((goffset
) prefs_common
.warn_large_insert_size
* 1024)) {
3678 msg
= g_strdup_printf(_("You are about to insert a file of %s "
3679 "in the message body. Are you sure you want to do that?"),
3680 to_human_readable(size
));
3681 aval
= alertpanel_full(_("Are you sure?"), msg
, GTK_STOCK_CANCEL
,
3682 _("_Insert"), NULL
, ALERTFOCUS_SECOND
, TRUE
,
3683 NULL
, ALERT_QUESTION
);
3686 /* do we ask for confirmation next time? */
3687 if (aval
& G_ALERTDISABLE
) {
3688 /* no confirmation next time, disable feature in preferences */
3689 aval
&= ~G_ALERTDISABLE
;
3690 prefs_common
.warn_large_insert
= FALSE
;
3693 /* abort file insertion if user canceled action */
3694 if (aval
!= G_ALERTALTERNATE
) {
3695 return COMPOSE_INSERT_NO_FILE
;
3701 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3702 FILE_OP_ERROR(file
, "claws_fopen");
3703 return COMPOSE_INSERT_READ_ERROR
;
3706 prev_autowrap
= compose
->autowrap
;
3707 compose
->autowrap
= FALSE
;
3709 text
= GTK_TEXT_VIEW(compose
->text
);
3710 buffer
= gtk_text_view_get_buffer(text
);
3711 mark
= gtk_text_buffer_get_insert(buffer
);
3712 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
3714 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
3715 G_CALLBACK(text_inserted
),
3718 cur_encoding
= conv_get_locale_charset_str_no_utf8();
3720 file_contents
= g_string_new("");
3721 while (claws_fgets(buf
, sizeof(buf
), fp
) != NULL
) {
3724 if (g_utf8_validate(buf
, -1, NULL
) == TRUE
)
3725 str
= g_strdup(buf
);
3727 codeconv_set_strict(TRUE
);
3728 str
= conv_codeset_strdup
3729 (buf
, cur_encoding
, CS_INTERNAL
);
3730 codeconv_set_strict(FALSE
);
3733 result
= COMPOSE_INSERT_INVALID_CHARACTER
;
3739 /* strip <CR> if DOS/Windows file,
3740 replace <CR> with <LF> if Macintosh file. */
3743 if (len
> 0 && str
[len
- 1] != '\n') {
3745 if (str
[len
] == '\r') str
[len
] = '\n';
3748 file_contents
= g_string_append(file_contents
, str
);
3752 if (result
== COMPOSE_INSERT_SUCCESS
) {
3753 gtk_text_buffer_insert(buffer
, &iter
, file_contents
->str
, -1);
3755 compose_changed_cb(NULL
, compose
);
3756 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
3757 G_CALLBACK(text_inserted
),
3759 compose
->autowrap
= prev_autowrap
;
3760 if (compose
->autowrap
)
3761 compose_wrap_all(compose
);
3764 g_string_free(file_contents
, TRUE
);
3770 static gboolean
compose_attach_append(Compose
*compose
, const gchar
*file
,
3771 const gchar
*filename
,
3772 const gchar
*content_type
,
3773 const gchar
*charset
)
3781 GtkListStore
*store
;
3783 gboolean has_binary
= FALSE
;
3785 if (!is_file_exist(file
)) {
3786 gchar
*file_from_uri
= g_filename_from_uri(file
, NULL
, NULL
);
3787 gboolean result
= FALSE
;
3788 if (file_from_uri
&& is_file_exist(file_from_uri
)) {
3789 result
= compose_attach_append(
3790 compose
, file_from_uri
,
3791 filename
, content_type
,
3794 g_free(file_from_uri
);
3797 alertpanel_error("File %s doesn't exist or permission denied\n", filename
);
3800 if ((size
= get_file_size(file
)) < 0) {
3801 alertpanel_error("Can't get file size of %s\n", filename
);
3805 /* In batch mode, we allow 0-length files to be attached no questions asked */
3806 if (size
== 0 && !compose
->batch
) {
3807 gchar
* msg
= g_strdup_printf(_("File %s is empty."), filename
);
3808 AlertValue aval
= alertpanel_full(_("Empty file"), msg
,
3809 GTK_STOCK_CANCEL
, _("_Attach anyway"), NULL
,
3810 ALERTFOCUS_SECOND
, FALSE
, NULL
, ALERT_WARNING
);
3813 if (aval
!= G_ALERTALTERNATE
) {
3817 if ((fp
= claws_fopen(file
, "rb")) == NULL
) {
3818 alertpanel_error(_("Can't read %s."), filename
);
3823 ainfo
= g_new0(AttachInfo
, 1);
3824 auto_ainfo
= g_auto_pointer_new_with_free
3825 (ainfo
, (GFreeFunc
) compose_attach_info_free
);
3826 ainfo
->file
= g_strdup(file
);
3829 ainfo
->content_type
= g_strdup(content_type
);
3830 if (!g_ascii_strcasecmp(content_type
, "message/rfc822")) {
3832 MsgFlags flags
= {0, 0};
3834 if (procmime_get_encoding_for_text_file(file
, &has_binary
) == ENC_7BIT
)
3835 ainfo
->encoding
= ENC_7BIT
;
3837 ainfo
->encoding
= ENC_8BIT
;
3839 msginfo
= procheader_parse_file(file
, flags
, FALSE
, FALSE
);
3840 if (msginfo
&& msginfo
->subject
)
3841 name
= g_strdup(msginfo
->subject
);
3843 name
= g_path_get_basename(filename
? filename
: file
);
3845 ainfo
->name
= g_strdup_printf(_("Message: %s"), name
);
3847 procmsg_msginfo_free(&msginfo
);
3849 if (!g_ascii_strncasecmp(content_type
, "text/", 5)) {
3850 ainfo
->charset
= g_strdup(charset
);
3851 ainfo
->encoding
= procmime_get_encoding_for_text_file(file
, &has_binary
);
3853 ainfo
->encoding
= ENC_BASE64
;
3855 name
= g_path_get_basename(filename
? filename
: file
);
3856 ainfo
->name
= g_strdup(name
);
3860 ainfo
->content_type
= procmime_get_mime_type(file
);
3861 if (!ainfo
->content_type
) {
3862 ainfo
->content_type
=
3863 g_strdup("application/octet-stream");
3864 ainfo
->encoding
= ENC_BASE64
;
3865 } else if (!g_ascii_strncasecmp(ainfo
->content_type
, "text/", 5))
3867 procmime_get_encoding_for_text_file(file
, &has_binary
);
3869 ainfo
->encoding
= ENC_BASE64
;
3870 name
= g_path_get_basename(filename
? filename
: file
);
3871 ainfo
->name
= g_strdup(name
);
3875 if (ainfo
->name
!= NULL
3876 && !strcmp(ainfo
->name
, ".")) {
3877 g_free(ainfo
->name
);
3881 if (!strcmp(ainfo
->content_type
, "unknown") || has_binary
) {
3882 g_free(ainfo
->content_type
);
3883 ainfo
->content_type
= g_strdup("application/octet-stream");
3884 g_free(ainfo
->charset
);
3885 ainfo
->charset
= NULL
;
3888 ainfo
->size
= (goffset
)size
;
3889 size_text
= to_human_readable((goffset
)size
);
3891 store
= GTK_LIST_STORE(gtk_tree_view_get_model
3892 (GTK_TREE_VIEW(compose
->attach_clist
)));
3894 gtk_list_store_append(store
, &iter
);
3895 gtk_list_store_set(store
, &iter
,
3896 COL_MIMETYPE
, ainfo
->content_type
,
3897 COL_SIZE
, size_text
,
3898 COL_NAME
, ainfo
->name
,
3899 COL_CHARSET
, ainfo
->charset
,
3901 COL_AUTODATA
, auto_ainfo
,
3904 g_auto_pointer_free(auto_ainfo
);
3905 compose_attach_update_label(compose
);
3909 void compose_use_signing(Compose
*compose
, gboolean use_signing
)
3911 compose
->use_signing
= use_signing
;
3912 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", use_signing
);
3915 void compose_use_encryption(Compose
*compose
, gboolean use_encryption
)
3917 compose
->use_encryption
= use_encryption
;
3918 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", use_encryption
);
3921 #define NEXT_PART_NOT_CHILD(info) \
3923 node = info->node; \
3924 while (node->children) \
3925 node = g_node_last_child(node); \
3926 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3929 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
3933 MimeInfo
*firsttext
= NULL
;
3934 MimeInfo
*encrypted
= NULL
;
3937 const gchar
*partname
= NULL
;
3939 mimeinfo
= procmime_scan_message(msginfo
);
3940 if (!mimeinfo
) return;
3942 if (mimeinfo
->node
->children
== NULL
) {
3943 procmime_mimeinfo_free_all(&mimeinfo
);
3947 /* find first content part */
3948 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3949 while (child
&& child
->node
->children
&& (child
->type
== MIMETYPE_MULTIPART
))
3950 child
= (MimeInfo
*)child
->node
->children
->data
;
3953 if (child
->type
== MIMETYPE_TEXT
) {
3955 debug_print("First text part found\n");
3956 } else if (compose
->mode
== COMPOSE_REEDIT
&&
3957 child
->type
== MIMETYPE_APPLICATION
&&
3958 !g_ascii_strcasecmp(child
->subtype
, "pgp-encrypted")) {
3959 encrypted
= (MimeInfo
*)child
->node
->parent
->data
;
3962 child
= (MimeInfo
*) mimeinfo
->node
->children
->data
;
3963 while (child
!= NULL
) {
3966 if (child
== encrypted
) {
3967 /* skip this part of tree */
3968 NEXT_PART_NOT_CHILD(child
);
3972 if (child
->type
== MIMETYPE_MULTIPART
) {
3973 /* get the actual content */
3974 child
= procmime_mimeinfo_next(child
);
3978 if (child
== firsttext
) {
3979 child
= procmime_mimeinfo_next(child
);
3983 outfile
= procmime_get_tmp_file_name(child
);
3984 if ((err
= procmime_get_part(outfile
, child
)) < 0)
3985 g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err
));
3987 gchar
*content_type
;
3989 content_type
= procmime_get_content_type_str(child
->type
, child
->subtype
);
3991 /* if we meet a pgp signature, we don't attach it, but
3992 * we force signing. */
3993 if ((strcmp(content_type
, "application/pgp-signature") &&
3994 strcmp(content_type
, "application/pkcs7-signature") &&
3995 strcmp(content_type
, "application/x-pkcs7-signature"))
3996 || compose
->mode
== COMPOSE_REDIRECT
) {
3997 partname
= procmime_mimeinfo_get_parameter(child
, "filename");
3998 if (partname
== NULL
)
3999 partname
= procmime_mimeinfo_get_parameter(child
, "name");
4000 if (partname
== NULL
)
4002 compose_attach_append(compose
, outfile
,
4003 partname
, content_type
,
4004 procmime_mimeinfo_get_parameter(child
, "charset"));
4006 compose_force_signing(compose
, compose
->account
, NULL
);
4008 g_free(content_type
);
4011 NEXT_PART_NOT_CHILD(child
);
4013 procmime_mimeinfo_free_all(&mimeinfo
);
4016 #undef NEXT_PART_NOT_CHILD
4021 WAIT_FOR_INDENT_CHAR
,
4022 WAIT_FOR_INDENT_CHAR_OR_SPACE
,
4025 /* return indent length, we allow:
4026 indent characters followed by indent characters or spaces/tabs,
4027 alphabets and numbers immediately followed by indent characters,
4028 and the repeating sequences of the above
4029 If quote ends with multiple spaces, only the first one is included. */
4030 static gchar
*compose_get_quote_str(GtkTextBuffer
*buffer
,
4031 const GtkTextIter
*start
, gint
*len
)
4033 GtkTextIter iter
= *start
;
4037 IndentState state
= WAIT_FOR_INDENT_CHAR
;
4040 gint alnum_count
= 0;
4041 gint space_count
= 0;
4044 if (prefs_common
.quote_chars
== NULL
) {
4048 while (!gtk_text_iter_ends_line(&iter
)) {
4049 wc
= gtk_text_iter_get_char(&iter
);
4050 if (g_unichar_iswide(wc
))
4052 clen
= g_unichar_to_utf8(wc
, ch
);
4056 is_indent
= strchr(prefs_common
.quote_chars
, ch
[0]) ? TRUE
: FALSE
;
4057 is_space
= g_unichar_isspace(wc
);
4059 if (state
== WAIT_FOR_INDENT_CHAR
) {
4060 if (!is_indent
&& !g_unichar_isalnum(wc
))
4063 quote_len
+= alnum_count
+ space_count
+ 1;
4064 alnum_count
= space_count
= 0;
4065 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
4068 } else if (state
== WAIT_FOR_INDENT_CHAR_OR_SPACE
) {
4069 if (!is_indent
&& !is_space
&& !g_unichar_isalnum(wc
))
4073 else if (is_indent
) {
4074 quote_len
+= alnum_count
+ space_count
+ 1;
4075 alnum_count
= space_count
= 0;
4078 state
= WAIT_FOR_INDENT_CHAR
;
4082 gtk_text_iter_forward_char(&iter
);
4085 if (quote_len
> 0 && space_count
> 0)
4091 if (quote_len
> 0) {
4093 gtk_text_iter_forward_chars(&iter
, quote_len
);
4094 return gtk_text_buffer_get_text(buffer
, start
, &iter
, FALSE
);
4100 /* return >0 if the line is itemized */
4101 static int compose_itemized_length(GtkTextBuffer
*buffer
,
4102 const GtkTextIter
*start
)
4104 GtkTextIter iter
= *start
;
4109 if (gtk_text_iter_ends_line(&iter
))
4114 wc
= gtk_text_iter_get_char(&iter
);
4115 if (!g_unichar_isspace(wc
))
4117 gtk_text_iter_forward_char(&iter
);
4118 if (gtk_text_iter_ends_line(&iter
))
4122 clen
= g_unichar_to_utf8(wc
, ch
);
4123 if (!((clen
== 1 && strchr("*-+", ch
[0])) ||
4125 wc
== 0x2022 || /* BULLET */
4126 wc
== 0x2023 || /* TRIANGULAR BULLET */
4127 wc
== 0x2043 || /* HYPHEN BULLET */
4128 wc
== 0x204c || /* BLACK LEFTWARDS BULLET */
4129 wc
== 0x204d || /* BLACK RIGHTWARDS BULLET */
4130 wc
== 0x2219 || /* BULLET OPERATOR */
4131 wc
== 0x25d8 || /* INVERSE BULLET */
4132 wc
== 0x25e6 || /* WHITE BULLET */
4133 wc
== 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4134 wc
== 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4135 wc
== 0x2767 || /* ROTATED FLORAL HEART BULLET */
4136 wc
== 0x29be || /* CIRCLED WHITE BULLET */
4137 wc
== 0x29bf /* CIRCLED BULLET */
4141 gtk_text_iter_forward_char(&iter
);
4142 if (gtk_text_iter_ends_line(&iter
))
4144 wc
= gtk_text_iter_get_char(&iter
);
4145 if (g_unichar_isspace(wc
)) {
4151 /* return the string at the start of the itemization */
4152 static gchar
* compose_get_itemized_chars(GtkTextBuffer
*buffer
,
4153 const GtkTextIter
*start
)
4155 GtkTextIter iter
= *start
;
4158 GString
*item_chars
= g_string_new("");
4161 if (gtk_text_iter_ends_line(&iter
))
4166 wc
= gtk_text_iter_get_char(&iter
);
4167 if (!g_unichar_isspace(wc
))
4169 gtk_text_iter_forward_char(&iter
);
4170 if (gtk_text_iter_ends_line(&iter
))
4172 g_string_append_unichar(item_chars
, wc
);
4175 str
= item_chars
->str
;
4176 g_string_free(item_chars
, FALSE
);
4180 /* return the number of spaces at a line's start */
4181 static int compose_left_offset_length(GtkTextBuffer
*buffer
,
4182 const GtkTextIter
*start
)
4184 GtkTextIter iter
= *start
;
4187 if (gtk_text_iter_ends_line(&iter
))
4191 wc
= gtk_text_iter_get_char(&iter
);
4192 if (!g_unichar_isspace(wc
))
4195 gtk_text_iter_forward_char(&iter
);
4196 if (gtk_text_iter_ends_line(&iter
))
4200 gtk_text_iter_forward_char(&iter
);
4201 if (gtk_text_iter_ends_line(&iter
))
4206 static gboolean
compose_get_line_break_pos(GtkTextBuffer
*buffer
,
4207 const GtkTextIter
*start
,
4208 GtkTextIter
*break_pos
,
4212 GtkTextIter iter
= *start
, line_end
= *start
;
4213 PangoLogAttr
*attrs
;
4220 gboolean can_break
= FALSE
;
4221 gboolean do_break
= FALSE
;
4222 gboolean was_white
= FALSE
;
4223 gboolean prev_dont_break
= FALSE
;
4225 gtk_text_iter_forward_to_line_end(&line_end
);
4226 str
= gtk_text_buffer_get_text(buffer
, &iter
, &line_end
, FALSE
);
4227 len
= g_utf8_strlen(str
, -1);
4231 g_warning("compose_get_line_break_pos: len = 0!");
4235 /* g_print("breaking line: %d: %s (len = %d)\n",
4236 gtk_text_iter_get_line(&iter), str, len); */
4238 attrs
= g_new(PangoLogAttr
, len
+ 1);
4240 pango_default_break(str
, -1, NULL
, attrs
, len
+ 1);
4244 /* skip quote and leading spaces */
4245 for (i
= 0; *p
!= '\0' && i
< len
; i
++) {
4248 wc
= g_utf8_get_char(p
);
4249 if (i
>= quote_len
&& !g_unichar_isspace(wc
))
4251 if (g_unichar_iswide(wc
))
4253 else if (*p
== '\t')
4257 p
= g_utf8_next_char(p
);
4260 for (; *p
!= '\0' && i
< len
; i
++) {
4261 PangoLogAttr
*attr
= attrs
+ i
;
4262 gunichar wc
= g_utf8_get_char(p
);
4265 /* attr->is_line_break will be false for some characters that
4266 * we want to break a line before, like '/' or ':', so we
4267 * also allow breaking on any non-wide character. The
4268 * mentioned pango attribute is still useful to decide on
4269 * line breaks when wide characters are involved. */
4270 if ((!g_unichar_iswide(wc
) || attr
->is_line_break
)
4271 && can_break
&& was_white
&& !prev_dont_break
)
4274 was_white
= attr
->is_white
;
4276 /* don't wrap URI */
4277 if ((uri_len
= get_uri_len(p
)) > 0) {
4279 if (pos
> 0 && col
> max_col
) {
4289 if (g_unichar_iswide(wc
)) {
4291 if (prev_dont_break
&& can_break
&& attr
->is_line_break
)
4293 } else if (*p
== '\t')
4297 if (pos
> 0 && col
> max_col
) {
4302 if (*p
== '-' || *p
== '/')
4303 prev_dont_break
= TRUE
;
4305 prev_dont_break
= FALSE
;
4307 p
= g_utf8_next_char(p
);
4311 /* debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4316 *break_pos
= *start
;
4317 gtk_text_iter_set_line_offset(break_pos
, pos
);
4322 static gboolean
compose_join_next_line(Compose
*compose
,
4323 GtkTextBuffer
*buffer
,
4325 const gchar
*quote_str
)
4327 GtkTextIter iter_
= *iter
, cur
, prev
, next
, end
;
4328 PangoLogAttr attrs
[3];
4330 gchar
*next_quote_str
;
4333 gboolean keep_cursor
= FALSE
;
4335 if (!gtk_text_iter_forward_line(&iter_
) ||
4336 gtk_text_iter_ends_line(&iter_
)) {
4339 next_quote_str
= compose_get_quote_str(buffer
, &iter_
, "e_len
);
4341 if ((quote_str
|| next_quote_str
) &&
4342 g_strcmp0(quote_str
, next_quote_str
) != 0) {
4343 g_free(next_quote_str
);
4346 g_free(next_quote_str
);
4349 if (quote_len
> 0) {
4350 gtk_text_iter_forward_chars(&end
, quote_len
);
4351 if (gtk_text_iter_ends_line(&end
)) {
4356 /* don't join itemized lines */
4357 if (compose_itemized_length(buffer
, &end
) > 0) {
4361 /* don't join signature separator */
4362 if (compose_is_sig_separator(compose
, buffer
, &iter_
)) {
4365 /* delete quote str */
4367 gtk_text_buffer_delete(buffer
, &iter_
, &end
);
4369 /* don't join line breaks put by the user */
4371 gtk_text_iter_backward_char(&cur
);
4372 if (gtk_text_iter_has_tag(&cur
, compose
->no_join_tag
)) {
4373 gtk_text_iter_forward_char(&cur
);
4377 gtk_text_iter_forward_char(&cur
);
4378 /* delete linebreak and extra spaces */
4379 while (gtk_text_iter_backward_char(&cur
)) {
4380 wc1
= gtk_text_iter_get_char(&cur
);
4381 if (!g_unichar_isspace(wc1
))
4386 while (!gtk_text_iter_ends_line(&cur
)) {
4387 wc1
= gtk_text_iter_get_char(&cur
);
4388 if (!g_unichar_isspace(wc1
))
4390 gtk_text_iter_forward_char(&cur
);
4393 if (!gtk_text_iter_equal(&prev
, &next
)) {
4396 mark
= gtk_text_buffer_get_insert(buffer
);
4397 gtk_text_buffer_get_iter_at_mark(buffer
, &cur
, mark
);
4398 if (gtk_text_iter_equal(&prev
, &cur
))
4400 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4404 /* insert space if required */
4405 gtk_text_iter_backward_char(&prev
);
4406 wc1
= gtk_text_iter_get_char(&prev
);
4407 wc2
= gtk_text_iter_get_char(&next
);
4408 gtk_text_iter_forward_char(&next
);
4409 str
= gtk_text_buffer_get_text(buffer
, &prev
, &next
, FALSE
);
4410 pango_default_break(str
, -1, NULL
, attrs
, 3);
4411 if (!attrs
[1].is_line_break
||
4412 (!g_unichar_iswide(wc1
) || !g_unichar_iswide(wc2
))) {
4413 gtk_text_buffer_insert(buffer
, &iter_
, " ", 1);
4415 gtk_text_iter_backward_char(&iter_
);
4416 gtk_text_buffer_place_cursor(buffer
, &iter_
);
4425 #define ADD_TXT_POS(bp_, ep_, pti_) \
4426 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4427 last = last->next; \
4428 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4429 last->next = NULL; \
4431 g_warning("alloc error scanning URIs"); \
4434 static gboolean
compose_beautify_paragraph(Compose
*compose
, GtkTextIter
*par_iter
, gboolean force
)
4436 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4437 GtkTextBuffer
*buffer
;
4438 GtkTextIter iter
, break_pos
, end_of_line
;
4439 gchar
*quote_str
= NULL
;
4441 gboolean wrap_quote
= force
|| prefs_common
.linewrap_quote
;
4442 gboolean prev_autowrap
= compose
->autowrap
;
4443 gint startq_offset
= -1, noq_offset
= -1;
4444 gint uri_start
= -1, uri_stop
= -1;
4445 gint nouri_start
= -1, nouri_stop
= -1;
4446 gint num_blocks
= 0;
4447 gint quotelevel
= -1;
4448 gboolean modified
= force
;
4449 gboolean removed
= FALSE
;
4450 gboolean modified_before_remove
= FALSE
;
4452 gboolean start
= TRUE
;
4453 gint itemized_len
= 0, rem_item_len
= 0;
4454 gchar
*itemized_chars
= NULL
;
4455 gboolean item_continuation
= FALSE
;
4460 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4464 compose
->autowrap
= FALSE
;
4466 buffer
= gtk_text_view_get_buffer(text
);
4467 undo_wrapping(compose
->undostruct
, TRUE
);
4472 mark
= gtk_text_buffer_get_insert(buffer
);
4473 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
4477 if (compose
->draft_timeout_tag
== COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
) {
4478 if (gtk_text_iter_ends_line(&iter
)) {
4479 while (gtk_text_iter_ends_line(&iter
) &&
4480 gtk_text_iter_forward_line(&iter
))
4483 while (gtk_text_iter_backward_line(&iter
)) {
4484 if (gtk_text_iter_ends_line(&iter
)) {
4485 gtk_text_iter_forward_line(&iter
);
4491 /* move to line start */
4492 gtk_text_iter_set_line_offset(&iter
, 0);
4495 itemized_len
= compose_itemized_length(buffer
, &iter
);
4497 if (!itemized_len
) {
4498 itemized_len
= compose_left_offset_length(buffer
, &iter
);
4499 item_continuation
= TRUE
;
4503 itemized_chars
= compose_get_itemized_chars(buffer
, &iter
);
4505 /* go until paragraph end (empty line) */
4506 while (start
|| !gtk_text_iter_ends_line(&iter
)) {
4507 gchar
*scanpos
= NULL
;
4508 /* parse table - in order of priority */
4510 const gchar
*needle
; /* token */
4512 /* token search function */
4513 gchar
*(*search
) (const gchar
*haystack
,
4514 const gchar
*needle
);
4515 /* part parsing function */
4516 gboolean (*parse
) (const gchar
*start
,
4517 const gchar
*scanpos
,
4521 /* part to URI function */
4522 gchar
*(*build_uri
) (const gchar
*bp
,
4526 static struct table parser
[] = {
4527 {"http://", strcasestr
, get_uri_part
, make_uri_string
},
4528 {"https://", strcasestr
, get_uri_part
, make_uri_string
},
4529 {"ftp://", strcasestr
, get_uri_part
, make_uri_string
},
4530 {"sftp://", strcasestr
, get_uri_part
, make_uri_string
},
4531 {"gopher://",strcasestr
, get_uri_part
, make_uri_string
},
4532 {"www.", strcasestr
, get_uri_part
, make_http_string
},
4533 {"mailto:", strcasestr
, get_uri_part
, make_uri_string
},
4534 {"@", strcasestr
, get_email_part
, make_email_string
}
4536 const gint PARSE_ELEMS
= sizeof parser
/ sizeof parser
[0];
4537 gint last_index
= PARSE_ELEMS
;
4539 gchar
*o_walk
= NULL
, *walk
= NULL
, *bp
= NULL
, *ep
= NULL
;
4543 if (!prev_autowrap
&& num_blocks
== 0) {
4545 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
4546 G_CALLBACK(text_inserted
),
4549 if (gtk_text_iter_has_tag(&iter
, compose
->no_wrap_tag
) && !force
)
4552 uri_start
= uri_stop
= -1;
4554 quote_str
= compose_get_quote_str(buffer
, &iter
, "e_len
);
4557 /* debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4558 if (startq_offset
== -1)
4559 startq_offset
= gtk_text_iter_get_offset(&iter
);
4560 quotelevel
= get_quote_level(quote_str
, prefs_common
.quote_chars
);
4561 if (quotelevel
> 2) {
4562 /* recycle colors */
4563 if (prefs_common
.recycle_quote_colors
)
4572 if (startq_offset
== -1)
4573 noq_offset
= gtk_text_iter_get_offset(&iter
);
4577 if (prev_autowrap
== FALSE
&& !force
&& !wrap_quote
) {
4580 if (gtk_text_iter_ends_line(&iter
)) {
4582 } else if (compose_get_line_break_pos(buffer
, &iter
, &break_pos
,
4583 prefs_common
.linewrap_len
,
4585 GtkTextIter prev
, next
, cur
;
4586 if (prev_autowrap
!= FALSE
|| force
) {
4587 compose
->automatic_break
= TRUE
;
4589 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4590 compose
->automatic_break
= FALSE
;
4591 if (itemized_len
&& compose
->autoindent
) {
4592 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4593 if (!item_continuation
)
4594 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4596 } else if (quote_str
&& wrap_quote
) {
4597 compose
->automatic_break
= TRUE
;
4599 gtk_text_buffer_insert(buffer
, &break_pos
, "\n", 1);
4600 compose
->automatic_break
= FALSE
;
4601 if (itemized_len
&& compose
->autoindent
) {
4602 gtk_text_buffer_insert(buffer
, &break_pos
, itemized_chars
, -1);
4603 if (!item_continuation
)
4604 gtk_text_buffer_insert(buffer
, &break_pos
, " ", 2);
4608 /* remove trailing spaces */
4610 rem_item_len
= itemized_len
;
4611 while (compose
->autoindent
&& rem_item_len
-- > 0)
4612 gtk_text_iter_backward_char(&cur
);
4613 gtk_text_iter_backward_char(&cur
);
4616 while (!gtk_text_iter_starts_line(&cur
)) {
4619 gtk_text_iter_backward_char(&cur
);
4620 wc
= gtk_text_iter_get_char(&cur
);
4621 if (!g_unichar_isspace(wc
))
4625 if (!gtk_text_iter_equal(&prev
, &next
)) {
4626 gtk_text_buffer_delete(buffer
, &prev
, &next
);
4628 gtk_text_iter_forward_char(&break_pos
);
4632 gtk_text_buffer_insert(buffer
, &break_pos
,
4636 modified
|= compose_join_next_line(compose
, buffer
, &iter
, quote_str
);
4638 /* move iter to current line start */
4639 gtk_text_iter_set_line_offset(&iter
, 0);
4646 /* move iter to next line start */
4652 if (!prev_autowrap
&& num_blocks
> 0) {
4654 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
4655 G_CALLBACK(text_inserted
),
4659 while (!gtk_text_iter_ends_line(&end_of_line
)) {
4660 gtk_text_iter_forward_char(&end_of_line
);
4662 o_walk
= walk
= gtk_text_buffer_get_text(buffer
, &iter
, &end_of_line
, FALSE
);
4664 nouri_start
= gtk_text_iter_get_offset(&iter
);
4665 nouri_stop
= gtk_text_iter_get_offset(&end_of_line
);
4667 walk_pos
= gtk_text_iter_get_offset(&iter
);
4668 /* FIXME: this looks phony. scanning for anything in the parse table */
4669 for (n
= 0; n
< PARSE_ELEMS
; n
++) {
4672 tmp
= parser
[n
].search(walk
, parser
[n
].needle
);
4674 if (scanpos
== NULL
|| tmp
< scanpos
) {
4683 /* check if URI can be parsed */
4684 if (parser
[last_index
].parse(walk
, scanpos
, (const gchar
**)&bp
,
4685 (const gchar
**)&ep
, FALSE
)
4686 && (size_t) (ep
- bp
- 1) > strlen(parser
[last_index
].needle
)) {
4690 strlen(parser
[last_index
].needle
);
4693 uri_start
= walk_pos
+ (bp
- o_walk
);
4694 uri_stop
= walk_pos
+ (ep
- o_walk
);
4698 gtk_text_iter_forward_line(&iter
);
4701 if (startq_offset
!= -1) {
4702 GtkTextIter startquote
, endquote
;
4703 gtk_text_buffer_get_iter_at_offset(
4704 buffer
, &startquote
, startq_offset
);
4707 switch (quotelevel
) {
4709 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote0_tag
) ||
4710 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) {
4711 gtk_text_buffer_apply_tag_by_name(
4712 buffer
, "quote0", &startquote
, &endquote
);
4713 gtk_text_buffer_remove_tag_by_name(
4714 buffer
, "quote1", &startquote
, &endquote
);
4715 gtk_text_buffer_remove_tag_by_name(
4716 buffer
, "quote2", &startquote
, &endquote
);
4721 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote1_tag
) ||
4722 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) {
4723 gtk_text_buffer_apply_tag_by_name(
4724 buffer
, "quote1", &startquote
, &endquote
);
4725 gtk_text_buffer_remove_tag_by_name(
4726 buffer
, "quote0", &startquote
, &endquote
);
4727 gtk_text_buffer_remove_tag_by_name(
4728 buffer
, "quote2", &startquote
, &endquote
);
4733 if (!gtk_text_iter_has_tag(&startquote
, compose
->quote2_tag
) ||
4734 !gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
)) {
4735 gtk_text_buffer_apply_tag_by_name(
4736 buffer
, "quote2", &startquote
, &endquote
);
4737 gtk_text_buffer_remove_tag_by_name(
4738 buffer
, "quote0", &startquote
, &endquote
);
4739 gtk_text_buffer_remove_tag_by_name(
4740 buffer
, "quote1", &startquote
, &endquote
);
4746 } else if (noq_offset
!= -1) {
4747 GtkTextIter startnoquote
, endnoquote
;
4748 gtk_text_buffer_get_iter_at_offset(
4749 buffer
, &startnoquote
, noq_offset
);
4752 if ((gtk_text_iter_has_tag(&startnoquote
, compose
->quote0_tag
)
4753 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote0_tag
)) ||
4754 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote1_tag
)
4755 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote1_tag
)) ||
4756 (gtk_text_iter_has_tag(&startnoquote
, compose
->quote2_tag
)
4757 && gtk_text_iter_has_tag(&end_of_line
, compose
->quote2_tag
))) {
4758 gtk_text_buffer_remove_tag_by_name(
4759 buffer
, "quote0", &startnoquote
, &endnoquote
);
4760 gtk_text_buffer_remove_tag_by_name(
4761 buffer
, "quote1", &startnoquote
, &endnoquote
);
4762 gtk_text_buffer_remove_tag_by_name(
4763 buffer
, "quote2", &startnoquote
, &endnoquote
);
4769 if (uri_start
!= nouri_start
&& uri_stop
!= nouri_stop
) {
4770 GtkTextIter nouri_start_iter
, nouri_end_iter
;
4771 gtk_text_buffer_get_iter_at_offset(
4772 buffer
, &nouri_start_iter
, nouri_start
);
4773 gtk_text_buffer_get_iter_at_offset(
4774 buffer
, &nouri_end_iter
, nouri_stop
);
4775 if (gtk_text_iter_has_tag(&nouri_start_iter
, compose
->uri_tag
) &&
4776 gtk_text_iter_has_tag(&nouri_end_iter
, compose
->uri_tag
)) {
4777 gtk_text_buffer_remove_tag_by_name(
4778 buffer
, "link", &nouri_start_iter
, &nouri_end_iter
);
4779 modified_before_remove
= modified
;
4784 if (uri_start
>= 0 && uri_stop
> 0) {
4785 GtkTextIter uri_start_iter
, uri_end_iter
, back
;
4786 gtk_text_buffer_get_iter_at_offset(
4787 buffer
, &uri_start_iter
, uri_start
);
4788 gtk_text_buffer_get_iter_at_offset(
4789 buffer
, &uri_end_iter
, uri_stop
);
4790 back
= uri_end_iter
;
4791 gtk_text_iter_backward_char(&back
);
4792 if (!gtk_text_iter_has_tag(&uri_start_iter
, compose
->uri_tag
) ||
4793 !gtk_text_iter_has_tag(&back
, compose
->uri_tag
)) {
4794 gtk_text_buffer_apply_tag_by_name(
4795 buffer
, "link", &uri_start_iter
, &uri_end_iter
);
4797 if (removed
&& !modified_before_remove
) {
4803 /* debug_print("not modified, out after %d lines\n", lines); */
4807 /* debug_print("modified, out after %d lines\n", lines); */
4809 g_free(itemized_chars
);
4812 undo_wrapping(compose
->undostruct
, FALSE
);
4813 compose
->autowrap
= prev_autowrap
;
4818 void compose_action_cb(void *data
)
4820 Compose
*compose
= (Compose
*)data
;
4821 compose_wrap_all(compose
);
4824 static void compose_wrap_all(Compose
*compose
)
4826 compose_wrap_all_full(compose
, FALSE
);
4829 static void compose_wrap_all_full(Compose
*compose
, gboolean force
)
4831 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
4832 GtkTextBuffer
*buffer
;
4834 gboolean modified
= TRUE
;
4836 buffer
= gtk_text_view_get_buffer(text
);
4838 gtk_text_buffer_get_start_iter(buffer
, &iter
);
4840 undo_wrapping(compose
->undostruct
, TRUE
);
4842 while (!gtk_text_iter_is_end(&iter
) && modified
)
4843 modified
= compose_beautify_paragraph(compose
, &iter
, force
);
4845 undo_wrapping(compose
->undostruct
, FALSE
);
4849 static void compose_set_title(Compose
*compose
)
4855 edited
= compose
->modified
? _(" [Edited]") : "";
4857 subject
= gtk_editable_get_chars(
4858 GTK_EDITABLE(compose
->subject_entry
), 0, -1);
4860 #ifndef GENERIC_UMPC
4861 if (subject
&& strlen(subject
))
4862 str
= g_strdup_printf(_("%s - Compose message%s"),
4865 str
= g_strdup_printf(_("[no subject] - Compose message%s"), edited
);
4867 str
= g_strdup(_("Compose message"));
4870 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
4876 * compose_current_mail_account:
4878 * Find a current mail account (the currently selected account, or the
4879 * default account, if a news account is currently selected). If a
4880 * mail account cannot be found, display an error message.
4882 * Return value: Mail account, or NULL if not found.
4884 static PrefsAccount
*
4885 compose_current_mail_account(void)
4889 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
4892 ac
= account_get_default();
4893 if (!ac
|| ac
->protocol
== A_NNTP
) {
4894 alertpanel_error(_("Account for sending mail is not specified.\n"
4895 "Please select a mail account before sending."));
4902 #define QUOTE_IF_REQUIRED(out, str) \
4904 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4908 len = strlen(str) + 3; \
4909 if ((__tmp = alloca(len)) == NULL) { \
4910 g_warning("can't allocate memory"); \
4911 g_string_free(header, TRUE); \
4914 g_snprintf(__tmp, len, "\"%s\"", str); \
4919 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4920 g_warning("can't allocate memory"); \
4921 g_string_free(header, TRUE); \
4924 strcpy(__tmp, str); \
4930 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4932 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4936 len = strlen(str) + 3; \
4937 if ((__tmp = alloca(len)) == NULL) { \
4938 g_warning("can't allocate memory"); \
4941 g_snprintf(__tmp, len, "\"%s\"", str); \
4946 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4947 g_warning("can't allocate memory"); \
4950 strcpy(__tmp, str); \
4956 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
,
4959 gchar
*from
= NULL
, *header
= NULL
;
4960 ComposeHeaderEntry
*header_entry
;
4963 cm_return_if_fail(account
!= NULL
);
4965 compose
->account
= account
;
4966 if (account
->name
&& *account
->name
) {
4968 QUOTE_IF_REQUIRED_NORMAL(buf
, account
->name
, return);
4969 qbuf
= escape_internal_quotes(buf
, '"');
4970 from
= g_strdup_printf("%s <%s>",
4971 qbuf
, account
->address
);
4974 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4976 from
= g_strdup_printf("<%s>",
4978 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), from
);
4983 compose_set_title(compose
);
4985 compose_activate_privacy_system(compose
, account
, FALSE
);
4987 if (account
->default_sign
&& privacy_system_can_sign(compose
->privacy_system
) &&
4988 compose
->mode
!= COMPOSE_REDIRECT
)
4989 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", TRUE
);
4991 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Sign", FALSE
);
4992 if (account
->default_encrypt
&& privacy_system_can_encrypt(compose
->privacy_system
) &&
4993 compose
->mode
!= COMPOSE_REDIRECT
)
4994 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", TRUE
);
4996 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Options/Encrypt", FALSE
);
4998 if (!init
&& compose
->mode
!= COMPOSE_REDIRECT
) {
4999 undo_block(compose
->undostruct
);
5000 compose_insert_sig(compose
, TRUE
);
5001 undo_unblock(compose
->undostruct
);
5004 header_entry
= (ComposeHeaderEntry
*) compose
->header_list
->data
;
5005 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry
->combo
), &iter
))
5006 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
5007 header_entry
->combo
)), &iter
, COMBOBOX_TEXT
, &header
, -1);
5009 if (header
&& !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry
->entry
)))) {
5010 if (account
->protocol
== A_NNTP
) {
5011 if (!strcmp(header
, _("To:")))
5012 combobox_select_by_text(
5013 GTK_COMBO_BOX(header_entry
->combo
),
5016 if (!strcmp(header
, _("Newsgroups:")))
5017 combobox_select_by_text(
5018 GTK_COMBO_BOX(header_entry
->combo
),
5026 /* use account's dict info if set */
5027 if (compose
->gtkaspell
) {
5028 if (account
->enable_default_dictionary
)
5029 gtkaspell_change_dict(compose
->gtkaspell
,
5030 account
->default_dictionary
, FALSE
);
5031 if (account
->enable_default_alt_dictionary
)
5032 gtkaspell_change_alt_dict(compose
->gtkaspell
,
5033 account
->default_alt_dictionary
);
5034 if (account
->enable_default_dictionary
5035 || account
->enable_default_alt_dictionary
)
5036 compose_spell_menu_changed(compose
);
5041 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
5042 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
5043 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
5044 gboolean recipient_found
= FALSE
;
5048 /* free to and newsgroup list */
5049 slist_free_strings_full(compose
->to_list
);
5050 compose
->to_list
= NULL
;
5052 slist_free_strings_full(compose
->newsgroup_list
);
5053 compose
->newsgroup_list
= NULL
;
5055 /* search header entries for to and newsgroup entries */
5056 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5059 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5060 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5063 if (entry
[0] != '\0') {
5064 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
5065 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5066 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
5067 recipient_found
= TRUE
;
5070 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
5071 if (!g_ascii_strcasecmp(header
, prefs_common_translated_header_name(*strptr
))) {
5072 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
5073 recipient_found
= TRUE
;
5080 return recipient_found
;
5083 static gboolean
compose_check_for_set_recipients(Compose
*compose
)
5085 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
) {
5086 gboolean found_other
= FALSE
;
5088 /* search header entries for to and newsgroup entries */
5089 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5092 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5093 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5096 if (strcmp(entry
, compose
->account
->auto_cc
)
5097 || strcmp(header
, prefs_common_translated_header_name("Cc:"))) {
5108 if (compose
->batch
) {
5109 gtk_widget_show_all(compose
->window
);
5111 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5112 prefs_common_translated_header_name("Cc"));
5113 aval
= alertpanel(_("Send"),
5115 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5117 if (aval
!= G_ALERTALTERNATE
)
5121 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
) {
5122 gboolean found_other
= FALSE
;
5124 /* search header entries for to and newsgroup entries */
5125 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5128 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5129 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5132 if (strcmp(entry
, compose
->account
->auto_bcc
)
5133 || strcmp(header
, prefs_common_translated_header_name("Bcc:"))) {
5145 if (compose
->batch
) {
5146 gtk_widget_show_all(compose
->window
);
5148 text
= g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5149 prefs_common_translated_header_name("Bcc"));
5150 aval
= alertpanel(_("Send"),
5152 GTK_STOCK_CANCEL
, _("_Send"), NULL
, ALERTFOCUS_SECOND
);
5154 if (aval
!= G_ALERTALTERNATE
)
5161 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_everything
)
5165 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
5166 if (compose
->batch
) {
5167 gtk_widget_show_all(compose
->window
);
5169 alertpanel_error(_("Recipient is not specified."));
5173 if (compose_check_for_set_recipients(compose
) == FALSE
) {
5177 if (!compose
->batch
&& prefs_common
.warn_empty_subj
== TRUE
) {
5178 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5179 if (*str
== '\0' && check_everything
== TRUE
&&
5180 compose
->mode
!= COMPOSE_REDIRECT
) {
5184 message
= g_strdup_printf(_("Subject is empty. %s"),
5185 compose
->sending
?_("Send it anyway?"):
5186 _("Queue it anyway?"));
5188 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5189 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5190 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5192 if (aval
& G_ALERTDISABLE
) {
5193 aval
&= ~G_ALERTDISABLE
;
5194 prefs_common
.warn_empty_subj
= FALSE
;
5196 if (aval
!= G_ALERTALTERNATE
)
5201 if (!compose
->batch
&& prefs_common
.warn_sending_many_recipients_num
> 0
5202 && check_everything
== TRUE
) {
5206 /* count To and Cc recipients */
5207 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5211 header
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
5212 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
5215 if ((entry
[0] != '\0') &&
5216 (!strcmp(header
, prefs_common_translated_header_name("To:")) ||
5217 !strcmp(header
, prefs_common_translated_header_name("Cc:")))) {
5223 if (cnt
> prefs_common
.warn_sending_many_recipients_num
) {
5227 message
= g_strdup_printf(_("Sending to %d recipients. %s"), cnt
,
5228 compose
->sending
?_("Send it anyway?"):
5229 _("Queue it anyway?"));
5231 aval
= alertpanel_full(compose
->sending
?_("Send"):_("Send later"), message
,
5232 GTK_STOCK_CANCEL
, compose
->sending
?_("_Send"):_("_Queue"), NULL
,
5233 ALERTFOCUS_FIRST
, TRUE
, NULL
, ALERT_QUESTION
);
5235 if (aval
& G_ALERTDISABLE
) {
5236 aval
&= ~G_ALERTDISABLE
;
5237 prefs_common
.warn_sending_many_recipients_num
= 0;
5239 if (aval
!= G_ALERTALTERNATE
)
5244 if (check_everything
&& hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
, compose
))
5250 static void _display_queue_error(ComposeQueueResult val
)
5253 case COMPOSE_QUEUE_SUCCESS
:
5255 case COMPOSE_QUEUE_ERROR_NO_MSG
:
5256 alertpanel_error(_("Could not queue message."));
5258 case COMPOSE_QUEUE_ERROR_WITH_ERRNO
:
5259 alertpanel_error(_("Could not queue message:\n\n%s."),
5262 case COMPOSE_QUEUE_ERROR_SIGNING_FAILED
:
5263 alertpanel_error(_("Could not queue message for sending:\n\n"
5264 "Signature failed: %s"),
5265 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5267 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
:
5268 alertpanel_error(_("Could not queue message for sending:\n\n"
5269 "Encryption failed: %s"),
5270 privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5272 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
:
5273 alertpanel_error(_("Could not queue message for sending:\n\n"
5274 "Charset conversion failed."));
5276 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
:
5277 alertpanel_error(_("Could not queue message for sending:\n\n"
5278 "Couldn't get recipient encryption key."));
5280 case COMPOSE_QUEUE_SIGNING_CANCELLED
:
5281 debug_print("signing cancelled\n");
5284 /* unhandled error */
5285 debug_print("oops, unhandled compose_queue() return value %d\n",
5291 gint
compose_send(Compose
*compose
)
5294 FolderItem
*folder
= NULL
;
5295 ComposeQueueResult val
= COMPOSE_QUEUE_ERROR_NO_MSG
;
5296 gchar
*msgpath
= NULL
;
5297 gboolean discard_window
= FALSE
;
5298 gchar
*errstr
= NULL
;
5299 gchar
*tmsgid
= NULL
;
5300 MainWindow
*mainwin
= mainwindow_get_mainwindow();
5301 gboolean queued_removed
= FALSE
;
5303 if (prefs_common
.send_dialog_invisible
5304 || compose
->batch
== TRUE
)
5305 discard_window
= TRUE
;
5307 compose_allow_user_actions (compose
, FALSE
);
5308 compose
->sending
= TRUE
;
5310 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
5311 if (compose
->batch
) {
5312 gtk_widget_show_all(compose
->window
);
5318 val
= compose_queue(compose
, &msgnum
, &folder
, &msgpath
, TRUE
);
5320 if (val
!= COMPOSE_QUEUE_SUCCESS
) {
5321 if (compose
->batch
) {
5322 gtk_widget_show_all(compose
->window
);
5325 _display_queue_error(val
);
5330 tmsgid
= compose
->msgid
? g_strdup(compose
->msgid
) : NULL
;
5331 if (discard_window
) {
5332 compose
->sending
= FALSE
;
5333 compose_close(compose
);
5334 /* No more compose access in the normal codepath
5335 * after this point! */
5340 alertpanel_error(_("The message was queued but could not be "
5341 "sent.\nUse \"Send queued messages\" from "
5342 "the main window to retry."));
5343 if (!discard_window
) {
5350 if (msgpath
== NULL
) {
5351 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
5352 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5355 val
= procmsg_send_message_queue_with_lock(msgpath
, &errstr
, folder
, msgnum
, &queued_removed
);
5356 claws_unlink(msgpath
);
5359 if (!discard_window
) {
5361 if (!queued_removed
)
5362 folder_item_remove_msg(folder
, msgnum
);
5363 folder_item_scan(folder
);
5365 /* make sure we delete that */
5366 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5368 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5369 folder_item_remove_msg(folder
, tmp
->msgnum
);
5370 procmsg_msginfo_free(&tmp
);
5377 if (!queued_removed
)
5378 folder_item_remove_msg(folder
, msgnum
);
5379 folder_item_scan(folder
);
5381 /* make sure we delete that */
5382 MsgInfo
*tmp
= folder_item_get_msginfo_by_msgid(folder
, tmsgid
);
5384 debug_print("removing %d via %s\n", tmp
->msgnum
, tmsgid
);
5385 folder_item_remove_msg(folder
, tmp
->msgnum
);
5386 procmsg_msginfo_free(&tmp
);
5389 if (!discard_window
) {
5390 compose
->sending
= FALSE
;
5391 compose_allow_user_actions (compose
, TRUE
);
5392 compose_close(compose
);
5396 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5397 "the main window to retry."), errstr
);
5400 alertpanel_error_log(_("The message was queued but could not be "
5401 "sent.\nUse \"Send queued messages\" from "
5402 "the main window to retry."));
5404 if (!discard_window
) {
5413 toolbar_main_set_sensitive(mainwin
);
5414 main_window_set_menu_sensitive(mainwin
);
5420 compose_allow_user_actions (compose
, TRUE
);
5421 compose
->sending
= FALSE
;
5422 compose
->modified
= TRUE
;
5423 toolbar_main_set_sensitive(mainwin
);
5424 main_window_set_menu_sensitive(mainwin
);
5429 static gboolean
compose_use_attach(Compose
*compose
)
5431 GtkTreeModel
*model
= gtk_tree_view_get_model
5432 (GTK_TREE_VIEW(compose
->attach_clist
));
5433 return gtk_tree_model_iter_n_children(model
, NULL
) > 0;
5436 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
5439 gchar buf
[BUFFSIZE
];
5441 gboolean first_to_address
;
5442 gboolean first_cc_address
;
5444 ComposeHeaderEntry
*headerentry
;
5445 const gchar
*headerentryname
;
5446 const gchar
*cc_hdr
;
5447 const gchar
*to_hdr
;
5448 gboolean err
= FALSE
;
5450 debug_print("Writing redirect header\n");
5452 cc_hdr
= prefs_common_translated_header_name("Cc:");
5453 to_hdr
= prefs_common_translated_header_name("To:");
5455 first_to_address
= TRUE
;
5456 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5457 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5458 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5460 if (g_utf8_collate(headerentryname
, to_hdr
) == 0) {
5461 const gchar
*entstr
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5462 Xstrdup_a(str
, entstr
, return -1);
5464 if (str
[0] != '\0') {
5465 compose_convert_header
5466 (compose
, buf
, sizeof(buf
), str
,
5467 strlen("Resent-To") + 2, TRUE
);
5469 if (first_to_address
) {
5470 err
|= (fprintf(fp
, "Resent-To: ") < 0);
5471 first_to_address
= FALSE
;
5473 err
|= (fprintf(fp
, ",") < 0);
5475 err
|= (fprintf(fp
, "%s", buf
) < 0);
5479 if (!first_to_address
) {
5480 err
|= (fprintf(fp
, "\n") < 0);
5483 first_cc_address
= TRUE
;
5484 for (list
= compose
->header_list
; list
; list
= list
->next
) {
5485 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
5486 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
5488 if (g_utf8_collate(headerentryname
, cc_hdr
) == 0) {
5489 const gchar
*strg
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
5490 Xstrdup_a(str
, strg
, return -1);
5492 if (str
[0] != '\0') {
5493 compose_convert_header
5494 (compose
, buf
, sizeof(buf
), str
,
5495 strlen("Resent-Cc") + 2, TRUE
);
5497 if (first_cc_address
) {
5498 err
|= (fprintf(fp
, "Resent-Cc: ") < 0);
5499 first_cc_address
= FALSE
;
5501 err
|= (fprintf(fp
, ",") < 0);
5503 err
|= (fprintf(fp
, "%s", buf
) < 0);
5507 if (!first_cc_address
) {
5508 err
|= (fprintf(fp
, "\n") < 0);
5511 return (err
? -1:0);
5514 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
5516 gchar date
[RFC822_DATE_BUFFSIZE
];
5517 gchar buf
[BUFFSIZE
];
5519 const gchar
*entstr
;
5520 /* struct utsname utsbuf; */
5521 gboolean err
= FALSE
;
5523 cm_return_val_if_fail(fp
!= NULL
, -1);
5524 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
5525 cm_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
5528 if (prefs_common
.hide_timezone
)
5529 get_rfc822_date_hide_tz(date
, sizeof(date
));
5531 get_rfc822_date(date
, sizeof(date
));
5532 err
|= (fprintf(fp
, "Resent-Date: %s\n", date
) < 0);
5535 if (compose
->account
->name
&& *compose
->account
->name
) {
5536 compose_convert_header
5537 (compose
, buf
, sizeof(buf
), compose
->account
->name
,
5538 strlen("From: "), TRUE
);
5539 err
|= (fprintf(fp
, "Resent-From: %s <%s>\n",
5540 buf
, compose
->account
->address
) < 0);
5542 err
|= (fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
) < 0);
5545 entstr
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
5546 if (*entstr
!= '\0') {
5547 Xstrdup_a(str
, entstr
, return -1);
5550 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
5551 strlen("Subject: "), FALSE
);
5552 err
|= (fprintf(fp
, "Subject: %s\n", buf
) < 0);
5556 /* Resent-Message-ID */
5557 if (compose
->account
->gen_msgid
) {
5558 gchar
*addr
= prefs_account_generate_msgid(compose
->account
);
5559 err
|= (fprintf(fp
, "Resent-Message-ID: <%s>\n", addr
) < 0);
5561 g_free(compose
->msgid
);
5562 compose
->msgid
= addr
;
5564 compose
->msgid
= NULL
;
5567 if (compose_redirect_write_headers_from_headerlist(compose
, fp
))
5570 /* separator between header and body */
5571 err
|= (claws_fputs("\n", fp
) == EOF
);
5573 return (err
? -1:0);
5576 static gint
compose_redirect_write_to_file(Compose
*compose
, FILE *fdest
)
5581 gchar rewrite_buf
[BUFFSIZE
];
5583 gboolean skip
= FALSE
;
5584 gboolean err
= FALSE
;
5585 gchar
*not_included
[]={
5586 "Return-Path:", "Delivered-To:", "Received:",
5587 "Subject:", "X-UIDL:", "AF:",
5588 "NF:", "PS:", "SRH:",
5589 "SFN:", "DSR:", "MID:",
5590 "CFG:", "PT:", "S:",
5591 "RQ:", "SSV:", "NSV:",
5592 "SSH:", "R:", "MAID:",
5593 "NAID:", "RMID:", "FMID:",
5594 "SCF:", "RRCPT:", "NG:",
5595 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5596 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5597 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5598 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5599 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5604 if ((fp
= claws_fopen(compose
->redirect_filename
, "rb")) == NULL
) {
5605 FILE_OP_ERROR(compose
->redirect_filename
, "claws_fopen");
5609 while ((ret
= procheader_get_one_field_asis(&buf
, fp
)) != -1) {
5611 for (i
= 0; not_included
[i
] != NULL
; i
++) {
5612 if (g_ascii_strncasecmp(buf
, not_included
[i
],
5613 strlen(not_included
[i
])) == 0) {
5623 if (claws_fputs(buf
, fdest
) == -1) {
5629 if (!prefs_common
.redirect_keep_from
) {
5630 if (g_ascii_strncasecmp(buf
, "From:",
5631 strlen("From:")) == 0) {
5632 err
|= (claws_fputs(" (by way of ", fdest
) == EOF
);
5633 if (compose
->account
->name
5634 && *compose
->account
->name
) {
5635 gchar buffer
[BUFFSIZE
];
5637 compose_convert_header
5638 (compose
, buffer
, sizeof(buffer
),
5639 compose
->account
->name
,
5642 err
|= (fprintf(fdest
, "%s <%s>",
5644 compose
->account
->address
) < 0);
5646 err
|= (fprintf(fdest
, "%s",
5647 compose
->account
->address
) < 0);
5648 err
|= (claws_fputs(")", fdest
) == EOF
);
5654 if (claws_fputs("\n", fdest
) == -1)
5661 if (compose_redirect_write_headers(compose
, fdest
))
5664 while ((len
= claws_fread(rewrite_buf
, sizeof(gchar
), sizeof(rewrite_buf
), fp
)) > 0) {
5665 if (claws_fwrite(rewrite_buf
, sizeof(gchar
), len
, fdest
) != len
)
5679 static gint
compose_write_to_file(Compose
*compose
, FILE *fp
, gint action
, gboolean attach_parts
)
5681 GtkTextBuffer
*buffer
;
5682 GtkTextIter start
, end
, tmp
;
5683 gchar
*chars
, *tmp_enc_file
, *content
;
5685 const gchar
*out_codeset
;
5686 EncodingType encoding
= ENC_UNKNOWN
;
5687 MimeInfo
*mimemsg
, *mimetext
;
5689 const gchar
*src_codeset
= CS_INTERNAL
;
5690 gchar
*from_addr
= NULL
;
5691 gchar
*from_name
= NULL
;
5694 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5695 attach_parts
= TRUE
;
5697 /* We're sending the message, generate a Message-ID
5699 if (compose
->msgid
== NULL
&&
5700 compose
->account
->gen_msgid
) {
5701 compose
->msgid
= prefs_account_generate_msgid(compose
->account
);
5705 /* create message MimeInfo */
5706 mimemsg
= procmime_mimeinfo_new();
5707 mimemsg
->type
= MIMETYPE_MESSAGE
;
5708 mimemsg
->subtype
= g_strdup("rfc822");
5709 mimemsg
->content
= MIMECONTENT_MEM
;
5710 mimemsg
->tmp
= TRUE
; /* must free content later */
5711 mimemsg
->data
.mem
= compose_get_header(compose
);
5713 /* Create text part MimeInfo */
5714 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
5715 gtk_text_buffer_get_end_iter(buffer
, &end
);
5718 /* We make sure that there is a newline at the end. */
5719 if (action
== COMPOSE_WRITE_FOR_SEND
&& gtk_text_iter_backward_char(&tmp
)) {
5720 chars
= gtk_text_buffer_get_text(buffer
, &tmp
, &end
, FALSE
);
5721 if (*chars
!= '\n') {
5722 gtk_text_buffer_insert(buffer
, &end
, "\n", 1);
5727 /* get all composed text */
5728 gtk_text_buffer_get_start_iter(buffer
, &start
);
5729 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
5731 out_codeset
= conv_get_charset_str(compose
->out_encoding
);
5733 if (!out_codeset
&& is_ascii_str(chars
)) {
5734 out_codeset
= CS_US_ASCII
;
5735 } else if (prefs_common
.outgoing_fallback_to_ascii
&&
5736 is_ascii_str(chars
)) {
5737 out_codeset
= CS_US_ASCII
;
5738 encoding
= ENC_7BIT
;
5742 gchar
*test_conv_global_out
= NULL
;
5743 gchar
*test_conv_reply
= NULL
;
5745 /* automatic mode. be automatic. */
5746 codeconv_set_strict(TRUE
);
5748 out_codeset
= conv_get_outgoing_charset_str();
5750 debug_print("trying to convert to %s\n", out_codeset
);
5751 test_conv_global_out
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5754 if (!test_conv_global_out
&& compose
->orig_charset
5755 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
5756 out_codeset
= compose
->orig_charset
;
5757 debug_print("failure; trying to convert to %s\n", out_codeset
);
5758 test_conv_reply
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5761 if (!test_conv_global_out
&& !test_conv_reply
) {
5763 out_codeset
= CS_INTERNAL
;
5764 debug_print("failure; finally using %s\n", out_codeset
);
5766 g_free(test_conv_global_out
);
5767 g_free(test_conv_reply
);
5768 codeconv_set_strict(FALSE
);
5771 if (encoding
== ENC_UNKNOWN
) {
5772 if (prefs_common
.encoding_method
== CTE_BASE64
)
5773 encoding
= ENC_BASE64
;
5774 else if (prefs_common
.encoding_method
== CTE_QUOTED_PRINTABLE
)
5775 encoding
= ENC_QUOTED_PRINTABLE
;
5776 else if (prefs_common
.encoding_method
== CTE_8BIT
)
5777 encoding
= ENC_8BIT
;
5779 encoding
= procmime_get_encoding_for_charset(out_codeset
);
5782 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5783 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
5785 if (action
== COMPOSE_WRITE_FOR_SEND
) {
5786 codeconv_set_strict(TRUE
);
5787 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
5788 codeconv_set_strict(FALSE
);
5793 msg
= g_strdup_printf(_("Can't convert the character encoding of the message \n"
5794 "to the specified %s charset.\n"
5795 "Send it as %s?"), out_codeset
, src_codeset
);
5796 aval
= alertpanel_full(_("Error"), msg
, GTK_STOCK_CANCEL
,
5797 _("_Send"), NULL
, ALERTFOCUS_SECOND
, FALSE
,
5801 if (aval
!= G_ALERTALTERNATE
) {
5803 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION
;
5806 out_codeset
= src_codeset
;
5812 out_codeset
= src_codeset
;
5817 if (prefs_common
.rewrite_first_from
&& (encoding
== ENC_8BIT
|| encoding
== ENC_7BIT
)) {
5818 if (!strncmp(buf
, "From ", sizeof("From ")-1) ||
5819 strstr(buf
, "\nFrom ") != NULL
) {
5820 encoding
= ENC_QUOTED_PRINTABLE
;
5824 mimetext
= procmime_mimeinfo_new();
5825 mimetext
->content
= MIMECONTENT_MEM
;
5826 mimetext
->tmp
= TRUE
; /* must free content later */
5827 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5828 * and free the data, which we need later. */
5829 mimetext
->data
.mem
= g_strdup(buf
);
5830 mimetext
->type
= MIMETYPE_TEXT
;
5831 mimetext
->subtype
= g_strdup("plain");
5832 g_hash_table_insert(mimetext
->typeparameters
, g_strdup("charset"),
5833 g_strdup(out_codeset
));
5835 /* protect trailing spaces when signing message */
5836 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5837 privacy_system_can_sign(compose
->privacy_system
)) {
5838 encoding
= ENC_QUOTED_PRINTABLE
;
5842 debug_print("main text: %Id bytes encoded as %s in %d\n",
5844 debug_print("main text: %zd bytes encoded as %s in %d\n",
5846 strlen(buf
), out_codeset
, encoding
);
5848 /* check for line length limit */
5849 if (action
== COMPOSE_WRITE_FOR_SEND
&&
5850 encoding
!= ENC_QUOTED_PRINTABLE
&& encoding
!= ENC_BASE64
&&
5851 check_line_length(buf
, 1000, &line
) < 0) {
5854 msg
= g_strdup_printf
5855 (_("Line %d exceeds the line length limit (998 bytes).\n"
5856 "The contents of the message might be broken on the way to the delivery.\n"
5858 "Send it anyway?"), line
+ 1);
5859 aval
= alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
,
5862 if (aval
!= G_ALERTALTERNATE
) {
5864 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5868 if (encoding
!= ENC_UNKNOWN
)
5869 procmime_encode_content(mimetext
, encoding
);
5871 /* append attachment parts */
5872 if (compose_use_attach(compose
) && attach_parts
) {
5873 MimeInfo
*mimempart
;
5874 gchar
*boundary
= NULL
;
5875 mimempart
= procmime_mimeinfo_new();
5876 mimempart
->content
= MIMECONTENT_EMPTY
;
5877 mimempart
->type
= MIMETYPE_MULTIPART
;
5878 mimempart
->subtype
= g_strdup("mixed");
5882 boundary
= generate_mime_boundary(NULL
);
5883 } while (strstr(buf
, boundary
) != NULL
);
5885 g_hash_table_insert(mimempart
->typeparameters
, g_strdup("boundary"),
5888 mimetext
->disposition
= DISPOSITIONTYPE_INLINE
;
5890 g_node_append(mimempart
->node
, mimetext
->node
);
5891 g_node_append(mimemsg
->node
, mimempart
->node
);
5893 if (compose_add_attachments(compose
, mimempart
) < 0)
5894 return COMPOSE_QUEUE_ERROR_NO_MSG
;
5896 g_node_append(mimemsg
->node
, mimetext
->node
);
5900 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) != 0) {
5901 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
5902 /* extract name and address */
5903 if (strstr(spec
, " <") && strstr(spec
, ">")) {
5904 from_addr
= g_strdup(strrchr(spec
, '<')+1);
5905 *(strrchr(from_addr
, '>')) = '\0';
5906 from_name
= g_strdup(spec
);
5907 *(strrchr(from_name
, '<')) = '\0';
5914 /* sign message if sending */
5915 if (action
== COMPOSE_WRITE_FOR_SEND
&& compose
->use_signing
&&
5916 privacy_system_can_sign(compose
->privacy_system
))
5917 if (!privacy_sign(compose
->privacy_system
, mimemsg
,
5918 compose
->account
, from_addr
)) {
5921 if (!privacy_peek_error())
5922 return COMPOSE_QUEUE_SIGNING_CANCELLED
;
5924 return COMPOSE_QUEUE_ERROR_SIGNING_FAILED
;
5929 if (compose
->use_encryption
) {
5930 if (compose
->encdata
!= NULL
&&
5931 strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
5933 /* First, write an unencrypted copy and save it to outbox, if
5934 * user wants that. */
5935 if (compose
->account
->save_encrypted_as_clear_text
) {
5936 debug_print("saving sent message unencrypted...\n");
5937 FILE *tmpfp
= get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file
);
5939 claws_fclose(tmpfp
);
5941 /* fp now points to a file with headers written,
5942 * let's make a copy. */
5944 content
= file_read_stream_to_str(fp
);
5946 str_write_to_file(content
, tmp_enc_file
, TRUE
);
5949 /* Now write the unencrypted body. */
5950 if ((tmpfp
= claws_fopen(tmp_enc_file
, "a")) != NULL
) {
5951 procmime_write_mimeinfo(mimemsg
, tmpfp
);
5952 claws_fclose(tmpfp
);
5954 outbox
= folder_find_item_from_identifier(compose_get_save_to(compose
));
5956 outbox
= folder_get_default_outbox();
5958 procmsg_save_to_outbox(outbox
, tmp_enc_file
, TRUE
);
5959 claws_unlink(tmp_enc_file
);
5961 g_warning("Can't open file '%s'", tmp_enc_file
);
5964 g_warning("couldn't get tempfile");
5967 if (!privacy_encrypt(compose
->privacy_system
, mimemsg
, compose
->encdata
)) {
5968 debug_print("Couldn't encrypt mime structure: %s.\n",
5969 privacy_get_error());
5970 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED
;
5975 procmime_write_mimeinfo(mimemsg
, fp
);
5977 procmime_mimeinfo_free_all(&mimemsg
);
5982 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
5984 GtkTextBuffer
*buffer
;
5985 GtkTextIter start
, end
;
5990 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
5991 FILE_OP_ERROR(file
, "claws_fopen");
5995 /* chmod for security */
5996 if (change_file_mode_rw(fp
, file
) < 0) {
5997 FILE_OP_ERROR(file
, "chmod");
5998 g_warning("can't change file mode");
6001 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
6002 gtk_text_buffer_get_start_iter(buffer
, &start
);
6003 gtk_text_buffer_get_end_iter(buffer
, &end
);
6004 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
6006 chars
= conv_codeset_strdup
6007 (tmp
, CS_INTERNAL
, conv_get_locale_charset_str());
6016 len
= strlen(chars
);
6017 if (claws_fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
6018 FILE_OP_ERROR(file
, "claws_fwrite");
6027 if (claws_safe_fclose(fp
) == EOF
) {
6028 FILE_OP_ERROR(file
, "claws_fclose");
6035 static gint
compose_remove_reedit_target(Compose
*compose
, gboolean force
)
6038 MsgInfo
*msginfo
= compose
->targetinfo
;
6040 cm_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
6041 if (!msginfo
) return -1;
6043 if (!force
&& MSG_IS_LOCKED(msginfo
->flags
))
6046 item
= msginfo
->folder
;
6047 cm_return_val_if_fail(item
!= NULL
, -1);
6049 if (procmsg_msg_exist(msginfo
) &&
6050 (folder_has_parent_of_type(item
, F_QUEUE
) ||
6051 folder_has_parent_of_type(item
, F_DRAFT
)
6052 || msginfo
== compose
->autosaved_draft
)) {
6053 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
6054 g_warning("can't remove the old message");
6057 debug_print("removed reedit target %d\n", msginfo
->msgnum
);
6064 static void compose_remove_draft(Compose
*compose
)
6067 MsgInfo
*msginfo
= compose
->targetinfo
;
6068 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
6070 if (procmsg_msg_exist(msginfo
)) {
6071 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
6076 ComposeQueueResult
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gchar
**msgpath
,
6077 gboolean remove_reedit_target
)
6079 return compose_queue_sub (compose
, msgnum
, item
, msgpath
, FALSE
, remove_reedit_target
);
6082 static gboolean
compose_warn_encryption(Compose
*compose
)
6084 const gchar
*warning
= privacy_get_encrypt_warning(compose
->privacy_system
);
6085 AlertValue val
= G_ALERTALTERNATE
;
6087 if (warning
== NULL
)
6090 val
= alertpanel_full(_("Encryption warning"), warning
,
6091 GTK_STOCK_CANCEL
, _("C_ontinue"), NULL
, ALERTFOCUS_SECOND
,
6092 TRUE
, NULL
, ALERT_WARNING
);
6093 if (val
& G_ALERTDISABLE
) {
6094 val
&= ~G_ALERTDISABLE
;
6095 if (val
== G_ALERTALTERNATE
)
6096 privacy_inhibit_encrypt_warning(compose
->privacy_system
,
6100 if (val
== G_ALERTALTERNATE
) {
6107 static ComposeQueueResult
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
,
6108 gchar
**msgpath
, gboolean perform_checks
,
6109 gboolean remove_reedit_target
)
6116 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
6117 gboolean err
= FALSE
;
6119 debug_print("queueing message...\n");
6120 cm_return_val_if_fail(compose
->account
!= NULL
, -1);
6122 if (compose_check_entries(compose
, perform_checks
) == FALSE
) {
6123 if (compose
->batch
) {
6124 gtk_widget_show_all(compose
->window
);
6126 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6129 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
6130 g_warning("can't get recipient list.");
6131 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6134 if (compose
->to_list
) {
6135 if (compose
->account
->protocol
!= A_NNTP
)
6136 mailac
= compose
->account
;
6137 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
6138 mailac
= cur_account
;
6139 else if (!(mailac
= compose_current_mail_account())) {
6140 alertpanel_error(_("No account for sending mails available!"));
6141 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6145 if (compose
->newsgroup_list
) {
6146 if (compose
->account
->protocol
== A_NNTP
)
6147 newsac
= compose
->account
;
6149 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6150 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6154 /* write queue header */
6155 tmp
= g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6156 G_DIR_SEPARATOR
, compose
, (guint
) rand());
6157 debug_print("queuing to %s\n", tmp
);
6158 if ((fp
= claws_fopen(tmp
, "w+b")) == NULL
) {
6159 FILE_OP_ERROR(tmp
, "claws_fopen");
6161 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6164 if (change_file_mode_rw(fp
, tmp
) < 0) {
6165 FILE_OP_ERROR(tmp
, "chmod");
6166 g_warning("can't change file mode");
6169 /* queueing variables */
6170 err
|= (fprintf(fp
, "AF:\n") < 0);
6171 err
|= (fprintf(fp
, "NF:0\n") < 0);
6172 err
|= (fprintf(fp
, "PS:10\n") < 0);
6173 err
|= (fprintf(fp
, "SRH:1\n") < 0);
6174 err
|= (fprintf(fp
, "SFN:\n") < 0);
6175 err
|= (fprintf(fp
, "DSR:\n") < 0);
6177 err
|= (fprintf(fp
, "MID:<%s>\n", compose
->msgid
) < 0);
6179 err
|= (fprintf(fp
, "MID:\n") < 0);
6180 err
|= (fprintf(fp
, "CFG:\n") < 0);
6181 err
|= (fprintf(fp
, "PT:0\n") < 0);
6182 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
6183 err
|= (fprintf(fp
, "RQ:\n") < 0);
6185 err
|= (fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
) < 0);
6187 err
|= (fprintf(fp
, "SSV:\n") < 0);
6189 err
|= (fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
) < 0);
6191 err
|= (fprintf(fp
, "NSV:\n") < 0);
6192 err
|= (fprintf(fp
, "SSH:\n") < 0);
6193 /* write recipient list */
6194 if (compose
->to_list
) {
6195 err
|= (fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
) < 0);
6196 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
6198 err
|= (fprintf(fp
, ",<%s>", (gchar
*)cur
->data
) < 0);
6199 err
|= (fprintf(fp
, "\n") < 0);
6201 /* write newsgroup list */
6202 if (compose
->newsgroup_list
) {
6203 err
|= (fprintf(fp
, "NG:") < 0);
6204 err
|= (fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
) < 0);
6205 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
6206 err
|= (fprintf(fp
, ",%s", (gchar
*)cur
->data
) < 0);
6207 err
|= (fprintf(fp
, "\n") < 0);
6211 err
|= (fprintf(fp
, "MAID:%d\n", mailac
->account_id
) < 0);
6213 err
|= (fprintf(fp
, "NAID:%d\n", newsac
->account_id
) < 0);
6216 if (compose
->privacy_system
!= NULL
) {
6217 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
6218 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
6219 if (compose
->use_encryption
) {
6220 if (!compose_warn_encryption(compose
)) {
6224 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6226 if (mailac
&& mailac
->encrypt_to_self
) {
6227 GSList
*tmp_list
= g_slist_copy(compose
->to_list
);
6228 tmp_list
= g_slist_append(tmp_list
, compose
->account
->address
);
6229 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, tmp_list
);
6230 g_slist_free(tmp_list
);
6232 compose
->encdata
= privacy_get_encrypt_data(compose
->privacy_system
, compose
->to_list
);
6234 if (compose
->encdata
!= NULL
) {
6235 if (strcmp(compose
->encdata
, "_DONT_ENCRYPT_")) {
6236 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6237 err
|= (fprintf(fp
, "X-Claws-Encrypt-Data:%s\n",
6238 compose
->encdata
) < 0);
6239 } /* else we finally dont want to encrypt */
6241 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
6242 /* and if encdata was null, it means there's been a problem in
6245 g_warning("failed to write queue message");
6249 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY
;
6254 /* Save copy folder */
6255 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
6256 gchar
*savefolderid
;
6258 savefolderid
= compose_get_save_to(compose
);
6259 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
6260 g_free(savefolderid
);
6262 /* Save copy folder */
6263 if (compose
->return_receipt
) {
6264 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
6266 /* Message-ID of message replying to */
6267 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
6268 gchar
*folderid
= NULL
;
6270 if (compose
->replyinfo
->folder
)
6271 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
6272 if (folderid
== NULL
)
6273 folderid
= g_strdup("NULL");
6275 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
6278 /* Message-ID of message forwarding to */
6279 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
6280 gchar
*folderid
= NULL
;
6282 if (compose
->fwdinfo
->folder
)
6283 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
6284 if (folderid
== NULL
)
6285 folderid
= g_strdup("NULL");
6287 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
6291 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
6292 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
6294 /* end of headers */
6295 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
6297 if (compose
->redirect_filename
!= NULL
) {
6298 if (compose_redirect_write_to_file(compose
, fp
) < 0) {
6302 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6306 if ((result
= compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_SEND
, TRUE
)) < 0) {
6314 g_warning("failed to write queue message");
6318 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6320 if (claws_safe_fclose(fp
) == EOF
) {
6321 FILE_OP_ERROR(tmp
, "claws_fclose");
6324 return COMPOSE_QUEUE_ERROR_WITH_ERRNO
;
6327 if (item
&& *item
) {
6330 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
6333 g_warning("can't find queue folder");
6336 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6338 folder_item_scan(queue
);
6339 if ((num
= folder_item_add_msg(queue
, tmp
, NULL
, FALSE
)) < 0) {
6340 g_warning("can't queue the message");
6343 return COMPOSE_QUEUE_ERROR_NO_MSG
;
6346 if (msgpath
== NULL
) {
6352 if (compose
->mode
== COMPOSE_REEDIT
&& compose
->targetinfo
) {
6353 MsgInfo
*mi
= folder_item_get_msginfo(queue
, num
);
6355 procmsg_msginfo_change_flags(mi
,
6356 compose
->targetinfo
->flags
.perm_flags
,
6357 compose
->targetinfo
->flags
.tmp_flags
& ~(MSG_COPY
| MSG_MOVE
| MSG_MOVE_DONE
),
6360 g_slist_free(mi
->tags
);
6361 mi
->tags
= g_slist_copy(compose
->targetinfo
->tags
);
6362 procmsg_msginfo_free(&mi
);
6366 if (compose
->mode
== COMPOSE_REEDIT
&& remove_reedit_target
) {
6367 compose_remove_reedit_target(compose
, FALSE
);
6370 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
6375 return COMPOSE_QUEUE_SUCCESS
;
6378 static int compose_add_attachments(Compose
*compose
, MimeInfo
*parent
)
6381 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
6386 GError
*error
= NULL
;
6391 gchar
*type
, *subtype
;
6392 GtkTreeModel
*model
;
6395 model
= gtk_tree_view_get_model(tree_view
);
6397 if (!gtk_tree_model_get_iter_first(model
, &iter
))
6400 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
6402 if (!is_file_exist(ainfo
->file
)) {
6403 gchar
*msg
= g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo
->file
);
6404 AlertValue val
= alertpanel_full(_("Warning"), msg
,
6405 _("Cancel sending"), _("Ignore attachment"), NULL
,
6406 ALERTFOCUS_FIRST
, FALSE
, NULL
, ALERT_WARNING
);
6408 if (val
== G_ALERTDEFAULT
) {
6414 f
= g_file_new_for_path(ainfo
->file
);
6415 fi
= g_file_query_info(f
, "standard::size",
6416 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
6417 if (error
!= NULL
) {
6418 g_warning(error
->message
);
6419 g_error_free(error
);
6423 size
= g_file_info_get_size(fi
);
6427 if (g_stat(ainfo
->file
, &statbuf
) < 0)
6429 size
= statbuf
.st_size
;
6432 mimepart
= procmime_mimeinfo_new();
6433 mimepart
->content
= MIMECONTENT_FILE
;
6434 mimepart
->data
.filename
= g_strdup(ainfo
->file
);
6435 mimepart
->tmp
= FALSE
; /* or we destroy our attachment */
6436 mimepart
->offset
= 0;
6437 mimepart
->length
= size
;
6439 type
= g_strdup(ainfo
->content_type
);
6441 if (!strchr(type
, '/')) {
6443 type
= g_strdup("application/octet-stream");
6446 subtype
= strchr(type
, '/') + 1;
6447 *(subtype
- 1) = '\0';
6448 mimepart
->type
= procmime_get_media_type(type
);
6449 mimepart
->subtype
= g_strdup(subtype
);
6452 if (mimepart
->type
== MIMETYPE_MESSAGE
&&
6453 !g_ascii_strcasecmp(mimepart
->subtype
, "rfc822")) {
6454 mimepart
->disposition
= DISPOSITIONTYPE_INLINE
;
6455 } else if (mimepart
->type
== MIMETYPE_TEXT
) {
6456 if (!ainfo
->name
&& g_ascii_strcasecmp(mimepart
->subtype
, "plain")) {
6457 /* Text parts with no name come from multipart/alternative
6458 * forwards. Make sure the recipient won't look at the
6459 * original HTML part by mistake. */
6460 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6461 ainfo
->name
= g_strdup_printf(_("Original %s part"),
6465 g_hash_table_insert(mimepart
->typeparameters
,
6466 g_strdup("charset"), g_strdup(ainfo
->charset
));
6468 if (ainfo
->name
&& mimepart
->type
!= MIMETYPE_MESSAGE
) {
6469 if (mimepart
->type
== MIMETYPE_APPLICATION
&&
6470 !g_strcmp0(mimepart
->subtype
, "octet-stream"))
6471 g_hash_table_insert(mimepart
->typeparameters
,
6472 g_strdup("name"), g_strdup(ainfo
->name
));
6473 g_hash_table_insert(mimepart
->dispositionparameters
,
6474 g_strdup("filename"), g_strdup(ainfo
->name
));
6475 mimepart
->disposition
= DISPOSITIONTYPE_ATTACHMENT
;
6478 if (mimepart
->type
== MIMETYPE_MESSAGE
6479 || mimepart
->type
== MIMETYPE_MULTIPART
)
6480 ainfo
->encoding
= ENC_BINARY
;
6481 else if (compose
->use_signing
|| compose
->fwdinfo
!= NULL
) {
6482 if (ainfo
->encoding
== ENC_7BIT
)
6483 ainfo
->encoding
= ENC_QUOTED_PRINTABLE
;
6484 else if (ainfo
->encoding
== ENC_8BIT
)
6485 ainfo
->encoding
= ENC_BASE64
;
6488 procmime_encode_content(mimepart
, ainfo
->encoding
);
6490 g_node_append(parent
->node
, mimepart
->node
);
6491 } while (gtk_tree_model_iter_next(model
, &iter
));
6496 static gchar
*compose_quote_list_of_addresses(gchar
*str
)
6498 GSList
*list
= NULL
, *item
= NULL
;
6499 gchar
*qname
= NULL
, *faddr
= NULL
, *result
= NULL
;
6501 list
= address_list_append_with_comments(list
, str
);
6502 for (item
= list
; item
!= NULL
; item
= item
->next
) {
6503 gchar
*spec
= item
->data
;
6504 gchar
*endofname
= strstr(spec
, " <");
6505 if (endofname
!= NULL
) {
6508 QUOTE_IF_REQUIRED_NORMAL(qname
, spec
, return NULL
);
6509 qqname
= escape_internal_quotes(qname
, '"');
6511 if (*qname
!= *spec
|| qqname
!= qname
) { /* has been quoted, compute new */
6512 gchar
*addr
= g_strdup(endofname
);
6513 gchar
*name
= (qqname
!= qname
)? qqname
: g_strdup(qname
);
6514 faddr
= g_strconcat(name
, addr
, NULL
);
6517 debug_print("new auto-quoted address: '%s'\n", faddr
);
6521 result
= g_strdup((faddr
!= NULL
)? faddr
: spec
);
6523 result
= g_strconcat(result
,
6525 (faddr
!= NULL
)? faddr
: spec
,
6528 if (faddr
!= NULL
) {
6533 slist_free_strings_full(list
);
6538 #define IS_IN_CUSTOM_HEADER(header) \
6539 (compose->account->add_customhdr && \
6540 custom_header_find(compose->account->customhdr_list, header) != NULL)
6542 static const gchar
*compose_untranslated_header_name(gchar
*header_name
)
6544 /* return the untranslated header name, if header_name is a known
6545 header name, in either its translated or untranslated form, with
6546 or without trailing colon. otherwise, returns header_name. */
6547 gchar
*translated_header_name
;
6548 gchar
*translated_header_name_wcolon
;
6549 const gchar
*untranslated_header_name
;
6550 const gchar
*untranslated_header_name_wcolon
;
6553 cm_return_val_if_fail(header_name
!= NULL
, NULL
);
6555 for (i
= 0; HEADERS
[i
].header_name
!= NULL
; i
++) {
6556 untranslated_header_name
= HEADERS
[i
].header_name
;
6557 untranslated_header_name_wcolon
= HEADERS
[i
].header_name_w_colon
;
6559 translated_header_name
= gettext(untranslated_header_name
);
6560 translated_header_name_wcolon
= gettext(untranslated_header_name_wcolon
);
6562 if (!strcmp(header_name
, untranslated_header_name
) ||
6563 !strcmp(header_name
, translated_header_name
)) {
6564 return untranslated_header_name
;
6566 if (!strcmp(header_name
, untranslated_header_name_wcolon
) ||
6567 !strcmp(header_name
, translated_header_name_wcolon
)) {
6568 return untranslated_header_name_wcolon
;
6572 debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name
);
6576 static void compose_add_headerfield_from_headerlist(Compose
*compose
,
6578 const gchar
*fieldname
,
6579 const gchar
*seperator
)
6581 gchar
*str
, *fieldname_w_colon
;
6582 gboolean add_field
= FALSE
;
6584 ComposeHeaderEntry
*headerentry
;
6585 const gchar
*headerentryname
;
6586 const gchar
*trans_fieldname
;
6589 if (IS_IN_CUSTOM_HEADER(fieldname
))
6592 debug_print("Adding %s-fields\n", fieldname
);
6594 fieldstr
= g_string_sized_new(64);
6596 fieldname_w_colon
= g_strconcat(fieldname
, ":", NULL
);
6597 trans_fieldname
= prefs_common_translated_header_name(fieldname_w_colon
);
6599 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6600 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6601 headerentryname
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
)))));
6603 if (!g_utf8_collate(trans_fieldname
, headerentryname
)) {
6604 gchar
* ustr
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
6606 str
= compose_quote_list_of_addresses(ustr
);
6608 if (str
!= NULL
&& str
[0] != '\0') {
6610 g_string_append(fieldstr
, seperator
);
6611 g_string_append(fieldstr
, str
);
6620 buf
= g_new0(gchar
, fieldstr
->len
* 4 + 256);
6621 compose_convert_header
6622 (compose
, buf
, fieldstr
->len
* 4 + 256, fieldstr
->str
,
6623 strlen(fieldname
) + 2, TRUE
);
6624 g_string_append_printf(header
, "%s: %s\n", fieldname
, buf
);
6628 g_free(fieldname_w_colon
);
6629 g_string_free(fieldstr
, TRUE
);
6634 static gchar
*compose_get_manual_headers_info(Compose
*compose
)
6636 GString
*sh_header
= g_string_new(" ");
6638 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6640 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6641 ComposeHeaderEntry
*headerentry
;
6644 gchar
*headername_wcolon
;
6645 const gchar
*headername_trans
;
6647 gboolean standard_header
= FALSE
;
6649 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6651 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6653 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6658 if (!strstr(tmp
, ":")) {
6659 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6660 headername
= g_strdup(tmp
);
6662 headername_wcolon
= g_strdup(tmp
);
6663 headername
= g_strdup(strtok(tmp
, ":"));
6667 string
= std_headers
;
6668 while (*string
!= NULL
) {
6669 headername_trans
= prefs_common_translated_header_name(*string
);
6670 if (!strcmp(headername_trans
, headername_wcolon
))
6671 standard_header
= TRUE
;
6674 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
6675 g_string_append_printf(sh_header
, "%s ", headername
);
6677 g_free(headername_wcolon
);
6679 g_string_truncate(sh_header
, strlen(sh_header
->str
) - 1); /* remove last space */
6680 return g_string_free(sh_header
, FALSE
);
6683 static gchar
*compose_get_header(Compose
*compose
)
6685 gchar date
[RFC822_DATE_BUFFSIZE
];
6686 gchar buf
[BUFFSIZE
];
6687 const gchar
*entry_str
;
6691 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
6693 gchar
*from_name
= NULL
, *from_address
= NULL
;
6696 cm_return_val_if_fail(compose
->account
!= NULL
, NULL
);
6697 cm_return_val_if_fail(compose
->account
->address
!= NULL
, NULL
);
6699 header
= g_string_sized_new(64);
6702 if (prefs_common
.hide_timezone
)
6703 get_rfc822_date_hide_tz(date
, sizeof(date
));
6705 get_rfc822_date(date
, sizeof(date
));
6706 g_string_append_printf(header
, "Date: %s\n", date
);
6710 if (compose
->account
->name
&& *compose
->account
->name
) {
6712 QUOTE_IF_REQUIRED(buf
, compose
->account
->name
);
6713 tmp
= g_strdup_printf("%s <%s>",
6714 buf
, compose
->account
->address
);
6716 tmp
= g_strdup_printf("%s",
6717 compose
->account
->address
);
6719 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
)), tmp
)
6720 || strlen(gtk_entry_get_text(GTK_ENTRY(compose
->from_name
))) == 0) {
6722 from_name
= compose
->account
->name
? g_strdup(compose
->account
->name
):NULL
;
6723 from_address
= g_strdup(compose
->account
->address
);
6725 gchar
*spec
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
6726 /* extract name and address */
6727 if (strstr(spec
, " <") && strstr(spec
, ">")) {
6728 from_address
= g_strdup(strrchr(spec
, '<')+1);
6729 *(strrchr(from_address
, '>')) = '\0';
6730 from_name
= g_strdup(spec
);
6731 *(strrchr(from_name
, '<')) = '\0';
6734 from_address
= g_strdup(spec
);
6741 if (from_name
&& *from_name
) {
6743 compose_convert_header
6744 (compose
, buf
, sizeof(buf
), from_name
,
6745 strlen("From: "), TRUE
);
6746 QUOTE_IF_REQUIRED(name
, buf
);
6747 qname
= escape_internal_quotes(name
, '"');
6749 g_string_append_printf(header
, "From: %s <%s>\n",
6750 qname
, from_address
);
6751 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6752 compose
->return_receipt
) {
6753 compose_convert_header(compose
, buf
, sizeof(buf
), from_name
,
6754 strlen("Disposition-Notification-To: "),
6756 g_string_append_printf(header
, "Disposition-Notification-To: %s <%s>\n", buf
, from_address
);
6761 g_string_append_printf(header
, "From: %s\n", from_address
);
6762 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6763 compose
->return_receipt
)
6764 g_string_append_printf(header
, "Disposition-Notification-To: %s\n", from_address
);
6768 g_free(from_address
);
6771 compose_add_headerfield_from_headerlist(compose
, header
, "To", ", ");
6774 compose_add_headerfield_from_headerlist(compose
, header
, "Newsgroups", ",");
6777 compose_add_headerfield_from_headerlist(compose
, header
, "Cc", ", ");
6781 * If this account is a NNTP account remove Bcc header from
6782 * message body since it otherwise will be publicly shown
6784 if (compose
->account
->protocol
!= A_NNTP
)
6785 compose_add_headerfield_from_headerlist(compose
, header
, "Bcc", ", ");
6788 str
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
6790 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6793 compose_convert_header(compose
, buf
, sizeof(buf
), str
,
6794 strlen("Subject: "), FALSE
);
6795 g_string_append_printf(header
, "Subject: %s\n", buf
);
6801 if (compose
->msgid
!= NULL
&& strlen(compose
->msgid
) > 0) {
6802 g_string_append_printf(header
, "Message-ID: <%s>\n",
6806 if (compose
->remove_references
== FALSE
) {
6808 if (compose
->inreplyto
&& compose
->to_list
)
6809 g_string_append_printf(header
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
6812 if (compose
->references
)
6813 g_string_append_printf(header
, "References: %s\n", compose
->references
);
6817 compose_add_headerfield_from_headerlist(compose
, header
, "Followup-To", ",");
6820 compose_add_headerfield_from_headerlist(compose
, header
, "Reply-To", ", ");
6823 if (compose
->account
->organization
&&
6824 strlen(compose
->account
->organization
) &&
6825 !IS_IN_CUSTOM_HEADER("Organization")) {
6826 compose_convert_header(compose
, buf
, sizeof(buf
),
6827 compose
->account
->organization
,
6828 strlen("Organization: "), FALSE
);
6829 g_string_append_printf(header
, "Organization: %s\n", buf
);
6832 /* Program version and system info */
6833 if (compose
->account
->gen_xmailer
&&
6834 g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6835 !compose
->newsgroup_list
) {
6836 g_string_append_printf(header
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6838 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6841 if (compose
->account
->gen_xmailer
&&
6842 g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6843 g_string_append_printf(header
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6845 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
6849 /* custom headers */
6850 if (compose
->account
->add_customhdr
) {
6853 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
6855 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
6857 if (custom_header_is_allowed(chdr
->name
)
6858 && chdr
->value
!= NULL
6859 && *(chdr
->value
) != '\0') {
6860 compose_convert_header
6861 (compose
, buf
, sizeof(buf
),
6863 strlen(chdr
->name
) + 2, FALSE
);
6864 g_string_append_printf(header
, "%s: %s\n", chdr
->name
, buf
);
6869 /* Automatic Faces and X-Faces */
6870 if (get_account_xface (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6871 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6873 else if (get_default_xface (buf
, sizeof(buf
)) == 0) {
6874 g_string_append_printf(header
, "X-Face: %s\n", buf
);
6876 if (get_account_face (buf
, sizeof(buf
), compose
->account
->account_name
) == 0) {
6877 g_string_append_printf(header
, "Face: %s\n", buf
);
6879 else if (get_default_face (buf
, sizeof(buf
)) == 0) {
6880 g_string_append_printf(header
, "Face: %s\n", buf
);
6884 switch (compose
->priority
) {
6885 case PRIORITY_HIGHEST
: g_string_append_printf(header
, "Importance: high\n"
6886 "X-Priority: 1 (Highest)\n");
6888 case PRIORITY_HIGH
: g_string_append_printf(header
, "Importance: high\n"
6889 "X-Priority: 2 (High)\n");
6891 case PRIORITY_NORMAL
: break;
6892 case PRIORITY_LOW
: g_string_append_printf(header
, "Importance: low\n"
6893 "X-Priority: 4 (Low)\n");
6895 case PRIORITY_LOWEST
: g_string_append_printf(header
, "Importance: low\n"
6896 "X-Priority: 5 (Lowest)\n");
6898 default: debug_print("compose: priority unknown : %d\n",
6902 /* get special headers */
6903 for (list
= compose
->header_list
; list
; list
= list
->next
) {
6904 ComposeHeaderEntry
*headerentry
;
6907 gchar
*headername_wcolon
;
6908 const gchar
*headername_trans
;
6911 gboolean standard_header
= FALSE
;
6913 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
6915 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry
->combo
))))));
6917 if (*tmp
== '\0' || strchr(tmp
, ' ') != NULL
|| strchr(tmp
, '\r') != NULL
|| strchr(tmp
, '\n') != NULL
) {
6922 if (!strstr(tmp
, ":")) {
6923 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
6924 headername
= g_strdup(tmp
);
6926 headername_wcolon
= g_strdup(tmp
);
6927 headername
= g_strdup(strtok(tmp
, ":"));
6931 entry_str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
6932 Xstrdup_a(headervalue
, entry_str
, return NULL
);
6933 subst_char(headervalue
, '\r', ' ');
6934 subst_char(headervalue
, '\n', ' ');
6935 g_strstrip(headervalue
);
6936 if (*headervalue
!= '\0') {
6937 string
= std_headers
;
6938 while (*string
!= NULL
&& !standard_header
) {
6939 headername_trans
= prefs_common_translated_header_name(*string
);
6940 /* support mixed translated and untranslated headers */
6941 if (!strcmp(headername_trans
, headername_wcolon
) || !strcmp(*string
, headername_wcolon
))
6942 standard_header
= TRUE
;
6945 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
)) {
6946 /* store untranslated header name */
6947 g_string_append_printf(header
, "%s %s\n",
6948 compose_untranslated_header_name(headername_wcolon
), headervalue
);
6952 g_free(headername_wcolon
);
6956 g_string_free(header
, FALSE
);
6961 #undef IS_IN_CUSTOM_HEADER
6963 static void compose_convert_header(Compose
*compose
, gchar
*dest
, gint len
, gchar
*src
,
6964 gint header_len
, gboolean addr_field
)
6966 gchar
*tmpstr
= NULL
;
6967 const gchar
*out_codeset
= NULL
;
6969 cm_return_if_fail(src
!= NULL
);
6970 cm_return_if_fail(dest
!= NULL
);
6972 if (len
< 1) return;
6974 tmpstr
= g_strdup(src
);
6976 subst_char(tmpstr
, '\n', ' ');
6977 subst_char(tmpstr
, '\r', ' ');
6980 if (!g_utf8_validate(tmpstr
, -1, NULL
)) {
6981 gchar
*mybuf
= g_malloc(strlen(tmpstr
)*2 +1);
6982 conv_localetodisp(mybuf
, strlen(tmpstr
)*2 +1, tmpstr
);
6987 codeconv_set_strict(TRUE
);
6988 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
6989 conv_get_charset_str(compose
->out_encoding
));
6990 codeconv_set_strict(FALSE
);
6992 if (!dest
|| *dest
== '\0') {
6993 gchar
*test_conv_global_out
= NULL
;
6994 gchar
*test_conv_reply
= NULL
;
6996 /* automatic mode. be automatic. */
6997 codeconv_set_strict(TRUE
);
6999 out_codeset
= conv_get_outgoing_charset_str();
7001 debug_print("trying to convert to %s\n", out_codeset
);
7002 test_conv_global_out
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7005 if (!test_conv_global_out
&& compose
->orig_charset
7006 && strcmp(compose
->orig_charset
, CS_US_ASCII
)) {
7007 out_codeset
= compose
->orig_charset
;
7008 debug_print("failure; trying to convert to %s\n", out_codeset
);
7009 test_conv_reply
= conv_codeset_strdup(src
, CS_INTERNAL
, out_codeset
);
7012 if (!test_conv_global_out
&& !test_conv_reply
) {
7014 out_codeset
= CS_INTERNAL
;
7015 debug_print("finally using %s\n", out_codeset
);
7017 g_free(test_conv_global_out
);
7018 g_free(test_conv_reply
);
7019 conv_encode_header_full(dest
, len
, tmpstr
, header_len
, addr_field
,
7021 codeconv_set_strict(FALSE
);
7026 static void compose_add_to_addressbook_cb(GtkMenuItem
*menuitem
, gpointer user_data
)
7030 cm_return_if_fail(user_data
!= NULL
);
7032 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data
)));
7033 g_strstrip(address
);
7034 if (*address
!= '\0') {
7035 gchar
*name
= procheader_get_fromname(address
);
7036 extract_address(address
);
7037 #ifndef USE_ALT_ADDRBOOK
7038 addressbook_add_contact(name
, address
, NULL
, NULL
);
7040 debug_print("%s: %s\n", name
, address
);
7041 if (addressadd_selection(name
, address
, NULL
, NULL
)) {
7042 debug_print( "addressbook_add_contact - added\n" );
7049 static void compose_entry_popup_extend(GtkEntry
*entry
, GtkMenu
*menu
, gpointer user_data
)
7051 GtkWidget
*menuitem
;
7054 cm_return_if_fail(menu
!= NULL
);
7055 cm_return_if_fail(GTK_IS_MENU_SHELL(menu
));
7057 menuitem
= gtk_separator_menu_item_new();
7058 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7059 gtk_widget_show(menuitem
);
7061 menuitem
= gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
7062 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), menuitem
);
7064 address
= g_strdup(gtk_entry_get_text(GTK_ENTRY(entry
)));
7065 g_strstrip(address
);
7066 if (*address
== '\0') {
7067 gtk_widget_set_sensitive(GTK_WIDGET(menuitem
), FALSE
);
7070 g_signal_connect(G_OBJECT(menuitem
), "activate",
7071 G_CALLBACK(compose_add_to_addressbook_cb
), entry
);
7072 gtk_widget_show(menuitem
);
7075 void compose_add_extra_header(gchar
*header
, GtkListStore
*model
)
7078 if (strcmp(header
, "")) {
7079 COMBOBOX_ADD(model
, header
, COMPOSE_TO
);
7083 void compose_add_extra_header_entries(GtkListStore
*model
)
7087 gchar buf
[BUFFSIZE
];
7090 if (extra_headers
== NULL
) {
7091 exhrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "extraheaderrc", NULL
);
7092 if ((exh
= claws_fopen(exhrc
, "rb")) == NULL
) {
7093 debug_print("extra headers file not found\n");
7094 goto extra_headers_done
;
7096 while (claws_fgets(buf
, BUFFSIZE
, exh
) != NULL
) {
7097 lastc
= strlen(buf
) - 1; /* remove trailing control chars */
7098 while (lastc
>= 0 && buf
[lastc
] != ':')
7099 buf
[lastc
--] = '\0';
7100 if (lastc
> 0 && buf
[0] != '#' && buf
[lastc
] == ':') {
7101 buf
[lastc
] = '\0'; /* remove trailing : for comparison */
7102 if (custom_header_is_allowed(buf
)) {
7104 extra_headers
= g_slist_prepend(extra_headers
, g_strdup(buf
));
7107 g_message("disallowed extra header line: %s\n", buf
);
7111 g_message("invalid extra header line: %s\n", buf
);
7117 extra_headers
= g_slist_prepend(extra_headers
, g_strdup("")); /* end of list */
7118 extra_headers
= g_slist_reverse(extra_headers
);
7120 g_slist_foreach(extra_headers
, (GFunc
)compose_add_extra_header
, (gpointer
)model
);
7124 static void _ldap_srv_func(gpointer data
, gpointer user_data
)
7126 LdapServer
*server
= (LdapServer
*)data
;
7127 gboolean
*enable
= (gboolean
*)user_data
;
7129 debug_print("%s server '%s'\n", (*enable
== TRUE
? "enabling" : "disabling"), server
->control
->hostName
);
7130 server
->searchFlag
= *enable
;
7134 static void compose_create_header_entry(Compose
*compose
)
7136 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
7143 const gchar
*header
= NULL
;
7144 ComposeHeaderEntry
*headerentry
;
7145 gboolean standard_header
= FALSE
;
7146 GtkListStore
*model
;
7149 headerentry
= g_new0(ComposeHeaderEntry
, 1);
7151 /* Combo box model */
7152 model
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_INT
, G_TYPE_BOOLEAN
);
7153 COMBOBOX_ADD(model
, prefs_common_translated_header_name("To:"),
7155 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Cc:"),
7157 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Bcc:"),
7159 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Newsgroups:"),
7160 COMPOSE_NEWSGROUPS
);
7161 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Reply-To:"),
7163 COMBOBOX_ADD(model
, prefs_common_translated_header_name("Followup-To:"),
7164 COMPOSE_FOLLOWUPTO
);
7165 compose_add_extra_header_entries(model
);
7168 combo
= gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model
));
7169 GtkCellRenderer
*cell
= gtk_cell_renderer_text_new();
7170 gtk_cell_renderer_set_alignment(cell
, 0.0, 0.5);
7171 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), cell
, TRUE
);
7172 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo
), 0);
7173 gtk_combo_box_set_active(GTK_COMBO_BOX(combo
), 0);
7174 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo
))), "grab_focus",
7175 G_CALLBACK(compose_grab_focus_cb
), compose
);
7176 gtk_widget_show(combo
);
7178 /* Putting only the combobox child into focus chain of its parent causes
7179 * the parent to be skipped when changing focus via Tab or Shift+Tab.
7180 * This eliminates need to pres Tab twice in order to really get from the
7181 * combobox to next widget. */
7183 l
= g_list_prepend(l
, gtk_bin_get_child(GTK_BIN(combo
)));
7184 gtk_container_set_focus_chain(GTK_CONTAINER(combo
), l
);
7187 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1,
7188 compose
->header_nextrow
, compose
->header_nextrow
+1,
7189 GTK_SHRINK
, GTK_FILL
, 0, 0);
7190 if (compose
->header_last
&& (compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
)) {
7191 const gchar
*last_header_entry
= gtk_entry_get_text(
7192 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7194 while (*string
!= NULL
) {
7195 if (!strcmp(prefs_common_translated_header_name(*string
), last_header_entry
))
7196 standard_header
= TRUE
;
7199 if (standard_header
)
7200 header
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))));
7202 if (!compose
->header_last
|| !standard_header
) {
7203 switch(compose
->account
->protocol
) {
7205 header
= prefs_common_translated_header_name("Newsgroups:");
7208 header
= prefs_common_translated_header_name("To:");
7213 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo
)))), header
);
7215 gtk_editable_set_editable(
7216 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo
)))),
7217 prefs_common
.type_any_header
);
7219 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo
)))), "grab_focus",
7220 G_CALLBACK(compose_grab_focus_cb
), compose
);
7222 /* Entry field with cleanup button */
7223 button
= gtk_button_new();
7224 gtk_button_set_image(GTK_BUTTON(button
),
7225 gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
));
7226 gtk_widget_show(button
);
7227 CLAWS_SET_TIP(button
,
7228 _("Delete entry contents"));
7229 entry
= gtk_entry_new();
7230 gtk_widget_show(entry
);
7231 CLAWS_SET_TIP(entry
,
7232 _("Use <tab> to autocomplete from addressbook"));
7233 hbox
= gtk_hbox_new (FALSE
, 0);
7234 gtk_widget_show(hbox
);
7235 gtk_box_pack_start (GTK_BOX (hbox
), entry
, TRUE
, TRUE
, 0);
7236 gtk_box_pack_start (GTK_BOX (hbox
), button
, FALSE
, FALSE
, 0);
7237 gtk_table_attach(GTK_TABLE(compose
->header_table
), hbox
, 1, 2,
7238 compose
->header_nextrow
, compose
->header_nextrow
+1,
7239 GTK_EXPAND
| GTK_FILL
, GTK_FILL
, 0, 0);
7241 g_signal_connect(G_OBJECT(entry
), "key-press-event",
7242 G_CALLBACK(compose_headerentry_key_press_event_cb
),
7244 g_signal_connect(G_OBJECT(entry
), "changed",
7245 G_CALLBACK(compose_headerentry_changed_cb
),
7247 g_signal_connect_after(G_OBJECT(entry
), "grab_focus",
7248 G_CALLBACK(compose_grab_focus_cb
), compose
);
7250 g_signal_connect(G_OBJECT(button
), "clicked",
7251 G_CALLBACK(compose_headerentry_button_clicked_cb
),
7255 gtk_drag_dest_set(entry
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7256 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7257 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7258 g_signal_connect(G_OBJECT(entry
), "drag_data_received",
7259 G_CALLBACK(compose_header_drag_received_cb
),
7261 g_signal_connect(G_OBJECT(entry
), "drag-drop",
7262 G_CALLBACK(compose_drag_drop
),
7264 g_signal_connect(G_OBJECT(entry
), "populate-popup",
7265 G_CALLBACK(compose_entry_popup_extend
),
7269 #ifndef PASSWORD_CRYPTO_OLD
7270 GSList
*pwd_servers
= addrindex_get_password_protected_ldap_servers();
7271 if (pwd_servers
!= NULL
&& master_passphrase() == NULL
) {
7272 gboolean enable
= FALSE
;
7273 debug_print("Master passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7274 /* Temporarily disable password-protected LDAP servers,
7275 * because user did not provide a master passphrase.
7276 * We can safely enable searchFlag on all servers in this list
7277 * later, since addrindex_get_password_protected_ldap_servers()
7278 * includes servers which have it enabled initially. */
7279 g_slist_foreach(pwd_servers
, _ldap_srv_func
, &enable
);
7280 compose
->passworded_ldap_servers
= pwd_servers
;
7282 #endif /* PASSWORD_CRYPTO_OLD */
7283 #endif /* USE_LDAP */
7285 address_completion_register_entry(GTK_ENTRY(entry
), TRUE
);
7287 headerentry
->compose
= compose
;
7288 headerentry
->combo
= combo
;
7289 headerentry
->entry
= entry
;
7290 headerentry
->button
= button
;
7291 headerentry
->hbox
= hbox
;
7292 headerentry
->headernum
= compose
->header_nextrow
;
7293 headerentry
->type
= PREF_NONE
;
7295 compose
->header_nextrow
++;
7296 compose
->header_last
= headerentry
;
7297 compose
->header_list
=
7298 g_slist_append(compose
->header_list
,
7302 static void compose_add_header_entry(Compose
*compose
, const gchar
*header
,
7303 gchar
*text
, ComposePrefType pref_type
)
7305 ComposeHeaderEntry
*last_header
= compose
->header_last
;
7306 gchar
*tmp
= g_strdup(text
), *email
;
7307 gboolean replyto_hdr
;
7309 replyto_hdr
= (!strcasecmp(header
,
7310 prefs_common_translated_header_name("Reply-To:")) ||
7312 prefs_common_translated_header_name("Followup-To:")) ||
7314 prefs_common_translated_header_name("In-Reply-To:")));
7316 extract_address(tmp
);
7317 email
= g_utf8_strdown(tmp
, -1);
7319 if (replyto_hdr
== FALSE
&&
7320 g_hash_table_lookup(compose
->email_hashtable
, email
) != NULL
)
7322 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7323 header
, text
, (gint
) pref_type
);
7329 if (!strcasecmp(header
, prefs_common_translated_header_name("In-Reply-To:")))
7330 gtk_entry_set_text(GTK_ENTRY(
7331 gtk_bin_get_child(GTK_BIN(last_header
->combo
))), header
);
7333 combobox_select_by_text(GTK_COMBO_BOX(last_header
->combo
), header
);
7334 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
7335 last_header
->type
= pref_type
;
7337 if (replyto_hdr
== FALSE
)
7338 g_hash_table_insert(compose
->email_hashtable
, email
,
7339 GUINT_TO_POINTER(1));
7346 static void compose_destroy_headerentry(Compose
*compose
,
7347 ComposeHeaderEntry
*headerentry
)
7349 gchar
*text
= gtk_editable_get_chars(GTK_EDITABLE(headerentry
->entry
), 0, -1);
7352 extract_address(text
);
7353 email
= g_utf8_strdown(text
, -1);
7354 g_hash_table_remove(compose
->email_hashtable
, email
);
7358 gtk_widget_destroy(headerentry
->combo
);
7359 gtk_widget_destroy(headerentry
->entry
);
7360 gtk_widget_destroy(headerentry
->button
);
7361 gtk_widget_destroy(headerentry
->hbox
);
7362 g_free(headerentry
);
7365 static void compose_remove_header_entries(Compose
*compose
)
7368 for (list
= compose
->header_list
; list
; list
= list
->next
)
7369 compose_destroy_headerentry(compose
, (ComposeHeaderEntry
*)list
->data
);
7371 compose
->header_last
= NULL
;
7372 g_slist_free(compose
->header_list
);
7373 compose
->header_list
= NULL
;
7374 compose
->header_nextrow
= 1;
7375 compose_create_header_entry(compose
);
7378 static GtkWidget
*compose_create_header(Compose
*compose
)
7380 GtkWidget
*from_optmenu_hbox
;
7381 GtkWidget
*header_table_main
;
7382 GtkWidget
*header_scrolledwin
;
7383 GtkWidget
*header_table
;
7385 /* parent with account selection and from header */
7386 header_table_main
= gtk_table_new(2, 2, FALSE
);
7387 gtk_widget_show(header_table_main
);
7388 gtk_container_set_border_width(GTK_CONTAINER(header_table_main
), BORDER_WIDTH
);
7390 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
7391 gtk_table_attach(GTK_TABLE(header_table_main
), from_optmenu_hbox
,
7392 0, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7394 /* child with header labels and entries */
7395 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
7396 gtk_widget_show(header_scrolledwin
);
7397 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
7399 header_table
= gtk_table_new(2, 2, FALSE
);
7400 gtk_widget_show(header_table
);
7401 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 0);
7402 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
7403 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table
),
7404 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin
)));
7405 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin
))), GTK_SHADOW_NONE
);
7407 gtk_table_attach(GTK_TABLE(header_table_main
), header_scrolledwin
,
7408 0, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 0, 2);
7410 compose
->header_table
= header_table
;
7411 compose
->header_list
= NULL
;
7412 compose
->header_nextrow
= 0;
7414 compose_create_header_entry(compose
);
7416 compose
->table
= NULL
;
7418 return header_table_main
;
7421 static gboolean
popup_attach_button_pressed(GtkWidget
*widget
, gpointer data
)
7423 Compose
*compose
= (Compose
*)data
;
7424 GdkEventButton event
;
7427 event
.time
= gtk_get_current_event_time();
7429 return attach_button_pressed(compose
->attach_clist
, &event
, compose
);
7432 static GtkWidget
*compose_create_attach(Compose
*compose
)
7434 GtkWidget
*attach_scrwin
;
7435 GtkWidget
*attach_clist
;
7437 GtkListStore
*store
;
7438 GtkCellRenderer
*renderer
;
7439 GtkTreeViewColumn
*column
;
7440 GtkTreeSelection
*selection
;
7442 /* attachment list */
7443 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
7444 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
7445 GTK_POLICY_AUTOMATIC
,
7446 GTK_POLICY_AUTOMATIC
);
7447 gtk_widget_set_size_request(attach_scrwin
, -1, 80);
7449 store
= gtk_list_store_new(N_ATTACH_COLS
,
7455 G_TYPE_AUTO_POINTER
,
7457 attach_clist
= GTK_WIDGET(gtk_tree_view_new_with_model
7458 (GTK_TREE_MODEL(store
)));
7459 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
7460 g_object_unref(store
);
7462 renderer
= gtk_cell_renderer_text_new();
7463 column
= gtk_tree_view_column_new_with_attributes
7464 (_("Mime type"), renderer
, "text",
7465 COL_MIMETYPE
, NULL
);
7466 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7468 renderer
= gtk_cell_renderer_text_new();
7469 column
= gtk_tree_view_column_new_with_attributes
7470 (_("Size"), renderer
, "text",
7472 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7474 renderer
= gtk_cell_renderer_text_new();
7475 column
= gtk_tree_view_column_new_with_attributes
7476 (_("Name"), renderer
, "text",
7478 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist
), column
);
7480 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist
),
7481 prefs_common
.use_stripes_everywhere
);
7482 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist
));
7483 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
7485 g_signal_connect(G_OBJECT(attach_clist
), "row_activated",
7486 G_CALLBACK(attach_selected
), compose
);
7487 g_signal_connect(G_OBJECT(attach_clist
), "button_press_event",
7488 G_CALLBACK(attach_button_pressed
), compose
);
7489 g_signal_connect(G_OBJECT(attach_clist
), "popup-menu",
7490 G_CALLBACK(popup_attach_button_pressed
), compose
);
7491 g_signal_connect(G_OBJECT(attach_clist
), "key_press_event",
7492 G_CALLBACK(attach_key_pressed
), compose
);
7495 gtk_drag_dest_set(attach_clist
,
7496 GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
7497 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
7498 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
7499 g_signal_connect(G_OBJECT(attach_clist
), "drag_data_received",
7500 G_CALLBACK(compose_attach_drag_received_cb
),
7502 g_signal_connect(G_OBJECT(attach_clist
), "drag-drop",
7503 G_CALLBACK(compose_drag_drop
),
7506 compose
->attach_scrwin
= attach_scrwin
;
7507 compose
->attach_clist
= attach_clist
;
7509 return attach_scrwin
;
7512 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
7514 static GtkWidget
*compose_create_others(Compose
*compose
)
7517 GtkWidget
*savemsg_checkbtn
;
7518 GtkWidget
*savemsg_combo
;
7519 GtkWidget
*savemsg_select
;
7522 gchar
*folderidentifier
;
7524 /* Table for settings */
7525 table
= gtk_table_new(3, 1, FALSE
);
7526 gtk_container_set_border_width(GTK_CONTAINER(table
), BORDER_WIDTH
);
7527 gtk_widget_show(table
);
7528 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
7531 /* Save Message to folder */
7532 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
7533 gtk_widget_show(savemsg_checkbtn
);
7534 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7535 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7536 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
7539 savemsg_combo
= gtk_combo_box_text_new_with_entry();
7540 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
7541 compose
->savemsg_combo
= savemsg_combo
;
7542 gtk_widget_show(savemsg_combo
);
7544 if (prefs_common
.compose_save_to_history
)
7545 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo
),
7546 prefs_common
.compose_save_to_history
);
7547 gtk_table_attach(GTK_TABLE(table
), savemsg_combo
, 1, 2, rowcount
, rowcount
+ 1, GTK_FILL
|GTK_EXPAND
, GTK_SHRINK
, 0, 0);
7548 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), prefs_common
.savemsg
);
7549 g_signal_connect_after(G_OBJECT(savemsg_combo
), "grab_focus",
7550 G_CALLBACK(compose_grab_focus_cb
), compose
);
7551 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
7552 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
7553 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), TRUE
);
7555 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), FALSE
);
7556 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo
), TRUE
);
7557 folderidentifier
= folder_item_get_identifier(account_get_special_folder
7558 (compose
->account
, F_OUTBOX
));
7559 compose_set_save_to(compose
, folderidentifier
);
7560 g_free(folderidentifier
);
7563 savemsg_select
= gtkut_get_browse_file_btn(_("_Browse"));
7564 gtk_widget_show(savemsg_select
);
7565 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
7566 g_signal_connect(G_OBJECT(savemsg_select
), "clicked",
7567 G_CALLBACK(compose_savemsg_select_cb
),
7573 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
7578 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
, FALSE
,
7579 _("Select folder to save message to"));
7582 path
= folder_item_get_identifier(dest
);
7584 compose_set_save_to(compose
, path
);
7588 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
, gboolean wrap
,
7589 GdkAtom clip
, GtkTextIter
*insert_place
);
7592 static gboolean
text_clicked(GtkWidget
*text
, GdkEventButton
*event
,
7596 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
7598 if (event
->button
== 3) {
7600 GtkTextIter sel_start
, sel_end
;
7601 gboolean stuff_selected
;
7603 /* move the cursor to allow GtkAspell to check the word
7604 * under the mouse */
7605 if (event
->x
&& event
->y
) {
7606 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7607 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7609 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7612 GtkTextMark
*mark
= gtk_text_buffer_get_insert(buffer
);
7613 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
7616 stuff_selected
= gtk_text_buffer_get_selection_bounds(
7618 &sel_start
, &sel_end
);
7620 gtk_text_buffer_place_cursor (buffer
, &iter
);
7621 /* reselect stuff */
7623 && gtk_text_iter_in_range(&iter
, &sel_start
, &sel_end
)) {
7624 gtk_text_buffer_select_range(buffer
,
7625 &sel_start
, &sel_end
);
7627 return FALSE
; /* pass the event so that the right-click goes through */
7630 if (event
->button
== 2) {
7635 /* get the middle-click position to paste at the correct place */
7636 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text
),
7637 GTK_TEXT_WINDOW_TEXT
, event
->x
, event
->y
,
7639 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text
),
7642 entry_paste_clipboard(compose
, text
,
7643 prefs_common
.linewrap_pastes
,
7644 GDK_SELECTION_PRIMARY
, &iter
);
7652 static void compose_spell_menu_changed(void *data
)
7654 Compose
*compose
= (Compose
*)data
;
7656 GtkWidget
*menuitem
;
7657 GtkWidget
*parent_item
;
7658 GtkMenu
*menu
= GTK_MENU(gtk_menu_new());
7661 if (compose
->gtkaspell
== NULL
)
7664 parent_item
= gtk_ui_manager_get_widget(compose
->ui_manager
,
7665 "/Menu/Spelling/Options");
7667 /* setting the submenu removes /Spelling/Options from the factory
7668 * so we need to save it */
7670 if (parent_item
== NULL
) {
7671 parent_item
= compose
->aspell_options_menu
;
7672 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), NULL
);
7674 compose
->aspell_options_menu
= parent_item
;
7676 spell_menu
= gtkaspell_make_config_menu(compose
->gtkaspell
);
7678 spell_menu
= g_slist_reverse(spell_menu
);
7679 for (items
= spell_menu
;
7680 items
; items
= items
->next
) {
7681 menuitem
= GTK_WIDGET(GTK_MENU_ITEM(items
->data
));
7682 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu
), GTK_WIDGET(menuitem
));
7683 gtk_widget_show(GTK_WIDGET(menuitem
));
7685 g_slist_free(spell_menu
);
7687 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item
), GTK_WIDGET(menu
));
7688 gtk_widget_show(parent_item
);
7691 static void compose_dict_changed(void *data
)
7693 Compose
*compose
= (Compose
*) data
;
7695 if(!compose
->gtkaspell
)
7697 if(compose
->gtkaspell
->recheck_when_changing_dict
== FALSE
)
7700 gtkaspell_highlight_all(compose
->gtkaspell
);
7701 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose
->subject_entry
));
7705 static gboolean
compose_popup_menu(GtkWidget
*widget
, gpointer data
)
7707 Compose
*compose
= (Compose
*)data
;
7708 GdkEventButton event
;
7711 event
.time
= gtk_get_current_event_time();
7715 return text_clicked(compose
->text
, &event
, compose
);
7718 static gboolean compose_force_window_origin
= TRUE
;
7719 static Compose
*compose_create(PrefsAccount
*account
,
7728 GtkWidget
*handlebox
;
7730 GtkWidget
*notebook
;
7732 GtkWidget
*attach_hbox
;
7733 GtkWidget
*attach_lab1
;
7734 GtkWidget
*attach_lab2
;
7739 GtkWidget
*subject_hbox
;
7740 GtkWidget
*subject_frame
;
7741 GtkWidget
*subject_entry
;
7745 GtkWidget
*edit_vbox
;
7746 GtkWidget
*ruler_hbox
;
7748 GtkWidget
*scrolledwin
;
7750 GtkTextBuffer
*buffer
;
7751 GtkClipboard
*clipboard
;
7753 UndoMain
*undostruct
;
7755 GtkWidget
*popupmenu
;
7756 GtkWidget
*tmpl_menu
;
7757 GtkActionGroup
*action_group
= NULL
;
7760 GtkAspell
* gtkaspell
= NULL
;
7763 static GdkGeometry geometry
;
7765 cm_return_val_if_fail(account
!= NULL
, NULL
);
7767 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER_BG
],
7768 &default_header_bgcolor
);
7769 gtkut_convert_int_to_gdk_color(prefs_common
.color
[COL_DEFAULT_HEADER
],
7770 &default_header_color
);
7772 debug_print("Creating compose window...\n");
7773 compose
= g_new0(Compose
, 1);
7775 compose
->batch
= batch
;
7776 compose
->account
= account
;
7777 compose
->folder
= folder
;
7779 compose
->mutex
= cm_mutex_new();
7780 compose
->set_cursor_pos
= -1;
7782 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose");
7784 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
7785 gtk_widget_set_size_request(window
, prefs_common
.compose_width
,
7786 prefs_common
.compose_height
);
7788 if (!geometry
.max_width
) {
7789 geometry
.max_width
= gdk_screen_width();
7790 geometry
.max_height
= gdk_screen_height();
7793 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7794 &geometry
, GDK_HINT_MAX_SIZE
);
7795 if (!geometry
.min_width
) {
7796 geometry
.min_width
= 600;
7797 geometry
.min_height
= 440;
7799 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
7800 &geometry
, GDK_HINT_MIN_SIZE
);
7802 #ifndef GENERIC_UMPC
7803 if (compose_force_window_origin
)
7804 gtk_window_move(GTK_WINDOW(window
), prefs_common
.compose_x
,
7805 prefs_common
.compose_y
);
7807 g_signal_connect(G_OBJECT(window
), "delete_event",
7808 G_CALLBACK(compose_delete_cb
), compose
);
7809 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
7810 gtk_widget_realize(window
);
7812 gtkut_widget_set_composer_icon(window
);
7814 vbox
= gtk_vbox_new(FALSE
, 0);
7815 gtk_container_add(GTK_CONTAINER(window
), vbox
);
7817 compose
->ui_manager
= gtk_ui_manager_new();
7818 action_group
= cm_menu_create_action_group_full(compose
->ui_manager
,"Menu", compose_entries
,
7819 G_N_ELEMENTS(compose_entries
), (gpointer
)compose
);
7820 gtk_action_group_add_toggle_actions(action_group
, compose_toggle_entries
,
7821 G_N_ELEMENTS(compose_toggle_entries
), (gpointer
)compose
);
7822 gtk_action_group_add_radio_actions(action_group
, compose_radio_rm_entries
,
7823 G_N_ELEMENTS(compose_radio_rm_entries
), COMPOSE_REPLY
, G_CALLBACK(compose_reply_change_mode_cb
), (gpointer
)compose
);
7824 gtk_action_group_add_radio_actions(action_group
, compose_radio_prio_entries
,
7825 G_N_ELEMENTS(compose_radio_prio_entries
), PRIORITY_NORMAL
, G_CALLBACK(compose_set_priority_cb
), (gpointer
)compose
);
7826 gtk_action_group_add_radio_actions(action_group
, compose_radio_enc_entries
,
7827 G_N_ELEMENTS(compose_radio_enc_entries
), C_AUTO
, G_CALLBACK(compose_set_encoding_cb
), (gpointer
)compose
);
7829 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Menu", NULL
, GTK_UI_MANAGER_MENUBAR
)
7831 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU
)
7832 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU
)
7834 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU
)
7836 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU
)
7837 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU
)
7838 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU
)
7841 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM
)
7842 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM
)
7843 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7844 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM
)
7845 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM
)
7846 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM
)
7847 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM
)
7848 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7849 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM
)
7850 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7851 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM
)
7852 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR
)
7853 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM
)
7856 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM
)
7857 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM
)
7858 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7860 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM
)
7861 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM
)
7862 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM
)
7864 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU
)
7865 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM
)
7866 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM
)
7867 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM
)
7869 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM
)
7871 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU
)
7872 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM
)
7873 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM
)
7874 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM
)
7875 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM
)
7876 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM
)
7877 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM
)
7878 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM
)
7879 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM
)
7880 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM
)
7881 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM
)
7882 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM
)
7883 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM
)
7884 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM
)
7885 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM
)
7887 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7889 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM
)
7890 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM
)
7891 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM
)
7892 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM
)
7893 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM
)
7895 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR
)
7897 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM
)
7901 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM
)
7902 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM
)
7903 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM
)
7904 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM
)
7905 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR
)
7906 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU
)
7910 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU
)
7911 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM
)
7912 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM
)
7913 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM
)
7914 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM
)
7916 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7917 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU
)
7918 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
7919 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM
)
7920 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM
)
7923 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7924 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU
)
7925 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM
)
7926 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM
)
7927 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM
)
7928 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM
)
7929 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM
)
7931 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7932 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM
)
7933 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7934 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM
)
7935 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR
)
7937 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU
)
7939 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_AUTO
, "Options/Encoding/"CS_AUTO
, GTK_UI_MANAGER_MENUITEM
)
7940 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7941 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_US_ASCII
, "Options/Encoding/"CS_US_ASCII
, GTK_UI_MANAGER_MENUITEM
)
7942 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_UTF_8
, "Options/Encoding/"CS_UTF_8
, GTK_UI_MANAGER_MENUITEM
)
7943 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR
)
7945 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU
)
7946 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
)
7947 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
)
7948 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252
, "Options/Encoding/Western/"CS_WINDOWS_1252
, GTK_UI_MANAGER_MENUITEM
)
7950 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_2
, "Options/Encoding/"CS_ISO_8859_2
, GTK_UI_MANAGER_MENUITEM
)
7952 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU
)
7953 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
)
7954 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
)
7956 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_7
, "Options/Encoding/"CS_ISO_8859_7
, GTK_UI_MANAGER_MENUITEM
)
7958 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU
)
7959 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
)
7960 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255
, "Options/Encoding/Hebrew/"CS_WINDOWS_1255
, GTK_UI_MANAGER_MENUITEM
)
7962 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU
)
7963 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
)
7964 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256
, "Options/Encoding/Arabic/"CS_WINDOWS_1256
, GTK_UI_MANAGER_MENUITEM
)
7966 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", CS_ISO_8859_9
, "Options/Encoding/"CS_ISO_8859_9
, GTK_UI_MANAGER_MENUITEM
)
7968 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU
)
7969 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
)
7970 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R
, "Options/Encoding/Cyrillic/"CS_KOI8_R
, GTK_UI_MANAGER_MENUITEM
)
7971 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR
, "Options/Encoding/Cyrillic/"CS_MACCYR
, GTK_UI_MANAGER_MENUITEM
)
7972 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U
, "Options/Encoding/Cyrillic/"CS_KOI8_U
, GTK_UI_MANAGER_MENUITEM
)
7973 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251
, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251
, GTK_UI_MANAGER_MENUITEM
)
7975 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU
)
7976 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
)
7977 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
)
7978 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_EUC_JP
, "Options/Encoding/Japanese/"CS_EUC_JP
, GTK_UI_MANAGER_MENUITEM
)
7979 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS
, "Options/Encoding/Japanese/"CS_SHIFT_JIS
, GTK_UI_MANAGER_MENUITEM
)
7981 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU
)
7982 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB18030
, "Options/Encoding/Chinese/"CS_GB18030
, GTK_UI_MANAGER_MENUITEM
)
7983 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GB2312
, "Options/Encoding/Chinese/"CS_GB2312
, GTK_UI_MANAGER_MENUITEM
)
7984 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_GBK
, "Options/Encoding/Chinese/"CS_GBK
, GTK_UI_MANAGER_MENUITEM
)
7985 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_BIG5
, "Options/Encoding/Chinese/"CS_BIG5
, GTK_UI_MANAGER_MENUITEM
)
7986 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Chinese", CS_EUC_TW
, "Options/Encoding/Chinese/"CS_EUC_TW
, GTK_UI_MANAGER_MENUITEM
)
7988 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU
)
7989 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Korean", CS_EUC_KR
, "Options/Encoding/Korean/"CS_EUC_KR
, GTK_UI_MANAGER_MENUITEM
)
7990 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
)
7992 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU
)
7993 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_TIS_620
, "Options/Encoding/Thai/"CS_TIS_620
, GTK_UI_MANAGER_MENUITEM
)
7994 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874
, "Options/Encoding/Thai/"CS_WINDOWS_874
, GTK_UI_MANAGER_MENUITEM
)
7998 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM
)
7999 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM
)
8000 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU
)
8001 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8002 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU
)
8003 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM
)
8006 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM
)
8008 menubar
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu");
8009 gtk_widget_show_all(menubar
);
8011 gtk_window_add_accel_group(GTK_WINDOW(window
), gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8012 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
8014 if (prefs_common
.toolbar_detachable
) {
8015 handlebox
= gtk_handle_box_new();
8017 handlebox
= gtk_hbox_new(FALSE
, 0);
8019 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
8021 gtk_widget_realize(handlebox
);
8022 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
8025 vbox2
= gtk_vbox_new(FALSE
, 2);
8026 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
8027 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 0);
8030 notebook
= gtk_notebook_new();
8031 gtk_widget_show(notebook
);
8033 /* header labels and entries */
8034 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8035 compose_create_header(compose
),
8036 gtk_label_new_with_mnemonic(_("Hea_der")));
8037 /* attachment list */
8038 attach_hbox
= gtk_hbox_new(FALSE
, 0);
8039 gtk_widget_show(attach_hbox
);
8041 attach_lab1
= gtk_label_new_with_mnemonic(_("_Attachments"));
8042 gtk_widget_show(attach_lab1
);
8043 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab1
, TRUE
, TRUE
, 0);
8045 attach_lab2
= gtk_label_new("");
8046 gtk_widget_show(attach_lab2
);
8047 gtk_box_pack_start(GTK_BOX(attach_hbox
), attach_lab2
, FALSE
, FALSE
, 0);
8049 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8050 compose_create_attach(compose
),
8053 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
8054 compose_create_others(compose
),
8055 gtk_label_new_with_mnemonic(_("Othe_rs")));
8058 subject_hbox
= gtk_hbox_new(FALSE
, 0);
8059 gtk_widget_show(subject_hbox
);
8061 subject_frame
= gtk_frame_new(NULL
);
8062 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_NONE
);
8063 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, 0);
8064 gtk_widget_show(subject_frame
);
8066 subject
= gtk_hbox_new(FALSE
, HSPACING_NARROW
);
8067 gtk_container_set_border_width(GTK_CONTAINER(subject
), 0);
8068 gtk_widget_show(subject
);
8070 label
= gtk_label_new_with_mnemonic(_("S_ubject:"));
8071 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 0);
8072 gtk_widget_show(label
);
8075 subject_entry
= claws_spell_entry_new();
8077 subject_entry
= gtk_entry_new();
8079 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 0);
8080 g_signal_connect_after(G_OBJECT(subject_entry
), "grab_focus",
8081 G_CALLBACK(compose_grab_focus_cb
), compose
);
8082 gtk_label_set_mnemonic_widget(GTK_LABEL(label
), subject_entry
);
8083 gtk_widget_show(subject_entry
);
8084 compose
->subject_entry
= subject_entry
;
8085 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
8087 edit_vbox
= gtk_vbox_new(FALSE
, 0);
8089 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
8092 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
8093 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
8095 ruler
= gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL
);
8096 gtk_shruler_set_range(GTK_SHRULER(ruler
), 0.0, 100.0, 1.0);
8097 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
8101 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
8102 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
8103 GTK_POLICY_AUTOMATIC
,
8104 GTK_POLICY_AUTOMATIC
);
8105 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
8107 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
8109 text
= gtk_text_view_new();
8110 if (prefs_common
.show_compose_margin
) {
8111 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text
), 6);
8112 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text
), 6);
8114 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
8115 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD_CHAR
);
8116 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), TRUE
);
8117 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8118 gtk_text_buffer_add_selection_clipboard(buffer
, clipboard
);
8120 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
8121 g_signal_connect_after(G_OBJECT(text
), "size_allocate",
8122 G_CALLBACK(compose_edit_size_alloc
),
8124 g_signal_connect(G_OBJECT(buffer
), "changed",
8125 G_CALLBACK(compose_changed_cb
), compose
);
8126 g_signal_connect(G_OBJECT(text
), "grab_focus",
8127 G_CALLBACK(compose_grab_focus_cb
), compose
);
8128 g_signal_connect(G_OBJECT(buffer
), "insert_text",
8129 G_CALLBACK(text_inserted
), compose
);
8130 g_signal_connect(G_OBJECT(text
), "button_press_event",
8131 G_CALLBACK(text_clicked
), compose
);
8132 g_signal_connect(G_OBJECT(text
), "popup-menu",
8133 G_CALLBACK(compose_popup_menu
), compose
);
8134 g_signal_connect(G_OBJECT(subject_entry
), "changed",
8135 G_CALLBACK(compose_changed_cb
), compose
);
8136 g_signal_connect(G_OBJECT(subject_entry
), "activate",
8137 G_CALLBACK(compose_subject_entry_activated
), compose
);
8140 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
,
8141 sizeof(compose_mime_types
)/sizeof(compose_mime_types
[0]),
8142 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
8143 g_signal_connect(G_OBJECT(text
), "drag_data_received",
8144 G_CALLBACK(compose_insert_drag_received_cb
),
8146 g_signal_connect(G_OBJECT(text
), "drag-drop",
8147 G_CALLBACK(compose_drag_drop
),
8149 g_signal_connect(G_OBJECT(text
), "key-press-event",
8150 G_CALLBACK(completion_set_focus_to_subject
),
8152 gtk_widget_show_all(vbox
);
8154 /* pane between attach clist and text */
8155 paned
= gtk_vpaned_new();
8156 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
8157 gtk_paned_pack1(GTK_PANED(paned
), notebook
, FALSE
, FALSE
);
8158 gtk_paned_pack2(GTK_PANED(paned
), edit_vbox
, TRUE
, FALSE
);
8159 gtk_paned_set_position(GTK_PANED(paned
), prefs_common
.compose_notebook_height
);
8160 g_signal_connect(G_OBJECT(notebook
), "size_allocate",
8161 G_CALLBACK(compose_notebook_size_alloc
), paned
);
8163 gtk_widget_show_all(paned
);
8166 if (prefs_common
.textfont
) {
8167 PangoFontDescription
*font_desc
;
8169 font_desc
= pango_font_description_from_string
8170 (prefs_common
.textfont
);
8172 gtk_widget_modify_font(text
, font_desc
);
8173 pango_font_description_free(font_desc
);
8177 gtk_action_group_add_actions(action_group
, compose_popup_entries
,
8178 G_N_ELEMENTS(compose_popup_entries
), (gpointer
)compose
);
8179 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/", "Popup", NULL
, GTK_UI_MANAGER_MENUBAR
)
8180 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU
)
8181 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM
)
8182 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM
)
8183 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR
)
8184 MENUITEM_ADDUI_MANAGER(compose
->ui_manager
, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM
)
8186 popupmenu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose
->ui_manager
, "/Popup/Compose")));
8188 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
8189 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
8190 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/RemoveReferences", FALSE
);
8192 tmpl_menu
= gtk_ui_manager_get_widget(compose
->ui_manager
, "/Menu/Tools/Template");
8194 undostruct
= undo_init(text
);
8195 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
8198 address_completion_start(window
);
8200 compose
->window
= window
;
8201 compose
->vbox
= vbox
;
8202 compose
->menubar
= menubar
;
8203 compose
->handlebox
= handlebox
;
8205 compose
->vbox2
= vbox2
;
8207 compose
->paned
= paned
;
8209 compose
->attach_label
= attach_lab2
;
8211 compose
->notebook
= notebook
;
8212 compose
->edit_vbox
= edit_vbox
;
8213 compose
->ruler_hbox
= ruler_hbox
;
8214 compose
->ruler
= ruler
;
8215 compose
->scrolledwin
= scrolledwin
;
8216 compose
->text
= text
;
8218 compose
->focused_editable
= NULL
;
8220 compose
->popupmenu
= popupmenu
;
8222 compose
->tmpl_menu
= tmpl_menu
;
8224 compose
->mode
= mode
;
8225 compose
->rmode
= mode
;
8227 compose
->targetinfo
= NULL
;
8228 compose
->replyinfo
= NULL
;
8229 compose
->fwdinfo
= NULL
;
8231 compose
->email_hashtable
= g_hash_table_new_full(g_str_hash
,
8232 g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
8234 compose
->replyto
= NULL
;
8236 compose
->bcc
= NULL
;
8237 compose
->followup_to
= NULL
;
8239 compose
->ml_post
= NULL
;
8241 compose
->inreplyto
= NULL
;
8242 compose
->references
= NULL
;
8243 compose
->msgid
= NULL
;
8244 compose
->boundary
= NULL
;
8246 compose
->autowrap
= prefs_common
.autowrap
;
8247 compose
->autoindent
= prefs_common
.auto_indent
;
8248 compose
->use_signing
= FALSE
;
8249 compose
->use_encryption
= FALSE
;
8250 compose
->privacy_system
= NULL
;
8251 compose
->encdata
= NULL
;
8253 compose
->modified
= FALSE
;
8255 compose
->return_receipt
= FALSE
;
8257 compose
->to_list
= NULL
;
8258 compose
->newsgroup_list
= NULL
;
8260 compose
->undostruct
= undostruct
;
8262 compose
->sig_str
= NULL
;
8264 compose
->exteditor_file
= NULL
;
8265 compose
->exteditor_pid
= -1;
8266 compose
->exteditor_tag
= -1;
8267 compose
->exteditor_socket
= NULL
;
8268 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
; /* inhibit auto-drafting while loading */
8270 compose
->folder_update_callback_id
=
8271 hooks_register_hook(FOLDER_UPDATE_HOOKLIST
,
8272 compose_update_folder_hook
,
8273 (gpointer
) compose
);
8276 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
8277 if (mode
!= COMPOSE_REDIRECT
) {
8278 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
8279 strcmp(prefs_common
.dictionary
, "")) {
8280 gtkaspell
= gtkaspell_new(prefs_common
.dictionary
,
8281 prefs_common
.alt_dictionary
,
8282 conv_get_locale_charset_str(),
8283 prefs_common
.color
[COL_MISSPELLED
],
8284 prefs_common
.check_while_typing
,
8285 prefs_common
.recheck_when_changing_dict
,
8286 prefs_common
.use_alternate
,
8287 prefs_common
.use_both_dicts
,
8288 GTK_TEXT_VIEW(text
),
8289 GTK_WINDOW(compose
->window
),
8290 compose_dict_changed
,
8291 compose_spell_menu_changed
,
8294 alertpanel_error(_("Spell checker could not "
8296 gtkaspell_checkers_strerror());
8297 gtkaspell_checkers_reset_error();
8299 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", TRUE
);
8303 compose
->gtkaspell
= gtkaspell
;
8304 compose_spell_menu_changed(compose
);
8305 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry
), gtkaspell
);
8308 compose_select_account(compose
, account
, TRUE
);
8310 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoWrap", prefs_common
.autowrap
);
8311 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Edit/AutoIndent", prefs_common
.auto_indent
);
8313 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
8314 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8316 if (account
->set_autobcc
&& account
->auto_bcc
&& mode
!= COMPOSE_REEDIT
)
8317 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8319 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
8320 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8322 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/ReplyMode", compose
->mode
== COMPOSE_REPLY
);
8323 if (account
->protocol
!= A_NNTP
)
8324 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8325 prefs_common_translated_header_name("To:"));
8327 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose
->header_last
->combo
)))),
8328 prefs_common_translated_header_name("Newsgroups:"));
8330 #ifndef USE_ALT_ADDRBOOK
8331 addressbook_set_target_compose(compose
);
8333 if (mode
!= COMPOSE_REDIRECT
)
8334 compose_set_template_menu(compose
);
8336 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools/Template", FALSE
);
8339 compose_list
= g_list_append(compose_list
, compose
);
8341 if (!prefs_common
.show_ruler
)
8342 gtk_widget_hide(ruler_hbox
);
8344 cm_toggle_menu_set_active_full(compose
->ui_manager
, "Menu/Tools/ShowRuler", prefs_common
.show_ruler
);
8347 compose
->priority
= PRIORITY_NORMAL
;
8348 compose_update_priority_menu_item(compose
);
8350 compose_set_out_encoding(compose
);
8353 compose_update_actions_menu(compose
);
8355 /* Privacy Systems menu */
8356 compose_update_privacy_systems_menu(compose
);
8357 compose_activate_privacy_system(compose
, account
, TRUE
);
8359 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
8361 gtk_widget_realize(window
);
8363 gtk_widget_show(window
);
8369 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
8374 GtkWidget
*optmenubox
;
8375 GtkWidget
*fromlabel
;
8378 GtkWidget
*from_name
= NULL
;
8380 gint num
= 0, def_menu
= 0;
8382 accounts
= account_get_list();
8383 cm_return_val_if_fail(accounts
!= NULL
, NULL
);
8385 optmenubox
= gtk_event_box_new();
8386 optmenu
= gtkut_sc_combobox_create(optmenubox
, FALSE
);
8387 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
8389 hbox
= gtk_hbox_new(FALSE
, 4);
8390 from_name
= gtk_entry_new();
8392 g_signal_connect_after(G_OBJECT(from_name
), "grab_focus",
8393 G_CALLBACK(compose_grab_focus_cb
), compose
);
8394 g_signal_connect_after(G_OBJECT(from_name
), "activate",
8395 G_CALLBACK(from_name_activate_cb
), optmenu
);
8397 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
8398 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
8399 gchar
*name
, *from
= NULL
;
8401 if (ac
== compose
->account
) def_menu
= num
;
8403 name
= g_markup_printf_escaped("<i>%s</i>",
8406 if (ac
== compose
->account
) {
8407 if (ac
->name
&& *ac
->name
) {
8409 QUOTE_IF_REQUIRED_NORMAL(buf
, ac
->name
, return NULL
);
8410 from
= g_strdup_printf("%s <%s>",
8412 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8414 from
= g_strdup_printf("%s",
8416 gtk_entry_set_text(GTK_ENTRY(from_name
), from
);
8418 if (cur_account
!= compose
->account
) {
8419 gtk_widget_modify_base(
8420 GTK_WIDGET(from_name
),
8421 GTK_STATE_NORMAL
, &default_header_bgcolor
);
8422 gtk_widget_modify_text(
8423 GTK_WIDGET(from_name
),
8424 GTK_STATE_NORMAL
, &default_header_color
);
8427 COMBOBOX_ADD(menu
, name
, ac
->account_id
);
8432 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), def_menu
);
8434 g_signal_connect(G_OBJECT(optmenu
), "changed",
8435 G_CALLBACK(account_activated
),
8437 g_signal_connect(G_OBJECT(from_name
), "populate-popup",
8438 G_CALLBACK(compose_entry_popup_extend
),
8441 fromlabel
= gtk_label_new_with_mnemonic(_("_From:"));
8442 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel
), from_name
);
8444 gtk_box_pack_start(GTK_BOX(hbox
), fromlabel
, FALSE
, FALSE
, 4);
8445 gtk_box_pack_start(GTK_BOX(hbox
), optmenubox
, FALSE
, FALSE
, 0);
8446 gtk_box_pack_start(GTK_BOX(hbox
), from_name
, TRUE
, TRUE
, 0);
8448 /* Putting only the GtkEntry into focus chain of parent hbox causes
8449 * the account selector combobox next to it to be unreachable when
8450 * navigating widgets in GtkTable with up/down arrow keys.
8451 * Note: gtk_widget_set_can_focus() was not enough. */
8453 l
= g_list_prepend(l
, from_name
);
8454 gtk_container_set_focus_chain(GTK_CONTAINER(hbox
), l
);
8457 CLAWS_SET_TIP(optmenubox
,
8458 _("Account to use for this email"));
8459 CLAWS_SET_TIP(from_name
,
8460 _("Sender address to be used"));
8462 compose
->account_combo
= optmenu
;
8463 compose
->from_name
= from_name
;
8468 static void compose_set_priority_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8470 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8471 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8472 Compose
*compose
= (Compose
*) data
;
8474 compose
->priority
= value
;
8478 static void compose_reply_change_mode(Compose
*compose
,
8481 gboolean was_modified
= compose
->modified
;
8483 gboolean all
= FALSE
, ml
= FALSE
, sender
= FALSE
, followup
= FALSE
;
8485 cm_return_if_fail(compose
->replyinfo
!= NULL
);
8487 if (action
== COMPOSE_REPLY
&& prefs_common
.default_reply_list
)
8489 if (action
== COMPOSE_REPLY
&& compose
->rmode
== COMPOSE_FOLLOWUP_AND_REPLY_TO
)
8491 if (action
== COMPOSE_REPLY_TO_ALL
)
8493 if (action
== COMPOSE_REPLY_TO_SENDER
)
8495 if (action
== COMPOSE_REPLY_TO_LIST
)
8498 compose_remove_header_entries(compose
);
8499 compose_reply_set_entry(compose
, compose
->replyinfo
, all
, ml
, sender
, followup
);
8500 if (compose
->account
->set_autocc
&& compose
->account
->auto_cc
)
8501 compose_entry_append(compose
, compose
->account
->auto_cc
, COMPOSE_CC
, PREF_ACCOUNT
);
8503 if (compose
->account
->set_autobcc
&& compose
->account
->auto_bcc
)
8504 compose_entry_append(compose
, compose
->account
->auto_bcc
, COMPOSE_BCC
, PREF_ACCOUNT
);
8506 if (compose
->account
->set_autoreplyto
&& compose
->account
->auto_replyto
)
8507 compose_entry_append(compose
, compose
->account
->auto_replyto
, COMPOSE_REPLYTO
, PREF_ACCOUNT
);
8508 compose_show_first_last_header(compose
, TRUE
);
8509 compose
->modified
= was_modified
;
8510 compose_set_title(compose
);
8513 static void compose_reply_change_mode_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
8515 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
8516 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
8517 Compose
*compose
= (Compose
*) data
;
8520 compose_reply_change_mode(compose
, value
);
8523 static void compose_update_priority_menu_item(Compose
* compose
)
8525 GtkWidget
*menuitem
= NULL
;
8526 switch (compose
->priority
) {
8527 case PRIORITY_HIGHEST
:
8528 menuitem
= gtk_ui_manager_get_widget
8529 (compose
->ui_manager
, "/Menu/Options/Priority/Highest");
8532 menuitem
= gtk_ui_manager_get_widget
8533 (compose
->ui_manager
, "/Menu/Options/Priority/High");
8535 case PRIORITY_NORMAL
:
8536 menuitem
= gtk_ui_manager_get_widget
8537 (compose
->ui_manager
, "/Menu/Options/Priority/Normal");
8540 menuitem
= gtk_ui_manager_get_widget
8541 (compose
->ui_manager
, "/Menu/Options/Priority/Low");
8543 case PRIORITY_LOWEST
:
8544 menuitem
= gtk_ui_manager_get_widget
8545 (compose
->ui_manager
, "/Menu/Options/Priority/Lowest");
8548 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8551 static void compose_set_privacy_system_cb(GtkWidget
*widget
, gpointer data
)
8553 Compose
*compose
= (Compose
*) data
;
8555 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8557 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget
));
8559 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
8562 systemid
= g_object_get_data(G_OBJECT(widget
), "privacy_system");
8563 g_free(compose
->privacy_system
);
8564 compose
->privacy_system
= NULL
;
8565 g_free(compose
->encdata
);
8566 compose
->encdata
= NULL
;
8567 if (systemid
!= NULL
) {
8568 compose
->privacy_system
= g_strdup(systemid
);
8570 can_sign
= privacy_system_can_sign(systemid
);
8571 can_encrypt
= privacy_system_can_encrypt(systemid
);
8574 debug_print("activated privacy system: %s\n", systemid
!= NULL
? systemid
: "None");
8576 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8577 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8578 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8579 gtk_widget_set_sensitive(
8580 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8582 gtk_toggle_tool_button_set_active(
8583 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
),
8584 can_sign
? compose
->use_signing
: FALSE
);
8586 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8587 gtk_widget_set_sensitive(
8588 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8590 gtk_toggle_tool_button_set_active(
8591 GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
),
8592 can_encrypt
? compose
->use_encryption
: FALSE
);
8596 static void compose_update_privacy_system_menu_item(Compose
* compose
, gboolean warn
)
8598 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8599 GtkWidget
*menuitem
= NULL
;
8600 GList
*children
, *amenu
;
8601 gboolean can_sign
= FALSE
, can_encrypt
= FALSE
;
8602 gboolean found
= FALSE
;
8604 if (compose
->privacy_system
!= NULL
) {
8606 menuitem
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8607 gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
)));
8608 cm_return_if_fail(menuitem
!= NULL
);
8610 children
= gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem
)));
8613 while (amenu
!= NULL
) {
8614 systemid
= g_object_get_data(G_OBJECT(amenu
->data
), "privacy_system");
8615 if (systemid
!= NULL
) {
8616 if (strcmp(systemid
, compose
->privacy_system
) == 0 &&
8617 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8618 menuitem
= GTK_WIDGET(amenu
->data
);
8620 can_sign
= privacy_system_can_sign(systemid
);
8621 can_encrypt
= privacy_system_can_encrypt(systemid
);
8625 } else if (strlen(compose
->privacy_system
) == 0 &&
8626 GTK_IS_CHECK_MENU_ITEM(amenu
->data
)) {
8627 menuitem
= GTK_WIDGET(amenu
->data
);
8630 can_encrypt
= FALSE
;
8635 amenu
= amenu
->next
;
8637 g_list_free(children
);
8638 if (menuitem
!= NULL
)
8639 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
8641 if (warn
&& !found
&& strlen(compose
->privacy_system
)) {
8642 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8643 "will not be able to sign or encrypt this message."),
8644 compose
->privacy_system
);
8648 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Sign", can_sign
);
8649 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options/Encrypt", can_encrypt
);
8650 if (compose
->toolbar
->privacy_sign_btn
!= NULL
) {
8651 gtk_widget_set_sensitive(
8652 GTK_WIDGET(compose
->toolbar
->privacy_sign_btn
),
8655 if (compose
->toolbar
->privacy_encrypt_btn
!= NULL
) {
8656 gtk_widget_set_sensitive(
8657 GTK_WIDGET(compose
->toolbar
->privacy_encrypt_btn
),
8662 static void compose_set_out_encoding(Compose
*compose
)
8664 CharSet out_encoding
;
8665 const gchar
*branch
= NULL
;
8666 out_encoding
= conv_get_charset_from_str(prefs_common
.outgoing_charset
);
8668 switch(out_encoding
) {
8669 case C_AUTO
: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8670 case C_US_ASCII
: branch
= "Menu/Options/Encoding/" CS_US_ASCII
; break;
8671 case C_UTF_8
: branch
= "Menu/Options/Encoding/" CS_UTF_8
; break;
8672 case C_ISO_8859_2
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_2
; break;
8673 case C_ISO_8859_7
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_7
; break;
8674 case C_ISO_8859_9
: branch
= "Menu/Options/Encoding/" CS_ISO_8859_9
; break;
8675 case C_ISO_8859_1
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_1
; break;
8676 case C_ISO_8859_15
: branch
= "Menu/Options/Encoding/Western/" CS_ISO_8859_15
; break;
8677 case C_WINDOWS_1252
: branch
= "Menu/Options/Encoding/Western/" CS_WINDOWS_1252
; break;
8678 case C_ISO_8859_13
: branch
= "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13
; break;
8679 case C_ISO_8859_4
: branch
= "Menu/Options/Encoding/Baltic" CS_ISO_8859_4
; break;
8680 case C_ISO_8859_8
: branch
= "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8
; break;
8681 case C_WINDOWS_1255
: branch
= "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255
; break;
8682 case C_ISO_8859_6
: branch
= "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6
; break;
8683 case C_WINDOWS_1256
: branch
= "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256
; break;
8684 case C_ISO_8859_5
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5
; break;
8685 case C_KOI8_R
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R
; break;
8686 case C_MACCYR
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_MACCYR
; break;
8687 case C_KOI8_U
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U
; break;
8688 case C_WINDOWS_1251
: branch
= "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251
; break;
8689 case C_ISO_2022_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP
; break;
8690 case C_ISO_2022_JP_2
: branch
= "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2
; break;
8691 case C_EUC_JP
: branch
= "Menu/Options/Encoding/Japanese/" CS_EUC_JP
; break;
8692 case C_SHIFT_JIS
: branch
= "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS
; break;
8693 case C_GB18030
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB18030
; break;
8694 case C_GB2312
: branch
= "Menu/Options/Encoding/Chinese/" CS_GB2312
; break;
8695 case C_GBK
: branch
= "Menu/Options/Encoding/Chinese/" CS_GBK
; break;
8696 case C_BIG5
: branch
= "Menu/Options/Encoding/Chinese/" CS_BIG5
; break;
8697 case C_EUC_TW
: branch
= "Menu/Options/Encoding/Chinese/" CS_EUC_TW
; break;
8698 case C_EUC_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_EUC_KR
; break;
8699 case C_ISO_2022_KR
: branch
= "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR
; break;
8700 case C_TIS_620
: branch
= "Menu/Options/Encoding/Thai/" CS_TIS_620
; break;
8701 case C_WINDOWS_874
: branch
= "Menu/Options/Encoding/Thai/" CS_WINDOWS_874
; break;
8702 default: branch
= "Menu/Options/Encoding/" CS_AUTO
; break;
8704 cm_toggle_menu_set_active_full(compose
->ui_manager
, (gchar
*)branch
, TRUE
);
8707 static void compose_set_template_menu(Compose
*compose
)
8709 GSList
*tmpl_list
, *cur
;
8713 tmpl_list
= template_get_config();
8715 menu
= gtk_menu_new();
8717 gtk_menu_set_accel_group (GTK_MENU (menu
),
8718 gtk_ui_manager_get_accel_group(compose
->ui_manager
));
8719 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
8720 Template
*tmpl
= (Template
*)cur
->data
;
8721 gchar
*accel_path
= NULL
;
8722 item
= gtk_menu_item_new_with_label(tmpl
->name
);
8723 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
8724 g_signal_connect(G_OBJECT(item
), "activate",
8725 G_CALLBACK(compose_template_activate_cb
),
8727 g_object_set_data(G_OBJECT(item
), "template", tmpl
);
8728 gtk_widget_show(item
);
8729 accel_path
= g_strconcat("<ComposeTemplates>" , "/", tmpl
->name
, NULL
);
8730 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item
), accel_path
);
8734 gtk_widget_show(menu
);
8735 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
8738 void compose_update_actions_menu(Compose
*compose
)
8740 action_update_compose_menu(compose
->ui_manager
, "/Menu/Tools/Actions", compose
);
8743 static void compose_update_privacy_systems_menu(Compose
*compose
)
8745 static gchar
*branch_path
= "/Menu/Options/PrivacySystem";
8746 GSList
*systems
, *cur
;
8748 GtkWidget
*system_none
;
8750 GtkWidget
*privacy_menuitem
= gtk_ui_manager_get_widget(compose
->ui_manager
, branch_path
);
8751 GtkWidget
*privacy_menu
= gtk_menu_new();
8753 system_none
= gtk_radio_menu_item_new_with_mnemonic(NULL
, _("_None"));
8754 g_object_set_data_full(G_OBJECT(system_none
), "privacy_system", NULL
, NULL
);
8756 g_signal_connect(G_OBJECT(system_none
), "activate",
8757 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8759 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), system_none
);
8760 gtk_widget_show(system_none
);
8762 systems
= privacy_get_system_ids();
8763 for (cur
= systems
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
8764 gchar
*systemid
= cur
->data
;
8766 group
= gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none
));
8767 widget
= gtk_radio_menu_item_new_with_label(group
,
8768 privacy_system_get_name(systemid
));
8769 g_object_set_data_full(G_OBJECT(widget
), "privacy_system",
8770 g_strdup(systemid
), g_free
);
8771 g_signal_connect(G_OBJECT(widget
), "activate",
8772 G_CALLBACK(compose_set_privacy_system_cb
), compose
);
8774 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu
), widget
);
8775 gtk_widget_show(widget
);
8778 g_slist_free(systems
);
8779 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem
), privacy_menu
);
8780 gtk_widget_show_all(privacy_menu
);
8781 gtk_widget_show_all(privacy_menuitem
);
8784 void compose_reflect_prefs_all(void)
8789 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8790 compose
= (Compose
*)cur
->data
;
8791 compose_set_template_menu(compose
);
8795 void compose_reflect_prefs_pixmap_theme(void)
8800 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
8801 compose
= (Compose
*)cur
->data
;
8802 toolbar_update(TOOLBAR_COMPOSE
, compose
);
8806 static const gchar
*compose_quote_char_from_context(Compose
*compose
)
8808 const gchar
*qmark
= NULL
;
8810 cm_return_val_if_fail(compose
!= NULL
, NULL
);
8812 switch (compose
->mode
) {
8813 /* use forward-specific quote char */
8814 case COMPOSE_FORWARD
:
8815 case COMPOSE_FORWARD_AS_ATTACH
:
8816 case COMPOSE_FORWARD_INLINE
:
8817 if (compose
->folder
&& compose
->folder
->prefs
&&
8818 compose
->folder
->prefs
->forward_with_format
)
8819 qmark
= compose
->folder
->prefs
->forward_quotemark
;
8820 else if (compose
->account
->forward_with_format
)
8821 qmark
= compose
->account
->forward_quotemark
;
8823 qmark
= prefs_common
.fw_quotemark
;
8826 /* use reply-specific quote char in all other modes */
8828 if (compose
->folder
&& compose
->folder
->prefs
&&
8829 compose
->folder
->prefs
->reply_with_format
)
8830 qmark
= compose
->folder
->prefs
->reply_quotemark
;
8831 else if (compose
->account
->reply_with_format
)
8832 qmark
= compose
->account
->reply_quotemark
;
8834 qmark
= prefs_common
.quotemark
;
8838 if (qmark
== NULL
|| *qmark
== '\0')
8844 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
8848 GtkTextBuffer
*buffer
;
8852 gchar
*parsed_str
= NULL
;
8853 gint cursor_pos
= 0;
8854 const gchar
*err_msg
= _("The body of the template has an error at line %d.");
8857 /* process the body */
8859 text
= GTK_TEXT_VIEW(compose
->text
);
8860 buffer
= gtk_text_view_get_buffer(text
);
8863 qmark
= compose_quote_char_from_context(compose
);
8865 if (compose
->replyinfo
!= NULL
) {
8868 gtk_text_buffer_set_text(buffer
, "", -1);
8869 mark
= gtk_text_buffer_get_insert(buffer
);
8870 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8872 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
8873 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8875 } else if (compose
->fwdinfo
!= NULL
) {
8878 gtk_text_buffer_set_text(buffer
, "", -1);
8879 mark
= gtk_text_buffer_get_insert(buffer
);
8880 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8882 parsed_str
= compose_quote_fmt(compose
, compose
->fwdinfo
,
8883 tmpl
->value
, qmark
, NULL
, FALSE
, FALSE
, err_msg
);
8886 MsgInfo
* dummyinfo
= compose_msginfo_new_from_compose(compose
);
8888 GtkTextIter start
, end
;
8891 gtk_text_buffer_get_start_iter(buffer
, &start
);
8892 gtk_text_buffer_get_iter_at_offset(buffer
, &end
, -1);
8893 tmp
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
8895 /* clear the buffer now */
8897 gtk_text_buffer_set_text(buffer
, "", -1);
8899 parsed_str
= compose_quote_fmt(compose
, dummyinfo
,
8900 tmpl
->value
, qmark
, tmp
, FALSE
, FALSE
, err_msg
);
8901 procmsg_msginfo_free( &dummyinfo
);
8907 gtk_text_buffer_set_text(buffer
, "", -1);
8908 mark
= gtk_text_buffer_get_insert(buffer
);
8909 gtk_text_buffer_get_iter_at_mark(buffer
, &iter
, mark
);
8912 if (replace
&& parsed_str
&& compose
->account
->auto_sig
)
8913 compose_insert_sig(compose
, FALSE
);
8915 if (replace
&& parsed_str
) {
8916 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8917 gtk_text_buffer_place_cursor(buffer
, &iter
);
8921 cursor_pos
= quote_fmt_get_cursor_pos();
8922 compose
->set_cursor_pos
= cursor_pos
;
8923 if (cursor_pos
== -1)
8925 gtk_text_buffer_get_start_iter(buffer
, &iter
);
8926 gtk_text_buffer_get_iter_at_offset(buffer
, &iter
, cursor_pos
);
8927 gtk_text_buffer_place_cursor(buffer
, &iter
);
8930 /* process the other fields */
8932 compose_attach_from_list(compose
, quote_fmt_get_attachments_list(), FALSE
);
8933 compose_template_apply_fields(compose
, tmpl
);
8934 quote_fmt_reset_vartable();
8935 quote_fmtlex_destroy();
8937 compose_changed_cb(NULL
, compose
);
8940 if (compose
->gtkaspell
&& compose
->gtkaspell
->check_while_typing
)
8941 gtkaspell_highlight_all(compose
->gtkaspell
);
8945 static void compose_template_apply_fields_error(const gchar
*header
)
8950 tr
= g_strdup(C_("'%s' stands for a header name",
8951 "Template '%s' format error."));
8952 text
= g_strdup_printf(tr
, prefs_common_translated_header_name(header
));
8953 alertpanel_error("%s", text
);
8959 static void compose_template_apply_fields(Compose
*compose
, Template
*tmpl
)
8961 MsgInfo
* dummyinfo
= NULL
;
8962 MsgInfo
*msginfo
= NULL
;
8965 if (compose
->replyinfo
!= NULL
)
8966 msginfo
= compose
->replyinfo
;
8967 else if (compose
->fwdinfo
!= NULL
)
8968 msginfo
= compose
->fwdinfo
;
8970 dummyinfo
= compose_msginfo_new_from_compose(compose
);
8971 msginfo
= dummyinfo
;
8974 if (tmpl
->from
&& *tmpl
->from
!= '\0') {
8976 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8977 compose
->gtkaspell
);
8979 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
8981 quote_fmt_scan_string(tmpl
->from
);
8984 buf
= quote_fmt_get_buffer();
8986 compose_template_apply_fields_error("From");
8988 gtk_entry_set_text(GTK_ENTRY(compose
->from_name
), buf
);
8991 quote_fmt_reset_vartable();
8992 quote_fmtlex_destroy();
8995 if (tmpl
->to
&& *tmpl
->to
!= '\0') {
8997 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
8998 compose
->gtkaspell
);
9000 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9002 quote_fmt_scan_string(tmpl
->to
);
9005 buf
= quote_fmt_get_buffer();
9007 compose_template_apply_fields_error("To");
9009 compose_entry_append(compose
, buf
, COMPOSE_TO
, PREF_TEMPLATE
);
9012 quote_fmt_reset_vartable();
9013 quote_fmtlex_destroy();
9016 if (tmpl
->cc
&& *tmpl
->cc
!= '\0') {
9018 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9019 compose
->gtkaspell
);
9021 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9023 quote_fmt_scan_string(tmpl
->cc
);
9026 buf
= quote_fmt_get_buffer();
9028 compose_template_apply_fields_error("Cc");
9030 compose_entry_append(compose
, buf
, COMPOSE_CC
, PREF_TEMPLATE
);
9033 quote_fmt_reset_vartable();
9034 quote_fmtlex_destroy();
9037 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0') {
9039 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9040 compose
->gtkaspell
);
9042 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9044 quote_fmt_scan_string(tmpl
->bcc
);
9047 buf
= quote_fmt_get_buffer();
9049 compose_template_apply_fields_error("Bcc");
9051 compose_entry_append(compose
, buf
, COMPOSE_BCC
, PREF_TEMPLATE
);
9054 quote_fmt_reset_vartable();
9055 quote_fmtlex_destroy();
9058 if (tmpl
->replyto
&& *tmpl
->replyto
!= '\0') {
9060 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9061 compose
->gtkaspell
);
9063 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9065 quote_fmt_scan_string(tmpl
->replyto
);
9068 buf
= quote_fmt_get_buffer();
9070 compose_template_apply_fields_error("Reply-To");
9072 compose_entry_append(compose
, buf
, COMPOSE_REPLYTO
, PREF_TEMPLATE
);
9075 quote_fmt_reset_vartable();
9076 quote_fmtlex_destroy();
9079 /* process the subject */
9080 if (tmpl
->subject
&& *tmpl
->subject
!= '\0') {
9082 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
,
9083 compose
->gtkaspell
);
9085 quote_fmt_init(msginfo
, NULL
, NULL
, FALSE
, compose
->account
, FALSE
);
9087 quote_fmt_scan_string(tmpl
->subject
);
9090 buf
= quote_fmt_get_buffer();
9092 compose_template_apply_fields_error("Subject");
9094 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf
);
9097 quote_fmt_reset_vartable();
9098 quote_fmtlex_destroy();
9101 procmsg_msginfo_free( &dummyinfo
);
9104 static void compose_destroy(Compose
*compose
)
9106 GtkAllocation allocation
;
9107 GtkTextBuffer
*buffer
;
9108 GtkClipboard
*clipboard
;
9110 compose_list
= g_list_remove(compose_list
, compose
);
9113 gboolean enable
= TRUE
;
9114 g_slist_foreach(compose
->passworded_ldap_servers
,
9115 _ldap_srv_func
, &enable
);
9116 g_slist_free(compose
->passworded_ldap_servers
);
9119 if (compose
->updating
) {
9120 debug_print("danger, not destroying anything now\n");
9121 compose
->deferred_destroy
= TRUE
;
9125 /* NOTE: address_completion_end() does nothing with the window
9126 * however this may change. */
9127 address_completion_end(compose
->window
);
9129 slist_free_strings_full(compose
->to_list
);
9130 slist_free_strings_full(compose
->newsgroup_list
);
9131 slist_free_strings_full(compose
->header_list
);
9133 slist_free_strings_full(extra_headers
);
9134 extra_headers
= NULL
;
9136 compose
->header_list
= compose
->newsgroup_list
= compose
->to_list
= NULL
;
9138 g_hash_table_destroy(compose
->email_hashtable
);
9140 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
,
9141 compose
->folder_update_callback_id
);
9143 procmsg_msginfo_free(&(compose
->targetinfo
));
9144 procmsg_msginfo_free(&(compose
->replyinfo
));
9145 procmsg_msginfo_free(&(compose
->fwdinfo
));
9147 g_free(compose
->replyto
);
9148 g_free(compose
->cc
);
9149 g_free(compose
->bcc
);
9150 g_free(compose
->newsgroups
);
9151 g_free(compose
->followup_to
);
9153 g_free(compose
->ml_post
);
9155 g_free(compose
->inreplyto
);
9156 g_free(compose
->references
);
9157 g_free(compose
->msgid
);
9158 g_free(compose
->boundary
);
9160 g_free(compose
->redirect_filename
);
9161 if (compose
->undostruct
)
9162 undo_destroy(compose
->undostruct
);
9164 g_free(compose
->sig_str
);
9166 g_free(compose
->exteditor_file
);
9168 g_free(compose
->orig_charset
);
9170 g_free(compose
->privacy_system
);
9171 g_free(compose
->encdata
);
9173 #ifndef USE_ALT_ADDRBOOK
9174 if (addressbook_get_target_compose() == compose
)
9175 addressbook_set_target_compose(NULL
);
9178 if (compose
->gtkaspell
) {
9179 gtkaspell_delete(compose
->gtkaspell
);
9180 compose
->gtkaspell
= NULL
;
9184 if (!compose
->batch
) {
9185 gtk_widget_get_allocation(compose
->window
, &allocation
);
9186 prefs_common
.compose_width
= allocation
.width
;
9187 prefs_common
.compose_height
= allocation
.height
;
9190 if (!gtk_widget_get_parent(compose
->paned
))
9191 gtk_widget_destroy(compose
->paned
);
9192 gtk_widget_destroy(compose
->popupmenu
);
9194 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9195 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
9196 gtk_text_buffer_remove_selection_clipboard(buffer
, clipboard
);
9198 message_search_close(compose
);
9199 gtk_widget_destroy(compose
->window
);
9200 toolbar_destroy(compose
->toolbar
);
9201 g_free(compose
->toolbar
);
9202 cm_mutex_free(compose
->mutex
);
9206 static void compose_attach_info_free(AttachInfo
*ainfo
)
9208 g_free(ainfo
->file
);
9209 g_free(ainfo
->content_type
);
9210 g_free(ainfo
->name
);
9211 g_free(ainfo
->charset
);
9215 static void compose_attach_update_label(Compose
*compose
)
9220 GtkTreeModel
*model
;
9224 if (compose
== NULL
)
9227 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(compose
->attach_clist
));
9228 if (!gtk_tree_model_get_iter_first(model
, &iter
)) {
9229 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), "");
9233 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9234 total_size
= ainfo
->size
;
9235 while(gtk_tree_model_iter_next(model
, &iter
)) {
9236 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9237 total_size
+= ainfo
->size
;
9240 text
= g_strdup_printf(" (%d/%s)", i
, to_human_readable(total_size
));
9241 gtk_label_set_text(GTK_LABEL(compose
->attach_label
), text
);
9245 static void compose_attach_remove_selected(GtkAction
*action
, gpointer data
)
9247 Compose
*compose
= (Compose
*)data
;
9248 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9249 GtkTreeSelection
*selection
;
9251 GtkTreeModel
*model
;
9253 selection
= gtk_tree_view_get_selection(tree_view
);
9254 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9255 cm_return_if_fail(sel
);
9257 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9258 GtkTreePath
*path
= cur
->data
;
9259 GtkTreeRowReference
*ref
= gtk_tree_row_reference_new
9262 gtk_tree_path_free(path
);
9265 for (cur
= sel
; cur
!= NULL
; cur
= cur
->next
) {
9266 GtkTreeRowReference
*ref
= cur
->data
;
9267 GtkTreePath
*path
= gtk_tree_row_reference_get_path(ref
);
9270 if (gtk_tree_model_get_iter(model
, &iter
, path
))
9271 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
9273 gtk_tree_path_free(path
);
9274 gtk_tree_row_reference_free(ref
);
9278 compose_attach_update_label(compose
);
9281 static struct _AttachProperty
9284 GtkWidget
*mimetype_entry
;
9285 GtkWidget
*encoding_optmenu
;
9286 GtkWidget
*path_entry
;
9287 GtkWidget
*filename_entry
;
9289 GtkWidget
*cancel_btn
;
9292 static void gtk_tree_path_free_(gpointer ptr
, gpointer data
)
9294 gtk_tree_path_free((GtkTreePath
*)ptr
);
9297 static void compose_attach_property(GtkAction
*action
, gpointer data
)
9299 Compose
*compose
= (Compose
*)data
;
9300 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
9302 GtkComboBox
*optmenu
;
9303 GtkTreeSelection
*selection
;
9305 GtkTreeModel
*model
;
9308 static gboolean cancelled
;
9310 /* only if one selected */
9311 selection
= gtk_tree_view_get_selection(tree_view
);
9312 if (gtk_tree_selection_count_selected_rows(selection
) != 1)
9315 sel
= gtk_tree_selection_get_selected_rows(selection
, &model
);
9316 cm_return_if_fail(sel
);
9318 path
= (GtkTreePath
*) sel
->data
;
9319 gtk_tree_model_get_iter(model
, &iter
, path
);
9320 gtk_tree_model_get(model
, &iter
, COL_DATA
, &ainfo
, -1);
9323 g_list_foreach(sel
, gtk_tree_path_free_
, NULL
);
9329 if (!attach_prop
.window
)
9330 compose_attach_property_create(&cancelled
);
9331 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), TRUE
);
9332 gtk_widget_grab_focus(attach_prop
.ok_btn
);
9333 gtk_widget_show(attach_prop
.window
);
9334 gtk_window_set_transient_for(GTK_WINDOW(attach_prop
.window
),
9335 GTK_WINDOW(compose
->window
));
9337 optmenu
= GTK_COMBO_BOX(attach_prop
.encoding_optmenu
);
9338 if (ainfo
->encoding
== ENC_UNKNOWN
)
9339 combobox_select_by_data(optmenu
, ENC_BASE64
);
9341 combobox_select_by_data(optmenu
, ainfo
->encoding
);
9343 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
9344 ainfo
->content_type
? ainfo
->content_type
: "");
9345 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
9346 ainfo
->file
? ainfo
->file
: "");
9347 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
9348 ainfo
->name
? ainfo
->name
: "");
9351 const gchar
*entry_text
;
9353 gchar
*cnttype
= NULL
;
9360 gtk_widget_hide(attach_prop
.window
);
9361 gtk_window_set_modal(GTK_WINDOW(attach_prop
.window
), FALSE
);
9366 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
9367 if (*entry_text
!= '\0') {
9370 text
= g_strstrip(g_strdup(entry_text
));
9371 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
9372 cnttype
= g_strdup(text
);
9375 alertpanel_error(_("Invalid MIME type."));
9381 ainfo
->encoding
= combobox_get_active_data(optmenu
);
9383 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
9384 if (*entry_text
!= '\0') {
9385 if (is_file_exist(entry_text
) &&
9386 (size
= get_file_size(entry_text
)) > 0)
9387 file
= g_strdup(entry_text
);
9390 (_("File doesn't exist or is empty."));
9396 entry_text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
9397 if (*entry_text
!= '\0') {
9398 g_free(ainfo
->name
);
9399 ainfo
->name
= g_strdup(entry_text
);
9403 g_free(ainfo
->content_type
);
9404 ainfo
->content_type
= cnttype
;
9407 g_free(ainfo
->file
);
9411 ainfo
->size
= (goffset
)size
;
9413 /* update tree store */
9414 text
= to_human_readable(ainfo
->size
);
9415 gtk_tree_model_get_iter(model
, &iter
, path
);
9416 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
9417 COL_MIMETYPE
, ainfo
->content_type
,
9419 COL_NAME
, ainfo
->name
,
9420 COL_CHARSET
, ainfo
->charset
,
9426 gtk_tree_path_free(path
);
9429 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9431 label = gtk_label_new(str); \
9432 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9433 GTK_FILL, 0, 0, 0); \
9434 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9436 entry = gtk_entry_new(); \
9437 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9438 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9441 static void compose_attach_property_create(gboolean
*cancelled
)
9447 GtkWidget
*mimetype_entry
;
9450 GtkListStore
*optmenu_menu
;
9451 GtkWidget
*path_entry
;
9452 GtkWidget
*filename_entry
;
9455 GtkWidget
*cancel_btn
;
9456 GList
*mime_type_list
, *strlist
;
9459 debug_print("Creating attach_property window...\n");
9461 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "compose_attach_property");
9462 gtk_widget_set_size_request(window
, 480, -1);
9463 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
9464 gtk_window_set_title(GTK_WINDOW(window
), _("Properties"));
9465 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
9466 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
9467 g_signal_connect(G_OBJECT(window
), "delete_event",
9468 G_CALLBACK(attach_property_delete_event
),
9470 g_signal_connect(G_OBJECT(window
), "key_press_event",
9471 G_CALLBACK(attach_property_key_pressed
),
9474 vbox
= gtk_vbox_new(FALSE
, 8);
9475 gtk_container_add(GTK_CONTAINER(window
), vbox
);
9477 table
= gtk_table_new(4, 2, FALSE
);
9478 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
9479 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
9480 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
9482 label
= gtk_label_new(_("MIME type"));
9483 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
9485 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9486 mimetype_entry
= gtk_combo_box_text_new_with_entry();
9487 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
9488 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9490 /* stuff with list */
9491 mime_type_list
= procmime_get_mime_type_list();
9493 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
9494 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
9497 tmp
= g_strdup_printf("%s/%s", type
->type
, type
->sub_type
);
9499 if (g_list_find_custom(strlist
, tmp
, (GCompareFunc
)g_strcmp0
))
9502 strlist
= g_list_insert_sorted(strlist
, (gpointer
)tmp
,
9503 (GCompareFunc
)g_strcmp0
);
9506 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
9507 mime_type_list
= mime_type_list
->next
) {
9508 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry
), mime_type_list
->data
);
9509 g_free(mime_type_list
->data
);
9511 g_list_free(strlist
);
9512 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry
), 0);
9513 mimetype_entry
= gtk_bin_get_child(GTK_BIN((mimetype_entry
)));
9515 label
= gtk_label_new(_("Encoding"));
9516 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
9518 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
9520 hbox
= gtk_hbox_new(FALSE
, 0);
9521 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
9522 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
9524 optmenu
= gtkut_sc_combobox_create(NULL
, TRUE
);
9525 optmenu_menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu
)));
9527 COMBOBOX_ADD(optmenu_menu
, "7bit", ENC_7BIT
);
9528 COMBOBOX_ADD(optmenu_menu
, "8bit", ENC_8BIT
);
9529 COMBOBOX_ADD(optmenu_menu
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
9530 COMBOBOX_ADD(optmenu_menu
, "base64", ENC_BASE64
);
9531 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu
), 0);
9533 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
9535 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
9536 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
9538 gtkut_stock_button_set_create(&hbbox
, &cancel_btn
, GTK_STOCK_CANCEL
,
9539 &ok_btn
, GTK_STOCK_OK
,
9541 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
9542 gtk_widget_grab_default(ok_btn
);
9544 g_signal_connect(G_OBJECT(ok_btn
), "clicked",
9545 G_CALLBACK(attach_property_ok
),
9547 g_signal_connect(G_OBJECT(cancel_btn
), "clicked",
9548 G_CALLBACK(attach_property_cancel
),
9551 gtk_widget_show_all(vbox
);
9553 attach_prop
.window
= window
;
9554 attach_prop
.mimetype_entry
= mimetype_entry
;
9555 attach_prop
.encoding_optmenu
= optmenu
;
9556 attach_prop
.path_entry
= path_entry
;
9557 attach_prop
.filename_entry
= filename_entry
;
9558 attach_prop
.ok_btn
= ok_btn
;
9559 attach_prop
.cancel_btn
= cancel_btn
;
9562 #undef SET_LABEL_AND_ENTRY
9564 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
9570 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
9576 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
9577 gboolean
*cancelled
)
9585 static gboolean
attach_property_key_pressed(GtkWidget
*widget
,
9587 gboolean
*cancelled
)
9589 if (event
&& event
->keyval
== GDK_KEY_Escape
) {
9593 if (event
&& event
->keyval
== GDK_KEY_Return
) {
9601 static void compose_exec_ext_editor(Compose
*compose
)
9606 GdkNativeWindow socket_wid
= 0;
9610 tmp
= g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9611 G_DIR_SEPARATOR
, compose
);
9613 if (compose_get_ext_editor_uses_socket()) {
9614 /* Only allow one socket */
9615 if (compose
->exteditor_socket
!= NULL
) {
9616 if (gtk_widget_is_focus(compose
->exteditor_socket
)) {
9617 /* Move the focus off of the socket */
9618 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9623 /* Create the receiving GtkSocket */
9624 socket
= gtk_socket_new ();
9625 g_signal_connect (GTK_OBJECT(socket
), "plug-removed",
9626 G_CALLBACK(compose_ext_editor_plug_removed_cb
),
9628 gtk_box_pack_start(GTK_BOX(compose
->edit_vbox
), socket
, TRUE
, TRUE
, 0);
9629 gtk_widget_set_size_request(socket
, prefs_common
.compose_width
, -1);
9630 /* Realize the socket so that we can use its ID */
9631 gtk_widget_realize(socket
);
9632 socket_wid
= gtk_socket_get_id(GTK_SOCKET (socket
));
9633 compose
->exteditor_socket
= socket
;
9636 if (pipe(pipe_fds
) < 0) {
9642 if ((pid
= fork()) < 0) {
9649 /* close the write side of the pipe */
9652 compose
->exteditor_file
= g_strdup(tmp
);
9653 compose
->exteditor_pid
= pid
;
9655 compose_set_ext_editor_sensitive(compose
, FALSE
);
9658 compose
->exteditor_ch
= g_io_channel_unix_new(pipe_fds
[0]);
9660 compose
->exteditor_ch
= g_io_channel_win32_new_fd(pipe_fds
[0]);
9662 compose
->exteditor_tag
= g_io_add_watch(compose
->exteditor_ch
,
9666 } else { /* process-monitoring process */
9672 /* close the read side of the pipe */
9675 if (compose_write_body_to_file(compose
, tmp
) < 0) {
9676 fd_write_all(pipe_fds
[1], "2\n", 2);
9680 pid_ed
= compose_exec_ext_editor_real(tmp
, socket_wid
);
9682 fd_write_all(pipe_fds
[1], "1\n", 2);
9686 /* wait until editor is terminated */
9687 waitpid(pid_ed
, NULL
, 0);
9689 fd_write_all(pipe_fds
[1], "0\n", 2);
9696 #endif /* G_OS_UNIX */
9699 static gboolean
compose_can_autosave(Compose
*compose
)
9701 if (compose
->privacy_system
&& compose
->use_encryption
)
9702 return prefs_common
.autosave
&& prefs_common
.autosave_encrypted
;
9704 return prefs_common
.autosave
;
9708 static gboolean
compose_get_ext_editor_cmd_valid()
9710 gboolean has_s
= FALSE
;
9711 gboolean has_w
= FALSE
;
9712 const gchar
*p
= prefs_common_get_ext_editor_cmd();
9715 while ((p
= strchr(p
, '%'))) {
9721 } else if (*p
== 'w') {
9732 static gint
compose_exec_ext_editor_real(const gchar
*file
, GdkNativeWindow socket_wid
)
9739 cm_return_val_if_fail(file
!= NULL
, -1);
9741 if ((pid
= fork()) < 0) {
9746 if (pid
!= 0) return pid
;
9748 /* grandchild process */
9750 if (setpgid(0, getppid()))
9753 if (compose_get_ext_editor_cmd_valid()) {
9754 if (compose_get_ext_editor_uses_socket()) {
9755 p
= g_strdup(prefs_common_get_ext_editor_cmd());
9756 s
= strstr(p
, "%w");
9758 if (strstr(p
, "%s") < s
)
9759 buf
= g_strdup_printf(p
, file
, socket_wid
);
9761 buf
= g_strdup_printf(p
, socket_wid
, file
);
9764 buf
= g_strdup_printf(prefs_common_get_ext_editor_cmd(), file
);
9767 if (prefs_common_get_ext_editor_cmd())
9768 g_warning("External editor command-line is invalid: '%s'",
9769 prefs_common_get_ext_editor_cmd());
9770 buf
= g_strdup_printf(DEFAULT_EDITOR_CMD
, file
);
9773 cmdline
= strsplit_with_quote(buf
, " ", 0);
9775 execvp(cmdline
[0], cmdline
);
9778 g_strfreev(cmdline
);
9783 static gboolean
compose_ext_editor_kill(Compose
*compose
)
9785 pid_t pgid
= compose
->exteditor_pid
* -1;
9788 ret
= kill(pgid
, 0);
9790 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
9794 msg
= g_strdup_printf
9795 (_("The external editor is still working.\n"
9796 "Force terminating the process?\n"
9797 "process group id: %d"), -pgid
);
9798 val
= alertpanel_full(_("Notice"), msg
, GTK_STOCK_NO
, GTK_STOCK_YES
,
9799 NULL
, ALERTFOCUS_FIRST
, FALSE
, NULL
,
9804 if (val
== G_ALERTALTERNATE
) {
9805 g_source_remove(compose
->exteditor_tag
);
9806 g_io_channel_shutdown(compose
->exteditor_ch
,
9808 g_io_channel_unref(compose
->exteditor_ch
);
9810 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
9811 waitpid(compose
->exteditor_pid
, NULL
, 0);
9813 g_warning("Terminated process group id: %d. "
9814 "Temporary file: %s", -pgid
, compose
->exteditor_file
);
9816 compose_set_ext_editor_sensitive(compose
, TRUE
);
9818 g_free(compose
->exteditor_file
);
9819 compose
->exteditor_file
= NULL
;
9820 compose
->exteditor_pid
= -1;
9821 compose
->exteditor_ch
= NULL
;
9822 compose
->exteditor_tag
= -1;
9830 static gboolean
compose_input_cb(GIOChannel
*source
, GIOCondition condition
,
9834 Compose
*compose
= (Compose
*)data
;
9837 debug_print("Compose: input from monitoring process\n");
9839 if (g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
) != G_IO_STATUS_NORMAL
) {
9844 g_io_channel_shutdown(source
, FALSE
, NULL
);
9845 g_io_channel_unref(source
);
9847 waitpid(compose
->exteditor_pid
, NULL
, 0);
9849 if (buf
[0] == '0') { /* success */
9850 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
9851 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(text
);
9852 GtkTextIter start
, end
;
9855 gtk_text_buffer_set_text(buffer
, "", -1);
9856 compose_insert_file(compose
, compose
->exteditor_file
);
9857 compose_changed_cb(NULL
, compose
);
9859 /* Check if we should save the draft or not */
9860 if (compose_can_autosave(compose
))
9861 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
9863 if (claws_unlink(compose
->exteditor_file
) < 0)
9864 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9866 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose
->text
));
9867 gtk_text_buffer_get_start_iter(buffer
, &start
);
9868 gtk_text_buffer_get_end_iter(buffer
, &end
);
9869 chars
= gtk_text_buffer_get_text(buffer
, &start
, &end
, FALSE
);
9870 if (chars
&& strlen(chars
) > 0)
9871 compose
->modified
= TRUE
;
9873 } else if (buf
[0] == '1') { /* failed */
9874 g_warning("Couldn't exec external editor");
9875 if (claws_unlink(compose
->exteditor_file
) < 0)
9876 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
9877 } else if (buf
[0] == '2') {
9878 g_warning("Couldn't write to file");
9879 } else if (buf
[0] == '3') {
9880 g_warning("Pipe read failed");
9883 compose_set_ext_editor_sensitive(compose
, TRUE
);
9885 g_free(compose
->exteditor_file
);
9886 compose
->exteditor_file
= NULL
;
9887 compose
->exteditor_pid
= -1;
9888 compose
->exteditor_ch
= NULL
;
9889 compose
->exteditor_tag
= -1;
9890 if (compose
->exteditor_socket
) {
9891 gtk_widget_destroy(compose
->exteditor_socket
);
9892 compose
->exteditor_socket
= NULL
;
9899 static char *ext_editor_menu_entries
[] = {
9900 "Menu/Message/Send",
9901 "Menu/Message/SendLater",
9902 "Menu/Message/InsertFile",
9903 "Menu/Message/InsertSig",
9904 "Menu/Message/ReplaceSig",
9905 "Menu/Message/Save",
9906 "Menu/Message/Print",
9911 "Menu/Tools/ShowRuler",
9912 "Menu/Tools/Actions",
9917 static void compose_set_ext_editor_sensitive(Compose
*compose
,
9922 for (i
= 0; ext_editor_menu_entries
[i
]; ++i
) {
9923 cm_menu_set_sensitive_full(compose
->ui_manager
,
9924 ext_editor_menu_entries
[i
], sensitive
);
9927 if (compose_get_ext_editor_uses_socket()) {
9929 if (compose
->exteditor_socket
)
9930 gtk_widget_hide(compose
->exteditor_socket
);
9931 gtk_widget_show(compose
->scrolledwin
);
9932 if (prefs_common
.show_ruler
)
9933 gtk_widget_show(compose
->ruler_hbox
);
9934 /* Fix the focus, as it doesn't go anywhere when the
9935 * socket is hidden or destroyed */
9936 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9938 g_assert (compose
->exteditor_socket
!= NULL
);
9939 /* Fix the focus, as it doesn't go anywhere when the
9940 * edit box is hidden */
9941 if (gtk_widget_is_focus(compose
->text
))
9942 gtk_widget_child_focus(compose
->window
, GTK_DIR_TAB_BACKWARD
);
9943 gtk_widget_hide(compose
->scrolledwin
);
9944 gtk_widget_hide(compose
->ruler_hbox
);
9945 gtk_widget_show(compose
->exteditor_socket
);
9948 gtk_widget_set_sensitive(compose
->text
, sensitive
);
9950 if (compose
->toolbar
->send_btn
)
9951 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
9952 if (compose
->toolbar
->sendl_btn
)
9953 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
9954 if (compose
->toolbar
->draft_btn
)
9955 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
9956 if (compose
->toolbar
->insert_btn
)
9957 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
9958 if (compose
->toolbar
->sig_btn
)
9959 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
9960 if (compose
->toolbar
->exteditor_btn
)
9961 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
9962 if (compose
->toolbar
->linewrap_current_btn
)
9963 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_current_btn
, sensitive
);
9964 if (compose
->toolbar
->linewrap_all_btn
)
9965 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_all_btn
, sensitive
);
9968 static gboolean
compose_get_ext_editor_uses_socket()
9970 return (prefs_common_get_ext_editor_cmd() &&
9971 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9974 static gboolean
compose_ext_editor_plug_removed_cb(GtkSocket
*socket
, Compose
*compose
)
9976 compose
->exteditor_socket
= NULL
;
9977 /* returning FALSE allows destruction of the socket */
9980 #endif /* G_OS_UNIX */
9983 * compose_undo_state_changed:
9985 * Change the sensivity of the menuentries undo and redo
9987 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
9988 gint redo_state
, gpointer data
)
9990 Compose
*compose
= (Compose
*)data
;
9992 switch (undo_state
) {
9993 case UNDO_STATE_TRUE
:
9994 if (!undostruct
->undo_state
) {
9995 undostruct
->undo_state
= TRUE
;
9996 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", TRUE
);
9999 case UNDO_STATE_FALSE
:
10000 if (undostruct
->undo_state
) {
10001 undostruct
->undo_state
= FALSE
;
10002 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", FALSE
);
10005 case UNDO_STATE_UNCHANGED
:
10007 case UNDO_STATE_REFRESH
:
10008 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Undo", undostruct
->undo_state
);
10011 g_warning("Undo state not recognized");
10015 switch (redo_state
) {
10016 case UNDO_STATE_TRUE
:
10017 if (!undostruct
->redo_state
) {
10018 undostruct
->redo_state
= TRUE
;
10019 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", TRUE
);
10022 case UNDO_STATE_FALSE
:
10023 if (undostruct
->redo_state
) {
10024 undostruct
->redo_state
= FALSE
;
10025 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", FALSE
);
10028 case UNDO_STATE_UNCHANGED
:
10030 case UNDO_STATE_REFRESH
:
10031 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit/Redo", undostruct
->redo_state
);
10034 g_warning("Redo state not recognized");
10039 /* callback functions */
10041 static void compose_notebook_size_alloc(GtkNotebook
*notebook
,
10042 GtkAllocation
*allocation
,
10045 prefs_common
.compose_notebook_height
= gtk_paned_get_position(paned
);
10048 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
10049 * includes "non-client" (windows-izm) in calculation, so this calculation
10050 * may not be accurate.
10052 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
10053 GtkAllocation
*allocation
,
10054 GtkSHRuler
*shruler
)
10056 if (prefs_common
.show_ruler
) {
10057 gint char_width
= 0, char_height
= 0;
10058 gint line_width_in_chars
;
10060 gtkut_get_font_size(GTK_WIDGET(widget
),
10061 &char_width
, &char_height
);
10062 line_width_in_chars
=
10063 (allocation
->width
- allocation
->x
) / char_width
;
10065 /* got the maximum */
10066 gtk_shruler_set_range(GTK_SHRULER(shruler
),
10067 0.0, line_width_in_chars
, 0);
10076 ComposePrefType type
;
10077 gboolean entry_marked
;
10078 } HeaderEntryState
;
10080 static void account_activated(GtkComboBox
*optmenu
, gpointer data
)
10082 Compose
*compose
= (Compose
*)data
;
10085 gchar
*folderidentifier
;
10086 gint account_id
= 0;
10087 GtkTreeModel
*menu
;
10089 GSList
*list
, *saved_list
= NULL
;
10090 HeaderEntryState
*state
;
10092 /* Get ID of active account in the combo box */
10093 menu
= gtk_combo_box_get_model(optmenu
);
10094 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu
, &iter
));
10095 gtk_tree_model_get(menu
, &iter
, 1, &account_id
, -1);
10097 ac
= account_find_from_id(account_id
);
10098 cm_return_if_fail(ac
!= NULL
);
10100 if (ac
!= compose
->account
) {
10101 compose_select_account(compose
, ac
, FALSE
);
10103 for (list
= compose
->header_list
; list
; list
= list
->next
) {
10104 ComposeHeaderEntry
*hentry
=(ComposeHeaderEntry
*)list
->data
;
10106 if (hentry
->type
== PREF_ACCOUNT
|| !list
->next
) {
10107 compose_destroy_headerentry(compose
, hentry
);
10110 state
= g_malloc0(sizeof(HeaderEntryState
));
10111 state
->header
= gtk_editable_get_chars(GTK_EDITABLE(
10112 gtk_bin_get_child(GTK_BIN(hentry
->combo
))), 0, -1);
10113 state
->entry
= gtk_editable_get_chars(
10114 GTK_EDITABLE(hentry
->entry
), 0, -1);
10115 state
->type
= hentry
->type
;
10117 saved_list
= g_slist_append(saved_list
, state
);
10118 compose_destroy_headerentry(compose
, hentry
);
10121 compose
->header_last
= NULL
;
10122 g_slist_free(compose
->header_list
);
10123 compose
->header_list
= NULL
;
10124 compose
->header_nextrow
= 1;
10125 compose_create_header_entry(compose
);
10127 if (ac
->set_autocc
&& ac
->auto_cc
)
10128 compose_entry_append(compose
, ac
->auto_cc
,
10129 COMPOSE_CC
, PREF_ACCOUNT
);
10130 if (ac
->set_autobcc
&& ac
->auto_bcc
)
10131 compose_entry_append(compose
, ac
->auto_bcc
,
10132 COMPOSE_BCC
, PREF_ACCOUNT
);
10133 if (ac
->set_autoreplyto
&& ac
->auto_replyto
)
10134 compose_entry_append(compose
, ac
->auto_replyto
,
10135 COMPOSE_REPLYTO
, PREF_ACCOUNT
);
10137 for (list
= saved_list
; list
; list
= list
->next
) {
10138 state
= (HeaderEntryState
*) list
->data
;
10140 compose_add_header_entry(compose
, state
->header
,
10141 state
->entry
, state
->type
);
10143 g_free(state
->header
);
10144 g_free(state
->entry
);
10147 g_slist_free(saved_list
);
10149 combobox_select_by_data(GTK_COMBO_BOX(compose
->header_last
->combo
),
10150 (ac
->protocol
== A_NNTP
) ?
10151 COMPOSE_NEWSGROUPS
: COMPOSE_TO
);
10154 /* Set message save folder */
10155 compose_set_save_to(compose
, NULL
);
10156 if (compose
->folder
&& compose
->folder
->prefs
&& compose
->folder
->prefs
->save_copy_to_folder
) {
10157 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10158 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10159 folderidentifier
= folder_item_get_identifier(compose
->folder
);
10160 compose_set_save_to(compose
, folderidentifier
);
10161 g_free(folderidentifier
);
10162 } else if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
10163 if (compose
->account
->set_sent_folder
|| prefs_common
.savemsg
)
10164 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
10166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), FALSE
);
10167 gtk_widget_set_sensitive(GTK_WIDGET(compose
->savemsg_combo
), TRUE
);
10168 folderidentifier
= folder_item_get_identifier(account_get_special_folder
10169 (compose
->account
, F_OUTBOX
));
10170 compose_set_save_to(compose
, folderidentifier
);
10171 g_free(folderidentifier
);
10175 static void attach_selected(GtkTreeView
*tree_view
, GtkTreePath
*tree_path
,
10176 GtkTreeViewColumn
*column
, Compose
*compose
)
10178 compose_attach_property(NULL
, compose
);
10181 static gboolean
attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
10184 Compose
*compose
= (Compose
*)data
;
10185 GtkTreeSelection
*attach_selection
;
10186 gint attach_nr_selected
;
10189 if (!event
) return FALSE
;
10191 if (event
->button
== 3) {
10192 attach_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(widget
));
10193 attach_nr_selected
= gtk_tree_selection_count_selected_rows(attach_selection
);
10195 /* If no rows, or just one row is selected, right-click should
10196 * open menu relevant to the row being right-clicked on. We
10197 * achieve that by selecting the clicked row first. If more
10198 * than one row is selected, we shouldn't modify the selection,
10199 * as user may want to remove selected rows (attachments). */
10200 if (attach_nr_selected
< 2) {
10201 gtk_tree_selection_unselect_all(attach_selection
);
10202 attach_nr_selected
= 0;
10203 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
10204 event
->x
, event
->y
, &path
, NULL
, NULL
, NULL
);
10205 if (path
!= NULL
) {
10206 gtk_tree_selection_select_path(attach_selection
, path
);
10207 gtk_tree_path_free(path
);
10208 attach_nr_selected
++;
10212 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Remove", (attach_nr_selected
> 0));
10213 /* Properties menu item makes no sense with more than one row
10214 * selected, the properties dialog can only edit one attachment. */
10215 cm_menu_set_sensitive_full(compose
->ui_manager
, "Popup/Compose/Properties", (attach_nr_selected
== 1));
10217 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
10218 NULL
, NULL
, event
->button
, event
->time
);
10225 static gboolean
attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
10228 Compose
*compose
= (Compose
*)data
;
10230 if (!event
) return FALSE
;
10232 switch (event
->keyval
) {
10233 case GDK_KEY_Delete
:
10234 compose_attach_remove_selected(NULL
, compose
);
10240 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
10242 toolbar_comp_set_sensitive(compose
, allow
);
10243 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Message", allow
);
10244 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Edit", allow
);
10246 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", allow
);
10248 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Options", allow
);
10249 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Tools", allow
);
10250 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Help", allow
);
10252 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose
->text
), allow
);
10256 static void compose_send_cb(GtkAction
*action
, gpointer data
)
10258 Compose
*compose
= (Compose
*)data
;
10261 if (compose
->exteditor_tag
!= -1) {
10262 debug_print("ignoring send: external editor still open\n");
10266 if (prefs_common
.work_offline
&&
10267 !inc_offline_should_override(TRUE
,
10268 _("Claws Mail needs network access in order "
10269 "to send this email.")))
10272 if (compose
->draft_timeout_tag
>= 0) { /* CLAWS: disable draft timeout */
10273 g_source_remove(compose
->draft_timeout_tag
);
10274 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
10277 compose_send(compose
);
10280 static void compose_send_later_cb(GtkAction
*action
, gpointer data
)
10282 Compose
*compose
= (Compose
*)data
;
10283 ComposeQueueResult val
;
10286 compose_allow_user_actions(compose
, FALSE
);
10287 val
= compose_queue_sub(compose
, NULL
, NULL
, NULL
, TRUE
, TRUE
);
10288 compose_allow_user_actions(compose
, TRUE
);
10291 if (val
== COMPOSE_QUEUE_SUCCESS
) {
10292 compose_close(compose
);
10294 _display_queue_error(val
);
10297 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10300 #define DRAFTED_AT_EXIT "drafted_at_exit"
10301 static void compose_register_draft(MsgInfo
*info
)
10303 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10304 DRAFTED_AT_EXIT
, NULL
);
10305 FILE *fp
= claws_fopen(filepath
, "ab");
10308 fprintf(fp
, "%s\t%d\n", folder_item_get_identifier(info
->folder
),
10316 gboolean
compose_draft (gpointer data
, guint action
)
10318 Compose
*compose
= (Compose
*)data
;
10323 MsgFlags flag
= {0, 0};
10324 static gboolean lock
= FALSE
;
10325 MsgInfo
*newmsginfo
;
10327 gboolean target_locked
= FALSE
;
10328 gboolean err
= FALSE
;
10330 if (lock
) return FALSE
;
10332 if (compose
->sending
)
10335 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
10336 cm_return_val_if_fail(draft
!= NULL
, FALSE
);
10338 if (!g_mutex_trylock(compose
->mutex
)) {
10339 /* we don't want to lock the mutex once it's available,
10340 * because as the only other part of compose.c locking
10341 * it is compose_close - which means once unlocked,
10342 * the compose struct will be freed */
10343 debug_print("couldn't lock mutex, probably sending\n");
10349 tmp
= g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10350 G_DIR_SEPARATOR
, compose
);
10351 if ((fp
= claws_fopen(tmp
, "wb")) == NULL
) {
10352 FILE_OP_ERROR(tmp
, "claws_fopen");
10356 /* chmod for security */
10357 if (change_file_mode_rw(fp
, tmp
) < 0) {
10358 FILE_OP_ERROR(tmp
, "chmod");
10359 g_warning("can't change file mode");
10362 /* Save draft infos */
10363 err
|= (fprintf(fp
, "X-Claws-Account-Id:%d\n", compose
->account
->account_id
) < 0);
10364 err
|= (fprintf(fp
, "S:%s\n", compose
->account
->address
) < 0);
10366 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
10367 gchar
*savefolderid
;
10369 savefolderid
= compose_get_save_to(compose
);
10370 err
|= (fprintf(fp
, "SCF:%s\n", savefolderid
) < 0);
10371 g_free(savefolderid
);
10373 if (compose
->return_receipt
) {
10374 err
|= (fprintf(fp
, "RRCPT:1\n") < 0);
10376 if (compose
->privacy_system
) {
10377 err
|= (fprintf(fp
, "X-Claws-Sign:%d\n", compose
->use_signing
) < 0);
10378 err
|= (fprintf(fp
, "X-Claws-Encrypt:%d\n", compose
->use_encryption
) < 0);
10379 err
|= (fprintf(fp
, "X-Claws-Privacy-System:%s\n", compose
->privacy_system
) < 0);
10382 /* Message-ID of message replying to */
10383 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
10384 gchar
*folderid
= NULL
;
10386 if (compose
->replyinfo
->folder
)
10387 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
10388 if (folderid
== NULL
)
10389 folderid
= g_strdup("NULL");
10391 err
|= (fprintf(fp
, "RMID:%s\t%d\t%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
) < 0);
10394 /* Message-ID of message forwarding to */
10395 if ((compose
->fwdinfo
!= NULL
) && (compose
->fwdinfo
->msgid
!= NULL
)) {
10396 gchar
*folderid
= NULL
;
10398 if (compose
->fwdinfo
->folder
)
10399 folderid
= folder_item_get_identifier(compose
->fwdinfo
->folder
);
10400 if (folderid
== NULL
)
10401 folderid
= g_strdup("NULL");
10403 err
|= (fprintf(fp
, "FMID:%s\t%d\t%s\n", folderid
, compose
->fwdinfo
->msgnum
, compose
->fwdinfo
->msgid
) < 0);
10407 err
|= (fprintf(fp
, "X-Claws-Auto-Wrapping:%d\n", compose
->autowrap
) < 0);
10408 err
|= (fprintf(fp
, "X-Claws-Auto-Indent:%d\n", compose
->autoindent
) < 0);
10410 sheaders
= compose_get_manual_headers_info(compose
);
10411 err
|= (fprintf(fp
, "X-Claws-Manual-Headers:%s\n", sheaders
) < 0);
10414 /* end of headers */
10415 err
|= (fprintf(fp
, "X-Claws-End-Special-Headers: 1\n") < 0);
10422 if (compose_write_to_file(compose
, fp
, COMPOSE_WRITE_FOR_STORE
, action
!= COMPOSE_AUTO_SAVE
) < 0) {
10426 if (claws_safe_fclose(fp
) == EOF
) {
10430 flag
.perm_flags
= MSG_NEW
|MSG_UNREAD
;
10431 if (compose
->targetinfo
) {
10432 target_locked
= MSG_IS_LOCKED(compose
->targetinfo
->flags
);
10434 flag
.perm_flags
|= MSG_LOCKED
;
10436 flag
.tmp_flags
= MSG_DRAFT
;
10438 folder_item_scan(draft
);
10439 if ((msgnum
= folder_item_add_msg(draft
, tmp
, &flag
, TRUE
)) < 0) {
10440 MsgInfo
*tmpinfo
= NULL
;
10441 debug_print("didn't get msgnum after adding draft [%s]\n", compose
->msgid
?compose
->msgid
:"no msgid");
10442 if (compose
->msgid
) {
10443 tmpinfo
= folder_item_get_msginfo_by_msgid(draft
, compose
->msgid
);
10446 msgnum
= tmpinfo
->msgnum
;
10447 procmsg_msginfo_free(&tmpinfo
);
10448 debug_print("got draft msgnum %d from scanning\n", msgnum
);
10450 debug_print("didn't get draft msgnum after scanning\n");
10453 debug_print("got draft msgnum %d from adding\n", msgnum
);
10459 if (action
!= COMPOSE_AUTO_SAVE
) {
10460 if (action
!= COMPOSE_DRAFT_FOR_EXIT
)
10461 alertpanel_error(_("Could not save draft."));
10464 gtkut_window_popup(compose
->window
);
10465 val
= alertpanel_full(_("Could not save draft"),
10466 _("Could not save draft.\n"
10467 "Do you want to cancel exit or discard this email?"),
10468 _("_Cancel exit"), _("_Discard email"), NULL
, ALERTFOCUS_FIRST
,
10469 FALSE
, NULL
, ALERT_QUESTION
);
10470 if (val
== G_ALERTALTERNATE
) {
10472 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10473 compose_close(compose
);
10477 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10486 if (compose
->mode
== COMPOSE_REEDIT
) {
10487 compose_remove_reedit_target(compose
, TRUE
);
10490 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
10493 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
10495 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
|MSG_LOCKED
, MSG_DRAFT
);
10497 procmsg_msginfo_set_flags(newmsginfo
, MSG_NEW
|MSG_UNREAD
, MSG_DRAFT
);
10498 if (compose_use_attach(compose
) && action
!= COMPOSE_AUTO_SAVE
)
10499 procmsg_msginfo_set_flags(newmsginfo
, 0,
10500 MSG_HAS_ATTACHMENT
);
10502 if (action
== COMPOSE_DRAFT_FOR_EXIT
) {
10503 compose_register_draft(newmsginfo
);
10505 procmsg_msginfo_free(&newmsginfo
);
10508 folder_item_scan(draft
);
10510 if (action
== COMPOSE_QUIT_EDITING
|| action
== COMPOSE_DRAFT_FOR_EXIT
) {
10512 g_mutex_unlock(compose
->mutex
); /* must be done before closing */
10513 compose_close(compose
);
10520 GError
*error
= NULL
;
10525 goffset size
, mtime
;
10527 path
= folder_item_fetch_msg(draft
, msgnum
);
10528 if (path
== NULL
) {
10529 debug_print("can't fetch %s:%d\n", draft
->path
, msgnum
);
10533 f
= g_file_new_for_path(path
);
10534 fi
= g_file_query_info(f
, "standard::size,time::modified",
10535 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
10536 if (error
!= NULL
) {
10537 debug_print("couldn't query file info for '%s': %s\n",
10538 path
, error
->message
);
10539 g_error_free(error
);
10544 size
= g_file_info_get_size(fi
);
10545 g_file_info_get_modification_time(fi
, &tv
);
10547 g_object_unref(fi
);
10550 if (g_stat(path
, &s
) < 0) {
10551 FILE_OP_ERROR(path
, "stat");
10556 mtime
= s
.st_mtime
;
10560 procmsg_msginfo_free(&(compose
->targetinfo
));
10561 compose
->targetinfo
= procmsg_msginfo_new();
10562 compose
->targetinfo
->msgnum
= msgnum
;
10563 compose
->targetinfo
->size
= size
;
10564 compose
->targetinfo
->mtime
= mtime
;
10565 compose
->targetinfo
->folder
= draft
;
10567 procmsg_msginfo_set_flags(compose
->targetinfo
, MSG_LOCKED
, 0);
10568 compose
->mode
= COMPOSE_REEDIT
;
10570 if (action
== COMPOSE_AUTO_SAVE
) {
10571 compose
->autosaved_draft
= compose
->targetinfo
;
10573 compose
->modified
= FALSE
;
10574 compose_set_title(compose
);
10578 g_mutex_unlock(compose
->mutex
);
10582 void compose_clear_exit_drafts(void)
10584 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10585 DRAFTED_AT_EXIT
, NULL
);
10586 if (is_file_exist(filepath
))
10587 claws_unlink(filepath
);
10592 void compose_reopen_exit_drafts(void)
10594 gchar
*filepath
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
10595 DRAFTED_AT_EXIT
, NULL
);
10596 FILE *fp
= claws_fopen(filepath
, "rb");
10600 while (claws_fgets(buf
, sizeof(buf
), fp
)) {
10601 gchar
**parts
= g_strsplit(buf
, "\t", 2);
10602 const gchar
*folder
= parts
[0];
10603 int msgnum
= parts
[1] ? atoi(parts
[1]):-1;
10605 if (folder
&& *folder
&& msgnum
> -1) {
10606 FolderItem
*item
= folder_find_item_from_identifier(folder
);
10607 MsgInfo
*info
= folder_item_get_msginfo(item
, msgnum
);
10609 compose_reedit(info
, FALSE
);
10616 compose_clear_exit_drafts();
10619 static void compose_save_cb(GtkAction
*action
, gpointer data
)
10621 Compose
*compose
= (Compose
*)data
;
10622 compose_draft(compose
, COMPOSE_KEEP_EDITING
);
10623 compose
->rmode
= COMPOSE_REEDIT
;
10626 void compose_attach_from_list(Compose
*compose
, GList
*file_list
, gboolean free_data
)
10628 if (compose
&& file_list
) {
10631 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10632 gchar
*file
= (gchar
*) tmp
->data
;
10633 gchar
*utf8_filename
= conv_filename_to_utf8(file
);
10634 compose_attach_append(compose
, file
, utf8_filename
, NULL
, NULL
);
10635 compose_changed_cb(NULL
, compose
);
10640 g_free(utf8_filename
);
10645 static void compose_attach_cb(GtkAction
*action
, gpointer data
)
10647 Compose
*compose
= (Compose
*)data
;
10650 if (compose
->redirect_filename
!= NULL
)
10653 /* Set focus_window properly, in case we were called via popup menu,
10654 * which unsets it (via focus_out_event callback on compose window). */
10655 manage_window_focus_in(compose
->window
, NULL
, NULL
);
10657 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10660 compose_attach_from_list(compose
, file_list
, TRUE
);
10661 g_list_free(file_list
);
10665 static void compose_insert_file_cb(GtkAction
*action
, gpointer data
)
10667 Compose
*compose
= (Compose
*)data
;
10669 gint files_inserted
= 0;
10671 file_list
= filesel_select_multiple_files_open(_("Select file"), NULL
);
10676 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
10677 gchar
*file
= (gchar
*) tmp
->data
;
10678 gchar
*filedup
= g_strdup(file
);
10679 gchar
*shortfile
= g_path_get_basename(filedup
);
10680 ComposeInsertResult res
;
10681 /* insert the file if the file is short or if the user confirmed that
10682 he/she wants to insert the large file */
10683 res
= compose_insert_file(compose
, file
);
10684 if (res
== COMPOSE_INSERT_READ_ERROR
) {
10685 alertpanel_error(_("File '%s' could not be read."), shortfile
);
10686 } else if (res
== COMPOSE_INSERT_INVALID_CHARACTER
) {
10687 alertpanel_error(_("File '%s' contained invalid characters\n"
10688 "for the current encoding, insertion may be incorrect."),
10690 } else if (res
== COMPOSE_INSERT_SUCCESS
)
10697 g_list_free(file_list
);
10701 if (files_inserted
> 0 && compose
->gtkaspell
&&
10702 compose
->gtkaspell
->check_while_typing
)
10703 gtkaspell_highlight_all(compose
->gtkaspell
);
10707 static void compose_insert_sig_cb(GtkAction
*action
, gpointer data
)
10709 Compose
*compose
= (Compose
*)data
;
10711 compose_insert_sig(compose
, FALSE
);
10714 static void compose_replace_sig_cb(GtkAction
*action
, gpointer data
)
10716 Compose
*compose
= (Compose
*)data
;
10718 compose_insert_sig(compose
, TRUE
);
10721 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
10725 Compose
*compose
= (Compose
*)data
;
10727 gtkut_widget_get_uposition(widget
, &x
, &y
);
10728 if (!compose
->batch
) {
10729 prefs_common
.compose_x
= x
;
10730 prefs_common
.compose_y
= y
;
10732 if (compose
->sending
|| compose
->updating
)
10734 compose_close_cb(NULL
, compose
);
10738 void compose_close_toolbar(Compose
*compose
)
10740 compose_close_cb(NULL
, compose
);
10743 static void compose_close_cb(GtkAction
*action
, gpointer data
)
10745 Compose
*compose
= (Compose
*)data
;
10749 if (compose
->exteditor_tag
!= -1) {
10750 if (!compose_ext_editor_kill(compose
))
10755 if (compose
->modified
) {
10756 gboolean reedit
= (compose
->rmode
== COMPOSE_REEDIT
);
10757 if (!g_mutex_trylock(compose
->mutex
)) {
10758 /* we don't want to lock the mutex once it's available,
10759 * because as the only other part of compose.c locking
10760 * it is compose_close - which means once unlocked,
10761 * the compose struct will be freed */
10762 debug_print("couldn't lock mutex, probably sending\n");
10765 if (!reedit
|| compose
->folder
->stype
== F_DRAFT
) {
10766 val
= alertpanel(_("Discard message"),
10767 _("This message has been modified. Discard it?"),
10768 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10771 val
= alertpanel(_("Save changes"),
10772 _("This message has been modified. Save the latest changes?"),
10773 _("_Don't save"), _("_Save to Drafts"), GTK_STOCK_CANCEL
,
10774 ALERTFOCUS_SECOND
);
10776 g_mutex_unlock(compose
->mutex
);
10778 case G_ALERTDEFAULT
:
10779 if (compose_can_autosave(compose
) && !reedit
)
10780 compose_remove_draft(compose
);
10782 case G_ALERTALTERNATE
:
10783 compose_draft(data
, COMPOSE_QUIT_EDITING
);
10790 compose_close(compose
);
10793 static void compose_print_cb(GtkAction
*action
, gpointer data
)
10795 Compose
*compose
= (Compose
*) data
;
10797 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
10798 if (compose
->targetinfo
)
10799 messageview_print(compose
->targetinfo
, FALSE
, -1, -1, 0);
10802 static void compose_set_encoding_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
)
10804 gboolean active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
10805 gint value
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
10806 Compose
*compose
= (Compose
*) data
;
10809 compose
->out_encoding
= (CharSet
)value
;
10812 static void compose_address_cb(GtkAction
*action
, gpointer data
)
10814 Compose
*compose
= (Compose
*)data
;
10816 #ifndef USE_ALT_ADDRBOOK
10817 addressbook_open(compose
);
10819 GError
* error
= NULL
;
10820 addressbook_connect_signals(compose
);
10821 addressbook_dbus_open(TRUE
, &error
);
10823 g_warning("%s", error
->message
);
10824 g_error_free(error
);
10829 static void about_show_cb(GtkAction
*action
, gpointer data
)
10834 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
10836 Compose
*compose
= (Compose
*)data
;
10841 tmpl
= g_object_get_data(G_OBJECT(widget
), "template");
10842 cm_return_if_fail(tmpl
!= NULL
);
10844 msg
= g_strdup_printf(_("Do you want to apply the template '%s'?"),
10846 val
= alertpanel(_("Apply template"), msg
,
10847 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL
, ALERTFOCUS_FIRST
);
10850 if (val
== G_ALERTDEFAULT
)
10851 compose_template_apply(compose
, tmpl
, TRUE
);
10852 else if (val
== G_ALERTALTERNATE
)
10853 compose_template_apply(compose
, tmpl
, FALSE
);
10856 static void compose_ext_editor_cb(GtkAction
*action
, gpointer data
)
10858 Compose
*compose
= (Compose
*)data
;
10861 if (compose
->exteditor_tag
!= -1) {
10862 debug_print("ignoring open external editor: external editor still open\n");
10866 compose_exec_ext_editor(compose
);
10869 static void compose_undo_cb(GtkAction
*action
, gpointer data
)
10871 Compose
*compose
= (Compose
*)data
;
10872 gboolean prev_autowrap
= compose
->autowrap
;
10874 compose
->autowrap
= FALSE
;
10875 undo_undo(compose
->undostruct
);
10876 compose
->autowrap
= prev_autowrap
;
10879 static void compose_redo_cb(GtkAction
*action
, gpointer data
)
10881 Compose
*compose
= (Compose
*)data
;
10882 gboolean prev_autowrap
= compose
->autowrap
;
10884 compose
->autowrap
= FALSE
;
10885 undo_redo(compose
->undostruct
);
10886 compose
->autowrap
= prev_autowrap
;
10889 static void entry_cut_clipboard(GtkWidget
*entry
)
10891 if (GTK_IS_EDITABLE(entry
))
10892 gtk_editable_cut_clipboard (GTK_EDITABLE(entry
));
10893 else if (GTK_IS_TEXT_VIEW(entry
))
10894 gtk_text_buffer_cut_clipboard(
10895 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10896 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
10900 static void entry_copy_clipboard(GtkWidget
*entry
)
10902 if (GTK_IS_EDITABLE(entry
))
10903 gtk_editable_copy_clipboard (GTK_EDITABLE(entry
));
10904 else if (GTK_IS_TEXT_VIEW(entry
))
10905 gtk_text_buffer_copy_clipboard(
10906 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
)),
10907 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
10910 static void entry_paste_clipboard(Compose
*compose
, GtkWidget
*entry
,
10911 gboolean wrap
, GdkAtom clip
, GtkTextIter
*insert_place
)
10913 if (GTK_IS_TEXT_VIEW(entry
)) {
10914 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10915 GtkTextMark
*mark_start
= gtk_text_buffer_get_insert(buffer
);
10916 GtkTextIter start_iter
, end_iter
;
10918 gchar
*contents
= gtk_clipboard_wait_for_text(gtk_clipboard_get(clip
));
10920 if (contents
== NULL
)
10923 /* we shouldn't delete the selection when middle-click-pasting, or we
10924 * can't mid-click-paste our own selection */
10925 if (clip
!= GDK_SELECTION_PRIMARY
) {
10926 undo_paste_clipboard(GTK_TEXT_VIEW(compose
->text
), compose
->undostruct
);
10927 gtk_text_buffer_delete_selection(buffer
, FALSE
, TRUE
);
10930 if (insert_place
== NULL
) {
10931 /* if insert_place isn't specified, insert at the cursor.
10932 * used for Ctrl-V pasting */
10933 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10934 start
= gtk_text_iter_get_offset(&start_iter
);
10935 gtk_text_buffer_insert(buffer
, &start_iter
, contents
, strlen(contents
));
10937 /* if insert_place is specified, paste here.
10938 * used for mid-click-pasting */
10939 start
= gtk_text_iter_get_offset(insert_place
);
10940 gtk_text_buffer_insert(buffer
, insert_place
, contents
, strlen(contents
));
10941 if (prefs_common
.primary_paste_unselects
)
10942 gtk_text_buffer_select_range(buffer
, insert_place
, insert_place
);
10946 /* paste unwrapped: mark the paste so it's not wrapped later */
10947 end
= start
+ strlen(contents
);
10948 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, start
);
10949 gtk_text_buffer_get_iter_at_offset(buffer
, &end_iter
, end
);
10950 gtk_text_buffer_apply_tag_by_name(buffer
, "no_wrap", &start_iter
, &end_iter
);
10951 } else if (wrap
&& clip
== GDK_SELECTION_PRIMARY
) {
10952 /* rewrap paragraph now (after a mid-click-paste) */
10953 mark_start
= gtk_text_buffer_get_insert(buffer
);
10954 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark_start
);
10955 gtk_text_iter_backward_char(&start_iter
);
10956 compose_beautify_paragraph(compose
, &start_iter
, TRUE
);
10958 } else if (GTK_IS_EDITABLE(entry
))
10959 gtk_editable_paste_clipboard (GTK_EDITABLE(entry
));
10961 compose
->modified
= TRUE
;
10964 static void entry_allsel(GtkWidget
*entry
)
10966 if (GTK_IS_EDITABLE(entry
))
10967 gtk_editable_select_region(GTK_EDITABLE(entry
), 0, -1);
10968 else if (GTK_IS_TEXT_VIEW(entry
)) {
10969 GtkTextIter startiter
, enditer
;
10970 GtkTextBuffer
*textbuf
;
10972 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry
));
10973 gtk_text_buffer_get_start_iter(textbuf
, &startiter
);
10974 gtk_text_buffer_get_end_iter(textbuf
, &enditer
);
10976 gtk_text_buffer_move_mark_by_name(textbuf
,
10977 "selection_bound", &startiter
);
10978 gtk_text_buffer_move_mark_by_name(textbuf
,
10979 "insert", &enditer
);
10983 static void compose_cut_cb(GtkAction
*action
, gpointer data
)
10985 Compose
*compose
= (Compose
*)data
;
10986 if (compose
->focused_editable
10987 #ifndef GENERIC_UMPC
10988 && gtk_widget_has_focus(compose
->focused_editable
)
10991 entry_cut_clipboard(compose
->focused_editable
);
10994 static void compose_copy_cb(GtkAction
*action
, gpointer data
)
10996 Compose
*compose
= (Compose
*)data
;
10997 if (compose
->focused_editable
10998 #ifndef GENERIC_UMPC
10999 && gtk_widget_has_focus(compose
->focused_editable
)
11002 entry_copy_clipboard(compose
->focused_editable
);
11005 static void compose_paste_cb(GtkAction
*action
, gpointer data
)
11007 Compose
*compose
= (Compose
*)data
;
11008 gint prev_autowrap
;
11009 GtkTextBuffer
*buffer
;
11011 if (compose
->focused_editable
11012 #ifndef GENERIC_UMPC
11013 && gtk_widget_has_focus(compose
->focused_editable
)
11016 entry_paste_clipboard(compose
, compose
->focused_editable
,
11017 prefs_common
.linewrap_pastes
,
11018 GDK_SELECTION_CLIPBOARD
, NULL
);
11023 #ifndef GENERIC_UMPC
11024 gtk_widget_has_focus(compose
->text
) &&
11026 compose
->gtkaspell
&&
11027 compose
->gtkaspell
->check_while_typing
)
11028 gtkaspell_highlight_all(compose
->gtkaspell
);
11032 static void compose_paste_as_quote_cb(GtkAction
*action
, gpointer data
)
11034 Compose
*compose
= (Compose
*)data
;
11035 gint wrap_quote
= prefs_common
.linewrap_quote
;
11036 if (compose
->focused_editable
11037 #ifndef GENERIC_UMPC
11038 && gtk_widget_has_focus(compose
->focused_editable
)
11041 /* let text_insert() (called directly or at a later time
11042 * after the gtk_editable_paste_clipboard) know that
11043 * text is to be inserted as a quotation. implemented
11044 * by using a simple refcount... */
11045 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data(
11046 G_OBJECT(compose
->focused_editable
),
11047 "paste_as_quotation"));
11048 g_object_set_data(G_OBJECT(compose
->focused_editable
),
11049 "paste_as_quotation",
11050 GINT_TO_POINTER(paste_as_quotation
+ 1));
11051 prefs_common
.linewrap_quote
= prefs_common
.linewrap_pastes
;
11052 entry_paste_clipboard(compose
, compose
->focused_editable
,
11053 prefs_common
.linewrap_pastes
,
11054 GDK_SELECTION_CLIPBOARD
, NULL
);
11055 prefs_common
.linewrap_quote
= wrap_quote
;
11059 static void compose_paste_no_wrap_cb(GtkAction
*action
, gpointer data
)
11061 Compose
*compose
= (Compose
*)data
;
11062 gint prev_autowrap
;
11063 GtkTextBuffer
*buffer
;
11065 if (compose
->focused_editable
11066 #ifndef GENERIC_UMPC
11067 && gtk_widget_has_focus(compose
->focused_editable
)
11070 entry_paste_clipboard(compose
, compose
->focused_editable
, FALSE
,
11071 GDK_SELECTION_CLIPBOARD
, NULL
);
11076 #ifndef GENERIC_UMPC
11077 gtk_widget_has_focus(compose
->text
) &&
11079 compose
->gtkaspell
&&
11080 compose
->gtkaspell
->check_while_typing
)
11081 gtkaspell_highlight_all(compose
->gtkaspell
);
11085 static void compose_paste_wrap_cb(GtkAction
*action
, gpointer data
)
11087 Compose
*compose
= (Compose
*)data
;
11088 gint prev_autowrap
;
11089 GtkTextBuffer
*buffer
;
11091 if (compose
->focused_editable
11092 #ifndef GENERIC_UMPC
11093 && gtk_widget_has_focus(compose
->focused_editable
)
11096 entry_paste_clipboard(compose
, compose
->focused_editable
, TRUE
,
11097 GDK_SELECTION_CLIPBOARD
, NULL
);
11102 #ifndef GENERIC_UMPC
11103 gtk_widget_has_focus(compose
->text
) &&
11105 compose
->gtkaspell
&&
11106 compose
->gtkaspell
->check_while_typing
)
11107 gtkaspell_highlight_all(compose
->gtkaspell
);
11111 static void compose_allsel_cb(GtkAction
*action
, gpointer data
)
11113 Compose
*compose
= (Compose
*)data
;
11114 if (compose
->focused_editable
11115 #ifndef GENERIC_UMPC
11116 && gtk_widget_has_focus(compose
->focused_editable
)
11119 entry_allsel(compose
->focused_editable
);
11122 static void textview_move_beginning_of_line (GtkTextView
*text
)
11124 GtkTextBuffer
*buffer
;
11128 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11130 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11131 mark
= gtk_text_buffer_get_insert(buffer
);
11132 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11133 gtk_text_iter_set_line_offset(&ins
, 0);
11134 gtk_text_buffer_place_cursor(buffer
, &ins
);
11137 static void textview_move_forward_character (GtkTextView
*text
)
11139 GtkTextBuffer
*buffer
;
11143 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11145 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11146 mark
= gtk_text_buffer_get_insert(buffer
);
11147 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11148 if (gtk_text_iter_forward_cursor_position(&ins
))
11149 gtk_text_buffer_place_cursor(buffer
, &ins
);
11152 static void textview_move_backward_character (GtkTextView
*text
)
11154 GtkTextBuffer
*buffer
;
11158 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11160 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11161 mark
= gtk_text_buffer_get_insert(buffer
);
11162 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11163 if (gtk_text_iter_backward_cursor_position(&ins
))
11164 gtk_text_buffer_place_cursor(buffer
, &ins
);
11167 static void textview_move_forward_word (GtkTextView
*text
)
11169 GtkTextBuffer
*buffer
;
11174 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11176 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11177 mark
= gtk_text_buffer_get_insert(buffer
);
11178 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11179 count
= gtk_text_iter_inside_word (&ins
) ? 2 : 1;
11180 if (gtk_text_iter_forward_word_ends(&ins
, count
)) {
11181 gtk_text_iter_backward_word_start(&ins
);
11182 gtk_text_buffer_place_cursor(buffer
, &ins
);
11186 static void textview_move_backward_word (GtkTextView
*text
)
11188 GtkTextBuffer
*buffer
;
11192 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11194 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11195 mark
= gtk_text_buffer_get_insert(buffer
);
11196 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11197 if (gtk_text_iter_backward_word_starts(&ins
, 1))
11198 gtk_text_buffer_place_cursor(buffer
, &ins
);
11201 static void textview_move_end_of_line (GtkTextView
*text
)
11203 GtkTextBuffer
*buffer
;
11207 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11209 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11210 mark
= gtk_text_buffer_get_insert(buffer
);
11211 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11212 if (gtk_text_iter_forward_to_line_end(&ins
))
11213 gtk_text_buffer_place_cursor(buffer
, &ins
);
11216 static void textview_move_next_line (GtkTextView
*text
)
11218 GtkTextBuffer
*buffer
;
11223 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11225 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11226 mark
= gtk_text_buffer_get_insert(buffer
);
11227 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11228 offset
= gtk_text_iter_get_line_offset(&ins
);
11229 if (gtk_text_iter_forward_line(&ins
)) {
11230 gtk_text_iter_set_line_offset(&ins
, offset
);
11231 gtk_text_buffer_place_cursor(buffer
, &ins
);
11235 static void textview_move_previous_line (GtkTextView
*text
)
11237 GtkTextBuffer
*buffer
;
11242 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11244 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11245 mark
= gtk_text_buffer_get_insert(buffer
);
11246 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11247 offset
= gtk_text_iter_get_line_offset(&ins
);
11248 if (gtk_text_iter_backward_line(&ins
)) {
11249 gtk_text_iter_set_line_offset(&ins
, offset
);
11250 gtk_text_buffer_place_cursor(buffer
, &ins
);
11254 static void textview_delete_forward_character (GtkTextView
*text
)
11256 GtkTextBuffer
*buffer
;
11258 GtkTextIter ins
, end_iter
;
11260 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11262 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11263 mark
= gtk_text_buffer_get_insert(buffer
);
11264 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11266 if (gtk_text_iter_forward_char(&end_iter
)) {
11267 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11271 static void textview_delete_backward_character (GtkTextView
*text
)
11273 GtkTextBuffer
*buffer
;
11275 GtkTextIter ins
, end_iter
;
11277 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11279 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11280 mark
= gtk_text_buffer_get_insert(buffer
);
11281 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11283 if (gtk_text_iter_backward_char(&end_iter
)) {
11284 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11288 static void textview_delete_forward_word (GtkTextView
*text
)
11290 GtkTextBuffer
*buffer
;
11292 GtkTextIter ins
, end_iter
;
11294 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11296 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11297 mark
= gtk_text_buffer_get_insert(buffer
);
11298 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11300 if (gtk_text_iter_forward_word_end(&end_iter
)) {
11301 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11305 static void textview_delete_backward_word (GtkTextView
*text
)
11307 GtkTextBuffer
*buffer
;
11309 GtkTextIter ins
, end_iter
;
11311 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11313 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11314 mark
= gtk_text_buffer_get_insert(buffer
);
11315 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11317 if (gtk_text_iter_backward_word_start(&end_iter
)) {
11318 gtk_text_buffer_delete(buffer
, &end_iter
, &ins
);
11322 static void textview_delete_line (GtkTextView
*text
)
11324 GtkTextBuffer
*buffer
;
11326 GtkTextIter ins
, start_iter
, end_iter
;
11328 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11330 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11331 mark
= gtk_text_buffer_get_insert(buffer
);
11332 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11335 gtk_text_iter_set_line_offset(&start_iter
, 0);
11338 if (gtk_text_iter_ends_line(&end_iter
)){
11339 if (!gtk_text_iter_forward_char(&end_iter
))
11340 gtk_text_iter_backward_char(&start_iter
);
11343 gtk_text_iter_forward_to_line_end(&end_iter
);
11344 gtk_text_buffer_delete(buffer
, &start_iter
, &end_iter
);
11347 static void textview_delete_to_line_end (GtkTextView
*text
)
11349 GtkTextBuffer
*buffer
;
11351 GtkTextIter ins
, end_iter
;
11353 cm_return_if_fail(GTK_IS_TEXT_VIEW(text
));
11355 buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(text
));
11356 mark
= gtk_text_buffer_get_insert(buffer
);
11357 gtk_text_buffer_get_iter_at_mark(buffer
, &ins
, mark
);
11359 if (gtk_text_iter_ends_line(&end_iter
))
11360 gtk_text_iter_forward_char(&end_iter
);
11362 gtk_text_iter_forward_to_line_end(&end_iter
);
11363 gtk_text_buffer_delete(buffer
, &ins
, &end_iter
);
11366 #define DO_ACTION(name, act) { \
11367 if(!strcmp(name, a_name)) { \
11371 static ComposeCallAdvancedAction
compose_call_advanced_action_from_path(GtkAction
*action
)
11373 const gchar
*a_name
= gtk_action_get_name(action
);
11374 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER
);
11375 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER
);
11376 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD
);
11377 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD
);
11378 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
);
11379 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE
);
11380 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE
);
11381 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE
);
11382 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER
);
11383 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER
);
11384 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD
);
11385 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD
);
11386 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE
);
11387 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
);
11388 return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11391 static void compose_advanced_action_cb(GtkAction
*gaction
, gpointer data
)
11393 Compose
*compose
= (Compose
*)data
;
11394 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
11395 ComposeCallAdvancedAction action
= COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED
;
11397 action
= compose_call_advanced_action_from_path(gaction
);
11400 void (*do_action
) (GtkTextView
*text
);
11401 } action_table
[] = {
11402 {textview_move_beginning_of_line
},
11403 {textview_move_forward_character
},
11404 {textview_move_backward_character
},
11405 {textview_move_forward_word
},
11406 {textview_move_backward_word
},
11407 {textview_move_end_of_line
},
11408 {textview_move_next_line
},
11409 {textview_move_previous_line
},
11410 {textview_delete_forward_character
},
11411 {textview_delete_backward_character
},
11412 {textview_delete_forward_word
},
11413 {textview_delete_backward_word
},
11414 {textview_delete_line
},
11415 {textview_delete_to_line_end
}
11418 if (!gtk_widget_has_focus(GTK_WIDGET(text
))) return;
11420 if (action
>= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE
&&
11421 action
<= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
) {
11422 if (action_table
[action
].do_action
)
11423 action_table
[action
].do_action(text
);
11425 g_warning("Not implemented yet.");
11429 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
11431 GtkAllocation allocation
;
11435 if (GTK_IS_EDITABLE(widget
)) {
11436 str
= gtk_editable_get_chars(GTK_EDITABLE(widget
), 0, -1);
11437 gtk_editable_set_position(GTK_EDITABLE(widget
),
11440 if ((parent
= gtk_widget_get_parent(widget
))
11441 && (parent
= gtk_widget_get_parent(parent
))
11442 && (parent
= gtk_widget_get_parent(parent
))) {
11443 if (GTK_IS_SCROLLED_WINDOW(parent
)) {
11444 gtk_widget_get_allocation(widget
, &allocation
);
11445 gint y
= allocation
.y
;
11446 gint height
= allocation
.height
;
11447 GtkAdjustment
*shown
= gtk_scrolled_window_get_vadjustment
11448 (GTK_SCROLLED_WINDOW(parent
));
11450 gfloat value
= gtk_adjustment_get_value(shown
);
11451 gfloat upper
= gtk_adjustment_get_upper(shown
);
11452 gfloat page_size
= gtk_adjustment_get_page_size(shown
);
11453 if (y
< (int)value
) {
11454 gtk_adjustment_set_value(shown
, y
- 1);
11456 if ((y
+ height
) > ((int)value
+ (int)page_size
)) {
11457 if ((y
- height
- 1) < ((int)upper
- (int)page_size
)) {
11458 gtk_adjustment_set_value(shown
,
11459 y
+ height
- (int)page_size
- 1);
11461 gtk_adjustment_set_value(shown
,
11462 (int)upper
- (int)page_size
- 1);
11469 if (GTK_IS_EDITABLE(widget
) || GTK_IS_TEXT_VIEW(widget
))
11470 compose
->focused_editable
= widget
;
11472 #ifdef GENERIC_UMPC
11473 if (GTK_IS_TEXT_VIEW(widget
)
11474 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->edit_vbox
) {
11475 g_object_ref(compose
->notebook
);
11476 g_object_ref(compose
->edit_vbox
);
11477 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11478 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11479 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11480 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->notebook
);
11481 g_object_unref(compose
->notebook
);
11482 g_object_unref(compose
->edit_vbox
);
11483 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11484 G_CALLBACK(compose_grab_focus_cb
),
11486 gtk_widget_grab_focus(widget
);
11487 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11488 G_CALLBACK(compose_grab_focus_cb
),
11490 } else if (!GTK_IS_TEXT_VIEW(widget
)
11491 && gtk_paned_get_child1(GTK_PANED(compose
->paned
)) != compose
->notebook
) {
11492 g_object_ref(compose
->notebook
);
11493 g_object_ref(compose
->edit_vbox
);
11494 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->notebook
);
11495 gtk_container_remove(GTK_CONTAINER(compose
->paned
), compose
->edit_vbox
);
11496 gtk_paned_add1(GTK_PANED(compose
->paned
), compose
->notebook
);
11497 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
11498 g_object_unref(compose
->notebook
);
11499 g_object_unref(compose
->edit_vbox
);
11500 g_signal_handlers_block_by_func(G_OBJECT(widget
),
11501 G_CALLBACK(compose_grab_focus_cb
),
11503 gtk_widget_grab_focus(widget
);
11504 g_signal_handlers_unblock_by_func(G_OBJECT(widget
),
11505 G_CALLBACK(compose_grab_focus_cb
),
11511 static void compose_changed_cb(GtkTextBuffer
*textbuf
, Compose
*compose
)
11513 compose
->modified
= TRUE
;
11514 /* compose_beautify_paragraph(compose, NULL, TRUE); */
11515 #ifndef GENERIC_UMPC
11516 compose_set_title(compose
);
11520 static void compose_wrap_cb(GtkAction
*action
, gpointer data
)
11522 Compose
*compose
= (Compose
*)data
;
11523 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11526 static void compose_wrap_all_cb(GtkAction
*action
, gpointer data
)
11528 Compose
*compose
= (Compose
*)data
;
11529 compose_wrap_all_full(compose
, TRUE
);
11532 static void compose_find_cb(GtkAction
*action
, gpointer data
)
11534 Compose
*compose
= (Compose
*)data
;
11536 message_search_compose(compose
);
11539 static void compose_toggle_autowrap_cb(GtkToggleAction
*action
,
11542 Compose
*compose
= (Compose
*)data
;
11543 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11544 if (compose
->autowrap
)
11545 compose_wrap_all_full(compose
, TRUE
);
11546 compose
->autowrap
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11549 static void compose_toggle_autoindent_cb(GtkToggleAction
*action
,
11552 Compose
*compose
= (Compose
*)data
;
11553 compose
->autoindent
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11556 static void compose_toggle_sign_cb(GtkToggleAction
*action
, gpointer data
)
11558 Compose
*compose
= (Compose
*)data
;
11560 compose
->use_signing
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11561 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_sign_btn
), compose
->use_signing
);
11564 static void compose_toggle_encrypt_cb(GtkToggleAction
*action
, gpointer data
)
11566 Compose
*compose
= (Compose
*)data
;
11568 compose
->use_encryption
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
));
11569 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose
->toolbar
->privacy_encrypt_btn
), compose
->use_encryption
);
11572 static void compose_activate_privacy_system(Compose
*compose
, PrefsAccount
*account
, gboolean warn
)
11574 g_free(compose
->privacy_system
);
11575 g_free(compose
->encdata
);
11577 compose
->privacy_system
= g_strdup(account
->default_privacy_system
);
11578 compose_update_privacy_system_menu_item(compose
, warn
);
11581 static void compose_apply_folder_privacy_settings(Compose
*compose
, FolderItem
*folder_item
)
11583 if (folder_item
!= NULL
) {
11584 if (folder_item
->prefs
->always_sign
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11585 privacy_system_can_sign(compose
->privacy_system
)) {
11586 compose_use_signing(compose
,
11587 (folder_item
->prefs
->always_sign
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11589 if (folder_item
->prefs
->always_encrypt
!= SIGN_OR_ENCRYPT_DEFAULT
&&
11590 privacy_system_can_encrypt(compose
->privacy_system
)) {
11591 compose_use_encryption(compose
,
11592 (folder_item
->prefs
->always_encrypt
== SIGN_OR_ENCRYPT_ALWAYS
) ? TRUE
: FALSE
);
11597 static void compose_toggle_ruler_cb(GtkToggleAction
*action
, gpointer data
)
11599 Compose
*compose
= (Compose
*)data
;
11601 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
))) {
11602 gtk_widget_show(compose
->ruler_hbox
);
11603 prefs_common
.show_ruler
= TRUE
;
11605 gtk_widget_hide(compose
->ruler_hbox
);
11606 gtk_widget_queue_resize(compose
->edit_vbox
);
11607 prefs_common
.show_ruler
= FALSE
;
11611 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
11612 GdkDragContext
*context
,
11615 GtkSelectionData
*data
,
11618 gpointer user_data
)
11620 Compose
*compose
= (Compose
*)user_data
;
11624 type
= gtk_selection_data_get_data_type(data
);
11625 if ((gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list"))
11626 && gtk_drag_get_source_widget(context
) !=
11627 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11628 list
= uri_list_extract_filenames(
11629 (const gchar
*)gtk_selection_data_get_data(data
));
11630 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11631 gchar
*utf8_filename
= conv_filename_to_utf8((const gchar
*)tmp
->data
);
11632 compose_attach_append
11633 (compose
, (const gchar
*)tmp
->data
,
11634 utf8_filename
, NULL
, NULL
);
11635 g_free(utf8_filename
);
11638 compose_changed_cb(NULL
, compose
);
11639 list_free_strings_full(list
);
11640 } else if (gtk_drag_get_source_widget(context
)
11641 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview
)) {
11642 /* comes from our summaryview */
11643 SummaryView
* summaryview
= NULL
;
11644 GSList
* list
= NULL
, *cur
= NULL
;
11646 if (mainwindow_get_mainwindow())
11647 summaryview
= mainwindow_get_mainwindow()->summaryview
;
11650 list
= summary_get_selected_msg_list(summaryview
);
11652 for (cur
= list
; cur
; cur
= cur
->next
) {
11653 MsgInfo
*msginfo
= (MsgInfo
*)cur
->data
;
11654 gchar
*file
= NULL
;
11656 file
= procmsg_get_message_file_full(msginfo
,
11659 compose_attach_append(compose
, (const gchar
*)file
,
11660 (const gchar
*)file
, "message/rfc822", NULL
);
11664 g_slist_free(list
);
11668 static gboolean
compose_drag_drop(GtkWidget
*widget
,
11669 GdkDragContext
*drag_context
,
11671 guint time
, gpointer user_data
)
11673 /* not handling this signal makes compose_insert_drag_received_cb
11678 static gboolean completion_set_focus_to_subject
11679 (GtkWidget
*widget
,
11680 GdkEventKey
*event
,
11683 cm_return_val_if_fail(compose
!= NULL
, FALSE
);
11685 /* make backtab move to subject field */
11686 if(event
->keyval
== GDK_KEY_ISO_Left_Tab
) {
11687 gtk_widget_grab_focus(compose
->subject_entry
);
11693 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
11694 GdkDragContext
*drag_context
,
11697 GtkSelectionData
*data
,
11700 gpointer user_data
)
11702 Compose
*compose
= (Compose
*)user_data
;
11708 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11710 type
= gtk_selection_data_get_data_type(data
);
11711 if (gdk_atom_name(type
) && !strcmp(gdk_atom_name(type
), "text/uri-list")) {
11712 AlertValue val
= G_ALERTDEFAULT
;
11713 const gchar
* ddata
= (const gchar
*)gtk_selection_data_get_data(data
);
11715 list
= uri_list_extract_filenames(ddata
);
11716 num_files
= g_list_length(list
);
11717 if (list
== NULL
&& strstr(ddata
, "://")) {
11718 /* Assume a list of no files, and data has ://, is a remote link */
11719 gchar
*tmpdata
= g_strstrip(g_strdup(ddata
));
11720 gchar
*tmpfile
= get_tmp_file();
11721 str_write_to_file(tmpdata
, tmpfile
, TRUE
);
11723 compose_insert_file(compose
, tmpfile
);
11724 claws_unlink(tmpfile
);
11726 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11727 compose_beautify_paragraph(compose
, NULL
, TRUE
);
11730 switch (prefs_common
.compose_dnd_mode
) {
11731 case COMPOSE_DND_ASK
:
11732 msg
= g_strdup_printf(
11734 "Do you want to insert the contents of the file "
11735 "into the message body, or attach it to the email?",
11736 "Do you want to insert the contents of the %d files "
11737 "into the message body, or attach them to the email?",
11740 val
= alertpanel_full(_("Insert or attach?"), msg
,
11741 GTK_STOCK_CANCEL
, _("_Insert"), _("_Attach"),
11743 TRUE
, NULL
, ALERT_QUESTION
);
11746 case COMPOSE_DND_INSERT
:
11747 val
= G_ALERTALTERNATE
;
11749 case COMPOSE_DND_ATTACH
:
11750 val
= G_ALERTOTHER
;
11753 /* unexpected case */
11754 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11757 if (val
& G_ALERTDISABLE
) {
11758 val
&= ~G_ALERTDISABLE
;
11759 /* remember what action to perform by default, only if we don't click Cancel */
11760 if (val
== G_ALERTALTERNATE
)
11761 prefs_common
.compose_dnd_mode
= COMPOSE_DND_INSERT
;
11762 else if (val
== G_ALERTOTHER
)
11763 prefs_common
.compose_dnd_mode
= COMPOSE_DND_ATTACH
;
11766 if (val
== G_ALERTDEFAULT
|| val
== G_ALERTCANCEL
) {
11767 gtk_drag_finish(drag_context
, FALSE
, FALSE
, time
);
11768 list_free_strings_full(list
);
11770 } else if (val
== G_ALERTOTHER
) {
11771 compose_attach_drag_received_cb(widget
, drag_context
, x
, y
, data
, info
, time
, user_data
);
11772 list_free_strings_full(list
);
11776 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
) {
11777 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
11779 list_free_strings_full(list
);
11780 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11785 static void compose_header_drag_received_cb (GtkWidget
*widget
,
11786 GdkDragContext
*drag_context
,
11789 GtkSelectionData
*data
,
11792 gpointer user_data
)
11794 GtkEditable
*entry
= (GtkEditable
*)user_data
;
11795 const gchar
*email
= (const gchar
*)gtk_selection_data_get_data(data
);
11797 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11800 if (!strncmp(email
, "mailto:", strlen("mailto:"))) {
11801 gchar
*decoded
=g_new(gchar
, strlen(email
));
11804 decode_uri(decoded
, email
+ strlen("mailto:")); /* will fit */
11805 gtk_editable_delete_text(entry
, 0, -1);
11806 gtk_editable_insert_text(entry
, decoded
, strlen(decoded
), &start
);
11807 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11811 gtk_drag_finish(drag_context
, TRUE
, FALSE
, time
);
11814 static void compose_toggle_return_receipt_cb(GtkToggleAction
*action
, gpointer data
)
11816 Compose
*compose
= (Compose
*)data
;
11818 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11819 compose
->return_receipt
= TRUE
;
11821 compose
->return_receipt
= FALSE
;
11824 static void compose_toggle_remove_refs_cb(GtkToggleAction
*action
, gpointer data
)
11826 Compose
*compose
= (Compose
*)data
;
11828 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)))
11829 compose
->remove_references
= TRUE
;
11831 compose
->remove_references
= FALSE
;
11834 static gboolean
compose_headerentry_button_clicked_cb (GtkWidget
*button
,
11835 ComposeHeaderEntry
*headerentry
)
11837 gtk_entry_set_text(GTK_ENTRY(headerentry
->entry
), "");
11838 gtk_widget_modify_base(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11839 gtk_widget_modify_text(GTK_WIDGET(headerentry
->entry
), GTK_STATE_NORMAL
, NULL
);
11843 static gboolean
compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
11844 GdkEventKey
*event
,
11845 ComposeHeaderEntry
*headerentry
)
11847 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
11848 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
11849 !(event
->state
& GDK_MODIFIER_MASK
) &&
11850 (event
->keyval
== GDK_KEY_BackSpace
) &&
11851 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
11852 gtk_container_remove
11853 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11854 headerentry
->combo
);
11855 gtk_container_remove
11856 (GTK_CONTAINER(headerentry
->compose
->header_table
),
11857 headerentry
->entry
);
11858 headerentry
->compose
->header_list
=
11859 g_slist_remove(headerentry
->compose
->header_list
,
11861 g_free(headerentry
);
11862 } else if (event
->keyval
== GDK_KEY_Tab
) {
11863 if (headerentry
->compose
->header_last
== headerentry
) {
11864 /* Override default next focus, and give it to subject_entry
11865 * instead of notebook tabs
11867 g_signal_stop_emission_by_name(G_OBJECT(entry
), "key-press-event");
11868 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
11875 static gboolean
scroll_postpone(gpointer data
)
11877 Compose
*compose
= (Compose
*)data
;
11879 if (compose
->batch
)
11882 GTK_EVENTS_FLUSH();
11883 compose_show_first_last_header(compose
, FALSE
);
11887 static void compose_headerentry_changed_cb(GtkWidget
*entry
,
11888 ComposeHeaderEntry
*headerentry
)
11890 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
11891 compose_create_header_entry(headerentry
->compose
);
11892 g_signal_handlers_disconnect_matched
11893 (G_OBJECT(entry
), G_SIGNAL_MATCH_DATA
,
11894 0, 0, NULL
, NULL
, headerentry
);
11896 if (!headerentry
->compose
->batch
)
11897 g_timeout_add(0, scroll_postpone
, headerentry
->compose
);
11901 static gboolean
compose_defer_auto_save_draft(Compose
*compose
)
11903 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_UNSET
;
11904 compose_draft((gpointer
)compose
, COMPOSE_AUTO_SAVE
);
11908 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
11910 GtkAdjustment
*vadj
;
11912 cm_return_if_fail(compose
);
11917 cm_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
11918 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose
->header_table
)));
11919 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11920 gtk_widget_get_parent(compose
->header_table
)));
11921 gtk_adjustment_set_value(vadj
, (show_first
?
11922 gtk_adjustment_get_lower(vadj
) :
11923 (gtk_adjustment_get_upper(vadj
) -
11924 gtk_adjustment_get_page_size(vadj
))));
11925 gtk_adjustment_changed(vadj
);
11928 static void text_inserted(GtkTextBuffer
*buffer
, GtkTextIter
*iter
,
11929 const gchar
*text
, gint len
, Compose
*compose
)
11931 gint paste_as_quotation
= GPOINTER_TO_INT(g_object_get_data
11932 (G_OBJECT(compose
->text
), "paste_as_quotation"));
11935 cm_return_if_fail(text
!= NULL
);
11937 g_signal_handlers_block_by_func(G_OBJECT(buffer
),
11938 G_CALLBACK(text_inserted
),
11940 if (paste_as_quotation
) {
11942 const gchar
*qmark
;
11944 GtkTextIter start_iter
;
11947 len
= strlen(text
);
11949 new_text
= g_strndup(text
, len
);
11951 qmark
= compose_quote_char_from_context(compose
);
11953 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
11954 gtk_text_buffer_place_cursor(buffer
, iter
);
11956 pos
= gtk_text_iter_get_offset(iter
);
11958 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
, TRUE
, FALSE
,
11959 _("Quote format error at line %d."));
11960 quote_fmt_reset_vartable();
11962 g_object_set_data(G_OBJECT(compose
->text
), "paste_as_quotation",
11963 GINT_TO_POINTER(paste_as_quotation
- 1));
11965 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
11966 gtk_text_buffer_place_cursor(buffer
, iter
);
11967 gtk_text_buffer_delete_mark(buffer
, mark
);
11969 gtk_text_buffer_get_iter_at_offset(buffer
, &start_iter
, pos
);
11970 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, &start_iter
, FALSE
);
11971 compose_beautify_paragraph(compose
, &start_iter
, FALSE
);
11972 gtk_text_buffer_get_iter_at_mark(buffer
, &start_iter
, mark
);
11973 gtk_text_buffer_delete_mark(buffer
, mark
);
11975 if (strcmp(text
, "\n") || compose
->automatic_break
11976 || gtk_text_iter_starts_line(iter
)) {
11977 GtkTextIter before_ins
;
11978 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
11979 if (!strstr(text
, "\n") && gtk_text_iter_has_tag(iter
, compose
->no_join_tag
)) {
11980 before_ins
= *iter
;
11981 gtk_text_iter_backward_chars(&before_ins
, len
);
11982 gtk_text_buffer_remove_tag_by_name(buffer
, "no_join", &before_ins
, iter
);
11985 /* check if the preceding is just whitespace or quote */
11986 GtkTextIter start_line
;
11987 gchar
*tmp
= NULL
, *quote
= NULL
;
11988 gint quote_len
= 0, is_normal
= 0;
11989 start_line
= *iter
;
11990 gtk_text_iter_set_line_offset(&start_line
, 0);
11991 tmp
= gtk_text_buffer_get_text(buffer
, &start_line
, iter
, FALSE
);
11994 if (*tmp
== '\0') {
11997 quote
= compose_get_quote_str(buffer
, &start_line
, "e_len
);
12005 gtk_text_buffer_insert(buffer
, iter
, text
, len
);
12007 gtk_text_buffer_insert_with_tags_by_name(buffer
,
12008 iter
, text
, len
, "no_join", NULL
);
12013 if (!paste_as_quotation
) {
12014 mark
= gtk_text_buffer_create_mark(buffer
, NULL
, iter
, FALSE
);
12015 compose_beautify_paragraph(compose
, iter
, FALSE
);
12016 gtk_text_buffer_get_iter_at_mark(buffer
, iter
, mark
);
12017 gtk_text_buffer_delete_mark(buffer
, mark
);
12020 g_signal_handlers_unblock_by_func(G_OBJECT(buffer
),
12021 G_CALLBACK(text_inserted
),
12023 g_signal_stop_emission_by_name(G_OBJECT(buffer
), "insert-text");
12025 if (compose_can_autosave(compose
) &&
12026 gtk_text_buffer_get_char_count(buffer
) % prefs_common
.autosave_length
== 0 &&
12027 compose
->draft_timeout_tag
!= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
/* disabled while loading */)
12028 compose
->draft_timeout_tag
= g_timeout_add
12029 (500, (GSourceFunc
) compose_defer_auto_save_draft
, compose
);
12033 static void compose_check_all(GtkAction
*action
, gpointer data
)
12035 Compose
*compose
= (Compose
*)data
;
12036 if (!compose
->gtkaspell
)
12039 if (gtk_widget_has_focus(compose
->subject_entry
))
12040 claws_spell_entry_check_all(
12041 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12043 gtkaspell_check_all(compose
->gtkaspell
);
12046 static void compose_highlight_all(GtkAction
*action
, gpointer data
)
12048 Compose
*compose
= (Compose
*)data
;
12049 if (compose
->gtkaspell
) {
12050 claws_spell_entry_recheck_all(
12051 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12052 gtkaspell_highlight_all(compose
->gtkaspell
);
12056 static void compose_check_backwards(GtkAction
*action
, gpointer data
)
12058 Compose
*compose
= (Compose
*)data
;
12059 if (!compose
->gtkaspell
) {
12060 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12064 if (gtk_widget_has_focus(compose
->subject_entry
))
12065 claws_spell_entry_check_backwards(
12066 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12068 gtkaspell_check_backwards(compose
->gtkaspell
);
12071 static void compose_check_forwards_go(GtkAction
*action
, gpointer data
)
12073 Compose
*compose
= (Compose
*)data
;
12074 if (!compose
->gtkaspell
) {
12075 cm_menu_set_sensitive_full(compose
->ui_manager
, "Menu/Spelling", FALSE
);
12079 if (gtk_widget_has_focus(compose
->subject_entry
))
12080 claws_spell_entry_check_forwards_go(
12081 CLAWS_SPELL_ENTRY(compose
->subject_entry
));
12083 gtkaspell_check_forwards_go(compose
->gtkaspell
);
12088 *\brief Guess originating forward account from MsgInfo and several
12089 * "common preference" settings. Return NULL if no guess.
12091 static PrefsAccount
*compose_find_account(MsgInfo
*msginfo
)
12093 PrefsAccount
*account
= NULL
;
12095 cm_return_val_if_fail(msginfo
, NULL
);
12096 cm_return_val_if_fail(msginfo
->folder
, NULL
);
12097 cm_return_val_if_fail(msginfo
->folder
->prefs
, NULL
);
12099 if (msginfo
->folder
->prefs
->enable_default_account
)
12100 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
12102 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
12104 Xstrdup_a(to
, msginfo
->to
, return NULL
);
12105 extract_address(to
);
12106 account
= account_find_from_address(to
, FALSE
);
12109 if (!account
&& prefs_common
.forward_account_autosel
) {
12111 if (!procheader_get_header_from_msginfo
12112 (msginfo
, &cc
, "Cc:")) {
12113 gchar
*buf
= cc
+ strlen("Cc:");
12114 extract_address(buf
);
12115 account
= account_find_from_address(buf
, FALSE
);
12120 if (!account
&& prefs_common
.forward_account_autosel
) {
12121 gchar
*deliveredto
= NULL
;
12122 if (!procheader_get_header_from_msginfo
12123 (msginfo
, &deliveredto
, "Delivered-To:")) {
12124 gchar
*buf
= deliveredto
+ strlen("Delivered-To:");
12125 extract_address(buf
);
12126 account
= account_find_from_address(buf
, FALSE
);
12127 g_free(deliveredto
);
12132 account
= msginfo
->folder
->folder
->account
;
12137 gboolean
compose_close(Compose
*compose
)
12141 cm_return_val_if_fail(compose
, FALSE
);
12143 if (!g_mutex_trylock(compose
->mutex
)) {
12144 /* we have to wait for the (possibly deferred by auto-save)
12145 * drafting to be done, before destroying the compose under
12147 debug_print("waiting for drafting to finish...\n");
12148 compose_allow_user_actions(compose
, FALSE
);
12149 if (compose
->close_timeout_tag
== 0) {
12150 compose
->close_timeout_tag
=
12151 g_timeout_add (500, (GSourceFunc
) compose_close
,
12157 if (compose
->draft_timeout_tag
>= 0) {
12158 g_source_remove(compose
->draft_timeout_tag
);
12159 compose
->draft_timeout_tag
= COMPOSE_DRAFT_TIMEOUT_FORBIDDEN
;
12162 gtkut_widget_get_uposition(compose
->window
, &x
, &y
);
12163 if (!compose
->batch
) {
12164 prefs_common
.compose_x
= x
;
12165 prefs_common
.compose_y
= y
;
12167 g_mutex_unlock(compose
->mutex
);
12168 compose_destroy(compose
);
12173 * Add entry field for each address in list.
12174 * \param compose E-Mail composition object.
12175 * \param listAddress List of (formatted) E-Mail addresses.
12177 static void compose_add_field_list( Compose
*compose
, GList
*listAddress
) {
12180 node
= listAddress
;
12182 addr
= ( gchar
* ) node
->data
;
12183 compose_entry_append( compose
, addr
, COMPOSE_TO
, PREF_NONE
);
12184 node
= g_list_next( node
);
12188 static void compose_reply_from_messageview_real(MessageView
*msgview
, GSList
*msginfo_list
,
12189 guint action
, gboolean opening_multiple
)
12191 gchar
*body
= NULL
;
12192 GSList
*new_msglist
= NULL
;
12193 MsgInfo
*tmp_msginfo
= NULL
;
12194 gboolean originally_enc
= FALSE
;
12195 gboolean originally_sig
= FALSE
;
12196 Compose
*compose
= NULL
;
12197 gchar
*s_system
= NULL
;
12199 cm_return_if_fail(msginfo_list
!= NULL
);
12201 if (g_slist_length(msginfo_list
) == 1 && !opening_multiple
&& msgview
!= NULL
) {
12202 MimeInfo
*mimeinfo
= messageview_get_selected_mime_part(msgview
);
12203 MsgInfo
*orig_msginfo
= (MsgInfo
*)msginfo_list
->data
;
12205 if (mimeinfo
!= NULL
&& mimeinfo
->type
== MIMETYPE_MESSAGE
&&
12206 !g_ascii_strcasecmp(mimeinfo
->subtype
, "rfc822")) {
12207 tmp_msginfo
= procmsg_msginfo_new_from_mimeinfo(
12208 orig_msginfo
, mimeinfo
);
12209 if (tmp_msginfo
!= NULL
) {
12210 new_msglist
= g_slist_append(NULL
, tmp_msginfo
);
12212 originally_enc
= MSG_IS_ENCRYPTED(orig_msginfo
->flags
);
12213 privacy_msginfo_get_signed_state(orig_msginfo
, &s_system
);
12214 originally_sig
= MSG_IS_SIGNED(orig_msginfo
->flags
);
12216 tmp_msginfo
->folder
= orig_msginfo
->folder
;
12217 tmp_msginfo
->msgnum
= orig_msginfo
->msgnum
;
12218 if (orig_msginfo
->tags
) {
12219 tmp_msginfo
->tags
= g_slist_copy(orig_msginfo
->tags
);
12220 tmp_msginfo
->folder
->tags_dirty
= TRUE
;
12226 if (!opening_multiple
&& msgview
!= NULL
)
12227 body
= messageview_get_selection(msgview
);
12230 compose
= compose_reply_mode((ComposeMode
)action
, new_msglist
, body
);
12231 procmsg_msginfo_free(&tmp_msginfo
);
12232 g_slist_free(new_msglist
);
12234 compose
= compose_reply_mode((ComposeMode
)action
, msginfo_list
, body
);
12236 if (compose
&& originally_enc
) {
12237 compose_force_encryption(compose
, compose
->account
, FALSE
, s_system
);
12240 if (compose
&& originally_sig
&& compose
->account
->default_sign_reply
) {
12241 compose_force_signing(compose
, compose
->account
, s_system
);
12245 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12248 void compose_reply_from_messageview(MessageView
*msgview
, GSList
*msginfo_list
,
12251 if ((!prefs_common
.forward_as_attachment
|| action
!= COMPOSE_FORWARD
)
12252 && msginfo_list
!= NULL
12253 && action
!= COMPOSE_FORWARD_AS_ATTACH
&& g_slist_length(msginfo_list
) > 1) {
12254 GSList
*cur
= msginfo_list
;
12255 gchar
*msg
= g_strdup_printf(_("You are about to reply to %d "
12256 "messages. Opening the windows "
12257 "could take some time. Do you "
12258 "want to continue?"),
12259 g_slist_length(msginfo_list
));
12260 if (g_slist_length(msginfo_list
) > 9
12261 && alertpanel(_("Warning"), msg
, GTK_STOCK_CANCEL
, GTK_STOCK_YES
, NULL
,
12262 ALERTFOCUS_SECOND
) != G_ALERTALTERNATE
) {
12267 /* We'll open multiple compose windows */
12268 /* let the WM place the next windows */
12269 compose_force_window_origin
= FALSE
;
12270 for (; cur
; cur
= cur
->next
) {
12272 tmplist
.data
= cur
->data
;
12273 tmplist
.next
= NULL
;
12274 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, TRUE
);
12276 compose_force_window_origin
= TRUE
;
12278 /* forwarding multiple mails as attachments is done via a
12279 * single compose window */
12280 if (msginfo_list
!= NULL
) {
12281 compose_reply_from_messageview_real(msgview
, msginfo_list
, action
, FALSE
);
12282 } else if (msgview
!= NULL
) {
12284 tmplist
.data
= msgview
->msginfo
;
12285 tmplist
.next
= NULL
;
12286 compose_reply_from_messageview_real(msgview
, &tmplist
, action
, FALSE
);
12288 debug_print("Nothing to reply to\n");
12293 void compose_check_for_email_account(Compose
*compose
)
12295 PrefsAccount
*ac
= NULL
, *curr
= NULL
;
12301 if (compose
->account
&& compose
->account
->protocol
== A_NNTP
) {
12302 ac
= account_get_cur_account();
12303 if (ac
->protocol
== A_NNTP
) {
12304 list
= account_get_list();
12306 for( ; list
!= NULL
; list
= g_list_next(list
)) {
12307 curr
= (PrefsAccount
*) list
->data
;
12308 if (curr
->protocol
!= A_NNTP
) {
12314 combobox_select_by_data(GTK_COMBO_BOX(compose
->account_combo
),
12319 void compose_reply_to_address(MessageView
*msgview
, MsgInfo
*msginfo
,
12320 const gchar
*address
)
12322 GSList
*msginfo_list
= NULL
;
12323 gchar
*body
= messageview_get_selection(msgview
);
12326 msginfo_list
= g_slist_prepend(msginfo_list
, msginfo
);
12328 compose
= compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS
, msginfo_list
, body
);
12329 compose_check_for_email_account(compose
);
12330 compose_set_folder_prefs(compose
, msginfo
->folder
, FALSE
);
12331 compose_entry_append(compose
, address
, COMPOSE_TO
, PREF_NONE
);
12332 compose_reply_set_subject(compose
, msginfo
);
12335 hooks_invoke(COMPOSE_CREATED_HOOKLIST
, compose
);
12338 void compose_set_position(Compose
*compose
, gint pos
)
12340 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12342 gtkut_text_view_set_position(text
, pos
);
12345 gboolean
compose_search_string(Compose
*compose
,
12346 const gchar
*str
, gboolean case_sens
)
12348 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12350 return gtkut_text_view_search_string(text
, str
, case_sens
);
12353 gboolean
compose_search_string_backward(Compose
*compose
,
12354 const gchar
*str
, gboolean case_sens
)
12356 GtkTextView
*text
= GTK_TEXT_VIEW(compose
->text
);
12358 return gtkut_text_view_search_string_backward(text
, str
, case_sens
);
12361 /* allocate a msginfo structure and populate its data from a compose data structure */
12362 static MsgInfo
*compose_msginfo_new_from_compose(Compose
*compose
)
12364 MsgInfo
*newmsginfo
;
12366 gchar date
[RFC822_DATE_BUFFSIZE
];
12368 cm_return_val_if_fail( compose
!= NULL
, NULL
);
12370 newmsginfo
= procmsg_msginfo_new();
12373 get_rfc822_date(date
, sizeof(date
));
12374 newmsginfo
->date
= g_strdup(date
);
12377 if (compose
->from_name
) {
12378 newmsginfo
->from
= gtk_editable_get_chars(GTK_EDITABLE(compose
->from_name
), 0, -1);
12379 newmsginfo
->fromname
= procheader_get_fromname(newmsginfo
->from
);
12383 if (compose
->subject_entry
)
12384 newmsginfo
->subject
= gtk_editable_get_chars(GTK_EDITABLE(compose
->subject_entry
), 0, -1);
12386 /* to, cc, reply-to, newsgroups */
12387 for (list
= compose
->header_list
; list
; list
= list
->next
) {
12388 gchar
*header
= gtk_editable_get_chars(
12390 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry
*)list
->data
)->combo
)))), 0, -1);
12391 gchar
*entry
= gtk_editable_get_chars(
12392 GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
12394 if ( strcasecmp(header
, prefs_common_translated_header_name("To:")) == 0 ) {
12395 if ( newmsginfo
->to
== NULL
) {
12396 newmsginfo
->to
= g_strdup(entry
);
12397 } else if (entry
&& *entry
) {
12398 gchar
*tmp
= g_strconcat(newmsginfo
->to
, ", ", entry
, NULL
);
12399 g_free(newmsginfo
->to
);
12400 newmsginfo
->to
= tmp
;
12403 if ( strcasecmp(header
, prefs_common_translated_header_name("Cc:")) == 0 ) {
12404 if ( newmsginfo
->cc
== NULL
) {
12405 newmsginfo
->cc
= g_strdup(entry
);
12406 } else if (entry
&& *entry
) {
12407 gchar
*tmp
= g_strconcat(newmsginfo
->cc
, ", ", entry
, NULL
);
12408 g_free(newmsginfo
->cc
);
12409 newmsginfo
->cc
= tmp
;
12412 if ( strcasecmp(header
,
12413 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12414 if ( newmsginfo
->newsgroups
== NULL
) {
12415 newmsginfo
->newsgroups
= g_strdup(entry
);
12416 } else if (entry
&& *entry
) {
12417 gchar
*tmp
= g_strconcat(newmsginfo
->newsgroups
, ", ", entry
, NULL
);
12418 g_free(newmsginfo
->newsgroups
);
12419 newmsginfo
->newsgroups
= tmp
;
12427 /* other data is unset */
12433 /* update compose's dictionaries from folder dict settings */
12434 static void compose_set_dictionaries_from_folder_prefs(Compose
*compose
,
12435 FolderItem
*folder_item
)
12437 cm_return_if_fail(compose
!= NULL
);
12439 if (compose
->gtkaspell
&& folder_item
&& folder_item
->prefs
) {
12440 FolderItemPrefs
*prefs
= folder_item
->prefs
;
12442 if (prefs
->enable_default_dictionary
)
12443 gtkaspell_change_dict(compose
->gtkaspell
,
12444 prefs
->default_dictionary
, FALSE
);
12445 if (folder_item
->prefs
->enable_default_alt_dictionary
)
12446 gtkaspell_change_alt_dict(compose
->gtkaspell
,
12447 prefs
->default_alt_dictionary
);
12448 if (prefs
->enable_default_dictionary
12449 || prefs
->enable_default_alt_dictionary
)
12450 compose_spell_menu_changed(compose
);
12455 static void compose_subject_entry_activated(GtkWidget
*widget
, gpointer data
)
12457 Compose
*compose
= (Compose
*)data
;
12459 cm_return_if_fail(compose
!= NULL
);
12461 gtk_widget_grab_focus(compose
->text
);
12464 static void from_name_activate_cb(GtkWidget
*widget
, gpointer data
)
12466 gtk_combo_box_popup(GTK_COMBO_BOX(data
));