aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNot Zed <NotZed@Ximian.com>2003-09-18 05:19:04 +0800
committerMichael Zucci <zucchi@src.gnome.org>2003-09-18 05:19:04 +0800
commit81a0ff5bc44a3bd11399e6b3c985735737606c8c (patch)
tree54b5ed4342a6843c1db4c7e75f2e1b1fe9b82dff
parenta36a1bb70b6ebcb51ac39304370c89bda63e11b9 (diff)
downloadgsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.tar
gsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.tar.gz
gsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.tar.bz2
gsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.tar.lz
gsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.tar.xz
gsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.tar.zst
gsoc2013-evolution-81a0ff5bc44a3bd11399e6b3c985735737606c8c.zip
cvs removed.
2003-09-17 Not Zed <NotZed@Ximian.com> * folder-browser.c, folder-browser.h, folder-browser-ui.c folder-browser-ui.h, mail-callbacks.c, mail-callbacks.h mail-display.c, mail-display.h, mail-display-stream.c mail-display-stream.h, mail-format.c, mail-format.h mail-identify.c, mail-search.c, mail-search.h message-browser.c, message-browser.h, subscribe-dialog.c subscribe-dialog.h, mail-font-prefs.c, mail-font-prefs.h: cvs removed. * Makefile.am: Removed mail-font-prefs.[ch], hasn't been built for ages. * em-*.c: killed a bunch of printfs. * em-format-html-display.c (efhd_html_button_press_event): update for html object api chagnes. ** Merge in mail-refactor-2 branch. svn path=/trunk/; revision=22602
-rw-r--r--mail/ChangeLog862
-rw-r--r--mail/Makefile.am64
-rw-r--r--mail/component-factory.c22
-rw-r--r--mail/em-camel-stream.c326
-rw-r--r--mail/em-camel-stream.h71
-rw-r--r--mail/em-composer-utils.c623
-rw-r--r--mail/em-composer-utils.h43
-rw-r--r--mail/em-folder-browser.c933
-rw-r--r--mail/em-folder-browser.h60
-rw-r--r--mail/em-folder-view.c1866
-rw-r--r--mail/em-folder-view.h117
-rw-r--r--mail/em-format-html-display.c1159
-rw-r--r--mail/em-format-html-display.h63
-rw-r--r--mail/em-format-html-print.c186
-rw-r--r--mail/em-format-html-print.h37
-rw-r--r--mail/em-format-html-quote.c277
-rw-r--r--mail/em-format-html-quote.h49
-rw-r--r--mail/em-format-html.c1542
-rw-r--r--mail/em-format-html.h154
-rw-r--r--mail/em-format-quote.c289
-rw-r--r--mail/em-format-quote.h56
-rw-r--r--mail/em-format.c1228
-rw-r--r--mail/em-format.h200
-rw-r--r--mail/em-html-stream.c168
-rw-r--r--mail/em-html-stream.h (renamed from mail/mail-display-stream.h)44
-rw-r--r--mail/em-icon-stream.c201
-rw-r--r--mail/em-icon-stream.h62
-rw-r--r--mail/em-inline-filter.c335
-rw-r--r--mail/em-inline-filter.h62
-rw-r--r--mail/em-marshal.list1
-rw-r--r--mail/em-message-browser.c171
-rw-r--r--mail/em-message-browser.h30
-rw-r--r--mail/em-popup.c823
-rw-r--r--mail/em-popup.h158
-rw-r--r--mail/em-subscribe-editor.c849
-rw-r--r--mail/em-subscribe-editor.h9
-rw-r--r--mail/em-sync-stream.c286
-rw-r--r--mail/em-sync-stream.h69
-rw-r--r--mail/em-utils.c2327
-rw-r--r--mail/em-utils.h124
-rw-r--r--mail/evolution-mail.schemas16
-rw-r--r--mail/folder-browser-factory.c109
-rw-r--r--mail/folder-browser-factory.h3
-rw-r--r--mail/folder-browser-ui.c816
-rw-r--r--mail/folder-browser-ui.h36
-rw-r--r--mail/folder-browser.c2692
-rw-r--r--mail/folder-browser.h194
-rw-r--r--mail/mail-account-gui.c22
-rw-r--r--mail/mail-callbacks.c3239
-rw-r--r--mail/mail-callbacks.h144
-rw-r--r--mail/mail-display-stream.c104
-rw-r--r--mail/mail-display.c2995
-rw-r--r--mail/mail-display.h137
-rw-r--r--mail/mail-font-prefs.c130
-rw-r--r--mail/mail-font-prefs.h66
-rw-r--r--mail/mail-format.c2131
-rw-r--r--mail/mail-format.h71
-rw-r--r--mail/mail-identify.c130
-rw-r--r--mail/mail-local.c42
-rw-r--r--mail/mail-mt.c29
-rw-r--r--mail/mail-ops.c33
-rw-r--r--mail/mail-ops.h1
-rw-r--r--mail/mail-search.c403
-rw-r--r--mail/mail-search.glade197
-rw-r--r--mail/mail-search.h75
-rw-r--r--mail/mail-send-recv.c25
-rw-r--r--mail/mail-tools.c131
-rw-r--r--mail/mail-tools.h4
-rw-r--r--mail/mail-vfolder.c1
-rw-r--r--mail/mail.h2
-rw-r--r--mail/message-browser.c402
-rw-r--r--mail/message-browser.h63
-rw-r--r--mail/message-list.c490
-rw-r--r--mail/message-list.h73
-rw-r--r--mail/subscribe-dialog.c1663
-rw-r--r--mail/subscribe-dialog.glade2
-rw-r--r--mail/subscribe-dialog.h62
77 files changed, 16729 insertions, 15950 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog
index c443170f80..ff1d644a98 100644
--- a/mail/ChangeLog
+++ b/mail/ChangeLog
@@ -1,3 +1,24 @@
+2003-09-17 Not Zed <NotZed@Ximian.com>
+
+ * folder-browser.c, folder-browser.h, folder-browser-ui.c
+ folder-browser-ui.h, mail-callbacks.c, mail-callbacks.h
+ mail-display.c, mail-display.h, mail-display-stream.c
+ mail-display-stream.h, mail-format.c, mail-format.h
+ mail-identify.c, mail-search.c, mail-search.h
+ message-browser.c, message-browser.h, subscribe-dialog.c
+ subscribe-dialog.h, mail-font-prefs.c, mail-font-prefs.h: cvs
+ removed.
+
+ * Makefile.am: Removed mail-font-prefs.[ch], hasn't been built for
+ ages.
+
+ * em-*.c: killed a bunch of printfs.
+
+ * em-format-html-display.c (efhd_html_button_press_event): update
+ for html object api chagnes.
+
+ ** Merge in mail-refactor-2 branch.
+
2003-09-17 Jeffrey Stedfast <fejj@ximian.com>
* evolution-mbox-upgrade.c: New source file to migrate from the
@@ -168,6 +189,847 @@
CamelOperation argument, for overriding the status handler. Fixed
most calles to pass NULL to use the default.
+2003-09-15 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-browser.c (emfb_activate): remove warning about folder
+ not being loaded yet - it basically never is.
+
+ * em-popup.c (emp_standard_menu_factory): dont setup any global
+ select menu's yet. they're all handled by folderview atm.
+
+2003-09-11 Not Zed <NotZed@Ximian.com>
+
+ * em-format-html.c (efh_finalise, efh_format_timeout): Use a
+ proper hash free func, otherwise it dont work.
+
+2003-09-11 Not Zed <NotZed@Ximian.com>
+
+ * em-inline-filter.[ch]: A new class which implements an inline
+ snooper via a mime filter, so it is fully streamable. contents
+ merely passes through the filter.
+
+ * em-format-html.c (efh_finalise): free text substitute parts
+ table.
+ (efh_text_plain): transform a text part into a multipart, scanning
+ for inline data. Keep the multipart around for redraws.
+ (efh_format_timeout): clear the text substitute parts table.
+
+2003-09-10 Not Zed <NotZed@Ximian.com>
+
+ * em-format-html-display.c (efhd_init): hook onto realise so we
+ get the real theme-applied style.
+ (efhd_gtkhtml_realise): get the theme data for colour defaults.
+ Tweak the colour selection to make it work better with dark
+ themes.
+
+ * em-format-quote.c (emfq_format_source): we need to implement
+ this.
+ (emfq_format_error): we need not to call parent, it doesn't
+ implement it.
+
+ * message-list.c (message_list_select_uid): don't emit
+ changedhere, let it go through the table cursor change.
+
+ * em-folder-browser.c (em_folder_browser_show_preview): use
+ folderview.preview_active for this state.
+ (em_folder_browser_show_preview): clear the current message when
+ we turn off the message view, and load the current one when turn it on.
+
+ * em-folder-view.c (emfv_destroy): zero out preview + list.
+ (emfv_list_message_selected): check preview_active before doing
+ anything.
+ (emfv_edit_cut):
+ (emfv_edit_copy): only run if preview active.
+
+ * em-format-html.c (efh_format_do): output the proper html
+ headers, etc.
+
+2003-09-10 Jeffrey Stedfast <fejj@ximian.com>
+
+ * mail-account-gui.c (mail_account_gui_save): Allow the user to
+ select any fodler for his/her Drafts and Sent folders. Fixes bug
+ #45412.
+
+2003-09-09 Not Zed <NotZed@Ximian.com>
+
+ * em-utils.c (forward_non_attached): implement forward quoted.
+
+ * em-format-quote.[ch]: New class, em-format-html-quote wont cut
+ it. Sigh.
+
+ * em-format-html-quote.c (efhq_base_init): move type init stuff
+ here.
+ (efhq_complete): remove, this is a signal.
+ (efhq_multipart_related): we need to override the base class, we
+ don't want to output any attachment html.
+
+ * em-format-html-print.c (efhp_base_init): move builtin type init
+ to here.
+
+ * em-format-html.c (efh_init): get xmailer mask from gconf (sigh).
+ (efh_format_header): inmplement most of xmailer mask thing.
+ rupert icon not done yet, probably needs to be done as part of
+ em-format-html-display, sigh.
+ (type_builtin_table[]): add image/svg to supported image formats.
+ (efhd_base_init): move type init to here.
+ (efh_text_enriched): write to the filtered_stream, not stream.
+
+ * em-format.c (em_format_format_text): oops, actually use any
+ supplied charset.
+ (emf_base_init): move hashtable init into base_init, so we get a
+ new copy for each derived class too.
+
+ * mail-send-recv.c (build_dialogue): use an eclippedlabel for
+ status. Fixed all uses.
+
+2003-09-08 Not Zed <NotZed@Ximian.com>
+
+ * em-format-html.h (EMFormatHTML): added a simple_headers option,
+ only output headers in basic format. Added hide_headers option,
+ to disable all header output.
+
+ * em-format-html-quote.c (efhq_format_message): blockquote the
+ contnet, thats how you cite it!
+ (efhq_init): turn on simple headers for html output.
+ (efhq_format_message): output headers and part directly, bypassing
+ parent format_message.
+ (efhq_format_message): implement hide_headers.
+
+ * em-format-html.c (efh_busy): implement busy.
+ (efh_format_message): implement simple_headers option.
+ (efh_format_text_header): implement simple_headers option.
+ (efh_format_message): move the header formatting stuff into
+ exported em_format_html_format_headers.
+ (efh_format_message): only output headers if not hidden.
+
+ * em-format.c (emf_busy): base implementation of a new virtual
+ method, returns TRUE if the object is still busy
+ rendering/downloading stuff.
+
+ * em-utils.c (em_utils_message_to_html): renamed from
+ em_utils_quote_message. Also make sure the html conversion is
+ complete before getting the data.
+ (em_utils_part_to_html): similar, but for parts.
+ (composer_set_body): put in David Woodhouse's timezone in
+ attribution patch.
+ (composer_set_body): we want to quote the part (content), we don't
+ want message headers.
+ (em_utils_message_to_html): add a 'show headers' argument.
+
+ * folder-browser-factory.c (control_activate): removed.
+ (control_deactivate): removed.
+
+ * mail-identify.c:
+ * folder-browser.[ch], folder-browser-ui.[ch]:
+ * mail-callbacks.[ch], mail-search.[ch]:
+ * mail-display.[ch], mail-format.[ch], mail-display-stream.[ch]:
+ * message-browser.[ch]: Removed, fixed all users.
+
+ * component-factory.c (factory): change callbacks to em_utils
+ ones.
+ (configure_folder_popup): comment out some of the reconfigure
+ stuff. Wont have it in 1.6?
+ (handle_external_uri_cb): use em_utils stuff.
+ (user_create_new_item_cb): "
+ (owner_unset_cb): "
+
+ * em-composer-utils.c (composer_send_cb): rename to
+ em_utils_composer_send_cb, and export, sigh, needed for factory.
+ (composer_save_draft_cb): Same.
+
+2003-09-05 Not Zed <NotZed@Ximian.com>
+
+ * em-format.c (type_builtin_table[]): Add a fallback multipart/*
+ for other types (e.g. multipart/report)
+ (em_format_fallback_handler): fix some bad logic.
+
+ * em-folder-browser.c: track the pane size if the user changes it.
+ (emfb_set_folder): Added a bit of a mess that will select the
+ first unread message the first time you visit a folder.
+ (emfb_destroy): impelment. clear up outstanding signal handlers.
+ (emfb_list_built): Select the first unread message. this isn't
+ entirely reliable as yet, and not configurable at all.
+
+ * em-format-html-display.c (em_format_html_display_set_animate)
+ (em_format_html_display_set_caret_mode): guess? :)
+ (efhd_attachment_button): dont desensitise the button, just dont
+ hook onto it, otherwise it looks fugly.
+
+ * em-folder-view.c (emfv_list_done_message_selected): dont lookup
+ gconf values every time. use g_timeout_add rather than
+ gtk_timeout, remove fixme's.
+ (emfv_setting_notify): listner for gconf changes, reflect
+ internally.
+ (emfv_setting_setup): setup listner for gconf, and read initial
+ settings.
+ (emfv_activate): use local copy of settings rather than snooping
+ gconf.
+ (emfv_caret_mode): propagate caret-mode to display
+
+ * em-format-html-quote.c (efhq_format_message): remove gconf
+ stuff, our parent already has citation colour.
+
+ * em-format-html.c (efh_format_timeout): remove gconf stuff.
+ (em_format_html_set_load_http, em_format_html_set_mark_citations):
+ set options on formatter, re-renders if required.
+ (type_builtin_table[]): text/* should go to text/plain, not
+ text/enriched.
+
+2003-09-04 Not Zed <NotZed@Ximian.com>
+
+ * em-utils.c (confirm_expunge): rename it to emu_confirm_expunge
+ and remove leading whitespace before function.
+ (em_utils_expunge_folder): we want to expunge the folder, not
+ empty the trash. Jeff didn't even run this once ...
+
+ * em-popup.c: Lots of new features, 'global' popup menu's on a
+ per-selection type, via factories, popup selections (targets), a
+ standard factory for many menu items.
+ (emp_apps_open_in): duh, fix uri using logic
+ (emp_standard_menu_factory): only add apps to app list if
+ !requires_terminal.
+
+ * em-format-html-display.c (efhd_open_in, efhd_popup_free_items):
+ moved to em-popup.c
+ (efhd_popup_save_attachment, efhd_popup_save_message)
+ (efhd_popup_reply_sender, efhd_popup_reply_list)
+ (efhd_popup_reply_all, efhd_popup_forward): (re)moved to em-popup.c
+ (efhd_attachment_popup): use enew popu stuff.
+ (efhd_attachment_button): Scale the icons for mime-type icons.
+
+ * em-folder-view.c (em_folder_view_disable_mask): removed
+ ... moved to em-popup.
+ (em_folder_view_get_popup_target): new method, get the selection
+ target for the folder view.
+ (emfv_html_popup_saveas, emfv_html_popup_link_open)
+ (emfv_html_popup_link_copy, emfv_html_popup_address_send)
+ (emfv_html_popup_address_add, emfv_format_popup_free_items): moved
+ to em-popup.c
+ (emfv_format_popup_event): use new popup stuff.
+ (emfv_popup): use new popup stuff, but still just use all local
+ menu's.
+
+2003-09-03 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-view.c (emfv_format_popup_event): implement, a popup
+ menu for right-clicking on links and images.
+ (emfv_html_popup_link_copy): implement.
+ (emfv_init): setup an invisible for selection stuff.
+ (emfv_destroy): free invisible
+
+ * em-utils.c (em_utils_get_proxy_uri): utility to get the current
+ system proxy setting.
+ (emu_set_proxy): implementation.
+
+ * em-camel-stream.[ch]: removed.
+
+ * em-format-html.c (efh_url_requested, efh_format_timeout): use
+ em_html_stream rather than em_camel_stream.
+ (emfh_gethttp): set the system proxy on the new stream.
+ (emfh_multipart_related_check): use puri rather than purin inside
+ the loop - duh.
+ (emfh_multipart_related_check): removed 'unrelated part' warning,
+ they can be (and normally are) added by the callbacks.
+
+ * em-format-html.h (EMFormatHTMLJob): s/estream/stream/
+
+ * em-html-stream.[ch]: New subclass of emsyncstream, replacement
+ for em-camel-stream.
+
+2003-09-04 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-browser.c (emfb_folder_expunge): Call
+ em_utils_expunge_folder instead.
+
+ * em-utils.c (em_utils_expunge_folder): New function.
+ (confirm_expunge): Make private.
+
+2003-09-04 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-browser.c (emfb_folder_expunge): Confirm hat the user
+ wants to expunge.
+ (emfb_empty_trash): Implemented.
+
+ * em-utils.c (em_utils_prompt_user): Make public (used to be
+ e_question).
+ (em_utils_confirm_expunge): New function to confirm that the user
+ wants to expunge.
+ (em_utils_empty_trash): New function to empty all Trash folders.
+
+ * em-composer-utils.c: Get rid of e_question and use em-utils'
+ em_utils_prompt_user() function instead.
+
+ * em-format-html-quote.[c,h]: New formatter for quoting
+ replies/forwards/etc.
+
+ * em-utils.c (em_utils_quote_message): New function.
+ (composer_set_body): Use the new em_utils_quote_message()
+ function.
+ (em_utils_temp_save_part): Only g_free mfilename if it was
+ malloc'd.
+
+ * mail-tools.c (mail_tool_quote_message): Removed.
+ (mail_tool_forward_message): Removed.
+
+2003-09-03 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-format.c (em_format_class_remove_handler): New function to
+ remove a mime-type handler from a class.
+
+ * em-format-html.c (efh_init): Set the CITATION bit for the
+ default html flags.
+
+ * em-format-html-display.c: Fixed some compiler warnings by adding
+ appropriate includes.
+ (efhd_multipart_signed): Don't write "inlined signature...".
+
+2003-09-03 Not Zed <NotZed@Ximian.com>
+
+ * em-icon-stream.[ch]: New subclass of emsyncstream, write camel
+ stream one side, creates an icon on the other.
+
+ * em-sync-stream.[ch]: New file, an abstract class for creating
+ write-any-thread-act-gui-thread stuff. 'em-camel-stream' will
+ subclass this.
+
+ * em-format-html-display.c (efhd_attachment_button): setup a job
+ to write out an icon if the type is an image type, otherwise try
+ and get the icon directly. no caching yet. the system icons
+ aren't scaled properly either.
+ (efhd_write_icon_job): async job to write out image content.
+
+2003-09-02 Not Zed <NotZed@Ximian.com>
+
+ * em-format-html.c (emfh_new_job): renamed to
+ em_format_html_job_new and made public.
+ (emfh_queue_job): renamed to em_format_html_job_queue, and made
+ public.
+
+ * em-format-html.h: Made EMFormatHTMLJob a public structure.
+
+2003-09-02 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-view.h (struct _EMFolderView): track the uicomponent
+ while we're activated.
+
+ * em-message-browser.c (em_message_browser_window_new): kill
+ warning.
+ (emmb_init, emmb_finalise): kill printf
+
+ * em-format-html.c (efh_format_header): Converted code from head
+ from David Woodhouse <dwmw2@infradead.org>'s timezone display
+ patch.
+ (efh_format_text_header): support new flag, HEADER_HTML - header
+ alredy in html format.
+
+ * em-format-html-print.c (em_format_html_print_print): only ref
+ print_config if != NULL.
+
+ * em-folder-browser.c (emfb_tree_key_press): handle
+ space/backspace in messagelist to scroll the message view.
+ (emfb_create_view_menus): setup view menu's, this should probably
+ live in message-list.
+ (emfb_init): setup the folderbrowser enable map into the list.
+ (emfb_enable_map): folder browser enable map
+
+ * em-utils.c (em_utils_adjustment_page): new helper to scroll an
+ adjustment up/down 1 page.
+
+ * em-folder-view.c (emfv_list_double_click): implement, open
+ window.
+ (emfv_list_key_press): implement keybinding overrides. Enhance
+ delete key to undelete if everything is already deleted.
+ (emfv_build_enable_mask): separate out enable mask creation.
+ (emfv_popup): use above to get mask.
+ (emfv_enable_menus): enable/sensitize menus, use the same disable
+ mask system as used for the popups.
+ (emfv_destroy): change to use g_source_remove on seen_id.
+ (emfv_finalise): free up folders, clean up async event thing.
+ (emfv_init): setup an async event handler
+ (emfv_set_folder): handle hook/unhook of folder_changed events.
+ (emfv_folder_changed): proxy folder changed to main thread, ignore
+ the details of what changed.
+ (emfv_gui_folder_changed): update the menu's to reflect any folder
+ changes.
+ (emfv_build_disable_mask): added CAN_THREADED.
+ (em_folder_view_disable_mask): make public (rename from
+ emfv_build_disable_mask).
+ (emfv_enable_menus): changed to work on a list of arrays of
+ enablers, so they can be subclassed.
+ (emfv_init): add our enable map to the ui.
+ (em_folder_view_disable_mask): added support for can hidden (there
+ are hidden messages).
+
+2003-09-01 Not Zed <NotZed@Ximian.com>
+
+ * em-popup.c: New, simple menu-merging popup menu implementation.
+ NOTE: should be temporary, but needs something that has similar
+ merging facilities.
+
+ * em-folder-view.c (emfv_popup*): added popup callbacks, implement
+ a popup menu, using em_popup.
+ (emfv_message_*): replaced a whole bunch of one-line, or simple
+ functions with macro's to map to the popup implementation.
+ (emfv_tools_vfolder*, emfv_tools_filter*): map to popup
+ equivalents.
+ (emfv_init): drop printf
+
+ * em-format-html-display.c (efhd_attachment_popup): use the
+ em_popup stuff to build a dynamic menu.
+
+ * em-utils.c (em_utils_temp_save_part): change assignment order ot
+ kill warning.
+ (emu_get_save_filesel): handle null/empty name by appending / to
+ the filename.
+
+2003-08-30 Not Zed <NotZed@Ximian.com>
+
+ * mail-search.glade: forgot to add this yesterday.
+
+ * em-utils.h: don't include stuff we dont need to.
+
+ * em-folder-view.c (emfv_message_forward): just call
+ em_utils_forward_messages.
+
+ * em-format-html-display.c (em_format_html_display_search):
+ removed unused.
+ (efhd_drag_data_get): cleanup, use em_utils_temp_save_part.
+ (efhd_attachment_popup): quick hack, setup a bunch more menu
+ items, for forwarding inline messages, hook up saving parts, and
+ messages, and hook up the 'open in' menu.
+ (efhd_open_in): implement.
+
+ * em-utils.c (em_utils_save_message): Renamed to
+ em_utils_save_part.
+ (em_utils_filesel_prompt): removed, it just makes things more
+ complex than having a single response handler.
+ (em_utils_save_part): move dialog stuff here, it also creates a
+ name based on the type of part its given.
+ (emu_get_save_filesel): new method to create a fileselector with
+ standard options.
+ (emu_save_part_response): handle file selector response for save
+ part.
+ (can_save): renamed to emu_can_save
+ (em_utils_save_messages): use get_save_filesel
+ (emu_can_save): handle the path="" case
+ (em_utils_save_part): Add a prompt argument.
+ (filesel_ok_cb): removed.
+ (emu_update_save_path): update the gconf save_dir setting.
+ (em_utils_forward_messages): helper to forward using default
+ style.
+ (forward_non_attached): remove uids argument.
+ (em_utils_forward_message): helper to forward a message using the
+ default forward style.
+ (forward_non_attached): removed folder argument.
+ (em_utils_temp_save_part): helper to save a part to a temporary
+ file, e.g. for dnd, app launch, etc.
+
+2003-08-29 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-view.c: set 'outgoing' properly.
+
+ * em-folder-browser.c (emfb_tools_subscriptions): enforce a single
+ instance of the subscribe editor.
+ (emfb_subscribe_editor_destroy): clear subscribe editor handle.
+
+2003-08-29 Not Zed <NotZed@Ximian.com>
+
+ * em-camel-stream.c (em_camel_stream_new): Added some optional
+ logging code.
+ (stream_close): and here.
+ (stream_write): and here.
+
+ * em-folder-browser.c (emfb_init): remove fixme about search bar,
+ its there now. also fixme's about dnd/selection, they are handled
+ in lower-level widgets.
+ (em_folder_browser_show_preview): dont exit if show preview set,
+ but only if it hasn't changed.
+ (emfb_view_hide_selected, emfb_view_show_all)
+ (emfb_view_hide_read): removed some spurious printfs.
+
+ * Makefile.am (glade_DATA): Added mail-search.glade. FIXME:
+ should all glade files be merged into 1?
+
+ * em-format-html-display.c (efhd_format_clone): remove search
+ match count code from here - wont be finished rendering at this
+ point anyway.
+ (em_format_html_display_search): new api for running an
+ interactive search popup.
+ (efhd_update_matches, efhd_update_search)
+ (efhd_search_entry_activate, efhd_search_case_toggled)
+ (efhd_search_response): helpers/callbacks for search popup.
+ (efhd_class_init): hook into complete signal on EMFormat.
+ (efhd_complete): complete rendering handler, update match count.
+
+ * em-folder-view.c: removed fixme about api's - yes, do need two
+ set_folder api's.
+ (emfv_edit_cut, emfv_edit_copy): removed printfs
+ (emfv_edit_paste): removed commented call to html_paste, we never
+ want to do that.
+
+2003-08-29 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-view.c (emfv_message_mark_unread): If there is a
+ mark-as-read timeout handler registered, unregister it here.
+ (emfv_tools_filter_mlist): Implemented.
+ (emfv_tools_filter_recipient): Implemented.
+ (emfv_tools_filter_sender): Implemented.
+ (emfv_tools_filter_subject): Implemented.
+ (emfv_tools_vfolder_mlist): Implemented.
+ (emfv_tools_vfolder_recipient): Implemented.
+ (emfv_tools_vfolder_sender): Implemented.
+ (emfv_tools_vfolder_subject): Implemented.
+
+2003-08-28 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-browser.c (emfb_search_menu_activated)
+ (emfb_search_config_search, emfb_search_search_activated)
+ (emfb_search__query_changed): Implement search-bar callbacks.
+ (emfb_init): setup search bar.
+
+2003-08-28 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-view.c (emfv_message_reply): common reply code entry
+ point, also implement simple reply-to-highlighted text (currently
+ disabled).
+ (emfv_activate): disable resend message on non-sent folders.
+ (emfv_message_reply_all, emfv_message_reply_list)
+ (emfv_message_reply_sender): use message_reply for common code.
+ (em_folder_view_open_selected): in drafts or outbox, edit the
+ message instead.
+ (emfv_activate): force a sync on deactivate.
+
+ * em-utils.c (em_utils_selection_get_mailbox): get mailbox
+ (message/rfc822?) selection data.
+ (em_utils_read_messages_from_stream): helper to move stuff from a
+ mbox stream to a folder.
+ (em_utils_folder_is_drafts, em_utils_folder_is_sent)
+ (em_utils_folder_is_outbox): from folder browser helpers for
+ customising the user experience.
+
+ * message-list.c (message_list_construct): hook onto dnd stufd.
+ (ml_tree_drag_data_get): implement drag sending.
+ (ml_tree_drag_data_received): implement drag recieving.
+
+ * em-format-html-display.c (efhd_drag_data_get): implemented.
+ (efhd_drag_data_delete): implemented.
+ (efhd_attachment_button): setup dnd callbacks.
+
+2003-08-28 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-view.c (emfv_destroy): override the destroy method for
+ GtkObject - unregister the mark-as-seen timeout if one is
+ currently registered.
+ (emfv_list_done_message_selected): Add the mark-as-seen timeout
+ functionality here.
+ (emfv_format_link_clicked): Implemented.
+
+2003-08-27 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-utils.c (get_reply_list): Implemented.
+
+2003-08-27 Not Zed <NotZed@Ximian.com>
+
+ * em-message-browser.c (emmb_activate): disable Edit->Paste menu
+ always.
+
+ * em-folder-browser.c (emfb_edit_paste): do a message-list paste,
+ not a html one.
+ (emfb_edit_cut, emfb_edit_copy, emfb_edit_paste): Moved to folder-view.
+
+ * message-list.c (message_list_paste): trigger a paste action.
+ (message_list_set_folder): added a uri argument, and save it
+ internally, fixed all callers.
+ (message_list_finalise): free the folder uri.
+
+ * em-utils.c (em_utils_selection_set_mailbox): New helper to set
+ the current selection as text in a berkely mailbox format.
+ (em_utils_write_messages): helper to write stuff to a stream in
+ mbox format.
+
+2003-08-27 Not Zed <NotZed@Ximian.com>
+
+ * message-list.c (on_selection_changed_cmd): own/deown the primary
+ selection when it changes.
+ (message_list_init): init private data and invisible for
+ selection.
+ (message_list_destroy): free invisible.
+ (message_list_finalise): free private data.
+ (get_selected_cb): removed.
+ (message_list_copy): new method to do copy and cut. cut/copy to
+ the clipboard.
+ (ml_selection_clear_event): clear the right selection when
+ requested.
+ (message_list_has_primary_selection): helper to find out if the
+ message-list has the selection. is there a gtk way for this?
+
+2003-08-26 Not Zed <NotZed@Ximian.com>
+
+ * mail-local.c (mlf_meta_set, mlf_meta_get): proxy meta-data stuff
+ to subservient folder.
+
+2003-08-23 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-view.c (emfv_init): init preview here always.
+
+2003-08-25 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-view.c (em_folder_view_print): Use
+ e_dialog_set_transient_for().
+ (emfv_message_delete): Fixed a FIXME.
+
+ * em-folder-browser.c (emfb_edit_cut): Implemented.
+ (emfb_edit_copy): Implemented.
+ (emfb_edit_paste): Implemented.
+
+ * em-format-html-display.c (em_format_html_display_cut): New function.
+ (em_format_html_display_copy): New.
+ (em_format_html_display_paste): New.
+
+2003-08-25 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-utils.c (em_utils_flag_for_followup): Use
+ e_dialog_set_transient_for().
+ (em_utils_filesel_prompt): Same.
+ (post_reply_to_message): Here too.
+ (em_utils_edit_filters): Same.
+ (create_new_composer): And here.
+ (em_utils_compose_new_message_with_mailto): Here too.
+ (em_utils_post_to_url): "
+ (redirect_get_composer): Same.
+ (reply_get_composer): Again...
+
+ * em-folder-browser.c (emfb_tools_filters): Implemented.
+
+ * em-utils.c (em_utils_edit_filters): New function to open the
+ filter editor dialog.
+
+2003-08-22 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-utils.c (em_utils_flag_for_followup): Implemented.
+ (em_utils_flag_for_followup_clear): Implemented.
+ (em_utils_flag_for_followup_completed): Implemented.
+
+ * em-folder-view.c (emfv_message_followup_flag): Implemented.
+ (emfv_message_followup_clear): Implemented.
+ (emfv_message_followup_completed): Implemented.
+
+2003-08-22 Not Zed <NotZed@Ximian.com>
+
+ * em-camel-stream.c (em_camel_stream_new): now take the gtkhtml
+ too, and hook onto it's destroy so we don't try writing anymore
+ after its gone.
+ (stream_write, stream_flush, stream_close, emcs_gui_received):
+ NOOP if the gtkhtml has been destroyed.
+ (emcs_gtkhtml_destroy): null out the stream when the gtkhtml gets
+ destroyed, it is no longer valid.
+ (emcs_gui_received): dont try to soak all outstanding events, it
+ always runs synchronous anyway, just get one and exit.
+
+ * em-format-html.c (efh_gtkhtml_destroy): if the gtkhtml gets
+ destroyed, abort any pending timeouts/processing.
+ (efh_format_source): fixed implementation to write out all
+ headers.
+ (efh_multipart_related, emfh_multipart_related_check): separate
+ checking for unused parts into a separate job, which is run after
+ previous ones are executed. keep track of visibility tree level
+ in job, etc.
+
+2003-08-22 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-browser.c (emfb_mail_compose): Implemented.
+ (emfb_mail_post): Implemented.
+
+ * em-utils.c (em_utils_compose_new_message): New function.
+ (em_utils_compose_new_message_with_mailto): New.
+ (em_utils_post_to_url): New.
+
+2003-08-21 Not Zed <NotZed@Ximian.com>
+
+ * subscribe-dialog.glade: removed the text in the progress thing.
+ It never showed up anyway and caused weird resizing stuff when the
+ progress bar was active.
+
+ * em-subscribe-editor.c: Found the correct version of the new
+ subscribe code (on branch, duh!), and integrated it.
+ (sub_selection_changed): Sensitise buttons based on selection.
+
+ * em-format-html.c (efh_text_plain, efh_text_enriched)
+ (efh_write_text_html): Use format_text for text output.
+ (efh_write_image): use explicit image writer.
+ (emfh_gethttp): added some progress stuff.
+ (efh_format_do): maintain the accessible uri tree during jobs.
+ (efh_url_requested): store the current uri accessibility tree node
+ in the job, so it can be properly set for sub-jobs.
+ (emh_multipart_related): moved here, can't use super-class version
+ as it doesn't know about async jobs.
+ (type_buildin_table[]): Added image/jpg and image/jpeg for the
+ brokenmailers out there and to reduce the whinge.
+
+ * em-format.c (em_format_format_content): For text parts, perform
+ default charset/charset snooping/decoding. No longer closes the
+ stream once complete.
+ (emf_write_related): close stream ourselves.
+
+2003-08-21 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-folder-view.c (emfv_message_forward): Implemented.
+ (emfv_message_forward_attached): Implemented.
+ (emfv_message_forward_inline): Implemented.
+ (emfv_message_forward_quoted): Implemented.
+ (emfv_message_redirect): Implemented.
+ (emfv_message_post_reply): Implemented.
+ (emfv_message_reply_all): Implemented.
+ (emfv_message_reply_list): Implemented.
+ (emfv_message_reply_sender): Implemented.
+ (emfv_message_resend): Implemented.
+ (emfv_message_saveas): Implemented.
+
+ * em-composer-utils.c: New source file containing all the composer
+ send/draft callback mess.
+
+ * em-utils.c (em_utils_uids_copy): New convenience function to
+ copy a list of uids.
+ (em_utils_uids_free): New convenience function to free a list of
+ uids.
+ (em_utils_save_message): New function to save a CamelMimeMessage
+ (prompts the user for a location to save).
+ (em_utils_save_messages): New function to save a list of messages
+ (given a folder and list of uids).
+ (em_utils_configure_account): Configure a new account...
+ (em_utils_check_user_can_send_mail): Make sure the user has a
+ transport setup.
+ (em_utils_edit_message): New function to edit a message object.
+ (em_utils_edit_messages): New function to open a composer to edit
+ each message.
+ (em_utils_forward_attached): New function to forward messages as
+ an attachment,
+ (em_utils_forward_inline): Forward a bunch of messages inline.
+ (em_utils_forward_quoted): Forward a bunch of messages quoted.
+ (em_utils_redirect_message): Redirect a message object.
+ (em_utils_redirect_message_by_uid): Redirect a message given a
+ folder and uid.
+ (em_utils_reply_to_message): Reply to a message object.
+ (em_utils_reply_to_message_by_uid): Reply to a message given a
+ folder and uid.
+ (em_utils_post_reply_to_message_by_uid): Post a reply to a message
+ given a folder and uid.
+
+ * mail-ops.c (filter_folder_free): Use em_utils_uids_free().
+ (transfer_messages_free): Same.
+ (get_messages_free): Here too.
+ (save_messages_free): Same.
+
+2003-08-20 Not Zed <NotZed@Ximian.com>
+
+ * em-subscribe-editor.[ch]: new widget, a dialog for editing
+ subscriptions.
+
+ * em-format-html.c (efh_format_done): emit a complete when done.
+
+ * em-format.c (emf_class_init): Added a 'complete' signal, so that
+ printing knows when to print.
+
+ * em-format-html-print.c (em_format_html_print_print): Changed to
+ take the message and source formatter too. Runs an async render
+ then prints.
+
+2003-08-19 Not Zed <NotZed@Ximian.com>
+
+ * em-*.c: stacks more changes, added some bonobo menu setup, and
+ implemented the trivial functions.
+
+ * em-message-browser.[ch]: New message browser, inherits from
+ em-folder-view. Basically works.
+
+ * message-list.c (message_list_select_uid): if we're selecting
+ while still loading, setup a pending select.
+ (regen_list_free): Check for a pending select, and select the
+ message if we're now idle.
+
+ * em-folder-view.c (em_folder_view_set_message,
+ em_folder_view_set_folder): Make virtual macro's.
+ (emfv_control_activate): added hook to enable bonobo state when setup.
+
+ * em-format.c (emf_format_clone): base implementation, just clears
+ state data.
+
+ * em-format.h: change ::format to ::format_clone. Make
+ em_format_format_clone a macro/virtual method.
+
+ * mail-mt.c (em_channel_setup): new function to setup i/o
+ channels, so we can control the recursive flag. leave off for
+ now.
+ (mail_msg_init): setup MsgPort channels using above.
+
+ * em-format-html.c (efh_format): serialise/de-recursify formatting
+ via a timeout function.
+ (efh_format_timeout): keep polling to find out if cancellation is
+ complete, then kick off a new render.
+
+2003-08-18 Not Zed <NotZed@Ximian.com>
+
+ * em-*.c: more updates, incl threaded formatting queue.
+
+2003-08-18 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-format-html-display.c (em_format_html_display_zoom_in): New
+ method to zoom-in on the gtkhtml contents.
+ (em_format_html_display_zoom_out): Same but for zoom-out
+ (em_format_html_display_zoom_reset): You get the idea.
+
+2003-08-12 Jeffrey Stedfast <fejj@ximian.com>
+
+ * mail-callbacks.c (invert_selection): Use
+ message_list_invert_selection().
+ (select_thread): Use message_list_select_thread().
+ (select_all): Use message_list_select_all().
+
+ * message-list.c (message_list_select_all): New function.
+ (message_list_select_thread): New function.
+ (message_list_invert_selection): New function.
+
+2003-08-12 Jeffrey Stedfast <fejj@ximian.com>
+
+ * mail-session.c: Synced up with HEAD.
+
+ * component-factory.c:
+
+ * mail-folder-cache.[c,h]:
+
+ * mail-tools.h:
+
+ * mail-ops.[c,h]:
+
+ * mail-send-recv.c:
+
+ * mail-format.[c,h]:
+
+ * mail-display.c:
+
+ * mail-account-gui.c:
+
+ * mail-local.c:
+
+ * mail-offline-handler.c:
+
+ * subscribe-dialog.c:
+
+2003-08-12 Jeffrey Stedfast <fejj@ximian.com>
+
+ * em-format-html-display.c: Fixed some compiler warnings.
+
+ * em-format.c: Updated for new mime-parser changes made to HEAD.
+
+ * em-folder-view.c: Fixed some compiler warnings.
+
+ * em-format-html.c: Fixed some compiler warnings.
+
+2003-08-06 Not Zed <NotZed@Ximian.com>
+
+ * em-format-html-display.c (efhd_format_attachment): Added bonobo
+ embeddables.
+
2003-08-01 Harry Lu <harry.lu@sun.com>
*Fix for bug #6951
diff --git a/mail/Makefile.am b/mail/Makefile.am
index b1b93266cb..d7d2d347cc 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -70,12 +70,44 @@ libevolution_mail_la_SOURCES = \
component-factory.h \
e-searching-tokenizer.c \
e-searching-tokenizer.h \
- folder-browser.c \
- folder-browser.h \
+ em-inline-filter.c \
+ em-inline-filter.h \
+ em-folder-view.c \
+ em-folder-view.h \
+ em-folder-browser.c \
+ em-folder-browser.h \
+ em-format.c \
+ em-format.h \
+ em-format-html.c \
+ em-format-html.h \
+ em-format-html-display.c \
+ em-format-html-display.h \
+ em-format-html-print.c \
+ em-format-html-print.h \
+ em-format-html-quote.c \
+ em-format-html-quote.h \
+ em-format-quote.c \
+ em-format-quote.h \
+ em-marshal.c \
+ em-marshal.h \
+ em-message-browser.c \
+ em-message-browser.h \
+ em-composer-utils.c \
+ em-composer-utils.h \
+ em-popup.c \
+ em-popup.h \
+ em-utils.c \
+ em-utils.h \
+ em-subscribe-editor.c \
+ em-subscribe-editor.h \
+ em-sync-stream.c \
+ em-sync-stream.h \
+ em-icon-stream.c \
+ em-icon-stream.h \
+ em-html-stream.c \
+ em-html-stream.h \
folder-browser-factory.c \
folder-browser-factory.h \
- folder-browser-ui.c \
- folder-browser-ui.h \
folder-info.c \
folder-info.h \
mail-account-editor.c \
@@ -86,8 +118,6 @@ libevolution_mail_la_SOURCES = \
mail-accounts.h \
mail-autofilter.c \
mail-autofilter.h \
- mail-callbacks.c \
- mail-callbacks.h \
mail-composer-prefs.c \
mail-composer-prefs.h \
mail-config.c \
@@ -100,15 +130,8 @@ libevolution_mail_la_SOURCES = \
mail-config-factory.h \
mail-preferences.c \
mail-preferences.h \
- mail-display.c \
- mail-display.h \
- mail-display-stream.c \
- mail-display-stream.h \
mail-folder-cache.c \
mail-folder-cache.h \
- mail-format.h \
- mail-format.c \
- mail-identify.c \
mail-importer.c \
mail-importer.h \
mail-local.c \
@@ -119,8 +142,6 @@ libevolution_mail_la_SOURCES = \
mail-offline-handler.h \
mail-ops.c \
mail-ops.h \
- mail-search.c \
- mail-search.h \
mail-send-recv.c \
mail-send-recv.h \
mail-session.c \
@@ -132,22 +153,14 @@ libevolution_mail_la_SOURCES = \
mail-types.h \
mail-vfolder.c \
mail-vfolder.h \
- message-browser.c \
- message-browser.h \
message-list.c \
message-list.h \
message-tag-editor.c \
message-tag-editor.h \
message-tag-followup.c \
message-tag-followup.h \
- subscribe-dialog.c \
- subscribe-dialog.h \
mail.h
-# needs gtkhtml prop manager ported
-# mail-font-prefs.c \
-# mail-font-prefs.h
-
libevolution_mail_la_LIBADD = \
$(top_builddir)/shell/importer/libevolution-importer.la \
$(top_builddir)/camel/libcamel.la \
@@ -177,7 +190,10 @@ server_DATA = $(server_in_files:.server.in.in=.server)
sed -e "s|\@COMPONENTDIR\@|$(componentdir)|" $< > $@
@INTLTOOL_SERVER_RULE@
-glade_DATA = mail-config.glade local-config.glade subscribe-dialog.glade message-tags.glade
+MARSHAL_GENERATED = em-marshal.c em-marshal.h
+@EVO_MARSHAL_RULE@
+
+glade_DATA = mail-config.glade local-config.glade subscribe-dialog.glade message-tags.glade mail-search.glade
etspec_DATA = mail-accounts.etspec message-list.etspec subscribe-dialog.etspec
diff --git a/mail/component-factory.c b/mail/component-factory.c
index 8789bcde8a..9fda34bec0 100644
--- a/mail/component-factory.c
+++ b/mail/component-factory.c
@@ -49,7 +49,6 @@
#include "folder-browser-factory.h"
#include "evolution-shell-component.h"
#include "evolution-shell-component-dnd.h"
-#include "folder-browser.h"
#include "folder-info.h"
#include "mail.h"
#include "mail-config.h"
@@ -64,7 +63,7 @@
#include "mail-mt.h"
#include "mail-importer.h"
#include "mail-folder-cache.h"
-
+#include "em-utils.h"
#include "component-factory.h"
#include "mail-send-recv.h"
@@ -417,17 +416,21 @@ static void
configure_folder_popup(BonoboUIComponent *component, void *user_data, const char *cname)
{
char *uri = user_data;
+
+ /* FIXME: re-implement */
if (strncmp(uri, "vfolder:", 8) == 0)
vfolder_edit_rule(uri);
+#if 0
else {
- FolderBrowser *fb = folder_browser_factory_get_browser(uri);
+ struct _EMFolderBrowser *fb = folder_browser_factory_get_browser(uri);
if (fb)
configure_folder(component, fb, cname);
else
mail_local_reconfigure_folder(uri, NULL, NULL);
}
+#endif
}
static void
@@ -801,7 +804,7 @@ owner_set_cb (EvolutionShellComponent *shell_component,
{
/* setup the global quick-search context */
char *user = g_strdup_printf ("%s/searches.xml", evolution_dir);
- char *system = g_strdup (EVOLUTION_PRIVDATADIR "/vfoldertypes.xml");
+ char *system = g_strdup (EVOLUTION_PRIVDATADIR "/searchtypes.xml");
search_context = rule_context_new ();
g_object_set_data_full(G_OBJECT(search_context), "user", user, g_free);
@@ -876,7 +879,7 @@ handle_external_uri_cb (EvolutionShellComponent *shell_component,
/* FIXME: Sigh. This shouldn't be here. But the code is messy, so
I'll just put it here anyway. */
- send_to_url (uri, NULL);
+ em_utils_compose_new_message_with_mailto(NULL, uri);
}
static void
@@ -887,10 +890,11 @@ user_create_new_item_cb (EvolutionShellComponent *shell_component,
gpointer data)
{
if (!strcmp (id, "message")) {
- send_to_url (NULL, parent_folder_physical_uri);
+ em_utils_compose_new_message_with_mailto(NULL, NULL);
+ /*send_to_url (NULL, parent_folder_physical_uri);*/
return;
} else if (!strcmp (id, "post")) {
- post_to_url (parent_folder_physical_uri);
+ em_utils_post_to_url (NULL, parent_folder_physical_uri);
return;
}
@@ -928,7 +932,7 @@ owner_unset_cb (EvolutionShellComponent *shell_component, gpointer user_data)
g_signal_handler_disconnect((GtkObject *)shell_component, shell_component_handlers[i].hand);
if (gconf_client_get_bool (gconf, "/apps/evolution/mail/trash/empty_on_exit", NULL))
- empty_trash (NULL, NULL, NULL);
+ em_utils_empty_trash(NULL);
unref_standard_folders ();
mail_local_storage_shutdown ();
@@ -1617,7 +1621,7 @@ factory (BonoboGenericFactory *factory,
|| strcmp (component_id, MAIL_COMPOSER_PREFS_CONTROL_ID) == 0)
return mail_config_control_factory_cb (factory, component_id, evolution_shell_client_corba_objref (global_shell_client));
else if (strcmp(component_id, COMPOSER_IID) == 0)
- return (BonoboObject *)evolution_composer_new(composer_send_cb, composer_save_draft_cb);
+ return (BonoboObject *)evolution_composer_new(em_utils_composer_send_cb, em_utils_composer_save_draft_cb);
g_warning (FACTORY_ID ": Don't know what to do with %s", component_id);
return NULL;
diff --git a/mail/em-camel-stream.c b/mail/em-camel-stream.c
new file mode 100644
index 0000000000..a1274d634a
--- /dev/null
+++ b/mail/em-camel-stream.c
@@ -0,0 +1,326 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-object.h>
+#include <gtkhtml/gtkhtml.h>
+#include <gtkhtml/gtkhtml-stream.h>
+#include <gtk/gtkmain.h>
+#include "em-camel-stream.h"
+
+#include "mail-mt.h"
+
+#define EMCS_BUFFER_SIZE (4096)
+
+/*#define LOG_STREAM*/
+
+#define d(x)
+
+enum _write_msg_t {
+ EMCS_WRITE,
+ EMCS_FLUSH,
+ EMCS_CLOSE_OK,
+ EMCS_CLOSE_ERROR,
+};
+
+struct _write_msg {
+ EMsg msg;
+
+ enum _write_msg_t op;
+
+ const char *data;
+ size_t n;
+};
+
+static void em_camel_stream_class_init (EMCamelStreamClass *klass);
+static void em_camel_stream_init (CamelObject *object);
+static void em_camel_stream_finalize (CamelObject *object);
+
+static ssize_t stream_write(CamelStream *stream, const char *buffer, size_t n);
+static int stream_close(CamelStream *stream);
+static int stream_flush(CamelStream *stream);
+
+static CamelStreamClass *parent_class = NULL;
+
+CamelType
+em_camel_stream_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (CAMEL_STREAM_TYPE,
+ "EMCamelStream",
+ sizeof (EMCamelStream),
+ sizeof (EMCamelStreamClass),
+ (CamelObjectClassInitFunc) em_camel_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) em_camel_stream_init,
+ (CamelObjectFinalizeFunc) em_camel_stream_finalize);
+ }
+
+ return type;
+}
+
+static void
+em_camel_stream_class_init (EMCamelStreamClass *klass)
+{
+ CamelStreamClass *stream_class = CAMEL_STREAM_CLASS (klass);
+
+ parent_class = (CamelStreamClass *) CAMEL_STREAM_TYPE;
+
+ /* virtual method overload */
+ stream_class->write = stream_write;
+ stream_class->flush = stream_flush;
+ stream_class->close = stream_close;
+}
+
+static gboolean
+emcs_gui_received(GIOChannel *source, GIOCondition cond, void *data)
+{
+ EMCamelStream *estream = data;
+ struct _write_msg *msg;
+
+ d(printf("%p: gui sync op job waiting\n", estream));
+
+ msg = (struct _write_msg *)e_msgport_get(estream->data_port);
+ /* Should never happen ... */
+ if (msg == NULL)
+ return TRUE;
+
+ d(printf("%p: running sync op %d\n", estream, msg->op));
+
+ /* force out any pending data before doing anything else */
+ if (estream->used > 0) {
+ d(printf("sync write %d\n", estream->used));
+
+ if (estream->html_stream)
+ gtk_html_stream_write(estream->html_stream, estream->buffer, estream->used);
+ estream->used = 0;
+ }
+
+ switch (msg->op) {
+ case EMCS_WRITE:
+ d(printf("sync write %d\n", msg->n));
+ if (estream->html_stream)
+ gtk_html_stream_write(estream->html_stream, msg->data, msg->n);
+ break;
+ case EMCS_FLUSH:
+ stream_flush((CamelStream *)estream);
+ break;
+ case EMCS_CLOSE_OK:
+ if (estream->html_stream) {
+ gtk_html_stream_close(estream->html_stream, GTK_HTML_STREAM_OK);
+ estream->html_stream = NULL;
+ }
+ break;
+ case EMCS_CLOSE_ERROR:
+ if (estream->html_stream) {
+ gtk_html_stream_close(estream->html_stream, GTK_HTML_STREAM_ERROR);
+ estream->html_stream = NULL;
+ }
+ break;
+ }
+
+ e_msgport_reply((EMsg *)msg);
+ d(printf("%p: gui sync op jobs done\n", estream));
+
+ return TRUE;
+}
+
+static void
+em_camel_stream_init (CamelObject *object)
+{
+ EMCamelStream *estream = (EMCamelStream *)object;
+
+ estream->data_port = e_msgport_new();
+ estream->reply_port = e_msgport_new();
+
+ estream->gui_channel = g_io_channel_unix_new(e_msgport_fd(estream->data_port));
+ estream->gui_watch = g_io_add_watch(estream->gui_channel, G_IO_IN, emcs_gui_received, estream);
+
+ estream->used = 0;
+ estream->buffer = g_malloc(EMCS_BUFFER_SIZE);
+
+ d(printf("%p: new estream\n", estream));
+}
+
+static void
+sync_op(EMCamelStream *estream, enum _write_msg_t op, const char *data, size_t n)
+{
+ struct _write_msg msg;
+
+ d(printf("%p: launching sync op %d\n", estream, op));
+ /* we do everything synchronous, we should never have any locks, and
+ this prevents overflow from banked up data */
+ msg.msg.reply_port = estream->reply_port;
+ msg.op = op;
+ msg.data = data;
+ msg.n = n;
+ e_msgport_put(estream->data_port, &msg.msg);
+ e_msgport_wait(estream->reply_port);
+ g_assert(e_msgport_get(msg.msg.reply_port) == &msg.msg);
+ d(printf("%p: returned sync op %d\n", estream, op));
+}
+
+static void
+em_camel_stream_finalize (CamelObject *object)
+{
+ EMCamelStream *estream = (EMCamelStream *)object;
+
+ d(printf("%p: finalising stream\n", object));
+ if (estream->html_stream) {
+ d(printf("%p: html stream still open - error\n", object));
+ if (pthread_self() == mail_gui_thread)
+ gtk_html_stream_close(estream->html_stream, GTK_HTML_STREAM_ERROR);
+ else
+ sync_op(estream, EMCS_CLOSE_ERROR, NULL, 0);
+ }
+
+ /* TODO: is this stuff safe to do in another thread? */
+ g_source_remove(estream->gui_watch);
+ g_io_channel_unref(estream->gui_channel);
+ e_msgport_destroy(estream->data_port);
+ estream->data_port = NULL;
+ e_msgport_destroy(estream->reply_port);
+ estream->reply_port = NULL;
+ g_free(estream->buffer);
+}
+
+static ssize_t
+stream_write (CamelStream *stream, const char *buffer, size_t n)
+{
+ EMCamelStream *estream = EM_CAMEL_STREAM (stream);
+
+ if (estream->html_stream == NULL)
+ return -1;
+
+#ifdef LOG_STREAM
+ if (estream->save)
+ fwrite(buffer, sizeof(char), n, estream->save);
+#endif
+
+ if (pthread_self() == mail_gui_thread)
+ gtk_html_stream_write(estream->html_stream, buffer, n);
+ else {
+#if 1
+ size_t left = EMCS_BUFFER_SIZE-estream->used;
+
+ /* A super-simple buffer, if we get too much to fit, just do a sync
+ write, which will implicitly clear our previous writes first */
+ d(printf("thread write '%d'\n", n));
+
+ if (n >= left) {
+ sync_op(estream, EMCS_WRITE, buffer, n);
+ } else {
+ memcpy(estream->buffer + estream->used, buffer, n);
+ estream->used += n;
+ }
+#else
+ sync_op(estream, EMCS_WRITE, buffer, n);
+#endif
+ }
+ return (ssize_t) n;
+}
+
+static int
+stream_flush(CamelStream *stream)
+{
+ EMCamelStream *estream = (EMCamelStream *)stream;
+
+ if (estream->html_stream) {
+ if (pthread_self() == mail_gui_thread) {
+ /* FIXME: flush html stream via gtkhtml_stream_flush which doens't exist yet ... */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ } else {
+ sync_op(estream, EMCS_FLUSH, NULL, 0);
+ }
+ }
+
+ return 0;
+}
+
+static int
+stream_close(CamelStream *stream)
+{
+ EMCamelStream *estream = (EMCamelStream *)stream;
+
+ d(printf("%p: closing stream\n", stream));
+
+#ifdef LOG_STREAM
+ if (estream->save) {
+ fclose(estream->save);
+ estream->save = NULL;
+ }
+#endif
+
+ if (estream->html_stream) {
+ if (pthread_self() == mail_gui_thread) {
+ gtk_html_stream_close(estream->html_stream, GTK_HTML_STREAM_OK);
+ estream->html_stream = NULL;
+ } else {
+ sync_op(estream, EMCS_CLOSE_OK, NULL, 0);
+ }
+ }
+
+ return 0;
+}
+
+static void
+emcs_gtkhtml_destroy(struct _GtkHTML *html, EMCamelStream *emcs)
+{
+ d(printf("%p: emcs gtkhtml destroy\n", emcs));
+ emcs->html = NULL;
+ emcs->html_stream = NULL;
+}
+
+/* TODO: Could pass NULL for html_stream, and do a gtk_html_begin
+ on first data -> less flashing */
+CamelStream *
+em_camel_stream_new(struct _GtkHTML *html, struct _GtkHTMLStream *html_stream)
+{
+ EMCamelStream *new;
+
+ new = EM_CAMEL_STREAM (camel_object_new (EM_CAMEL_STREAM_TYPE));
+ new->html_stream = html_stream;
+ g_signal_connect(html, "destroy", G_CALLBACK(emcs_gtkhtml_destroy), new);
+
+#ifdef LOG_STREAM
+ {
+ static int count;
+ char name[32];
+
+ sprintf(name, "camel-stream.%d.html", count++);
+ printf("saving raw html to '%s'\n", name);
+ new->save = fopen(name, "w");
+ }
+#endif
+ return CAMEL_STREAM (new);
+}
diff --git a/mail/em-camel-stream.h b/mail/em-camel-stream.h
new file mode 100644
index 0000000000..955a15a5e2
--- /dev/null
+++ b/mail/em-camel-stream.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef EM_CAMEL_STREAM_H
+#define EM_CAMEL_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EM_CAMEL_STREAM_TYPE (em_camel_stream_get_type ())
+#define EM_CAMEL_STREAM(obj) (CAMEL_CHECK_CAST((obj), EM_CAMEL_STREAM_TYPE, EMCamelStream))
+#define EM_CAMEL_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_CAMEL_STREAM_TYPE, EMCamelStreamClass))
+#define MAIL_IS_DISPLAY_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_CAMEL_STREAM_TYPE))
+
+struct _GtkHTML;
+struct _GtkHTMLStream;
+
+#include <camel/camel-stream.h>
+#include "e-util/e-msgport.h"
+
+typedef struct _EMCamelStream {
+ CamelStream parent_stream;
+
+ struct _GtkHTML *html;
+ struct _GtkHTMLStream *html_stream;
+
+ struct _EMsgPort *data_port, *reply_port;
+ struct _GIOChannel *gui_channel;
+ guint gui_watch;
+ char *buffer;
+ int used;
+ void *save;
+} EMCamelStream;
+
+typedef struct {
+ CamelStreamClass parent_class;
+
+} EMCamelStreamClass;
+
+
+CamelType em_camel_stream_get_type (void);
+
+/* the html_stream is closed when we are finalised (with an error), or closed (ok) */
+CamelStream *em_camel_stream_new(struct _GtkHTML *html, struct _GtkHTMLStream *html_stream);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* EM_CAMEL_STREAM_H */
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
new file mode 100644
index 0000000000..e181c2cb33
--- /dev/null
+++ b/mail/em-composer-utils.c
@@ -0,0 +1,623 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "mail-mt.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "mail-config.h"
+#include "mail-session.h"
+#include "mail-send-recv.h"
+
+#include <e-util/e-dialog-utils.h> /* e_notice */
+
+#include "em-utils.h"
+#include "em-composer-utils.h"
+
+struct emcs_t {
+ unsigned int ref_count;
+
+ CamelFolder *drafts_folder;
+ char *drafts_uid;
+
+ CamelFolder *folder;
+ guint32 flags, set;
+ char *uid;
+};
+
+static struct emcs_t *
+emcs_new (void)
+{
+ struct emcs_t *emcs;
+
+ emcs = g_new (struct emcs_t, 1);
+ emcs->ref_count = 1;
+ emcs->drafts_folder = NULL;
+ emcs->drafts_uid = NULL;
+ emcs->folder = NULL;
+ emcs->flags = 0;
+ emcs->set = 0;
+ emcs->uid = NULL;
+
+ return emcs;
+}
+
+static void
+free_emcs (struct emcs_t *emcs)
+{
+ if (emcs->drafts_folder)
+ camel_object_unref (emcs->drafts_folder);
+ g_free (emcs->drafts_uid);
+
+ if (emcs->folder)
+ camel_object_unref (emcs->folder);
+ g_free (emcs->uid);
+ g_free (emcs);
+}
+
+static void
+emcs_ref (struct emcs_t *emcs)
+{
+ emcs->ref_count++;
+}
+
+static void
+emcs_unref (struct emcs_t *emcs)
+{
+ emcs->ref_count--;
+ if (emcs->ref_count == 0)
+ free_emcs (emcs);
+}
+
+static void
+composer_destroy_cb (gpointer user_data, GObject *deadbeef)
+{
+ emcs_unref (user_data);
+}
+
+static gboolean
+ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EDestination **recipients)
+{
+ gboolean show_again, res;
+ GConfClient *gconf;
+ GString *str;
+ int i;
+
+ gconf = mail_config_get_gconf_client ();
+
+ if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL))
+ return TRUE;
+
+ /* FIXME: this wording sucks */
+ str = g_string_new (_("You are sending an HTML-formatted message. Please make sure that\n"
+ "the following recipients are willing and able to receive HTML mail:\n"));
+ for (i = 0; recipients[i] != NULL; ++i) {
+ if (!e_destination_get_html_mail_pref (recipients[i])) {
+ const char *name;
+
+ name = e_destination_get_textrep (recipients[i], FALSE);
+
+ g_string_append_printf (str, " %s\n", name);
+ }
+ }
+
+ g_string_append (str, _("Send anyway?"));
+ res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again, "%s", str->str);
+ g_string_free (str, TRUE);
+
+ gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", show_again, NULL);
+
+ return res;
+}
+
+static gboolean
+ask_confirm_for_empty_subject (EMsgComposer *composer)
+{
+ gboolean show_again, res;
+ GConfClient *gconf;
+
+ gconf = mail_config_get_gconf_client ();
+
+ if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", NULL))
+ return TRUE;
+
+ res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again,
+ _("This message has no subject.\nReally send?"));
+
+ gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", show_again, NULL);
+
+ return res;
+}
+
+static gboolean
+ask_confirm_for_only_bcc (EMsgComposer *composer, gboolean hidden_list_case)
+{
+ gboolean show_again, res;
+ const char *first_text;
+ GConfClient *gconf;
+
+ gconf = mail_config_get_gconf_client ();
+
+ if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", NULL))
+ return TRUE;
+
+ /* If the user is mailing a hidden contact list, it is possible for
+ them to create a message with only Bcc recipients without really
+ realizing it. To try to avoid being totally confusing, I've changed
+ this dialog to provide slightly different text in that case, to
+ better explain what the hell is going on. */
+
+ if (hidden_list_case) {
+ first_text = _("Since the contact list you are sending to "
+ "is configured to hide the list's addresses, "
+ "this message will contain only Bcc recipients.");
+ } else {
+ first_text = _("This message contains only Bcc recipients.");
+ }
+
+ res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again,
+ "%s\n%s", first_text,
+ _("It is possible that the mail server may reveal the recipients "
+ "by adding an Apparently-To header.\nSend anyway?"));
+
+ gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", show_again, NULL);
+
+ return res;
+}
+
+
+struct _send_data {
+ struct emcs_t *emcs;
+ EMsgComposer *composer;
+ gboolean send;
+};
+
+static void
+composer_send_queued_cb (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info,
+ int queued, const char *appended_uid, void *data)
+{
+ struct emcs_t *emcs;
+ struct _send_data *send = data;
+
+ emcs = send->emcs;
+
+ if (queued) {
+ if (emcs && emcs->drafts_folder) {
+ /* delete the old draft message */
+ camel_folder_set_message_flags (emcs->drafts_folder, emcs->drafts_uid,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
+ camel_object_unref (emcs->drafts_folder);
+ emcs->drafts_folder = NULL;
+ g_free (emcs->drafts_uid);
+ emcs->drafts_uid = NULL;
+ }
+
+ if (emcs && emcs->folder) {
+ /* set any replied flags etc */
+ camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set);
+ camel_object_unref (emcs->folder);
+ emcs->folder = NULL;
+ g_free (emcs->uid);
+ emcs->uid = NULL;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (send->composer));
+
+ if (send->send && camel_session_is_online (session)) {
+ /* queue a message send */
+ mail_send ();
+ }
+ } else {
+ if (!emcs) {
+ /* disconnect the previous signal handlers */
+ g_signal_handlers_disconnect_matched (send->composer, G_SIGNAL_MATCH_FUNC, 0,
+ 0, NULL, em_utils_composer_send_cb, NULL);
+ g_signal_handlers_disconnect_matched (send->composer, G_SIGNAL_MATCH_FUNC, 0,
+ 0, NULL, em_utils_composer_save_draft_cb, NULL);
+
+ /* reconnect to the signals using a non-NULL emcs for the callback data */
+ em_composer_utils_setup_default_callbacks (send->composer);
+ }
+
+ e_msg_composer_set_enable_autosave (send->composer, TRUE);
+ gtk_widget_show (GTK_WIDGET (send->composer));
+ }
+
+ camel_message_info_free (info);
+
+ if (send->emcs)
+ emcs_unref (send->emcs);
+
+ g_object_unref (send->composer);
+ g_free (send);
+}
+
+static CamelMimeMessage *
+composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_object_data)
+{
+ CamelMimeMessage *message = NULL;
+ EDestination **recipients, **recipients_bcc;
+ gboolean send_html, confirm_html;
+ CamelInternetAddress *cia;
+ int hidden = 0, shown = 0;
+ int num = 0, num_bcc = 0;
+ const char *subject;
+ GConfClient *gconf;
+ EAccount *account;
+ int i;
+
+ gconf = mail_config_get_gconf_client ();
+
+ /* We should do all of the validity checks based on the composer, and not on
+ the created message, as extra interaction may occur when we get the message
+ (e.g. to get a passphrase to sign a message) */
+
+ /* get the message recipients */
+ recipients = e_msg_composer_get_recipients (composer);
+
+ cia = camel_internet_address_new ();
+
+ /* see which ones are visible/present, etc */
+ if (recipients) {
+ for (i = 0; recipients[i] != NULL; i++) {
+ const char *addr = e_destination_get_address (recipients[i]);
+
+ if (addr && addr[0]) {
+ camel_address_decode ((CamelAddress *) cia, addr);
+ if (camel_address_length ((CamelAddress *) cia) > 0) {
+ camel_address_remove ((CamelAddress *) cia, -1);
+ num++;
+ if (e_destination_is_evolution_list (recipients[i])
+ && !e_destination_list_show_addresses (recipients[i])) {
+ hidden++;
+ } else {
+ shown++;
+ }
+ }
+ }
+ }
+ }
+
+ recipients_bcc = e_msg_composer_get_bcc (composer);
+ if (recipients_bcc) {
+ for (i = 0; recipients_bcc[i] != NULL; i++) {
+ const char *addr = e_destination_get_address (recipients_bcc[i]);
+
+ if (addr && addr[0]) {
+ camel_address_decode ((CamelAddress *) cia, addr);
+ if (camel_address_length ((CamelAddress *) cia) > 0) {
+ camel_address_remove ((CamelAddress *) cia, -1);
+ num_bcc++;
+ }
+ }
+ }
+
+ e_destination_freev (recipients_bcc);
+ }
+
+ camel_object_unref (cia);
+
+ /* I'm sensing a lack of love, er, I mean recipients. */
+ if (num == 0 && !post) {
+ e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING,
+ _("You must specify recipients in order to send this message."));
+ goto finished;
+ }
+
+ if (num > 0 && (num == num_bcc || shown == 0)) {
+ /* this means that the only recipients are Bcc's */
+ if (!ask_confirm_for_only_bcc (composer, shown == 0))
+ goto finished;
+ }
+
+ send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL);
+ confirm_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL);
+
+ /* Only show this warning if our default is to send html. If it isn't, we've
+ manually switched into html mode in the composer and (presumably) had a good
+ reason for doing this. */
+ if (e_msg_composer_get_send_html (composer) && send_html && confirm_html) {
+ gboolean html_problem = FALSE;
+
+ if (recipients) {
+ for (i = 0; recipients[i] != NULL && !html_problem; i++) {
+ if (!e_destination_get_html_mail_pref (recipients[i]))
+ html_problem = TRUE;
+ }
+ }
+
+ if (html_problem) {
+ html_problem = !ask_confirm_for_unwanted_html_mail (composer, recipients);
+ if (html_problem)
+ goto finished;
+ }
+ }
+
+ /* Check for no subject */
+ subject = e_msg_composer_get_subject (composer);
+ if (subject == NULL || subject[0] == '\0') {
+ if (!ask_confirm_for_empty_subject (composer))
+ goto finished;
+ }
+
+ /* actually get the message now, this will sign/encrypt etc */
+ message = e_msg_composer_get_message (composer, save_html_object_data);
+ if (message == NULL)
+ goto finished;
+
+ /* Add info about the sending account */
+ account = e_msg_composer_get_preferred_account (composer);
+
+ if (account) {
+ camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Account", account->name);
+ camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Transport", account->transport->url);
+ camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Fcc", account->sent_folder_uri);
+ if (account->id->organization && *account->id->organization)
+ camel_medium_set_header (CAMEL_MEDIUM (message), "Organization", account->id->organization);
+ }
+
+ /* Get the message recipients and 'touch' them, boosting their use scores */
+ if (recipients)
+ e_destination_touchv (recipients);
+
+ finished:
+
+ if (recipients)
+ e_destination_freev (recipients);
+
+ return message;
+}
+
+static void
+got_post_folder (char *uri, CamelFolder *folder, void *data)
+{
+ CamelFolder **fp = data;
+
+ *fp = folder;
+
+ if (folder)
+ camel_object_ref (folder);
+}
+
+void
+em_utils_composer_send_cb (EMsgComposer *composer, gpointer user_data)
+{
+ extern CamelFolder *outbox_folder;
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+ struct _send_data *send;
+ gboolean post = FALSE;
+ CamelFolder *folder;
+ XEvolution *xev;
+ char *url;
+
+ url = e_msg_composer_hdrs_get_post_to ((EMsgComposerHdrs *) composer->hdrs);
+ if (url && *url) {
+ post = TRUE;
+
+ mail_msg_wait (mail_get_folder (url, 0, got_post_folder, &folder, mail_thread_new));
+
+ if (!folder) {
+ g_free (url);
+ return;
+ }
+ } else {
+ folder = outbox_folder;
+ camel_object_ref (folder);
+ }
+
+ g_free (url);
+
+ message = composer_get_message (composer, post, FALSE);
+ if (!message)
+ return;
+
+ if (post) {
+ /* Remove the X-Evolution* headers if we are in Post-To mode */
+ xev = mail_tool_remove_xevolution_headers (message);
+ mail_tool_destroy_xevolution (xev);
+ }
+
+ info = camel_message_info_new ();
+ info->flags = CAMEL_MESSAGE_SEEN;
+
+ send = g_malloc (sizeof (*send));
+ send->emcs = user_data;
+ if (send->emcs)
+ emcs_ref (send->emcs);
+ send->send = !post;
+ send->composer = composer;
+ g_object_ref (composer);
+ gtk_widget_hide (GTK_WIDGET (composer));
+
+ e_msg_composer_set_enable_autosave (composer, FALSE);
+
+ mail_append_mail (folder, message, info, composer_send_queued_cb, send);
+ camel_object_unref (message);
+ camel_object_unref (folder);
+}
+
+struct _save_draft_info {
+ struct emcs_t *emcs;
+ EMsgComposer *composer;
+ int quit;
+};
+
+static void
+save_draft_done (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, int ok,
+ const char *appended_uid, void *user_data)
+{
+ struct _save_draft_info *sdi = user_data;
+ struct emcs_t *emcs;
+ CORBA_Environment ev;
+
+ if (!ok)
+ goto done;
+ CORBA_exception_init (&ev);
+ GNOME_GtkHTML_Editor_Engine_runCommand (sdi->composer->editor_engine, "saved", &ev);
+ CORBA_exception_free (&ev);
+
+ if ((emcs = sdi->emcs) == NULL) {
+ emcs = emcs_new ();
+
+ /* disconnect the previous signal handlers */
+ g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (em_utils_composer_send_cb), NULL);
+ g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (em_utils_composer_save_draft_cb), NULL);
+
+ /* reconnect to the signals using a non-NULL emcs for the callback data */
+ em_composer_utils_setup_default_callbacks (sdi->composer);
+ }
+
+ if (emcs->drafts_folder) {
+ /* delete the original draft message */
+ camel_folder_set_message_flags (emcs->drafts_folder, emcs->drafts_uid,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
+ camel_object_unref (emcs->drafts_folder);
+ emcs->drafts_folder = NULL;
+ g_free (emcs->drafts_uid);
+ emcs->drafts_uid = NULL;
+ }
+
+ if (emcs->folder) {
+ /* set the replied flags etc */
+ camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set);
+ camel_object_unref (emcs->folder);
+ emcs->folder = NULL;
+ g_free (emcs->uid);
+ emcs->uid = NULL;
+ }
+
+ if (appended_uid) {
+ camel_object_ref (folder);
+ emcs->drafts_folder = folder;
+ emcs->drafts_uid = g_strdup (appended_uid);
+ }
+
+ if (sdi->quit)
+ gtk_widget_destroy (GTK_WIDGET (sdi->composer));
+
+ done:
+ g_object_unref (sdi->composer);
+ if (sdi->emcs)
+ emcs_unref (sdi->emcs);
+ g_free (info);
+ g_free (sdi);
+}
+
+static void
+save_draft_folder (char *uri, CamelFolder *folder, gpointer data)
+{
+ CamelFolder **save = data;
+
+ if (folder) {
+ *save = folder;
+ camel_object_ref (folder);
+ }
+}
+
+void
+em_utils_composer_save_draft_cb (EMsgComposer *composer, int quit, gpointer user_data)
+{
+ extern char *default_drafts_folder_uri;
+ extern CamelFolder *drafts_folder;
+ struct _save_draft_info *sdi;
+ CamelFolder *folder = NULL;
+ CamelMimeMessage *msg;
+ CamelMessageInfo *info;
+ EAccount *account;
+
+ account = e_msg_composer_get_preferred_account (composer);
+ if (account && account->drafts_folder_uri &&
+ strcmp (account->drafts_folder_uri, default_drafts_folder_uri) != 0) {
+ int id;
+
+ id = mail_get_folder (account->drafts_folder_uri, 0, save_draft_folder, &folder, mail_thread_new);
+ mail_msg_wait (id);
+
+ if (!folder) {
+ if (!em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, NULL,
+ _("Unable to open the drafts folder for this account.\n"
+ "Would you like to use the default drafts folder?")))
+ return;
+
+ folder = drafts_folder;
+ camel_object_ref (drafts_folder);
+ }
+ } else {
+ folder = drafts_folder;
+ camel_object_ref (folder);
+ }
+
+ msg = e_msg_composer_get_message_draft (composer);
+
+ info = g_new0 (CamelMessageInfo, 1);
+ info->flags = CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN;
+
+ sdi = g_malloc (sizeof (struct _save_draft_info));
+ sdi->composer = composer;
+ g_object_ref (composer);
+ sdi->emcs = user_data;
+ if (sdi->emcs)
+ emcs_ref (sdi->emcs);
+ sdi->quit = quit;
+
+ mail_append_mail (folder, msg, info, save_draft_done, sdi);
+ camel_object_unref (folder);
+ camel_object_unref (msg);
+}
+
+
+void
+em_composer_utils_setup_callbacks (EMsgComposer *composer, CamelFolder *folder, const char *uid,
+ guint32 flags, guint32 set, CamelFolder *drafts, const char *drafts_uid)
+{
+ struct emcs_t *emcs;
+
+ emcs = emcs_new ();
+
+ if (folder && uid) {
+ camel_object_ref (folder);
+ emcs->folder = folder;
+ emcs->uid = g_strdup (uid);
+ emcs->flags = flags;
+ emcs->set = set;
+ }
+
+ if (drafts && drafts_uid) {
+ camel_object_ref (drafts);
+ emcs->drafts_folder = drafts;
+ emcs->drafts_uid = g_strdup (drafts_uid);
+ }
+
+ g_signal_connect (composer, "send", G_CALLBACK (em_utils_composer_send_cb), emcs);
+ g_signal_connect (composer, "save-draft", G_CALLBACK (em_utils_composer_save_draft_cb), emcs);
+
+ g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, emcs);
+}
diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h
new file mode 100644
index 0000000000..7e4aeb0274
--- /dev/null
+++ b/mail/em-composer-utils.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __EM_COMPOSER_UTILS_H__
+#define __EM_COMPOSER_UTILS_H__
+
+#include <composer/e-msg-composer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+void em_composer_utils_setup_callbacks (EMsgComposer *composer, CamelFolder *folder, const char *uid,
+ guint32 flags, guint32 set, CamelFolder *drafts, const char *drafts_uid);
+
+#define em_composer_utils_setup_default_callbacks(composer) em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, NULL, NULL)
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EM_COMPOSER_UTILS_H__ */
diff --git a/mail/em-folder-browser.c b/mail/em-folder-browser.c
new file mode 100644
index 0000000000..7341f91543
--- /dev/null
+++ b/mail/em-folder-browser.c
@@ -0,0 +1,933 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkvpaned.h>
+#include <gtkhtml/gtkhtml.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <libgnomeprintui/gnome-print-dialog.h>
+
+#include "mail-mt.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "mail-config.h"
+
+#include <e-util/e-passwords.h>
+#include <e-util/e-dialog-utils.h>
+
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-filter-enriched.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-url.h>
+
+#include <bonobo/bonobo-main.h>
+#include <bonobo/bonobo-object.h>
+#include <bonobo/bonobo-generic-factory.h>
+#include <bonobo/bonobo-control.h>
+#include <bonobo/bonobo-ui-component.h>
+#include <bonobo/bonobo-ui-util.h>
+
+/* for efilterbar stuff */
+#include <e-util/e-sexp.h>
+#include "mail-vfolder.h"
+#include "filter/vfolder-rule.h"
+#include <widgets/misc/e-filter-bar.h>
+#include <camel/camel-search-private.h>
+
+/* gal view crap */
+#include <gal/menus/gal-view-etable.h>
+#include <gal/menus/gal-view-instance.h>
+#include <gal/menus/gal-view-factory-etable.h>
+#include "widgets/menus/gal-view-menus.h"
+
+#include "e-util/e-dialog-utils.h"
+#include "em-utils.h"
+#include "em-format-html-display.h"
+#include "em-format-html-print.h"
+#include "em-folder-browser.h"
+#include "em-subscribe-editor.h"
+#include "message-list.h"
+
+#include "mail-ops.h"
+
+#include "evolution-shell-component-utils.h" /* Pixmap stuff, sigh */
+
+#define d(x)
+
+struct _EMFolderBrowserPrivate {
+ GtkWidget *preview; /* container for message display */
+
+ GtkWidget *subscribe_editor;
+
+ GalViewInstance *view_instance;
+ GalViewMenus *view_menus;
+
+ guint vpane_resize_id;
+ guint list_built_id; /* hook onto list-built for delayed 'select first unread' stuff */
+};
+
+static void emfb_activate(EMFolderView *emfv, BonoboUIComponent *uic, int state);
+static void emfb_set_folder(EMFolderView *emfv, CamelFolder *folder, const char *uri);
+
+/* FilterBar stuff ... */
+static void emfb_search_config_search(EFilterBar *efb, FilterRule *rule, int id, const char *query, void *data);
+static void emfb_search_menu_activated(ESearchBar *esb, int id, EMFolderBrowser *fb);
+static void emfb_search_search_activated(ESearchBar *esb, EMFolderBrowser *emfb);
+static void emfb_search_query_changed(ESearchBar *esb, EMFolderBrowser *fb);
+
+static int emfb_list_key_press(ETree *tree, int row, ETreePath path, int col, GdkEvent *ev, EMFolderBrowser *fb);
+
+static const EMFolderViewEnable emfb_enable_map[];
+
+enum {
+ ESB_SAVE,
+};
+
+static ESearchBarItem emfb_search_items[] = {
+ E_FILTERBAR_ADVANCED,
+ { NULL, 0, NULL },
+ E_FILTERBAR_SAVE,
+ E_FILTERBAR_EDIT,
+ { NULL, 0, NULL },
+ { N_("Create _Virtual Folder From Search..."), ESB_SAVE, NULL },
+ { NULL, -1, NULL }
+};
+
+static EMFolderViewClass *emfb_parent;
+
+/* Needed since the paned wont take the position its given otherwise ... */
+static void
+emfb_pane_realised(GtkWidget *w, EMFolderBrowser *emfb)
+{
+ GConfClient *gconf;
+
+ gconf = mail_config_get_gconf_client ();
+ gtk_paned_set_position((GtkPaned *)emfb->vpane, gconf_client_get_int(gconf, "/apps/evolution/mail/display/paned_size", NULL));
+}
+
+static gboolean
+emfb_pane_button_release_event(GtkWidget *w, GdkEventButton *e, EMFolderBrowser *emfb)
+{
+ GConfClient *gconf = mail_config_get_gconf_client ();
+
+ if (GTK_WIDGET_REALIZED (w))
+ gconf_client_set_int(gconf, "/apps/evolution/mail/display/paned_size",
+ gtk_paned_get_position(GTK_PANED(w)), NULL);
+
+ return FALSE;
+}
+
+static void
+emfb_init(GObject *o)
+{
+ EMFolderBrowser *emfb = (EMFolderBrowser *)o;
+ struct _EMFolderBrowserPrivate *p;
+ /* FIXME ... */
+ extern RuleContext *search_context;
+
+ p = emfb->priv = g_malloc0(sizeof(struct _EMFolderBrowserPrivate));
+
+ emfb->view.preview_active = TRUE;
+
+ g_slist_free(emfb->view.ui_files);
+ emfb->view.ui_files = g_slist_append(NULL, EVOLUTION_UIDIR "/evolution-mail-global.xml");
+ emfb->view.ui_files = g_slist_append(emfb->view.ui_files, EVOLUTION_UIDIR "/evolution-mail-list.xml");
+ emfb->view.ui_files = g_slist_append(emfb->view.ui_files, EVOLUTION_UIDIR "/evolution-mail-message.xml");
+
+ emfb->view.enable_map = g_slist_prepend(emfb->view.enable_map, (void *)emfb_enable_map);
+
+ if (search_context) {
+ const char *systemrules = g_object_get_data (G_OBJECT (search_context), "system");
+ const char *userrules = g_object_get_data (G_OBJECT (search_context), "user");
+
+ emfb->search = e_filter_bar_new(search_context, systemrules, userrules, emfb_search_config_search, emfb);
+ e_search_bar_set_menu ((ESearchBar *)emfb->search, emfb_search_items);
+ gtk_widget_show((GtkWidget *)emfb->search);
+
+ g_signal_connect(emfb->search, "menu_activated", G_CALLBACK(emfb_search_menu_activated), emfb);
+ g_signal_connect(emfb->search, "search_activated", G_CALLBACK(emfb_search_search_activated), emfb);
+ g_signal_connect(emfb->search, "query_changed", G_CALLBACK(emfb_search_query_changed), emfb);
+
+ gtk_box_pack_start((GtkBox *)emfb, (GtkWidget *)emfb->search, FALSE, TRUE, 0);
+ }
+
+ emfb->vpane = gtk_vpaned_new();
+ g_signal_connect(emfb->vpane, "realize", G_CALLBACK(emfb_pane_realised), emfb);
+ emfb->priv->vpane_resize_id = g_signal_connect(emfb->vpane, "button_release_event", G_CALLBACK(emfb_pane_button_release_event), emfb);
+
+ gtk_widget_show(emfb->vpane);
+
+ gtk_box_pack_start_defaults((GtkBox *)emfb, emfb->vpane);
+
+ gtk_paned_add1((GtkPaned *)emfb->vpane, (GtkWidget *)emfb->view.list);
+ gtk_widget_show((GtkWidget *)emfb->view.list);
+
+ /* currently: just use a scrolledwindow for preview widget */
+ p->preview = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy((GtkScrolledWindow *)p->preview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type((GtkScrolledWindow *)p->preview, GTK_SHADOW_IN);
+ gtk_widget_show(p->preview);
+
+ gtk_container_add((GtkContainer *)p->preview, (GtkWidget *)emfb->view.preview->formathtml.html);
+ gtk_widget_show((GtkWidget *)emfb->view.preview->formathtml.html);
+
+ gtk_paned_add2((GtkPaned *)emfb->vpane, p->preview);
+ gtk_widget_show(p->preview);
+
+ g_signal_connect(emfb->view.list->tree, "key_press", G_CALLBACK(emfb_list_key_press), emfb);
+}
+
+static void
+emfb_finalise(GObject *o)
+{
+ EMFolderBrowser *emfb = (EMFolderBrowser *)o;
+
+ g_free(emfb->priv);
+
+ ((GObjectClass *)emfb_parent)->finalize(o);
+}
+
+static void
+emfb_destroy(GtkObject *o)
+{
+ EMFolderBrowser *emfb = (EMFolderBrowser *)o;
+
+ if (emfb->priv->list_built_id) {
+ g_signal_handler_disconnect(((EMFolderView *)emfb)->list, emfb->priv->list_built_id);
+ emfb->priv->list_built_id = 0;
+ }
+
+ ((GtkObjectClass *)emfb_parent)->destroy(o);
+}
+
+static void
+emfb_class_init(GObjectClass *klass)
+{
+ klass->finalize = emfb_finalise;
+ ((GtkObjectClass *)klass)->destroy = emfb_destroy;
+ ((EMFolderViewClass *)klass)->set_folder = emfb_set_folder;
+ ((EMFolderViewClass *)klass)->activate = emfb_activate;
+}
+
+GType
+em_folder_browser_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFolderBrowserClass),
+ NULL, NULL,
+ (GClassInitFunc)emfb_class_init,
+ NULL, NULL,
+ sizeof(EMFolderBrowser), 0,
+ (GInstanceInitFunc)emfb_init
+ };
+ emfb_parent = g_type_class_ref(em_folder_view_get_type());
+ type = g_type_register_static(em_folder_view_get_type(), "EMFolderBrowser", &info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *em_folder_browser_new(void)
+{
+ EMFolderBrowser *emfb = g_object_new(em_folder_browser_get_type(), 0);
+
+ return (GtkWidget *)emfb;
+}
+
+void em_folder_browser_show_preview(EMFolderBrowser *emfb, gboolean state)
+{
+ if ((emfb->view.preview_active ^ state) == 0
+ || emfb->view.list == NULL)
+ return;
+
+ emfb->view.preview_active = state;
+
+ if (state) {
+ GConfClient *gconf = mail_config_get_gconf_client ();
+ int paned_size /*, y*/;
+
+ paned_size = gconf_client_get_int(gconf, "/apps/evolution/mail/display/paned_size", NULL);
+
+ /*y = save_cursor_pos (emfb);*/
+ gtk_paned_set_position (GTK_PANED (emfb->vpane), paned_size);
+ gtk_widget_show (GTK_WIDGET (emfb->priv->preview));
+
+ if (emfb->view.list->cursor_uid)
+ message_list_select_uid(emfb->view.list, emfb->view.list->cursor_uid);
+
+ /* need to load/show the current message? */
+ /*do_message_selected (emfb);*/
+ /*set_cursor_pos (emfb, y);*/
+ } else {
+ em_format_format((EMFormat *)emfb->view.preview, NULL);
+ gtk_widget_hide(emfb->priv->preview);
+ /*
+ mail_display_set_message (emfb->mail_display, NULL, NULL, NULL);
+ emfb_ui_message_loaded (emfb);*/
+ }
+
+ /* FIXME: need to update menu's to reflect ui changes */
+}
+
+/* ********************************************************************** */
+
+/* FIXME: Need to separate system rules from user ones */
+/* FIXME: Ugh! */
+
+static void
+emfb_search_menu_activated(ESearchBar *esb, int id, EMFolderBrowser *emfb)
+{
+ EFilterBar *efb = (EFilterBar *)esb;
+
+ d(printf("menu activated\n"));
+
+ switch (id) {
+ case ESB_SAVE:
+ d(printf("Save vfolder\n"));
+ if (efb->current_query) {
+ FilterRule *rule = vfolder_clone_rule(efb->current_query);
+ char *name, *text;
+
+ text = e_search_bar_get_text(esb);
+ name = g_strdup_printf("%s %s", rule->name, (text&&text[0])?text:"''");
+ g_free (text);
+ filter_rule_set_name(rule, name);
+ g_free (name);
+
+ filter_rule_set_source(rule, FILTER_SOURCE_INCOMING);
+ vfolder_rule_add_source((VfolderRule *)rule, emfb->view.folder_uri);
+ vfolder_gui_add_rule((VfolderRule *)rule);
+ }
+ break;
+ }
+}
+
+static void
+emfb_search_config_search(EFilterBar *efb, FilterRule *rule, int id, const char *query, void *data)
+{
+ EMFolderBrowser *emfb = data;
+ GList *partl;
+ struct _camel_search_words *words;
+ int i;
+ GSList *strings = NULL;
+
+ /* we scan the parts of a rule, and set all the types we know about to the query string */
+ partl = rule->parts;
+ while (partl) {
+ FilterPart *part = partl->data;
+
+ if (!strcmp(part->name, "subject")) {
+ FilterInput *input = (FilterInput *)filter_part_find_element(part, "subject");
+ if (input)
+ filter_input_set_value(input, query);
+ } else if (!strcmp(part->name, "body")) {
+ FilterInput *input = (FilterInput *)filter_part_find_element(part, "word");
+ if (input)
+ filter_input_set_value(input, query);
+
+ words = camel_search_words_split(query);
+ for (i=0;i<words->len;i++)
+ strings = g_slist_prepend(strings, g_strdup(words->words[i]->word));
+ camel_search_words_free (words);
+ } else if(!strcmp(part->name, "sender")) {
+ FilterInput *input = (FilterInput *)filter_part_find_element(part, "sender");
+ if (input)
+ filter_input_set_value(input, query);
+ } else if(!strcmp(part->name, "to")) {
+ FilterInput *input = (FilterInput *)filter_part_find_element(part, "recipient");
+ if (input)
+ filter_input_set_value(input, query);
+ }
+
+ partl = partl->next;
+ }
+
+ em_format_html_display_set_search(emfb->view.preview,
+ EM_FORMAT_HTML_DISPLAY_SEARCH_SECONDARY|EM_FORMAT_HTML_DISPLAY_SEARCH_ICASE,
+ strings);
+ while (strings) {
+ GSList *n = strings->next;
+
+ g_free(strings->data);
+ g_slist_free_1(strings);
+ strings = n;
+ }
+}
+
+static void
+emfb_search_search_activated(ESearchBar *esb, EMFolderBrowser *emfb)
+{
+ char *search_word;
+
+ if (emfb->view.list == NULL)
+ return;
+
+ g_object_get (esb, "query", &search_word, NULL);
+ message_list_set_search(emfb->view.list, search_word);
+ g_free(search_word);
+}
+
+static void
+emfb_search_query_changed(ESearchBar *esb, EMFolderBrowser *emfb)
+{
+ int id;
+
+ id = e_search_bar_get_item_id(esb);
+ if (id == E_FILTERBAR_ADVANCED_ID)
+ emfb_search_search_activated(esb, emfb);
+}
+
+/* ********************************************************************** */
+
+static int
+emfb_list_key_press(ETree *tree, int row, ETreePath path, int col, GdkEvent *ev, EMFolderBrowser *emfb)
+{
+ if ((ev->key.state & GDK_CONTROL_MASK) != 0)
+ return FALSE;
+
+ switch (ev->key.keyval) {
+ case GDK_space:
+ em_utils_adjustment_page(gtk_scrolled_window_get_vadjustment((GtkScrolledWindow *)emfb->priv->preview), TRUE);
+ break;
+ case GDK_BackSpace:
+ em_utils_adjustment_page(gtk_scrolled_window_get_vadjustment((GtkScrolledWindow *)emfb->priv->preview), FALSE);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* ********************************************************************** */
+
+static void
+emfb_edit_invert_selection(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_invert_selection(emfv->list);
+}
+
+static void
+emfb_edit_select_all(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select_all(emfv->list);
+}
+
+static void
+emfb_edit_select_thread(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select_thread(emfv->list);
+}
+
+static void
+emfb_folder_properties(BonoboUIComponent *uid, void *data, const char *path)
+{
+ /* If only we could remove this ... */
+ /* Should it be part of the factory? */
+ printf("FIXME: folderproperties\n");
+}
+
+static void
+emfb_folder_expunge(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderBrowser *emfb = data;
+
+ em_utils_expunge_folder ((GtkWidget *) emfb, emfb->view.folder);
+}
+
+static void
+emfb_mark_all_read(BonoboUIComponent *uid, void *data, const char *path)
+{
+ /* FIXME: make a 'mark messages' function? */
+ EMFolderView *emfv = data;
+ GPtrArray *uids;
+ int i;
+
+ uids = camel_folder_get_uids(emfv->folder);
+ camel_folder_freeze(emfv->folder);
+ for (i=0;i<uids->len;i++)
+ camel_folder_set_message_flags(emfv->folder, uids->pdata[i], CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+ camel_folder_thaw(emfv->folder);
+ camel_folder_free_uids(emfv->folder, uids);
+}
+
+static void
+emfb_view_hide_read(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_hide_add(emfv->list, "(match-all (system-flag \"seen\"))", ML_HIDE_SAME, ML_HIDE_SAME);
+}
+
+static void
+emfb_view_hide_selected(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+ GPtrArray *uids;
+
+ /* TODO: perhaps this should sit directly on message_list? */
+ /* is it worth it, it's so trivial */
+ uids = message_list_get_selected(emfv->list);
+ message_list_hide_uids(emfv->list, uids);
+ message_list_free_uids(emfv->list, uids);
+}
+
+static void
+emfb_view_show_all(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_hide_clear(emfv->list);
+}
+
+/* ********************************************************************** */
+
+static void
+emfb_empty_trash(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ em_utils_empty_trash ((GtkWidget *) emfv);
+}
+
+static void
+emfb_forget_passwords(BonoboUIComponent *uid, void *data, const char *path)
+{
+ e_passwords_forget_passwords();
+}
+
+static void
+emfb_mail_compose(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderBrowser *emfb = data;
+
+ em_utils_compose_new_message ((GtkWidget *) emfb);
+}
+
+static void
+emfb_mail_stop(BonoboUIComponent *uid, void *data, const char *path)
+{
+ camel_operation_cancel(NULL);
+}
+
+static void
+emfb_mail_post(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+ char *url;
+
+ url = mail_tools_folder_to_url (emfv->folder);
+ em_utils_post_to_url ((GtkWidget *) emfv, url);
+ g_free (url);
+}
+
+static void
+emfb_tools_filters(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderBrowser *emfb = data;
+
+ em_utils_edit_filters ((GtkWidget *) emfb);
+}
+
+static void
+emfb_subscribe_editor_destroy(GtkWidget *w, EMFolderBrowser *emfb)
+{
+ emfb->priv->subscribe_editor = NULL;
+}
+
+static void
+emfb_tools_subscriptions(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderBrowser *emfb = data;
+
+ if (emfb->priv->subscribe_editor) {
+ gdk_window_show(emfb->priv->subscribe_editor->window);
+ } else {
+ emfb->priv->subscribe_editor = (GtkWidget *)em_subscribe_editor_new();
+ e_dialog_set_transient_for((GtkWindow *)emfb->priv->subscribe_editor, (GtkWidget *)emfb);
+ g_signal_connect(emfb->priv->subscribe_editor, "destroy", G_CALLBACK(emfb_subscribe_editor_destroy), emfb);
+ gtk_widget_show(emfb->priv->subscribe_editor);
+ }
+}
+
+static void
+emfb_tools_vfolders(BonoboUIComponent *uid, void *data, const char *path)
+{
+ /* FIXME: rename/refactor this */
+ vfolder_edit();
+}
+
+static BonoboUIVerb emfb_verbs[] = {
+ BONOBO_UI_UNSAFE_VERB ("EditInvertSelection", emfb_edit_invert_selection),
+ BONOBO_UI_UNSAFE_VERB ("EditSelectAll", emfb_edit_select_all),
+ BONOBO_UI_UNSAFE_VERB ("EditSelectThread", emfb_edit_select_thread),
+ BONOBO_UI_UNSAFE_VERB ("ChangeFolderProperties", emfb_folder_properties),
+ BONOBO_UI_UNSAFE_VERB ("FolderExpunge", emfb_folder_expunge),
+ /* HideDeleted is a toggle */
+ BONOBO_UI_UNSAFE_VERB ("MessageMarkAllAsRead", emfb_mark_all_read),
+ BONOBO_UI_UNSAFE_VERB ("ViewHideRead", emfb_view_hide_read),
+ BONOBO_UI_UNSAFE_VERB ("ViewHideSelected", emfb_view_hide_selected),
+ BONOBO_UI_UNSAFE_VERB ("ViewShowAll", emfb_view_show_all),
+ /* ViewThreaded is a toggle */
+
+ BONOBO_UI_UNSAFE_VERB ("EmptyTrash", emfb_empty_trash),
+ BONOBO_UI_UNSAFE_VERB ("ForgetPasswords", emfb_forget_passwords),
+ BONOBO_UI_UNSAFE_VERB ("MailCompose", emfb_mail_compose),
+ BONOBO_UI_UNSAFE_VERB ("MailPost", emfb_mail_post),
+ BONOBO_UI_UNSAFE_VERB ("MailStop", emfb_mail_stop),
+ BONOBO_UI_UNSAFE_VERB ("ToolsFilters", emfb_tools_filters),
+ BONOBO_UI_UNSAFE_VERB ("ToolsSubscriptions", emfb_tools_subscriptions),
+ BONOBO_UI_UNSAFE_VERB ("ToolsVFolders", emfb_tools_vfolders),
+ /* ViewPreview is a toggle */
+
+ BONOBO_UI_VERB_END
+};
+
+static EPixmap emfb_pixmaps[] = {
+ E_PIXMAP ("/commands/ChangeFolderProperties", "configure_16_folder.xpm"),
+ E_PIXMAP ("/commands/ViewHideRead", "hide_read_messages.xpm"),
+ E_PIXMAP ("/commands/ViewHideSelected", "hide_selected_messages.xpm"),
+ E_PIXMAP ("/commands/ViewShowAll", "show_all_messages.xpm"),
+
+ E_PIXMAP ("/commands/MailCompose", "new-message.xpm"),
+
+ E_PIXMAP_END
+};
+
+static const EMFolderViewEnable emfb_enable_map[] = {
+ { "EditSelectThread", EM_FOLDER_VIEW_SELECT_THREADED },
+ { "ViewHideSelected", EM_POPUP_SELECT_MANY },
+ { "ViewShowAll", EM_FOLDER_VIEW_SELECT_HIDDEN },
+ { NULL },
+};
+
+static void
+emfb_hide_deleted(BonoboUIComponent *uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data)
+{
+ GConfClient *gconf;
+ EMFolderView *emfv = data;
+
+ if (type != Bonobo_UIComponent_STATE_CHANGED)
+ return;
+
+ gconf = mail_config_get_gconf_client ();
+ gconf_client_set_bool(gconf, "/apps/evolution/mail/display/show_deleted", state[0] == '0', NULL);
+ if (!(emfv->folder && (emfv->folder->folder_flags & CAMEL_FOLDER_IS_TRASH)))
+ message_list_set_hidedeleted(emfv->list, state[0] != '0');
+}
+
+static void
+emfb_view_threaded(BonoboUIComponent *uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data)
+{
+ GConfClient *gconf;
+ EMFolderView *emfv = data;
+
+ if (type != Bonobo_UIComponent_STATE_CHANGED)
+ return;
+
+ gconf = mail_config_get_gconf_client ();
+ gconf_client_set_bool(gconf, "/apps/evolution/mail/display/thread_list", state[0] != '0', NULL);
+
+ if (camel_object_meta_set(emfv->folder, "evolution:thread_list", state))
+ camel_object_state_write(emfv->folder);
+
+ /* FIXME: do set_threaded via meta-data listener on folder? */
+ message_list_set_threaded(emfv->list, state[0] != '0');
+
+ /* FIXME: update selection state? */
+}
+
+static void
+emfb_view_preview(BonoboUIComponent *uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data)
+{
+ GConfClient *gconf;
+ EMFolderView *emfv = data;
+
+ if (type != Bonobo_UIComponent_STATE_CHANGED)
+ return;
+
+ gconf = mail_config_get_gconf_client ();
+ gconf_client_set_bool(gconf, "/apps/evolution/mail/display/show_preview", state[0] != '0', NULL);
+
+ if (camel_object_meta_set(emfv->folder, "evolution:show_preview", state))
+ camel_object_state_write(emfv->folder);
+
+ /* FIXME: do this via folder listener */
+ em_folder_browser_show_preview((EMFolderBrowser *)emfv, state[0] != '0');
+}
+
+/* TODO: This should probably be handled by message-list, by storing/queueing
+ up the select operation if its busy rebuilding the message-list */
+static void
+emfb_list_built(MessageList *ml, EMFolderBrowser *emfb)
+{
+ g_signal_handler_disconnect(ml, emfb->priv->list_built_id);
+ emfb->priv->list_built_id = 0;
+
+ if (((EMFolderView *)emfb)->list->cursor_uid == NULL)
+ message_list_select(((EMFolderView *)emfb)->list,
+ MESSAGE_LIST_SELECT_NEXT, 0, CAMEL_MESSAGE_SEEN, TRUE);
+}
+
+static void
+emfb_set_folder(EMFolderView *emfv, CamelFolder *folder, const char *uri)
+{
+ /* This is required since we get activated the first time
+ before the folder is open and need to override the
+ defaults */
+ if (folder) {
+ char *sstate;
+
+ if ((sstate = camel_object_meta_get(folder, "evolution:show_preview")))
+ em_folder_browser_show_preview((EMFolderBrowser *)emfv, sstate[0] != '0');
+
+ if ((sstate = camel_object_meta_get(folder, "evolution:thread_list")))
+ message_list_set_threaded(emfv->list, sstate[0] == '1');
+
+ if (emfv->list->cursor_uid == NULL && ((EMFolderBrowser *)emfv)->priv->list_built_id == 0)
+ ((EMFolderBrowser *)emfv)->priv->list_built_id =
+ g_signal_connect(emfv->list, "message_list_built", G_CALLBACK(emfb_list_built), emfv);
+ }
+
+ emfb_parent->set_folder(emfv, folder, uri);
+}
+
+/* TODO: All this mess should sit directly on MessageList, but it would
+ need to become BonoboUIComponent aware ... */
+
+static void
+emfb_list_display_view(GalViewInstance *instance, GalView *view, EMFolderBrowser *emfb)
+{
+ if (GAL_IS_VIEW_ETABLE(view))
+ gal_view_etable_attach_tree(GAL_VIEW_ETABLE(view), emfb->view.list->tree);
+}
+
+static void
+emfb_create_view_menus(EMFolderBrowser *emfb, BonoboUIComponent *uic)
+{
+ struct _EMFolderBrowserPrivate *p = emfb->priv;
+ static GalViewCollection *collection = NULL;
+ char *id;
+ gboolean outgoing;
+
+ g_assert(p->view_instance == NULL);
+ g_assert(p->view_menus == NULL);
+
+ outgoing = em_utils_folder_is_drafts(emfb->view.folder, emfb->view.folder_uri)
+ || em_utils_folder_is_sent(emfb->view.folder, emfb->view.folder_uri)
+ || em_utils_folder_is_outbox(emfb->view.folder, emfb->view.folder_uri);
+
+ if (collection == NULL) {
+ ETableSpecification *spec;
+ char *dir;
+ GalViewFactory *factory;
+
+ collection = gal_view_collection_new();
+
+ gal_view_collection_set_title(collection, _("Mail"));
+
+ dir = g_build_filename(g_get_home_dir(), "/evolution/views/mail/", NULL);
+ gal_view_collection_set_storage_directories(collection, EVOLUTION_GALVIEWSDIR "/mail/", dir);
+ g_free(dir);
+
+ spec = e_table_specification_new();
+ e_table_specification_load_from_file(spec, EVOLUTION_ETSPECDIR "/message-list.etspec");
+
+ factory = gal_view_factory_etable_new(spec);
+ g_object_unref(spec);
+ gal_view_collection_add_factory(collection, factory);
+ g_object_unref(factory);
+
+ gal_view_collection_load(collection);
+ }
+
+ /* TODO: should this go through mail-config api? */
+ id = mail_config_folder_to_safe_url(emfb->view.folder);
+ p->view_instance = gal_view_instance_new(collection, id);
+ g_free(id);
+
+ if (outgoing)
+ gal_view_instance_set_default_view(p->view_instance, "As_Sent_Folder");
+
+ if (!gal_view_instance_exists(p->view_instance)) {
+ char *path;
+ struct stat st;
+
+ gal_view_instance_load(p->view_instance);
+
+ path = mail_config_folder_to_cachename(emfb->view.folder, "et-header-");
+ if (path && stat (path, &st) == 0 && st.st_size > 0 && S_ISREG (st.st_mode)) {
+ ETableSpecification *spec;
+ ETableState *state;
+ GalView *view;
+
+ spec = e_table_specification_new();
+ e_table_specification_load_from_file(spec, EVOLUTION_ETSPECDIR "/message-list.etspec");
+ view = gal_view_etable_new(spec, "");
+ g_object_unref(spec);
+
+ state = e_table_state_new();
+ e_table_state_load_from_file(state, path);
+ gal_view_etable_set_state(GAL_VIEW_ETABLE (view), state);
+ g_object_unref(state);
+
+ gal_view_instance_set_custom_view(p->view_instance, view);
+ g_object_unref(view);
+ }
+ g_free(path);
+ }
+
+ p->view_menus = gal_view_menus_new(p->view_instance);
+ gal_view_menus_apply(p->view_menus, uic, NULL);
+
+ /* Due to CORBA reentrancy, the view could be gone now. */
+ if (p->view_instance == NULL)
+ return;
+
+ g_signal_connect(p->view_instance, "display_view", G_CALLBACK(emfb_list_display_view), emfb);
+ emfb_list_display_view(p->view_instance, gal_view_instance_get_current_view(p->view_instance), emfb);
+}
+
+static void
+emfb_activate(EMFolderView *emfv, BonoboUIComponent *uic, int act)
+{
+ struct _EMFolderBrowserPrivate *p = ((EMFolderBrowser *)emfv)->priv;
+
+ if (act) {
+ GConfClient *gconf;
+ gboolean state;
+ char *sstate;
+
+ gconf = mail_config_get_gconf_client ();
+
+ /* parent loads all ui files via ui_files */
+ emfb_parent->activate(emfv, uic, act);
+
+ bonobo_ui_component_add_verb_list_with_data(uic, emfb_verbs, emfv);
+ e_pixmaps_update(uic, emfb_pixmaps);
+
+#if 0
+ /* FIXME: finish */
+ /* (Pre)view pane size (do this first because it affects the
+ preview settings - see folder_browser_set_message_preview()
+ internals for details) */
+ g_signal_handler_block(emfb->vpane, emfb->priv->vpane_resize_id);
+ gtk_paned_set_position((GtkPaned *)emfb->vpane, gconf_client_get_int (gconf, "/apps/evolution/mail/display/paned_size", NULL));
+ g_signal_handler_unblock(emfb->vpane, emfb->priv->vpane_resize_id);
+#endif
+
+ /* (Pre)view toggle */
+ if (emfv->folder
+ && (sstate = camel_object_meta_get(emfv->folder, "evolution:show_preview"))) {
+ state = sstate[0] == '1';
+ g_free(sstate);
+ } else {
+ state = gconf_client_get_bool(gconf, "/apps/evolution/mail/display/show_preview", NULL);
+ }
+
+ bonobo_ui_component_set_prop(uic, "/commands/ViewPreview", "state", state?"1":"0", NULL);
+ em_folder_browser_show_preview((EMFolderBrowser *)emfv, state);
+ bonobo_ui_component_add_listener(uic, "ViewPreview", emfb_view_preview, emfv);
+
+ /* Stop button */
+ state = mail_msg_active((unsigned int)-1);
+ bonobo_ui_component_set_prop(uic, "/commands/MailStop", "sensitive", state?"1":"0", NULL);
+
+ /* HideDeleted */
+ state = !gconf_client_get_bool(gconf, "/apps/evolution/mail/display/show_deleted", NULL);
+ bonobo_ui_component_set_prop(uic, "/commands/HideDeleted", "state", state ? "1" : "0", NULL);
+ bonobo_ui_component_add_listener(uic, "HideDeleted", emfb_hide_deleted, emfv);
+ if (!(emfv->folder && (emfv->folder->folder_flags & CAMEL_FOLDER_IS_TRASH)))
+ message_list_set_hidedeleted (emfv->list, state);
+ else
+ bonobo_ui_component_set_prop(uic, "/commands/HideDeleted", "sensitive", state?"1":"0", NULL);
+
+ /* FIXME: If we have no folder, we can't do a few of the lookups we need,
+ perhaps we should postpone till we can */
+
+ /* ViewThreaded */
+ if (emfv->folder
+ && (sstate = camel_object_meta_get(emfv->folder, "evolution:thread_list"))) {
+ state = sstate[0] == '1';
+ g_free(sstate);
+ } else {
+ state = gconf_client_get_bool(gconf, "/apps/evolution/mail/display/thread_list", NULL);
+ }
+
+ bonobo_ui_component_set_prop(uic, "/commands/ViewThreaded", "state", state?"1":"0", NULL);
+ bonobo_ui_component_add_listener(uic, "ViewThreaded", emfb_view_threaded, emfv);
+ message_list_set_threaded(emfv->list, state);
+
+ /* FIXME: Selection state */
+
+ /* FIXME: property menu customisation */
+ /*folder_browser_setup_property_menu (fb, fb->uicomp);*/
+
+ if (((EMFolderBrowser *)emfv)->search)
+ e_search_bar_set_ui_component((ESearchBar *)((EMFolderBrowser *)emfv)->search, uic);
+
+ if (emfv->folder)
+ emfb_create_view_menus((EMFolderBrowser *)emfv, uic);
+ } else {
+ const BonoboUIVerb *v;
+
+ for (v = &emfb_verbs[0]; v->cname; v++)
+ bonobo_ui_component_remove_verb(uic, v->cname);
+
+ if (p->view_instance) {
+ g_object_unref(p->view_instance);
+ p->view_instance = NULL;
+ g_object_unref(p->view_menus);
+ p->view_menus = NULL;
+ }
+
+ if (((EMFolderBrowser *)emfv)->search)
+ e_search_bar_set_ui_component((ESearchBar *)((EMFolderBrowser *)emfv)->search, NULL);
+
+ emfb_parent->activate(emfv, uic, act);
+ }
+}
diff --git a/mail/em-folder-browser.h b/mail/em-folder-browser.h
new file mode 100644
index 0000000000..248deaf9b4
--- /dev/null
+++ b/mail/em-folder-browser.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _EM_FOLDER_BROWSER_H
+#define _EM_FOLDER_BROWSER_H
+
+#include "em-folder-view.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+typedef struct _EMFolderBrowser EMFolderBrowser;
+typedef struct _EMFolderBrowserClass EMFolderBrowserClass;
+
+struct _EMFolderBrowser {
+ EMFolderView view;
+
+ struct _EMFolderBrowserPrivate *priv;
+
+ GtkWidget *vpane;
+ struct _EFilterBar *search;
+};
+
+struct _EMFolderBrowserClass {
+ EMFolderViewClass parent_class;
+};
+
+GType em_folder_browser_get_type(void);
+
+GtkWidget *em_folder_browser_new(void);
+
+void em_folder_browser_show_preview(EMFolderBrowser *emfv, gboolean state);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _EM_FOLDER_BROWSER_H */
diff --git a/mail/em-folder-view.c b/mail/em-folder-view.c
new file mode 100644
index 0000000000..7122f3da36
--- /dev/null
+++ b/mail/em-folder-view.c
@@ -0,0 +1,1866 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkvpaned.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gtkhtml/gtkhtml.h>
+
+#include <libgnome/gnome-url.h>
+
+#include <libgnomeprintui/gnome-print-dialog.h>
+
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-filter-enriched.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-url.h>
+
+#include <bonobo/bonobo-main.h>
+#include <bonobo/bonobo-object.h>
+#include <bonobo/bonobo-generic-factory.h>
+#include <bonobo/bonobo-control.h>
+#include <bonobo/bonobo-ui-component.h>
+#include <bonobo/bonobo-ui-util.h>
+
+#include "widgets/misc/e-charset-picker.h"
+
+#include <e-util/e-dialog-utils.h>
+
+#include "em-format-html-display.h"
+#include "em-format-html-print.h"
+#include "em-folder-view.h"
+#include "em-message-browser.h"
+#include "message-list.h"
+#include "em-utils.h"
+
+#include <gtkhtml/gtkhtml.h>
+#include <gtkhtml/htmlobject.h>
+#include <gtkhtml/htmlengine.h>
+#include <gtkhtml/htmlengine-save.h>
+
+#include "mail-mt.h"
+#include "mail-ops.h"
+#include "mail-config.h" /* hrm, pity we need this ... */
+#include "mail-autofilter.h"
+#include "mail-vfolder.h"
+
+#include "evolution-shell-component-utils.h" /* Pixmap stuff, sigh */
+
+static void emfv_folder_changed(CamelFolder *folder, CamelFolderChangeInfo *changes, EMFolderView *emfv);
+
+static void emfv_list_message_selected(MessageList *ml, const char *uid, EMFolderView *emfv);
+static int emfv_list_right_click(ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, EMFolderView *emfv);
+static void emfv_list_double_click(ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, EMFolderView *emfv);
+static int emfv_list_key_press(ETree *tree, int row, ETreePath path, int col, GdkEvent *ev, EMFolderView *emfv);
+
+static void emfv_format_link_clicked(EMFormatHTMLDisplay *efhd, const char *uri, EMFolderView *);
+static int emfv_format_popup_event(EMFormatHTMLDisplay *efhd, GdkEventButton *event, const char *uri, CamelMimePart *part, EMFolderView *);
+
+static void emfv_enable_menus(EMFolderView *emfv);
+
+static void emfv_set_folder(EMFolderView *emfv, CamelFolder *folder, const char *uri);
+static void emfv_set_folder_uri(EMFolderView *emfv, const char *uri);
+static void emfv_set_message(EMFolderView *emfv, const char *uid);
+static void emfv_activate(EMFolderView *emfv, BonoboUIComponent *uic, int state);
+
+static void emfv_message_reply(EMFolderView *emfv, int mode);
+static void vfolder_type_current (EMFolderView *emfv, int type);
+static void filter_type_current (EMFolderView *emfv, int type);
+
+static void emfv_setting_setup(EMFolderView *emfv);
+
+static const EMFolderViewEnable emfv_enable_map[];
+
+struct _EMFolderViewPrivate {
+ guint seen_id;
+ guint setting_notify_id;
+
+ CamelObjectHookID folder_changed_id;
+
+ GtkWidget *invisible;
+ char *selection_uri;
+};
+
+static GtkVBoxClass *emfv_parent;
+
+static void emfv_selection_get(GtkWidget *widget, GtkSelectionData *data, guint info, guint time_stamp, EMFolderView *emfv);
+static void emfv_selection_clear_event(GtkWidget *widget, GdkEventSelection *event, EMFolderView *emfv);
+
+static void
+emfv_init(GObject *o)
+{
+ EMFolderView *emfv = (EMFolderView *)o;
+ struct _EMFolderViewPrivate *p;
+
+ gtk_box_set_homogeneous (GTK_BOX (emfv), FALSE);
+
+ p = emfv->priv = g_malloc0(sizeof(struct _EMFolderViewPrivate));
+
+ emfv->ui_files = g_slist_append(NULL, EVOLUTION_UIDIR "/evolution-mail-message.xml");
+ emfv->ui_app_name = "evolution-mail";
+
+ emfv->enable_map = g_slist_prepend(NULL, (void *)emfv_enable_map);
+
+ emfv->list = (MessageList *)message_list_new();
+ g_signal_connect(emfv->list, "message_selected", G_CALLBACK(emfv_list_message_selected), emfv);
+
+ /* FIXME: should this hang off message-list instead? */
+ g_signal_connect(emfv->list->tree, "right_click", G_CALLBACK(emfv_list_right_click), emfv);
+ g_signal_connect(emfv->list->tree, "double_click", G_CALLBACK(emfv_list_double_click), emfv);
+ g_signal_connect(emfv->list->tree, "key_press", G_CALLBACK(emfv_list_key_press), emfv);
+
+ emfv->preview = (EMFormatHTMLDisplay *)em_format_html_display_new();
+ g_signal_connect(emfv->preview, "link_clicked", G_CALLBACK(emfv_format_link_clicked), emfv);
+ g_signal_connect(emfv->preview, "popup_event", G_CALLBACK(emfv_format_popup_event), emfv);
+
+ p->invisible = gtk_invisible_new();
+ g_object_ref(p->invisible);
+ gtk_object_sink((GtkObject *)p->invisible);
+ g_signal_connect(p->invisible, "selection_get", G_CALLBACK(emfv_selection_get), emfv);
+ g_signal_connect(p->invisible, "selection_clear_event", G_CALLBACK(emfv_selection_clear_event), emfv);
+ gtk_selection_add_target(p->invisible, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 0);
+ gtk_selection_add_target(p->invisible, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 1);
+
+ emfv->async = mail_async_event_new();
+
+ emfv_setting_setup(emfv);
+}
+
+static void
+emfv_finalise(GObject *o)
+{
+ EMFolderView *emfv = (EMFolderView *)o;
+ struct _EMFolderViewPrivate *p = emfv->priv;
+
+ if (emfv->async)
+ mail_async_event_destroy(emfv->async);
+
+ if (emfv->folder) {
+ if (p->folder_changed_id)
+ camel_object_remove_event(emfv->folder, p->folder_changed_id);
+ camel_object_unref(emfv->folder);
+ g_free(emfv->folder_uri);
+ }
+
+ g_slist_free(emfv->ui_files);
+ g_slist_free(emfv->enable_map);
+
+ g_free(p);
+
+ ((GObjectClass *)emfv_parent)->finalize(o);
+}
+
+static void
+emfv_destroy (GtkObject *o)
+{
+ EMFolderView *emfv = (EMFolderView *) o;
+ struct _EMFolderViewPrivate *p = emfv->priv;
+
+ if (p->seen_id) {
+ g_source_remove(p->seen_id);
+ p->seen_id = 0;
+ }
+
+ if (p->setting_notify_id) {
+ GConfClient *gconf = gconf_client_get_default();
+
+ gconf_client_notify_remove(gconf, p->setting_notify_id);
+ p->setting_notify_id = 0;
+ g_object_unref(gconf);
+ }
+
+ if (p->invisible) {
+ g_object_unref(p->invisible);
+ p->invisible = NULL;
+ }
+
+ emfv->preview = NULL;
+ emfv->list = NULL;
+ emfv->preview_active = FALSE;
+ emfv->uic = NULL;
+
+ ((GtkObjectClass *) emfv_parent)->destroy (o);
+}
+
+static void
+emfv_class_init(GObjectClass *klass)
+{
+ klass->finalize = emfv_finalise;
+
+ ((GtkObjectClass *) klass)->destroy = emfv_destroy;
+
+ ((EMFolderViewClass *)klass)->set_folder = emfv_set_folder;
+ ((EMFolderViewClass *)klass)->set_folder_uri = emfv_set_folder_uri;
+ ((EMFolderViewClass *)klass)->set_message = emfv_set_message;
+ ((EMFolderViewClass *)klass)->activate = emfv_activate;
+}
+
+GType
+em_folder_view_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFolderViewClass),
+ NULL, NULL,
+ (GClassInitFunc)emfv_class_init,
+ NULL, NULL,
+ sizeof(EMFolderView), 0,
+ (GInstanceInitFunc)emfv_init
+ };
+ emfv_parent = g_type_class_ref(gtk_vbox_get_type());
+ type = g_type_register_static(gtk_vbox_get_type(), "EMFolderView", &info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *em_folder_view_new(void)
+{
+ EMFolderView *emfv = g_object_new(em_folder_view_get_type(), 0);
+
+ return (GtkWidget *)emfv;
+}
+
+/* flag all selected messages. Return number flagged */
+/* FIXME: Should this be part of message-list instead? */
+int
+em_folder_view_mark_selected(EMFolderView *emfv, guint32 mask, guint32 set)
+{
+ GPtrArray *uids;
+ int i;
+
+ if (emfv->folder == NULL)
+ return 0;
+
+ uids = message_list_get_selected(emfv->list);
+ camel_folder_freeze(emfv->folder);
+
+ for (i=0; i<uids->len; i++)
+ camel_folder_set_message_flags(emfv->folder, uids->pdata[i], mask, set);
+
+ message_list_free_uids(emfv->list, uids);
+ camel_folder_thaw(emfv->folder);
+
+ return i;
+}
+
+/* should this be elsewhere/take a uid list? */
+int
+em_folder_view_open_selected(EMFolderView *emfv)
+{
+ GPtrArray *uids;
+ int i = 0;
+
+ /* FIXME: handle editing message? Should be a different method? editing handled by 'Resend' method already */
+
+ uids = message_list_get_selected(emfv->list);
+
+ if (em_utils_folder_is_drafts(emfv->folder, emfv->folder_uri)
+ || em_utils_folder_is_outbox(emfv->folder, emfv->folder_uri)) {
+ em_utils_edit_messages((GtkWidget *)emfv, emfv->folder, uids);
+ } else {
+ /* TODO: have an em_utils_open_messages call? */
+
+ /* FIXME: 'are you sure' for > 10 messages; is this even necessary? */
+
+ for (i=0; i<uids->len; i++) {
+ EMMessageBrowser *emmb;
+
+ emmb = (EMMessageBrowser *)em_message_browser_window_new();
+ /* FIXME: session needs to be passed easier than this */
+ em_format_set_session((EMFormat *)((EMFolderView *)emmb)->preview, ((EMFormat *)emfv->preview)->session);
+ em_folder_view_set_folder((EMFolderView *)emmb, emfv->folder, emfv->folder_uri);
+ em_folder_view_set_message((EMFolderView *)emmb, uids->pdata[i]);
+ gtk_widget_show(emmb->window);
+ }
+
+ message_list_free_uids(emfv->list, uids);
+ }
+
+ return i;
+}
+
+/* ********************************************************************** */
+
+static void
+emfv_set_folder(EMFolderView *emfv, CamelFolder *folder, const char *uri)
+{
+ int isout = (folder && uri
+ && (em_utils_folder_is_drafts(folder, uri)
+ || em_utils_folder_is_sent(folder, uri)
+ || em_utils_folder_is_outbox(folder, uri)));
+
+ message_list_set_folder(emfv->list, folder, uri, isout);
+ g_free(emfv->folder_uri);
+ emfv->folder_uri = g_strdup(uri);
+ if (folder != emfv->folder) {
+ if (emfv->folder) {
+ if (emfv->priv->folder_changed_id)
+ camel_object_remove_event(emfv->folder, emfv->priv->folder_changed_id);
+ camel_object_unref(emfv->folder);
+ }
+ emfv->folder = folder;
+ if (folder) {
+ emfv->priv->folder_changed_id = camel_object_hook_event(folder, "folder_changed",
+ (CamelObjectEventHookFunc)emfv_folder_changed, emfv);
+ camel_object_ref(folder);
+ }
+ }
+
+ emfv_enable_menus(emfv);
+}
+
+static void
+emfv_got_folder(char *uri, CamelFolder *folder, void *data)
+{
+ EMFolderView *emfv = data;
+
+ em_folder_view_set_folder(emfv, folder, uri);
+}
+
+static void
+emfv_set_folder_uri(EMFolderView *emfv, const char *uri)
+{
+ if (emfv->preview)
+ em_format_format((EMFormat *)emfv->preview, NULL);
+
+ mail_get_folder(uri, 0, emfv_got_folder, emfv, mail_thread_new);
+}
+
+static void
+emfv_set_message(EMFolderView *emfv, const char *uid)
+{
+ message_list_select_uid(emfv->list, uid);
+}
+
+/* ********************************************************************** */
+
+static void
+emfv_selection_get(GtkWidget *widget, GtkSelectionData *data, guint info, guint time_stamp, EMFolderView *emfv)
+{
+ struct _EMFolderViewPrivate *p = emfv->priv;
+
+ if (p->selection_uri == NULL)
+ return;
+
+ gtk_selection_data_set(data, data->target, 8, p->selection_uri, strlen(p->selection_uri));
+}
+
+static void
+emfv_selection_clear_event(GtkWidget *widget, GdkEventSelection *event, EMFolderView *emfv)
+{
+#if 0 /* do i care? */
+ struct _EMFolderViewPrivate *p = emfv->priv;
+
+ g_free(p->selection_uri);
+ p->selection_uri = NULL;
+#endif
+}
+
+/* ********************************************************************** */
+
+/* Popup menu
+ In many cases these are the functions called by the bonobo callbacks too */
+
+struct _emfv_label_item {
+ EMPopupItem item;
+
+ EMFolderView *emfv;
+ const char *label;
+};
+
+static void
+emfv_popup_open(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_open_selected(emfv);
+}
+
+static void
+emfv_popup_resend(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids;
+
+ if (!em_utils_check_user_can_send_mail((GtkWidget *)emfv))
+ return;
+
+ uids = message_list_get_selected(emfv->list);
+ em_utils_edit_messages((GtkWidget *)emfv, emfv->folder, uids);
+}
+
+static void
+emfv_popup_saveas(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids;
+
+ uids = message_list_get_selected(emfv->list);
+ em_utils_save_messages((GtkWidget *)emfv, emfv->folder, uids);
+}
+
+static void
+emfv_popup_print(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_print(emfv, FALSE);
+}
+
+static void
+emfv_popup_reply_sender(GtkWidget *w, EMFolderView *emfv)
+{
+ emfv_message_reply(emfv, REPLY_MODE_SENDER);
+}
+
+static void
+emfv_popup_reply_list(GtkWidget *w, EMFolderView *emfv)
+{
+ emfv_message_reply(emfv, REPLY_MODE_LIST);
+}
+
+static void
+emfv_popup_reply_all(GtkWidget *w, EMFolderView *emfv)
+{
+ emfv_message_reply(emfv, REPLY_MODE_ALL);
+}
+
+static void
+emfv_popup_forward(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids;
+
+ if (!em_utils_check_user_can_send_mail((GtkWidget *)emfv))
+ return;
+
+ uids = message_list_get_selected(emfv->list);
+ em_utils_forward_messages((GtkWidget *)emfv, emfv->folder, uids);
+}
+
+static void
+emfv_popup_flag_followup(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids = message_list_get_selected(emfv->list);
+
+ em_utils_flag_for_followup((GtkWidget *)emfv, emfv->folder, uids);
+}
+
+static void
+emfv_popup_flag_completed(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids;
+
+ uids = message_list_get_selected(emfv->list);
+ em_utils_flag_for_followup_completed((GtkWidget *)emfv, emfv->folder, uids);
+}
+
+static void
+emfv_popup_flag_clear(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids = message_list_get_selected(emfv->list);
+
+ em_utils_flag_for_followup_clear((GtkWidget *)emfv, emfv->folder, uids);
+}
+
+static void
+emfv_popup_mark_read(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+}
+
+static void
+emfv_popup_mark_unread(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED, 0);
+
+ if (emfv->priv->seen_id) {
+ g_source_remove(emfv->priv->seen_id);
+ emfv->priv->seen_id = 0;
+ }
+}
+
+static void
+emfv_popup_mark_important(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_FLAGGED);
+}
+
+static void
+emfv_popup_mark_unimportant(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_FLAGGED, 0);
+}
+
+static void
+emfv_popup_delete(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids;
+
+ uids = message_list_get_selected (emfv->list);
+ em_folder_view_mark_selected (emfv, CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_SEEN|CAMEL_MESSAGE_DELETED);
+
+ if (uids->len == 1)
+ message_list_select (emfv->list, MESSAGE_LIST_SELECT_NEXT, 0, 0, FALSE);
+
+ em_utils_uids_free (uids);
+}
+
+static void
+emfv_popup_undelete(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_DELETED, 0);
+}
+
+static void
+emfv_popup_move(GtkWidget *w, EMFolderView *emfv)
+{
+ /* FIXME */
+}
+
+static void
+emfv_popup_copy(GtkWidget *w, EMFolderView *emfv)
+{
+ /* FIXME */
+}
+
+static void
+emfv_set_label(EMFolderView *emfv, const char *label)
+{
+ GPtrArray *uids = message_list_get_selected(emfv->list);
+ int i;
+
+ for (i=0;i<uids->len;i++)
+ camel_folder_set_message_user_tag(emfv->folder, uids->pdata[i], "label", label);
+
+ message_list_free_uids(emfv->list, uids);
+}
+
+static void
+emfv_popup_label_clear(GtkWidget *w, EMFolderView *emfv)
+{
+ emfv_set_label(emfv, NULL);
+}
+
+static void
+emfv_popup_label_set(GtkWidget *w, struct _emfv_label_item *item)
+{
+ emfv_set_label(item->emfv, item->label);
+}
+
+static void
+emfv_popup_add_sender(GtkWidget *w, EMFolderView *emfv)
+{
+ /* FIXME */
+ printf("UNIMPLEMENTED: add sender to addressbook\n");
+}
+
+static void
+emfv_popup_apply_filters(GtkWidget *w, EMFolderView *emfv)
+{
+ GPtrArray *uids = message_list_get_selected(emfv->list);
+
+ mail_filter_on_demand(emfv->folder, uids);
+}
+
+/* filter callbacks, this will eventually be a wizard, see
+ filter_type_current/vfolder_type_current for implementation */
+
+#define EMFV_POPUP_AUTO_TYPE(autotype, name, type) \
+static void \
+name(GtkWidget *w, EMFolderView *emfv) \
+{ \
+ autotype(emfv, type); \
+}
+
+EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_subject, AUTO_SUBJECT)
+EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_sender, AUTO_FROM)
+EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_recipients, AUTO_TO)
+EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_mlist, AUTO_MLIST)
+
+EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_subject, AUTO_SUBJECT)
+EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_sender, AUTO_FROM)
+EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_recipients, AUTO_TO)
+EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_mlist, AUTO_MLIST)
+
+/* TODO: Move some of these to be 'standard' menu's */
+
+static EMPopupItem emfv_popup_menu[] = {
+ { EM_POPUP_ITEM, "00.emfv.00", N_("_Open"), G_CALLBACK(emfv_popup_open), NULL, NULL, 0 },
+ { EM_POPUP_ITEM, "00.emfv.01", N_("_Edit as New Message..."), G_CALLBACK(emfv_popup_resend), NULL, NULL, EM_POPUP_SELECT_RESEND },
+ { EM_POPUP_ITEM, "00.emfv.02", N_("_Save As..."), G_CALLBACK(emfv_popup_saveas), NULL, "save-as-16.png", 0 },
+ { EM_POPUP_ITEM, "00.emfv.03", N_("_Print"), G_CALLBACK(emfv_popup_print), NULL, "print.xpm", 0 },
+
+ { EM_POPUP_BAR, "10.emfv" },
+ { EM_POPUP_ITEM, "10.emfv.00", N_("_Reply to Sender"), G_CALLBACK(emfv_popup_reply_sender), NULL, "reply.xpm", EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "10.emfv.01", N_("Reply to _List"), G_CALLBACK(emfv_popup_reply_list), NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_MAILING_LIST },
+ { EM_POPUP_ITEM, "10.emfv.02", N_("Reply to _All"), G_CALLBACK(emfv_popup_reply_all), NULL, "reply_to_all.xpm", EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "10.emfv.03", N_("_Forward"), G_CALLBACK(emfv_popup_forward), NULL, "forward.xpm", EM_POPUP_SELECT_MANY },
+
+ { EM_POPUP_BAR, "20.emfv", NULL, NULL, NULL, NULL, EM_POPUP_SELECT_FLAG_FOLLOWUP|EM_POPUP_SELECT_FLAG_COMPLETED|EM_POPUP_SELECT_FLAG_CLEAR },
+ { EM_POPUP_ITEM, "20.emfv.00", N_("Follo_w Up..."), G_CALLBACK(emfv_popup_flag_followup), NULL, "flag-for-followup-16.png", EM_POPUP_SELECT_FLAG_FOLLOWUP },
+ { EM_POPUP_ITEM, "20.emfv.01", N_("Fla_g Completed"), G_CALLBACK(emfv_popup_flag_completed), NULL, NULL, EM_POPUP_SELECT_FLAG_COMPLETED },
+ { EM_POPUP_ITEM, "20.emfv.02", N_("Cl_ear Flag"), G_CALLBACK(emfv_popup_flag_clear), NULL, NULL, EM_POPUP_SELECT_FLAG_CLEAR },
+
+ { EM_POPUP_BAR, "30.emfv" },
+ { EM_POPUP_ITEM, "30.emfv.00", N_("Mar_k as Read"), G_CALLBACK(emfv_popup_mark_read), NULL, "mail-read.xpm", EM_POPUP_SELECT_MARK_READ },
+ { EM_POPUP_ITEM, "30.emfv.01", N_("Mark as _Unread"), G_CALLBACK(emfv_popup_mark_unread), NULL, "mail-new.xpm", EM_POPUP_SELECT_MARK_UNREAD },
+ { EM_POPUP_ITEM, "30.emfv.02", N_("Mark as _Important"), G_CALLBACK(emfv_popup_mark_important), NULL, "priority-high.xpm", EM_POPUP_SELECT_MARK_IMPORTANT },
+ { EM_POPUP_ITEM, "30.emfv.03", N_("_Mark as Unimportant"), G_CALLBACK(emfv_popup_mark_unimportant), NULL, NULL, EM_POPUP_SELECT_MARK_UNIMPORTANT },
+
+ { EM_POPUP_BAR, "40.emfv" },
+ { EM_POPUP_ITEM, "40.emfv.00", N_("_Delete"), G_CALLBACK(emfv_popup_delete), NULL, "evolution-trash-mini.png", EM_POPUP_SELECT_DELETE },
+ { EM_POPUP_ITEM, "40.emfv.01", N_("U_ndelete"), G_CALLBACK(emfv_popup_undelete), NULL, "undelete_message-16.png", EM_POPUP_SELECT_UNDELETE },
+
+ { EM_POPUP_BAR, "50.emfv" },
+ { EM_POPUP_ITEM, "50.emfv.00", N_("Mo_ve to Folder..."), G_CALLBACK(emfv_popup_move) },
+ { EM_POPUP_ITEM, "50.emfv.01", N_("_Copy to Folder..."), G_CALLBACK(emfv_popup_copy) },
+
+ { EM_POPUP_BAR, "60.label" },
+ { EM_POPUP_SUBMENU, "60.label.00", N_("Label") },
+ { EM_POPUP_IMAGE, "60.label.00/00.label", N_("None"), G_CALLBACK(emfv_popup_label_clear) },
+ { EM_POPUP_BAR, "60.label.00/00.label.00" },
+
+ { EM_POPUP_BAR, "70.emfv", NULL, NULL, NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_ADD_SENDER },
+ { EM_POPUP_ITEM, "70.emfv.00", N_("Add Sender to Address_book"), G_CALLBACK(emfv_popup_add_sender), NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_ADD_SENDER },
+
+ { EM_POPUP_BAR, "80.emfv" },
+ { EM_POPUP_ITEM, "80.emfv.00", N_("Appl_y Filters"), G_CALLBACK(emfv_popup_apply_filters) },
+
+ { EM_POPUP_BAR, "90.filter" },
+ { EM_POPUP_SUBMENU, "90.filter.00", N_("Crea_te Rule From Message"), NULL, NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/00.00", N_("VFolder on _Subject"), G_CALLBACK(emfv_popup_vfolder_subject), NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/00.01", N_("VFolder on Se_nder"), G_CALLBACK(emfv_popup_vfolder_sender), NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/00.02", N_("VFolder on _Recipients"), G_CALLBACK(emfv_popup_vfolder_recipients), NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/00.03", N_("VFolder on Mailing _List"),
+ G_CALLBACK(emfv_popup_vfolder_mlist), NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_MAILING_LIST },
+
+ { EM_POPUP_BAR, "90.filter.00/10" },
+ { EM_POPUP_ITEM, "90.filter.00/10.00", N_("Filter on Sub_ject"), G_CALLBACK(emfv_popup_filter_subject), NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/10.01", N_("Filter on Sen_der"), G_CALLBACK(emfv_popup_filter_sender), NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/10.02", N_("Filter on Re_cipients"), G_CALLBACK(emfv_popup_filter_recipients), NULL, NULL, EM_POPUP_SELECT_ONE },
+ { EM_POPUP_ITEM, "90.filter.00/10.03", N_("Filter on _Mailing List"),
+ G_CALLBACK(emfv_popup_filter_mlist), NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_MAILING_LIST },
+};
+
+static void
+emfv_popup_labels_free(void *data)
+{
+ GSList *l = data;
+
+ while (l) {
+ GSList *n = l->next;
+ struct _emfv_label_item *item = l->data;
+
+ g_free(item->item.path);
+ g_free(item);
+
+ g_slist_free_1(l);
+ l = n;
+ }
+}
+
+static void
+emfv_popup(EMFolderView *emfv, GdkEvent *event)
+{
+ GSList *menus = NULL, *l, *label_list = NULL;
+ GtkMenu *menu;
+ EMPopup *emp;
+ EMPopupTarget *target;
+ int i;
+
+ emp = em_popup_new("com.ximian.mail.folderview.popup.select");
+ target = em_folder_view_get_popup_target(emfv);
+
+ for (i=0;i<sizeof(emfv_popup_menu)/sizeof(emfv_popup_menu[0]);i++) {
+ EMPopupItem *item = &emfv_popup_menu[i];
+
+ item->activate_data = emfv;
+ menus = g_slist_prepend(menus, item);
+ }
+
+ em_popup_add_items(emp, menus, (GDestroyNotify)g_slist_free);
+
+ i = 1;
+ for (l = mail_config_get_labels(); l; l = l->next) {
+ struct _emfv_label_item *item;
+ MailConfigLabel *label = l->data;
+ GdkPixmap *pixmap;
+ GdkColor colour;
+ GdkGC *gc;
+
+ item = g_malloc0(sizeof(*item));
+ item->item.type = EM_POPUP_IMAGE;
+ item->item.path = g_strdup_printf("60.label.00/00.label.%02d", i++);
+ item->item.label = label->name;
+ item->item.activate = G_CALLBACK(emfv_popup_label_set);
+ item->item.activate_data = item;
+ item->emfv = emfv;
+ item->label = label->tag;
+
+ gdk_color_parse(label->colour, &colour);
+ gdk_color_alloc(gdk_colormap_get_system(), &colour);
+
+ pixmap = gdk_pixmap_new(((GtkWidget *)emfv)->window, 16, 16, -1);
+ gc = gdk_gc_new(((GtkWidget *)emfv)->window);
+ gdk_gc_set_foreground(gc, &colour);
+ gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, 16, 16);
+ gdk_gc_unref(gc);
+
+ item->item.image = gtk_image_new_from_pixmap(pixmap, NULL);
+ gtk_widget_show(item->item.image);
+
+ label_list = g_slist_prepend(label_list, item);
+ }
+
+ em_popup_add_items(emp, label_list, emfv_popup_labels_free);
+
+ menu = em_popup_create_menu_once(emp, target, target->mask, target->mask);
+
+ if (event == NULL || event->type == GDK_KEY_PRESS) {
+ /* FIXME: menu pos function */
+ gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, event->key.time);
+ } else {
+ gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button.button, event->button.time);
+ }
+}
+
+/* ********************************************************************** */
+
+/* Bonobo menu's */
+
+/* a lot of stuff maps directly to the popup menu equivalent */
+#define EMFV_MAP_CALLBACK(from, to) \
+static void \
+from(BonoboUIComponent *uid, void *data, const char *path) \
+{ \
+ to(NULL, (EMFolderView *)data); \
+}
+
+EMFV_MAP_CALLBACK(emfv_message_apply_filters, emfv_popup_apply_filters)
+EMFV_MAP_CALLBACK(emfv_message_copy, emfv_popup_copy)
+EMFV_MAP_CALLBACK(emfv_message_move, emfv_popup_move)
+EMFV_MAP_CALLBACK(emfv_message_forward, emfv_popup_forward)
+EMFV_MAP_CALLBACK(emfv_message_reply_all, emfv_popup_reply_all)
+EMFV_MAP_CALLBACK(emfv_message_reply_list, emfv_popup_reply_list)
+EMFV_MAP_CALLBACK(emfv_message_reply_sender, emfv_popup_reply_sender)
+EMFV_MAP_CALLBACK(emfv_message_mark_read, emfv_popup_mark_read)
+EMFV_MAP_CALLBACK(emfv_message_mark_unread, emfv_popup_mark_unread)
+EMFV_MAP_CALLBACK(emfv_message_mark_important, emfv_popup_mark_important)
+EMFV_MAP_CALLBACK(emfv_message_mark_unimportant, emfv_popup_mark_unimportant)
+EMFV_MAP_CALLBACK(emfv_message_delete, emfv_popup_delete)
+EMFV_MAP_CALLBACK(emfv_message_undelete, emfv_popup_undelete)
+EMFV_MAP_CALLBACK(emfv_message_followup_flag, emfv_popup_flag_followup)
+/*EMFV_MAP_CALLBACK(emfv_message_followup_clear, emfv_popup_flag_clear)
+ EMFV_MAP_CALLBACK(emfv_message_followup_completed, emfv_popup_flag_completed)*/
+EMFV_MAP_CALLBACK(emfv_message_open, emfv_popup_open)
+EMFV_MAP_CALLBACK(emfv_message_resend, emfv_popup_resend)
+EMFV_MAP_CALLBACK(emfv_message_saveas, emfv_popup_saveas)
+EMFV_MAP_CALLBACK(emfv_print_message, emfv_popup_print)
+
+static void
+emfv_edit_cut(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (message_list_has_primary_selection(emfv->list))
+ message_list_copy(emfv->list, TRUE);
+ else if (emfv->preview_active)
+ em_format_html_display_cut(emfv->preview);
+}
+
+static void
+emfv_edit_copy(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (message_list_has_primary_selection(emfv->list))
+ message_list_copy(emfv->list, FALSE);
+ else if (emfv->preview_active)
+ em_format_html_display_copy(emfv->preview);
+}
+
+static void
+emfv_edit_paste(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_paste(emfv->list);
+}
+
+static void
+emfv_mail_next(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select(emfv->list, MESSAGE_LIST_SELECT_NEXT, 0, 0, FALSE);
+}
+
+static void
+emfv_mail_next_flagged(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select(emfv->list, MESSAGE_LIST_SELECT_NEXT, CAMEL_MESSAGE_FLAGGED, CAMEL_MESSAGE_FLAGGED, FALSE);
+}
+
+static void
+emfv_mail_next_unread(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select(emfv->list, MESSAGE_LIST_SELECT_NEXT, 0, CAMEL_MESSAGE_SEEN, TRUE);
+}
+
+static void
+emfv_mail_next_thread(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select_next_thread(emfv->list);
+}
+
+static void
+emfv_mail_previous(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select(emfv->list, MESSAGE_LIST_SELECT_PREVIOUS, 0, 0, FALSE);
+}
+
+static void
+emfv_mail_previous_flagged(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select(emfv->list, MESSAGE_LIST_SELECT_PREVIOUS, CAMEL_MESSAGE_FLAGGED, CAMEL_MESSAGE_FLAGGED, TRUE);
+}
+
+static void
+emfv_mail_previous_unread(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ message_list_select(emfv->list, MESSAGE_LIST_SELECT_PREVIOUS, 0, CAMEL_MESSAGE_SEEN, TRUE);
+}
+
+static void
+emfv_add_sender_addressbook(BonoboUIComponent *uid, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ emfv = emfv;
+ /* FIXME: need to find out what the new addressbook API is for this... */
+}
+
+static void
+emfv_message_forward_attached (BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+ GPtrArray *uids;
+
+ if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv))
+ return;
+
+ uids = message_list_get_selected (emfv->list);
+ em_utils_forward_attached ((GtkWidget *) emfv, emfv->folder, uids);
+}
+
+static void
+emfv_message_forward_inline (BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+ GPtrArray *uids;
+
+ if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv))
+ return;
+
+ uids = message_list_get_selected (emfv->list);
+ em_utils_forward_inline ((GtkWidget *) emfv, emfv->folder, uids);
+}
+
+static void
+emfv_message_forward_quoted (BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+ GPtrArray *uids;
+
+ if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv))
+ return;
+
+ uids = message_list_get_selected (emfv->list);
+ em_utils_forward_quoted ((GtkWidget *) emfv, emfv->folder, uids);
+}
+
+static void
+emfv_message_redirect (BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (emfv->list->cursor_uid == NULL)
+ return;
+
+ if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv))
+ return;
+
+ em_utils_redirect_message_by_uid ((GtkWidget *) emfv, emfv->folder, emfv->list->cursor_uid);
+}
+
+static void
+emfv_message_post_reply (BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (emfv->list->cursor_uid == NULL)
+ return;
+
+ if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv))
+ return;
+
+ em_utils_post_reply_to_message_by_uid ((GtkWidget *) emfv, emfv->folder, emfv->list->cursor_uid);
+}
+
+static void
+emfv_message_reply(EMFolderView *emfv, int mode)
+{
+ /* GtkClipboard *clip; */
+
+ if (emfv->list->cursor_uid == NULL)
+ return;
+
+ if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv))
+ return;
+
+ /* Look away! Look away! */
+
+ /* HACK: Nasty internal gtkhtml poking going on here */
+
+ /* Disabled since there's no simple way to find out if
+ gtkhtml has the primary selection right now */
+
+ /* Ugh, to use the clipboard we need to request the selection
+ and have an async callback - painful to deal with */
+
+ /*clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY);*/
+ if (FALSE /*gtk_clipboard_get_owner(clip) == (GObject *)emfv->preview*/
+ && ((EMFormatHTML *)emfv->preview)->html->engine->primary) {
+ CamelMimeMessage *msg, *src;
+ struct _header_raw *header;
+ HTMLEngineSaveState *state;
+
+ src = (CamelMimeMessage *)((EMFormat *)emfv->preview)->message;
+ msg = camel_mime_message_new();
+
+ header = ((CamelMimePart *)src)->headers;
+ while (header) {
+ /* FIXME: shouldn't we strip out *all* Content-* headers? */
+ if (g_ascii_strcasecmp(header->name, "content-type") != 0)
+ camel_medium_add_header((CamelMedium *)msg, header->name, header->value);
+ header = header->next;
+ }
+
+ state = html_engine_save_buffer_new(((EMFormatHTML *)emfv->preview)->html->engine, TRUE);
+ html_object_save(((EMFormatHTML *)emfv->preview)->html->engine->primary, state);
+ camel_mime_part_set_content((CamelMimePart *)msg,
+ ((GString *)state->user_data)->str,
+ ((GString *)state->user_data)->len,
+ "text/html");
+
+ html_engine_save_buffer_free(state);
+
+ em_utils_reply_to_message((GtkWidget *)emfv, msg, mode);
+ camel_object_unref(msg);
+ } else {
+ em_utils_reply_to_message_by_uid ((GtkWidget *) emfv, emfv->folder, emfv->list->cursor_uid, mode);
+ }
+
+ /*g_object_unref(clip);*/
+}
+
+static void
+emfv_message_search(BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ em_format_html_display_search(emfv->preview);
+}
+
+static void
+emfv_print_preview_message(BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ em_folder_view_print(emfv, TRUE);
+}
+
+static void
+emfv_text_zoom_in(BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (emfv->preview)
+ em_format_html_display_zoom_in(emfv->preview);
+}
+
+static void
+emfv_text_zoom_out(BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (emfv->preview)
+ em_format_html_display_zoom_out(emfv->preview);
+}
+
+static void
+emfv_text_zoom_reset(BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (emfv->preview)
+ em_format_html_display_zoom_reset(emfv->preview);
+}
+
+/* ********************************************************************** */
+
+struct _filter_data {
+ CamelFolder *folder;
+ const char *source;
+ char *uid;
+ int type;
+ char *uri;
+ char *mlist;
+};
+
+static void
+filter_data_free (struct _filter_data *fdata)
+{
+ g_free (fdata->uid);
+ g_free (fdata->uri);
+ if (fdata->folder)
+ camel_object_unref (fdata->folder);
+ g_free (fdata->mlist);
+ g_free (fdata);
+}
+
+static void
+filter_type_got_message (CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *user_data)
+{
+ struct _filter_data *data = user_data;
+
+ if (msg)
+ filter_gui_add_from_message (msg, data->source, data->type);
+
+ filter_data_free (data);
+}
+
+static void
+filter_type_uid (CamelFolder *folder, const char *uid, const char *source, int type)
+{
+ struct _filter_data *data;
+
+ data = g_malloc0 (sizeof (*data));
+ data->type = type;
+ data->source = source;
+
+ mail_get_message (folder, uid, filter_type_got_message, data, mail_thread_new);
+}
+
+static void
+filter_type_current (EMFolderView *emfv, int type)
+{
+ const char *source;
+ GPtrArray *uids;
+
+ if (em_utils_folder_is_sent (emfv->folder, emfv->folder_uri)
+ || em_utils_folder_is_outbox (emfv->folder, emfv->folder_uri))
+ source = FILTER_SOURCE_OUTGOING;
+ else
+ source = FILTER_SOURCE_INCOMING;
+
+ uids = message_list_get_selected (emfv->list);
+
+ if (uids->len == 1)
+ filter_type_uid (emfv->folder, (char *) uids->pdata[0], source, type);
+
+ em_utils_uids_free (uids);
+}
+
+EMFV_MAP_CALLBACK(emfv_tools_filter_subject, emfv_popup_filter_subject)
+EMFV_MAP_CALLBACK(emfv_tools_filter_sender, emfv_popup_filter_sender)
+EMFV_MAP_CALLBACK(emfv_tools_filter_recipient, emfv_popup_filter_recipients)
+EMFV_MAP_CALLBACK(emfv_tools_filter_mlist, emfv_popup_filter_mlist)
+
+static void
+vfolder_type_got_message (CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *user_data)
+{
+ struct _filter_data *data = user_data;
+
+ if (msg)
+ vfolder_gui_add_from_message (msg, data->type, data->uri);
+
+ filter_data_free (data);
+}
+
+static void
+vfolder_type_uid (CamelFolder *folder, const char *uid, const char *uri, int type)
+{
+ struct _filter_data *data;
+
+ data = g_malloc0 (sizeof (*data));
+ data->type = type;
+ data->uri = g_strdup (uri);
+
+ mail_get_message (folder, uid, vfolder_type_got_message, data, mail_thread_new);
+}
+
+static void
+vfolder_type_current (EMFolderView *emfv, int type)
+{
+ GPtrArray *uids;
+
+ uids = message_list_get_selected (emfv->list);
+
+ if (uids->len == 1)
+ vfolder_type_uid (emfv->folder, (char *) uids->pdata[0], emfv->folder_uri, type);
+
+ em_utils_uids_free (uids);
+}
+
+EMFV_MAP_CALLBACK(emfv_tools_vfolder_subject, emfv_popup_vfolder_subject)
+EMFV_MAP_CALLBACK(emfv_tools_vfolder_sender, emfv_popup_vfolder_sender)
+EMFV_MAP_CALLBACK(emfv_tools_vfolder_recipient, emfv_popup_vfolder_recipients)
+EMFV_MAP_CALLBACK(emfv_tools_vfolder_mlist, emfv_popup_vfolder_mlist)
+
+/* ********************************************************************** */
+
+static void
+emfv_view_load_images(BonoboUIComponent *uic, void *data, const char *path)
+{
+ EMFolderView *emfv = data;
+
+ if (emfv->preview)
+ em_format_html_load_http((EMFormatHTML *)emfv->preview);
+}
+
+static BonoboUIVerb emfv_message_verbs[] = {
+ BONOBO_UI_UNSAFE_VERB ("EditCut", emfv_edit_cut),
+ BONOBO_UI_UNSAFE_VERB ("EditCopy", emfv_edit_copy),
+ BONOBO_UI_UNSAFE_VERB ("EditPaste", emfv_edit_paste),
+
+ BONOBO_UI_UNSAFE_VERB ("MailNext", emfv_mail_next),
+ BONOBO_UI_UNSAFE_VERB ("MailNextFlagged", emfv_mail_next_flagged),
+ BONOBO_UI_UNSAFE_VERB ("MailNextUnread", emfv_mail_next_unread),
+ BONOBO_UI_UNSAFE_VERB ("MailNextThread", emfv_mail_next_thread),
+ BONOBO_UI_UNSAFE_VERB ("MailPrevious", emfv_mail_previous),
+ BONOBO_UI_UNSAFE_VERB ("MailPreviousFlagged", emfv_mail_previous_flagged),
+ BONOBO_UI_UNSAFE_VERB ("MailPreviousUnread", emfv_mail_previous_unread),
+
+ BONOBO_UI_UNSAFE_VERB ("AddSenderToAddressbook", emfv_add_sender_addressbook),
+
+ BONOBO_UI_UNSAFE_VERB ("MessageApplyFilters", emfv_message_apply_filters),
+ BONOBO_UI_UNSAFE_VERB ("MessageCopy", emfv_message_copy),
+ BONOBO_UI_UNSAFE_VERB ("MessageDelete", emfv_message_delete),
+ BONOBO_UI_UNSAFE_VERB ("MessageForward", emfv_message_forward),
+ BONOBO_UI_UNSAFE_VERB ("MessageForwardAttached", emfv_message_forward_attached),
+ BONOBO_UI_UNSAFE_VERB ("MessageForwardInline", emfv_message_forward_inline),
+ BONOBO_UI_UNSAFE_VERB ("MessageForwardQuoted", emfv_message_forward_quoted),
+ BONOBO_UI_UNSAFE_VERB ("MessageRedirect", emfv_message_redirect),
+ BONOBO_UI_UNSAFE_VERB ("MessageMarkAsRead", emfv_message_mark_read),
+ BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnRead", emfv_message_mark_unread),
+ BONOBO_UI_UNSAFE_VERB ("MessageMarkAsImportant", emfv_message_mark_important),
+ BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnimportant", emfv_message_mark_unimportant),
+ BONOBO_UI_UNSAFE_VERB ("MessageFollowUpFlag", emfv_message_followup_flag),
+ BONOBO_UI_UNSAFE_VERB ("MessageMove", emfv_message_move),
+ BONOBO_UI_UNSAFE_VERB ("MessageOpen", emfv_message_open),
+ BONOBO_UI_UNSAFE_VERB ("MessagePostReply", emfv_message_post_reply),
+ BONOBO_UI_UNSAFE_VERB ("MessageReplyAll", emfv_message_reply_all),
+ BONOBO_UI_UNSAFE_VERB ("MessageReplyList", emfv_message_reply_list),
+ BONOBO_UI_UNSAFE_VERB ("MessageReplySender", emfv_message_reply_sender),
+ BONOBO_UI_UNSAFE_VERB ("MessageResend", emfv_message_resend),
+ BONOBO_UI_UNSAFE_VERB ("MessageSaveAs", emfv_message_saveas),
+ BONOBO_UI_UNSAFE_VERB ("MessageSearch", emfv_message_search),
+ BONOBO_UI_UNSAFE_VERB ("MessageUndelete", emfv_message_undelete),
+
+ BONOBO_UI_UNSAFE_VERB ("PrintMessage", emfv_print_message),
+ BONOBO_UI_UNSAFE_VERB ("PrintPreviewMessage", emfv_print_preview_message),
+
+ BONOBO_UI_UNSAFE_VERB ("TextZoomIn", emfv_text_zoom_in),
+ BONOBO_UI_UNSAFE_VERB ("TextZoomOut", emfv_text_zoom_out),
+ BONOBO_UI_UNSAFE_VERB ("TextZoomReset", emfv_text_zoom_reset),
+
+ /* TODO: This stuff should just be 1 item that runs a wizard */
+ BONOBO_UI_UNSAFE_VERB ("ToolsFilterMailingList", emfv_tools_filter_mlist),
+ BONOBO_UI_UNSAFE_VERB ("ToolsFilterRecipient", emfv_tools_filter_recipient),
+ BONOBO_UI_UNSAFE_VERB ("ToolsFilterSender", emfv_tools_filter_sender),
+ BONOBO_UI_UNSAFE_VERB ("ToolsFilterSubject", emfv_tools_filter_subject),
+ BONOBO_UI_UNSAFE_VERB ("ToolsVFolderMailingList", emfv_tools_vfolder_mlist),
+ BONOBO_UI_UNSAFE_VERB ("ToolsVFolderRecipient", emfv_tools_vfolder_recipient),
+ BONOBO_UI_UNSAFE_VERB ("ToolsVFolderSender", emfv_tools_vfolder_sender),
+ BONOBO_UI_UNSAFE_VERB ("ToolsVFolderSubject", emfv_tools_vfolder_subject),
+
+ BONOBO_UI_UNSAFE_VERB ("ViewLoadImages", emfv_view_load_images),
+ /* ViewHeaders stuff is a radio */
+ /* CaretMode is a toggle */
+
+ BONOBO_UI_VERB_END
+};
+static EPixmap emfv_message_pixmaps[] = {
+ E_PIXMAP ("/commands/EditCut", "16_cut.png"),
+ E_PIXMAP ("/commands/EditCopy", "16_copy.png"),
+ E_PIXMAP ("/commands/EditPaste", "16_paste.png"),
+
+ E_PIXMAP ("/commands/PrintMessage", "print.xpm"),
+ E_PIXMAP ("/commands/PrintPreviewMessage", "print-preview.xpm"),
+ E_PIXMAP ("/commands/MessageDelete", "evolution-trash-mini.png"),
+ E_PIXMAP ("/commands/MessageUndelete", "undelete_message-16.png"),
+ E_PIXMAP ("/commands/MessageCopy", "copy_16_message.xpm"),
+ E_PIXMAP ("/commands/MessageMove", "move_message.xpm"),
+ E_PIXMAP ("/commands/MessageReplyAll", "reply_to_all.xpm"),
+ E_PIXMAP ("/commands/MessageReplySender", "reply.xpm"),
+ E_PIXMAP ("/commands/MessageForward", "forward.xpm"),
+ E_PIXMAP ("/commands/MessageApplyFilters", "apply-filters-16.xpm"),
+ E_PIXMAP ("/commands/MessageSearch", "search-16.png"),
+ E_PIXMAP ("/commands/MessageSaveAs", "save-as-16.png"),
+ E_PIXMAP ("/commands/MessageMarkAsRead", "mail-read.xpm"),
+ E_PIXMAP ("/commands/MessageMarkAsUnRead", "mail-new.xpm"),
+ E_PIXMAP ("/commands/MessageMarkAsImportant", "priority-high.xpm"),
+ E_PIXMAP ("/commands/MessageFollowUpFlag", "flag-for-followup-16.png"),
+
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageReplySender", "buttons/reply.png"),
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageReplyAll", "buttons/reply-to-all.png"),
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageForward", "buttons/forward.png"),
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/PrintMessage", "buttons/print.png"),
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageMove", "buttons/move-message.png"),
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageCopy", "buttons/copy-message.png"),
+ E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageDelete", "buttons/delete-message.png"),
+
+ E_PIXMAP ("/Toolbar/MailNextButtons/MailNext", "buttons/next-message.png"),
+ E_PIXMAP ("/Toolbar/MailNextButtons/MailPrevious", "buttons/previous-message.png"),
+
+ E_PIXMAP_END
+};
+
+/* this is added to emfv->enable_map in :init() */
+static const EMFolderViewEnable emfv_enable_map[] = {
+ { "EditCut", EM_POPUP_SELECT_MANY },
+ { "EditCopy", EM_POPUP_SELECT_MANY },
+ { "EditPaste", 0 },
+
+ /* FIXME: should these be single-selection? */
+ { "MailNext", EM_POPUP_SELECT_MANY },
+ { "MailNextFlagged", EM_POPUP_SELECT_MANY },
+ { "MailNextUnread", EM_POPUP_SELECT_MANY },
+ { "MailNextThread", EM_POPUP_SELECT_MANY },
+ { "MailPrevious", EM_POPUP_SELECT_MANY },
+ { "MailPreviousFlagged", EM_POPUP_SELECT_MANY },
+ { "MailPreviousUnread", EM_POPUP_SELECT_MANY },
+
+ { "AddSenderToAddressbook", EM_POPUP_SELECT_ADD_SENDER },
+
+ { "MessageApplyFilters", EM_POPUP_SELECT_MANY },
+ { "MessageCopy", EM_POPUP_SELECT_MANY },
+ { "MessageDelete", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_DELETE },
+ { "MessageForward", EM_POPUP_SELECT_MANY },
+ { "MessageForwardAttached", EM_POPUP_SELECT_MANY },
+ { "MessageForwardInline", EM_POPUP_SELECT_ONE },
+ { "MessageForwardQuoted", EM_POPUP_SELECT_ONE },
+ { "MessageRedirect", EM_POPUP_SELECT_ONE },
+ { "MessageMarkAsRead", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_READ },
+ { "MessageMarkAsUnRead", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNREAD },
+ { "MessageMarkAsImportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_IMPORTANT },
+ { "MessageMarkAsUnimportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNIMPORTANT },
+ { "MessageFollowUpFlag", EM_POPUP_SELECT_MANY },
+ { "MessageMove", EM_POPUP_SELECT_MANY },
+ { "MessageOpen", EM_POPUP_SELECT_MANY },
+ { "MessagePostReply", EM_POPUP_SELECT_ONE },
+ { "MessageReplyAll", EM_POPUP_SELECT_ONE },
+ { "MessageReplyList", EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_MAILING_LIST },
+ { "MessageReplySender", EM_POPUP_SELECT_ONE },
+ { "MessageResend", EM_POPUP_SELECT_RESEND },
+ { "MessageSaveAs", EM_POPUP_SELECT_MANY },
+ { "MessageSearch", EM_POPUP_SELECT_ONE },
+ { "MessageUndelete", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_UNDELETE },
+ { "PrintMessage", EM_POPUP_SELECT_ONE },
+ { "PrintPreviewMessage", EM_POPUP_SELECT_ONE },
+
+ { "TextZoomIn", EM_POPUP_SELECT_ONE },
+ { "TextZoomOut", EM_POPUP_SELECT_ONE },
+ { "TextZoomReset", EM_POPUP_SELECT_ONE },
+
+ { "ToolsFilterMailingList", EM_POPUP_SELECT_ONE },
+ { "ToolsFilterRecipient", EM_POPUP_SELECT_ONE },
+ { "ToolsFilterSender", EM_POPUP_SELECT_ONE },
+ { "ToolsFilterSubject", EM_POPUP_SELECT_ONE },
+ { "ToolsVFolderMailingList", EM_POPUP_SELECT_ONE },
+ { "ToolsVFolderRecipient", EM_POPUP_SELECT_ONE },
+ { "ToolsVFolderSender", EM_POPUP_SELECT_ONE },
+ { "ToolsVFolderSubject", EM_POPUP_SELECT_ONE },
+
+ { "ViewLoadImages", EM_POPUP_SELECT_ONE },
+
+ { NULL },
+
+ /* always enabled
+
+ { "ViewFullHeaders", IS_0MESSAGE, 0 },
+ { "ViewNormal", IS_0MESSAGE, 0 },
+ { "ViewSource", IS_0MESSAGE, 0 },
+ { "CaretMode", IS_0MESSAGE, 0 }, */
+};
+
+static void
+emfv_enable_menus(EMFolderView *emfv)
+{
+ guint32 disable_mask;
+ GString *name;
+ GSList *l;
+ EMPopupTarget *t;
+
+ if (emfv->uic == NULL)
+ return;
+
+ if (emfv->folder) {
+ t = em_folder_view_get_popup_target(emfv);
+ disable_mask = t->mask;
+ em_popup_target_free(t);
+ } else {
+ disable_mask = ~0;
+ }
+
+ name = g_string_new("");
+ for (l = emfv->enable_map; l; l = l->next) {
+ EMFolderViewEnable *map = l->data;
+ int i;
+
+ for (i=0;map[i].name;i++) {
+ int state = (map[i].mask & disable_mask) == 0;
+
+ g_string_printf(name, "/commands/%s", map[i].name);
+ bonobo_ui_component_set_prop(emfv->uic, name->str, "sensitive", state?"1":"0", NULL);
+ }
+ }
+
+ g_string_free(name, TRUE);
+}
+
+/* must match em_format_mode_t order */
+static const char * const emfv_display_styles[] = {
+ "/commands/ViewNormal",
+ "/commands/ViewFullHeaders",
+ "/commands/ViewSource"
+};
+
+static void
+emfv_view_mode(BonoboUIComponent *uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data)
+{
+ EMFolderView *emfv = data;
+ int i;
+
+ if (type != Bonobo_UIComponent_STATE_CHANGED
+ || state[0] == '0')
+ return;
+
+ /* TODO: I don't like this stuff much, is there any way we can move listening for such events
+ elsehwere? Probably not I guess, unless there's a EMFolderViewContainer for bonobo usage
+ of a folder view */
+
+ for (i=0;i<= EM_FORMAT_SOURCE;i++) {
+ if (strcmp(emfv_display_styles[i]+strlen("/commands/"), path) == 0) {
+ em_format_set_mode((EMFormat *)emfv->preview, i);
+
+ if (TRUE /* set preferences but not for EMMessageBrowser? */) {
+ GConfClient *gconf = mail_config_get_gconf_client ();
+
+ gconf_client_set_int (gconf, "/apps/evolution/mail/display/message_style", i, NULL);
+ }
+ break;
+ }
+ }
+}
+
+static void
+emfv_caret_mode(BonoboUIComponent *uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data)
+{
+ EMFolderView *emfv = data;
+
+ if (type != Bonobo_UIComponent_STATE_CHANGED)
+ return;
+
+ em_format_html_display_set_caret_mode(emfv->preview, state[0] != '0');
+
+ gconf_client_set_bool(mail_config_get_gconf_client(), "/apps/evolution/mail/display/caret_mode", state[0] != '0', NULL);
+}
+
+static void
+emfv_charset_changed(BonoboUIComponent *uic, const char *path, Bonobo_UIComponent_EventType type, const char *state, void *data)
+{
+ EMFolderView *emfv = data;
+
+ if (type != Bonobo_UIComponent_STATE_CHANGED)
+ return;
+
+ /* menu items begin with "Charset-" = 8 characters */
+ if (state[0] != '0' && strlen(path) > 8) {
+ path += 8;
+ /* default charset used in mail view */
+ if (!strcmp(path, _("Default")))
+ path = NULL;
+
+ em_format_set_charset((EMFormat *)emfv->preview, path);
+ }
+}
+
+static void
+emfv_activate(EMFolderView *emfv, BonoboUIComponent *uic, int act)
+{
+ if (act) {
+ em_format_mode_t style;
+ gboolean state;
+ GSList *l;
+
+ emfv->uic = uic;
+
+ for (l = emfv->ui_files;l;l = l->next)
+ bonobo_ui_util_set_ui(uic, PREFIX, (char *)l->data, emfv->ui_app_name, NULL);
+
+ bonobo_ui_component_add_verb_list_with_data(uic, emfv_message_verbs, emfv);
+ e_pixmaps_update(uic, emfv_message_pixmaps);
+
+ state = emfv->preview->caret_mode;
+ bonobo_ui_component_set_prop(uic, "/commands/CaretMode", "state", state?"1":"0", NULL);
+ bonobo_ui_component_add_listener(uic, "CaretMode", emfv_caret_mode, emfv);
+
+ style = ((EMFormat *)emfv->preview)->mode;
+ bonobo_ui_component_set_prop(uic, emfv_display_styles[style], "state", "1", NULL);
+ bonobo_ui_component_add_listener(uic, "ViewNormal", emfv_view_mode, emfv);
+ bonobo_ui_component_add_listener(uic, "ViewFullHeaders", emfv_view_mode, emfv);
+ bonobo_ui_component_add_listener(uic, "ViewSource", emfv_view_mode, emfv);
+ em_format_set_mode((EMFormat *)emfv->preview, style);
+
+ if (emfv->folder && !em_utils_folder_is_sent(emfv->folder, emfv->folder_uri))
+ bonobo_ui_component_set_prop(uic, "/commands/MessageResend", "sensitive", "0", NULL);
+
+ /* default charset used in mail view */
+ e_charset_picker_bonobo_ui_populate (uic, "/menu/View", _("Default"), emfv_charset_changed, emfv);
+
+ emfv_enable_menus(emfv);
+ } else {
+ const BonoboUIVerb *v;
+
+ /* TODO: Should this just rm /? */
+ for (v = &emfv_message_verbs[0]; v->cname; v++)
+ bonobo_ui_component_remove_verb(uic, v->cname);
+
+ if (emfv->folder)
+ mail_sync_folder(emfv->folder, NULL, NULL);
+
+ emfv->uic = NULL;
+ }
+}
+
+int em_folder_view_print(EMFolderView *emfv, int preview)
+{
+ /*struct _EMFolderViewPrivate *p = emfv->priv;*/
+ EMFormatHTMLPrint *print;
+ GnomePrintConfig *config = NULL;
+ int res;
+ struct _CamelMedium *msg;
+
+ /* FIXME: need to load the message first */
+ if (!emfv->preview_active)
+ return 0;
+
+ msg = emfv->preview->formathtml.format.message;
+ if (msg == NULL)
+ return 0;
+
+ if (!preview) {
+ GtkDialog *dialog = (GtkDialog *)gnome_print_dialog_new(NULL, _("Print Message"), GNOME_PRINT_DIALOG_COPIES);
+
+ gtk_dialog_set_default_response(dialog, GNOME_PRINT_DIALOG_RESPONSE_PRINT);
+ e_dialog_set_transient_for ((GtkWindow *) dialog, (GtkWidget *) emfv);
+
+ switch (gtk_dialog_run(dialog)) {
+ case GNOME_PRINT_DIALOG_RESPONSE_PRINT:
+ break;
+ case GNOME_PRINT_DIALOG_RESPONSE_PREVIEW:
+ preview = TRUE;
+ break;
+ default:
+ gtk_widget_destroy((GtkWidget *)dialog);
+ return 0;
+ }
+
+ config = gnome_print_dialog_get_config((GnomePrintDialog *)dialog);
+ gtk_widget_destroy((GtkWidget *)dialog);
+ }
+
+ print = em_format_html_print_new();
+ res = em_format_html_print_print(print, msg, (EMFormatHTML *)emfv->preview, config, preview);
+ g_object_unref(print);
+ if (config)
+ g_object_unref(config);
+
+ return res;
+}
+
+EMPopupTarget *
+em_folder_view_get_popup_target(EMFolderView *emfv)
+{
+ EMPopupTarget *t;
+
+ t = em_popup_target_new_select(emfv->folder, emfv->folder_uri, message_list_get_selected(emfv->list));
+ t->widget = (GtkWidget *)emfv;
+
+ if (emfv->list->threaded)
+ t->mask &= ~EM_FOLDER_VIEW_SELECT_THREADED;
+
+ if (message_list_hidden(emfv->list) != 0)
+ t->mask &= ~EM_FOLDER_VIEW_SELECT_HIDDEN;
+
+ return t;
+}
+
+/* ********************************************************************** */
+
+struct mst_t {
+ EMFolderView *emfv;
+ char *uid;
+};
+
+static void
+mst_free (struct mst_t *mst)
+{
+ mst->emfv->priv->seen_id = 0;
+
+ g_free (mst->uid);
+ g_free (mst);
+}
+
+static int
+do_mark_seen (gpointer user_data)
+{
+ struct mst_t *mst = user_data;
+ EMFolderView *emfv = mst->emfv;
+ MessageList *list = emfv->list;
+
+ if (mst->uid && list->cursor_uid && !strcmp (mst->uid, list->cursor_uid))
+ camel_folder_set_message_flags (emfv->folder, mst->uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+
+ return FALSE;
+}
+
+static void
+emfv_list_done_message_selected(CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *data)
+{
+ EMFolderView *emfv = data;
+
+ em_format_format((EMFormat *) emfv->preview, (struct _CamelMedium *)msg);
+
+ if (emfv->priv->seen_id)
+ g_source_remove(emfv->priv->seen_id);
+
+ if (msg && emfv->mark_seen) {
+ if (emfv->mark_seen_timeout > 0) {
+ struct mst_t *mst;
+
+ mst = g_new (struct mst_t, 1);
+ mst->emfv = emfv;
+ mst->uid = g_strdup (uid);
+
+ emfv->priv->seen_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, emfv->mark_seen_timeout,
+ (GSourceFunc)do_mark_seen, mst, (GDestroyNotify)mst_free);
+ } else {
+ camel_folder_set_message_flags(emfv->folder, uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+ }
+ }
+}
+
+static void
+emfv_list_message_selected(MessageList *ml, const char *uid, EMFolderView *emfv)
+{
+ /* FIXME: ui stuff based on messageinfo, if available */
+
+ if (emfv->preview_active) {
+ if (uid)
+ mail_get_message(emfv->folder, uid, emfv_list_done_message_selected, emfv, mail_thread_new);
+ else
+ em_format_format((EMFormat *)emfv->preview, NULL);
+ }
+
+ emfv_enable_menus(emfv);
+}
+
+static void
+emfv_list_double_click(ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, EMFolderView *emfv)
+{
+ /* Ignore double-clicks on columns that handle thier own state */
+ if (MESSAGE_LIST_COLUMN_IS_ACTIVE (col))
+ return;
+
+ em_folder_view_open_selected(emfv);
+}
+
+static int
+emfv_list_right_click(ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, EMFolderView *emfv)
+{
+ emfv_popup(emfv, event);
+
+ return TRUE;
+}
+
+static int
+emfv_list_key_press(ETree *tree, int row, ETreePath path, int col, GdkEvent *ev, EMFolderView *emfv)
+{
+ GPtrArray *uids;
+ int i;
+ guint32 flags;
+
+ if ((ev->key.state & GDK_CONTROL_MASK) != 0)
+ return FALSE;
+
+ switch (ev->key.keyval) {
+ case GDK_Return:
+ case GDK_KP_Enter:
+ case GDK_ISO_Enter:
+ em_folder_view_open_selected(emfv);
+ break;
+ case GDK_Delete:
+ case GDK_KP_Delete:
+ /* If any messages are undeleted, run delete, if all are deleted, run undelete */
+ flags = 0;
+ uids = message_list_get_selected(emfv->list);
+ for (i = 0; i < uids->len; i++) {
+ if ((camel_folder_get_message_flags(emfv->folder, uids->pdata[i]) & CAMEL_MESSAGE_DELETED) == 0)
+ break;
+ }
+ message_list_free_uids(emfv->list, uids);
+ if (i == uids->len)
+ emfv_popup_undelete(NULL, emfv);
+ else
+ emfv_popup_delete(NULL, emfv);
+ break;
+ case GDK_Menu:
+ /* FIXME: location of popup */
+ emfv_popup(emfv, NULL);
+ break;
+ case '!':
+ uids = message_list_get_selected(emfv->list);
+
+ camel_folder_freeze(emfv->folder);
+ for (i = 0; i < uids->len; i++) {
+ flags = camel_folder_get_message_flags(emfv->folder, uids->pdata[i]) ^ CAMEL_MESSAGE_FLAGGED;
+ if (flags & CAMEL_MESSAGE_FLAGGED)
+ flags &= ~CAMEL_MESSAGE_DELETED;
+ camel_folder_set_message_flags(emfv->folder, uids->pdata[i],
+ CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_DELETED, flags);
+ }
+ camel_folder_thaw(emfv->folder);
+
+ message_list_free_uids(emfv->list, uids);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+emfv_format_link_clicked(EMFormatHTMLDisplay *efhd, const char *uri, EMFolderView *emfv)
+{
+ if (!strncasecmp (uri, "mailto:", 7)) {
+ em_utils_compose_new_message_with_mailto ((GtkWidget *) efhd, uri);
+ } else if (*uri == '#') {
+ gtk_html_jump_to_anchor (((EMFormatHTML *) efhd)->html, uri + 1);
+ } else if (!strncasecmp (uri, "thismessage:", 12)) {
+ /* ignore */
+ } else if (!strncasecmp (uri, "cid:", 4)) {
+ /* ignore */
+ } else {
+ GError *err = NULL;
+
+ gnome_url_show (uri, &err);
+
+ if (err) {
+ g_warning ("gnome_url_show: %s", err->message);
+ g_error_free (err);
+ }
+ }
+}
+
+static int
+emfv_format_popup_event(EMFormatHTMLDisplay *efhd, GdkEventButton *event, const char *uri, CamelMimePart *part, EMFolderView *emfv)
+{
+ EMPopup *emp;
+ EMPopupTarget *target;
+ GtkMenu *menu;
+
+ /* FIXME: this maybe should just fit on em-html-display, it has access to the
+ snooped part type */
+
+ emp = em_popup_new("com.ximian.mail.folderview.popup.uri");
+ if (part)
+ target = em_popup_target_new_part(part, NULL);
+ else
+ target = em_popup_target_new_uri(uri);
+
+ menu = em_popup_create_menu_once(emp, target, target->mask, target->mask);
+ gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
+
+ return TRUE;
+}
+
+static void
+emfv_gui_folder_changed(CamelFolder *folder, void *dummy, EMFolderView *emfv)
+{
+ emfv_enable_menus(emfv);
+ g_object_unref(emfv);
+}
+
+static void
+emfv_folder_changed(CamelFolder *folder, CamelFolderChangeInfo *changes, EMFolderView *emfv)
+{
+ g_object_ref(emfv);
+ mail_async_event_emit(emfv->async, MAIL_ASYNC_GUI, (MailAsyncFunc)emfv_gui_folder_changed, folder, NULL, emfv);
+}
+
+/* keep these two tables in sync */
+enum {
+ EMFV_ANIMATE_IMAGES = 1,
+ EMFV_CITATION_COLOUR,
+ EMFV_CITATION_MARK,
+ EMFV_CARET_MODE,
+ EMFV_MESSAGE_STYLE,
+ EMFV_MARK_SEEN,
+ EMFV_MARK_SEEN_TIMEOUT,
+ EMFV_SETTINGS /* last, for loop count */
+};
+
+/* IF these get too long, update key field */
+static const char * const emfv_display_keys[] = {
+ "animate_images",
+ "citation_colour",
+ "mark_citations",
+ "caret_mode",
+ "message_style",
+ "mark_seen",
+ "mark_seen_timeout"
+};
+
+static GHashTable *emfv_setting_key;
+
+static void
+emfv_setting_notify(GConfClient *gconf, guint cnxn_id, GConfEntry *entry, EMFolderView *emfv)
+{
+ char *tkey;
+
+ g_return_if_fail (gconf_entry_get_key (entry) != NULL);
+ g_return_if_fail (gconf_entry_get_value (entry) != NULL);
+
+ tkey = strrchr(entry->key, '/');
+ g_return_if_fail (tkey != NULL);
+
+ switch(GPOINTER_TO_INT(g_hash_table_lookup(emfv_setting_key, tkey+1))) {
+ case EMFV_ANIMATE_IMAGES:
+ em_format_html_display_set_animate(emfv->preview, gconf_value_get_bool(gconf_entry_get_value(entry)));
+ break;
+ case EMFV_CITATION_COLOUR: {
+ const char *s;
+ GdkColor colour;
+ guint32 rgb;
+
+ s = gconf_value_get_string(gconf_entry_get_value(entry));
+ gdk_color_parse(s?s:"#737373", &colour);
+ rgb = ((colour.red & 0xff00) << 8) | (colour.green & 0xff00) | ((colour.blue & 0xff00) >> 8);
+ em_format_html_set_mark_citations((EMFormatHTML *)emfv->preview,
+ ((EMFormatHTML *)emfv->preview)->mark_citations, rgb);
+ break; }
+ case EMFV_CITATION_MARK:
+ em_format_html_set_mark_citations((EMFormatHTML *)emfv->preview,
+ gconf_value_get_bool(gconf_entry_get_value(entry)),
+ ((EMFormatHTML *)emfv->preview)->citation_colour);
+ break;
+ case EMFV_CARET_MODE:
+ em_format_html_display_set_caret_mode(emfv->preview, gconf_value_get_bool(gconf_entry_get_value(entry)));
+ break;
+ case EMFV_MESSAGE_STYLE: {
+ int style = gconf_value_get_int(gconf_entry_get_value(entry));
+
+ if (style < EM_FORMAT_NORMAL || style > EM_FORMAT_SOURCE)
+ style = EM_FORMAT_NORMAL;
+ em_format_set_mode((EMFormat *)emfv->preview, style);
+ break; }
+ case EMFV_MARK_SEEN:
+ emfv->mark_seen = gconf_value_get_bool(gconf_entry_get_value(entry));
+ break;
+ case EMFV_MARK_SEEN_TIMEOUT:
+ emfv->mark_seen_timeout = gconf_value_get_int(gconf_entry_get_value(entry));
+ break;
+ }
+}
+
+static void
+emfv_setting_setup(EMFolderView *emfv)
+{
+ GConfClient *gconf = gconf_client_get_default();
+ GConfEntry *entry;
+ GError *err = NULL;
+ int i;
+ char key[64];
+
+ if (emfv_setting_key == NULL) {
+ emfv_setting_key = g_hash_table_new(g_str_hash, g_str_equal);
+ for (i=1;i<EMFV_SETTINGS;i++)
+ g_hash_table_insert(emfv_setting_key, (void *)emfv_display_keys[i-1], GINT_TO_POINTER(i));
+ }
+
+ gconf_client_add_dir(gconf, "/apps/evolution/mail/display", GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+ for (i=1;err == NULL && i<EMFV_SETTINGS;i++) {
+ sprintf(key, "/apps/evolution/mail/display/%s", emfv_display_keys[i-1]);
+ entry = gconf_client_get_entry(gconf, key, NULL, TRUE, &err);
+ if (entry) {
+ emfv_setting_notify(gconf, 0, entry, emfv);
+ gconf_entry_free(entry);
+ }
+ }
+
+ if (err) {
+ g_warning("Could not load display settings: %s", err->message);
+ g_error_free(err);
+ }
+
+ emfv->priv->setting_notify_id = gconf_client_notify_add(gconf, "/apps/evolution/mail/display",
+ (GConfClientNotifyFunc)emfv_setting_notify,
+ emfv, NULL, NULL);
+ g_object_unref(gconf);
+}
diff --git a/mail/em-folder-view.h b/mail/em-folder-view.h
new file mode 100644
index 0000000000..284de69c1b
--- /dev/null
+++ b/mail/em-folder-view.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _EM_FOLDER_VIEW_H
+#define _EM_FOLDER_VIEW_H
+
+#include <gtk/gtkvbox.h>
+#include "em-popup.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+struct _MessageList;
+struct _EMFormatHTMLDisplay;
+struct _CamelFolder;
+struct _CamelMedium;
+
+typedef struct _EMFolderView EMFolderView;
+typedef struct _EMFolderViewClass EMFolderViewClass;
+
+typedef struct _EMFolderViewEnable EMFolderViewEnable;
+
+enum {
+ EM_FOLDER_VIEW_SELECT_THREADED = EM_POPUP_SELECT_LAST,
+ EM_FOLDER_VIEW_SELECT_HIDDEN = EM_POPUP_SELECT_LAST<<1,
+ EM_FOLDER_VIEW_SELECT_LAST = EM_POPUP_SELECT_LAST<<2,
+};
+
+struct _EMFolderViewEnable {
+ const char *name; /* bonobo name, relative to /commands/ */
+ guint32 mask; /* disable mask, see EM_FOLDER_VIEW_CAN* flags */
+};
+
+struct _EMFolderView {
+ GtkVBox parent;
+
+ struct _EMFolderViewPrivate *priv;
+
+ struct _MessageList *list;
+ struct _EMFormatHTMLDisplay *preview;
+
+ struct _CamelFolder *folder;
+ char *folder_uri;
+
+ /* used to load ui from base activate implementation */
+ GSList *ui_files; /* const char * list, TODO: should this be on class? */
+ const char *ui_app_name;
+
+ /* for proxying jobs to main or other threads */
+ struct _MailAsyncEvent *async;
+
+ struct _BonoboUIComponent *uic; /* if we're active, this will be set */
+ GSList *enable_map; /* bonobo menu enable map, entries are 0-terminated EMFolderViewEnable arryas
+ TODO: should this be on class? */
+
+ int mark_seen_timeout; /* local copy of gconf stuff */
+ int mark_seen:1;
+ int preview_active:1; /* is preview being used */
+};
+
+struct _EMFolderViewClass {
+ GtkVBoxClass parent_class;
+
+ /* if used as a control, used to activate/deactivate custom menu's */
+ void (*activate)(EMFolderView *, struct _BonoboUIComponent *uic, int state);
+
+ void (*set_folder_uri)(EMFolderView *emfv, const char *uri);
+ void (*set_folder)(EMFolderView *emfv, struct _CamelFolder *folder, const char *uri);
+ void (*set_message)(EMFolderView *emfv, const char *uid);
+};
+
+GType em_folder_view_get_type(void);
+
+GtkWidget *em_folder_view_new(void);
+
+#define em_folder_view_activate(emfv, uic, state) ((EMFolderViewClass *)G_OBJECT_GET_CLASS(emfv))->activate((emfv), (uic), (state))
+#define em_folder_view_set_folder(emfv, folder, uri) ((EMFolderViewClass *)G_OBJECT_GET_CLASS(emfv))->set_folder((emfv), (folder), (uri))
+#define em_folder_view_set_folder_uri(emfv, uri) ((EMFolderViewClass *)G_OBJECT_GET_CLASS(emfv))->set_folder_uri((emfv), (uri))
+#define em_folder_view_set_message(emfv, uid) ((EMFolderViewClass *)G_OBJECT_GET_CLASS(emfv))->set_message((emfv), (uid))
+
+struct _EMPopupTarget *em_folder_view_get_popup_target(EMFolderView *emfv);
+
+int em_folder_view_mark_selected(EMFolderView *emfv, guint32 mask, guint32 set);
+int em_folder_view_open_selected(EMFolderView *emfv);
+
+int em_folder_view_print(EMFolderView *emfv, int preview);
+
+/* this could be on message-list */
+guint32 em_folder_view_disable_mask(EMFolderView *emfv);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _EM_FOLDER_VIEW_H */
diff --git a/mail/em-format-html-display.c b/mail/em-format-html-display.c
new file mode 100644
index 0000000000..2b59f09e85
--- /dev/null
+++ b/mail/em-format-html-display.c
@@ -0,0 +1,1159 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gtkhtml/gtkhtml.h>
+#include <gtkhtml/htmlengine.h>
+#include <gtkhtml/htmlobject.h>
+#include <gtkhtml/htmliframe.h>
+#include <gtkhtml/htmlinterval.h>
+#include <gtkhtml/gtkhtml-embedded.h>
+#include <gtkhtml/gtkhtml-search.h>
+
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkarrow.h>
+
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+
+#include <glade/glade.h>
+
+#include <libgnomevfs/gnome-vfs-mime-handlers.h>
+
+#if 0
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <libgnomevfs/gnome-vfs-mime.h>
+#endif
+
+#include <bonobo/bonobo-control-frame.h>
+#include <bonobo/bonobo-stream-memory.h>
+#include <bonobo/bonobo-widget.h>
+
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-multipart-signed.h>
+#include <camel/camel-internet-address.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-gpg-context.h>
+
+#include <e-util/e-msgport.h>
+#include <e-util/e-gui-utils.h>
+#include <e-util/e-dialog-utils.h>
+
+#include "mail-config.h"
+
+#include "em-format-html-display.h"
+#include "em-marshal.h"
+#include "e-searching-tokenizer.h"
+#include "em-icon-stream.h"
+#include "em-utils.h"
+#include "em-popup.h"
+#include "em-icon-stream.h"
+
+#define d(x)
+
+#define EFHD_TABLE_OPEN "<table>"
+
+struct _EMFormatHTMLDisplayPrivate {
+ /* For the interactive search dialogue */
+ /* TODO: Should this be more subtle, like the mozilla one? */
+ GtkDialog *search_dialog;
+ GtkWidget *search_entry;
+ GtkWidget *search_matches_label;
+ GtkWidget *search_case_check;
+ char *search_text;
+ int search_wrap; /* are we doing a wrap search */
+};
+
+static int efhd_html_button_press_event (GtkWidget *widget, GdkEventButton *event, EMFormatHTMLDisplay *efh);
+static void efhd_html_link_clicked (GtkHTML *html, const char *url, EMFormatHTMLDisplay *efhd);
+
+struct _attach_puri {
+ EMFormatPURI puri;
+
+ const EMFormatHandler *handle;
+
+ /* for the > and V buttons */
+ GtkWidget *forward, *down;
+ /* currently no way to correlate this data to the frame :( */
+ GtkHTML *frame;
+ CamelStream *output;
+ unsigned int shown:1;
+};
+
+static void efhd_iframe_created(GtkHTML *html, GtkHTML *iframe, EMFormatHTMLDisplay *efh);
+/*static void efhd_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTMLDisplay *efh);
+ static gboolean efhd_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTMLDisplay *efh);*/
+
+static void efhd_format_clone(EMFormat *, CamelMedium *, EMFormat *);
+static void efhd_format_error(EMFormat *emf, CamelStream *stream, const char *txt);
+static void efhd_format_message(EMFormat *, CamelStream *, CamelMedium *);
+static void efhd_format_source(EMFormat *, CamelStream *, CamelMimePart *);
+static void efhd_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *);
+static void efhd_complete(EMFormat *);
+
+static void efhd_builtin_init(EMFormatHTMLDisplayClass *efhc);
+
+enum {
+ EFHD_LINK_CLICKED,
+ EFHD_POPUP_EVENT,
+ EFHD_LAST_SIGNAL,
+};
+
+static guint efhd_signals[EFHD_LAST_SIGNAL] = { 0 };
+
+
+static EMFormatHTMLClass *efhd_parent;
+
+static void
+efhd_gtkhtml_realise(GtkHTML *html, EMFormatHTMLDisplay *efhd)
+{
+ GtkStyle *style;
+
+ /* FIXME: does this have to be re-done every time we draw? */
+
+ /* My favorite thing to do... muck around with colors so we respect people's stupid themes.
+ However, we only do this if we are rendering to the screen -- we ignore the theme
+ when we are printing. */
+ style = gtk_widget_get_style((GtkWidget *)html);
+ if (style) {
+ int state = GTK_WIDGET_STATE(html);
+ gushort r, g, b;
+#define SCALE (238)
+
+ /* choose a suitably darker or lighter colour */
+ r = style->base[state].red >> 8;
+ g = style->base[state].green >> 8;
+ b = style->base[state].blue >> 8;
+
+ if (r+b+g > 128*3) {
+ r = (r*SCALE) >> 8;
+ g = (g*SCALE) >> 8;
+ b = (b*SCALE) >> 8;
+ } else {
+ r = 128 - ((SCALE * r) >> 9);
+ g = 128 - ((SCALE * g) >> 9);
+ b = 128 - ((SCALE * b) >> 9);
+ }
+
+ efhd->formathtml.header_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+
+ r = style->text[state].red >> 8;
+ g = style->text[state].green >> 8;
+ b = style->text[state].blue >> 8;
+
+ efhd->formathtml.text_colour = ((r<<16) | (g<< 8) | b) & 0xffffff;
+ }
+#undef SCALE
+}
+
+static void
+efhd_init(GObject *o)
+{
+ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)o;
+#define efh ((EMFormatHTML *)efhd)
+
+ efhd->priv = g_malloc0(sizeof(*efhd->priv));
+
+ efhd->search_tok = (ESearchingTokenizer *)e_searching_tokenizer_new();
+ html_engine_set_tokenizer(efh->html->engine, (HTMLTokenizer *)efhd->search_tok);
+
+ g_signal_connect(efh->html, "realize", G_CALLBACK(efhd_gtkhtml_realise), o);
+
+ /* we want to convert url's etc */
+ efh->text_html_flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+#undef efh
+}
+
+static void
+efhd_finalise(GObject *o)
+{
+ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)o;
+
+ /* check pending stuff */
+
+ g_free(efhd->priv->search_text);
+ g_free(efhd->priv);
+
+ ((GObjectClass *)efhd_parent)->finalize(o);
+}
+
+static gboolean
+efhd_bool_accumulator(GSignalInvocationHint *ihint, GValue *out, const GValue *in, void *data)
+{
+ gboolean val = g_value_get_boolean(in);
+
+ g_value_set_boolean(out, val);
+
+ return !val;
+}
+
+static void
+efhd_class_init(GObjectClass *klass)
+{
+ ((EMFormatClass *)klass)->format_clone = efhd_format_clone;
+ ((EMFormatClass *)klass)->format_error = efhd_format_error;
+ ((EMFormatClass *)klass)->format_message = efhd_format_message;
+ ((EMFormatClass *)klass)->format_source = efhd_format_source;
+ ((EMFormatClass *)klass)->format_attachment = efhd_format_attachment;
+ ((EMFormatClass *)klass)->complete = efhd_complete;
+
+ klass->finalize = efhd_finalise;
+
+ efhd_signals[EFHD_LINK_CLICKED] =
+ g_signal_new("link_clicked",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(EMFormatHTMLDisplayClass, link_clicked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ efhd_signals[EFHD_POPUP_EVENT] =
+ g_signal_new("popup_event",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(EMFormatHTMLDisplayClass, popup_event),
+ efhd_bool_accumulator, NULL,
+ em_marshal_BOOLEAN__BOXED_POINTER_POINTER,
+ G_TYPE_BOOLEAN, 3,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
+ G_TYPE_POINTER, G_TYPE_POINTER);
+
+ efhd_builtin_init((EMFormatHTMLDisplayClass *)klass);
+}
+
+GType
+em_format_html_display_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFormatHTMLDisplayClass),
+ NULL, NULL,
+ (GClassInitFunc)efhd_class_init,
+ NULL, NULL,
+ sizeof(EMFormatHTMLDisplay), 0,
+ (GInstanceInitFunc)efhd_init
+ };
+ efhd_parent = g_type_class_ref(em_format_html_get_type());
+ type = g_type_register_static(em_format_html_get_type(), "EMFormatHTMLDisplay", &info, 0);
+ }
+
+ return type;
+}
+
+EMFormatHTMLDisplay *em_format_html_display_new(void)
+{
+ EMFormatHTMLDisplay *efhd;
+
+ efhd = g_object_new(em_format_html_display_get_type(), 0);
+
+ g_signal_connect(efhd->formathtml.html, "iframe_created", G_CALLBACK(efhd_iframe_created), efhd);
+ g_signal_connect(efhd->formathtml.html, "link_clicked", G_CALLBACK(efhd_html_link_clicked), efhd);
+ g_signal_connect(efhd->formathtml.html, "button_press_event", G_CALLBACK(efhd_html_button_press_event), efhd);
+
+ return efhd;
+}
+
+void em_format_html_display_goto_anchor(EMFormatHTMLDisplay *efhd, const char *name)
+{
+ printf("FIXME: go to anchor '%s'\n", name);
+}
+
+void em_format_html_display_set_animate(EMFormatHTMLDisplay *efhd, gboolean state)
+{
+ efhd->animate = state;
+ gtk_html_set_animate(((EMFormatHTML *)efhd)->html, state);
+}
+
+void em_format_html_display_set_caret_mode(EMFormatHTMLDisplay *efhd, gboolean state)
+{
+ efhd->caret_mode = state;
+ gtk_html_set_caret_mode(((EMFormatHTML *)efhd)->html, state);
+}
+
+void
+em_format_html_display_set_search(EMFormatHTMLDisplay *efhd, int type, GSList *strings)
+{
+ switch(type&3) {
+ case EM_FORMAT_HTML_DISPLAY_SEARCH_PRIMARY:
+ e_searching_tokenizer_set_primary_case_sensitivity(efhd->search_tok, (type&EM_FORMAT_HTML_DISPLAY_SEARCH_ICASE) == 0);
+ e_searching_tokenizer_set_primary_search_string(efhd->search_tok, NULL);
+ while (strings) {
+ e_searching_tokenizer_add_primary_search_string(efhd->search_tok, strings->data);
+ strings = strings->next;
+ }
+ break;
+ case EM_FORMAT_HTML_DISPLAY_SEARCH_SECONDARY:
+ default:
+ e_searching_tokenizer_set_secondary_case_sensitivity(efhd->search_tok, (type&EM_FORMAT_HTML_DISPLAY_SEARCH_ICASE) == 0);
+ e_searching_tokenizer_set_secondary_search_string(efhd->search_tok, NULL);
+ while (strings) {
+ e_searching_tokenizer_add_secondary_search_string(efhd->search_tok, strings->data);
+ strings = strings->next;
+ }
+ break;
+ }
+
+ d(printf("redrawing with search\n"));
+ em_format_format_clone((EMFormat *)efhd, ((EMFormat *)efhd)->message, (EMFormat *)efhd);
+}
+
+static void
+efhd_update_matches(EMFormatHTMLDisplay *efhd)
+{
+ struct _EMFormatHTMLDisplayPrivate *p = efhd->priv;
+ char *str;
+ /* message-search popup match count string */
+ char *fmt = _("Matches: %d");
+
+ if (p->search_dialog) {
+ str = alloca(strlen(fmt)+32);
+ sprintf(str, fmt, e_searching_tokenizer_match_count(efhd->search_tok));
+ gtk_label_set_text((GtkLabel *)p->search_matches_label, str);
+ }
+}
+
+static void
+efhd_update_search(EMFormatHTMLDisplay *efhd)
+{
+ struct _EMFormatHTMLDisplayPrivate *p = efhd->priv;
+ GSList *words = NULL;
+ int flags = 0;
+
+ if (p->search_text == NULL)
+ return;
+
+ if (!gtk_toggle_button_get_active((GtkToggleButton *)p->search_case_check))
+ flags = EM_FORMAT_HTML_DISPLAY_SEARCH_ICASE | EM_FORMAT_HTML_DISPLAY_SEARCH_PRIMARY;
+ else
+ flags = EM_FORMAT_HTML_DISPLAY_SEARCH_PRIMARY;
+
+ if (p->search_text)
+ words = g_slist_append(words, p->search_text);
+
+ em_format_html_display_set_search(efhd, flags, words);
+ g_slist_free(words);
+}
+
+static void
+efhd_search_response(GtkWidget *w, int button, EMFormatHTMLDisplay *efhd)
+{
+ struct _EMFormatHTMLDisplayPrivate *p = efhd->priv;
+
+ if (button == GTK_RESPONSE_ACCEPT) {
+ char *txt = g_strdup(gtk_entry_get_text((GtkEntry *)p->search_entry));
+
+ g_strstrip(txt);
+ if (p->search_text && strcmp(p->search_text, txt) == 0 && !p->search_wrap) {
+ if (!gtk_html_engine_search_next(((EMFormatHTML *)efhd)->html))
+ p->search_wrap = TRUE;
+ g_free(txt);
+ } else {
+ g_free(p->search_text);
+ p->search_text = txt;
+ if (!p->search_wrap)
+ efhd_update_search(efhd);
+ p->search_wrap = FALSE;
+ gtk_html_engine_search(((EMFormatHTML *)efhd)->html, txt,
+ gtk_toggle_button_get_active((GtkToggleButton *)p->search_case_check),
+ TRUE, FALSE);
+ }
+ } else {
+ g_free(p->search_text);
+ p->search_text = NULL;
+ gtk_widget_destroy((GtkWidget *)p->search_dialog);
+ p->search_dialog = NULL;
+ }
+}
+
+static void
+efhd_search_case_toggled(GtkWidget *w, EMFormatHTMLDisplay *efhd)
+{
+ struct _EMFormatHTMLDisplayPrivate *p = efhd->priv;
+
+ g_free(p->search_text);
+ p->search_text = NULL;
+ efhd_search_response(w, GTK_RESPONSE_ACCEPT, efhd);
+}
+
+static void
+efhd_search_entry_activate(GtkWidget *w, EMFormatHTMLDisplay *efhd)
+{
+ efhd_search_response(w, GTK_RESPONSE_ACCEPT, efhd);
+}
+
+/**
+ * em_format_html_display_search:
+ * @efhd:
+ *
+ * Run an interactive search dialogue.
+ **/
+void
+em_format_html_display_search(EMFormatHTMLDisplay *efhd)
+{
+ struct _EMFormatHTMLDisplayPrivate *p = efhd->priv;
+ GladeXML *xml;
+
+ if (p->search_dialog) {
+ gdk_window_raise(((GtkWidget *)p->search_dialog)->window);
+ return;
+ }
+
+ xml = glade_xml_new (EVOLUTION_GLADEDIR "/mail-search.glade", "search_message_dialog", NULL);
+ if (xml == NULL) {
+ g_warning("Cannot open search dialog glade file");
+ /* ?? */
+ return;
+ }
+
+ /* TODO: The original put the subject in the frame, but it had some
+ ugly arbitrary string-cutting code to make sure it fit. */
+
+ p->search_dialog = (GtkDialog *)glade_xml_get_widget(xml, "search_message_dialog");
+ p->search_entry = glade_xml_get_widget(xml, "search_entry");
+ p->search_matches_label = glade_xml_get_widget(xml, "search_matches_label");
+ p->search_case_check = glade_xml_get_widget(xml, "search_case_check");
+ p->search_wrap = FALSE;
+
+ gtk_dialog_set_default_response((GtkDialog *)p->search_dialog, GTK_RESPONSE_ACCEPT);
+ efhd_update_matches(efhd);
+
+ g_signal_connect(p->search_entry, "activate", G_CALLBACK(efhd_search_entry_activate), efhd);
+ g_signal_connect(p->search_case_check, "toggled", G_CALLBACK(efhd_search_case_toggled), efhd);
+ g_signal_connect(p->search_dialog, "response", G_CALLBACK(efhd_search_response), efhd);
+ e_dialog_set_transient_for((GtkWindow *)p->search_dialog, (GtkWidget *)efhd);
+ gtk_widget_show((GtkWidget *)p->search_dialog);
+}
+
+void
+em_format_html_display_cut (EMFormatHTMLDisplay *efhd)
+{
+ gtk_html_cut (((EMFormatHTML *) efhd)->html);
+}
+
+void
+em_format_html_display_copy (EMFormatHTMLDisplay *efhd)
+{
+ gtk_html_copy (((EMFormatHTML *) efhd)->html);
+}
+
+void
+em_format_html_display_paste (EMFormatHTMLDisplay *efhd)
+{
+ gtk_html_paste (((EMFormatHTML *) efhd)->html, FALSE);
+}
+
+void
+em_format_html_display_zoom_in (EMFormatHTMLDisplay *efhd)
+{
+ gtk_html_zoom_in (((EMFormatHTML *) efhd)->html);
+}
+
+void
+em_format_html_display_zoom_out (EMFormatHTMLDisplay *efhd)
+{
+ gtk_html_zoom_out (((EMFormatHTML *) efhd)->html);
+}
+
+void
+em_format_html_display_zoom_reset (EMFormatHTMLDisplay *efhd)
+{
+ gtk_html_zoom_reset (((EMFormatHTML *) efhd)->html);
+}
+
+/* ********************************************************************** */
+
+static void
+efhd_iframe_created(GtkHTML *html, GtkHTML *iframe, EMFormatHTMLDisplay *efh)
+{
+ d(printf("Iframe created %p ... \n", iframe));
+
+ g_signal_connect(iframe, "button_press_event", G_CALLBACK (efhd_html_button_press_event), efh);
+
+ return;
+}
+
+static int
+efhd_html_button_press_event (GtkWidget *widget, GdkEventButton *event, EMFormatHTMLDisplay *efhd)
+{
+ HTMLEngine *e;
+ HTMLPoint *point;
+ const char *url;
+ gboolean res = FALSE;
+
+ if (event->button != 3)
+ return FALSE;
+
+ e = ((GtkHTML *)widget)->engine;
+ point = html_engine_get_point_at(e, event->x, event->y, FALSE);
+ if (point == NULL)
+ return FALSE;
+
+ d(printf("popup button pressed\n"));
+
+ if ( (url = html_object_get_src(point->object)) != NULL
+ || (url = html_object_get_url(point->object, 0)) != NULL) {
+ EMFormatPURI *puri;
+ char *uri;
+
+ uri = gtk_html_get_url_object_relative((GtkHTML *)widget, point->object, url);
+ puri = em_format_find_puri((EMFormat *)efhd, uri);
+
+ d(printf("poup event, uri = '%s' part = '%p'\n", uri, puri?puri->part:NULL));
+
+ g_signal_emit((GtkObject *)efhd, efhd_signals[EFHD_POPUP_EVENT], 0, event, uri, puri?puri->part:NULL, &res);
+ g_free(uri);
+ }
+
+ html_point_destroy(point);
+
+ return res;
+}
+
+static void
+efhd_html_link_clicked (GtkHTML *html, const char *url, EMFormatHTMLDisplay *efhd)
+{
+ d(printf("link clicked event '%s'\n", url));
+ g_signal_emit((GObject *)efhd, efhd_signals[EFHD_LINK_CLICKED], 0, url);
+}
+
+static void
+efhd_complete(EMFormat *emf)
+{
+ EMFormatHTMLDisplay *efhd = (EMFormatHTMLDisplay *)emf;
+
+ if (efhd->priv->search_dialog)
+ efhd_update_matches(efhd);
+}
+
+/* ********************************************************************** */
+
+static void
+efhd_signature_check(GtkWidget *w, EMFormatHTMLPObject *pobject)
+{
+ d(printf("insert signature check here ... redraw ? or what ?\n"));
+ /* blah, do the old way for now, force a complete re-draw */
+ em_format_set_inline((EMFormat *)pobject->format, pobject->part, TRUE);
+ em_format_format_clone((EMFormat *)pobject->format, ((EMFormat *)pobject->format)->message, (EMFormat *)pobject->format);
+}
+
+static gboolean
+efhd_signature_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
+{
+ GtkWidget *icon, *button;
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_file(EVOLUTION_ICONSDIR "/pgp-signature-nokey.png", NULL);
+ if (pixbuf == NULL)
+ return FALSE;
+
+ /* wtf isn't this just scaled on disk? */
+ icon = gtk_image_new_from_pixbuf(gdk_pixbuf_scale_simple(pixbuf, 24, 24, GDK_INTERP_BILINEAR));
+ g_object_unref(pixbuf);
+ gtk_widget_show(icon);
+
+ button = gtk_button_new();
+ g_signal_connect(button, "clicked", G_CALLBACK (efhd_signature_check), pobject);
+ /*g_signal_connect (button, "key_press_event", G_CALLBACK (inline_button_press), part);*/
+
+ gtk_container_add((GtkContainer *)button, icon);
+ gtk_widget_show(button);
+ gtk_container_add((GtkContainer *)eb, button);
+
+ return TRUE;
+}
+
+static void
+efhd_multipart_signed (EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ char *classid;
+ static int signedid;
+ CamelMultipartSigned *mps;
+ CamelMimePart *cpart;
+
+ mps = (CamelMultipartSigned *)camel_medium_get_content_object((CamelMedium *)part);
+ if (!CAMEL_IS_MULTIPART_SIGNED(mps)
+ || (cpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ em_format_part(emf, stream, cpart);
+
+ if (em_format_is_inline(emf, part)) {
+ em_format_html_multipart_signed_sign(emf, stream, part);
+ } else {
+ classid = g_strdup_printf("multipart-signed:///icon/%d", signedid++);
+
+ /* wtf is this so fugly? */
+ camel_stream_printf(stream,
+ "<br><table cellspacing=0 cellpadding=0>"
+ "<tr><td><table width=10 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td>"
+ "<td><object classid=\"%s\"></object></td>"
+ "<td><table width=3 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td>"
+ "<td><font size=-1>%s</font></td></tr>"
+ "<tr><td height=10>"
+ "<table cellspacing=0 cellpadding=0><tr>"
+ "<td height=10><a name=\"glue\"></td></tr>"
+ "</table></td></tr></table>\n",
+ classid,
+ _("This message is digitally signed. Click the lock icon for more information."));
+
+ em_format_html_add_pobject((EMFormatHTML *)emf, classid, efhd_signature_button, part);
+ g_free(classid);
+ }
+}
+
+/* ********************************************************************** */
+
+static EMFormatHandler type_builtin_table[] = {
+ { "multipart/signed", (EMFormatFunc)efhd_multipart_signed },
+};
+
+static void
+efhd_builtin_init(EMFormatHTMLDisplayClass *efhc)
+{
+ int i;
+
+ for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+ em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
+
+/* ********************************************************************** */
+
+static void efhd_format_clone(EMFormat *emf, CamelMedium *part, EMFormat *src)
+{
+ ((EMFormatClass *)efhd_parent)->format_clone(emf, part, src);
+}
+
+/* TODO: if these aren't going to do anything should remove */
+static void efhd_format_error(EMFormat *emf, CamelStream *stream, const char *txt)
+{
+ ((EMFormatClass *)efhd_parent)->format_error(emf, stream, txt);
+}
+
+static void efhd_format_message(EMFormat *emf, CamelStream *stream, CamelMedium *part)
+{
+ ((EMFormatClass *)efhd_parent)->format_message(emf, stream, part);
+}
+
+static void efhd_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ ((EMFormatClass *)efhd_parent)->format_source(emf, stream, part);
+}
+
+/* ********************************************************************** */
+
+/* if it hasn't been processed yet, format the attachment */
+static void
+efhd_attachment_show(GtkWidget *w, struct _attach_puri *info)
+{
+ d(printf("show attachment button called\n"));
+
+ info->shown = ~info->shown;
+ em_format_set_inline(info->puri.format, info->puri.part, info->shown);
+ /* FIXME: do this in an idle handler */
+ em_format_format_clone(info->puri.format, info->puri.format->message, info->puri.format);
+#if 0
+ /* FIXME: track shown state in parent */
+
+ if (info->shown) {
+ d(printf("hiding\n"));
+ info->shown = FALSE;
+ if (info->frame)
+ gtk_widget_hide((GtkWidget *)info->frame);
+ gtk_widget_show(info->forward);
+ gtk_widget_hide(info->down);
+ } else {
+ d(printf("showing\n"));
+ info->shown = TRUE;
+ if (info->frame)
+ gtk_widget_show((GtkWidget *)info->frame);
+ gtk_widget_hide(info->forward);
+ gtk_widget_show(info->down);
+
+ /* have we decoded it yet? */
+ if (info->output) {
+ info->handle->handler(info->puri.format, info->output, info->puri.part, info->handle);
+ camel_stream_close(info->output);
+ camel_object_unref(info->output);
+ info->output = NULL;
+ }
+ }
+
+ em_format_set_inline(info->puri.format, info->puri.part, info->shown);
+#endif
+}
+
+static EMPopupItem efhd_menu_items[] = {
+ { EM_POPUP_BAR, "05.display", },
+ { EM_POPUP_ITEM, "05.display.00", N_("_View Inline"), G_CALLBACK(efhd_attachment_show) },
+ { EM_POPUP_ITEM, "05.display.00", N_("_Hide"), G_CALLBACK(efhd_attachment_show) },
+};
+
+static void
+efhd_popup_place_widget(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data)
+{
+ GtkWidget *w = user_data;
+
+ gdk_window_get_origin(gtk_widget_get_parent_window(w), x, y);
+ *x += w->allocation.x + w->allocation.width;
+ *y += w->allocation.y;
+}
+
+static gboolean
+efhd_attachment_popup(GtkWidget *w, GdkEventButton *event, struct _attach_puri *info)
+{
+ GtkMenu *menu;
+ GSList *menus = NULL;
+ EMPopup *emp;
+ EMPopupTarget *target;
+ EMPopupItem *item;
+
+ d(printf("attachment popup, button %d\n", event->button));
+
+ if (event && event->button != 1 && event->button != 3) {
+ /* ?? gtk_propagate_event(GTK_WIDGET (user_data), (GdkEvent *)event);*/
+ return FALSE;
+ }
+
+ emp = em_popup_new("com.ximian.mail.formathtmldisplay.popup.part");
+ target = em_popup_target_new_part(info->puri.part, info->handle?info->handle->mime_type:NULL);
+ target->widget = w;
+
+ /* add our local menus */
+ efhd_menu_items[0].activate_data = info;
+ menus = g_slist_prepend(menus, &efhd_menu_items[0]);
+ item = &efhd_menu_items[info->shown?2:1];
+ item->activate_data = info;
+ menus = g_slist_prepend(menus, item);
+
+ menu = em_popup_create_menu_once(emp, target, target->mask, target->mask);
+ if (event)
+ gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
+ else
+ gtk_menu_popup(menu, NULL, NULL, (GtkMenuPositionFunc)efhd_popup_place_widget, w, 0, gtk_get_current_event_time());
+
+ return TRUE;
+}
+
+static gboolean
+efhd_attachment_popup_menu(GtkWidget *w, struct _attach_puri *info)
+{
+ return efhd_attachment_popup(w, NULL, info);
+}
+
+/* ********************************************************************** */
+
+static void
+efhd_drag_data_get(GtkWidget *w, GdkDragContext *drag, GtkSelectionData *data, guint info, guint time, EMFormatHTMLPObject *pobject)
+{
+ CamelMimePart *part = pobject->part;
+ char *uri, *path;
+ CamelStream *stream;
+
+ switch (info) {
+ case 0: /* mime/type request */
+ stream = camel_stream_mem_new();
+ /* TODO: shoudl format_format_text run on the content-object? */
+ /* TODO: should we just do format_content? */
+ if (header_content_type_is(((CamelDataWrapper *)part)->mime_type, "text", "*"))
+ /* FIXME: this should be an em_utils method, it only needs a default charset param */
+ em_format_format_text((EMFormat *)pobject->format, stream, (CamelDataWrapper *)part);
+ else {
+ CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
+
+ camel_data_wrapper_decode_to_stream(dw, stream);
+ }
+
+ gtk_selection_data_set(data, data->target, 8,
+ ((CamelStreamMem *)stream)->buffer->data,
+ ((CamelStreamMem *)stream)->buffer->len);
+ camel_object_unref(stream);
+ break;
+ case 1: /* text-uri-list request */
+ /* Kludge around Nautilus requesting the same data many times */
+ uri = g_object_get_data((GObject *)w, "e-drag-uri");
+ if (uri) {
+ gtk_selection_data_set(data, data->target, 8, uri, strlen(uri));
+ return;
+ }
+
+ path = em_utils_temp_save_part(w, part);
+ if (path == NULL)
+ return;
+
+ uri = g_strdup_printf("file://%s", path);
+ g_free(path);
+ gtk_selection_data_set(data, data->target, 8, uri, strlen(uri));
+ g_object_set_data_full((GObject *)w, "e-drag-uri", uri, g_free);
+ break;
+ default:
+ abort();
+ }
+}
+
+static void
+efhd_drag_data_delete(GtkWidget *w, GdkDragContext *drag, EMFormatHTMLPObject *pobject)
+{
+ char *uri;
+
+ uri = g_object_get_data((GObject *)w, "e-drag-uri");
+ if (uri) {
+ /* NB: this doesn't kill the dnd directory */
+ /* NB: is this ever called? */
+ unlink(uri+7);
+ g_object_set_data((GObject *)w, "e-drag-uri", NULL);
+ }
+}
+
+static void
+efhd_write_icon_job(struct _EMFormatHTMLJob *job, int cancelled)
+{
+ EMFormatHTMLPObject *pobject;
+ CamelDataWrapper *dw;
+
+ if (cancelled)
+ return;
+
+ pobject = job->u.data;
+ dw = camel_medium_get_content_object((CamelMedium *)pobject->part);
+ camel_data_wrapper_decode_to_stream(dw, job->stream);
+ camel_stream_close(job->stream);
+}
+
+/* attachment button callback */
+static gboolean
+efhd_attachment_button(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
+{
+ struct _attach_puri *info;
+ GtkWidget *hbox, *w, *button, *mainbox;
+ char *simple_type;
+ GtkTargetEntry drag_types[] = {
+ { NULL, 0, 0 },
+ { "text/uri-list", 0, 1 },
+ };
+
+ /* FIXME: handle default shown case */
+ d(printf("adding attachment button/content\n"));
+
+ info = (struct _attach_puri *)em_format_find_puri((EMFormat *)efh, pobject->classid);
+ g_assert(info != NULL);
+ g_assert(info->forward == NULL);
+
+ mainbox = gtk_hbox_new(FALSE, 0);
+
+ button = gtk_button_new();
+
+ if (info->handle)
+ g_signal_connect(button, "clicked", G_CALLBACK(efhd_attachment_show), info);
+ else
+ GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ info->forward = gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start((GtkBox *)hbox, info->forward, TRUE, TRUE, 0);
+ if (info->handle) {
+ info->down = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start((GtkBox *)hbox, info->down, TRUE, TRUE, 0);
+ }
+
+ w = gtk_image_new();
+ gtk_widget_set_size_request(w, 24, 24);
+ gtk_box_pack_start((GtkBox *)hbox, w, TRUE, TRUE, 0);
+ gtk_container_add((GtkContainer *)button, hbox);
+ gtk_box_pack_start((GtkBox *)mainbox, button, TRUE, TRUE, 0);
+
+ /* FIXME: loses any snoop info */
+ simple_type = header_content_type_simple(((CamelDataWrapper *)pobject->part)->mime_type);
+ camel_strdown(simple_type);
+
+ /* cache? */
+ /* FIXME: offline parts, just get icon */
+ if (header_content_type_is(((CamelDataWrapper *)pobject->part)->mime_type, "image", "*")) {
+ EMFormatHTMLJob *job;
+
+ job = em_format_html_job_new(efh, efhd_write_icon_job, pobject);
+ job->stream = (CamelStream *)em_icon_stream_new((GtkImage *)w);
+ em_format_html_job_queue(efh, job);
+ } else {
+ GdkPixbuf *pixbuf = e_icon_for_mime_type(simple_type, 24);
+ GdkPixbuf *mini = gdk_pixbuf_scale_simple(pixbuf, 24, 24, GDK_INTERP_BILINEAR);
+
+ gtk_image_set_from_pixbuf((GtkImage *)w, mini);
+ g_object_unref(mini);
+ g_object_unref(pixbuf);
+ }
+
+ drag_types[0].target = simple_type;
+ gtk_drag_source_set(button, GDK_BUTTON1_MASK, drag_types, sizeof(drag_types)/sizeof(drag_types[0]), GDK_ACTION_COPY);
+ g_signal_connect(button, "drag-data-get", G_CALLBACK(efhd_drag_data_get), pobject);
+ g_signal_connect (button, "drag-data-delete", G_CALLBACK(efhd_drag_data_delete), pobject);
+ g_free(simple_type);
+
+ button = gtk_button_new();
+ /*GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);*/
+ gtk_container_add((GtkContainer *)button, gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_ETCHED_IN));
+ g_signal_connect(button, "button_press_event", G_CALLBACK(efhd_attachment_popup), info);
+ g_signal_connect(button, "popup_menu", G_CALLBACK(efhd_attachment_popup_menu), info);
+ g_signal_connect(button, "clicked", G_CALLBACK(efhd_attachment_popup_menu), info);
+ gtk_box_pack_start((GtkBox *)mainbox, button, TRUE, TRUE, 0);
+
+ gtk_widget_show_all(mainbox);
+
+ if (info->shown)
+ gtk_widget_hide(info->forward);
+ else if (info->down)
+ gtk_widget_hide(info->down);
+
+ gtk_container_add((GtkContainer *)eb, mainbox);
+
+ return TRUE;
+}
+
+/* not used currently */
+/* frame source callback */
+static void
+efhd_attachment_frame(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+ struct _attach_puri *info = (struct _attach_puri *)puri;
+
+ if (info->shown) {
+ d(printf("writing to frame content, handler is '%s'\n", info->handle->mime_type));
+ info->handle->handler(emf, stream, info->puri.part, info->handle);
+ camel_stream_close(stream);
+ } else {
+ /* FIXME: this is leaked if the object is closed without showing it
+ NB: need a virtual puri_free method? */
+ info->output = stream;
+ camel_object_ref(stream);
+ }
+}
+
+static gboolean
+efhd_bonobo_object(EMFormatHTML *efh, GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject)
+{
+ CamelDataWrapper *wrapper;
+ Bonobo_ServerInfo *component;
+ GtkWidget *embedded;
+ Bonobo_PersistStream persist;
+ CORBA_Environment ev;
+ CamelStreamMem *cstream;
+ BonoboStream *bstream;
+ BonoboControlFrame *control_frame;
+ Bonobo_PropertyBag prop_bag;
+
+ component = gnome_vfs_mime_get_default_component(eb->type);
+ if (component == NULL)
+ return FALSE;
+
+ embedded = bonobo_widget_new_control(component->iid, NULL);
+ CORBA_free(component);
+ if (embedded == NULL)
+ return FALSE;
+
+ CORBA_exception_init(&ev);
+
+ control_frame = bonobo_widget_get_control_frame((BonoboWidget *)embedded);
+ prop_bag = bonobo_control_frame_get_control_property_bag(control_frame, NULL);
+ if (prop_bag != CORBA_OBJECT_NIL) {
+ /*
+ * Now we can take care of business. Currently, the only control
+ * that needs something passed to it through a property bag is
+ * the iTip control, and it needs only the From email address,
+ * but perhaps in the future we can generalize this section of code
+ * to pass a bunch of useful things to all embedded controls.
+ */
+ const CamelInternetAddress *from;
+ char *from_address;
+
+ from = camel_mime_message_get_from((CamelMimeMessage *)((EMFormat *)efh)->message);
+ from_address = camel_address_encode((CamelAddress *)from);
+ bonobo_property_bag_client_set_value_string(prop_bag, "from_address", from_address, &ev);
+ g_free(from_address);
+
+ Bonobo_Unknown_unref(prop_bag, &ev);
+ }
+
+ persist = (Bonobo_PersistStream)Bonobo_Unknown_queryInterface(bonobo_widget_get_objref((BonoboWidget *)embedded),
+ "IDL:Bonobo/PersistStream:1.0", NULL);
+ if (persist == CORBA_OBJECT_NIL) {
+ gtk_object_sink((GtkObject *)embedded);
+ CORBA_exception_free(&ev);
+ return FALSE;
+ }
+
+ /* Write the data to a CamelStreamMem... */
+ cstream = (CamelStreamMem *)camel_stream_mem_new();
+ wrapper = camel_medium_get_content_object((CamelMedium *)pobject->part);
+ camel_data_wrapper_decode_to_stream(wrapper, (CamelStream *)cstream);
+
+ /* ...convert the CamelStreamMem to a BonoboStreamMem... */
+ bstream = bonobo_stream_mem_create(cstream->buffer->data, cstream->buffer->len, TRUE, FALSE);
+ camel_object_unref(cstream);
+
+ /* ...and hydrate the PersistStream from the BonoboStream. */
+ Bonobo_PersistStream_load(persist,
+ bonobo_object_corba_objref(BONOBO_OBJECT (bstream)),
+ eb->type, &ev);
+ bonobo_object_unref(BONOBO_OBJECT (bstream));
+ Bonobo_Unknown_unref(persist, &ev);
+ CORBA_Object_release(persist, &ev);
+
+ if (ev._major != CORBA_NO_EXCEPTION) {
+ gtk_object_sink((GtkObject *)embedded);
+ CORBA_exception_free(&ev);
+ return FALSE;
+ }
+ CORBA_exception_free(&ev);
+
+ gtk_widget_show(embedded);
+ gtk_container_add(GTK_CONTAINER (eb), embedded);
+
+ return TRUE;
+}
+
+static gboolean
+efhd_check_server_prop(Bonobo_ServerInfo *component, const char *propname, const char *value)
+{
+ CORBA_sequence_CORBA_string stringv;
+ Bonobo_ActivationProperty *prop;
+ int i;
+
+ prop = bonobo_server_info_prop_find(component, propname);
+ if (!prop || prop->v._d != Bonobo_ACTIVATION_P_STRINGV)
+ return FALSE;
+
+ stringv = prop->v._u.value_stringv;
+ for (i = 0; i < stringv._length; i++) {
+ if (!g_ascii_strcasecmp(value, stringv._buffer[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+efhd_use_component(const char *mime_type)
+{
+ GList *components, *iter;
+ Bonobo_ServerInfo *component = NULL;
+
+ /* should this cache it? */
+
+ if (g_ascii_strcasecmp(mime_type, "text/x-vcard") != 0
+ && g_ascii_strcasecmp(mime_type, "text/calendar") != 0) {
+ const char **mime_types;
+ int i;
+
+ mime_types = mail_config_get_allowable_mime_types();
+ for (i = 0; mime_types[i]; i++) {
+ if (!g_ascii_strcasecmp(mime_types[i], mime_type))
+ goto type_ok;
+ }
+ return FALSE;
+ }
+type_ok:
+ components = gnome_vfs_mime_get_all_components (mime_type);
+ for (iter = components; iter; iter = iter->next) {
+ Bonobo_ServerInfo *comp = iter->data;
+
+ comp = iter->data;
+ if (efhd_check_server_prop(comp, "repo_ids", "IDL:Bonobo/PersistStream:1.0")
+ && efhd_check_server_prop(comp, "bonobo:supported_mime_types", mime_type)) {
+ component = comp;
+ break;
+ }
+ }
+ gnome_vfs_mime_component_list_free (components);
+
+ return component != NULL;
+}
+
+static void
+efhd_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type, const EMFormatHandler *handle)
+{
+ char *classid, *text, *html;
+ struct _attach_puri *info;
+
+ classid = g_strdup_printf("attachment-%p", part);
+ info = (struct _attach_puri *)em_format_add_puri(emf, sizeof(*info), classid, part, efhd_attachment_frame);
+ em_format_html_add_pobject((EMFormatHTML *)emf, classid, efhd_attachment_button, part);
+ info->handle = handle;
+ info->shown = em_format_is_inline(emf, info->puri.part) && handle != NULL;
+
+ camel_stream_write_string(stream,
+ "<table cellspacing=0 cellpadding=0><tr><td>"
+ "<table width=10 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td>");
+
+ camel_stream_printf(stream, "<td><object classid=\"%s\"></object></td>", classid);
+
+ camel_stream_write_string(stream,
+ "<td><table width=3 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td><td><font size=-1>");
+
+ /* output some info about it */
+ /* FIXME: should we look up mime_type from object again? */
+ text = em_format_describe_part(part, mime_type);
+ html = camel_text_to_html(text, ((EMFormatHTML *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+ camel_stream_write_string(stream, html);
+ g_free(html);
+ g_free(text);
+
+ camel_stream_write_string(stream, "</font></td></tr><tr></table>");
+
+ if (handle) {
+ if (info->shown)
+ handle->handler(emf, stream, part, handle);
+ /*camel_stream_printf(stream, "<iframe src=\"%s\" marginheight=0 marginwidth=0>%s</iframe>\n", classid, _("Attachment content could not be loaded"));*/
+ } else if (efhd_use_component(mime_type)) {
+ static int partid;
+
+ g_free(classid); /* messy */
+
+ classid = g_strdup_printf("bonobo-unknown:///em-formath-html-display/%p/%d", part, partid++);
+ em_format_html_add_pobject((EMFormatHTML *)emf, classid, efhd_bonobo_object, part);
+ camel_stream_printf(stream, "<object classid=\"%s\" type=\"%s\">\n", classid, mime_type);
+ }
+
+ g_free(classid);
+}
diff --git a/mail/em-format-html-display.h b/mail/em-format-html-display.h
new file mode 100644
index 0000000000..cf78642112
--- /dev/null
+++ b/mail/em-format-html-display.h
@@ -0,0 +1,63 @@
+
+/*
+ Concrete class for formatting mails to displayed html
+*/
+
+#ifndef _EM_FORMAT_HTML_DISPLAY_H
+#define _EM_FORMAT_HTML_DISPLAY_H
+
+#include "em-format-html.h"
+
+typedef struct _EMFormatHTMLDisplay EMFormatHTMLDisplay;
+typedef struct _EMFormatHTMLDisplayClass EMFormatHTMLDisplayClass;
+
+struct _CamelMimePart;
+
+struct _EMFormatHTMLDisplay {
+ EMFormatHTML formathtml;
+
+ struct _EMFormatHTMLDisplayPrivate *priv;
+
+ struct _ESearchingTokenizer *search_tok;
+
+ unsigned int animate:1;
+ unsigned int caret_mode:1;
+};
+
+#define EM_FORMAT_HTML_DISPLAY_SEARCH_PRIMARY (0)
+#define EM_FORMAT_HTML_DISPLAY_SEARCH_SECONDARY (1)
+#define EM_FORMAT_HTML_DISPLAY_SEARCH_ICASE (1<<8)
+
+struct _EMFormatHTMLDisplayClass {
+ EMFormatHTMLClass formathtml_class;
+
+ /* a link clicked normally */
+ void (*link_clicked)(EMFormatHTMLDisplay *efhd, const char *uri);
+ /* a part or a link button pressed event */
+ int (*popup_event)(EMFormatHTMLDisplay *efhd, GdkEventButton *event, const char *uri, struct _CamelMimePart *part);
+};
+
+GType em_format_html_display_get_type(void);
+EMFormatHTMLDisplay *em_format_html_display_new(void);
+
+void em_format_html_display_goto_anchor(EMFormatHTMLDisplay *efhd, const char *name);
+
+void em_format_html_display_set_animate(EMFormatHTMLDisplay *efhd, gboolean state);
+void em_format_html_display_set_caret_mode(EMFormatHTMLDisplay *efhd, gboolean state);
+
+void em_format_html_display_set_search(EMFormatHTMLDisplay *efhd, int type, GSList *strings);
+void em_format_html_display_search(EMFormatHTMLDisplay *efhd);
+
+void em_format_html_display_cut (EMFormatHTMLDisplay *efhd);
+void em_format_html_display_copy (EMFormatHTMLDisplay *efhd);
+void em_format_html_display_paste (EMFormatHTMLDisplay *efhd);
+
+void em_format_html_display_zoom_in (EMFormatHTMLDisplay *efhd);
+void em_format_html_display_zoom_out (EMFormatHTMLDisplay *efhd);
+void em_format_html_display_zoom_reset (EMFormatHTMLDisplay *efhd);
+
+/* experimental */
+struct _EPopupExtension;
+void em_format_html_display_set_popup(EMFormatHTMLDisplay *, struct _EPopupExtension *);
+
+#endif /* !_EM_FORMAT_HTML_DISPLAY_H */
diff --git a/mail/em-format-html-print.c b/mail/em-format-html-print.c
new file mode 100644
index 0000000000..d02e72cebd
--- /dev/null
+++ b/mail/em-format-html-print.c
@@ -0,0 +1,186 @@
+
+#include <libgnomeprint/gnome-print-job.h>
+#include <libgnomeprintui/gnome-print-job-preview.h>
+
+#include <gtkhtml/gtkhtml.h>
+#include <gtk/gtkwindow.h>
+
+#include <camel/camel-i18n.h>
+#include "em-format-html-print.h"
+
+#include <string.h>
+
+static void efhp_builtin_init(EMFormatHTMLPrintClass *efhc);
+
+static EMFormatHTMLClass *efhp_parent;
+
+static void
+efhp_init(GObject *o)
+{
+ EMFormatHTMLPrint *efhp = (EMFormatHTMLPrint *)o;
+ GtkWidget *html = (GtkWidget *)efhp->formathtml.html;
+
+ /* ?? */
+ gtk_widget_set_name(html, "EvolutionMailPrintHTMLWidget");
+
+ /* gtk widgets don't like to be realized outside top level widget
+ so we put new html widget into gtk window */
+ efhp->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_container_add((GtkContainer *)efhp->window, html);
+ gtk_widget_realize(html);
+}
+
+static void
+efhp_finalise(GObject *o)
+{
+ EMFormatHTMLPrint *efhp = (EMFormatHTMLPrint *)o;
+
+ gtk_widget_destroy(efhp->window);
+ if (efhp->config)
+ g_object_unref(efhp->config);
+
+ ((GObjectClass *)efhp_parent)->finalize(o);
+}
+
+static void
+efhp_base_init(EMFormatHTMLPrintClass *efhpklass)
+{
+ efhp_builtin_init(efhpklass);
+}
+
+static void
+efhp_class_init(GObjectClass *klass)
+{
+ klass->finalize = efhp_finalise;
+}
+
+GType
+em_format_html_print_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFormatHTMLPrintClass),
+ (GBaseInitFunc)efhp_base_init, NULL,
+ (GClassInitFunc)efhp_class_init,
+ NULL, NULL,
+ sizeof(EMFormatHTMLPrint), 0,
+ (GInstanceInitFunc)efhp_init
+ };
+ efhp_parent = g_type_class_ref(em_format_html_get_type());
+ type = g_type_register_static(em_format_html_get_type(), "EMFormatHTMLPrint", &info, 0);
+ }
+
+ return type;
+}
+
+EMFormatHTMLPrint *em_format_html_print_new(void)
+{
+ EMFormatHTMLPrint *efhp;
+
+ efhp = g_object_new(em_format_html_print_get_type(), 0);
+
+ return efhp;
+}
+
+struct footer_info {
+ GnomeFont *local_font;
+ gint page_num, pages;
+};
+
+static void
+efhp_footer_cb(GtkHTML *html, GnomePrintContext *print_context, double x, double y, double width, double height, void *data)
+{
+ struct footer_info *info = data;
+
+ /* do we want anything nicer here, like who its from, etc? */
+ if (info->local_font) {
+ char *text = g_strdup_printf (_("Page %d of %d"), info->page_num, info->pages);
+ /*gdouble tw = gnome_font_get_width_string (info->local_font, text);*/
+ /* FIXME: work out how to measure this */
+ gdouble tw = strlen(text) * 8;
+
+ gnome_print_gsave(print_context);
+ gnome_print_newpath(print_context);
+ gnome_print_setrgbcolor(print_context, .0, .0, .0);
+ gnome_print_moveto(print_context, x + width - tw, y - gnome_font_get_ascender(info->local_font));
+ gnome_print_setfont(print_context, info->local_font);
+ gnome_print_show(print_context, text);
+ gnome_print_grestore(print_context);
+
+ g_free(text);
+ info->page_num++;
+ }
+}
+
+/* perform preview, or print */
+/* returns GNOME_PRINT_OK on success */
+static void
+emfhp_complete(EMFormatHTMLPrint *efhp, void *data)
+{
+ GnomePrintContext *print_context;
+ GnomePrintJob *print_job;
+ gdouble line = 0.0;
+ struct footer_info info;
+ int res = GNOME_PRINT_OK;
+
+ print_job = gnome_print_job_new(efhp->config);
+ print_context = gnome_print_job_get_context(print_job);
+
+ gtk_html_print_set_master(efhp->formathtml.html, print_job);
+ info.local_font = gnome_font_find_closest("Helvetica", 10.0);
+ if (info.local_font) {
+ line = gnome_font_get_ascender(info.local_font) - gnome_font_get_descender(info.local_font);
+ info.page_num = 1;
+ info.pages = gtk_html_print_get_pages_num(efhp->formathtml.html, print_context, 0.0, line);
+ gtk_html_print_with_header_footer(efhp->formathtml.html, print_context, 0.0, line, NULL, efhp_footer_cb, &info);
+ gnome_font_unref(info.local_font);
+ } else {
+ gtk_html_print(efhp->formathtml.html, print_context);
+ }
+ gtk_html_print_set_master(efhp->formathtml.html, NULL);
+
+ gnome_print_job_close(print_job);
+
+ if (efhp->preview)
+ gtk_widget_show(gnome_print_job_preview_new(print_job, _("Print Preview")));
+ else
+ res = gnome_print_job_print(print_job);
+
+ g_object_unref(print_job);
+ g_object_unref(efhp);
+}
+
+int em_format_html_print_print(EMFormatHTMLPrint *efhp, struct _CamelMedium *msg, EMFormatHTML *source, struct _GnomePrintConfig *print_config, int preview)
+{
+ efhp->config = print_config;
+ if (print_config)
+ g_object_ref(print_config);
+ efhp->preview = preview;
+
+ ((EMFormatHTML *)efhp)->load_http = source->load_http_now;
+
+ g_signal_connect(efhp, "complete", G_CALLBACK(emfhp_complete), efhp);
+
+ g_object_ref(efhp);
+ em_format_format_clone((EMFormat *)efhp, msg, (EMFormat *)source);
+
+ return 0; /* damn async ... */
+}
+
+/* ********************************************************************** */
+
+/* if only ... but i doubt this is possible with gnome print/gtkhtml */
+static EMFormatHandler type_builtin_table[] = {
+ /*{ "application/postscript", (EMFormatFunc)efhp_application_postscript },*/
+};
+
+static void
+efhp_builtin_init(EMFormatHTMLPrintClass *efhc)
+{
+ int i;
+
+ for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+ em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
diff --git a/mail/em-format-html-print.h b/mail/em-format-html-print.h
new file mode 100644
index 0000000000..78e3139e0f
--- /dev/null
+++ b/mail/em-format-html-print.h
@@ -0,0 +1,37 @@
+
+/*
+ Concrete class for formatting mails to displayed html
+*/
+
+#ifndef _EM_FORMAT_HTML_PRINT_H
+#define _EM_FORMAT_HTML_PRINT_H
+
+#include "em-format-html.h"
+
+struct _GnomePrintConfig;
+
+typedef struct _EMFormatHTMLPrint EMFormatHTMLPrint;
+typedef struct _EMFormatHTMLPrintClass EMFormatHTMLPrintClass;
+
+struct _CamelMimePart;
+
+struct _EMFormatHTMLPrint {
+ EMFormatHTML formathtml;
+
+ struct _GtkWidget *window; /* used to realise the gtkhtml in a toplevel, i dont know why */
+ struct _GnomePrintConfig *config;
+
+ int preview:1;
+};
+
+struct _EMFormatHTMLPrintClass {
+ EMFormatHTMLClass formathtml_class;
+};
+
+GType em_format_html_print_get_type(void);
+
+EMFormatHTMLPrint *em_format_html_print_new(void);
+
+int em_format_html_print_print(EMFormatHTMLPrint *efhp, struct _CamelMedium *msg, EMFormatHTML *source, struct _GnomePrintConfig *print_config, int preview);
+
+#endif /* ! _EM_FORMAT_HTML_PRINT_H */
diff --git a/mail/em-format-html-quote.c b/mail/em-format-html-quote.c
new file mode 100644
index 0000000000..4333251186
--- /dev/null
+++ b/mail/em-format-html-quote.c
@@ -0,0 +1,277 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <camel/camel-stream.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include "em-format-html-quote.h"
+
+struct _EMFormatHTMLQuotePrivate {
+ char *credits;
+};
+
+static void efhq_format_clone (EMFormat *, CamelMedium *, EMFormat *);
+static void efhq_format_error (EMFormat *emf, CamelStream *stream, const char *txt);
+static void efhq_format_message (EMFormat *, CamelStream *, CamelMedium *);
+static void efhq_format_source (EMFormat *, CamelStream *, CamelMimePart *);
+static void efhq_format_attachment (EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *);
+
+static void efhq_builtin_init (EMFormatHTMLQuoteClass *efhc);
+
+static EMFormatHTMLClass *efhq_parent;
+
+static void
+efhq_init (GObject *o)
+{
+ EMFormatHTMLQuote *efhq = (EMFormatHTMLQuote *) o;
+
+ efhq->priv = g_malloc0 (sizeof (*efhq->priv));
+
+ /* we want to convert url's etc */
+ ((EMFormatHTML *) efhq)->text_html_flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+ /* we want simple header format */
+ ((EMFormatHTML *) efhq)->simple_headers = TRUE;
+}
+
+static void
+efhq_finalise (GObject *o)
+{
+ EMFormatHTMLQuote *efhq = (EMFormatHTMLQuote *) o;
+
+ g_free (efhq->priv->credits);
+ g_free (efhq->priv);
+
+ ((GObjectClass *) efhq_parent)->finalize (o);
+}
+
+static void
+efhq_base_init(EMFormatHTMLQuoteClass *efhqklass)
+{
+ efhq_builtin_init(efhqklass);
+}
+
+static void
+efhq_class_init (GObjectClass *klass)
+{
+ ((EMFormatClass *) klass)->format_clone = efhq_format_clone;
+ ((EMFormatClass *) klass)->format_error = efhq_format_error;
+ ((EMFormatClass *) klass)->format_message = efhq_format_message;
+ ((EMFormatClass *) klass)->format_source = efhq_format_source;
+ ((EMFormatClass *) klass)->format_attachment = efhq_format_attachment;
+
+ klass->finalize = efhq_finalise;
+}
+
+GType
+em_format_html_quote_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (EMFormatHTMLQuoteClass),
+ (GBaseInitFunc)efhq_base_init, NULL,
+ (GClassInitFunc)efhq_class_init,
+ NULL, NULL,
+ sizeof (EMFormatHTMLQuote), 0,
+ (GInstanceInitFunc) efhq_init
+ };
+
+ efhq_parent = g_type_class_ref (em_format_html_get_type ());
+ type = g_type_register_static (em_format_html_get_type (), "EMFormatHTMLQuote", &info, 0);
+ }
+
+ return type;
+}
+
+EMFormatHTMLQuote *
+em_format_html_quote_new (void)
+{
+ return (EMFormatHTMLQuote *) g_object_new (em_format_html_quote_get_type (), NULL);
+}
+
+EMFormatHTMLQuote *
+em_format_html_quote_new_with_credits (const char *credits)
+{
+ EMFormatHTMLQuote *emfq;
+
+ emfq = (EMFormatHTMLQuote *) g_object_new (em_format_html_quote_get_type (), NULL);
+ emfq->priv->credits = g_strdup (credits);
+
+ return emfq;
+}
+
+static void
+efhq_format_clone (EMFormat *emf, CamelMedium *part, EMFormat *src)
+{
+ ((EMFormatClass *) efhq_parent)->format_clone (emf, part, src);
+}
+
+static void
+efhq_format_error (EMFormat *emf, CamelStream *stream, const char *txt)
+{
+ /* FIXME: should we even bother writign error text for quoting? probably not... */
+ ((EMFormatClass *) efhq_parent)->format_error (emf, stream, txt);
+}
+
+static void
+efhq_format_message (EMFormat *emf, CamelStream *stream, CamelMedium *part)
+{
+ EMFormatHTMLQuote *emfq = (EMFormatHTMLQuote *) emf;
+
+ camel_stream_printf (stream, "%s<!--+GtkHTML:<DATA class=\"ClueFlow\" key=\"orig\" value=\"1\">-->\n"
+ "<font color=\"#%06x\">\n"
+ "<blockquote type=cite>\n",
+ emfq->priv->credits ? emfq->priv->credits : "",
+ emfq->formathtml.citation_colour);
+
+ if (!((EMFormatHTML *)emf)->hide_headers)
+ em_format_html_format_headers((EMFormatHTML *)emf, stream, part);
+
+ em_format_part(emf, stream, (CamelMimePart *)part);
+
+ camel_stream_write_string (stream, "</blockquote></font><!--+GtkHTML:<DATA class=\"ClueFlow\" clear=\"orig\">-->");
+}
+
+static void
+efhq_format_source (EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ /* FIXME: should we just format_message? */
+ ((EMFormatClass *) efhq_parent)->format_source (emf, stream, part);
+}
+
+static void
+efhq_format_attachment (EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type, const EMFormatHandler *handle)
+{
+ ;
+}
+
+#include <camel/camel-medium.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-url.h>
+
+static void
+efhq_multipart_related(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+ CamelMimePart *body_part, *display_part = NULL;
+ CamelContentType *content_type;
+ const char *location, *start;
+ int i, nparts;
+ CamelURL *base_save = NULL;
+
+ if (!CAMEL_IS_MULTIPART(mp)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ /* sigh, so much for oo code reuse ... */
+ /* FIXME: put in a function */
+ nparts = camel_multipart_get_number(mp);
+ content_type = camel_mime_part_get_content_type(part);
+ start = header_content_type_param(content_type, "start");
+ if (start && strlen(start)>2) {
+ int len;
+ const char *cid;
+
+ /* strip <>'s */
+ len = strlen (start) - 2;
+ start++;
+
+ for (i=0; i<nparts; i++) {
+ body_part = camel_multipart_get_part(mp, i);
+ cid = camel_mime_part_get_content_id(body_part);
+
+ if (cid && !strncmp(cid, start, len) && strlen(cid) == len) {
+ display_part = body_part;
+ break;
+ }
+ }
+ } else {
+ display_part = camel_multipart_get_part(mp, 0);
+ }
+
+ if (display_part == NULL) {
+ em_format_part_as(emf, stream, part, "multipart/mixed");
+ return;
+ }
+
+ /* stack of present location and pending uri's */
+ location = camel_mime_part_get_content_location(part);
+ if (location) {
+ base_save = emf->base;
+ emf->base = camel_url_new(location, NULL);
+ }
+ em_format_push_level(emf);
+
+ em_format_part(emf, stream, display_part);
+ em_format_pull_level(emf);
+
+ if (location) {
+ camel_url_free(emf->base);
+ emf->base = base_save;
+ }
+}
+
+static const char *type_remove_table[] = {
+ "image/gif",
+ "image/jpeg",
+ "image/png",
+ "image/x-png",
+ "image/tiff",
+ "image/x-bmp",
+ "image/bmp",
+ "image/x-cmu-raster",
+ "image/x-portable-anymap",
+ "image/x-portable-bitmap",
+ "image/x-portable-graymap",
+ "image/x-portable-pixmap",
+ "image/x-xpixmap",
+ "message/external-body",
+ "multipart/appledouble",
+ "multipart/signed",
+
+ "image/jpg",
+ "image/pjpeg",
+};
+
+static EMFormatHandler type_builtin_table[] = {
+ { "multipart/related", (EMFormatFunc)efhq_multipart_related },
+};
+
+static void
+efhq_builtin_init (EMFormatHTMLQuoteClass *efhc)
+{
+ int i;
+
+ for (i = 0; i < sizeof (type_remove_table) / sizeof (type_remove_table[0]); i++)
+ em_format_class_remove_handler ((EMFormatClass *) efhc, type_remove_table[i]);
+
+ for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+ em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
diff --git a/mail/em-format-html-quote.h b/mail/em-format-html-quote.h
new file mode 100644
index 0000000000..1422b04093
--- /dev/null
+++ b/mail/em-format-html-quote.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _EM_FORMAT_HTML_QUOTE_H
+#define _EM_FORMAT_HTML_QUOTE_H
+
+#include "em-format-html.h"
+
+typedef struct _EMFormatHTMLQuote EMFormatHTMLQuote;
+typedef struct _EMFormatHTMLQuoteClass EMFormatHTMLQuoteClass;
+
+struct _EMFormatHTMLQuote {
+ EMFormatHTML formathtml;
+
+ struct _EMFormatHTMLQuotePrivate *priv;
+};
+
+struct _EMFormatHTMLQuoteClass {
+ EMFormatHTMLClass formathtml_class;
+
+};
+
+GType em_format_html_quote_get_type (void);
+
+EMFormatHTMLQuote *em_format_html_quote_new (void);
+
+EMFormatHTMLQuote *em_format_html_quote_new_with_credits (const char *cedits);
+
+#endif /* !_EM_FORMAT_HTML_QUOTE_H */
diff --git a/mail/em-format-html.c b/mail/em-format-html.c
new file mode 100644
index 0000000000..9fa0210813
--- /dev/null
+++ b/mail/em-format-html.c
@@ -0,0 +1,1542 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <gal/util/e-iconv.h>
+#include <gal/util/e-util.h> /* for e_utf8_strftime, what about e_time_format_time? */
+#include "e-util/e-time-utils.h"
+
+#include <gtkhtml/gtkhtml.h>
+#include <gtkhtml/gtkhtml-embedded.h>
+#include <gtkhtml/gtkhtml-stream.h>
+#include <gtkhtml/htmlengine.h>
+
+#include <gconf/gconf-client.h>
+
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <libgnomevfs/gnome-vfs-mime-handlers.h>
+
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-filter-enriched.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-multipart-signed.h>
+#include <camel/camel-gpg-context.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-url.h>
+#include <camel/camel-stream-fs.h>
+#include <camel/camel-string-utils.h>
+#include <camel/camel-http-stream.h>
+#include <camel/camel-data-cache.h>
+#include <camel/camel-file-utils.h>
+
+#include <e-util/e-msgport.h>
+#include "mail-mt.h"
+
+#include "em-format-html.h"
+#include "em-html-stream.h"
+#include "em-utils.h"
+
+#define d(x)
+
+#define EFH_TABLE_OPEN "<table>"
+
+struct _EMFormatHTMLPrivate {
+ struct _CamelMedium *last_part; /* not reffed, DO NOT dereference */
+ volatile int format_id; /* format thread id */
+ guint format_timeout_id;
+ struct _format_msg *format_timeout_msg;
+
+ /* Table that re-maps text parts into a mutlipart/mixed */
+ GHashTable *text_inline_parts;
+
+ EDList pending_jobs;
+ GMutex *lock;
+};
+
+static void efh_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTML *efh);
+static gboolean efh_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTML *efh);
+static void efh_gtkhtml_destroy(GtkHTML *html, EMFormatHTML *efh);
+
+static void efh_format_clone(EMFormat *, CamelMedium *, EMFormat *);
+static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt);
+static void efh_format_message(EMFormat *, CamelStream *, CamelMedium *);
+static void efh_format_source(EMFormat *, CamelStream *, CamelMimePart *);
+static void efh_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *);
+static gboolean efh_busy(EMFormat *);
+
+static void efh_builtin_init(EMFormatHTMLClass *efhc);
+
+static void efh_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri);
+
+static EMFormatClass *efh_parent;
+static CamelDataCache *emfh_http_cache;
+
+#define EMFH_HTTP_CACHE_PATH "http"
+
+static void
+efh_init(GObject *o)
+{
+ EMFormatHTML *efh = (EMFormatHTML *)o;
+ GConfClient *gconf;
+
+ efh->priv = g_malloc0(sizeof(*efh->priv));
+
+ e_dlist_init(&efh->pending_object_list);
+ e_dlist_init(&efh->priv->pending_jobs);
+ efh->priv->lock = g_mutex_new();
+ efh->priv->format_id = -1;
+ efh->priv->text_inline_parts = g_hash_table_new(NULL, NULL);
+
+ efh->html = (GtkHTML *)gtk_html_new();
+ g_object_ref(efh->html);
+ gtk_object_sink((GtkObject *)efh->html);
+
+ gtk_html_set_default_content_type(efh->html, "text/html; charset=utf-8");
+ gtk_html_set_editable(efh->html, FALSE);
+
+ g_signal_connect(efh->html, "destroy", G_CALLBACK(efh_gtkhtml_destroy), efh);
+ g_signal_connect(efh->html, "url_requested", G_CALLBACK(efh_url_requested), efh);
+ g_signal_connect(efh->html, "object_requested", G_CALLBACK(efh_object_requested), efh);
+
+ efh->header_colour = 0xeeeeee;
+ efh->text_colour = 0;
+ efh->text_html_flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
+ | CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
+
+ /* TODO: should this be here? wont track changes ... */
+ gconf = gconf_client_get_default();
+ efh->xmailer_mask = gconf_client_get_int(gconf, "/apps/evolution/mail/display/xmailer_mask", NULL);
+ g_object_unref(gconf);
+}
+
+static void
+efh_gtkhtml_destroy(GtkHTML *html, EMFormatHTML *efh)
+{
+ if (efh->priv->format_timeout_id != 0) {
+ g_source_remove(efh->priv->format_timeout_id);
+ efh->priv->format_timeout_id = 0;
+ mail_msg_free(efh->priv->format_timeout_msg);
+ efh->priv->format_timeout_msg = NULL;
+ }
+
+ /* This probably works ... */
+ if (efh->priv->format_id != -1)
+ mail_msg_cancel(efh->priv->format_id);
+
+ if (efh->html) {
+ g_object_unref(efh->html);
+ efh->html = NULL;
+ }
+}
+
+static void
+efh_free_inline_parts(void *key, void *data, void *user)
+{
+ camel_object_unref(data);
+}
+
+static void
+efh_finalise(GObject *o)
+{
+ EMFormatHTML *efh = (EMFormatHTML *)o;
+
+ /* FIXME: check for leaked stuff */
+
+ em_format_html_clear_pobject(efh);
+
+ efh_gtkhtml_destroy(efh->html, efh);
+
+ g_hash_table_foreach(efh->priv->text_inline_parts, efh_free_inline_parts, NULL);
+ g_hash_table_destroy(efh->priv->text_inline_parts);
+
+ g_free(efh->priv);
+
+ ((GObjectClass *)efh_parent)->finalize(o);
+}
+
+static void
+efh_base_init(EMFormatHTMLClass *efhklass)
+{
+ efh_builtin_init(efhklass);
+}
+
+static void
+efh_class_init(GObjectClass *klass)
+{
+ ((EMFormatClass *)klass)->format_clone = efh_format_clone;
+ ((EMFormatClass *)klass)->format_error = efh_format_error;
+ ((EMFormatClass *)klass)->format_message = efh_format_message;
+ ((EMFormatClass *)klass)->format_source = efh_format_source;
+ ((EMFormatClass *)klass)->format_attachment = efh_format_attachment;
+ ((EMFormatClass *)klass)->busy = efh_busy;
+
+ klass->finalize = efh_finalise;
+}
+
+GType
+em_format_html_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFormatHTMLClass),
+ (GBaseInitFunc)efh_base_init, NULL,
+ (GClassInitFunc)efh_class_init,
+ NULL, NULL,
+ sizeof(EMFormatHTML), 0,
+ (GInstanceInitFunc)efh_init
+ };
+ extern char *evolution_dir;
+ char *path;
+
+ efh_parent = g_type_class_ref(em_format_get_type());
+ type = g_type_register_static(em_format_get_type(), "EMFormatHTML", &info, 0);
+
+ /* cache expiry - 2 hour access, 1 day max */
+ path = alloca(strlen(evolution_dir)+16);
+ sprintf(path, "%s/cache", evolution_dir);
+ emfh_http_cache = camel_data_cache_new(path, 0, NULL);
+ camel_data_cache_set_expire_age(emfh_http_cache, 24*60*60);
+ camel_data_cache_set_expire_access(emfh_http_cache, 2*60*60);
+ }
+
+ return type;
+}
+
+EMFormatHTML *em_format_html_new(void)
+{
+ EMFormatHTML *efh;
+
+ efh = g_object_new(em_format_html_get_type(), 0);
+
+ return efh;
+}
+
+/* force loading of http images */
+void em_format_html_load_http(EMFormatHTML *emfh)
+{
+ if (emfh->load_http)
+ return;
+
+ /* This will remain set while we're still rendering the same message, then it wont be */
+ emfh->load_http_now = TRUE;
+ d(printf("redrawing with images forced on\n"));
+ em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh);
+}
+
+void
+em_format_html_set_load_http(EMFormatHTML *emfh, int state)
+{
+ if (emfh->load_http ^ state) {
+ emfh->load_http = state;
+ em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh);
+ }
+}
+
+void
+em_format_html_set_mark_citations(EMFormatHTML *emfh, int state, guint32 citation_colour)
+{
+ if (emfh->mark_citations ^ state || emfh->citation_colour != citation_colour) {
+ emfh->mark_citations = state;
+ emfh->citation_colour = citation_colour;
+ em_format_format_clone((EMFormat *)emfh, emfh->format.message, (EMFormat *)emfh);
+ }
+}
+
+CamelMimePart *
+em_format_html_file_part(EMFormatHTML *efh, const char *mime_type, const char *path, const char *name)
+{
+ CamelMimePart *part;
+ CamelStream *stream;
+ CamelDataWrapper *dw;
+ char *filename;
+
+ filename = g_build_filename(path, name, NULL);
+ stream = camel_stream_fs_new_with_name(filename, O_RDONLY, 0);
+ g_free(filename);
+ if (stream == NULL)
+ return NULL;
+
+ part = camel_mime_part_new();
+ dw = camel_data_wrapper_new();
+ camel_data_wrapper_construct_from_stream(dw, stream);
+ camel_object_unref(stream);
+ if (mime_type)
+ camel_data_wrapper_set_mime_type(dw, mime_type);
+ part = camel_mime_part_new();
+ camel_medium_set_content_object((CamelMedium *)part, dw);
+ camel_object_unref(dw);
+ camel_mime_part_set_filename(part, name);
+
+ return part;
+}
+
+/* all this api is a pain in the bum ... */
+
+/* should it have a user-data field? */
+const char *
+em_format_html_add_pobject(EMFormatHTML *efh, const char *classid, EMFormatHTMLPObjectFunc func, CamelMimePart *part)
+{
+ EMFormatHTMLPObject *pobj;
+
+ pobj = g_malloc(sizeof(*pobj));
+ if (classid) {
+ pobj->classid = g_strdup(classid);
+ } else {
+ static unsigned int uriid = 0;
+
+ pobj->classid = g_strdup_printf("e-object:///%u", uriid++);
+ }
+
+ pobj->format = efh;
+ pobj->func = func;
+ pobj->part = part;
+
+ e_dlist_addtail(&efh->pending_object_list, (EDListNode *)pobj);
+
+ return pobj->classid;
+}
+
+EMFormatHTMLPObject *
+em_format_html_find_pobject(EMFormatHTML *emf, const char *classid)
+{
+ EMFormatHTMLPObject *pw;
+
+ pw = (EMFormatHTMLPObject *)emf->pending_object_list.head;
+ while (pw->next) {
+ if (!strcmp(pw->classid, classid))
+ return pw;
+ pw = pw->next;
+ }
+
+ return NULL;
+}
+
+EMFormatHTMLPObject *
+em_format_html_find_pobject_func(EMFormatHTML *emf, CamelMimePart *part, EMFormatHTMLPObjectFunc func)
+{
+ EMFormatHTMLPObject *pw;
+
+ pw = (EMFormatHTMLPObject *)emf->pending_object_list.head;
+ while (pw->next) {
+ if (pw->func == func && pw->part == part)
+ return pw;
+ pw = pw->next;
+ }
+
+ return NULL;
+}
+
+void
+em_format_html_remove_pobject(EMFormatHTML *emf, EMFormatHTMLPObject *pobject)
+{
+ e_dlist_remove((EDListNode *)pobject);
+ g_free(pobject->classid);
+ g_free(pobject);
+}
+
+void
+em_format_html_clear_pobject(EMFormatHTML *emf)
+{
+ d(printf("clearing pending objects\n"));
+ while (!e_dlist_empty(&emf->pending_object_list))
+ em_format_html_remove_pobject(emf, (EMFormatHTMLPObject *)emf->pending_object_list.head);
+}
+
+struct _EMFormatHTMLJob *
+em_format_html_job_new(EMFormatHTML *emfh, void (*callback)(struct _EMFormatHTMLJob *job, int cancelled), void *data)
+{
+ struct _EMFormatHTMLJob *job = g_malloc0(sizeof(*job));
+
+ job->format = emfh;
+ job->puri_level = ((EMFormat *)emfh)->pending_uri_level;
+ job->callback = callback;
+ job->u.data = data;
+ if (((EMFormat *)emfh)->base)
+ job->base = camel_url_copy(((EMFormat *)emfh)->base);
+
+ return job;
+}
+
+void
+em_format_html_job_queue(EMFormatHTML *emfh, struct _EMFormatHTMLJob *job)
+{
+ g_mutex_lock(emfh->priv->lock);
+ e_dlist_addtail(&emfh->priv->pending_jobs, (EDListNode *)job);
+ g_mutex_unlock(emfh->priv->lock);
+}
+
+/* ********************************************************************** */
+
+static void emfh_getpuri(struct _EMFormatHTMLJob *job, int cancelled)
+{
+ d(printf(" running getpuri task\n"));
+ if (!cancelled)
+ job->u.puri->func((EMFormat *)job->format, job->stream, job->u.puri);
+}
+
+static void emfh_gethttp(struct _EMFormatHTMLJob *job, int cancelled)
+{
+ CamelStream *cistream = NULL, *costream = NULL, *instream = NULL;
+ CamelURL *url;
+ ssize_t n, total = 0;
+ char buffer[1500];
+
+ if (cancelled
+ || (url = camel_url_new(job->u.uri, NULL)) == NULL)
+ goto badurl;
+
+ d(printf(" running load uri task: %s\n", job->u.uri));
+
+ if (emfh_http_cache)
+ instream = cistream = camel_data_cache_get(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+
+ if (instream == NULL) {
+ char *proxy;
+
+ if (!job->format->load_http_now) {
+ /* TODO: Ideally we would put the http requests into another queue and only send them out
+ if the user selects 'load images', when they do. The problem is how to maintain this
+ state with multiple renderings, and how to adjust the thread dispatch/setup routine to handle it */
+ /* FIXME: Need to handle 'load if sender in addressbook' case too */
+ camel_url_free(url);
+ goto done;
+ }
+
+ instream = camel_http_stream_new(CAMEL_HTTP_METHOD_GET, ((EMFormat *)job->format)->session, url);
+ proxy = em_utils_get_proxy_uri();
+ camel_http_stream_set_proxy((CamelHttpStream *)instream, proxy);
+ g_free(proxy);
+ camel_operation_start(NULL, _("Retrieving `%s'"), job->u.uri);
+ } else
+ camel_operation_start_transient(NULL, _("Retrieving `%s'"), job->u.uri);
+
+ camel_url_free(url);
+
+ if (instream == NULL)
+ goto done;
+
+ if (emfh_http_cache != NULL && cistream == NULL)
+ costream = camel_data_cache_add(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+
+ do {
+ /* FIXME: progress reporting in percentage, can we get the length always? do we care? */
+ n = camel_stream_read(instream, buffer, 1500);
+ if (n > 0) {
+ camel_operation_progress_count(NULL, total);
+ total += n;
+ d(printf(" read %d bytes\n", n));
+ if (costream && camel_stream_write(costream, buffer, n) == -1) {
+ camel_data_cache_remove(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+ camel_object_unref(costream);
+ costream = NULL;
+ }
+
+ camel_stream_write(job->stream, buffer, n);
+ } else if (n < 0 && costream) {
+ camel_data_cache_remove(emfh_http_cache, EMFH_HTTP_CACHE_PATH, job->u.uri, NULL);
+ camel_object_unref(costream);
+ costream = NULL;
+ }
+ } while (n>0);
+
+ /* indicates success */
+ if (n == 0)
+ camel_stream_close(job->stream);
+
+ if (costream)
+ camel_object_unref(costream);
+
+ camel_object_unref(instream);
+done:
+ camel_operation_end(NULL);
+badurl:
+ g_free(job->u.uri);
+}
+
+/* ********************************************************************** */
+
+static void
+efh_url_requested(GtkHTML *html, const char *url, GtkHTMLStream *handle, EMFormatHTML *efh)
+{
+ EMFormatPURI *puri;
+ struct _EMFormatHTMLJob *job = NULL;
+
+ d(printf("url requested, html = %p, url '%s'\n", html, url));
+
+ puri = em_format_find_visible_puri((EMFormat *)efh, url);
+ if (puri) {
+ puri->use_count++;
+
+ d(printf(" adding puri job\n"));
+ job = em_format_html_job_new(efh, emfh_getpuri, puri);
+ } else if (g_ascii_strncasecmp(url, "http:", 5) == 0 || g_ascii_strncasecmp(url, "https:", 6) == 0) {
+ d(printf(" adding job, get %s\n", url));
+ job = em_format_html_job_new(efh, emfh_gethttp, g_strdup(url));
+ } else {
+ d(printf("HTML Includes reference to unknown uri '%s'\n", url));
+ gtk_html_stream_close(handle, GTK_HTML_STREAM_ERROR);
+ }
+
+ if (job) {
+ job->stream = em_html_stream_new(html, handle);
+ em_format_html_job_queue(efh, job);
+ }
+}
+
+static gboolean
+efh_object_requested(GtkHTML *html, GtkHTMLEmbedded *eb, EMFormatHTML *efh)
+{
+ EMFormatHTMLPObject *pobject;
+ int res = FALSE;
+
+ if (eb->classid == NULL)
+ return FALSE;
+
+ pobject = em_format_html_find_pobject(efh, eb->classid);
+ if (pobject) {
+ /* This stops recursion of the part */
+ e_dlist_remove((EDListNode *)pobject);
+ res = pobject->func(efh, eb, pobject);
+ e_dlist_addhead(&efh->pending_object_list, (EDListNode *)pobject);
+ } else {
+ printf("HTML Includes reference to unknown object '%s'\n", eb->classid);
+ }
+
+ return res;
+}
+
+/* ********************************************************************** */
+#include "em-inline-filter.h"
+#include <camel/camel-stream-null.h>
+
+static void
+efh_text_plain(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilter *html_filter;
+ CamelMultipart *mp;
+ CamelContentType *type;
+ const char *format;
+ guint32 rgb = 0x737373, flags;
+ int i, count;
+
+ flags = efh->text_html_flags;
+
+ /* Check for RFC 2646 flowed text. */
+ type = camel_mime_part_get_content_type(part);
+ if (header_content_type_is(type, "text", "plain")
+ && (format = header_content_type_param(type, "format"))
+ && !g_ascii_strcasecmp(format, "flowed"))
+ flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
+
+ /* This scans the text part for inline-encoded data, creates
+ a multipart of all the parts inside it. */
+
+ /* FIXME: We should discard this multipart if it only contains
+ the original text, but it makes this hash lookup more complex */
+
+ /* TODO: We could probably put this in the superclass, since
+ no knowledge of html is required - but this messes with
+ filters a bit. Perhaps the superclass should just deal with
+ html anyway and be done with it ... */
+
+ mp = g_hash_table_lookup(efh->priv->text_inline_parts, part);
+ if (mp == NULL) {
+ EMInlineFilter *inline_filter;
+ CamelStream *null;
+
+ null = camel_stream_null_new();
+ filtered_stream = camel_stream_filter_new_with_stream(null);
+ camel_object_unref(null);
+ inline_filter = em_inline_filter_new(camel_mime_part_get_encoding(part));
+ camel_stream_filter_add(filtered_stream, (CamelMimeFilter *)inline_filter);
+ camel_data_wrapper_write_to_stream(camel_medium_get_content_object((CamelMedium *)part), (CamelStream *)filtered_stream);
+ camel_stream_close((CamelStream *)filtered_stream);
+ camel_object_unref(filtered_stream);
+ mp = em_inline_filter_get_multipart(inline_filter);
+ g_hash_table_insert(efh->priv->text_inline_parts, part, mp);
+ camel_object_unref(inline_filter);
+ }
+
+ filtered_stream = camel_stream_filter_new_with_stream(stream);
+ html_filter = camel_mime_filter_tohtml_new(flags, rgb);
+ camel_stream_filter_add(filtered_stream, html_filter);
+ camel_object_unref(html_filter);
+
+ /* We handle our made-up multipart here, so we don't recursively call ourselves */
+
+ count = camel_multipart_get_number(mp);
+ for (i=0;i<count;i++) {
+ CamelMimePart *newpart = camel_multipart_get_part(mp, i);
+
+ type = camel_mime_part_get_content_type(newpart);
+ if (header_content_type_is(type, "text", "plain")) {
+ camel_stream_write_string(stream, "<table><tr><td><tt>\n");
+ em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, camel_medium_get_content_object((CamelMedium *)newpart));
+ camel_stream_flush((CamelStream *)filtered_stream);
+ camel_stream_write_string(stream, "</tt></td></tr></table>\n");
+ } else {
+ em_format_part((EMFormat *)efh, stream, newpart);
+ }
+ }
+
+ camel_object_unref(filtered_stream);
+}
+
+static void
+efh_text_enriched(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilter *enriched;
+ CamelDataWrapper *dw;
+ guint32 flags = 0;
+
+ dw = camel_medium_get_content_object((CamelMedium *)part);
+
+ if (!strcmp(info->mime_type, "text/richtext")) {
+ flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
+ camel_stream_write_string( stream, "\n<!-- text/richtext -->\n");
+ } else {
+ camel_stream_write_string( stream, "\n<!-- text/enriched -->\n");
+ }
+
+ enriched = camel_mime_filter_enriched_new(flags);
+ filtered_stream = camel_stream_filter_new_with_stream (stream);
+ camel_stream_filter_add(filtered_stream, enriched);
+ camel_object_unref(enriched);
+
+ camel_stream_write_string(stream, EFH_TABLE_OPEN "<tr><td><tt>\n");
+ em_format_format_text((EMFormat *)efh, (CamelStream *)filtered_stream, dw);
+
+ camel_stream_write_string(stream, "</tt></td></tr></table>\n");
+ camel_object_unref(filtered_stream);
+}
+
+static void
+efh_write_text_html(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+ em_format_format_text(emf, stream, camel_medium_get_content_object((CamelMedium *)puri->part));
+}
+
+static void
+efh_text_html(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ const char *location, *base;
+ EMFormatPURI *puri;
+
+ camel_stream_write_string(stream, "\n<!-- text/html -->\n");
+
+ if ((base = camel_medium_get_header((CamelMedium *)part, "Content-Base"))) {
+ char *base_url;
+ size_t len;
+
+ len = strlen(base);
+ if (*base == '"' && *(base + len - 1) == '"') {
+ len -= 2;
+ base_url = alloca(len + 1);
+ memcpy(base_url, base + 1, len);
+ base_url[len] = '\0';
+ base = base_url;
+ }
+
+ /* FIXME: set base needs to go on the gtkhtml stream? */
+ gtk_html_set_base(efh->html, base);
+ }
+
+ puri = em_format_add_puri((EMFormat *)efh, sizeof(EMFormatPURI), NULL, part, efh_write_text_html);
+ location = puri->uri?puri->uri:puri->cid;
+ d(printf("adding iframe, location %s\n", location));
+ camel_stream_printf(stream,
+ "<iframe src=\"%s\" frameborder=0 scrolling=no>could not get %s</iframe>",
+ location, location);
+}
+
+/* This is a lot of code for something useless ... */
+static void
+efh_message_external(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ CamelContentType *type;
+ const char *access_type;
+ char *url = NULL, *desc = NULL;
+
+ /* needs to be cleaner */
+ type = camel_mime_part_get_content_type(part);
+ access_type = header_content_type_param(type, "access-type");
+ if (!access_type) {
+ camel_stream_printf(stream, _("Malformed external-body part."));
+ return;
+ }
+
+ if (!g_ascii_strcasecmp(access_type, "ftp") ||
+ !g_ascii_strcasecmp(access_type, "anon-ftp")) {
+ const char *name, *site, *dir, *mode;
+ char *path;
+ char ftype[16];
+
+ name = header_content_type_param(type, "name");
+ site = header_content_type_param(type, "site");
+ dir = header_content_type_param(type, "directory");
+ mode = header_content_type_param(type, "mode");
+ if (name == NULL || site == NULL)
+ goto fail;
+
+ /* Generate the path. */
+ if (dir)
+ path = g_strdup_printf("/%s/%s", *dir=='/'?dir+1:dir, name);
+ else
+ path = g_strdup_printf("/%s", *name=='/'?name+1:name);
+
+ if (mode && &mode)
+ sprintf(ftype, ";type=%c", *mode);
+ else
+ ftype[0] = 0;
+
+ url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype);
+ g_free (path);
+ desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url);
+ } else if (!g_ascii_strcasecmp (access_type, "local-file")) {
+ const char *name, *site;
+
+ name = header_content_type_param (type, "name");
+ site = header_content_type_param (type, "site");
+ if (name == NULL)
+ goto fail;
+
+ url = g_strdup_printf ("file:///%s", *name == '/' ? name+1:name);
+ if (site)
+ desc = g_strdup_printf(_("Pointer to local file (%s) valid at site \"%s\""), name, site);
+ else
+ desc = g_strdup_printf(_("Pointer to local file (%s)"), name);
+ } else if (!g_ascii_strcasecmp (access_type, "URL")) {
+ const char *urlparam;
+ char *s, *d;
+
+ /* RFC 2017 */
+
+ urlparam = header_content_type_param (type, "url");
+ if (urlparam == NULL)
+ goto fail;
+
+ /* For obscure MIMEy reasons, the URL may be split into words */
+ url = g_strdup (urlparam);
+ s = d = url;
+ while (*s) {
+ /* FIXME: use camel_isspace */
+ if (!isspace ((unsigned char)*s))
+ *d++ = *s;
+ s++;
+ }
+ *d = 0;
+ desc = g_strdup_printf (_("Pointer to remote data (%s)"), url);
+ } else
+ goto fail;
+
+ camel_stream_printf(stream, "<a href=\"%s\">%s</a>", url, desc);
+ g_free(url);
+ g_free(desc);
+
+ return;
+
+fail:
+ camel_stream_printf(stream, _("Pointer to unknown external data (\"%s\" type)"), access_type);
+}
+
+static void
+emfh_write_related(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+ em_format_format_content(emf, stream, puri->part);
+ camel_stream_close(stream);
+}
+
+static void
+emfh_multipart_related_check(struct _EMFormatHTMLJob *job, int cancelled)
+{
+ struct _EMFormatPURITree *ptree;
+ EMFormatPURI *puri, *purin;
+
+ if (cancelled)
+ return;
+
+ d(printf(" running multipart/related check task\n"));
+
+ ptree = job->puri_level;
+ puri = (EMFormatPURI *)ptree->uri_list.head;
+ purin = puri->next;
+ while (purin) {
+ if (puri->use_count == 0) {
+ d(printf("part '%s' '%s' used '%d'\n", puri->uri?puri->uri:"", puri->cid, puri->use_count));
+ if (puri->func == emfh_write_related)
+ em_format_part((EMFormat *)job->format, (CamelStream *)job->stream, puri->part);
+ /* else it was probably added by a previous format this loop */
+ }
+ puri = purin;
+ purin = purin->next;
+ }
+}
+
+/* RFC 2387 */
+static void
+efh_multipart_related(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+ CamelMimePart *body_part, *display_part = NULL;
+ CamelContentType *content_type;
+ const char *location, *start;
+ int i, nparts;
+ CamelURL *base_save = NULL;
+ EMFormatPURI *puri;
+ struct _EMFormatHTMLJob *job;
+
+ if (!CAMEL_IS_MULTIPART(mp)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ nparts = camel_multipart_get_number(mp);
+ content_type = camel_mime_part_get_content_type(part);
+ start = header_content_type_param(content_type, "start");
+ if (start && strlen(start)>2) {
+ int len;
+ const char *cid;
+
+ /* strip <>'s */
+ len = strlen (start) - 2;
+ start++;
+
+ for (i=0; i<nparts; i++) {
+ body_part = camel_multipart_get_part(mp, i);
+ cid = camel_mime_part_get_content_id(body_part);
+
+ if (cid && !strncmp(cid, start, len) && strlen(cid) == len) {
+ display_part = body_part;
+ break;
+ }
+ }
+ } else {
+ display_part = camel_multipart_get_part(mp, 0);
+ }
+
+ if (display_part == NULL) {
+ em_format_part_as(emf, stream, part, "multipart/mixed");
+ return;
+ }
+
+ /* stack of present location and pending uri's */
+ location = camel_mime_part_get_content_location(part);
+ if (location) {
+ d(printf("setting content location %s\n", location));
+ base_save = emf->base;
+ emf->base = camel_url_new(location, NULL);
+ }
+ em_format_push_level(emf);
+
+ /* queue up the parts for possible inclusion */
+ for (i = 0; i < nparts; i++) {
+ body_part = camel_multipart_get_part(mp, i);
+ if (body_part != display_part) {
+ puri = em_format_add_puri(emf, sizeof(EMFormatPURI), NULL, body_part, emfh_write_related);
+ d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid));
+ }
+ }
+
+ em_format_part(emf, stream, display_part);
+ camel_stream_flush(stream);
+
+ /* queue a job to check for un-referenced parts to add as attachments */
+ job = em_format_html_job_new((EMFormatHTML *)emf, emfh_multipart_related_check, NULL);
+ job->stream = stream;
+ camel_object_ref(stream);
+ em_format_html_job_queue((EMFormatHTML *)emf, job);
+
+ em_format_pull_level(emf);
+
+ if (location) {
+ camel_url_free(emf->base);
+ emf->base = base_save;
+ }
+}
+
+static const struct {
+ const char *icon;
+ const char *text;
+} signed_table[2] = {
+ { "pgp-signature-bad.png", N_("This message is digitally signed but can not be proven to be authentic.") },
+ { "pgp-signature-ok.png", N_("This message is digitally signed and has been found to be authentic.") }
+};
+
+void
+em_format_html_multipart_signed_sign(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ CamelMimePart *spart;
+ CamelMultipartSigned *mps;
+ CamelCipherValidity *valid = NULL;
+ CamelException ex;
+ const char *message = NULL;
+ int good = 0;
+ CamelCipherContext *cipher;
+ char *classid;
+ EMFormatPURI *iconpuri;
+ CamelMimePart *iconpart;
+ static int iconid;
+
+ mps = (CamelMultipartSigned *)camel_medium_get_content_object((CamelMedium *)part);
+ spart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_SIGNATURE);
+ camel_exception_init(&ex);
+ if (spart == NULL) {
+ message = _("No signature present");
+ } else if (emf->session == NULL) {
+ message = _("Session not initialised");
+ } else if ((cipher = camel_gpg_context_new(emf->session)) == NULL) {
+ message = _("Could not create signature verfication context");
+ } else {
+ valid = camel_multipart_signed_verify(mps, cipher, &ex);
+ camel_object_unref(cipher);
+ if (valid) {
+ good = camel_cipher_validity_get_valid(valid)?1:0;
+ message = camel_cipher_validity_get_description(valid);
+ } else {
+ message = camel_exception_get_description(&ex);
+ }
+ }
+
+ classid = g_strdup_printf("multipart-signed:///em-format-html/%p/icon/%d", part, iconid++);
+ iconpart = em_format_html_file_part((EMFormatHTML *)emf, "image/png", EVOLUTION_ICONSDIR, signed_table[good].icon);
+ if (iconpart) {
+ iconpuri = em_format_add_puri(emf, sizeof(*iconpuri), classid, iconpart, efh_write_image);
+ camel_object_unref(iconpart);
+ }
+
+ camel_stream_printf(stream, "<table><tr valign=top>"
+ "<td><img src=\"%s\"></td>"
+ "<td>%s<br><br>",
+ classid,
+ _(signed_table[good].text));
+ g_free(classid);
+
+ if (message) {
+ char *tmp = camel_text_to_html(message, ((EMFormatHTML *)emf)->text_html_flags, 0);
+
+ camel_stream_printf(stream, "<font size=-1%s>%s</font>", good?"":" color=red", tmp);
+ g_free(tmp);
+ }
+
+ camel_stream_write_string(stream, "</td></tr></table>");
+
+ camel_exception_clear(&ex);
+ camel_cipher_validity_free(valid);
+}
+
+static void
+efh_multipart_signed(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMimePart *cpart;
+ CamelMultipartSigned *mps;
+
+ mps = (CamelMultipartSigned *)camel_medium_get_content_object((CamelMedium *)part);
+ if (!CAMEL_IS_MULTIPART_SIGNED(mps)
+ || (cpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
+ em_format_format_error(emf, stream, _("Could not parse MIME message. Displaying as source."));
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ em_format_part(emf, stream, cpart);
+ em_format_html_multipart_signed_sign(emf, stream, part);
+}
+
+static void
+efh_write_image(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+ CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)puri->part);
+
+ d(printf("writing image '%s'\n", puri->uri?puri->uri:puri->cid));
+ camel_data_wrapper_decode_to_stream(dw, stream);
+ camel_stream_close(stream);
+}
+
+static void
+efh_image(EMFormatHTML *efh, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ EMFormatPURI *puri;
+ const char *location;
+
+ puri = em_format_add_puri((EMFormat *)efh, sizeof(EMFormatPURI), NULL, part, efh_write_image);
+ location = puri->uri?puri->uri:puri->cid;
+ d(printf("adding image '%s'\n", location));
+ camel_stream_printf(stream, "<img hspace=10 vspace=10 src=\"%s\">", location);
+}
+
+static EMFormatHandler type_builtin_table[] = {
+ { "image/gif", (EMFormatFunc)efh_image },
+ { "image/jpeg", (EMFormatFunc)efh_image },
+ { "image/png", (EMFormatFunc)efh_image },
+ { "image/x-png", (EMFormatFunc)efh_image },
+ { "image/tiff", (EMFormatFunc)efh_image },
+ { "image/x-bmp", (EMFormatFunc)efh_image },
+ { "image/bmp", (EMFormatFunc)efh_image },
+ { "image/svg", (EMFormatFunc)efh_image },
+ { "image/x-cmu-raster", (EMFormatFunc)efh_image },
+ { "image/x-ico", (EMFormatFunc)efh_image },
+ { "image/x-portable-anymap", (EMFormatFunc)efh_image },
+ { "image/x-portable-bitmap", (EMFormatFunc)efh_image },
+ { "image/x-portable-graymap", (EMFormatFunc)efh_image },
+ { "image/x-portable-pixmap", (EMFormatFunc)efh_image },
+ { "image/x-xpixmap", (EMFormatFunc)efh_image },
+ { "text/enriched", (EMFormatFunc)efh_text_enriched },
+ { "text/plain", (EMFormatFunc)efh_text_plain },
+ { "text/html", (EMFormatFunc)efh_text_html },
+ { "text/richtext", (EMFormatFunc)efh_text_enriched },
+ /*{ "text/*", (EMFormatFunc)efh_text_plain },*/
+ { "message/external-body", (EMFormatFunc)efh_message_external },
+ { "multipart/signed", (EMFormatFunc)efh_multipart_signed },
+ { "multipart/related", (EMFormatFunc)efh_multipart_related },
+
+ /* This is where one adds those busted, non-registered types,
+ that some idiot mailer writers out there decide to pull out
+ of their proverbials at random. */
+
+ { "image/jpg", (EMFormatFunc)efh_image },
+ { "image/pjpeg", (EMFormatFunc)efh_image },
+};
+
+static void
+efh_builtin_init(EMFormatHTMLClass *efhc)
+{
+ int i;
+
+ for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+ em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
+
+/* ********************************************************************** */
+
+/* Sigh, this is so we have a cancellable, async rendering thread */
+struct _format_msg {
+ struct _mail_msg msg;
+
+ EMFormatHTML *format;
+ EMFormat *format_source;
+ EMHTMLStream *estream;
+ CamelMedium *message;
+};
+
+static char *efh_format_desc(struct _mail_msg *mm, int done)
+{
+ return g_strdup(_("Formatting message"));
+}
+
+static void efh_format_do(struct _mail_msg *mm)
+{
+ struct _format_msg *m = (struct _format_msg *)mm;
+ struct _EMFormatHTMLJob *job;
+ struct _EMFormatPURITree *puri_level;
+ int cancelled = FALSE;
+ CamelURL *base;
+
+ if (m->format->html == NULL)
+ return;
+
+ camel_stream_printf((CamelStream *)m->estream,
+ "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n"
+ "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
+ "<body text=\"#%06x\"\n",
+ m->format->text_colour & 0xffffff);
+
+ /* <insert top-header stuff here> */
+
+ if (((EMFormat *)m->format)->mode == EM_FORMAT_SOURCE)
+ em_format_format_source((EMFormat *)m->format, (CamelStream *)m->estream, (CamelMimePart *)m->message);
+ else
+ em_format_format_message((EMFormat *)m->format, (CamelStream *)m->estream, m->message);
+ camel_stream_flush((CamelStream *)m->estream);
+
+ puri_level = ((EMFormat *)m->format)->pending_uri_level;
+ base = ((EMFormat *)m->format)->base;
+
+ /* now dispatch any added tasks ... */
+ g_mutex_lock(m->format->priv->lock);
+ while ((job = (struct _EMFormatHTMLJob *)e_dlist_remhead(&m->format->priv->pending_jobs))) {
+ g_mutex_unlock(m->format->priv->lock);
+
+ /* This is an implicit check to see if the gtkhtml has been destroyed */
+ if (!cancelled)
+ cancelled = m->format->html == NULL;
+
+ /* Now do an explicit check for user cancellation */
+ if (!cancelled)
+ cancelled = camel_operation_cancel_check(NULL);
+
+ /* call jobs even if cancelled, so they can clean up resources */
+ ((EMFormat *)m->format)->pending_uri_level = job->puri_level;
+ if (job->base)
+ ((EMFormat *)m->format)->base = job->base;
+ job->callback(job, cancelled);
+ ((EMFormat *)m->format)->base = base;
+
+ /* clean up the job */
+ camel_object_unref(job->stream);
+ if (job->base)
+ camel_url_free(job->base);
+ g_free(job);
+
+ /* incase anything got added above, force it through */
+ camel_stream_flush((CamelStream *)m->estream);
+
+ g_mutex_lock(m->format->priv->lock);
+ }
+ g_mutex_unlock(m->format->priv->lock);
+ d(printf("out of jobs, done\n"));
+
+ camel_stream_write_string((CamelStream *)m->estream, "</body>\n</html>\n");
+
+ camel_stream_close((CamelStream *)m->estream);
+ camel_object_unref(m->estream);
+ m->estream = NULL;
+
+ ((EMFormat *)m->format)->pending_uri_level = puri_level;
+}
+
+static void efh_format_done(struct _mail_msg *mm)
+{
+ struct _format_msg *m = (struct _format_msg *)mm;
+
+ d(printf("formatting finished\n"));
+
+ m->format->priv->format_id = -1;
+ g_signal_emit_by_name(m->format, "complete");
+}
+
+static void efh_format_free(struct _mail_msg *mm)
+{
+ struct _format_msg *m = (struct _format_msg *)mm;
+
+ d(printf("formatter freed\n"));
+ g_object_unref(m->format);
+ if (m->estream) {
+ camel_stream_close((CamelStream *)m->estream);
+ camel_object_unref(m->estream);
+ }
+ if (m->message)
+ camel_object_unref(m->message);
+ if (m->format_source)
+ g_object_unref(m->format_source);
+}
+
+static struct _mail_msg_op efh_format_op = {
+ efh_format_desc,
+ efh_format_do,
+ efh_format_done,
+ efh_format_free,
+};
+
+static gboolean
+efh_format_timeout(struct _format_msg *m)
+{
+ GtkHTMLStream *hstream;
+ EMFormatHTML *efh = m->format;
+ struct _EMFormatHTMLPrivate *p = efh->priv;
+
+ if (m->format->html == NULL) {
+ mail_msg_free(m);
+ return FALSE;
+ }
+
+ d(printf("timeout called ...\n"));
+ if (p->format_id != -1) {
+ d(printf(" still waiting for cancellation to take effect, waiting ...\n"));
+ return TRUE;
+ }
+
+ g_assert(e_dlist_empty(&p->pending_jobs));
+
+ d(printf(" ready to go, firing off format thread\n"));
+
+ /* call super-class to kick it off */
+ efh_parent->format_clone((EMFormat *)efh, m->message, m->format_source);
+ em_format_html_clear_pobject(m->format);
+
+ if (m->message == NULL) {
+ hstream = gtk_html_begin(efh->html);
+ gtk_html_stream_close(hstream, GTK_HTML_STREAM_OK);
+ mail_msg_free(m);
+ } else {
+ hstream = gtk_html_begin(efh->html);
+ m->estream = (EMHTMLStream *)em_html_stream_new(efh->html, hstream);
+
+ if (p->last_part == m->message) {
+ /* HACK: so we redraw in the same spot */
+ /* FIXME: It doesn't work! */
+ efh->html->engine->newPage = FALSE;
+ } else {
+ /* clear cache of inline-scanned text parts */
+ g_hash_table_foreach(p->text_inline_parts, efh_free_inline_parts, NULL);
+ g_hash_table_destroy(p->text_inline_parts);
+ p->text_inline_parts = g_hash_table_new(NULL, NULL);
+
+ p->last_part = m->message;
+ /* FIXME: Need to handle 'load if sender in addressbook' case too */
+ efh->load_http_now = efh->load_http;
+ }
+
+ efh->priv->format_id = m->msg.seq;
+ e_thread_put(mail_thread_new, (EMsg *)m);
+ }
+
+ efh->priv->format_timeout_id = 0;
+ efh->priv->format_timeout_msg = NULL;
+
+ return FALSE;
+}
+
+static void efh_format_clone(EMFormat *emf, CamelMedium *part, EMFormat *emfsource)
+{
+ EMFormatHTML *efh = (EMFormatHTML *)emf;
+ struct _format_msg *m;
+
+ /* How to sub-class ? Might need to adjust api ... */
+
+ if (efh->html == NULL)
+ return;
+
+ d(printf("efh_format called\n"));
+ if (efh->priv->format_timeout_id != 0) {
+ d(printf(" timeout for last still active, removing ...\n"));
+ g_source_remove(efh->priv->format_timeout_id);
+ efh->priv->format_timeout_id = 0;
+ mail_msg_free(efh->priv->format_timeout_msg);
+ efh->priv->format_timeout_msg = NULL;
+ }
+
+ m = mail_msg_new(&efh_format_op, NULL, sizeof(*m));
+ m->format = (EMFormatHTML *)emf;
+ g_object_ref(emf);
+ m->format_source = emfsource;
+ if (emfsource)
+ g_object_ref(emfsource);
+ m->message = part;
+ if (part)
+ camel_object_ref(part);
+
+ if (efh->priv->format_id == -1) {
+ d(printf(" idle, forcing format\n"));
+ efh_format_timeout(m);
+ } else {
+ d(printf(" still busy, cancelling and queuing wait\n"));
+ /* cancel and poll for completion */
+ mail_msg_cancel(efh->priv->format_id);
+ efh->priv->format_timeout_msg = m;
+ efh->priv->format_timeout_id = g_timeout_add(100, (GSourceFunc)efh_format_timeout, m);
+ }
+}
+
+static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt)
+{
+ char *html;
+
+ html = camel_text_to_html (txt, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL|CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+ camel_stream_printf(stream, "<em><font color=\"red\">%s</font></em><br>", html);
+ g_free(html);
+}
+
+static void
+efh_format_text_header(EMFormat *emf, CamelStream *stream, const char *label, const char *value, guint32 flags)
+{
+ char *mhtml = NULL;
+ const char *fmt, *html;
+
+ if (value == NULL)
+ return;
+
+ while (*value == ' ')
+ value++;
+
+ if (flags & EM_FORMAT_HTML_HEADER_HTML)
+ html = value;
+ else
+ html = mhtml = camel_text_to_html(value, ((EMFormatHTML *)emf)->text_html_flags, 0);
+
+ if (((EMFormatHTML *)emf)->simple_headers) {
+ fmt = "<b>%s</b>: %s<br>";
+ } else {
+ if (flags & EM_FORMAT_HTML_HEADER_NOCOLUMNS) {
+ if (flags & EM_FORMAT_HEADER_BOLD)
+ fmt = "<tr><td><b>%s:</b> %s</td></tr>";
+ else
+ fmt = "<tr><td>%s: %s</td></tr>";
+ } else {
+ if (flags & EM_FORMAT_HEADER_BOLD)
+ fmt = "<tr><th align=\"right\" valign=\"top\">%s:<b>&nbsp;</b></th><td>%s</td></tr>";
+ else
+ fmt = "<tr><td align=\"right\" valign=\"top\">%s:<b>&nbsp;</b></td><td>%s</td></tr>";
+ }
+ }
+
+ camel_stream_printf(stream, fmt, label, html);
+ g_free(mhtml);
+}
+
+static void
+efh_format_address(EMFormat *emf, CamelStream *stream, const CamelInternetAddress *cia, const char *name, guint32 flags)
+{
+ char *text;
+
+ if (cia == NULL || !camel_internet_address_get(cia, 0, NULL, NULL))
+ return;
+
+ text = camel_address_format((CamelAddress *)cia);
+ efh_format_text_header(emf, stream, name, text, flags | EM_FORMAT_HEADER_BOLD);
+ g_free(text);
+}
+
+static void
+efh_format_header(EMFormat *emf, CamelStream *stream, CamelMedium *part, const char *namein, guint32 flags, const char *charset)
+{
+#define msg ((CamelMimeMessage *)part)
+#define efh ((EMFormatHTML *)emf)
+ char *name;
+
+ name = alloca(strlen(namein)+1);
+ strcpy(name, namein);
+ camel_strdown(name);
+
+ if (!strcmp(name, "from"))
+ efh_format_address(emf, stream, camel_mime_message_get_from(msg), _("From"), flags);
+ else if (!strcmp(name, "reply-to"))
+ efh_format_address(emf, stream, camel_mime_message_get_reply_to(msg), _("Reply-To"), flags);
+ else if (!strcmp(name, "to"))
+ efh_format_address(emf, stream, camel_mime_message_get_recipients(msg, CAMEL_RECIPIENT_TYPE_TO), _("To"), flags);
+ else if (!strcmp(name, "cc"))
+ efh_format_address(emf, stream, camel_mime_message_get_recipients(msg, CAMEL_RECIPIENT_TYPE_CC), _("Cc"), flags);
+ else if (!strcmp(name, "bcc"))
+ efh_format_address(emf, stream, camel_mime_message_get_recipients(msg, CAMEL_RECIPIENT_TYPE_BCC), _("Bcc"), flags);
+ else {
+ const char *txt, *label;
+ char *value = NULL;
+
+ if (!strcmp(name, "subject")) {
+ txt = camel_mime_message_get_subject(msg);
+ label = _("Subject");
+ flags |= EM_FORMAT_HEADER_BOLD;
+ } else if (!strcmp(name, "x-evolution-mailer")) { /* pseudo-header */
+ txt = camel_medium_get_header(part, "x-mailer");
+ if (txt == NULL)
+ txt = camel_medium_get_header(part, "user-agent");
+ if (txt == NULL
+ || ((efh->xmailer_mask & EM_FORMAT_HTML_XMAILER_OTHER) == 0
+ && ((efh->xmailer_mask & EM_FORMAT_HTML_XMAILER_EVOLUTION) == 0
+ || strstr(txt, "Evolution") == NULL)))
+ return;
+
+ label = _("Mailer");
+ flags |= EM_FORMAT_HEADER_BOLD;
+ } else if (!strcmp(name, "date")) {
+ int msg_offset, local_tz;
+ time_t msg_date;
+ struct tm local;
+ const char *date;
+
+ date = camel_medium_get_header(part, "date");
+ if (date == NULL)
+ return;
+
+ /* Show the local timezone equivalent in brackets if the sender is remote */
+ msg_date = header_decode_date(date, &msg_offset);
+ e_localtime_with_offset(msg_date, &local, &local_tz);
+
+ /* Convert message offset to minutes (e.g. -0400 --> -240) */
+ msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100);
+ /* Turn into offset from localtime, not UTC */
+ msg_offset -= local_tz / 60;
+
+ if (msg_offset) {
+ char buf[32], *html;
+
+ msg_offset += (local.tm_hour * 60) + local.tm_min;
+ if (msg_offset >= (24 * 60) || msg_offset < 0) {
+ /* translators: strftime format for local time equivalent in Date header display, with day */
+ e_utf8_strftime(buf, sizeof(buf), _("<I> (%a, %R %Z)</I>"), &local);
+ } else {
+ /* translators: strftime format for local time equivalent in Date header display, without day */
+ e_utf8_strftime(buf, sizeof(buf), _("<I> (%R %Z)</I>"), &local);
+ }
+
+ html = camel_text_to_html(date, ((EMFormatHTML *)emf)->text_html_flags, 0);
+ txt = value = g_strdup_printf("%s %s", html, buf);
+ g_free(html);
+ flags |= EM_FORMAT_HTML_HEADER_HTML;
+ } else {
+ txt = date;
+ }
+
+ label = _("Date");
+ flags |= EM_FORMAT_HEADER_BOLD;
+ } else {
+ txt = camel_medium_get_header(part, name);
+ value = header_decode_string(txt, charset);
+ txt = value;
+ label = namein;
+ }
+
+ efh_format_text_header(emf, stream, label, txt, flags);
+ g_free(value);
+ }
+#undef msg
+#undef efh
+}
+
+void
+em_format_html_format_headers(EMFormatHTML *efh, CamelStream *stream, CamelMedium *part)
+{
+ EMFormatHeader *h;
+ const char *charset;
+ CamelContentType *ct;
+#define emf ((EMFormat *)efh)
+
+ ct = camel_mime_part_get_content_type((CamelMimePart *)part);
+ charset = header_content_type_param(ct, "charset");
+ charset = e_iconv_charset_name(charset);
+
+ if (!efh->simple_headers)
+ camel_stream_printf(stream,
+ "<table width=\"100%%\" cellpadding=5 cellspacing=0>"
+ "<tr><td>"
+ "<table width=\"100%%\" cellpaddding=1 cellspacing=0 bgcolor=\"#000000\">"
+ "<tr><td>"
+ "<table width=\"100%%\"cellpadding=0 cellspacing=0 bgcolor=\"#%06x\">"
+ "<tr><td>"
+ "<table><font color=\"#%06x\"",
+ efh->header_colour & 0xffffff,
+ efh->text_colour & 0xffffff);
+
+ /* dump selected headers */
+ h = (EMFormatHeader *)emf->header_list.head;
+ if (h->next == NULL || emf->mode == EM_FORMAT_ALLHEADERS) {
+ struct _header_raw *header;
+
+ header = ((CamelMimePart *)part)->headers;
+ while (header) {
+ efh_format_header(emf, stream, part, header->name, EM_FORMAT_HTML_HEADER_NOCOLUMNS, charset);
+ header = header->next;
+ }
+ } else {
+ while (h->next) {
+ efh_format_header(emf, stream, part, h->name, h->flags, charset);
+ h = h->next;
+ }
+ }
+
+ if (!efh->simple_headers)
+ camel_stream_printf(stream,
+ "</font></table>"
+ "</td></tr></table>"
+ "</td></tr></table>"
+ "</td></tr></table>");
+#undef emf
+}
+
+static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMedium *part)
+{
+#define efh ((EMFormatHTML *)emf)
+
+ if (!efh->hide_headers)
+ em_format_html_format_headers(efh, stream, part);
+
+ if (emf->message != part)
+ camel_stream_printf(stream, "<blockquote>");
+
+ em_format_part(emf, stream, (CamelMimePart *)part);
+
+ if (emf->message != part)
+ camel_stream_printf(stream, "</blockquote>");
+#undef efh
+}
+
+static void efh_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilter *html_filter;
+ CamelDataWrapper *dw = (CamelDataWrapper *)part;
+
+ filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
+ html_filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL
+ | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
+ | CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT, 0);
+ camel_stream_filter_add(filtered_stream, html_filter);
+ camel_object_unref(html_filter);
+
+ camel_stream_write_string((CamelStream *)stream, EFH_TABLE_OPEN "<tr><td><tt>");
+ em_format_format_text(emf, (CamelStream *)filtered_stream, dw);
+ camel_object_unref(filtered_stream);
+
+ camel_stream_write_string(stream, "</tt></td></tr></table>");
+}
+
+static void
+efh_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type, const EMFormatHandler *handle)
+{
+ char *text, *html;
+
+ /* we display all inlined attachments only */
+
+ /* this could probably be cleaned up ... */
+ camel_stream_write_string(stream,
+ "<table cellspacing=0 cellpadding=0><tr><td>"
+ "<table width=10 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td>"
+ "<td><table width=3 cellspacing=0 cellpadding=0>"
+ "<tr><td></td></tr></table></td><td><font size=-1>");
+
+ /* output some info about it */
+ text = em_format_describe_part(part, mime_type);
+ html = camel_text_to_html(text, ((EMFormatHTML *)emf)->text_html_flags & CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
+ camel_stream_write_string(stream, html);
+ g_free(html);
+ g_free(text);
+
+ camel_stream_write_string(stream, "</font></td></tr><tr></table>");
+
+ if (handle && em_format_is_inline(emf, part))
+ handle->handler(emf, stream, part, handle);
+}
+
+static gboolean
+efh_busy(EMFormat *emf)
+{
+ return (((EMFormatHTML *)emf)->priv->format_id != -1);
+}
diff --git a/mail/em-format-html.h b/mail/em-format-html.h
new file mode 100644
index 0000000000..c9b4705a96
--- /dev/null
+++ b/mail/em-format-html.h
@@ -0,0 +1,154 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+/*
+ Concrete class for formatting mails to html
+*/
+
+#ifndef _EM_FORMAT_HTML_H
+#define _EM_FORMAT_HTML_H
+
+#include "em-format.h"
+
+typedef struct _EMFormatHTML EMFormatHTML;
+typedef struct _EMFormatHTMLClass EMFormatHTMLClass;
+
+#if 0
+struct _EMFormatHTMLHandler {
+ EFrormatHandler base;
+};
+#endif
+
+struct _GtkHTMLEmbedded;
+struct _CamelMimePart;
+struct _CamelMedium;
+struct _CamelStream;
+
+/* A HTMLJob will be executed in another thread, in sequence,
+ It's job is to write to its stream, close it if successful,
+ then exit */
+
+typedef struct _EMFormatHTMLJob EMFormatHTMLJob;
+
+struct _EMFormatHTMLJob {
+ struct _EMFormatHTMLJob *next, *prev;
+
+ EMFormatHTML *format;
+ struct _CamelStream *stream;
+
+ /* We need to track the state of the visibility tree at
+ the point this uri was generated */
+ struct _EMFormatPURITree *puri_level;
+ struct _CamelURL *base;
+
+ void (*callback)(struct _EMFormatHTMLJob *job, int cancelled);
+ union {
+ char *uri;
+ struct _CamelMedium *msg;
+ EMFormatPURI *puri;
+ struct _EMFormatPURITree *puri_level;
+ void *data;
+ } u;
+};
+
+/* Pending object (classid: url) */
+typedef struct _EMFormatHTMLPObject EMFormatHTMLPObject;
+
+typedef gboolean (*EMFormatHTMLPObjectFunc)(EMFormatHTML *md, struct _GtkHTMLEmbedded *eb, EMFormatHTMLPObject *pobject);
+
+struct _EMFormatHTMLPObject {
+ struct _EMFormatHTMLPObject *next, *prev;
+
+ struct _EMFormatHTML *format;
+
+ char *classid;
+
+ EMFormatHTMLPObjectFunc func;
+ struct _CamelMimePart *part;
+};
+
+#define EM_FORMAT_HTML_HEADER_NOCOLUMNS (EM_FORMAT_HEADER_LAST)
+#define EM_FORMAT_HTML_HEADER_HTML (EM_FORMAT_HEADER_LAST<<1) /* header already in html format */
+#define EM_FORMAT_HTML_HEADER_LAST (EM_FORMAT_HEADER_LAST<<8)
+
+/* xmailer_mask bits */
+#define EM_FORMAT_HTML_XMAILER_EVOLUTION (1<<0)
+#define EM_FORMAT_HTML_XMAILER_OTHER (1<<1)
+#define EM_FORMAT_HTML_XMAILER_RUPERT (1<<2)
+
+struct _EMFormatHTML {
+ EMFormat format;
+
+ struct _EMFormatHTMLPrivate *priv;
+
+ struct _GtkHTML *html;
+
+ EDList pending_object_list;
+
+ GSList *headers;
+
+ guint32 text_html_flags; /* default flags for text to html conversion */
+ guint32 header_colour; /* header box colour */
+ guint32 text_colour;
+ guint32 citation_colour;
+ unsigned int xmailer_mask:4;
+ unsigned int load_http:1;
+ unsigned int load_http_now:1;
+ unsigned int mark_citations:1;
+ unsigned int simple_headers:1; /* simple header format, no box/table */
+ unsigned int hide_headers:1; /* no headers at all */
+};
+
+struct _EMFormatHTMLClass {
+ EMFormatClass format_class;
+};
+
+GType em_format_html_get_type(void);
+EMFormatHTML *em_format_html_new(void);
+
+void em_format_html_load_http(EMFormatHTML *emf);
+
+void em_format_html_set_load_http(EMFormatHTML *emf, int state);
+void em_format_html_set_mark_citations(EMFormatHTML *emf, int state, guint32 citation_colour);
+
+/* output headers */
+void em_format_html_format_headers(EMFormatHTML *efh, struct _CamelStream *stream, struct _CamelMedium *part);
+
+/* retrieves a pseudo-part icon wrapper for a file */
+struct _CamelMimePart *em_format_html_file_part(EMFormatHTML *efh, const char *mime_type, const char *path, const char *name);
+
+/* for implementers */
+const char *em_format_html_add_pobject(EMFormatHTML *efh, const char *classid, EMFormatHTMLPObjectFunc func, struct _CamelMimePart *part);
+EMFormatHTMLPObject * em_format_html_find_pobject(EMFormatHTML *emf, const char *classid);
+EMFormatHTMLPObject *em_format_html_find_pobject_func(EMFormatHTML *emf, struct _CamelMimePart *part, EMFormatHTMLPObjectFunc func);
+void em_format_html_remove_pobject(EMFormatHTML *emf, EMFormatHTMLPObject *pobject);
+void em_format_html_clear_pobject(EMFormatHTML *emf);
+
+EMFormatHTMLJob *em_format_html_job_new(EMFormatHTML *emfh, void (*callback)(struct _EMFormatHTMLJob *job, int cancelled), void *data)
+;
+void em_format_html_job_queue(EMFormatHTML *emfh, struct _EMFormatHTMLJob *job);
+
+/* outputs a signature test */
+void em_format_html_multipart_signed_sign(EMFormat *emf, struct _CamelStream *stream, struct _CamelMimePart *part);
+
+#endif /* ! EM_FORMAT_HTML_H */
diff --git a/mail/em-format-quote.c b/mail/em-format-quote.c
new file mode 100644
index 0000000000..113d854fc5
--- /dev/null
+++ b/mail/em-format-quote.c
@@ -0,0 +1,289 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-mime-filter-tohtml.h>
+#include <camel/camel-mime-filter-enriched.h>
+#include "em-format-quote.h"
+
+struct _EMFormatQuotePrivate {
+ int dummy;
+};
+
+static void emfq_format_clone(EMFormat *, CamelMedium *, EMFormat *);
+static void emfq_format_error(EMFormat *emf, CamelStream *stream, const char *txt);
+static void emfq_format_message(EMFormat *, CamelStream *, CamelMedium *);
+static void emfq_format_source(EMFormat *, CamelStream *, CamelMimePart *);
+static void emfq_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *);
+
+static void emfq_builtin_init(EMFormatQuoteClass *efhc);
+
+static EMFormatClass *emfq_parent;
+
+static void
+emfq_init(GObject *o)
+{
+ EMFormatQuote *emfq =(EMFormatQuote *) o;
+
+ emfq->priv = g_malloc0(sizeof(*emfq->priv));
+
+ /* we want to convert url's etc */
+ emfq->text_html_flags = CAMEL_MIME_FILTER_TOHTML_PRE | CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS
+ | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
+}
+
+static void
+emfq_finalise(GObject *o)
+{
+ EMFormatQuote *emfq =(EMFormatQuote *) o;
+
+ if (emfq->stream)
+ camel_object_unref(emfq->stream);
+ g_free(emfq->credits);
+ g_free(emfq->priv);
+
+ ((GObjectClass *) emfq_parent)->finalize(o);
+}
+
+static void
+emfq_base_init(EMFormatQuoteClass *emfqklass)
+{
+ emfq_builtin_init(emfqklass);
+}
+
+static void
+emfq_class_init(GObjectClass *klass)
+{
+ ((EMFormatClass *) klass)->format_clone = emfq_format_clone;
+ ((EMFormatClass *) klass)->format_error = emfq_format_error;
+ ((EMFormatClass *) klass)->format_message = emfq_format_message;
+ ((EMFormatClass *) klass)->format_source = emfq_format_source;
+ ((EMFormatClass *) klass)->format_attachment = emfq_format_attachment;
+
+ klass->finalize = emfq_finalise;
+}
+
+GType
+em_format_quote_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFormatQuoteClass),
+ (GBaseInitFunc)emfq_base_init, NULL,
+ (GClassInitFunc)emfq_class_init,
+ NULL, NULL,
+ sizeof(EMFormatQuote), 0,
+ (GInstanceInitFunc) emfq_init
+ };
+
+ emfq_parent = g_type_class_ref(em_format_get_type());
+ type = g_type_register_static(em_format_get_type(), "EMFormatQuote", &info, 0);
+ }
+
+ return type;
+}
+
+EMFormatQuote *
+em_format_quote_new(const char *credits, CamelStream *stream, guint32 flags)
+{
+ EMFormatQuote *emfq;
+
+ emfq = (EMFormatQuote *)g_object_new(em_format_quote_get_type(), NULL);
+
+ emfq->credits = g_strdup(credits);
+ emfq->stream = stream;
+ camel_object_ref(stream);
+ emfq->flags = flags;
+
+ return emfq;
+}
+
+static void
+emfq_format_clone(EMFormat *emf, CamelMedium *part, EMFormat *src)
+{
+#define emfq ((EMFormatQuote *)emf)
+
+ ((EMFormatClass *)emfq_parent)->format_clone(emf, part, src);
+
+ camel_stream_reset(emfq->stream);
+ em_format_format_message(emf, emfq->stream, part);
+ camel_stream_flush(emfq->stream);
+
+ g_signal_emit_by_name(emf, "complete");
+#undef emfq
+}
+
+static void
+emfq_format_error(EMFormat *emf, CamelStream *stream, const char *txt)
+{
+ /* FIXME: should we even bother writign error text for quoting? probably not... */
+}
+
+static void
+emfq_format_message(EMFormat *emf, CamelStream *stream, CamelMedium *part)
+{
+ EMFormatQuote *emfq =(EMFormatQuote *) emf;
+
+ if (emfq->credits)
+ camel_stream_printf(stream, "%s", emfq->credits);
+
+
+ if (emfq->flags & EM_FORMAT_QUOTE_CITE)
+ camel_stream_printf(stream, "<!--+GtkHTML:<DATA class=\"ClueFlow\" key=\"orig\" value=\"1\">-->\n"
+ "<blockquote type=cite>\n"
+ "<font color=\"#%06x\">\n",
+ emfq->citation_colour & 0xffffff);
+
+ if (emfq->flags & EM_FORMAT_QUOTE_HEADERS) {
+ camel_stream_printf(stream, "<b>To: </b> Header goes here<br>");
+ }
+
+ em_format_part(emf, stream, (CamelMimePart *)part);
+
+ if (emfq->flags & EM_FORMAT_QUOTE_CITE)
+ camel_stream_write_string(stream, "</blockquote></font><!--+GtkHTML:<DATA class=\"ClueFlow\" clear=\"orig\">-->");
+}
+
+static void
+emfq_format_source(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilter *html_filter;
+ CamelDataWrapper *dw = (CamelDataWrapper *)part;
+
+ filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
+ html_filter = camel_mime_filter_tohtml_new (CAMEL_MIME_FILTER_TOHTML_CONVERT_NL
+ | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES
+ | CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT, 0);
+ camel_stream_filter_add(filtered_stream, html_filter);
+ camel_object_unref(html_filter);
+
+ em_format_format_text(emf, (CamelStream *)filtered_stream, dw);
+ camel_object_unref(filtered_stream);
+}
+
+static void
+emfq_format_attachment(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type, const EMFormatHandler *handle)
+{
+ ;
+}
+
+#include <camel/camel-medium.h>
+#include <camel/camel-mime-part.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-url.h>
+
+static void
+emfq_text_plain(EMFormatQuote *emfq, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilter *html_filter;
+ CamelContentType *type;
+ const char *format;
+ guint32 rgb = 0x737373, flags;
+
+ flags = emfq->text_html_flags;
+
+ /* Check for RFC 2646 flowed text. */
+ type = camel_mime_part_get_content_type(part);
+ if (header_content_type_is(type, "text", "plain")
+ && (format = header_content_type_param(type, "format"))
+ && !g_ascii_strcasecmp(format, "flowed"))
+ flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
+
+ html_filter = camel_mime_filter_tohtml_new(flags, rgb);
+ filtered_stream = camel_stream_filter_new_with_stream(stream);
+ camel_stream_filter_add(filtered_stream, html_filter);
+ camel_object_unref(html_filter);
+
+ em_format_format_text((EMFormat *)emfq, (CamelStream *)filtered_stream, camel_medium_get_content_object((CamelMedium *)part));
+ camel_stream_flush((CamelStream *)filtered_stream);
+ camel_object_unref(filtered_stream);
+}
+
+static void
+emfq_text_enriched(EMFormatQuote *emfq, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilter *enriched;
+ CamelDataWrapper *dw;
+ guint32 flags = 0;
+
+ dw = camel_medium_get_content_object((CamelMedium *)part);
+
+ if (!strcmp(info->mime_type, "text/richtext")) {
+ flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
+ camel_stream_write_string( stream, "\n<!-- text/richtext -->\n");
+ } else {
+ camel_stream_write_string( stream, "\n<!-- text/enriched -->\n");
+ }
+
+ enriched = camel_mime_filter_enriched_new(flags);
+ filtered_stream = camel_stream_filter_new_with_stream (stream);
+ camel_stream_filter_add(filtered_stream, enriched);
+ camel_object_unref(enriched);
+
+ camel_stream_write_string(stream, "<br><hr><br>");
+ em_format_format_text((EMFormat *)emfq, (CamelStream *)filtered_stream, dw);
+ camel_object_unref(filtered_stream);
+}
+
+static void
+emfq_text_html(EMFormat *emf, CamelStream *stream, CamelMimePart *part, EMFormatHandler *info)
+{
+ camel_stream_write_string(stream, "\n<!-- text/html -->\n");
+ em_format_format_text(emf, stream, camel_medium_get_content_object((CamelMedium *)part));
+}
+
+static const char *type_remove_table[] = {
+ "message/external-body",
+ "multipart/appledouble",
+};
+
+static EMFormatHandler type_builtin_table[] = {
+ { "text/plain",(EMFormatFunc)emfq_text_plain },
+ { "text/enriched",(EMFormatFunc)emfq_text_enriched },
+ { "text/richtext",(EMFormatFunc)emfq_text_enriched },
+ { "text/html",(EMFormatFunc)emfq_text_html },
+/* { "multipart/related",(EMFormatFunc)emfq_multipart_related },*/
+};
+
+static void
+emfq_builtin_init(EMFormatQuoteClass *efhc)
+{
+ int i;
+
+ for (i = 0; i < sizeof(type_remove_table) / sizeof(type_remove_table[0]); i++)
+ em_format_class_remove_handler((EMFormatClass *) efhc, type_remove_table[i]);
+
+ for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+ em_format_class_add_handler((EMFormatClass *)efhc, &type_builtin_table[i]);
+}
diff --git a/mail/em-format-quote.h b/mail/em-format-quote.h
new file mode 100644
index 0000000000..218937b935
--- /dev/null
+++ b/mail/em-format-quote.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _EM_FORMAT_QUOTE_H
+#define _EM_FORMAT_QUOTE_H
+
+#include "em-format.h"
+
+typedef struct _EMFormatQuote EMFormatQuote;
+typedef struct _EMFormatQuoteClass EMFormatQuoteClass;
+
+#define EM_FORMAT_QUOTE_CITE (1<<0)
+#define EM_FORMAT_QUOTE_HEADERS (1<<1)
+
+struct _EMFormatQuote {
+ EMFormat format;
+
+ struct _EMFormatQuotePrivate *priv;
+
+ char *credits;
+ struct _CamelStream *stream;
+ guint32 flags;
+
+ guint32 text_html_flags;
+ guint32 citation_colour;
+};
+
+struct _EMFormatQuoteClass {
+ EMFormatClass format_class;
+};
+
+GType em_format_quote_get_type(void);
+
+EMFormatQuote *em_format_quote_new(const char *, struct _CamelStream *, guint32 flags);
+
+#endif /* !_EM_FORMAT_QUOTE_H */
diff --git a/mail/em-format.c b/mail/em-format.c
new file mode 100644
index 0000000000..d239266806
--- /dev/null
+++ b/mail/em-format.c
@@ -0,0 +1,1228 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <libgnomevfs/gnome-vfs-mime.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+
+#include <gconf/gconf-client.h>
+
+#include <e-util/e-msgport.h>
+#include <camel/camel-url.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-stream-mem.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-multipart-encrypted.h>
+#include <camel/camel-multipart-signed.h>
+#include <camel/camel-medium.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-gpg-context.h>
+#include <camel/camel-string-utils.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-null.h>
+#include <camel/camel-mime-filter-charset.h>
+#include <camel/camel-mime-filter-windows.h>
+
+#include "em-format.h"
+
+#define d(x)
+
+static void emf_builtin_init(EMFormatClass *);
+static const char *emf_snoop_part(CamelMimePart *part);
+
+static void emf_format_clone(EMFormat *emf, CamelMedium *msg, EMFormat *emfsource);
+static gboolean emf_busy(EMFormat *emf);
+
+enum {
+ EMF_COMPLETE,
+ EMF_LAST_SIGNAL,
+};
+
+static guint emf_signals[EMF_LAST_SIGNAL];
+static GObjectClass *emf_parent;
+
+static void
+emf_init(GObject *o)
+{
+ EMFormat *emf = (EMFormat *)o;
+
+ emf->inline_table = g_hash_table_new(NULL, NULL);
+ e_dlist_init(&emf->header_list);
+ em_format_default_headers(emf);
+}
+
+static void
+emf_finalise(GObject *o)
+{
+ EMFormat *emf = (EMFormat *)o;
+
+ if (emf->session)
+ camel_object_unref(emf->session);
+
+ if (emf->inline_table)
+ g_hash_table_destroy(emf->inline_table);
+
+ em_format_clear_headers(emf);
+ g_free(emf->charset);
+
+ /* FIXME: check pending jobs */
+
+ ((GObjectClass *)emf_parent)->finalize(o);
+}
+
+static void
+emf_base_init(EMFormatClass *emfklass)
+{
+ emfklass->type_handlers = g_hash_table_new(g_str_hash, g_str_equal);
+ emf_builtin_init(emfklass);
+}
+
+static void
+emf_class_init(GObjectClass *klass)
+{
+ ((EMFormatClass *)klass)->type_handlers = g_hash_table_new(g_str_hash, g_str_equal);
+ emf_builtin_init((EMFormatClass *)klass);
+
+ klass->finalize = emf_finalise;
+ ((EMFormatClass *)klass)->format_clone = emf_format_clone;
+ ((EMFormatClass *)klass)->busy = emf_busy;
+
+ emf_signals[EMF_COMPLETE] =
+ g_signal_new("complete",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMFormatClass, complete),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+GType
+em_format_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMFormatClass),
+ (GBaseInitFunc)emf_base_init, NULL,
+ (GClassInitFunc)emf_class_init,
+ NULL, NULL,
+ sizeof(EMFormat), 0,
+ (GInstanceInitFunc)emf_init
+ };
+ emf_parent = g_type_class_ref(G_TYPE_OBJECT);
+ type = g_type_register_static(G_TYPE_OBJECT, "EMFormat", &info, 0);
+ }
+
+ return type;
+}
+
+/**
+ * em_format_class_add_handler:
+ * @emfc: EMFormatClass
+ * @info: Callback information.
+ *
+ * Add a mime type handler to this class. This is only used by implementing
+ * classes.
+ *
+ * When a mime type described by @info is encountered, the callback will
+ * be invoked. Note that @info may be extended by sub-classes if
+ * they require additional context information.
+ *
+ * Use a mime type of "foo/ *" to insert a fallback handler for type "foo".
+ **/
+void
+em_format_class_add_handler(EMFormatClass *emfc, EMFormatHandler *info)
+{
+ g_hash_table_insert(emfc->type_handlers, info->mime_type, info);
+ /* FIXME: do we care? This is really gui stuff */
+ /*
+ if (info->applications == NULL)
+ info->applications = gnome_vfs_mime_get_short_list_applications(info->mime_type);*/
+}
+
+
+/**
+ * em_format_class_remove_handler:
+ * @emfc: EMFormatClass
+ * @mime_type: mime-type of handler to remove
+ *
+ * Remove a mime type handler from this class. This is only used by
+ * implementing classes.
+ **/
+void
+em_format_class_remove_handler (EMFormatClass *emfc, const char *mime_type)
+{
+ g_hash_table_remove (emfc->type_handlers, mime_type);
+}
+
+
+/**
+ * em_format_find_handler:
+ * @emf:
+ * @mime_type:
+ *
+ * Find a format handler by @mime_type.
+ *
+ * Return value: NULL if no handler is available.
+ **/
+const EMFormatHandler *
+em_format_find_handler(EMFormat *emf, const char *mime_type)
+{
+ EMFormatClass *emfc = (EMFormatClass *)G_OBJECT_GET_CLASS(emf);
+
+ return g_hash_table_lookup(emfc->type_handlers, mime_type);
+}
+
+/**
+ * em_format_fallback_handler:
+ * @emf:
+ * @mime_type:
+ *
+ * Try to find a format handler based on the major type of the @mime_type.
+ *
+ * The subtype is replaced with "*" and a lookup performed.
+ *
+ * Return value:
+ **/
+const EMFormatHandler *
+em_format_fallback_handler(EMFormat *emf, const char *mime_type)
+{
+ char *mime, *s;
+
+ s = strchr(mime_type, '/');
+ if (s == NULL)
+ mime = (char *)mime_type;
+ else {
+ size_t len = (s-mime_type)+1;
+
+ mime = alloca(len+2);
+ strncpy(mime, mime_type, len);
+ strcpy(mime+len, "*");
+ }
+
+ return em_format_find_handler(emf, mime);
+}
+
+/**
+ * em_format_add_puri:
+ * @emf:
+ * @size:
+ * @cid: Override the autogenerated content id.
+ * @part:
+ * @func:
+ *
+ * Add a pending-uri handler. When formatting parts that reference
+ * other parts, a pending-uri (PURI) can be used to track the reference.
+ *
+ * @size is used to allocate the structure, so that it can be directly
+ * subclassed by implementors.
+ *
+ * @cid can be used to override the key used to retreive the PURI, if NULL,
+ * then the content-location and the content-id of the @part are stored
+ * as lookup keys for the part.
+ *
+ * FIXME: This may need a free callback.
+ *
+ * Return value: A new PURI, with a referenced copy of @part, and the cid
+ * always set. The uri will be set if one is available. Clashes
+ * are resolved by forgetting the old PURI in the global index.
+ **/
+EMFormatPURI *
+em_format_add_puri(EMFormat *emf, size_t size, const char *cid, CamelMimePart *part, EMFormatPURIFunc func)
+{
+ EMFormatPURI *puri;
+ const char *tmp;
+ static unsigned int uriid;
+
+ g_assert(size >= sizeof(*puri));
+ puri = g_malloc0(size);
+
+ puri->format = emf;
+ puri->func = func;
+ puri->use_count = 0;
+ puri->cid = g_strdup(cid);
+
+ if (part) {
+ camel_object_ref(part);
+ puri->part = part;
+ }
+
+ if (part != NULL && cid == NULL) {
+ tmp = camel_mime_part_get_content_id(part);
+ if (tmp)
+ puri->cid = g_strdup_printf("cid:%s", tmp);
+ else
+ puri->cid = g_strdup_printf("em-no-cid-%u", uriid++);
+
+ d(printf("built cid '%s'\n", puri->cid));
+
+ /* not quite same as old behaviour, it also put in the relative uri and a fallback for no parent uri */
+ tmp = camel_mime_part_get_content_location(part);
+ puri->uri = NULL;
+ if (tmp == NULL) {
+ if (emf->base)
+ puri->uri = camel_url_to_string(emf->base, 0);
+ } else {
+ if (strchr(tmp, ':') == NULL && emf->base != NULL) {
+ CamelURL *uri;
+
+ uri = camel_url_new_with_base(emf->base, tmp);
+ puri->uri = camel_url_to_string(uri, 0);
+ camel_url_free(uri);
+ } else {
+ puri->uri = g_strdup(tmp);
+ }
+ }
+ }
+
+ g_assert(puri->cid != NULL);
+ g_assert(emf->pending_uri_level != NULL);
+ g_assert(emf->pending_uri_table != NULL);
+
+ e_dlist_addtail(&emf->pending_uri_level->uri_list, (EDListNode *)puri);
+
+ if (puri->uri)
+ g_hash_table_insert(emf->pending_uri_table, puri->uri, puri);
+ g_hash_table_insert(emf->pending_uri_table, puri->cid, puri);
+
+ return puri;
+}
+
+/**
+ * em_format_push_level:
+ * @emf:
+ *
+ * This is used to build a heirarchy of visible PURI objects based on
+ * the structure of the message. Used by multipart/alternative formatter.
+ *
+ * FIXME: This could probably also take a uri so it can automaticall update
+ * the base location.
+ **/
+void
+em_format_push_level(EMFormat *emf)
+{
+ struct _EMFormatPURITree *purilist;
+
+ d(printf("em_format_push_level\n"));
+ purilist = g_malloc0(sizeof(*purilist));
+ e_dlist_init(&purilist->children);
+ e_dlist_init(&purilist->uri_list);
+ purilist->parent = emf->pending_uri_level;
+ if (emf->pending_uri_tree == NULL) {
+ emf->pending_uri_tree = purilist;
+ } else {
+ e_dlist_addtail(&emf->pending_uri_level->children, (EDListNode *)purilist);
+ }
+ emf->pending_uri_level = purilist;
+}
+
+/**
+ * em_format_pull_level:
+ * @emf:
+ *
+ * Drop a level of visibility back to the parent. Note that
+ * no PURI values are actually freed.
+ **/
+void
+em_format_pull_level(EMFormat *emf)
+{
+ d(printf("em_format_pull_level\n"));
+ emf->pending_uri_level = emf->pending_uri_level->parent;
+}
+
+/**
+ * em_format_find_visible_puri:
+ * @emf:
+ * @uri:
+ *
+ * Search for a PURI based on the visibility defined by :push_level()
+ * and :pull_level().
+ *
+ * Return value:
+ **/
+EMFormatPURI *
+em_format_find_visible_puri(EMFormat *emf, const char *uri)
+{
+ EMFormatPURI *pw;
+ struct _EMFormatPURITree *ptree;
+
+ d(printf("checking for visible uri '%s'\n", uri));
+
+ ptree = emf->pending_uri_level;
+ while (ptree) {
+ pw = (EMFormatPURI *)ptree->uri_list.head;
+ while (pw->next) {
+ d(printf(" pw->uri = '%s' pw->cid = '%s\n", pw->uri?pw->uri:"", pw->cid));
+ if ((pw->uri && !strcmp(pw->uri, uri)) || !strcmp(pw->cid, uri))
+ return pw;
+ pw = pw->next;
+ }
+ ptree = ptree->parent;
+ }
+
+ return NULL;
+}
+
+/**
+ * em_format_find_puri:
+ * @emf:
+ * @uri:
+ *
+ * Search for a PURI based on a uri. Both the content-id
+ * and content-location are checked.
+ *
+ * Return value:
+ **/
+EMFormatPURI *
+em_format_find_puri(EMFormat *emf, const char *uri)
+{
+ return g_hash_table_lookup(emf->pending_uri_table, uri);
+}
+
+static void
+emf_clear_puri_node(struct _EMFormatPURITree *node)
+{
+ {
+ EMFormatPURI *pw, *pn;
+
+ /* clear puri's at this level */
+ pw = (EMFormatPURI *)node->uri_list.head;
+ pn = pw->next;
+ while (pn) {
+ g_free(pw->uri);
+ g_free(pw->cid);
+ if (pw->part)
+ camel_object_unref(pw->part);
+ g_free(pw);
+ pw = pn;
+ pn = pn->next;
+ }
+ }
+
+ {
+ struct _EMFormatPURITree *cw, *cn;
+
+ /* clear child nodes */
+ cw = (struct _EMFormatPURITree *)node->children.head;
+ cn = cw->next;
+ while (cn) {
+ emf_clear_puri_node(cw);
+ cw = cn;
+ cn = cn->next;
+ }
+ }
+
+ g_free(node);
+}
+
+/**
+ * em_format_clear_puri_tree:
+ * @emf:
+ *
+ * For use by implementors to clear out the message structure
+ * data.
+ **/
+void
+em_format_clear_puri_tree(EMFormat *emf)
+{
+ d(printf("clearing pending uri's\n"));
+
+ if (emf->pending_uri_table) {
+ g_hash_table_destroy(emf->pending_uri_table);
+ emf_clear_puri_node(emf->pending_uri_tree);
+ emf->pending_uri_level = NULL;
+ emf->pending_uri_tree = NULL;
+ }
+ emf->pending_uri_table = g_hash_table_new(g_str_hash, g_str_equal);
+ em_format_push_level(emf);
+}
+
+/* use mime_type == NULL to force showing as application/octet-stream */
+void
+em_format_part_as(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const char *mime_type)
+{
+ const EMFormatHandler *handle = NULL;
+
+ if (mime_type != NULL) {
+ if (g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0)
+ mime_type = emf_snoop_part(part);
+
+ handle = em_format_find_handler(emf, mime_type);
+ if (handle == NULL)
+ handle = em_format_fallback_handler(emf, mime_type);
+
+ if (handle != NULL
+ && !em_format_is_attachment(emf, part)) {
+ d(printf("running handler for type '%s'\n", mime_type));
+ handle->handler(emf, stream, part, handle);
+ return;
+ }
+ d(printf("this type is an attachment? '%s'\n", mime_type));
+ } else {
+ mime_type = "application/octet-stream";
+ }
+
+ ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_attachment(emf, stream, part, mime_type, handle);
+}
+
+void
+em_format_part(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ char *mime_type;
+ CamelDataWrapper *dw;
+
+ dw = camel_medium_get_content_object((CamelMedium *)part);
+ mime_type = camel_data_wrapper_get_mime_type(dw);
+ camel_strdown(mime_type);
+ em_format_part_as(emf, stream, part, mime_type);
+ g_free(mime_type);
+}
+
+static void
+emf_clone_inlines(void *key, void *val, void *data)
+{
+ g_hash_table_insert(((EMFormat *)data)->inline_table, key, val);
+}
+
+static void
+emf_format_clone(EMFormat *emf, CamelMedium *msg, EMFormat *emfsource)
+{
+ em_format_clear_puri_tree(emf);
+
+ if (emf != emfsource) {
+ g_hash_table_destroy(emf->inline_table);
+ emf->inline_table = g_hash_table_new(NULL, NULL);
+ if (emfsource) {
+ /* We clone the current state here */
+ g_hash_table_foreach(emfsource->inline_table, emf_clone_inlines, emf);
+ emf->mode = emfsource->mode;
+ g_free(emf->charset);
+ emf->charset = g_strdup(emfsource->charset);
+ /* FIXME: clone headers shown */
+ }
+ }
+
+ if (msg != emf->message) {
+ if (emf->message)
+ camel_object_unref(emf->message);
+ if (msg)
+ camel_object_ref(msg);
+ emf->message = msg;
+ }
+}
+
+static gboolean
+emf_busy(EMFormat *emf)
+{
+ return FALSE;
+}
+
+/**
+ * em_format_format_clone:
+ * @emf: Mail formatter.
+ * @msg: Mail message.
+ * @emfsource: Used as a basis for user-altered layout, e.g. inline viewed
+ * attachments.
+ *
+ * Format a message @msg. If @emfsource is non NULL, then the status of
+ * inlined expansion and so forth is copied direction from @emfsource.
+ *
+ * By passing the same value for @emf and @emfsource, you can perform
+ * a display refresh, or it can be used to generate an identical layout,
+ * e.g. to print what the user has shown inline.
+ **/
+/* e_format_format_clone is a macro */
+
+/**
+ * em_format_set_session:
+ * @emf:
+ * @s:
+ *
+ * Set the CamelSession to be used for signature verification and decryption
+ * purposes. If this is not set, then signatures cannot be verified or
+ * encrypted messages viewed.
+ **/
+void
+em_format_set_session(EMFormat *emf, struct _CamelSession *s)
+{
+ if (s)
+ camel_object_ref(s);
+ if (emf->session)
+ camel_object_unref(emf->session);
+ emf->session = s;
+}
+
+/**
+ * em_format_set_mode:
+ * @emf:
+ * @type:
+ *
+ * Set display mode, EM_FORMAT_SOURCE, EM_FORMAT_ALLHEADERS, or
+ * EM_FORMAT_NORMAL.
+ **/
+void
+em_format_set_mode(EMFormat *emf, em_format_mode_t type)
+{
+ if (emf->mode == type)
+ return;
+
+ emf->mode = type;
+
+ /* force redraw if type changed afterwards */
+ if (emf->message)
+ em_format_format_clone(emf, emf->message, emf);
+}
+
+/**
+ * em_format_set_charset:
+ * @emf:
+ * @charset:
+ *
+ * set override charset on formatter. message will be redisplayed if
+ * required.
+ **/
+void
+em_format_set_charset(EMFormat *emf, const char *charset)
+{
+ if ((emf->charset && charset && g_ascii_strcasecmp(emf->charset, charset) == 0)
+ || (emf->charset == NULL && charset == NULL)
+ || (emf->charset == charset))
+ return;
+
+ g_free(emf->charset);
+ emf->charset = g_strdup(charset);
+
+ if (emf->message)
+ em_format_format_clone(emf, emf->message, emf);
+}
+
+/**
+ * em_format_clear_headers:
+ * @emf:
+ *
+ * Clear the list of headers to be displayed. This will force all headers to
+ * be shown.
+ **/
+void
+em_format_clear_headers(EMFormat *emf)
+{
+ EMFormatHeader *eh;
+
+ while ((eh = (EMFormatHeader *)e_dlist_remhead(&emf->header_list)))
+ g_free(eh);
+}
+
+static const struct {
+ const char *name;
+ guint32 flags;
+} default_headers[] = {
+ { N_("From"), EM_FORMAT_HEADER_BOLD },
+ { N_("Reply-To"), EM_FORMAT_HEADER_BOLD },
+ { N_("To"), EM_FORMAT_HEADER_BOLD },
+ { N_("Cc"), EM_FORMAT_HEADER_BOLD },
+ { N_("Bcc"), EM_FORMAT_HEADER_BOLD },
+ { N_("Subject"), EM_FORMAT_HEADER_BOLD },
+ { N_("Date"), EM_FORMAT_HEADER_BOLD },
+ { "x-evolution-mailer", 0 }, /* DO NOT translate */
+};
+
+/**
+ * em_format_default_headers:
+ * @emf:
+ *
+ * Set the headers to show to the default list.
+ *
+ * From, Reply-To, To, Cc, Bcc, Subject and Date.
+ **/
+void
+em_format_default_headers(EMFormat *emf)
+{
+ int i;
+
+ em_format_clear_headers(emf);
+ for (i=0;i<sizeof(default_headers)/sizeof(default_headers[0]);i++)
+ em_format_add_header(emf, default_headers[i].name, default_headers[i].flags);
+}
+
+/**
+ * em_format_add_header:
+ * @emf:
+ * @name: The name of the header, as it will appear during output.
+ * @flags: EM_FORMAT_HEAD_* defines to control display attributes.
+ *
+ * Add a specific header to show. If any headers are set, they will
+ * be displayed in the order set by this function. Certain known
+ * headers included in this list will be shown using special
+ * formatting routines.
+ **/
+void em_format_add_header(EMFormat *emf, const char *name, guint32 flags)
+{
+ EMFormatHeader *h;
+
+ h = g_malloc(sizeof(*h) + strlen(name));
+ h->flags = flags;
+ strcpy(h->name, name);
+ e_dlist_addtail(&emf->header_list, (EDListNode *)h);
+}
+
+/**
+ * em_format_is_attachment:
+ * @emf:
+ * @part: Part to check.
+ *
+ * Returns true if the part is an attachment.
+ *
+ * A part is not considered an attachment if it is a
+ * multipart, or a text part with no filename. It is used
+ * to determine if an attachment header should be displayed for
+ * the part.
+ *
+ * Content-Disposition is not checked.
+ *
+ * Return value: TRUE/FALSE
+ **/
+int em_format_is_attachment(EMFormat *emf, CamelMimePart *part)
+{
+ /*CamelContentType *ct = camel_mime_part_get_content_type(part);*/
+ CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
+
+ /*printf("checking is attachment %s/%s\n", ct->type, ct->subtype);*/
+ return !(/*header_content_type_is(ct, "message", "*")
+ ||*/ header_content_type_is(dw->mime_type, "multipart", "*")
+ || (header_content_type_is(dw->mime_type, "text", "*")
+ && camel_mime_part_get_filename(part) == NULL));
+
+}
+
+/**
+ * em_format_is_inline:
+ * @emf:
+ * @part:
+ *
+ * Returns true if the part should be displayed inline. Any part with
+ * a Content-Disposition of inline, or any message type is displayed
+ * inline.
+ *
+ * ::set_inline() called on the same part will override any calculated
+ * value.
+ *
+ * Return value:
+ **/
+int em_format_is_inline(EMFormat *emf, CamelMimePart *part)
+{
+ void *dummy, *override;
+ const char *tmp;
+ CamelContentType *ct;
+
+ if (g_hash_table_lookup_extended(emf->inline_table, part, &dummy, &override))
+ return GPOINTER_TO_INT(override);
+
+ tmp = camel_mime_part_get_disposition(part);
+ if (tmp)
+ return g_ascii_strcasecmp(tmp, "inline") == 0;
+
+ /* messages are always inline? */
+ ct = camel_mime_part_get_content_type(part);
+
+ return header_content_type_is(ct, "message", "*");
+}
+
+/**
+ * em_format_set_inline:
+ * @emf:
+ * @part:
+ * @state:
+ *
+ * Force the attachment @part to be expanded or hidden explictly to match
+ * @state. This is used only to record the change for a redraw or
+ * cloned layout render and does not force a redraw.
+ **/
+void em_format_set_inline(EMFormat *emf, CamelMimePart *part, int state)
+{
+ g_hash_table_insert(emf->inline_table, part, GINT_TO_POINTER(state));
+}
+
+/* should this be virtual? */
+void
+em_format_format_content(EMFormat *emf, CamelStream *stream, CamelMimePart *part)
+{
+ CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
+
+ if (header_content_type_is(dw->mime_type, "text", "*"))
+ em_format_format_text(emf, stream, dw);
+ else
+ camel_data_wrapper_decode_to_stream(dw, stream);
+}
+
+/**
+ * em_format_format_content:
+ * @emf:
+ * @stream: Where to write the converted text
+ * @part: Part whose container is to be formatted
+ *
+ * Decode/output a part's content to @stream.
+ **/
+void
+em_format_format_text(EMFormat *emf, CamelStream *stream, CamelDataWrapper *dw)
+{
+ CamelStreamFilter *filter_stream;
+ CamelMimeFilterCharset *filter;
+ const char *charset;
+ char *fallback_charset = NULL;
+
+ if (emf->charset) {
+ charset = emf->charset;
+ } else if (dw->mime_type
+ && (charset = header_content_type_param(dw->mime_type, "charset"))
+ && g_ascii_strncasecmp(charset, "iso-8859-", 9) == 0) {
+ CamelMimeFilterWindows *windows;
+ CamelStream *null;
+
+ /* Since a few Windows mailers like to claim they sent
+ * out iso-8859-# encoded text when they really sent
+ * out windows-cp125#, do some simple sanity checking
+ * before we move on... */
+
+ null = camel_stream_null_new();
+ filter_stream = camel_stream_filter_new_with_stream(null);
+ camel_object_unref(null);
+
+ windows = (CamelMimeFilterWindows *)camel_mime_filter_windows_new(charset);
+ camel_stream_filter_add(filter_stream, (CamelMimeFilter *)windows);
+
+ camel_data_wrapper_decode_to_stream(dw, (CamelStream *)filter_stream);
+ camel_stream_flush((CamelStream *)filter_stream);
+ camel_object_unref(filter_stream);
+
+ charset = fallback_charset = g_strdup(camel_mime_filter_windows_real_charset(windows));
+ camel_object_unref(windows);
+ } else if (charset == NULL) {
+ /* FIXME: remove gconf query every time */
+ GConfClient *gconf = gconf_client_get_default();
+
+ charset = fallback_charset = gconf_client_get_string(gconf, "/apps/evolution/mail/format/charset", NULL);
+ g_object_unref(gconf);
+ }
+
+ filter_stream = camel_stream_filter_new_with_stream(stream);
+
+ if ((filter = camel_mime_filter_charset_new_convert(charset, "UTF-8"))) {
+ camel_stream_filter_add(filter_stream, (CamelMimeFilter *) filter);
+ camel_object_unref(filter);
+ }
+
+ g_free(fallback_charset);
+
+ camel_data_wrapper_decode_to_stream(dw, (CamelStream *)filter_stream);
+ camel_stream_flush((CamelStream *)filter_stream);
+ camel_object_unref(filter_stream);
+}
+
+/**
+ * em_format_describe_part:
+ * @part:
+ * @mimetype:
+ *
+ * Generate a simple textual description of a part, @mime_type represents the
+ * the content.
+ *
+ * Return value:
+ **/
+char *
+em_format_describe_part(CamelMimePart *part, const char *mime_type)
+{
+ GString *stext;
+ const char *text;
+ char *out;
+
+ stext = g_string_new("");
+ text = gnome_vfs_mime_get_description(mime_type);
+ g_string_append_printf(stext, _("%s attachment"), text?text:mime_type);
+ if ((text = camel_mime_part_get_filename (part)))
+ g_string_append_printf(stext, " (%s)", text);
+ if ((text = camel_mime_part_get_description(part)))
+ g_string_append_printf(stext, ", \"%s\"", text);
+
+ out = stext->str;
+ g_string_free(stext, FALSE);
+
+ return out;
+}
+
+/* ********************************************************************** */
+
+/* originally from mail-identify.c */
+static const char *
+emf_snoop_part(CamelMimePart *part)
+{
+ const char *filename, *name_type = NULL, *magic_type = NULL;
+ CamelDataWrapper *dw;
+
+ filename = camel_mime_part_get_filename (part);
+ if (filename) {
+ /* GNOME-VFS will misidentify TNEF attachments as MPEG */
+ if (!strcmp (filename, "winmail.dat"))
+ return "application/vnd.ms-tnef";
+
+ name_type = gnome_vfs_mime_type_from_name(filename);
+ }
+
+ dw = camel_medium_get_content_object((CamelMedium *)part);
+ if (!camel_data_wrapper_is_offline(dw)) {
+ CamelStreamMem *mem = (CamelStreamMem *)camel_stream_mem_new();
+
+ if (camel_data_wrapper_decode_to_stream(dw, (CamelStream *)mem) > 0)
+ magic_type = gnome_vfs_get_mime_type_for_data(mem->buffer->data, mem->buffer->len);
+ camel_object_unref(mem);
+ }
+
+ d(printf("snooped part, magic_type '%s' name_type '%s'\n", magic_type, name_type));
+
+ /* If GNOME-VFS doesn't recognize the data by magic, but it
+ * contains English words, it will call it text/plain. If the
+ * filename-based check came up with something different, use
+ * that instead and if it returns "application/octet-stream"
+ * try to do better with the filename check.
+ */
+
+ if (magic_type) {
+ if (name_type
+ && (!strcmp(magic_type, "text/plain")
+ || !strcmp(magic_type, "application/octet-stream")))
+ return name_type;
+ else
+ return magic_type;
+ } else
+ return name_type;
+
+ /* We used to load parts to check their type, we dont anymore,
+ see bug #11778 for some discussion */
+}
+
+/* RFC 1740 */
+static void
+emf_multipart_appledouble(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+
+ if (!CAMEL_IS_MULTIPART(mp)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ /* try the data fork for something useful, doubtful but who knows */
+ em_format_part(emf, stream, camel_multipart_get_part(mp, 1));
+}
+
+/* RFC ??? */
+static void
+emf_multipart_mixed(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+ int i, nparts;
+
+ if (!CAMEL_IS_MULTIPART(mp)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ nparts = camel_multipart_get_number(mp);
+ for (i = 0; i < nparts; i++) {
+ /* FIXME: separate part markers ...
+ if (i != 0)
+ camel_stream_printf(stream, "<hr>\n");*/
+
+ part = camel_multipart_get_part(mp, i);
+ em_format_part(emf, stream, part);
+ }
+}
+
+/* RFC 1740 */
+static void
+emf_multipart_alternative(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+ int i, nparts;
+ CamelMimePart *best = NULL;
+
+ if (!CAMEL_IS_MULTIPART(mp)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ /* as pre rfc, find the last part we know how to display */
+ nparts = camel_multipart_get_number(mp);
+ for (i = 0; i < nparts; i++) {
+ CamelMimePart *part = camel_multipart_get_part(mp, i);
+ CamelContentType *type = camel_mime_part_get_content_type (part);
+ char *mime_type = header_content_type_simple (type);
+
+ camel_strdown (mime_type);
+
+ /*if (want_plain && !strcmp (mime_type, "text/plain"))
+ return part;*/
+
+ if (em_format_find_handler(emf, mime_type)
+ || (best == NULL && em_format_fallback_handler(emf, mime_type)))
+ best = part;
+
+ g_free(mime_type);
+ }
+
+ if (best)
+ em_format_part(emf, stream, best);
+ else
+ emf_multipart_mixed(emf, stream, part, info);
+}
+
+static void
+emf_multipart_encrypted(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipartEncrypted *mpe;
+ CamelMimePart *mime_part;
+ CamelCipherContext *cipher;
+ CamelException ex;
+ const char *protocol;
+
+ /* Currently we only handle RFC2015-style PGP encryption. */
+ protocol = header_content_type_param (((CamelDataWrapper *) part)->mime_type, "protocol");
+ if (!protocol || strcmp (protocol, "application/pgp-encrypted") != 0)
+ return emf_multipart_mixed(emf, stream, part, info);
+
+ mpe = (CamelMultipartEncrypted *)camel_medium_get_content_object((CamelMedium *)part);
+
+ if (!CAMEL_IS_MULTIPART_ENCRYPTED(mpe)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ camel_exception_init (&ex);
+ cipher = camel_gpg_context_new(emf->session);
+ mime_part = camel_multipart_encrypted_decrypt(mpe, cipher, &ex);
+ camel_object_unref(cipher);
+
+ if (camel_exception_is_set(&ex)) {
+ /* FIXME: error handler */
+ em_format_format_error(emf, stream, camel_exception_get_description(&ex));
+ camel_exception_clear(&ex);
+ return;
+ }
+
+ em_format_part(emf, stream, mime_part);
+ camel_object_unref(mime_part);
+}
+
+static void
+emf_write_related(EMFormat *emf, CamelStream *stream, EMFormatPURI *puri)
+{
+ em_format_format_content(emf, stream, puri->part);
+ camel_stream_close(stream);
+}
+
+/* RFC 2387 */
+static void
+emf_multipart_related(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMultipart *mp = (CamelMultipart *)camel_medium_get_content_object((CamelMedium *)part);
+ CamelMimePart *body_part, *display_part = NULL;
+ CamelContentType *content_type;
+ const char *location, *start;
+ int i, nparts;
+ CamelURL *base_save = NULL;
+ struct _EMFormatPURITree *ptree;
+ EMFormatPURI *puri, *purin;
+
+ if (!CAMEL_IS_MULTIPART(mp)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ /* FIXME: put this stuff in a shared function */
+ nparts = camel_multipart_get_number(mp);
+ content_type = camel_mime_part_get_content_type(part);
+ start = header_content_type_param(content_type, "start");
+ if (start && strlen(start)>2) {
+ int len;
+ const char *cid;
+
+ /* strip <>'s */
+ len = strlen (start) - 2;
+ start++;
+
+ for (i=0; i<nparts; i++) {
+ body_part = camel_multipart_get_part(mp, i);
+ cid = camel_mime_part_get_content_id(body_part);
+
+ if (cid && !strncmp(cid, start, len) && strlen(cid) == len) {
+ display_part = body_part;
+ break;
+ }
+ }
+ } else {
+ display_part = camel_multipart_get_part(mp, 0);
+ }
+
+ if (display_part == NULL) {
+ emf_multipart_mixed(emf, stream, part, info);
+ return;
+ }
+
+ /* stack of present location and pending uri's */
+ location = camel_mime_part_get_content_location(part);
+ if (location) {
+ d(printf("setting content location %s\n", location));
+ base_save = emf->base;
+ emf->base = camel_url_new(location, NULL);
+ }
+ em_format_push_level(emf);
+
+ /* queue up the parts for possible inclusion */
+ for (i = 0; i < nparts; i++) {
+ body_part = camel_multipart_get_part(mp, i);
+ if (body_part != display_part) {
+ puri = em_format_add_puri(emf, sizeof(EMFormatPURI), NULL, body_part, emf_write_related);
+ d(printf(" part '%s' '%s' added\n", puri->uri?puri->uri:"", puri->cid));
+ }
+ }
+
+ em_format_part(emf, stream, display_part);
+ camel_stream_flush(stream);
+
+ ptree = emf->pending_uri_level;
+ puri = (EMFormatPURI *)ptree->uri_list.head;
+ purin = puri->next;
+ while (purin) {
+ if (purin->use_count == 0) {
+ d(printf("part '%s' '%s' used '%d'\n", purin->uri?purin->uri:"", purin->cid, purin->use_count));
+ if (purin->func == emf_write_related)
+ em_format_part(emf, stream, puri->part);
+ else
+ printf("unreferenced uri generated by format code: %s\n", purin->uri?purin->uri:purin->cid);
+ }
+ puri = purin;
+ purin = purin->next;
+ }
+ em_format_pull_level(emf);
+
+ if (location) {
+ camel_url_free(emf->base);
+ emf->base = base_save;
+ }
+}
+
+/* this is only a fallback implementation, implementations should override */
+static void
+emf_multipart_signed(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelMimePart *cpart, *spart;
+ CamelMultipartSigned *mps;
+ CamelCipherValidity *valid = NULL;
+ CamelException ex;
+ const char *message = NULL;
+ gboolean good = FALSE;
+ CamelCipherContext *cipher;
+
+ mps = (CamelMultipartSigned *)camel_medium_get_content_object((CamelMedium *)part);
+ if (!CAMEL_IS_MULTIPART_SIGNED(mps)
+ || (cpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_CONTENT)) == NULL) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ em_format_part(emf, stream, cpart);
+
+ spart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_SIGNATURE);
+ camel_exception_init(&ex);
+ if (spart == NULL) {
+ message = _("No signature present");
+ } else if (emf->session == NULL) {
+ message = _("Session not initialised");
+ } else if ((cipher = camel_gpg_context_new(emf->session)) == NULL) {
+ message = _("Could not create signature verfication context");
+ } else {
+ valid = camel_multipart_signed_verify(mps, cipher, &ex);
+ camel_object_unref(cipher);
+ if (valid) {
+ good = camel_cipher_validity_get_valid(valid);
+ message = camel_cipher_validity_get_description(valid);
+ } else {
+ message = camel_exception_get_description(&ex);
+ }
+ }
+
+ if (good)
+ em_format_format_error(emf, stream, _("This message is digitally signed and has been found to be authentic."));
+ else
+ em_format_format_error(emf, stream, _("This message is digitally signed but can not be proven to be authentic."));
+
+ if (message)
+ em_format_format_error(emf, stream, message);
+
+ camel_exception_clear(&ex);
+ camel_cipher_validity_free(valid);
+}
+
+/* this is only a fallback, any implementer should implement */
+static void
+emf_message_rfc822(EMFormat *emf, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info)
+{
+ CamelDataWrapper *dw = camel_medium_get_content_object((CamelMedium *)part);
+
+ if (!CAMEL_IS_MIME_MESSAGE(dw)) {
+ em_format_format_source(emf, stream, part);
+ return;
+ }
+
+ em_format_format_message(emf, stream, (CamelMedium *)dw);
+}
+
+static EMFormatHandler type_builtin_table[] = {
+ { "multipart/alternative", emf_multipart_alternative },
+ { "multipart/appledouble", emf_multipart_appledouble },
+ { "multipart/encrypted", emf_multipart_encrypted },
+ { "multipart/mixed", emf_multipart_mixed },
+ { "multipart/signed", emf_multipart_signed },
+ { "multipart/related", emf_multipart_related },
+ { "multipart/*", emf_multipart_mixed },
+ { "message/rfc822", emf_message_rfc822 },
+ { "message/news", emf_message_rfc822 },
+ { "message/*", emf_message_rfc822 },
+};
+
+static void
+emf_builtin_init(EMFormatClass *klass)
+{
+ int i;
+
+ for (i=0;i<sizeof(type_builtin_table)/sizeof(type_builtin_table[0]);i++)
+ g_hash_table_insert(klass->type_handlers, type_builtin_table[i].mime_type, &type_builtin_table[i]);
+}
diff --git a/mail/em-format.h b/mail/em-format.h
new file mode 100644
index 0000000000..98812ea377
--- /dev/null
+++ b/mail/em-format.h
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ Abstract class for formatting mime messages
+*/
+
+#ifndef _EM_FORMAT_H
+#define _EM_FORMAT_H
+
+#include <glib-object.h>
+#include "e-util/e-msgport.h"
+
+struct _CamelStream;
+struct _CamelMimePart;
+struct _CamelMedium;
+struct _CamelSession;
+struct _CamelURL;
+struct _CamelDataWrapper;
+
+typedef struct _EMFormat EMFormat;
+typedef struct _EMFormatClass EMFormatClass;
+
+typedef struct _EMFormatHandler EMFormatHandler;
+typedef struct _EMFormatHeader EMFormatHeader;
+
+typedef void (*EMFormatFunc) (EMFormat *md, struct _CamelStream *stream, struct _CamelMimePart *part, const EMFormatHandler *info);
+
+typedef enum _em_format_mode_t {
+ EM_FORMAT_NORMAL,
+ EM_FORMAT_ALLHEADERS,
+ EM_FORMAT_SOURCE,
+} em_format_mode_t;
+
+/* can be subclassed/extended ... */
+struct _EMFormatHandler {
+ char *mime_type;
+ EMFormatFunc handler;
+ GList *applications; /* gnome vfs short-list of applications, do we care? */
+};
+
+typedef struct _EMFormatPURI EMFormatPURI;
+typedef void (*EMFormatPURIFunc)(EMFormat *md, struct _CamelStream *stream, EMFormatPURI *puri);
+
+struct _EMFormatPURI {
+ struct _EMFormatPURI *next, *prev;
+
+ struct _EMFormat *format;
+
+ char *uri; /* will be the location of the part, may be empty */
+ char *cid; /* will always be set, a fake one created if needed */
+
+ EMFormatPURIFunc func;
+ struct _CamelMimePart *part;
+
+ unsigned int use_count; /* used by multipart/related to see if it was accessed */
+};
+
+/* used to stack pending uri's for visibility (multipart/related) */
+struct _EMFormatPURITree {
+ struct _EMFormatPURITree *next, *prev, *parent;
+
+ EDList uri_list;
+ EDList children;
+};
+
+struct _EMFormatHeader {
+ struct _EMFormatHeader *next, *prev;
+
+ guint32 flags; /* E_FORMAT_HEADER_* */
+ char name[1];
+};
+
+#define EM_FORMAT_HEADER_BOLD (1<<0)
+#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */
+
+struct _EMFormat {
+ GObject parent;
+
+ struct _CamelMedium *message; /* the current message */
+
+ EDList header_list; /* if empty, then all */
+
+ struct _CamelSession *session; /* session, used for authentication when required */
+ struct _CamelURL *base; /* current location (base url) */
+
+ /* for forcing inlining */
+ GHashTable *inline_table;
+
+ /* global lookup table for message */
+ GHashTable *pending_uri_table;
+
+ /* visibility tree, also stores every puri permanently */
+ struct _EMFormatPURITree *pending_uri_tree;
+ /* current level to search from */
+ struct _EMFormatPURITree *pending_uri_level;
+
+ em_format_mode_t mode; /* source/headers/etc */
+ char *charset; /* charset override */
+};
+
+struct _EMFormatClass {
+ GObjectClass parent_class;
+
+ GHashTable *type_handlers;
+
+ /* start formatting a message */
+ void (*format_clone)(EMFormat *, struct _CamelMedium *, EMFormat *);
+ /* some internel error/inconsistency */
+ void (*format_error)(EMFormat *, struct _CamelStream *, const char *msg);
+
+ /* use for external structured parts */
+ void (*format_attachment)(EMFormat *, struct _CamelStream *, struct _CamelMimePart *, const char *mime_type, const struct _EMFormatHandler *info);
+ /* for any message parts */
+ void (*format_message)(EMFormat *, struct _CamelStream *, struct _CamelMedium *);
+ /* use for unparsable content */
+ void (*format_source)(EMFormat *, struct _CamelStream *, struct _CamelMimePart *);
+
+ /* returns true if the formatter is still busy with pending stuff */
+ gboolean (*busy)(EMFormat *);
+
+ /* signals */
+ /* complete, alternative to polling busy, for asynchronous work */
+ void (*complete)(EMFormat *);
+};
+
+/* helper entry point */
+void em_format_set_session(EMFormat *emf, struct _CamelSession *s);
+
+void em_format_set_mode(EMFormat *emf, em_format_mode_t type);
+void em_format_set_charset(EMFormat *emf, const char *charset);
+
+void em_format_clear_headers(EMFormat *emf); /* also indicates to show all headers */
+void em_format_default_headers(EMFormat *emf);
+void em_format_add_header(EMFormat *emf, const char *name, guint32 flags);
+
+/* FIXME: Need a 'clone' api to copy details about the current view (inlines etc)
+ Or maybe it should live with sub-classes? */
+
+int em_format_is_attachment(EMFormat *emf, struct _CamelMimePart *part);
+int em_format_is_inline(EMFormat *emf, struct _CamelMimePart *part);
+/* FIXME: not sure about this api */
+void em_format_set_inline(EMFormat *emf, struct _CamelMimePart *part, int state);
+char *em_format_describe_part(struct _CamelMimePart *part, const char *mimetype);
+
+/* for implementers */
+GType em_format_get_type(void);
+
+void em_format_class_add_handler(EMFormatClass *emfc, EMFormatHandler *info);
+void em_format_class_remove_handler (EMFormatClass *emfc, const char *mime_type);
+const EMFormatHandler *em_format_find_handler(EMFormat *emf, const char *mime_type);
+const EMFormatHandler *em_format_fallback_handler(EMFormat *emf, const char *mime_type);
+
+/* puri is short for pending uri ... really */
+EMFormatPURI *em_format_add_puri(EMFormat *emf, size_t size, const char *uri, struct _CamelMimePart *part, EMFormatPURIFunc func);
+EMFormatPURI *em_format_find_visible_puri(EMFormat *emf, const char *uri);
+EMFormatPURI *em_format_find_puri(EMFormat *emf, const char *uri);
+void em_format_clear_puri_tree(EMFormat *emf);
+void em_format_push_level(EMFormat *emf);
+void em_format_pull_level(EMFormat *emf);
+
+/* clones inline state/view and format, or use to redraw */
+#define em_format_format_clone(emf, msg, src) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_clone((emf), (msg), (src))
+/* formats a new message */
+#define em_format_format(emf, msg) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_clone((emf), (msg), NULL)
+#define em_format_format_error(emf, stream, txt) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_error((emf), (stream), (txt))
+#define em_format_format_attachment(emf, stream, msg, type, info) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_attachment((emf), (stream), (msg), (type), (info))
+#define em_format_format_message(emf, stream, msg) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_message((emf), (stream), (msg))
+#define em_format_format_source(emf, stream, msg) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->format_source((emf), (stream), (msg))
+
+#define em_format_busy(emf) ((EMFormatClass *)G_OBJECT_GET_CLASS(emf))->busy((emf))
+
+/* raw content only */
+void em_format_format_content(EMFormat *emf, struct _CamelStream *stream, struct _CamelMimePart *part);
+/* raw content text parts - should this just be checked/done by above? */
+void em_format_format_text(EMFormat *emf, struct _CamelStream *stream, struct _CamelDataWrapper *part);
+
+void em_format_part_as(EMFormat *emf, struct _CamelStream *stream, struct _CamelMimePart *part, const char *mime_type);
+void em_format_part(EMFormat *emf, struct _CamelStream *stream, struct _CamelMimePart *part);
+
+#endif /* ! _EM_FORMAT_H */
diff --git a/mail/em-html-stream.c b/mail/em-html-stream.c
new file mode 100644
index 0000000000..e50d6caee0
--- /dev/null
+++ b/mail/em-html-stream.c
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <gtkhtml/gtkhtml.h>
+#include <gtkhtml/gtkhtml-stream.h>
+#include <gtk/gtkmain.h>
+#include "em-html-stream.h"
+
+#define d(x)
+
+static void em_html_stream_class_init (EMHTMLStreamClass *klass);
+static void em_html_stream_init (CamelObject *object);
+static void em_html_stream_finalize (CamelObject *object);
+
+static ssize_t emhs_sync_write(CamelStream *stream, const char *buffer, size_t n);
+static int emhs_sync_close(CamelStream *stream);
+static int emhs_sync_flush(CamelStream *stream);
+
+static EMSyncStreamClass *parent_class = NULL;
+
+CamelType
+em_html_stream_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ parent_class = (EMSyncStreamClass *)em_sync_stream_get_type();
+ type = camel_type_register (em_sync_stream_get_type(),
+ "EMHTMLStream",
+ sizeof (EMHTMLStream),
+ sizeof (EMHTMLStreamClass),
+ (CamelObjectClassInitFunc) em_html_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) em_html_stream_init,
+ (CamelObjectFinalizeFunc) em_html_stream_finalize);
+ }
+
+ return type;
+}
+
+static void
+em_html_stream_class_init (EMHTMLStreamClass *klass)
+{
+ ((EMSyncStreamClass *)klass)->sync_write = emhs_sync_write;
+ ((EMSyncStreamClass *)klass)->sync_flush = emhs_sync_flush;
+ ((EMSyncStreamClass *)klass)->sync_close = emhs_sync_close;
+}
+
+static void
+em_html_stream_init (CamelObject *object)
+{
+ /*EMHTMLStream *emhs = (EMHTMLStream *)object;*/
+}
+
+static void
+emhs_cleanup(EMHTMLStream *emhs)
+{
+ emhs->html_stream = NULL;
+ emhs->sync.cancel = TRUE;
+ g_signal_handler_disconnect(emhs->html, emhs->destroy_id);
+ g_object_unref(emhs->html);
+ emhs->html = NULL;
+}
+
+static void
+em_html_stream_finalize (CamelObject *object)
+{
+ EMHTMLStream *emhs = (EMHTMLStream *)object;
+
+ d(printf("%p: finalising stream\n", object));
+ if (emhs->html_stream) {
+ d(printf("%p: html stream still open - error\n", object));
+ /* set 'in finalise' flag */
+ camel_stream_close((CamelStream *)emhs);
+ }
+}
+
+static ssize_t
+emhs_sync_write(CamelStream *stream, const char *buffer, size_t n)
+{
+ EMHTMLStream *emhs = EM_HTML_STREAM (stream);
+
+ if (emhs->html_stream == NULL)
+ return -1;
+
+ gtk_html_stream_write(emhs->html_stream, buffer, n);
+
+ return (ssize_t) n;
+}
+
+static int
+emhs_sync_flush(CamelStream *stream)
+{
+ EMHTMLStream *emhs = (EMHTMLStream *)stream;
+
+ if (emhs->html_stream == NULL)
+ return -1;
+
+ /* FIXME: flush html stream via gtkhtml_stream_flush which doens't exist yet ... */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+ return 0;
+}
+
+static int
+emhs_sync_close(CamelStream *stream)
+{
+ EMHTMLStream *emhs = (EMHTMLStream *)stream;
+
+ if (emhs->html_stream == NULL)
+ return -1;
+
+ gtk_html_stream_close(emhs->html_stream, GTK_HTML_STREAM_OK);
+ emhs_cleanup(emhs);
+
+ return 0;
+}
+
+static void
+emhs_gtkhtml_destroy(struct _GtkHTML *html, EMHTMLStream *emhs)
+{
+ d(printf("%p: emhs gtkhtml destroy\n", emhs));
+ emhs_cleanup(emhs);
+}
+
+/* TODO: Could pass NULL for html_stream, and do a gtk_html_begin
+ on first data -> less flashing */
+CamelStream *
+em_html_stream_new(struct _GtkHTML *html, struct _GtkHTMLStream *html_stream)
+{
+ EMHTMLStream *new;
+
+ new = EM_HTML_STREAM (camel_object_new (EM_HTML_STREAM_TYPE));
+ new->html_stream = html_stream;
+ new->html = html;
+ g_object_ref(html);
+ new->destroy_id = g_signal_connect(html, "destroy", G_CALLBACK(emhs_gtkhtml_destroy), new);
+
+ em_sync_stream_set_buffer_size(&new->sync, 4096);
+
+ return (CamelStream *)new;
+}
diff --git a/mail/mail-display-stream.h b/mail/em-html-stream.h
index 943398c49a..dc4b59cde8 100644
--- a/mail/mail-display-stream.h
+++ b/mail/em-html-stream.h
@@ -20,43 +20,45 @@
*
*/
-
-#ifndef MAIL_DISPLAY_STREAM_H
-#define MAIL_DISPLAY_STREAM_H
+#ifndef EM_HTML_STREAM_H
+#define EM_HTML_STREAM_H
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */
-#include <camel/camel-stream.h>
-#include <gtkhtml/gtkhtml.h>
+#define EM_HTML_STREAM_TYPE (em_html_stream_get_type ())
+#define EM_HTML_STREAM(obj) (CAMEL_CHECK_CAST((obj), EM_HTML_STREAM_TYPE, EMHTMLStream))
+#define EM_HTML_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_HTML_STREAM_TYPE, EMHTMLStreamClass))
+#define EM_IS_HTML_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_HTML_STREAM_TYPE))
-#define MAIL_DISPLAY_STREAM_TYPE (mail_display_stream_get_type ())
-#define MAIL_DISPLAY_STREAM(obj) (CAMEL_CHECK_CAST((obj), MAIL_DISPLAY_STREAM_TYPE, MailDisplayStream))
-#define MAIL_DISPLAY_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), MAIL_DISPLAY_STREAM_TYPE, MailDisplayStreamClass))
-#define MAIL_IS_DISPLAY_STREAM(o) (CAMEL_CHECK_TYPE((o), MAIL_DISPLAY_STREAM_TYPE))
+struct _GtkHTML;
+struct _GtkHTMLStream;
-typedef struct _MailDisplayStream {
- CamelStream parent_stream;
-
- GtkHTML *html;
- GtkHTMLStream *html_stream;
-} MailDisplayStream;
+#include "em-sync-stream.h"
+
+typedef struct _EMHTMLStream {
+ EMSyncStream sync;
+
+ guint destroy_id;
+ struct _GtkHTML *html;
+ struct _GtkHTMLStream *html_stream;
+} EMHTMLStream;
typedef struct {
- CamelStreamClass parent_class;
+ EMSyncStreamClass parent_class;
-} MailDisplayStreamClass;
+} EMHTMLStreamClass;
-CamelType mail_display_stream_get_type (void);
+CamelType em_html_stream_get_type (void);
-/* Note: stream does not ref these objects! */
-CamelStream *mail_display_stream_new (GtkHTML *html, GtkHTMLStream *html_stream);
+/* the html_stream is closed when we are finalised (with an error), or closed (ok) */
+CamelStream *em_html_stream_new(struct _GtkHTML *html, struct _GtkHTMLStream *html_stream);
#ifdef __cplusplus
}
#endif /* __cplusplus */
-#endif /* MAIL_DISPLAY_STREAM_H */
+#endif /* EM_HTML_STREAM_H */
diff --git a/mail/em-icon-stream.c b/mail/em-icon-stream.c
new file mode 100644
index 0000000000..9ba67cf69a
--- /dev/null
+++ b/mail/em-icon-stream.c
@@ -0,0 +1,201 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixbuf-loader.h>
+#include <gtk/gtkimage.h>
+#include "em-icon-stream.h"
+
+#define d(x)
+
+static void em_icon_stream_class_init (EMIconStreamClass *klass);
+static void em_icon_stream_init (CamelObject *object);
+static void em_icon_stream_finalize (CamelObject *object);
+
+static ssize_t emis_sync_write(CamelStream *stream, const char *buffer, size_t n);
+static int emis_sync_close(CamelStream *stream);
+static int emis_sync_flush(CamelStream *stream);
+
+static EMSyncStreamClass *parent_class = NULL;
+
+CamelType
+em_icon_stream_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ parent_class = (EMSyncStreamClass *)em_sync_stream_get_type();
+ type = camel_type_register (em_sync_stream_get_type(),
+ "EMIconStream",
+ sizeof (EMIconStream),
+ sizeof (EMIconStreamClass),
+ (CamelObjectClassInitFunc) em_icon_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) em_icon_stream_init,
+ (CamelObjectFinalizeFunc) em_icon_stream_finalize);
+ }
+
+ return type;
+}
+
+static void
+em_icon_stream_class_init (EMIconStreamClass *klass)
+{
+ ((EMSyncStreamClass *)klass)->sync_write = emis_sync_write;
+ ((EMSyncStreamClass *)klass)->sync_flush = emis_sync_flush;
+ ((EMSyncStreamClass *)klass)->sync_close = emis_sync_close;
+}
+
+static void
+em_icon_stream_init (CamelObject *object)
+{
+ EMIconStream *emis = (EMIconStream *)object;
+
+ emis->width = 24;
+ emis->height = 24;
+}
+
+static void
+emis_cleanup(EMIconStream *emis)
+{
+ if (emis->loader) {
+ gdk_pixbuf_loader_close(emis->loader, NULL);
+ g_object_unref(emis->loader);
+ emis->loader = NULL;
+ }
+
+ if (emis->destroy_id) {
+ g_signal_handler_disconnect(emis->image, emis->destroy_id);
+ emis->destroy_id = 0;
+ }
+
+ emis->image = NULL;
+ emis->sync.cancel = TRUE;
+}
+
+static void
+em_icon_stream_finalize(CamelObject *object)
+{
+ EMIconStream *emis = (EMIconStream *)object;
+
+ emis_cleanup(emis);
+}
+
+static ssize_t
+emis_sync_write(CamelStream *stream, const char *buffer, size_t n)
+{
+ EMIconStream *emis = EM_ICON_STREAM (stream);
+
+ if (emis->loader == NULL)
+ return -1;
+
+ if (!gdk_pixbuf_loader_write(emis->loader, buffer, n, NULL)) {
+ emis_cleanup(emis);
+ return -1;
+ }
+
+ return (ssize_t) n;
+}
+
+static int
+emis_sync_flush(CamelStream *stream)
+{
+ return 0;
+}
+
+static int
+emis_sync_close(CamelStream *stream)
+{
+ EMIconStream *emis = (EMIconStream *)stream;
+ int width, height, ratio;
+ GdkPixbuf *pixbuf, *mini;
+
+ if (emis->loader == NULL)
+ return -1;
+
+ gdk_pixbuf_loader_close(emis->loader, NULL);
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf(emis->loader);
+ if (pixbuf == NULL) {
+ printf("couldn't get pixbuf from loader\n");
+ emis_cleanup(emis);
+ return -1;
+ }
+
+ width = gdk_pixbuf_get_width(pixbuf);
+ height = gdk_pixbuf_get_height(pixbuf);
+
+ if (width != emis->width || height != emis->height) {
+ if (width >= height) {
+ if (width > emis->width) {
+ ratio = width / emis->width;
+ width = emis->width;
+ height /= ratio;
+ }
+ } else {
+ if (height > emis->height) {
+ ratio = height / emis->height;
+ height = emis->height;
+ width /= ratio;
+ }
+ }
+
+ mini = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR);
+ gtk_image_set_from_pixbuf(emis->image, mini);
+ g_object_unref(mini);
+ } else {
+ gtk_image_set_from_pixbuf(emis->image, pixbuf);
+ }
+
+ g_object_unref(emis->loader);
+ emis->loader = NULL;
+
+ g_signal_handler_disconnect(emis->image, emis->destroy_id);
+ emis->destroy_id = 0;
+
+ return 0;
+}
+
+static void
+emis_image_destroy(struct _GtkImage *image, EMIconStream *emis)
+{
+ emis_cleanup(emis);
+}
+
+CamelStream *
+em_icon_stream_new(GtkImage *image)
+{
+ EMIconStream *new;
+
+ new = EM_ICON_STREAM(camel_object_new(EM_ICON_STREAM_TYPE));
+ new->image = image;
+ new->destroy_id = g_signal_connect(image, "destroy", G_CALLBACK(emis_image_destroy), new);
+ new->loader = gdk_pixbuf_loader_new();
+
+ return (CamelStream *)new;
+}
diff --git a/mail/em-icon-stream.h b/mail/em-icon-stream.h
new file mode 100644
index 0000000000..3776732578
--- /dev/null
+++ b/mail/em-icon-stream.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef EM_ICON_STREAM_H
+#define EM_ICON_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EM_ICON_STREAM_TYPE (em_icon_stream_get_type ())
+#define EM_ICON_STREAM(obj) (CAMEL_CHECK_CAST((obj), EM_ICON_STREAM_TYPE, EMIconStream))
+#define EM_ICON_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_ICON_STREAM_TYPE, EMIconStreamClass))
+#define EM_IS_ICON_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_ICON_STREAM_TYPE))
+
+struct _GtkHTML;
+struct _GtkIconStream;
+
+#include "em-sync-stream.h"
+
+typedef struct _EMIconStream {
+ EMSyncStream sync;
+
+ unsigned int width, height;
+ guint destroy_id;
+ struct _GdkPixbufLoader *loader;
+ struct _GtkImage *image;
+} EMIconStream;
+
+typedef struct {
+ EMSyncStreamClass parent_class;
+} EMIconStreamClass;
+
+CamelType em_icon_stream_get_type (void);
+
+CamelStream *em_icon_stream_new(GtkImage *image);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* EM_ICON_STREAM_H */
diff --git a/mail/em-inline-filter.c b/mail/em-inline-filter.c
new file mode 100644
index 0000000000..efaa09baf5
--- /dev/null
+++ b/mail/em-inline-filter.c
@@ -0,0 +1,335 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "em-inline-filter.h"
+#include <camel/camel-mime-part.h>
+#include <camel/camel-multipart.h>
+#include <camel/camel-stream-mem.h>
+
+#define d(x)
+
+static void em_inline_filter_class_init (EMInlineFilterClass *klass);
+static void em_inline_filter_init (CamelObject *object);
+static void em_inline_filter_finalize (CamelObject *object);
+
+static void emif_filter(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace);
+static void emif_complete(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace);
+static void emif_reset(CamelMimeFilter *f);
+
+static CamelMimeFilterClass *parent_class = NULL;
+
+CamelType
+em_inline_filter_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ parent_class = (CamelMimeFilterClass *)camel_mime_filter_get_type();
+
+ type = camel_type_register(camel_mime_filter_get_type(),
+ "EMInlineFilter",
+ sizeof (EMInlineFilter),
+ sizeof (EMInlineFilterClass),
+ (CamelObjectClassInitFunc) em_inline_filter_class_init,
+ NULL,
+ (CamelObjectInitFunc) em_inline_filter_init,
+ (CamelObjectFinalizeFunc) em_inline_filter_finalize);
+ }
+
+ return type;
+}
+
+static void
+em_inline_filter_class_init (EMInlineFilterClass *klass)
+{
+ ((CamelMimeFilterClass *)klass)->filter = emif_filter;
+ ((CamelMimeFilterClass *)klass)->complete = emif_complete;
+ ((CamelMimeFilterClass *)klass)->reset = emif_reset;
+}
+
+static void
+em_inline_filter_init (CamelObject *object)
+{
+ EMInlineFilter *emif = (EMInlineFilter *)object;
+
+ emif->data = g_byte_array_new();
+}
+
+
+static void
+em_inline_filter_finalize (CamelObject *object)
+{
+ EMInlineFilter *emif = (EMInlineFilter *)object;
+
+ emif_reset((CamelMimeFilter *)emif);
+ g_byte_array_free(emif->data, TRUE);
+ g_free(emif->filename);
+}
+
+enum {
+ EMIF_PLAIN,
+ EMIF_UUENC,
+ EMIF_BINHEX,
+ EMIF_POSTSCRIPT,
+ EMIF_PGPSIGNED,
+};
+const struct {
+ const char *name;
+ CamelMimePartEncodingType type;
+} emif_types[] = {
+ { "text/plain", CAMEL_MIME_PART_ENCODING_DEFAULT, },
+ { "application/octet-stream", CAMEL_MIME_PART_ENCODING_UUENCODE, },
+ { "application/mac-binhex40", CAMEL_MIME_PART_ENCODING_7BIT, },
+ { "application/postscript", CAMEL_MIME_PART_ENCODING_7BIT, },
+ { "text/plain", CAMEL_MIME_PART_ENCODING_7BIT, },
+};
+
+static void
+emif_add_part(EMInlineFilter *emif, const char *data, int len)
+{
+ CamelMimePartEncodingType type;
+ CamelStream *mem;
+ CamelDataWrapper *dw;
+ CamelMimePart *part;
+
+ if (emif->state == EMIF_PLAIN)
+ type = emif->base_encoding;
+ else
+ type = emif_types[emif->state].type;
+
+ g_byte_array_append(emif->data, data, len);
+ mem = camel_stream_mem_new_with_byte_array(emif->data);
+ emif->data = g_byte_array_new();
+
+ dw = camel_data_wrapper_new();
+ camel_data_wrapper_construct_from_stream(dw, mem);
+ camel_object_unref(mem);
+ camel_data_wrapper_set_mime_type(dw, emif_types[emif->state].name);
+ dw->encoding = type;
+
+ part = camel_mime_part_new();
+ camel_medium_set_content_object((CamelMedium *)part, dw);
+ camel_mime_part_set_encoding(part, type);
+ camel_object_unref(dw);
+
+ if (emif->filename) {
+ camel_mime_part_set_filename(part, emif->filename);
+ g_free(emif->filename);
+ emif->filename = NULL;
+ }
+
+ emif->parts = g_slist_append(emif->parts, part);
+}
+
+static int
+emif_scan(CamelMimeFilter *f, char *in, size_t len, int final)
+{
+ EMInlineFilter *emif = (EMInlineFilter *)f;
+ char *inptr = in, *inend = in+len;
+ char *data_start = in;
+ char *start = in;
+
+ while (inptr < inend) {
+ start = inptr;
+
+ while (inptr < inend && *inptr != '\n')
+ inptr++;
+
+ if (inptr == inend) {
+ if (!final) {
+ camel_mime_filter_backup(f, start, inend-start);
+ inend = start;
+ }
+ break;
+ }
+
+ *inptr++ = 0;
+
+ switch(emif->state) {
+ case EMIF_PLAIN:
+ /* This could use some funky plugin shit, but this'll do for now */
+ if (strncmp(start, "begin ", 6) == 0
+ && start[6] >= '0' && start[6] <= '7') {
+ int i = 7;
+
+ while (start[i] >='0' && start[i] <='7')
+ i++;
+
+ inptr[-1] = '\n';
+
+ if (start[i++] != ' ')
+ break;
+
+ emif_add_part(emif, data_start, start-data_start);
+ emif->filename = g_strndup(start+i, inptr-start-i-1);
+ data_start = start;
+ emif->state = EMIF_UUENC;
+ } else if (strncmp(start, "(This file must be converted with BinHex 4.0)", 45) == 0) {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_BINHEX;
+ } else if (strncmp(start, "%!PS-Adobe-", 11) == 0) {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_POSTSCRIPT;
+#if 0
+/* This should be hooked in once someone can work out how to handle it.
+ Maybe we need a multipart_gpg_inline_signed or some crap, if it
+ can't be converted to a real multipart/signed */
+ } else if (strncmp(start, "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0) {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, start-data_start);
+ data_start = start;
+ emif->state = EMIF_PGPSIGNED;
+#endif
+ }
+ break;
+ case EMIF_UUENC:
+ if (strcmp(start, "end") == 0) {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ }
+ break;
+ case EMIF_BINHEX:
+ if (inptr > (start+1) && inptr[-2] == ':') {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ }
+ break;
+ case EMIF_POSTSCRIPT:
+ if (strcmp(start, "%%EOF") == 0) {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ }
+ break;
+ case EMIF_PGPSIGNED:
+ /* This is currently a noop - it just turns it into a text part */
+ if (strcmp(start, "-----END PGP SIGNATURE-----") == 0) {
+ inptr[-1] = '\n';
+ emif_add_part(emif, data_start, inptr-data_start);
+ data_start = inptr;
+ emif->state = EMIF_PLAIN;
+ }
+ break;
+ }
+
+ inptr[-1] = '\n';
+ }
+
+ if (final) {
+ emif_add_part(emif, data_start, inend-data_start);
+ } else {
+ g_byte_array_append(emif->data, data_start, inend-data_start);
+ }
+
+ return 0;
+}
+
+static void
+emif_filter(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
+{
+ emif_scan(f, in, len, FALSE);
+
+ *out = in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+static void
+emif_complete(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace)
+{
+ emif_scan(f, in, len, TRUE);
+
+ *out = in;
+ *outlen = len;
+ *outprespace = prespace;
+}
+
+static void
+emif_reset(CamelMimeFilter *f)
+{
+ EMInlineFilter *emif = (EMInlineFilter *)f;
+ GSList *l;
+
+ l = emif->parts;
+ while (l) {
+ GSList *n = l->next;
+
+ camel_object_unref(l->data);
+ g_slist_free_1(l);
+
+ l = n;
+ }
+ emif->parts = NULL;
+ g_byte_array_set_size(emif->data, 0);
+}
+
+/**
+ * em_inline_filter_new:
+ * @base_encoding: The base transfer-encoding of the
+ * raw data being processed.
+ *
+ * Create a filter which will scan a (text) stream for
+ * embedded parts. You can then retrieve the contents
+ * as a CamelMultipart object.
+ *
+ * Return value:
+ **/
+EMInlineFilter *
+em_inline_filter_new(CamelMimePartEncodingType base_encoding)
+{
+ EMInlineFilter *emif;
+
+ emif = (EMInlineFilter *)camel_object_new(em_inline_filter_get_type());
+ emif->base_encoding = base_encoding;
+
+ return emif;
+}
+
+CamelMultipart *
+em_inline_filter_get_multipart(EMInlineFilter *emif)
+{
+ GSList *l = emif->parts;
+ CamelMultipart *mp;
+
+ mp = camel_multipart_new();
+ while (l) {
+ camel_multipart_add_part(mp, l->data);
+ l = l->next;
+ }
+
+ return mp;
+}
diff --git a/mail/em-inline-filter.h b/mail/em-inline-filter.h
new file mode 100644
index 0000000000..8efc8454a5
--- /dev/null
+++ b/mail/em-inline-filter.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef EM_INLINE_FILTER_H
+#define EM_INLINE_FILTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EM_INLINE_FILTER_TYPE (em_inline_filter_get_type ())
+#define EM_INLINE_FILTER(obj) (CAMEL_CHECK_CAST((obj), EM_INLINE_FILTER_TYPE, EMInlineFilter))
+#define EM_INLINE_FILTER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_INLINE_FILTER_TYPE, EMInlineFilterClass))
+#define EM_IS_INLINE_FILTER(o) (CAMEL_CHECK_TYPE((o), EM_INLINE_FILTER_TYPE))
+
+#include <camel/camel-mime-filter.h>
+#include <camel/camel-mime-utils.h>
+
+typedef struct _EMInlineFilter {
+ CamelMimeFilter filter;
+
+ int state;
+
+ CamelMimePartEncodingType base_encoding;
+ GByteArray *data;
+ char *filename;
+ GSList *parts;
+} EMInlineFilter;
+
+typedef struct _EMInlineFilterClass {
+ CamelMimeFilterClass filter_class;
+} EMInlineFilterClass;
+
+CamelType em_inline_filter_get_type(void);
+EMInlineFilter *em_inline_filter_new(CamelMimePartEncodingType base_encoding);
+struct _CamelMultipart *em_inline_filter_get_multipart(EMInlineFilter *emif);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* EM_INLINE_FILTER_H */
diff --git a/mail/em-marshal.list b/mail/em-marshal.list
new file mode 100644
index 0000000000..910bfb1b3d
--- /dev/null
+++ b/mail/em-marshal.list
@@ -0,0 +1 @@
+BOOLEAN:BOXED,POINTER,POINTER
diff --git a/mail/em-message-browser.c b/mail/em-message-browser.c
new file mode 100644
index 0000000000..503ebef9e5
--- /dev/null
+++ b/mail/em-message-browser.c
@@ -0,0 +1,171 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkbutton.h>
+
+#include <camel/camel-folder.h>
+
+#include <bonobo/bonobo-main.h>
+#include <bonobo/bonobo-object.h>
+#include <bonobo/bonobo-window.h>
+#include <bonobo/bonobo-generic-factory.h>
+#include <bonobo/bonobo-ui-component.h>
+#include <bonobo/bonobo-ui-util.h>
+
+#include "em-format-html-display.h"
+#include "em-message-browser.h"
+
+#include "evolution-shell-component-utils.h" /* Pixmap stuff, sigh */
+
+struct _EMMessageBrowserPrivate {
+ GtkWidget *preview; /* container for message display */
+};
+
+static void emmb_set_message(EMFolderView *emfv, const char *uid);
+static void emmb_activate(EMFolderView *emfv, BonoboUIComponent *uic, int state);
+
+static EMFolderViewClass *emmb_parent;
+
+static void
+emmb_init(GObject *o)
+{
+ EMMessageBrowser *emmb = (EMMessageBrowser *)o;
+ struct _EMMessageBrowserPrivate *p;
+
+ p = emmb->priv = g_malloc0(sizeof(struct _EMMessageBrowserPrivate));
+
+ ((EMFolderView *)emmb)->preview_active = TRUE;
+
+ g_slist_free(emmb->view.ui_files);
+ emmb->view.ui_files = g_slist_append(NULL, EVOLUTION_UIDIR "/evolution-mail-message.xml");
+ emmb->view.ui_files = g_slist_append(emmb->view.ui_files, EVOLUTION_UIDIR "/evolution-mail-messagedisplay.xml");
+
+ /* currently: just use a scrolledwindow for preview widget */
+ p->preview = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy((GtkScrolledWindow *)p->preview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type((GtkScrolledWindow *)p->preview, GTK_SHADOW_IN);
+ gtk_widget_show(p->preview);
+
+ gtk_container_add((GtkContainer *)p->preview, (GtkWidget *)emmb->view.preview->formathtml.html);
+ gtk_widget_show((GtkWidget *)emmb->view.preview->formathtml.html);
+
+ gtk_widget_show(p->preview);
+
+ gtk_box_pack_start_defaults((GtkBox *)emmb, p->preview);
+}
+
+static void
+emmb_finalise(GObject *o)
+{
+ EMMessageBrowser *emmb = (EMMessageBrowser *)o;
+
+ g_free(emmb->priv);
+ ((GObjectClass *)emmb_parent)->finalize(o);
+}
+
+static void
+emmb_class_init(GObjectClass *klass)
+{
+ klass->finalize = emmb_finalise;
+ ((EMFolderViewClass *)klass)->set_message = emmb_set_message;
+ ((EMFolderViewClass *)klass)->activate = emmb_activate;
+}
+
+GType
+em_message_browser_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMMessageBrowserClass),
+ NULL, NULL,
+ (GClassInitFunc)emmb_class_init,
+ NULL, NULL,
+ sizeof(EMMessageBrowser), 0,
+ (GInstanceInitFunc)emmb_init
+ };
+ emmb_parent = g_type_class_ref(em_folder_view_get_type());
+ type = g_type_register_static(em_folder_view_get_type(), "EMMessageBrowser", &info, 0);
+ }
+
+ return type;
+}
+
+GtkWidget *em_message_browser_new(void)
+{
+ EMMessageBrowser *emmb = g_object_new(em_message_browser_get_type(), 0);
+
+ return (GtkWidget *)emmb;
+}
+
+GtkWidget *em_message_browser_window_new(void)
+{
+ EMMessageBrowser *emmb;
+ BonoboUIContainer *uicont;
+ BonoboUIComponent *uic;
+
+ emmb = (EMMessageBrowser *)em_message_browser_new();
+ gtk_widget_show((GtkWidget *)emmb);
+ /* FIXME: title set elsewhere? */
+ emmb->window = g_object_new(bonobo_window_get_type(), "title", "Ximian Evolution", NULL);
+ bonobo_window_set_contents((BonoboWindow *)emmb->window, (GtkWidget *)emmb);
+
+ uicont = bonobo_window_get_ui_container((BonoboWindow *)emmb->window);
+ uic = bonobo_ui_component_new_default();
+ bonobo_ui_component_set_container(uic, BONOBO_OBJREF(uicont), NULL);
+
+ em_folder_view_activate((EMFolderView *)emmb, uic, TRUE);
+
+ /* FIXME: keep track of size changes for next instantation */
+ gtk_window_set_default_size((GtkWindow *)emmb->window, 600, 400);
+
+ /* cleanup? */
+
+ return (GtkWidget *)emmb;
+}
+
+/* ********************************************************************** */
+
+static void
+emmb_set_message(EMFolderView *emfv, const char *uid)
+{
+ emmb_parent->set_message(emfv, uid);
+
+ /* Well we don't know if it got displayed (yet) ... but whatever ... */
+ camel_folder_set_message_flags(emfv->folder, uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
+}
+
+static void
+emmb_activate(EMFolderView *emfv, BonoboUIComponent *uic, int state)
+{
+ emmb_parent->activate(emfv, uic, state);
+
+ if (state)
+ bonobo_ui_component_set_prop(uic, "/commands/EditPaste", "sensitive", "0", NULL);
+}
diff --git a/mail/em-message-browser.h b/mail/em-message-browser.h
new file mode 100644
index 0000000000..c36a87ee0b
--- /dev/null
+++ b/mail/em-message-browser.h
@@ -0,0 +1,30 @@
+
+#ifndef _EM_MESSAGE_BROWSER_H
+#define _EM_MESSAGE_BROWSER_H
+
+#include "em-folder-view.h"
+
+typedef struct _EMMessageBrowser EMMessageBrowser;
+typedef struct _EMMessageBrowserClass EMMessageBrowserClass;
+
+struct _EMMessageBrowser {
+ EMFolderView view;
+
+ /* container, if setup */
+ struct _GtkWidget *window;
+
+ struct _EMMessageBrowserPrivate *priv;
+};
+
+struct _EMMessageBrowserClass {
+ EMFolderViewClass parent_class;
+};
+
+GType em_message_browser_get_type(void);
+
+GtkWidget *em_message_browser_new(void);
+
+/* also sets up a bonobo container window w/ docks and so on */
+GtkWidget *em_message_browser_window_new(void);
+
+#endif /* ! _EM_MESSAGE_BROWSER_H */
diff --git a/mail/em-popup.c b/mail/em-popup.c
new file mode 100644
index 0000000000..d2b2ca1e6e
--- /dev/null
+++ b/mail/em-popup.c
@@ -0,0 +1,823 @@
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkcheckmenuitem.h>
+#include <gtk/gtkradiomenuitem.h>
+#include <gtk/gtkseparatormenuitem.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkimage.h>
+
+#include <libgnome/gnome-url.h>
+
+#include <glib.h>
+
+#include "em-popup.h"
+#include "e-util/e-msgport.h"
+#include "em-utils.h"
+
+#include <camel/camel-folder.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-string-utils.h>
+
+static void emp_standard_menu_factory(EMPopup *emp, EMPopupTarget *target, void *data);
+
+struct _EMPopupFactory {
+ struct _EMPopupFactory *next, *prev;
+
+ char *menuid;
+ EMPopupFactoryFunc factory;
+ void *factory_data;
+};
+
+struct _menu_node {
+ struct _menu_node *next, *prev;
+
+ GSList *menu;
+ GDestroyNotify freefunc;
+};
+
+struct _EMPopupPrivate {
+ EDList menus;
+};
+
+static EDList emp_factories = E_DLIST_INITIALISER(emp_factories);
+
+static GObjectClass *emp_parent;
+
+static void
+emp_init(GObject *o)
+{
+ EMPopup *emp = (EMPopup *)o;
+ struct _EMPopupPrivate *p;
+
+ p = emp->priv = g_malloc0(sizeof(struct _EMPopupPrivate));
+
+ e_dlist_init(&p->menus);
+}
+
+static void
+emp_finalise(GObject *o)
+{
+ EMPopup *emp = (EMPopup *)o;
+ struct _EMPopupPrivate *p = emp->priv;
+ struct _menu_node *mnode, *nnode;
+
+ g_free(emp->menuid);
+
+ mnode = (struct _menu_node *)p->menus.head;
+ nnode = mnode->next;
+ while (nnode) {
+ if (mnode->freefunc)
+ mnode->freefunc(mnode->menu);
+
+ g_free(mnode);
+ mnode = nnode;
+ nnode = nnode->next;
+ }
+
+ g_free(p);
+
+ ((GObjectClass *)emp_parent)->finalize(o);
+}
+
+static void
+emp_class_init(GObjectClass *klass)
+{
+ klass->finalize = emp_finalise;
+}
+
+GType
+em_popup_get_type(void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(EMPopupClass),
+ NULL, NULL,
+ (GClassInitFunc)emp_class_init,
+ NULL, NULL,
+ sizeof(EMPopup), 0,
+ (GInstanceInitFunc)emp_init
+ };
+ emp_parent = g_type_class_ref(G_TYPE_OBJECT);
+ type = g_type_register_static(G_TYPE_OBJECT, "EMPopup", &info, 0);
+
+ /* FIXME: this should probably sit somewhere in global setup */
+ em_popup_static_add_factory(NULL, (EMPopupFactoryFunc)emp_standard_menu_factory, NULL);
+ }
+
+ return type;
+}
+
+EMPopup *em_popup_new(const char *menuid)
+{
+ EMPopup *emp = g_object_new(em_popup_get_type(), 0);
+
+ emp->menuid = g_strdup(menuid);
+
+ return emp;
+}
+
+/**
+ * em_popup_add_items:
+ * @emp:
+ * @items:
+ * @freefunc:
+ *
+ * Add new EMPopupItems to the menu's. Any with the same path
+ * will override previously defined menu items, at menu building
+ * time.
+ **/
+void
+em_popup_add_items(EMPopup *emp, GSList *items, GDestroyNotify freefunc)
+{
+ struct _menu_node *node;
+
+ node = g_malloc(sizeof(*node));
+ node->menu = items;
+ node->freefunc = freefunc;
+ e_dlist_addtail(&emp->priv->menus, (EDListNode *)node);
+}
+
+/**
+ * em_popup_add_static_items:
+ * @emp:
+ * @target: Target of this menu.
+ *
+ * Will load up any matching menu items from an installed
+ * popup factory. If the menuid of @emp is NULL, then this
+ * has no effect.
+ *
+ **/
+void
+em_popup_add_static_items(EMPopup *emp, EMPopupTarget *target)
+{
+ struct _EMPopupFactory *f;
+
+ if (emp->menuid == NULL || target == NULL)
+ return;
+
+ /* setup the menu itself */
+ f = (struct _EMPopupFactory *)emp_factories.head;
+ while (f->next) {
+ if (f->menuid == NULL
+ || !strcmp(f->menuid, emp->menuid)) {
+ f->factory(emp, target, f->factory_data);
+ }
+ f = f->next;
+ }
+}
+
+static int
+emp_cmp(const void *ap, const void *bp)
+{
+ struct _EMPopupItem *a = *((void **)ap);
+ struct _EMPopupItem *b = *((void **)bp);
+
+ return strcmp(a->path, b->path);
+}
+
+/**
+ * em_popup_create:
+ * @menuitems:
+ * @hide_mask: used to hide menu items, not sure of it's utility,
+ * since you could just 'not add them' in the first place. Saves
+ * copying logic anyway.
+ * @disable_mask: used to disable menu items.
+ *
+ * TEMPORARY code to create a menu from a list of items.
+ *
+ * The menu items are merged based on their path element, and
+ * built into a menu tree.
+ *
+ * Return value:
+ **/
+GtkMenu *
+em_popup_create_menu(EMPopup *emp, guint32 hide_mask, guint32 disable_mask)
+{
+ struct _EMPopupPrivate *p = emp->priv;
+ struct _menu_node *mnode, *nnode;
+ GPtrArray *items = g_ptr_array_new();
+ GSList *l;
+ GString *ppath = g_string_new("");
+ GtkMenu *topmenu;
+ GHashTable *menu_hash = g_hash_table_new(g_str_hash, g_str_equal),
+ *group_hash = g_hash_table_new(g_str_hash, g_str_equal);
+ /*char *domain = NULL;*/
+ int i;
+
+ /* FIXME: need to override old ones with new names */
+ mnode = (struct _menu_node *)p->menus.head;
+ nnode = mnode->next;
+ while (nnode) {
+ for (l=mnode->menu; l; l = l->next)
+ g_ptr_array_add(items, l->data);
+ mnode = nnode;
+ nnode = nnode->next;
+ }
+
+ qsort(items->pdata, items->len, sizeof(items->pdata[0]), emp_cmp);
+
+ topmenu = (GtkMenu *)gtk_menu_new();
+ for (i=0;i<items->len;i++) {
+ GtkWidget *label;
+ struct _EMPopupItem *item = items->pdata[i];
+ GtkMenu *thismenu;
+ GtkMenuItem *menuitem;
+ char *tmp;
+
+ /* for bar's, the mask is exclusive or */
+ if (item->mask) {
+ if ((item->type & EM_POPUP_TYPE_MASK) == EM_POPUP_BAR) {
+ if ((item->mask & hide_mask) == item->mask)
+ continue;
+ } else if (item->mask & hide_mask)
+ continue;
+ }
+
+ g_string_truncate(ppath, 0);
+ tmp = strrchr(item->path, '/');
+ if (tmp) {
+ g_string_append_len(ppath, item->path, tmp-item->path);
+ thismenu = g_hash_table_lookup(menu_hash, ppath->str);
+ g_assert(thismenu != NULL);
+ } else {
+ thismenu = topmenu;
+ }
+
+ switch (item->type & EM_POPUP_TYPE_MASK) {
+ case EM_POPUP_ITEM:
+ if (item->image) {
+ char *path;
+ GtkWidget *image;
+
+ path = g_build_filename(EVOLUTION_IMAGES, (char *)item->image, NULL);
+ image = gtk_image_new_from_file(path);
+ g_free(path);
+
+ gtk_widget_show(image);
+ menuitem = (GtkMenuItem *)gtk_image_menu_item_new();
+ gtk_image_menu_item_set_image((GtkImageMenuItem *)menuitem, image);
+ } else {
+ menuitem = (GtkMenuItem *)gtk_menu_item_new();
+ }
+ break;
+ case EM_POPUP_TOGGLE:
+ menuitem = (GtkMenuItem *)gtk_check_menu_item_new();
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *)menuitem, item->type & EM_POPUP_ACTIVE);
+ break;
+ case EM_POPUP_RADIO:
+ menuitem = (GtkMenuItem *)gtk_radio_menu_item_new(g_hash_table_lookup(group_hash, ppath->str));
+ g_hash_table_insert(group_hash, ppath->str, gtk_radio_menu_item_get_group((GtkRadioMenuItem *)menuitem));
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *)menuitem, item->type & EM_POPUP_ACTIVE);
+ break;
+ case EM_POPUP_IMAGE:
+ menuitem = (GtkMenuItem *)gtk_image_menu_item_new();
+ gtk_image_menu_item_set_image((GtkImageMenuItem *)menuitem, item->image);
+ break;
+ case EM_POPUP_SUBMENU: {
+ GtkMenu *submenu = (GtkMenu *)gtk_menu_new();
+
+ g_hash_table_insert(menu_hash, item->path, submenu);
+ menuitem = (GtkMenuItem *)gtk_menu_item_new();
+ gtk_menu_item_set_submenu(menuitem, (GtkWidget *)submenu);
+ break; }
+ case EM_POPUP_BAR:
+ /* TODO: double-bar, end-bar stuff? */
+ menuitem = (GtkMenuItem *)gtk_separator_menu_item_new();
+ break;
+ default:
+ continue;
+ }
+
+ if (item->label) {
+ label = gtk_label_new_with_mnemonic(item->label);
+ gtk_misc_set_alignment((GtkMisc *)label, 0.0, 0.5);
+ gtk_widget_show(label);
+ gtk_container_add((GtkContainer *)menuitem, label);
+ }
+
+ if (item->activate)
+ g_signal_connect(menuitem, "activate", item->activate, item->activate_data);
+
+ gtk_menu_shell_append((GtkMenuShell *)thismenu, (GtkWidget *)menuitem);
+
+ if (item->mask & disable_mask)
+ gtk_widget_set_sensitive((GtkWidget *)menuitem, FALSE);
+
+ gtk_widget_show((GtkWidget *)menuitem);
+ }
+
+ g_string_free(ppath, TRUE);
+ g_ptr_array_free(items, TRUE);
+ g_hash_table_destroy(menu_hash);
+ g_hash_table_destroy(group_hash);
+
+ return topmenu;
+}
+
+static void
+emp_popup_done(GtkWidget *w, EMPopup *emp)
+{
+ gtk_widget_destroy(w);
+ g_object_unref(emp);
+}
+
+/**
+ * em_popup_create_menu_once:
+ * @emp: EMPopup, once the menu is shown, this cannot be
+ * considered a valid pointer.
+ * @target: If set, the target of the selection. Static menu
+ * items will be added. The target will be freed once complete.
+ * @hide_mask:
+ * @disable_mask:
+ *
+ * Like popup_create_menu, but automatically sets up the menu
+ * so that it is destroyed once a selection takes place, and
+ * the EMPopup is unreffed.
+ *
+ * Return value: A menu, to popup.
+ **/
+GtkMenu *
+em_popup_create_menu_once(EMPopup *emp, EMPopupTarget *target, guint32 hide_mask, guint32 disable_mask)
+{
+ GtkMenu *menu;
+
+ if (target)
+ em_popup_add_static_items(emp, target);
+
+ menu = em_popup_create_menu(emp, hide_mask, disable_mask);
+
+ g_signal_connect_swapped(menu, "selection_done", G_CALLBACK(em_popup_target_free), target);
+ g_signal_connect(menu, "selection_done", G_CALLBACK(emp_popup_done), emp);
+
+ return menu;
+}
+
+/* ********************************************************************** */
+
+/**
+ * em_popup_static_add_factory:
+ * @menuid:
+ * @func:
+ * @data:
+ *
+ * Add a popup factory which will be called to add_items() any
+ * extra menu's if wants to do the current PopupTarget.
+ *
+ * TODO: Make the menuid a pattern?
+ *
+ * Return value: A handle to the factory.
+ **/
+EMPopupFactory *
+em_popup_static_add_factory(const char *menuid, EMPopupFactoryFunc func, void *data)
+{
+ struct _EMPopupFactory *f = g_malloc0(sizeof(*f));
+
+ f->menuid = g_strdup(menuid);
+ f->factory = func;
+ f->factory_data = data;
+ e_dlist_addtail(&emp_factories, (EDListNode *)f);
+
+ return f;
+}
+
+/**
+ * em_popup_static_remove_factory:
+ * @f:
+ *
+ * Remove a popup factory.
+ **/
+void
+em_popup_static_remove_factory(EMPopupFactory *f)
+{
+ e_dlist_remove((EDListNode *)f);
+ g_free(f->menuid);
+ g_free(f);
+}
+
+/**
+ * em_popup_target_new_select:
+ * @folder: The selection will ref this for the life of it.
+ * @folder_uri:
+ * @uids: The selection will free this when done with it.
+ *
+ * Create a new selection popup target.
+ *
+ * Return value:
+ **/
+EMPopupTarget *
+em_popup_target_new_select(struct _CamelFolder *folder, const char *folder_uri, GPtrArray *uids)
+{
+ EMPopupTarget *t = g_malloc0(sizeof(*t));
+ guint32 mask = ~0;
+ int i;
+ const char *tmp;
+
+ t->type = EM_POPUP_TARGET_SELECT;
+ t->data.select.uids = uids;
+ t->data.select.folder = folder;
+ camel_object_ref(folder);
+ t->data.select.folder_uri = g_strdup(folder_uri);
+
+ if (em_utils_folder_is_sent(folder, folder_uri))
+ mask &= ~EM_POPUP_SELECT_RESEND;
+
+ if (!(em_utils_folder_is_drafts(folder, folder_uri)
+ || em_utils_folder_is_outbox(folder, folder_uri)))
+ mask &= ~EM_POPUP_SELECT_ADD_SENDER;
+
+ if (uids->len == 1)
+ mask &= ~EM_POPUP_SELECT_ONE;
+
+ if (uids->len >= 1)
+ mask &= ~EM_POPUP_SELECT_MANY;
+
+ for (i = 0; i < uids->len; i++) {
+ CamelMessageInfo *info = camel_folder_get_message_info(folder, uids->pdata[i]);
+
+ if (info == NULL)
+ continue;
+
+ if (info->flags & CAMEL_MESSAGE_SEEN)
+ mask &= ~EM_POPUP_SELECT_MARK_UNREAD;
+ else
+ mask &= ~EM_POPUP_SELECT_MARK_READ;
+
+ if (info->flags & CAMEL_MESSAGE_DELETED)
+ mask &= ~EM_POPUP_SELECT_UNDELETE;
+ else
+ mask &= ~EM_POPUP_SELECT_DELETE;
+
+ if (info->flags & CAMEL_MESSAGE_FLAGGED)
+ mask &= ~EM_POPUP_SELECT_MARK_UNIMPORTANT;
+ else
+ mask &= ~EM_POPUP_SELECT_MARK_IMPORTANT;
+
+ tmp = camel_tag_get (&info->user_tags, "follow-up");
+ if (tmp && *tmp) {
+ mask &= ~EM_POPUP_SELECT_FLAG_CLEAR;
+ tmp = camel_tag_get(&info->user_tags, "completed-on");
+ if (tmp == NULL || *tmp == 0)
+ mask &= ~EM_POPUP_SELECT_FLAG_COMPLETED;
+ } else
+ mask &= ~EM_POPUP_SELECT_FLAG_FOLLOWUP;
+
+ if (i == 0 && uids->len == 1
+ && (tmp = camel_message_info_mlist(info))
+ && tmp[0] != 0)
+ mask &= ~EM_POPUP_SELECT_MAILING_LIST;
+
+ camel_folder_free_message_info(folder, info);
+ }
+
+ t->mask = mask;
+
+ return t;
+}
+
+EMPopupTarget *
+em_popup_target_new_uri(const char *uri)
+{
+ EMPopupTarget *t = g_malloc0(sizeof(*t));
+ guint32 mask = ~0;
+
+ t->type = EM_POPUP_TARGET_URI;
+ t->data.uri = g_strdup(uri);
+
+ if (g_ascii_strncasecmp(uri, "http:", 5) == 0
+ || g_ascii_strncasecmp(uri, "https:", 6) == 0)
+ mask &= ~EM_POPUP_URI_HTTP;
+ if (g_ascii_strncasecmp(uri, "mailto:", 7) == 0)
+ mask &= ~EM_POPUP_URI_MAILTO;
+ else
+ mask &= ~EM_POPUP_URI_NOT_MAILTO;
+
+ t->mask = mask;
+
+ return t;
+}
+
+EMPopupTarget *
+em_popup_target_new_part(struct _CamelMimePart *part, const char *mime_type)
+{
+ EMPopupTarget *t = g_malloc0(sizeof(*t));
+ guint32 mask = ~0;
+
+ t->type = EM_POPUP_TARGET_PART;
+ t->data.part.part = part;
+ camel_object_ref(part);
+ if (mime_type)
+ t->data.part.mime_type = g_strdup(mime_type);
+ else
+ t->data.part.mime_type = camel_data_wrapper_get_mime_type((CamelDataWrapper *)part);
+
+ camel_strdown(t->data.part.mime_type);
+
+ if (CAMEL_IS_MIME_MESSAGE(camel_medium_get_content_object((CamelMedium *)part)))
+ mask &= ~EM_POPUP_PART_MESSAGE;
+
+ if (strncmp(t->data.part.mime_type, "image/", 6) == 0)
+ mask &= ~EM_POPUP_PART_IMAGE;
+
+ t->mask = mask;
+
+ return t;
+}
+
+void
+em_popup_target_free(EMPopupTarget *t)
+{
+ switch (t->type) {
+ case EM_POPUP_TARGET_SELECT:
+ camel_object_unref(t->data.select.folder);
+ g_free(t->data.select.folder_uri);
+ if (t->data.select.uids)
+ em_utils_uids_free(t->data.select.uids);
+ break;
+ case EM_POPUP_TARGET_URI:
+ g_free(t->data.uri);
+ break;
+ case EM_POPUP_TARGET_PART:
+ camel_object_unref(t->data.part.part);
+ g_free(t->data.part.mime_type);
+ break;
+ }
+
+ g_free(t);
+}
+
+/* ********************************************************************** */
+
+#if 0
+/* TODO: flesh these out where possible */
+static void
+emp_popup_open(GtkWidget *w, EMFolderView *emfv)
+{
+ em_folder_view_open_selected(emfv);
+}
+
+static void
+emp_popup_resend(GtkWidget *w, EMPopupTarget *t)
+{
+ if (!em_utils_check_user_can_send_mail(t->widget))
+ return;
+
+ em_utils_edit_messages(t->widget, t->data.select.folder, em_utils_uids_copy(t->data.select.uids));
+}
+
+static void
+emp_popup_saveas(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_save_messages(t->widget, t->data.select.folder, em_utils_uids_copy(t->data.select.uids));
+}
+
+static EMPopupItem emp_standard_select_popups[] = {
+ /*{ EM_POPUP_ITEM, "00.select.00", N_("_Open"), G_CALLBACK(emp_popup_open), NULL, NULL, 0 },*/
+ { EM_POPUP_ITEM, "00.select.01", N_("_Edit as New Message..."), G_CALLBACK(emp_popup_resend), NULL, NULL, EM_POPUP_SELECT_RESEND },
+ { EM_POPUP_ITEM, "00.select.02", N_("_Save As..."), G_CALLBACK(emp_popup_saveas), NULL, "save-as-16.png", 0 },
+};
+#endif
+
+/* ********************************************************************** */
+
+static void
+emp_part_popup_saveas(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_save_part(w, _("Save As..."), t->data.part.part);
+}
+
+static void
+emp_part_popup_set_background(GtkWidget *w, EMPopupTarget *t)
+{
+ /* set as background ... */
+ printf("UNIMPLEMENTED: set background, but it would be cool, no?\n");
+}
+
+static void
+emp_part_popup_reply_sender(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_reply_to_message(t->widget,
+ (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part),
+ REPLY_MODE_SENDER);
+}
+
+static void
+emp_part_popup_reply_list(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_reply_to_message(t->widget,
+ (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part),
+ REPLY_MODE_LIST);
+}
+
+static void
+emp_part_popup_reply_all(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_reply_to_message(t->widget,
+ (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part),
+ REPLY_MODE_ALL);
+}
+
+static void
+emp_part_popup_forward(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_forward_message(t->widget,
+ (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part));
+}
+
+static EMPopupItem emp_standard_object_popups[] = {
+ { EM_POPUP_ITEM, "00.part.00", N_("_Save As..."), G_CALLBACK(emp_part_popup_saveas), NULL, "save-as-16.png", 0 },
+ { EM_POPUP_ITEM, "00.part.10", N_("Set as _Background"), G_CALLBACK(emp_part_popup_set_background), NULL, NULL, EM_POPUP_PART_IMAGE },
+ { EM_POPUP_BAR, "10.part", NULL, NULL, NULL, NULL, EM_POPUP_PART_MESSAGE },
+ { EM_POPUP_ITEM, "10.part.00", N_("_Reply to sender"), G_CALLBACK(emp_part_popup_reply_sender), NULL, "reply.xpm" , EM_POPUP_PART_MESSAGE },
+ { EM_POPUP_ITEM, "10.part.01", N_("Reply to _List"), G_CALLBACK(emp_part_popup_reply_list), NULL, NULL, EM_POPUP_PART_MESSAGE},
+ { EM_POPUP_ITEM, "10.part.03", N_("Reply to _All"), G_CALLBACK(emp_part_popup_reply_all), NULL, "reply_to_all.xpm", EM_POPUP_PART_MESSAGE},
+ { EM_POPUP_BAR, "20.part", NULL, NULL, NULL, NULL, EM_POPUP_PART_MESSAGE },
+ { EM_POPUP_ITEM, "20.part.00", N_("_Forward"), G_CALLBACK(emp_part_popup_forward), NULL, "forward.xpm", EM_POPUP_PART_MESSAGE },
+
+};
+
+static const EMPopupItem emp_standard_part_apps_bar = { EM_POPUP_BAR, "99.object" };
+
+/* ********************************************************************** */
+
+static void
+emp_uri_popup_link_open(GtkWidget *w, EMPopupTarget *t)
+{
+ GError *err = NULL;
+
+ gnome_url_show(t->data.uri, &err);
+ if (err) {
+ g_warning("gnome_url_show: %s", err->message);
+ g_error_free(err);
+ }
+}
+
+static void
+emp_uri_popup_link_copy(GtkWidget *w, EMPopupTarget *t)
+{
+#if 0
+ g_free(p->selection_uri);
+ p->selection_uri = g_strdup(t->data.uri);
+
+ gtk_selection_owner_set(p->invisible, GDK_SELECTION_PRIMARY, gtk_get_current_event_time());
+ gtk_selection_owner_set(p->invisible, GDK_SELECTION_CLIPBOARD, gtk_get_current_event_time());
+#endif
+}
+
+static void
+emp_uri_popup_address_send(GtkWidget *w, EMPopupTarget *t)
+{
+ em_utils_compose_new_message_with_mailto(t->widget, t->data.uri);
+}
+
+static void
+emp_uri_popup_address_add(GtkWidget *w, EMPopupTarget *t)
+{
+ printf("UNIMPLEMENTED: Add address '%s'\n", t->data.uri);
+}
+
+static EMPopupItem emp_standard_uri_popups[] = {
+ { EM_POPUP_ITEM, "00.uri.00", N_("_Open Link in Browser"), G_CALLBACK(emp_uri_popup_link_open), NULL, NULL, EM_POPUP_URI_NOT_MAILTO },
+ { EM_POPUP_ITEM, "00.uri.01", N_("_Copy Link Location"), G_CALLBACK(emp_uri_popup_link_copy), NULL, NULL, EM_POPUP_URI_NOT_MAILTO },
+ { EM_POPUP_ITEM, "00.uri.10", N_("Se_nd message to..."), G_CALLBACK(emp_uri_popup_address_send), NULL, NULL, EM_POPUP_URI_MAILTO },
+ { EM_POPUP_ITEM, "00.uri.20", N_("_Add to Addressbook"), G_CALLBACK(emp_uri_popup_address_add), NULL, NULL, EM_POPUP_URI_MAILTO },
+};
+
+/* ********************************************************************** */
+
+#define LEN(x) (sizeof(x)/sizeof(x[0]))
+
+#include <libgnomevfs/gnome-vfs-mime-handlers.h>
+
+struct _open_in_item {
+ EMPopupItem item;
+ EMPopupTarget *target;
+ GnomeVFSMimeApplication *app;
+};
+
+static void
+emp_apps_open_in(GtkWidget *w, struct _open_in_item *item)
+{
+ char *path;
+
+ path = em_utils_temp_save_part(item->target->widget, item->target->data.part.part);
+ if (path) {
+ char *command;
+ int douri = (item->app->expects_uris == GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS);
+
+ command = g_strdup_printf(douri?"%s file://%s &":"%s %s &", item->app->command, path);
+
+ /* FIXME: Do not use system here */
+ system(command);
+ g_free(command);
+ g_free(path);
+ }
+}
+
+static void
+emp_apps_popup_free(GSList *free_list)
+{
+ while (free_list) {
+ GSList *n = free_list->next;
+ struct _open_in_item *item = free_list->data;
+
+ g_free(item->item.path);
+ g_free(item->item.label);
+ g_free(item);
+ g_slist_free_1(free_list);
+
+ free_list = n;
+ }
+}
+
+static void
+emp_standard_menu_factory(EMPopup *emp, EMPopupTarget *target, void *data)
+{
+ int i, len;
+ EMPopupItem *items;
+ GSList *menus = NULL;
+
+ switch (target->type) {
+ case EM_POPUP_TARGET_SELECT:
+ return;
+#if 0
+ items = emp_standard_select_popups;
+ len = LEN(emp_standard_select_popups);
+ break;
+#endif
+ case EM_POPUP_TARGET_URI:
+ items = emp_standard_uri_popups;
+ len = LEN(emp_standard_uri_popups);
+ break;
+ case EM_POPUP_TARGET_PART: {
+ GList *apps = gnome_vfs_mime_get_short_list_applications(target->data.part.mime_type);
+
+ /* FIXME: use the snoop_part stuff from em-format.c */
+ if (apps == NULL && strcmp(target->data.part.mime_type, "application/octet-stream") == 0) {
+ const char *filename = camel_mime_part_get_filename(target->data.part.part), *name_type;
+
+ if (filename) {
+ /* GNOME-VFS will misidentify TNEF attachments as MPEG */
+ if (!strcmp (filename, "winmail.dat"))
+ name_type = "application/vnd.ms-tnef";
+ else
+ name_type = gnome_vfs_mime_type_from_name(filename);
+ if (name_type)
+ apps = gnome_vfs_mime_get_short_list_applications(name_type);
+ }
+ }
+
+ if (apps) {
+ GString *label = g_string_new("");
+ GSList *open_menus = NULL;
+ GList *l;
+
+ menus = g_slist_prepend(menus, (void *)&emp_standard_part_apps_bar);
+
+ for (l=apps;l;l=l->next) {
+ GnomeVFSMimeApplication *app = l->data;
+ struct _open_in_item *item;
+
+ if (app->requires_terminal)
+ continue;
+
+ item = g_malloc0(sizeof(*item));
+ item->item.type = EM_POPUP_ITEM;
+ item->item.path = g_strdup_printf("99.object.%02d", i);
+ item->item.label = g_strdup_printf(_("Open in %s..."), app->name);
+ item->item.activate = G_CALLBACK(emp_apps_open_in);
+ item->item.activate_data = item;
+ item->target = target;
+ item->app = app;
+
+ open_menus = g_slist_prepend(open_menus, item);
+ }
+
+ if (open_menus)
+ em_popup_add_items(emp, open_menus, (GDestroyNotify)emp_apps_popup_free);
+
+ g_string_free(label, TRUE);
+ g_list_free(apps);
+ }
+
+ items = emp_standard_object_popups;
+ len = LEN(emp_standard_object_popups);
+ break; }
+ }
+
+ for (i=0;i<len;i++) {
+ if ((items[i].mask & target->mask) == 0) {
+ items[i].activate_data = target;
+ menus = g_slist_prepend(menus, &items[i]);
+ }
+ }
+
+ if (menus)
+ em_popup_add_items(emp, menus, (GDestroyNotify)g_slist_free);
+}
diff --git a/mail/em-popup.h b/mail/em-popup.h
new file mode 100644
index 0000000000..877b28fc28
--- /dev/null
+++ b/mail/em-popup.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michel Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __EM_POPUP_H__
+#define __EM_POPUP_H__
+
+#include <glib-object.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+/* NB: This is TEMPORARY, to be replaced by EggMenu, if it does what we need? */
+
+typedef struct _EMPopup EMPopup;
+typedef struct _EMPopupClass EMPopupClass;
+
+typedef struct _EMPopupItem EMPopupItem;
+typedef struct _EMPopupFactory EMPopupFactory; /* anonymous type */
+typedef struct _EMPopupTarget EMPopupTarget;
+
+typedef void (*EMPopupFactoryFunc)(EMPopup *emp, EMPopupTarget *target, void *data);
+
+/* Menu item descriptions */
+enum _em_popup_t {
+ EM_POPUP_ITEM = 0,
+ EM_POPUP_TOGGLE,
+ EM_POPUP_RADIO,
+ EM_POPUP_IMAGE,
+ EM_POPUP_SUBMENU,
+ EM_POPUP_BAR,
+ EM_POPUP_TYPE_MASK = 0xffff,
+ EM_POPUP_ACTIVE = 0x10000,
+};
+
+struct _EMPopupItem {
+ enum _em_popup_t type;
+ char *path; /* absolute path! must sort ascii-lexographically into the right spot */
+ char *label;
+ GCallback activate;
+ void *activate_data;
+ void *image; /* char* for item type, GtkWidget * for image type */
+ guint32 mask;
+};
+
+/* Current target description */
+/* Types of popup tagets */
+enum _em_popup_target_t {
+ EM_POPUP_TARGET_SELECT,
+ EM_POPUP_TARGET_URI,
+ EM_POPUP_TARGET_PART,
+};
+
+/* Flags that describe a TARGET_SELECT */
+enum {
+ EM_POPUP_SELECT_ONE = 1<<1,
+ EM_POPUP_SELECT_MANY = 1<<2,
+ EM_POPUP_SELECT_MARK_READ = 1<<3,
+ EM_POPUP_SELECT_MARK_UNREAD = 1<<4,
+ EM_POPUP_SELECT_DELETE = 1<<5,
+ EM_POPUP_SELECT_UNDELETE = 1<<6,
+ EM_POPUP_SELECT_MAILING_LIST = 1<<7,
+ EM_POPUP_SELECT_RESEND = 1<<8,
+ EM_POPUP_SELECT_MARK_IMPORTANT = 1<<9,
+ EM_POPUP_SELECT_MARK_UNIMPORTANT = 1<<10,
+ EM_POPUP_SELECT_FLAG_FOLLOWUP = 1<<11,
+ EM_POPUP_SELECT_FLAG_COMPLETED = 1<<12,
+ EM_POPUP_SELECT_FLAG_CLEAR = 1<<13,
+ EM_POPUP_SELECT_ADD_SENDER = 1<<14,
+ EM_POPUP_SELECT_LAST = 1<<16 /* reserve 2 slots */
+};
+
+/* Flags that describe a TARGET_URI */
+enum {
+ EM_POPUP_URI_HTTP = 1<<0,
+ EM_POPUP_URI_MAILTO = 1<<1,
+ EM_POPUP_URI_NOT_MAILTO = 1<<2,
+};
+
+/* Flags that describe TARGET_PART */
+enum {
+ EM_POPUP_PART_MESSAGE = 1<<0,
+ EM_POPUP_PART_IMAGE = 1<<1,
+};
+
+struct _EMPopupTarget {
+ enum _em_popup_target_t type;
+ guint32 mask; /* depends on type, see above */
+ struct _GtkWidget *widget; /* used if you need a parent toplevel, if available */
+ union {
+ char *uri;
+ struct {
+ struct _CamelFolder *folder;
+ char *folder_uri;
+ GPtrArray *uids;
+ } select;
+ struct {
+ char *mime_type;
+ struct _CamelMimePart *part;
+ } part;
+ } data;
+};
+
+/* The object */
+struct _EMPopup {
+ GObject object;
+
+ struct _EMPopupPrivate *priv;
+
+ char *menuid;
+};
+
+struct _EMPopupClass {
+ GObjectClass object_class;
+};
+
+GType em_popup_get_type(void);
+
+/* Static class methods */
+EMPopupFactory *em_popup_static_add_factory(const char *menuid, EMPopupFactoryFunc func, void *data);
+void em_popup_static_remove_factory(EMPopupFactory *f);
+
+EMPopup *em_popup_new(const char *menuid);
+void em_popup_add_items(EMPopup *, GSList *items, GDestroyNotify freefunc);
+void em_popup_add_static_items(EMPopup *emp, EMPopupTarget *target);
+struct _GtkMenu *em_popup_create_menu(EMPopup *, guint32 hide_mask, guint32 disable_mask);
+struct _GtkMenu *em_popup_create_menu_once(EMPopup *emp, EMPopupTarget *, guint32 hide_mask, guint32 disable_mask);
+
+EMPopupTarget *em_popup_target_new_uri(const char *uri);
+EMPopupTarget *em_popup_target_new_select(struct _CamelFolder *folder, const char *folder_uri, GPtrArray *uids);
+EMPopupTarget *em_popup_target_new_part(struct _CamelMimePart *part, const char *mime_type);
+void em_popup_target_free(EMPopupTarget *target);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EM_POPUP_H__ */
diff --git a/mail/em-subscribe-editor.c b/mail/em-subscribe-editor.c
new file mode 100644
index 0000000000..e00b79ce8c
--- /dev/null
+++ b/mail/em-subscribe-editor.c
@@ -0,0 +1,849 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * em-subscribe-editor.c *
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pthread.h>
+
+/*#include "evolution-shell-component-utils.h"
+ #include "mail.h"*/
+#include "mail-tools.h"
+#include "mail-ops.h"
+#include "mail-mt.h"
+/*#include "mail-folder-cache.h"*/
+#include "camel/camel-exception.h"
+#include "camel/camel-store.h"
+#include "camel/camel-session.h"
+#include "e-util/e-account-list.h"
+
+#include "em-subscribe-editor.h"
+
+#include "mail-config.h"
+
+#include <glade/glade.h>
+
+#include <gtk/gtkdialog.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreestore.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkcellrenderertoggle.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkoptionmenu.h>
+#include <gtk/gtkprogressbar.h>
+#include <gtk/gtkmenuitem.h>
+
+#define d(x)
+
+typedef struct _EMSubscribeEditor EMSubscribeEditor;
+struct _EMSubscribeEditor {
+ EDList stores;
+
+ int busy;
+ guint busy_id;
+
+ struct _EMSubscribe *current; /* the current one, if any */
+
+ GtkDialog *dialog;
+ GtkWidget *vbox; /* where new stores are added */
+ GtkWidget *optionmenu;
+ GtkWidget *none_selected; /* 'please select a xxx' message */
+ GtkWidget *none_selected_item;
+ GtkWidget *subscribe_button;
+ GtkWidget *unsubscribe_button;
+ GtkWidget *progress;
+};
+
+typedef struct _EMSubscribe EMSubscribe;
+struct _EMSubscribe {
+ struct _EMSubscribe *next;
+ struct _EMSubscribe *prev;
+
+ int ref_count;
+ int cancel;
+
+ struct _EMSubscribeEditor *editor; /* parent object*/
+
+ char *store_uri;
+ int store_id; /* looking up a store */
+
+ CamelStore *store;
+ GHashTable *folders;
+
+ GtkWidget *widget; /* widget to show for this store */
+ GtkTreeView *tree; /* tree, if we have it */
+
+ /* list of all returns from get_folder_info, accessed by other structures */
+ GSList *info_list;
+
+ /* pending LISTs, EMSubscribeNode's */
+ int pending_id;
+ EDList pending;
+
+ /* queue of pending UN/SUBSCRIBEs, EMsg's */
+ int subscribe_id;
+ EDList subscribe;
+
+ /* working variables at runtime */
+ int selected_count;
+ int selected_subscribed_count;
+ gboolean subscribed_state:1; /* for setting the selection*/
+};
+
+typedef struct _EMSubscribeNode EMSubscribeNode;
+struct _EMSubscribeNode {
+ struct _EMSubscribeNode *next;
+ struct _EMSubscribeNode *prev;
+
+ CamelFolderInfo *info;
+ GtkTreePath *path;
+};
+
+static void sub_editor_busy(EMSubscribeEditor *se, int dir);
+static int sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode *node);
+
+static void
+sub_node_free(char *key, EMSubscribeNode *node, EMSubscribe *sub)
+{
+ d(printf("sub node free '%s'\n", node->info?node->info->full_name:"<unknown>"));
+ if (node->path)
+ gtk_tree_path_free(node->path);
+ g_free(node);
+}
+
+static void
+sub_ref(EMSubscribe *sub)
+{
+ sub->ref_count++;
+}
+
+static void
+sub_unref(EMSubscribe *sub)
+{
+ GSList *l;
+
+ sub->ref_count--;
+ if (sub->ref_count == 0) {
+ d(printf("subscribe object finalised\n"));
+ /* we dont have to delete the "subscribe" task list, as it must be empty,
+ otherwise we wouldn't be unreffed (intentional circular reference) */
+ if (sub->folders) {
+ g_hash_table_foreach(sub->folders, (GHFunc)sub_node_free, sub);
+ g_hash_table_destroy(sub->folders);
+ }
+ l = sub->info_list;
+ while (l) {
+ GSList *n = l->next;
+
+ camel_store_free_folder_info(sub->store, (CamelFolderInfo *)l->data);
+ g_slist_free_1(l);
+ l = n;
+ }
+ if (sub->store)
+ camel_object_unref(sub->store);
+ g_free(sub->store_uri);
+ g_free(sub);
+ }
+}
+
+/* ** Subscribe folder operation **************************************** */
+
+struct _zsubscribe_msg {
+ struct _mail_msg msg;
+
+ EMSubscribe *sub;
+ EMSubscribeNode *node;
+ int subscribe;
+ char *path;
+};
+
+static void
+sub_folder_subscribe (struct _mail_msg *mm)
+{
+ struct _zsubscribe_msg *m = (struct _zsubscribe_msg *) mm;
+
+ if (m->subscribe)
+ camel_store_subscribe_folder (m->sub->store, m->node->info->full_name, &mm->ex);
+ else
+ camel_store_unsubscribe_folder (m->sub->store, m->node->info->full_name, &mm->ex);
+}
+
+static void
+sub_folder_subscribed (struct _mail_msg *mm)
+{
+ struct _zsubscribe_msg *m = (struct _zsubscribe_msg *) mm;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ EMSubscribeNode *node;
+ gboolean subscribed, issub;
+
+ m->sub->subscribe_id = -1;
+ if (m->sub->cancel)
+ return;
+
+ if (!camel_exception_is_set(&mm->ex)) {
+ if (m->subscribe)
+ m->node->info->flags |= CAMEL_FOLDER_SUBSCRIBED;
+ else
+ m->node->info->flags &= ~CAMEL_FOLDER_SUBSCRIBED;
+ }
+
+ /* make sure the tree view matches the correct state */
+ model = gtk_tree_view_get_model(m->sub->tree);
+ if (gtk_tree_model_get_iter_from_string(model, &iter, m->path)) {
+ issub = (m->node->info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0;
+ gtk_tree_model_get(model, &iter, 0, &subscribed, 2, &node, -1);
+ if (node == m->node)
+ gtk_tree_store_set((GtkTreeStore *)model, &iter, 0, issub, -1);
+ else
+ d(printf("node mismatch, or subscribe state changed failed\n"));
+ }
+
+ /* queue any further ones */
+ m = (struct _zsubscribe_msg *)e_dlist_remhead(&m->sub->subscribe);
+ if (m) {
+ m->sub->subscribe_id = m->msg.seq;
+ e_thread_put (mail_thread_new, (EMsg *)m);
+ }
+}
+
+static void
+sub_folder_free (struct _mail_msg *mm)
+{
+ struct _zsubscribe_msg *m = (struct _zsubscribe_msg *) mm;
+
+ g_free(m->path);
+ sub_unref(m->sub);
+}
+
+static struct _mail_msg_op sub_subscribe_folder_op = {
+ NULL, /*subscribe_folder_desc,*/
+ sub_folder_subscribe,
+ sub_folder_subscribed,
+ sub_folder_free,
+};
+
+/* spath is tree path in string form */
+static int
+sub_subscribe_folder (EMSubscribe *sub, EMSubscribeNode *node, int state, const char *spath)
+{
+ struct _zsubscribe_msg *m;
+ int id;
+
+ m = mail_msg_new (&sub_subscribe_folder_op, NULL, sizeof(*m));
+ m->sub = sub;
+ sub_ref(sub);
+ m->node = node;
+ m->subscribe = state;
+ m->path = g_strdup(spath);
+
+ id = m->msg.seq;
+ if (sub->subscribe_id == -1) {
+ sub->subscribe_id = id;
+ d(printf("running subscribe folder '%s'\n", spath));
+ e_thread_put (mail_thread_new, (EMsg *)m);
+ } else {
+ d(printf("queueing subscribe folder '%s'\n", spath));
+ e_dlist_addtail(&sub->subscribe, (EDListNode *)m);
+ }
+
+ return id;
+}
+
+/* ********************************************************************** */
+static void
+sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info, GtkTreeIter *parent, int pending)
+{
+ CamelFolderInfo *fi;
+ GtkTreeStore *treestore;
+ GtkTreeIter iter;
+ EMSubscribeNode *node;
+
+ treestore = (GtkTreeStore *)gtk_tree_view_get_model(sub->tree);
+
+ /* first, fill a level up */
+ fi = info;
+ while (fi) {
+ if (g_hash_table_lookup(sub->folders, fi->full_name) == NULL) {
+ gboolean state;
+
+ gtk_tree_store_append(treestore, &iter, parent);
+ node = g_malloc0(sizeof(*node));
+ node->info = fi;
+ /* FIXME: CAMEL_FOLDER_SUBSCRIBED not implemented properly in imap */
+ state = camel_store_folder_subscribed(sub->store, fi->full_name);
+ /* state = (fi->flags & CAMEL_FOLDER_SUBSCRIBED) != 0; */
+ gtk_tree_store_set(treestore, &iter, 0, state, 1, fi->name, 2, node, -1);
+ if ((fi->flags & CAMEL_FOLDER_NOINFERIORS) == 0) {
+ node->path = gtk_tree_model_get_path((GtkTreeModel *)treestore, &iter);
+ if (node->path) {
+ /* save time, if we have any children alread, dont re-scan */
+ if (fi->child) {
+ d(printf("scanning child '%s'\n", fi->child->full_name));
+ sub_fill_level(sub, fi->child, &iter, FALSE);
+ } else {
+ if (pending)
+ e_dlist_addtail(&sub->pending, (EDListNode *)node);
+ }
+ }
+ }
+ g_hash_table_insert(sub->folders, fi->full_name, node);
+ }
+ fi = fi->sibling;
+ }
+}
+
+/* async query of folderinfo */
+
+struct _emse_folderinfo_msg {
+ struct _mail_msg msg;
+
+ EMSubscribe *sub;
+ EMSubscribeNode *node;
+ CamelFolderInfo *info;
+};
+
+static void
+sub_folderinfo_get (struct _mail_msg *mm)
+{
+ struct _emse_folderinfo_msg *m = (struct _emse_folderinfo_msg *) mm;
+
+ camel_operation_register(mm->cancel);
+ m->info = camel_store_get_folder_info (m->sub->store, m->node?m->node->info->full_name:"", CAMEL_STORE_FOLDER_INFO_FAST, &mm->ex);
+ camel_operation_unregister(mm->cancel);
+}
+
+static void
+sub_folderinfo_got(struct _mail_msg *mm)
+{
+ struct _emse_folderinfo_msg *m = (struct _emse_folderinfo_msg *) mm;
+ EMSubscribeNode *node;
+
+ m->sub->pending_id = -1;
+ if (m->sub->cancel)
+ return;
+
+ if (camel_exception_is_set (&mm->ex)) {
+ g_warning ("Error getting folder info from store: %s",
+ camel_exception_get_description (&mm->ex));
+ }
+
+ if (m->info) {
+ if (m->node) {
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter(gtk_tree_view_get_model(m->sub->tree), &iter, m->node->path);
+ sub_fill_level(m->sub, m->info, &iter, FALSE);
+ } else {
+ sub_fill_level(m->sub, m->info, NULL, TRUE);
+ }
+ }
+
+ /* check for more to do */
+ node = (EMSubscribeNode *)e_dlist_remhead(&m->sub->pending);
+ if (node)
+ sub_queue_fill_level(m->sub, node);
+}
+
+static void
+sub_folderinfo_free(struct _mail_msg *mm)
+{
+ struct _emse_folderinfo_msg *m = (struct _emse_folderinfo_msg *) mm;
+
+ if (m->info)
+ m->sub->info_list = g_slist_prepend(m->sub->info_list, m->info);
+
+ if (!m->sub->cancel)
+ sub_editor_busy(m->sub->editor, -1);
+
+ sub_unref(m->sub);
+}
+
+static struct _mail_msg_op sub_folderinfo_op = {
+ NULL, /*sub_folderinfo_desc, we do our own progress reporting/cancellation */
+ sub_folderinfo_get,
+ sub_folderinfo_got,
+ sub_folderinfo_free,
+};
+
+static int
+sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode *node)
+{
+ struct _emse_folderinfo_msg *m;
+ int id;
+
+ d(printf("Starting get folderinfo of '%s'\n", node?node->info->full_name:"<root>"));
+
+ m = mail_msg_new (&sub_folderinfo_op, NULL, sizeof(*m));
+ sub_ref(sub);
+ m->sub = sub;
+ m->node = node;
+
+ sub->pending_id = m->msg.seq;
+
+ sub_editor_busy(sub->editor, 1);
+
+ e_thread_put (mail_thread_new, (EMsg *)m);
+ return id;
+}
+
+/* ********************************************************************** */
+
+/* (un) subscribes the current selection */
+static void sub_do_subscribe(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, void *data)
+{
+ EMSubscribe *sub = data;
+ EMSubscribeNode *node;
+ gboolean subscribed;
+
+ gtk_tree_model_get(model, iter, 0, &subscribed, 2, &node, -1);
+ if (sub->subscribed_state ^ subscribed) {
+ char *spath;
+
+ spath = gtk_tree_path_to_string(path);
+ gtk_tree_store_set((GtkTreeStore *)model, iter, 0, subscribed, -1);
+ sub_subscribe_folder(sub, node, sub->subscribed_state, spath);
+ g_free(spath);
+ }
+}
+
+static void
+sub_subscribe(EMSubscribe *sub, gboolean subscribed)
+{
+ GtkTreeSelection *selection;
+
+ if (sub->tree == NULL)
+ return;
+
+ sub->subscribed_state = subscribed;
+ selection = gtk_tree_view_get_selection (sub->tree);
+ gtk_tree_selection_selected_foreach(selection, sub_do_subscribe, sub);
+}
+
+static void
+sub_subscribe_toggled(GtkCellRendererToggle *render, const char *spath, EMSubscribe *sub)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_tree_view_get_model(sub->tree);
+ EMSubscribeNode *node;
+ gboolean subscribed;
+
+ d(printf("subscribe toggled?\n"));
+
+ if (gtk_tree_model_get_iter_from_string(model, &iter, spath)) {
+ gtk_tree_model_get(model, &iter, 0, &subscribed, 2, &node, -1);
+ subscribed = !subscribed;
+ d(printf("new state is %s\n", subscribed?"subscribed":"not subscribed"));
+ gtk_tree_store_set((GtkTreeStore *)model, &iter, 0, subscribed, -1);
+ sub_subscribe_folder(sub, node, subscribed, spath);
+ }
+}
+
+static void sub_do_changed(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, void *data)
+{
+ EMSubscribe *sub = data;
+ EMSubscribeNode *node;
+ gboolean subscribed;
+
+ gtk_tree_model_get(model, iter, 0, &subscribed, 2, &node, -1);
+
+ if (subscribed)
+ sub->selected_subscribed_count++;
+ sub->selected_count++;
+}
+
+static void
+sub_selection_changed(GtkTreeSelection *selection, EMSubscribe *sub)
+{
+ int dosub = TRUE, dounsub = TRUE;
+
+ sub->selected_count = 0;
+ sub->selected_subscribed_count = 0;
+ gtk_tree_selection_selected_foreach(selection, sub_do_changed, sub);
+
+ if (sub->selected_count == 0) {
+ dosub = FALSE;
+ dounsub = FALSE;
+ } else if (sub->selected_subscribed_count == sub->selected_count)
+ dosub = FALSE;
+ else if (sub->selected_subscribed_count == 0)
+ dounsub = FALSE;
+
+ gtk_widget_set_sensitive(sub->editor->subscribe_button, dosub);
+ gtk_widget_set_sensitive(sub->editor->unsubscribe_button, dounsub);
+}
+
+static void
+sub_row_expanded(GtkTreeView *tree, GtkTreeIter *iter, GtkTreePath *path, EMSubscribe *sub)
+{
+ EMSubscribeNode *node;
+ GtkTreeIter child;
+ GtkTreeModel *model = (GtkTreeModel *)gtk_tree_view_get_model(tree);
+ EDList list;
+
+ gtk_tree_model_get(model, iter, 2, &node, -1);
+ if (node->path == NULL) {
+ d(printf("path '%s' already processed\n", node->info->full_name));
+ return;
+ }
+ gtk_tree_path_free(node->path);
+ node->path = NULL;
+
+ e_dlist_init(&list);
+
+ /* add all children nodes to pending, and fire off a pending */
+ /* we add them to the head of the pending list, to make it more interactive */
+ gtk_tree_model_iter_children(model, &child, iter);
+ do {
+ gtk_tree_model_get(model, &child, 2, &node, -1);
+ if (node->path)
+ e_dlist_addtail(&list, (EDListNode *)node);
+ } while (gtk_tree_model_iter_next(model, &child));
+
+ while ( (node = (EMSubscribeNode *)e_dlist_remtail(&list)) )
+ e_dlist_addhead(&sub->pending, (EDListNode *)node);
+
+ if (sub->pending_id == -1
+ && (node = (EMSubscribeNode *)e_dlist_remtail(&sub->pending)))
+ sub_queue_fill_level(sub, node);
+}
+
+static void
+sub_destroy(GtkWidget *w, EMSubscribe *sub)
+{
+ struct _zsubscribe_msg *m;
+
+ d(printf("subscribe closed\n"));
+ sub->cancel = TRUE;
+
+ if (sub->pending_id != -1)
+ mail_msg_cancel(sub->pending_id);
+
+ if (sub->subscribe_id != -1)
+ mail_msg_cancel(sub->subscribe_id);
+
+ while ( (m = (struct _zsubscribe_msg *)e_dlist_remhead(&sub->subscribe)) )
+ mail_msg_free(m);
+
+ sub_unref(sub);
+}
+
+static EMSubscribe *
+subscribe_new(EMSubscribeEditor *se, const char *uri)
+{
+ EMSubscribe *sub;
+
+ sub = g_malloc0(sizeof(*sub));
+ sub->store_uri = g_strdup(uri);
+ sub->editor = se;
+ sub->ref_count = 1;
+ sub->pending_id = -1;
+ e_dlist_init(&sub->pending);
+ sub->subscribe_id = -1;
+ e_dlist_init(&sub->subscribe);
+ sub->store_id = -1;
+
+ return sub;
+}
+
+static void
+subscribe_set_store(EMSubscribe *sub, CamelStore *store)
+{
+ if (store == NULL || !camel_store_supports_subscriptions(store)) {
+ GtkWidget *w = gtk_label_new(_("This store does not support subscriptions, or the are not enabled."));
+
+ gtk_label_set_line_wrap((GtkLabel *)w, TRUE);
+ sub->widget = gtk_viewport_new(NULL, NULL);
+ gtk_viewport_set_shadow_type((GtkViewport *)sub->widget, GTK_SHADOW_IN);
+ gtk_container_add((GtkContainer *)sub->widget, w);
+ gtk_widget_show(w);
+ gtk_widget_show(sub->widget);
+ } else {
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeStore *model;
+
+ sub->store = store;
+ camel_object_ref(store);
+ sub->folders = g_hash_table_new(g_str_hash, g_str_equal);
+
+ model = gtk_tree_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
+ sub->tree = (GtkTreeView *) gtk_tree_view_new_with_model ((GtkTreeModel *) model);
+ gtk_widget_show ((GtkWidget *)sub->tree);
+
+ sub->widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sub->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sub->widget), GTK_SHADOW_IN);
+ gtk_container_add((GtkContainer *)sub->widget, (GtkWidget *)sub->tree);
+ gtk_widget_show(sub->widget);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_object_set(renderer, "activatable", TRUE, NULL);
+ gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Subscribed"), renderer, "active", 0, NULL);
+ g_signal_connect(renderer, "toggled", G_CALLBACK(sub_subscribe_toggled), sub);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Folder"), renderer, "text", 1, NULL);
+ gtk_tree_view_set_expander_column(sub->tree, gtk_tree_view_get_column(sub->tree, 1));
+
+ selection = gtk_tree_view_get_selection (sub->tree);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_view_set_headers_visible (sub->tree, FALSE);
+
+ g_signal_connect(sub->tree, "row-expanded", G_CALLBACK(sub_row_expanded), sub);
+ g_signal_connect(sub->tree, "destroy", G_CALLBACK(sub_destroy), sub);
+
+ sub_selection_changed(selection, sub);
+ g_signal_connect(selection, "changed", G_CALLBACK(sub_selection_changed), sub);
+
+ sub_queue_fill_level(sub, NULL);
+ }
+
+ gtk_box_pack_start((GtkBox *)sub->editor->vbox, sub->widget, TRUE, TRUE, 0);
+}
+
+static void
+sub_editor_destroy(GtkWidget *w, EMSubscribeEditor *se)
+{
+ /* need to clean out pending store opens */
+ d(printf("editor destroyed, freeing editor\n"));
+ if (se->busy_id)
+ g_source_remove(se->busy_id);
+
+ g_free(se);
+}
+
+static void
+sub_editor_close(GtkWidget *w, EMSubscribeEditor *se)
+{
+ gtk_widget_destroy((GtkWidget *)se->dialog);
+}
+
+static void
+sub_editor_refresh(GtkWidget *w, EMSubscribeEditor *se)
+{
+ EMSubscribe *sub = se->current;
+ GSList *l;
+
+ d(printf("sub editor refresh?\n"));
+ if (sub == NULL || sub->store == NULL)
+ return;
+
+ /* drop any currently pending */
+ if (sub->pending_id != -1)
+ mail_msg_cancel(sub->pending_id);
+
+ gtk_tree_store_clear((GtkTreeStore *)gtk_tree_view_get_model(sub->tree));
+
+ e_dlist_init(&sub->pending);
+ if (sub->folders) {
+ g_hash_table_foreach(sub->folders, (GHFunc)sub_node_free, sub);
+ g_hash_table_destroy(sub->folders);
+ }
+ sub->folders = g_hash_table_new(g_str_hash, g_str_equal);
+
+ l = sub->info_list;
+ sub->info_list = NULL;
+ while (l) {
+ GSList *n = l->next;
+
+ camel_store_free_folder_info(sub->store, (CamelFolderInfo *)l->data);
+ g_slist_free_1(l);
+ l = n;
+ }
+
+ sub_queue_fill_level(sub, NULL);
+}
+
+static void
+sub_editor_subscribe(GtkWidget *w, EMSubscribeEditor *se)
+{
+ d(printf("subscribe clicked, current = %p\n", se->current));
+
+ if (se->current)
+ sub_subscribe(se->current, TRUE);
+}
+
+static void
+sub_editor_unsubscribe(GtkWidget *w, EMSubscribeEditor *se)
+{
+ d(printf("unsubscribe clicked\n"));
+
+ if (se->current)
+ sub_subscribe(se->current, FALSE);
+}
+
+static void
+sub_editor_got_store(char *uri, CamelStore *store, void *data)
+{
+ struct _EMSubscribe *sub = data;
+
+ if (!sub->cancel)
+ subscribe_set_store(sub, store);
+ sub_unref(sub);
+}
+
+static void
+sub_editor_menu_changed(GtkWidget *w, EMSubscribeEditor *se)
+{
+ int i, n;
+ struct _EMSubscribe *sub;
+
+ d(printf("menu changed\n"));
+
+ i = 1;
+ n = gtk_option_menu_get_history((GtkOptionMenu *)se->optionmenu);
+ if (n == 0)
+ gtk_widget_show(se->none_selected);
+ else {
+ gtk_widget_hide(se->none_selected);
+ gtk_widget_hide(se->none_selected_item);
+ }
+
+ se->current = NULL;
+ sub = (struct _EMSubscribe *)se->stores.head;
+ while (sub->next) {
+ if (i == n) {
+ se->current = sub;
+ if (sub->widget) {
+ gtk_widget_show(sub->widget);
+ } else if (sub->store_id == -1) {
+ sub_ref(sub);
+ sub->store_id = mail_get_store(sub->store_uri, NULL, sub_editor_got_store, sub);
+ }
+ } else {
+ if (sub->widget)
+ gtk_widget_hide(sub->widget);
+ }
+ i++;
+ sub = sub->next;
+ }
+}
+
+static gboolean sub_editor_timeout(EMSubscribeEditor *se)
+{
+ gtk_progress_bar_pulse((GtkProgressBar *)se->progress);
+
+ return TRUE;
+}
+
+static void sub_editor_busy(EMSubscribeEditor *se, int dir)
+{
+ int was;
+
+ was = se->busy != 0;
+ se->busy += dir;
+ if (was && !se->busy) {
+ g_source_remove(se->busy_id);
+ se->busy_id = 0;
+ gtk_widget_hide(se->progress);
+ } else if (!was && se->busy) {
+ se->busy_id = g_timeout_add(1000/5, (GSourceFunc)sub_editor_timeout, se);
+ gtk_widget_show(se->progress);
+ }
+}
+
+GtkDialog *em_subscribe_editor_new(void)
+{
+ EMSubscribeEditor *se;
+ EAccountList *accounts;
+ EIterator *iter;
+ GladeXML *xml;
+ GtkWidget *menu, *w;
+
+ se = g_malloc0(sizeof(*se));
+ e_dlist_init(&se->stores);
+
+ xml = glade_xml_new (EVOLUTION_GLADEDIR "/subscribe-dialog.glade", "subscribe_dialog", NULL);
+ if (xml == NULL) {
+ /* ?? */
+ return NULL;
+ }
+ se->dialog = (GtkDialog *)glade_xml_get_widget (xml, "subscribe_dialog");
+ g_signal_connect(se->dialog, "destroy", G_CALLBACK(sub_editor_destroy), se);
+
+ se->vbox = glade_xml_get_widget(xml, "tree_box");
+
+ se->subscribe_button = glade_xml_get_widget (xml, "subscribe_button");
+ g_signal_connect(se->subscribe_button, "clicked", G_CALLBACK(sub_editor_subscribe), se);
+ se->unsubscribe_button = glade_xml_get_widget (xml, "unsubscribe_button");
+ g_signal_connect(se->unsubscribe_button, "clicked", G_CALLBACK(sub_editor_unsubscribe), se);
+
+ /* FIXME: This is just to get the shadow, is there a better way? */
+ w = gtk_label_new(_("Please select a server."));
+ se->none_selected = gtk_viewport_new(NULL, NULL);
+ gtk_viewport_set_shadow_type((GtkViewport *)se->none_selected, GTK_SHADOW_IN);
+ gtk_container_add((GtkContainer *)se->none_selected, w);
+ gtk_widget_show(w);
+
+ gtk_box_pack_start((GtkBox *)se->vbox, se->none_selected, TRUE, TRUE, 0);
+ gtk_widget_show(se->none_selected);
+
+ se->progress = glade_xml_get_widget(xml, "progress_bar");
+ gtk_widget_hide(se->progress);
+
+ w = glade_xml_get_widget(xml, "close_button");
+ g_signal_connect(w, "clicked", G_CALLBACK(sub_editor_close), se);
+
+ w = glade_xml_get_widget(xml, "refresh_button");
+ g_signal_connect(w, "clicked", G_CALLBACK(sub_editor_refresh), se);
+
+ /* setup stores menu */
+ se->optionmenu = glade_xml_get_widget(xml, "store_menu");
+ menu = gtk_menu_new();
+ se->none_selected_item = w = gtk_menu_item_new_with_label(_("No server has been selected"));
+ gtk_widget_show(w);
+ gtk_menu_shell_append ((GtkMenuShell *)menu, w);
+
+ accounts = mail_config_get_accounts ();
+ for (iter = e_list_get_iterator ((EList *) accounts);
+ e_iterator_is_valid (iter);
+ e_iterator_next (iter)) {
+ EAccount *account = (EAccount *) e_iterator_get (iter);
+
+ /* setup url table, and store table? */
+ if (account->enabled && account->source->url) {
+ d(printf("adding account '%s'\n", account->name));
+ w = gtk_menu_item_new_with_label(account->name);
+ gtk_menu_shell_append ((GtkMenuShell *)menu, w);
+ gtk_widget_show(w);
+ e_dlist_addtail(&se->stores, (EDListNode *)subscribe_new(se, account->source->url));
+ } else {
+ d(printf("not adding account '%s'\n", account->name));
+ }
+ }
+ g_object_unref(iter);
+
+ gtk_option_menu_set_menu((GtkOptionMenu *)se->optionmenu, menu);
+ g_signal_connect(se->optionmenu, "changed", G_CALLBACK(sub_editor_menu_changed), se);
+
+ gtk_window_set_default_size((GtkWindow *)se->dialog, 350, 400);
+
+ return se->dialog;
+}
diff --git a/mail/em-subscribe-editor.h b/mail/em-subscribe-editor.h
new file mode 100644
index 0000000000..ca02f14bee
--- /dev/null
+++ b/mail/em-subscribe-editor.h
@@ -0,0 +1,9 @@
+
+#ifndef _EM_SUBSCRIBE_EDITOR_H
+#define _EM_SUBSCRIBE_EDITOR_H
+
+#include <gtk/gtkdialog.h>
+
+GtkDialog *em_subscribe_editor_new(void);
+
+#endif /* ! _EM_SUBSCRIBE_EDITOR_H */
diff --git a/mail/em-sync-stream.c b/mail/em-sync-stream.c
new file mode 100644
index 0000000000..df5659253e
--- /dev/null
+++ b/mail/em-sync-stream.c
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ * Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2001 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <camel/camel-stream.h>
+#include <camel/camel-object.h>
+#include <gtk/gtkmain.h>
+#include "em-sync-stream.h"
+
+#include "mail-mt.h"
+
+/*#define LOG_STREAM*/
+
+#define d(x)
+
+#define EMSS_CLASS(x) ((EMSyncStreamClass *)(((CamelObject *)(x))->klass))
+
+struct _EMSyncStreamPrivate {
+ /* FIXME: use a single data port/gui channel for all instances */
+ /* TODO: possibly just use one of the mail-mt ports ... */
+ struct _EMsgPort *data_port, *reply_port;
+ struct _GIOChannel *gui_channel;
+ guint gui_watch;
+
+ char *buf_data;
+ int buf_used;
+ int buf_size;
+};
+
+/* Should probably expose messages to outside world ... so subclasses can extend */
+enum _write_msg_t {
+ EMSS_WRITE,
+ EMSS_FLUSH,
+ EMSS_CLOSE,
+};
+
+struct _write_msg {
+ EMsg msg;
+
+ enum _write_msg_t op;
+
+ const char *data;
+ size_t n;
+};
+
+static void em_sync_stream_class_init (EMSyncStreamClass *klass);
+static void em_sync_stream_init (CamelObject *object);
+static void em_sync_stream_finalize (CamelObject *object);
+
+static ssize_t stream_write(CamelStream *stream, const char *buffer, size_t n);
+static int stream_close(CamelStream *stream);
+static int stream_flush(CamelStream *stream);
+
+static CamelStreamClass *parent_class = NULL;
+
+CamelType
+em_sync_stream_get_type (void)
+{
+ static CamelType type = CAMEL_INVALID_TYPE;
+
+ if (type == CAMEL_INVALID_TYPE) {
+ type = camel_type_register (CAMEL_STREAM_TYPE,
+ "EMSyncStream",
+ sizeof (EMSyncStream),
+ sizeof (EMSyncStreamClass),
+ (CamelObjectClassInitFunc) em_sync_stream_class_init,
+ NULL,
+ (CamelObjectInitFunc) em_sync_stream_init,
+ (CamelObjectFinalizeFunc) em_sync_stream_finalize);
+ }
+
+ return type;
+}
+
+static void
+em_sync_stream_class_init (EMSyncStreamClass *klass)
+{
+ CamelStreamClass *stream_class = CAMEL_STREAM_CLASS (klass);
+
+ parent_class = (CamelStreamClass *) CAMEL_STREAM_TYPE;
+
+ /* virtual method overload */
+ stream_class->write = stream_write;
+ stream_class->flush = stream_flush;
+ stream_class->close = stream_close;
+}
+
+static gboolean
+emcs_gui_received(GIOChannel *source, GIOCondition cond, void *data)
+{
+ EMSyncStream *emss = data;
+ struct _EMSyncStreamPrivate *p = emss->priv;
+ struct _write_msg *msg;
+
+ d(printf("%p: gui sync op job waiting\n", emss));
+
+ msg = (struct _write_msg *)e_msgport_get(p->data_port);
+ /* Should never happen ... */
+ if (msg == NULL)
+ return TRUE;
+
+ d(printf("%p: running sync op %d\n", emss, msg->op));
+
+ /* force out any pending data before doing anything else */
+ if (p->buf_used > 0) {
+ EMSS_CLASS(emss)->sync_write((CamelStream *)emss, p->buf_data, p->buf_used);
+ p->buf_used = 0;
+ }
+
+ /* FIXME: need to handle return values */
+
+ switch (msg->op) {
+ case EMSS_WRITE:
+ EMSS_CLASS(emss)->sync_write((CamelStream *)emss, msg->data, msg->n);
+ break;
+ case EMSS_FLUSH:
+ EMSS_CLASS(emss)->sync_flush((CamelStream *)emss);
+ break;
+ case EMSS_CLOSE:
+ EMSS_CLASS(emss)->sync_close((CamelStream *)emss);
+ break;
+ }
+
+ e_msgport_reply((EMsg *)msg);
+ d(printf("%p: gui sync op jobs done\n", emss));
+
+ return TRUE;
+}
+
+static void
+em_sync_stream_init (CamelObject *object)
+{
+ EMSyncStream *emss = (EMSyncStream *)object;
+ struct _EMSyncStreamPrivate *p;
+
+ p = emss->priv = g_malloc0(sizeof(*p));
+
+ p->data_port = e_msgport_new();
+ p->reply_port = e_msgport_new();
+
+ p->gui_channel = g_io_channel_unix_new(e_msgport_fd(p->data_port));
+ p->gui_watch = g_io_add_watch(p->gui_channel, G_IO_IN, emcs_gui_received, emss);
+
+ d(printf("%p: new emss\n", emss));
+}
+
+static void
+sync_op(EMSyncStream *emss, enum _write_msg_t op, const char *data, size_t n)
+{
+ struct _EMSyncStreamPrivate *p = emss->priv;
+ struct _write_msg msg;
+
+ d(printf("%p: launching sync op %d\n", emss, op));
+
+ /* we do everything synchronous, we should never have any locks, and
+ this prevents overflow from banked up data */
+
+ msg.msg.reply_port = p->reply_port;
+ msg.op = op;
+ msg.data = data;
+ msg.n = n;
+
+ e_msgport_put(p->data_port, &msg.msg);
+ e_msgport_wait(p->reply_port);
+
+ g_assert(e_msgport_get(msg.msg.reply_port) == &msg.msg);
+ d(printf("%p: returned sync op %d\n", emss, op));
+}
+
+static void
+em_sync_stream_finalize (CamelObject *object)
+{
+ EMSyncStream *emss = (EMSyncStream *)object;
+ struct _EMSyncStreamPrivate *p = emss->priv;
+
+ /* TODO: is this stuff safe to do in another thread? */
+ g_source_remove(p->gui_watch);
+ g_io_channel_unref(p->gui_channel);
+
+ e_msgport_destroy(p->data_port);
+ e_msgport_destroy(p->reply_port);
+
+ p->data_port = NULL;
+ p->reply_port = NULL;
+
+ g_free(p->buf_data);
+ g_free(p);
+}
+
+static ssize_t
+stream_write (CamelStream *stream, const char *buffer, size_t n)
+{
+ EMSyncStream *emss = EM_SYNC_STREAM (stream);
+ struct _EMSyncStreamPrivate *p = emss->priv;
+
+ if (emss->cancel)
+ return -1;
+
+ if (pthread_self() == mail_gui_thread)
+ EMSS_CLASS(emss)->sync_write(stream, buffer, n);
+ else if (p->buf_size > 0) {
+ size_t left = p->buf_size-p->buf_used;
+
+ if (n >= left) {
+ sync_op(emss, EMSS_WRITE, buffer, n);
+ } else {
+ memcpy(p->buf_data + p->buf_used, buffer, n);
+ p->buf_used += n;
+ }
+ } else {
+ sync_op(emss, EMSS_WRITE, buffer, n);
+ }
+
+ return (ssize_t) n;
+}
+
+static int
+stream_flush(CamelStream *stream)
+{
+ EMSyncStream *emss = (EMSyncStream *)stream;
+
+ if (emss->cancel)
+ return -1;
+
+ if (pthread_self() == mail_gui_thread)
+ return ((EMSyncStreamClass *)(((CamelObject *)emss)->klass))->sync_flush(stream);
+ else
+ sync_op(emss, EMSS_FLUSH, NULL, 0);
+
+ return 0;
+}
+
+static int
+stream_close(CamelStream *stream)
+{
+ EMSyncStream *emss = (EMSyncStream *)stream;
+
+ if (emss->cancel)
+ return -1;
+
+ d(printf("%p: closing stream\n", stream));
+
+ if (pthread_self() == mail_gui_thread)
+ return ((EMSyncStreamClass *)(((CamelObject *)emss)->klass))->sync_close(stream);
+ else
+ sync_op(emss, EMSS_CLOSE, NULL, 0);
+
+ return 0;
+}
+
+void
+em_sync_stream_set_buffer_size(EMSyncStream *emss, size_t size)
+{
+ struct _EMSyncStreamPrivate *p = emss->priv;
+
+ g_free(p->buf_data);
+ p->buf_data = g_malloc(size);
+ p->buf_size = size;
+ p->buf_used = 0;
+}
diff --git a/mail/em-sync-stream.h b/mail/em-sync-stream.h
new file mode 100644
index 0000000000..abccae3311
--- /dev/null
+++ b/mail/em-sync-stream.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+EMSyncStream - Abstract class.
+A synchronous stream, that can be written from any thread, but whose
+requests are always handled in the main gui thread in the correct order.
+*/
+
+#ifndef EM_SYNC_STREAM_H
+#define EM_SYNC_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define EM_SYNC_STREAM_TYPE (em_sync_stream_get_type ())
+#define EM_SYNC_STREAM(obj) (CAMEL_CHECK_CAST((obj), EM_SYNC_STREAM_TYPE, EMSyncStream))
+#define EM_SYNC_STREAM_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), EM_SYNC_STREAM_TYPE, EMSyncStreamClass))
+#define EM_IS_SYNC_STREAM(o) (CAMEL_CHECK_TYPE((o), EM_SYNC_STREAM_TYPE))
+
+#include <glib.h>
+#include <camel/camel-stream.h>
+
+typedef struct _EMSyncStream {
+ CamelStream parent_stream;
+
+ struct _EMSyncStreamPrivate *priv;
+
+ int cancel;
+} EMSyncStream;
+
+typedef struct {
+ CamelStreamClass parent_class;
+
+ ssize_t (*sync_write) (CamelStream *stream, const char *buffer, size_t n);
+ int (*sync_close) (CamelStream *stream);
+ int (*sync_flush) (CamelStream *stream);
+
+} EMSyncStreamClass;
+
+CamelType em_sync_stream_get_type (void);
+void em_sync_stream_set_buffer_size(EMSyncStream *, size_t size);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* EM_SYNC_STREAM_H */
diff --git a/mail/em-utils.c b/mail/em-utils.c
new file mode 100644
index 0000000000..07de855e0f
--- /dev/null
+++ b/mail/em-utils.c
@@ -0,0 +1,2327 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+
+#include <camel/camel-stream-fs.h>
+#include <camel/camel-url-scanner.h>
+
+#include <filter/filter-editor.h>
+
+#include "mail-mt.h"
+#include "mail-ops.h"
+#include "mail-tools.h"
+#include "mail-config.h"
+#include "mail-config-druid.h"
+#include "message-tag-followup.h"
+
+#include <e-util/e-mktemp.h>
+#include <e-util/e-dialog-utils.h>
+
+#include "em-utils.h"
+#include "em-composer-utils.h"
+#include "em-format-quote.h"
+
+static EAccount *guess_account (CamelMimeMessage *message);
+
+/**
+ * em_utils_prompt_user:
+ * @parent: parent window
+ * @def: default response
+ * @again: continue prompting the user in the future
+ * @fmt: prompt format
+ * @Varargs: varargs
+ *
+ * Convenience function to query the user with a Yes/No dialog and a
+ * "Don't show this dialog again" checkbox. If the user checks that
+ * checkbox, then @again is set to %FALSE, otherwise it is set to
+ * %TRUE.
+ *
+ * Returns %TRUE if the user clicks Yes or %FALSE otherwise.
+ **/
+gboolean
+em_utils_prompt_user (GtkWindow *parent, int def, gboolean *again, const char *fmt, ...)
+{
+ GtkWidget *mbox, *check = NULL;
+ va_list ap;
+ int button;
+ char *str;
+
+ va_start (ap, fmt);
+ str = g_strdup_vprintf (fmt, ap);
+ va_end (ap);
+ mbox = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ "%s", str);
+ g_free (str);
+ gtk_dialog_set_default_response ((GtkDialog *) mbox, def);
+ if (again) {
+ check = gtk_check_button_new_with_label (_("Don't show this message again."));
+ gtk_box_pack_start ((GtkBox *)((GtkDialog *) mbox)->vbox, check, TRUE, TRUE, 10);
+ gtk_widget_show (check);
+ }
+
+ button = gtk_dialog_run ((GtkDialog *) mbox);
+ if (again)
+ *again = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
+ gtk_widget_destroy (mbox);
+
+ return button == GTK_RESPONSE_YES;
+}
+
+
+/**
+ * em_utils_uids_copy:
+ * @uids: array of uids
+ *
+ * Duplicates the array of uids held by @uids into a new
+ * GPtrArray. Use em_utils_uids_free() to free the resultant uid
+ * array.
+ *
+ * Returns a duplicate copy of @uids.
+ **/
+GPtrArray *
+em_utils_uids_copy (GPtrArray *uids)
+{
+ GPtrArray *copy;
+ int i;
+
+ copy = g_ptr_array_new ();
+ g_ptr_array_set_size (copy, uids->len);
+
+ for (i = 0; i < uids->len; i++)
+ copy->pdata[i] = g_strdup (uids->pdata[i]);
+
+ return copy;
+}
+
+/**
+ * em_utils_uids_free:
+ * @uids: array of uids
+ *
+ * Frees the array of uids pointed to by @uids back to the system.
+ **/
+void
+em_utils_uids_free (GPtrArray *uids)
+{
+ int i;
+
+ for (i = 0; i < uids->len; i++)
+ g_free (uids->pdata[i]);
+
+ g_ptr_array_free (uids, TRUE);
+}
+
+static void
+druid_destroy_cb (gpointer user_data, GObject *deadbeef)
+{
+ gtk_main_quit ();
+}
+
+/**
+ * em_utils_configure_account:
+ * @parent: parent window for the druid to be a child of.
+ *
+ * Displays a druid allowing the user to configure an account. If
+ * @parent is non-NULL, then the druid will be created as a child
+ * window of @parent's toplevel window.
+ *
+ * Returns %TRUE if an account has been configured or %FALSE
+ * otherwise.
+ **/
+gboolean
+em_utils_configure_account (GtkWidget *parent)
+{
+ MailConfigDruid *druid;
+
+ druid = mail_config_druid_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) druid, parent);
+
+ g_object_weak_ref ((GObject *) druid, (GWeakNotify) druid_destroy_cb, NULL);
+ gtk_widget_show ((GtkWidget *) druid);
+ gtk_grab_add ((GtkWidget *) druid);
+ gtk_main ();
+
+ return mail_config_is_configured ();
+}
+
+/**
+ * em_utils_check_user_can_send_mail:
+ * @parent: parent window for the druid to be a child of.
+ *
+ * If no accounts have been configured, the user will be given a
+ * chance to configure an account. In the case that no accounts are
+ * configured, a druid will be created. If @parent is non-NULL, then
+ * the druid will be created as a child window of @parent's toplevel
+ * window.
+ *
+ * Returns %TRUE if the user has an account configured (to send mail)
+ * or %FALSE otherwise.
+ **/
+gboolean
+em_utils_check_user_can_send_mail (GtkWidget *parent)
+{
+ EAccount *account;
+
+ if (!mail_config_is_configured ()) {
+ if (!em_utils_configure_account (parent))
+ return FALSE;
+ }
+
+ if (!(account = mail_config_get_default_account ()))
+ return FALSE;
+
+ /* Check for a transport */
+ if (!account->transport->url)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Editing Filters/vFolders... */
+
+static GtkWidget *filter_editor = NULL;
+
+static void
+filter_editor_response (GtkWidget *dialog, int button, gpointer user_data)
+{
+ extern char *evolution_dir;
+ FilterContext *fc;
+
+ if (button == GTK_RESPONSE_ACCEPT) {
+ char *user;
+
+ fc = g_object_get_data ((GObject *) dialog, "context");
+ user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ rule_context_save ((RuleContext *) fc, user);
+ g_free (user);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ filter_editor = NULL;
+}
+
+static const char *filter_source_names[] = {
+ "incoming",
+ "outgoing",
+ NULL,
+};
+
+/**
+ * em_utils_edit_filters:
+ * @parent: parent window
+ *
+ * Opens or raises the filters editor dialog so that the user may edit
+ * his/her filters. If @parent is non-NULL, then the dialog will be
+ * created as a child window of @parent's toplevel window.
+ **/
+void
+em_utils_edit_filters (GtkWidget *parent)
+{
+ extern char *evolution_dir;
+ char *user, *system;
+ FilterContext *fc;
+
+ if (filter_editor) {
+ gdk_window_raise (GTK_WIDGET (filter_editor)->window);
+ return;
+ }
+
+ fc = filter_context_new ();
+ user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
+ rule_context_load ((RuleContext *) fc, system, user);
+ g_free (user);
+
+ if (((RuleContext *) fc)->error) {
+ e_notice (parent, GTK_MESSAGE_ERROR,
+ _("Error loading filter information:\n%s"),
+ ((RuleContext *) fc)->error);
+ return;
+ }
+
+ filter_editor = (GtkWidget *) filter_editor_new (fc, filter_source_names);
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) filter_editor, parent);
+
+ gtk_window_set_title (GTK_WINDOW (filter_editor), _("Filters"));
+ g_object_set_data_full ((GObject *) filter_editor, "context", fc, (GtkDestroyNotify) g_object_unref);
+ g_signal_connect (filter_editor, "response", G_CALLBACK (filter_editor_response), NULL);
+ gtk_widget_show (GTK_WIDGET (filter_editor));
+}
+
+/* Composing messages... */
+
+static EMsgComposer *
+create_new_composer (GtkWidget *parent)
+{
+ EMsgComposer *composer;
+
+ composer = e_msg_composer_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ return composer;
+}
+
+/**
+ * em_utils_compose_new_message:
+ * @parent: parent window
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window.
+ **/
+void
+em_utils_compose_new_message (GtkWidget *parent)
+{
+ GtkWidget *composer;
+
+ composer = (GtkWidget *) create_new_composer (parent);
+
+ gtk_widget_show (composer);
+}
+
+/**
+ * em_utils_compose_new_message_with_mailto:
+ * @parent: parent window
+ * @url: mailto url
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window. If @url is non-NULL, the composer fields will be filled in
+ * according to the values in the mailto url.
+ **/
+void
+em_utils_compose_new_message_with_mailto (GtkWidget *parent, const char *url)
+{
+ EMsgComposer *composer;
+
+ if (url != NULL)
+ composer = e_msg_composer_new_from_url (url);
+ else
+ composer = e_msg_composer_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ gtk_widget_show ((GtkWidget *) composer);
+}
+
+/**
+ * em_utils_post_to_url:
+ * @parent: parent window
+ * @url: mailto url
+ *
+ * Opens a new composer window as a child window of @parent's toplevel
+ * window. If @url is non-NULL, the composer will default to posting
+ * mail to the folder specified by @url.
+ **/
+void
+em_utils_post_to_url (GtkWidget *parent, const char *url)
+{
+ EMsgComposer *composer;
+
+ composer = e_msg_composer_new_post ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ if (url != NULL)
+ e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show ((GtkWidget *) composer);
+}
+
+/* Editing messages... */
+
+static void
+edit_message (GtkWidget *parent, CamelMimeMessage *message, CamelFolder *drafts, const char *uid)
+{
+ EMsgComposer *composer;
+
+ composer = e_msg_composer_new_with_message (message);
+ em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, drafts, uid);
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+}
+
+/**
+ * em_utils_edit_message:
+ * @parent: parent window
+ * @message: message to edit
+ *
+ * Opens a composer filled in with the headers/mime-parts/etc of
+ * @message.
+ **/
+void
+em_utils_edit_message (GtkWidget *parent, CamelMimeMessage *message)
+{
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ edit_message (parent, message, NULL, NULL);
+}
+
+static void
+edit_messages (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, void *user_data)
+{
+ int i;
+
+ if (msgs == NULL)
+ return;
+
+ for (i = 0; i < msgs->len; i++) {
+ camel_medium_remove_header (CAMEL_MEDIUM (msgs->pdata[i]), "X-Mailer");
+
+ edit_message ((GtkWidget *) user_data, msgs->pdata[i], folder, uids->pdata[i]);
+ }
+}
+
+/**
+ * em_utils_edit_messages:
+ * @parent: parent window
+ * @folder: folder containing messages to edit
+ * @uids: uids of messages to edit
+ *
+ * Opens a composer for each message to be edited.
+ **/
+void
+em_utils_edit_messages (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_get_messages (folder, uids, edit_messages, parent);
+}
+
+/* Forwarding messages... */
+
+static void
+forward_attached (CamelFolder *folder, GPtrArray *messages, CamelMimePart *part, char *subject, void *user_data)
+{
+ EMsgComposer *composer;
+
+ if (part == NULL)
+ return;
+
+ composer = create_new_composer ((GtkWidget *) user_data);
+ e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject);
+ e_msg_composer_attach (composer, part);
+
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+}
+
+/**
+ * em_utils_forward_attached:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * If there is more than a single message in @uids, a multipart/digest
+ * will be constructed and attached to a new composer window preset
+ * with the appropriate header defaults for forwarding the first
+ * message in the list. If only one message is to be forwarded, it is
+ * forwarded as a simple message/rfc822 attachment.
+ **/
+void
+em_utils_forward_attached (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_build_attachment (folder, uids, forward_attached, parent);
+}
+
+static void
+forward_non_attached (GtkWidget *parent, GPtrArray *messages, int style)
+{
+ CamelMimeMessage *message;
+ CamelDataWrapper *wrapper;
+ EMsgComposer *composer;
+ char *subject, *text;
+ int i;
+ guint32 flags;
+
+ if (messages->len == 0)
+ return;
+
+ flags = EM_FORMAT_QUOTE_HEADERS;
+ if (style == MAIL_CONFIG_FORWARD_QUOTED)
+ flags |= EM_FORMAT_QUOTE_CITE;
+
+ for (i = 0; i < messages->len; i++) {
+ message = messages->pdata[i];
+ subject = mail_tool_generate_forward_subject (message);
+
+ text = em_utils_message_to_html(message, _("-------- Forwarded Message --------"), flags);
+
+ if (text) {
+ composer = create_new_composer (parent);
+ e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject);
+ e_msg_composer_set_body_text (composer, text);
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message));
+ if (CAMEL_IS_MULTIPART (wrapper))
+ e_msg_composer_add_message_attachments (composer, message, FALSE);
+
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+
+ g_free (text);
+ }
+
+ g_free (subject);
+ }
+}
+
+static void
+forward_inline (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data)
+{
+ forward_non_attached ((GtkWidget *) user_data, messages, MAIL_CONFIG_FORWARD_INLINE);
+}
+
+/**
+ * em_utils_forward_inline:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * Forwards each message in the 'inline' form, each in its own composer window.
+ **/
+void
+em_utils_forward_inline (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_get_messages (folder, uids, forward_inline, parent);
+}
+
+static void
+forward_quoted (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data)
+{
+ forward_non_attached ((GtkWidget *) user_data, messages, MAIL_CONFIG_FORWARD_QUOTED);
+}
+
+/**
+ * em_utils_forward_quoted:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * Forwards each message in the 'quoted' form (each line starting with
+ * a "> "), each in its own composer window.
+ **/
+void
+em_utils_forward_quoted (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ mail_get_messages (folder, uids, forward_quoted, parent);
+}
+
+/**
+ * em_utils_forward_message:
+ * @parent: parent window
+ * @message: message to be forwarded
+ *
+ * Forwards a message in the user's configured default style.
+ **/
+void
+em_utils_forward_message (GtkWidget *parent, CamelMimeMessage *message)
+{
+ GPtrArray *messages;
+ CamelMimePart *part;
+ GConfClient *gconf;
+ char *subject;
+ int mode;
+
+ messages = g_ptr_array_new ();
+ g_ptr_array_add (messages, message);
+
+ gconf = mail_config_get_gconf_client ();
+ mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL);
+
+ switch (mode) {
+ case MAIL_CONFIG_FORWARD_ATTACHED:
+ default:
+ part = mail_tool_make_message_attachment (message);
+
+ subject = mail_tool_generate_forward_subject (message);
+
+ forward_attached (NULL, messages, part, subject, parent);
+ camel_object_unref (part);
+ g_free (subject);
+ break;
+ case MAIL_CONFIG_FORWARD_INLINE:
+ forward_non_attached(parent, messages, MAIL_CONFIG_FORWARD_INLINE);
+ break;
+ case MAIL_CONFIG_FORWARD_QUOTED:
+ forward_non_attached(parent, messages, MAIL_CONFIG_FORWARD_QUOTED);
+ break;
+ }
+
+ g_ptr_array_free (messages, TRUE);
+}
+
+/**
+ * em_utils_forward_messages:
+ * @parent: parent window
+ * @folder: folder containing messages to forward
+ * @uids: uids of messages to forward
+ *
+ * Forwards a group of messages in the user's configured default
+ * style.
+ **/
+void
+em_utils_forward_messages (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ GConfClient *gconf;
+ int mode;
+
+ gconf = mail_config_get_gconf_client ();
+ mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL);
+
+ switch (mode) {
+ case MAIL_CONFIG_FORWARD_ATTACHED:
+ default:
+ em_utils_forward_attached (parent, folder, uids);
+ break;
+ case MAIL_CONFIG_FORWARD_INLINE:
+ em_utils_forward_inline (parent, folder, uids);
+ break;
+ case MAIL_CONFIG_FORWARD_QUOTED:
+ em_utils_forward_quoted (parent, folder, uids);
+ break;
+ }
+}
+
+/* Redirecting messages... */
+
+static EMsgComposer *
+redirect_get_composer (GtkWidget *parent, CamelMimeMessage *message)
+{
+ EMsgComposer *composer;
+ EAccount *account;
+
+ /* QMail will refuse to send a message if it finds one of
+ it's Delivered-To headers in the message, so remove all
+ Delivered-To headers. Fixes bug #23635. */
+ while (camel_medium_get_header (CAMEL_MEDIUM (message), "Delivered-To"))
+ camel_medium_remove_header (CAMEL_MEDIUM (message), "Delivered-To");
+
+ account = guess_account (message);
+
+ composer = e_msg_composer_new_redirect (message, account ? account->name : NULL);
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ return composer;
+}
+
+/**
+ * em_utils_redirect_message:
+ * @parent: parent window
+ * @message: message to redirect
+ *
+ * Opens a composer to redirect @message (Note: only headers will be
+ * editable). Adds Resent-From/Resent-To/etc headers.
+ **/
+void
+em_utils_redirect_message (GtkWidget *parent, CamelMimeMessage *message)
+{
+ EMsgComposer *composer;
+ CamelDataWrapper *wrapper;
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ composer = redirect_get_composer (parent, message);
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message));
+ if (CAMEL_IS_MULTIPART (wrapper))
+ e_msg_composer_add_message_attachments (composer, message, FALSE);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+ e_msg_composer_drop_editor_undo (composer);
+}
+
+static void
+redirect_msg (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data)
+{
+ if (message == NULL)
+ return;
+
+ em_utils_redirect_message ((GtkWidget *) user_data, message);
+}
+
+/**
+ * em_utils_redirect_message_by_uid:
+ * @parent: parent window
+ * @folder: folder containing message to be redirected
+ * @uid: uid of message to be redirected
+ *
+ * Opens a composer to redirect the message (Note: only headers will
+ * be editable). Adds Resent-From/Resent-To/etc headers.
+ **/
+void
+em_utils_redirect_message_by_uid (GtkWidget *parent, CamelFolder *folder, const char *uid)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uid != NULL);
+
+ mail_get_message (folder, uid, redirect_msg, parent, mail_thread_new);
+}
+
+/* Replying to messages... */
+
+static GHashTable *
+generate_account_hash (void)
+{
+ GHashTable *account_hash;
+ EAccount *account, *def;
+ EAccountList *accounts;
+ EIterator *iter;
+
+ accounts = mail_config_get_accounts ();
+ account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+
+ /* add the default account to the hash first */
+ if ((def = mail_config_get_default_account ())) {
+ if (def->id->address)
+ g_hash_table_insert (account_hash, (char *) def->id->address, (void *) def);
+ }
+
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ account = (EAccount *) e_iterator_get (iter);
+
+ if (account->id->address) {
+ EAccount *acnt;
+
+ /* Accounts with identical email addresses that are enabled
+ * take precedence over the accounts that aren't. If all
+ * accounts with matching email addresses are disabled, then
+ * the first one in the list takes precedence. The default
+ * account always takes precedence no matter what.
+ */
+ acnt = g_hash_table_lookup (account_hash, account->id->address);
+ if (acnt && acnt != def && !acnt->enabled && account->enabled) {
+ g_hash_table_remove (account_hash, acnt->id->address);
+ acnt = NULL;
+ }
+
+ if (!acnt)
+ g_hash_table_insert (account_hash, (char *) account->id->address, (void *) account);
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ return account_hash;
+}
+
+static EDestination **
+em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
+{
+ EDestination *dest, **destv;
+ int n, i, j;
+
+ if (iaddr == NULL)
+ return NULL;
+
+ if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0)
+ return NULL;
+
+ destv = g_malloc (sizeof (EDestination *) * (n + 1));
+ for (i = 0, j = 0; i < n; i++) {
+ const char *name, *addr;
+
+ if (camel_internet_address_get (iaddr, i, &name, &addr)) {
+ dest = e_destination_new ();
+ e_destination_set_name (dest, name);
+ e_destination_set_email (dest, addr);
+
+ destv[j++] = dest;
+ }
+ }
+
+ if (j == 0) {
+ g_free (destv);
+ return NULL;
+ }
+
+ destv[j] = NULL;
+
+ return destv;
+}
+
+static EMsgComposer *
+reply_get_composer (GtkWidget *parent, CamelMimeMessage *message, EAccount *account,
+ CamelInternetAddress *to, CamelInternetAddress *cc)
+{
+ const char *message_id, *references;
+ EDestination **tov, **ccv;
+ EMsgComposer *composer;
+ char *subject;
+
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
+ g_return_val_if_fail (to == NULL || CAMEL_IS_INTERNET_ADDRESS (to), NULL);
+ g_return_val_if_fail (cc == NULL || CAMEL_IS_INTERNET_ADDRESS (cc), NULL);
+
+ composer = e_msg_composer_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ /* construct the tov/ccv */
+ tov = em_utils_camel_address_to_destination (to);
+ ccv = em_utils_camel_address_to_destination (cc);
+
+ /* Set the subject of the new message. */
+ if ((subject = (char *) camel_mime_message_get_subject (message))) {
+ if (strncasecmp (subject, "Re: ", 4) != 0)
+ subject = g_strdup_printf ("Re: %s", subject);
+ else
+ subject = g_strdup (subject);
+ } else {
+ subject = g_strdup ("");
+ }
+
+ e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, ccv, NULL, subject);
+
+ g_free (subject);
+
+ /* Add In-Reply-To and References. */
+ message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id");
+ references = camel_medium_get_header (CAMEL_MEDIUM (message), "References");
+ if (message_id) {
+ char *reply_refs;
+
+ e_msg_composer_add_header (composer, "In-Reply-To", message_id);
+
+ if (references)
+ reply_refs = g_strdup_printf ("%s %s", references, message_id);
+ else
+ reply_refs = g_strdup (message_id);
+
+ e_msg_composer_add_header (composer, "References", reply_refs);
+ g_free (reply_refs);
+ } else if (references) {
+ e_msg_composer_add_header (composer, "References", references);
+ }
+
+ e_msg_composer_drop_editor_undo (composer);
+
+ return composer;
+}
+
+static EAccount *
+guess_account (CamelMimeMessage *message)
+{
+ const CamelInternetAddress *to, *cc;
+ GHashTable *account_hash;
+ EAccount *account = NULL;
+ const char *addr;
+ int i;
+
+ to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
+ cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
+
+ if (to == NULL && cc == NULL)
+ return NULL;
+
+ account_hash = generate_account_hash ();
+
+ if (to) {
+ for (i = 0; camel_internet_address_get (to, i, NULL, &addr); i++) {
+ account = g_hash_table_lookup (account_hash, addr);
+ if (account)
+ goto found;
+ }
+ }
+
+ if (cc) {
+ for (i = 0; camel_internet_address_get (cc, i, NULL, &addr); i++) {
+ account = g_hash_table_lookup (account_hash, addr);
+ if (account)
+ goto found;
+ }
+ }
+
+ found:
+
+ g_hash_table_destroy (account_hash);
+
+ return account;
+}
+
+static void
+get_reply_sender (CamelMimeMessage *message, CamelInternetAddress **to)
+{
+ const CamelInternetAddress *reply_to;
+ const char *name, *addr;
+ int i;
+
+ reply_to = camel_mime_message_get_reply_to (message);
+ if (!reply_to)
+ reply_to = camel_mime_message_get_from (message);
+
+ if (reply_to) {
+ *to = camel_internet_address_new ();
+
+ for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++)
+ camel_internet_address_add (*to, name, addr);
+ }
+}
+
+static gboolean
+get_reply_list (CamelMimeMessage *message, CamelInternetAddress **to)
+{
+ const char *header, *p;
+ char *addr;
+
+ /* Examples:
+ *
+ * List-Post: <mailto:list@host.com>
+ * List-Post: <mailto:moderator@host.com?subject=list%20posting>
+ * List-Post: NO (posting not allowed on this list)
+ */
+ if (!(header = camel_medium_get_header ((CamelMedium *) message, "List-Post")))
+ return FALSE;
+
+ while (*header == ' ' || *header == '\t')
+ header++;
+
+ /* check for NO */
+ if (!strncasecmp (header, "NO", 2))
+ return FALSE;
+
+ /* Search for the first mailto angle-bracket enclosed URL.
+ * (See rfc2369, Section 2, paragraph 3 for details) */
+ if (!(header = camel_strstrcase (header, "<mailto:")))
+ return FALSE;
+
+ header += 8;
+
+ p = header;
+ while (*p && !strchr ("?>", *p))
+ p++;
+
+ addr = g_strndup (header, p - header);
+
+ *to = camel_internet_address_new ();
+ camel_internet_address_add (*to, NULL, addr);
+
+ g_free (addr);
+
+ return TRUE;
+}
+
+static void
+concat_unique_addrs (CamelInternetAddress *dest, const CamelInternetAddress *src, GHashTable *rcpt_hash)
+{
+ const char *name, *addr;
+ int i;
+
+ for (i = 0; camel_internet_address_get (src, i, &name, &addr); i++) {
+ if (!g_hash_table_lookup (rcpt_hash, addr)) {
+ camel_internet_address_add (dest, name, addr);
+ g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1));
+ }
+ }
+}
+
+static void
+get_reply_all (CamelMimeMessage *message, CamelInternetAddress **to, CamelInternetAddress **cc)
+{
+ const CamelInternetAddress *reply_to, *to_addrs, *cc_addrs;
+ const char *name, *addr;
+ GHashTable *rcpt_hash;
+ int i;
+
+ rcpt_hash = generate_account_hash ();
+
+ reply_to = camel_mime_message_get_reply_to (message);
+ if (!reply_to)
+ reply_to = camel_mime_message_get_from (message);
+
+ to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
+ cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
+
+ *to = camel_internet_address_new ();
+ *cc = camel_internet_address_new ();
+
+ if (reply_to) {
+ for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++) {
+ /* ignore references to the Reply-To address in the To and Cc lists */
+ if (addr && !g_hash_table_lookup (rcpt_hash, addr)) {
+ /* In the case that we are doing a Reply-To-All, we do not want
+ to include the user's email address because replying to oneself
+ is kinda silly. */
+
+ camel_internet_address_add (*to, name, addr);
+ g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1));
+ }
+ }
+ }
+
+ concat_unique_addrs (*cc, to_addrs, rcpt_hash);
+ concat_unique_addrs (*cc, cc_addrs, rcpt_hash);
+
+ /* promote the first Cc: address to To: if To: is empty */
+ if (camel_address_length ((CamelAddress *) *to) == 0 && camel_address_length ((CamelAddress *) *cc) > 0) {
+ camel_internet_address_get (*cc, 0, &name, &addr);
+ camel_internet_address_add (*to, name, addr);
+ camel_address_remove ((CamelAddress *) *cc, 0);
+ }
+
+ g_hash_table_destroy (rcpt_hash);
+}
+
+static void
+composer_set_body (EMsgComposer *composer, CamelMimeMessage *message)
+{
+ const CamelInternetAddress *sender;
+ char *text, *credits, format[256];
+ const char *name, *addr;
+ CamelMimePart *part;
+ GConfClient *gconf;
+ time_t date;
+ int date_offset;
+
+ gconf = mail_config_get_gconf_client ();
+
+ switch (gconf_client_get_int (gconf, "/apps/evolution/mail/format/reply_style", NULL)) {
+ case MAIL_CONFIG_REPLY_DO_NOT_QUOTE:
+ /* do nothing */
+ break;
+ case MAIL_CONFIG_REPLY_ATTACH:
+ /* attach the original message as an attachment */
+ part = mail_tool_make_message_attachment (message);
+ e_msg_composer_attach (composer, part);
+ camel_object_unref (part);
+ break;
+ case MAIL_CONFIG_REPLY_QUOTED:
+ default:
+ /* do what any sane user would want when replying... */
+ sender = camel_mime_message_get_from (message);
+ if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) {
+ camel_internet_address_get (sender, 0, &name, &addr);
+ } else {
+ name = _("an unknown sender");
+ }
+
+ date = camel_mime_message_get_date(message, &date_offset);
+ /* Convert to UTC */
+ date += (date_offset / 100) * 60 * 60;
+ date += (date_offset % 100) * 60;
+
+ /* translators: attribution string used when quoting messages,
+ it must contain a single single %%+05d followed by a single '%%s' */
+ e_utf8_strftime(format, sizeof(format), _("On %a, %Y-%m-%d at %H:%M %%+05d, %%s wrote:"), gmtime(&date));
+ credits = g_strdup_printf(format, date_offset, name && *name ? name : addr);
+ text = em_utils_message_to_html(message, credits, EM_FORMAT_QUOTE_CITE);
+ g_free (credits);
+ e_msg_composer_set_body_text(composer, text);
+ g_free (text);
+ break;
+ }
+
+ e_msg_composer_drop_editor_undo (composer);
+}
+
+/**
+ * em_utils_reply_to_message:
+ * @parent: parent window
+ * @message: message to reply to
+ * @mode: reply mode
+ *
+ * Creates a new composer ready to reply to @message.
+ **/
+void
+em_utils_reply_to_message (GtkWidget *parent, CamelMimeMessage *message, int mode)
+{
+ CamelInternetAddress *to = NULL, *cc = NULL;
+ EMsgComposer *composer;
+ EAccount *account;
+
+ account = guess_account (message);
+
+ switch (mode) {
+ case REPLY_MODE_SENDER:
+ get_reply_sender (message, &to);
+ break;
+ case REPLY_MODE_LIST:
+ if (get_reply_list (message, &to))
+ break;
+ case REPLY_MODE_ALL:
+ get_reply_all (message, &to, &cc);
+ break;
+ }
+
+ composer = reply_get_composer (parent, message, account, to, cc);
+ e_msg_composer_add_message_attachments (composer, message, TRUE);
+
+ if (to != NULL)
+ camel_object_unref (to);
+
+ if (cc != NULL)
+ camel_object_unref (cc);
+
+ composer_set_body (composer, message);
+
+ em_composer_utils_setup_default_callbacks (composer);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+}
+
+struct rtm_t {
+ GtkWidget *parent;
+ int mode;
+};
+
+static void
+reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data)
+{
+ CamelInternetAddress *to = NULL, *cc = NULL;
+ struct rtm_t *rtm = user_data;
+ EMsgComposer *composer;
+ EAccount *account;
+ guint32 flags;
+
+ account = guess_account (message);
+ flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
+
+ switch (rtm->mode) {
+ case REPLY_MODE_SENDER:
+ get_reply_sender (message, &to);
+ break;
+ case REPLY_MODE_LIST:
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ if (get_reply_list (message, &to))
+ break;
+ case REPLY_MODE_ALL:
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ get_reply_all (message, &to, &cc);
+ break;
+ }
+
+ composer = reply_get_composer (rtm->parent, message, account, to, cc);
+ e_msg_composer_add_message_attachments (composer, message, TRUE);
+
+ if (to != NULL)
+ camel_object_unref (to);
+
+ if (cc != NULL)
+ camel_object_unref (cc);
+
+ composer_set_body (composer, message);
+
+ em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+}
+
+/**
+ * em_utils_reply_to_message_by_uid:
+ * @parent: parent window
+ * @folder: folder containing message to reply to
+ * @uid: message uid
+ * @mode: reply mode
+ *
+ * Creates a new composer ready to reply to the message referenced by
+ * @folder and @uid.
+ **/
+void
+em_utils_reply_to_message_by_uid (GtkWidget *parent, CamelFolder *folder, const char *uid, int mode)
+{
+ struct rtm_t *rtm;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uid != NULL);
+
+ rtm = g_malloc (sizeof (struct rtm_t));
+ rtm->parent = parent;
+ rtm->mode = mode;
+
+ mail_get_message (folder, uid, reply_to_message, rtm, mail_thread_new);
+}
+
+/* Posting replies... */
+
+static void
+post_reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data)
+{
+ /* FIXME: would be nice if this shared more code with reply_get_composer() */
+ const char *message_id, *references;
+ CamelInternetAddress *to = NULL;
+ GtkWidget *parent = user_data;
+ EDestination **tov = NULL;
+ EMsgComposer *composer;
+ char *subject, *url;
+ EAccount *account;
+ guint32 flags;
+
+ account = guess_account (message);
+ flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
+
+ get_reply_sender (message, &to);
+
+ composer = e_msg_composer_new_post ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) composer, parent);
+
+ /* construct the tov/ccv */
+ tov = em_utils_camel_address_to_destination (to);
+
+ /* Set the subject of the new message. */
+ if ((subject = (char *) camel_mime_message_get_subject (message))) {
+ if (strncasecmp (subject, "Re: ", 4) != 0)
+ subject = g_strdup_printf ("Re: %s", subject);
+ else
+ subject = g_strdup (subject);
+ } else {
+ subject = g_strdup ("");
+ }
+
+ e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, NULL, NULL, subject);
+
+ g_free (subject);
+
+ url = mail_tools_folder_to_url (folder);
+ e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) composer->hdrs, url);
+ g_free (url);
+
+ /* Add In-Reply-To and References. */
+ message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id");
+ references = camel_medium_get_header (CAMEL_MEDIUM (message), "References");
+ if (message_id) {
+ char *reply_refs;
+
+ e_msg_composer_add_header (composer, "In-Reply-To", message_id);
+
+ if (references)
+ reply_refs = g_strdup_printf ("%s %s", references, message_id);
+ else
+ reply_refs = g_strdup (message_id);
+
+ e_msg_composer_add_header (composer, "References", reply_refs);
+ g_free (reply_refs);
+ } else if (references) {
+ e_msg_composer_add_header (composer, "References", references);
+ }
+
+ e_msg_composer_drop_editor_undo (composer);
+
+ e_msg_composer_add_message_attachments (composer, message, TRUE);
+
+ if (to != NULL)
+ camel_object_unref (to);
+
+ composer_set_body (composer, message);
+
+ em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL);
+
+ gtk_widget_show (GTK_WIDGET (composer));
+ e_msg_composer_unset_changed (composer);
+}
+
+/**
+ * em_utils_post_reply_to_message_by_uid:
+ * @parent: parent window
+ * @folder: folder containing message to reply to
+ * @uid: message uid
+ * @mode: reply mode
+ *
+ * Creates a new composer (post mode) ready to reply to the message
+ * referenced by @folder and @uid.
+ **/
+void
+em_utils_post_reply_to_message_by_uid (GtkWidget *parent, CamelFolder *folder, const char *uid)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uid != NULL);
+
+ mail_get_message (folder, uid, post_reply_to_message, parent, mail_thread_new);
+}
+
+/* Saving messages... */
+
+static GtkFileSelection *
+emu_get_save_filesel(GtkWidget *parent, const char *title, const char *name)
+{
+ GtkFileSelection *filesel;
+ char *gdir, *mname = NULL, *filename;
+ const char *realname, *dir;
+ GConfClient *gconf;
+
+ filesel = (GtkFileSelection *)gtk_file_selection_new(title);
+ if (parent)
+ e_dialog_set_transient_for((GtkWindow *)filesel, parent);
+
+ gconf = gconf_client_get_default();
+ dir = gdir = gconf_client_get_string(gconf, "/apps/evolution/mail/save_dir", NULL);
+ g_object_unref(gconf);
+ if (dir == NULL)
+ dir = g_get_home_dir();
+
+ if (name && name[0]) {
+ realname = mname = g_strdup(name);
+ e_filename_make_safe(mname);
+ } else {
+ realname = "/";
+ }
+
+ filename = g_build_filename(dir, realname, NULL);
+ gtk_file_selection_set_filename(filesel, filename);
+ g_free(filename);
+ g_free(mname);
+ g_free (gdir);
+
+ return filesel;
+}
+
+static void
+emu_update_save_path(const char *filename)
+{
+ char *dir = g_path_get_dirname(filename);
+ GConfClient *gconf = gconf_client_get_default();
+
+ gconf_client_set_string(gconf, "/apps/evolution/mail/save_dir", dir, NULL);
+ g_object_unref(gconf);
+ g_free(dir);
+}
+
+static gboolean
+emu_can_save(GtkWindow *parent, const char *path)
+{
+ struct stat st;
+
+ if (path[0] == 0)
+ return FALSE;
+
+ /* make sure we can actually save to it... */
+ if (stat (path, &st) != -1 && !S_ISREG (st.st_mode))
+ return FALSE;
+
+ if (access (path, F_OK) == 0) {
+ if (access (path, W_OK) != 0) {
+ e_notice (parent, GTK_MESSAGE_ERROR,
+ _("Cannot save to `%s'\n %s"), path, g_strerror (errno));
+ return FALSE;
+ }
+
+ return em_utils_prompt_user (parent, GTK_RESPONSE_NO, NULL,
+ _("`%s' already exists.\nOverwrite it?"), path);
+ }
+
+ return TRUE;
+}
+
+static void
+emu_save_part_response(GtkFileSelection *filesel, int response, CamelMimePart *part)
+{
+ if (response == GTK_RESPONSE_OK) {
+ const char *path = gtk_file_selection_get_filename(filesel);
+
+ if (!emu_can_save((GtkWindow *)filesel, path))
+ return;
+
+ emu_update_save_path(path);
+ /* FIXME: popup error if it fails? */
+ mail_save_part(part, path, NULL, NULL);
+ }
+
+ gtk_widget_destroy((GtkWidget *)filesel);
+ camel_object_unref(part);
+}
+
+/**
+ * em_utils_save_part:
+ * @parent: parent window
+ * @prompt: prompt string
+ * @part: part to save
+ *
+ * Saves a mime part to disk (prompting the user for filename).
+ **/
+void
+em_utils_save_part(GtkWidget *parent, const char *prompt, CamelMimePart *part)
+{
+ const char *name;
+ GtkFileSelection *filesel;
+
+ name = camel_mime_part_get_filename(part);
+ if (name == NULL) {
+ if (CAMEL_IS_MIME_MESSAGE(part)) {
+ name = camel_mime_message_get_subject((CamelMimeMessage *)part);
+ if (name == NULL)
+ name = _("message");
+ } else {
+ name = _("attachment");
+ }
+ }
+
+ filesel = emu_get_save_filesel(parent, prompt, name);
+ camel_object_ref(part);
+ g_signal_connect(filesel, "response", G_CALLBACK(emu_save_part_response), part);
+ gtk_widget_show((GtkWidget *)filesel);
+}
+
+
+struct _save_messages_data {
+ CamelFolder *folder;
+ GPtrArray *uids;
+};
+
+static void
+emu_save_messages_response(GtkFileSelection *filesel, int response, struct _save_messages_data *data)
+{
+ if (response == GTK_RESPONSE_OK) {
+ const char *path = gtk_file_selection_get_filename(filesel);
+
+ if (!emu_can_save((GtkWindow *)filesel, path))
+ return;
+
+ emu_update_save_path(path);
+ mail_save_messages(data->folder, data->uids, path, NULL, NULL);
+ data->uids = NULL;
+ }
+
+ camel_object_unref(data->folder);
+ if (data->uids)
+ em_utils_uids_free(data->uids);
+ g_free(data);
+ gtk_widget_destroy((GtkWidget *)filesel);
+}
+
+/**
+ * em_utils_save_messages:
+ * @parent: parent window
+ * @folder: folder containing messages to save
+ * @uids: uids of messages to save
+ *
+ * Saves a group of messages to disk in mbox format (prompting the
+ * user for filename).
+ **/
+void
+em_utils_save_messages (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ struct _save_messages_data *data;
+ GtkFileSelection *filesel;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ filesel = emu_get_save_filesel(parent, _("Save Message..."), NULL);
+ camel_object_ref(folder);
+
+ data = g_malloc(sizeof(struct _save_messages_data));
+ data->folder = folder;
+ data->uids = uids;
+
+ g_signal_connect(filesel, "response", G_CALLBACK(emu_save_messages_response), data);
+ gtk_widget_show((GtkWidget *)filesel);
+}
+
+/* Flag-for-Followup... */
+
+/* tag-editor callback data */
+struct ted_t {
+ MessageTagEditor *editor;
+ CamelFolder *folder;
+ GPtrArray *uids;
+};
+
+static void
+ted_free (struct ted_t *ted)
+{
+ camel_object_unref (ted->folder);
+ em_utils_uids_free (ted->uids);
+ g_free (ted);
+}
+
+static void
+tag_editor_response (GtkWidget *dialog, int button, struct ted_t *ted)
+{
+ CamelFolder *folder;
+ CamelTag *tags, *t;
+ GPtrArray *uids;
+ int i;
+
+ if (button == GTK_RESPONSE_OK && (tags = message_tag_editor_get_tag_list (ted->editor))) {
+ folder = ted->folder;
+ uids = ted->uids;
+
+ camel_folder_freeze (folder);
+ for (i = 0; i < uids->len; i++) {
+ for (t = tags; t; t = t->next)
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], t->name, t->value);
+ }
+
+ camel_folder_thaw (folder);
+ camel_tag_list_free (&tags);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+/**
+ * em_utils_flag_for_followup:
+ * @parent: parent window
+ * @folder: folder containing messages to flag
+ * @uids: uids of messages to flag
+ *
+ * Open the Flag-for-Followup editor for the messages specified by
+ * @folder and @uids.
+ **/
+void
+em_utils_flag_for_followup (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ GtkWidget *editor;
+ struct ted_t *ted;
+ int i;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ editor = (GtkWidget *) message_tag_followup_new ();
+
+ if (parent != NULL)
+ e_dialog_set_transient_for ((GtkWindow *) editor, parent);
+
+ camel_object_ref (folder);
+
+ ted = g_new (struct ted_t, 1);
+ ted->editor = MESSAGE_TAG_EDITOR (editor);
+ ted->folder = folder;
+ ted->uids = uids;
+
+ for (i = 0; i < uids->len; i++) {
+ CamelMessageInfo *info;
+
+ info = camel_folder_get_message_info (folder, uids->pdata[i]);
+ message_tag_followup_append_message (MESSAGE_TAG_FOLLOWUP (editor),
+ camel_message_info_from (info),
+ camel_message_info_subject (info));
+ }
+
+ /* special-case... */
+ if (uids->len == 1) {
+ CamelMessageInfo *info;
+
+ info = camel_folder_get_message_info (folder, uids->pdata[0]);
+ if (info) {
+ if (info->user_tags)
+ message_tag_editor_set_tag_list (MESSAGE_TAG_EDITOR (editor), info->user_tags);
+ camel_folder_free_message_info (folder, info);
+ }
+ }
+
+ g_signal_connect (editor, "response", G_CALLBACK (tag_editor_response), ted);
+ g_object_weak_ref ((GObject *) editor, (GWeakNotify) ted_free, ted);
+
+ gtk_widget_show (editor);
+}
+
+/**
+ * em_utils_flag_for_followup_clear:
+ * @parent: parent window
+ * @folder: folder containing messages to unflag
+ * @uids: uids of messages to unflag
+ *
+ * Clears the Flag-for-Followup flag on the messages referenced by
+ * @folder and @uids.
+ **/
+void
+em_utils_flag_for_followup_clear (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ int i;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ camel_folder_freeze (folder);
+ for (i = 0; i < uids->len; i++) {
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "follow-up", "");
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "due-by", "");
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "completed-on", "");
+ }
+ camel_folder_thaw (folder);
+
+ em_utils_uids_free (uids);
+}
+
+/**
+ * em_utils_flag_for_followup_completed:
+ * @parent: parent window
+ * @folder: folder containing messages to 'complete'
+ * @uids: uids of messages to 'complete'
+ *
+ * Sets the completed state (and date/time) for each message
+ * referenced by @folder and @uids that is marked for
+ * Flag-for-Followup.
+ **/
+void
+em_utils_flag_for_followup_completed (GtkWidget *parent, CamelFolder *folder, GPtrArray *uids)
+{
+ char *now;
+ int i;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (uids != NULL);
+
+ now = header_format_date (time (NULL), 0);
+
+ camel_folder_freeze (folder);
+ for (i = 0; i < uids->len; i++) {
+ const char *tag;
+
+ tag = camel_folder_get_message_user_tag (folder, uids->pdata[i], "follow-up");
+ if (tag == NULL || *tag == '\0')
+ continue;
+
+ camel_folder_set_message_user_tag (folder, uids->pdata[i], "completed-on", now);
+ }
+ camel_folder_thaw (folder);
+
+ g_free (now);
+
+ em_utils_uids_free (uids);
+}
+
+#include "camel/camel-stream-mem.h"
+#include "camel/camel-stream-filter.h"
+#include "camel/camel-mime-filter-from.h"
+
+/* This kind of sucks, because for various reasons most callers need to run synchronously
+ in the gui thread, however this could take a long, blocking time, to run */
+static int
+em_utils_write_messages_to_stream(CamelFolder *folder, GPtrArray *uids, CamelStream *stream)
+{
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilterFrom *from_filter;
+ int i, res = 0;
+
+ from_filter = camel_mime_filter_from_new();
+ filtered_stream = camel_stream_filter_new_with_stream(stream);
+ camel_stream_filter_add(filtered_stream, (CamelMimeFilter *)from_filter);
+ camel_object_unref(from_filter);
+
+ for (i=0; i<uids->len; i++) {
+ CamelMimeMessage *message;
+ char *from;
+
+ message = camel_folder_get_message(folder, uids->pdata[i], NULL);
+ if (message == NULL) {
+ res = -1;
+ break;
+ }
+
+ /* we need to flush after each stream write since we are writing to the same stream */
+ from = camel_mime_message_build_mbox_from(message);
+
+ if (camel_stream_write_string(stream, from) == -1
+ || camel_stream_flush(stream) == -1
+ || camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, (CamelStream *)filtered_stream) == -1
+ || camel_stream_flush((CamelStream *)filtered_stream) == -1)
+ res = -1;
+
+ g_free(from);
+ camel_object_unref(message);
+
+ if (res == -1)
+ break;
+ }
+
+ camel_object_unref(filtered_stream);
+
+ return res;
+}
+
+/* This kind of sucks, because for various reasons most callers need to run synchronously
+ in the gui thread, however this could take a long, blocking time, to run */
+static int
+em_utils_read_messages_from_stream(CamelFolder *folder, CamelStream *stream)
+{
+ CamelException *ex = camel_exception_new();
+ CamelMimeParser *mp = camel_mime_parser_new();
+ int res = -1;
+
+ camel_mime_parser_scan_from(mp, TRUE);
+ camel_mime_parser_init_with_stream(mp, stream);
+ camel_object_unref(stream);
+
+ while (camel_mime_parser_step(mp, 0, 0) == HSCAN_FROM) {
+ CamelMimeMessage *msg;
+
+ /* NB: de-from filter, once written */
+ msg = camel_mime_message_new();
+ if (camel_mime_part_construct_from_parser((CamelMimePart *)msg, mp) == -1) {
+ camel_object_unref(msg);
+ break;
+ }
+
+ camel_folder_append_message(folder, msg, NULL, NULL, ex);
+ camel_object_unref(msg);
+
+ if (camel_exception_is_set (ex))
+ break;
+
+ camel_mime_parser_step(mp, 0, 0);
+ }
+
+ camel_object_unref(mp);
+ if (!camel_exception_is_set(ex))
+ res = 0;
+ camel_exception_free(ex);
+
+ return res;
+}
+
+/**
+ * em_utils_selection_set_mailbox:
+ * @data: selection data
+ * @folder: folder containign messages to copy into the selection
+ * @uids: uids of the messages to copy into the selection
+ *
+ * Creates a mailbox-format selection.
+ * Warning: Could be BIG!
+ * Warning: This could block the ui for an extended period.
+ **/
+void
+em_utils_selection_set_mailbox(GtkSelectionData *data, CamelFolder *folder, GPtrArray *uids)
+{
+ CamelStream *stream;
+
+ stream = camel_stream_mem_new();
+ if (em_utils_write_messages_to_stream(folder, uids, stream) == 0)
+ gtk_selection_data_set(data, data->target, 8,
+ ((CamelStreamMem *)stream)->buffer->data,
+ ((CamelStreamMem *)stream)->buffer->len);
+
+ camel_object_unref(stream);
+}
+
+/**
+ * em_utils_selection_get_mailbox:
+ * @data: selection data
+ * @folder:
+ *
+ * Receive a mailbox selection/dnd
+ * Warning: Could be BIG!
+ * Warning: This could block the ui for an extended period.
+ * FIXME: Exceptions?
+ **/
+void
+em_utils_selection_get_mailbox(GtkSelectionData *data, CamelFolder *folder)
+{
+ CamelStream *stream;
+
+ if (data->data == NULL || data->length == -1)
+ return;
+
+ /* TODO: a stream mem with read-only access to existing data? */
+ /* NB: Although copying would let us run this async ... which it should */
+ stream = camel_stream_mem_new_with_buffer(data->data, data->length);
+ em_utils_read_messages_from_stream(folder, stream);
+ camel_object_unref(stream);
+}
+
+/**
+ * em_utils_selection_set_uidlist:
+ * @data: selection data
+ * @uri:
+ * @uids:
+ *
+ * Sets a "x-evolution-message" format selection data.
+ *
+ * FIXME: be nice if this could take a folder argument rather than uri
+ **/
+void
+em_utils_selection_set_uidlist(GtkSelectionData *data, const char *uri, GPtrArray *uids)
+{
+ GByteArray *array = g_byte_array_new();
+ int i;
+
+ /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn\0" */
+ /* NB: original form missed trailing \0 */
+
+ g_byte_array_append(array, uri, strlen(uri)+1);
+
+ for (i=0; i<uids->len; i++)
+ g_byte_array_append(array, uids->pdata[i], strlen(uids->pdata[i])+1);
+
+ gtk_selection_data_set(data, data->target, 8, array->data, array->len);
+ g_byte_array_free(array, TRUE);
+}
+
+/**
+ * em_utils_selection_get_uidlist:
+ * @data: selection data
+ * @urip: Pointer to uri string, to be free'd by caller
+ * @uidsp: Pointer to an array of uid's.
+ *
+ * Convert an x-evolution-message type to a uri and a uid list.
+ *
+ * Return value: The number of uid's found. If 0, then @urip and
+ * @uidsp will be empty.
+ **/
+int
+em_utils_selection_get_uidlist(GtkSelectionData *data, char **urip, GPtrArray **uidsp)
+{
+ /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn" */
+ char *inptr, *inend;
+ GPtrArray *uids;
+ int res;
+
+ *urip = NULL;
+ *uidsp = NULL;
+
+ if (data == NULL || data->data == NULL || data->length == -1)
+ return 0;
+
+ uids = g_ptr_array_new();
+
+ inptr = data->data;
+ inend = data->data + data->length;
+ while (inptr < inend) {
+ char *start = inptr;
+
+ while (inptr < inend && *inptr)
+ inptr++;
+
+ if (start > (char *)data->data)
+ g_ptr_array_add(uids, g_strndup(start, inptr-start));
+
+ inptr++;
+ }
+
+ if (uids->len == 0) {
+ g_ptr_array_free(uids, TRUE);
+ res = 0;
+ } else {
+ *urip = g_strdup(data->data);
+ *uidsp = uids;
+ res = uids->len;
+ }
+
+ return res;
+}
+
+/**
+ * em_utils_selection_set_urilist:
+ * @data:
+ * @folder:
+ * @uids:
+ *
+ * Set the selection data @data to a uri which points to a file, which is
+ * a berkely mailbox format mailbox. The file is automatically cleaned
+ * up when the application quits.
+ **/
+void
+em_utils_selection_set_urilist(GtkSelectionData *data, CamelFolder *folder, GPtrArray *uids)
+{
+ const char *tmpdir;
+ CamelStream *fstream;
+ char *uri;
+ int fd;
+
+ tmpdir = e_mkdtemp("drag-n-drop-XXXXXX");
+ if (tmpdir == NULL)
+ return;
+
+ /* FIXME: this used to save a single message with the subject
+ as the filename but it was unsafe, and makes this messier,
+ the pain */
+
+ uri = alloca(strlen(tmpdir)+16);
+ sprintf(uri, "file:///%s/mbox", tmpdir);
+
+ fd = open(uri + 7, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd == -1)
+ return;
+
+ fstream = camel_stream_fs_new_with_fd(fd);
+ if (fstream) {
+ if (em_utils_write_messages_to_stream(folder, uids, fstream) == 0)
+ gtk_selection_data_set(data, data->target, 8, uri, strlen(uri));
+
+ camel_object_unref(fstream);
+ }
+}
+
+static void
+emu_save_part_done(CamelMimePart *part, char *name, int done, void *data)
+{
+ ((int *)data)[0] = done;
+}
+
+/**
+ * em_utils_temp_save_part:
+ * @parent:
+ * @part:
+ *
+ * Save a part's content to a temporary file, and return the
+ * filename.
+ *
+ * Return value: NULL if anything failed.
+ **/
+char *
+em_utils_temp_save_part(GtkWidget *parent, CamelMimePart *part)
+{
+ const char *tmpdir, *filename;
+ char *path, *mfilename = NULL;
+ int done;
+
+ tmpdir = e_mkdtemp("evolution-tmp-XXXXXX");
+ if (tmpdir == NULL) {
+ e_notice(parent, GTK_MESSAGE_ERROR,
+ _("Could not create temporary directory: %s"),
+ g_strerror (errno));
+
+ return NULL;
+ }
+
+ filename = camel_mime_part_get_filename (part);
+ if (filename == NULL) {
+ /* This is the default filename used for temporary file creation */
+ filename = _("Unknown");
+ } else {
+ mfilename = g_strdup(filename);
+ e_filename_make_safe(mfilename);
+ filename = mfilename;
+ }
+
+ path = g_build_filename(tmpdir, filename, NULL);
+ g_free(mfilename);
+
+ /* FIXME: This doesn't handle default charsets */
+ mail_msg_wait(mail_save_part(part, path, emu_save_part_done, &done));
+
+ if (!done) {
+ /* mail_save_part should popup an error box automagically */
+ g_free(path);
+ path = NULL;
+ }
+
+ return path;
+}
+
+extern CamelFolder *drafts_folder, *sent_folder, *outbox_folder;
+
+/**
+ * em_utils_folder_is_drafts:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is a Drafts folder.
+ *
+ * Returns %TRUE if this is a Drafts folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_drafts(CamelFolder *folder, const char *uri)
+{
+ EAccountList *accounts;
+ EAccount *account;
+ EIterator *iter;
+ int is = FALSE;
+
+ if (folder == drafts_folder)
+ return TRUE;
+
+ if (uri == NULL)
+ return FALSE;
+
+ accounts = mail_config_get_accounts();
+ iter = e_list_get_iterator((EList *)accounts);
+ while (e_iterator_is_valid(iter)) {
+ account = (EAccount *)e_iterator_get(iter);
+ if (account->drafts_folder_uri &&
+ camel_store_uri_cmp(folder->parent_store, account->drafts_folder_uri, uri)) {
+ is = TRUE;
+ break;
+ }
+
+ e_iterator_next(iter);
+ }
+
+ g_object_unref(iter);
+
+ return is;
+}
+
+/**
+ * em_utils_folder_is_sent:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is a Sent folder
+ *
+ * Returns %TRUE if this is a Sent folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_sent(CamelFolder *folder, const char *uri)
+{
+ EAccountList *accounts;
+ EAccount *account;
+ EIterator *iter;
+ int is = FALSE;
+
+ if (folder == sent_folder)
+ return TRUE;
+
+ if (uri == NULL)
+ return FALSE;
+
+ accounts = mail_config_get_accounts();
+ iter = e_list_get_iterator((EList *)accounts);
+ while (e_iterator_is_valid(iter)) {
+ account = (EAccount *)e_iterator_get(iter);
+ if (account->sent_folder_uri &&
+ camel_store_uri_cmp(folder->parent_store, account->sent_folder_uri, uri)) {
+ is = TRUE;
+ break;
+ }
+
+ e_iterator_next(iter);
+ }
+
+ g_object_unref(iter);
+
+ return is;
+}
+
+/**
+ * em_utils_folder_is_outbox:
+ * @folder: folder
+ * @uri: uri for this folder, if known
+ *
+ * Decides if @folder is an Outbox folder
+ *
+ * Returns %TRUE if this is an Outbox folder or %FALSE otherwise.
+ **/
+gboolean
+em_utils_folder_is_outbox(CamelFolder *folder, const char *uri)
+{
+ /* <Highlander>There can be only one.</Highlander> */
+ return folder == outbox_folder;
+}
+
+/**
+ * em_utils_adjustment_page:
+ * @adj:
+ * @down:
+ *
+ * Move an adjustment up/down forward/back one page.
+ **/
+void
+em_utils_adjustment_page(GtkAdjustment *adj, gboolean down)
+{
+ float page_size = adj->page_size - adj->step_increment;
+
+ if (down) {
+ if (adj->value < adj->upper - adj->page_size - page_size)
+ adj->value += page_size;
+ else if (adj->upper >= adj->page_size)
+ adj->value = adj->upper - adj->page_size;
+ else
+ adj->value = adj->lower;
+ } else {
+ if (adj->value > adj->lower + page_size)
+ adj->value -= page_size;
+ else
+ adj->value = adj->lower;
+ }
+
+ gtk_adjustment_value_changed(adj);
+}
+
+/* ********************************************************************** */
+static char *emu_proxy_uri;
+
+static void
+emu_set_proxy(GConfClient *client)
+{
+ char *server;
+ int port;
+
+ if (!gconf_client_get_bool(client, "/system/http_proxy/use_http_proxy", NULL)) {
+ g_free(emu_proxy_uri);
+ emu_proxy_uri = NULL;
+
+ return;
+ }
+
+ /* TODO: Should lock ... */
+
+ server = gconf_client_get_string(client, "/system/http_proxy/host", NULL);
+ port = gconf_client_get_int(client, "/system/http_proxy/port", NULL);
+
+ if (server && server[0]) {
+ g_free(emu_proxy_uri);
+
+ if (gconf_client_get_bool(client, "/system/http_proxy/use_authentication", NULL)) {
+ char *user = gconf_client_get_string(client, "/system/http_proxy/authentication_user", NULL);
+ char *pass = gconf_client_get_string(client, "/system/http_proxy/authentication_password", NULL);
+
+ emu_proxy_uri = g_strdup_printf("http://%s:%s@%s:%d", user, pass, server, port);
+ g_free(user);
+ g_free(pass);
+ } else {
+ emu_proxy_uri = g_strdup_printf("http://%s:%d", server, port);
+ }
+ }
+
+ g_free(server);
+}
+
+static void
+emu_proxy_changed(GConfClient *client, guint32 cnxn_id, GConfEntry *entry, gpointer user_data)
+{
+ emu_set_proxy(client);
+}
+
+/**
+ * em_utils_get_proxy_uri:
+ *
+ * Get the system proxy uri.
+ *
+ * Return value: Must be freed when finished with.
+ **/
+char *
+em_utils_get_proxy_uri(void)
+{
+ static int init;
+
+ if (!init) {
+ GConfClient *client = gconf_client_get_default();
+
+ gconf_client_add_dir(client, "/system/http_proxy", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ gconf_client_notify_add(client, "/system/http_proxy", emu_proxy_changed, NULL, NULL, NULL);
+ emu_set_proxy(client);
+ g_object_unref(client);
+ init = TRUE;
+ }
+
+ return g_strdup(emu_proxy_uri);
+}
+
+/**
+ * em_utils_part_to_html:
+ * @part:
+ *
+ * Converts a mime part's contents into html text. If @credits is given,
+ * then it will be used as an attribution string, and the
+ * content will be cited. Otherwise no citation or attribution
+ * will be performed.
+ *
+ * Return Value: The part in displayable html format.
+ **/
+char *
+em_utils_part_to_html(CamelMimePart *part)
+{
+ EMFormatQuote *emfq;
+ CamelStreamMem *mem;
+ GByteArray *buf;
+ char *text;
+
+ buf = g_byte_array_new ();
+ mem = (CamelStreamMem *) camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (mem, buf);
+
+ emfq = em_format_quote_new(NULL, (CamelStream *)mem, 0);
+ em_format_part((EMFormat *) emfq, (CamelStream *) mem, part);
+ g_object_unref (emfq);
+
+ camel_stream_write ((CamelStream *) mem, "", 1);
+ camel_object_unref (mem);
+
+ text = buf->data;
+ g_byte_array_free (buf, FALSE);
+
+ return text;
+}
+
+/**
+ * em_utils_message_to_html:
+ * @message:
+ * @credits:
+ * @flags: EMFormatQuote flags
+ *
+ * Convert a message to html, quoting if the @credits attribution
+ * string is given.
+ *
+ * Return value: The html version.
+ **/
+char *
+em_utils_message_to_html(CamelMimeMessage *message, const char *credits, guint32 flags)
+{
+ EMFormatQuote *emfq;
+ CamelStreamMem *mem;
+ GByteArray *buf;
+ char *text;
+
+ buf = g_byte_array_new ();
+ mem = (CamelStreamMem *) camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (mem, buf);
+
+ emfq = em_format_quote_new(credits, (CamelStream *)mem, flags);
+ em_format_format((EMFormat *)emfq, (CamelMedium *)message);
+ g_object_unref (emfq);
+
+ camel_stream_write ((CamelStream *) mem, "", 1);
+ camel_object_unref (mem);
+
+ text = buf->data;
+ g_byte_array_free (buf, FALSE);
+
+ return text;
+}
+
+/* ********************************************************************** */
+
+static gboolean
+emu_confirm_expunge (GtkWidget *parent)
+{
+ gboolean res, show_again;
+ GConfClient *gconf;
+
+ gconf = mail_config_get_gconf_client ();
+
+ if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/expunge", NULL))
+ return TRUE;
+
+ /* FIXME: we need to get the parent GtkWindow from @parent... */
+
+ res = em_utils_prompt_user (NULL, GTK_RESPONSE_NO, &show_again,
+ _("This operation will permanently erase all messages marked as\n"
+ "deleted. If you continue, you will not be able to recover these messages.\n"
+ "\nReally erase these messages?"));
+
+ gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/expunge", show_again, NULL);
+
+ return res;
+}
+
+/**
+ * em_utils_expunge_folder:
+ * @parent: parent window
+ * @folder: folder to expunge
+ *
+ * Expunges @folder.
+ **/
+void
+em_utils_expunge_folder (GtkWidget *parent, CamelFolder *folder)
+{
+ if (!emu_confirm_expunge(parent))
+ return;
+
+ mail_expunge_folder(folder, NULL, NULL);
+}
+
+/**
+ * em_utils_empty_trash:
+ * @parent: parent window
+ *
+ * Empties all Trash folders.
+ **/
+void
+em_utils_empty_trash (GtkWidget *parent)
+{
+ extern CamelSession *session;
+ CamelProvider *provider;
+ EAccountList *accounts;
+ EAccount *account;
+ EIterator *iter;
+ CamelException ex;
+
+ if (!emu_confirm_expunge (parent))
+ return;
+
+ camel_exception_init (&ex);
+
+ /* expunge all remote stores */
+ accounts = mail_config_get_accounts ();
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ account = (EAccount *) e_iterator_get (iter);
+
+ /* make sure this is a valid source */
+ if (account->enabled && account->source->url) {
+ provider = camel_session_get_provider (session, account->source->url, &ex);
+ if (provider) {
+ /* make sure this store is a remote store */
+ if (provider->flags & CAMEL_PROVIDER_IS_STORAGE &&
+ provider->flags & CAMEL_PROVIDER_IS_REMOTE) {
+ mail_empty_trash (account, NULL, NULL);
+ }
+ }
+
+ /* clear the exception for the next round */
+ camel_exception_clear (&ex);
+ }
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+
+ /* Now empty the local trash folder */
+ mail_empty_trash (NULL, NULL, NULL);
+}
diff --git a/mail/em-utils.h b/mail/em-utils.h
new file mode 100644
index 0000000000..0114c86ae2
--- /dev/null
+++ b/mail/em-utils.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __EM_UTILS_H__
+#define __EM_UTILS_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+struct _GtkWidget;
+struct _GtkWindow;
+struct _CamelFolder;
+struct _CamelStream;
+struct _CamelMimeMessage;
+struct _GtkSelectionData;
+struct _GtkAdjustment;
+struct _EMsgComposer;
+
+gboolean em_utils_prompt_user (struct _GtkWindow *parent, int def, gboolean *again, const char *fmt, ...);
+
+GPtrArray *em_utils_uids_copy (GPtrArray *uids);
+void em_utils_uids_free (GPtrArray *uids);
+
+gboolean em_utils_configure_account (struct _GtkWidget *parent);
+gboolean em_utils_check_user_can_send_mail (struct _GtkWidget *parent);
+
+void em_utils_edit_filters (struct _GtkWidget *parent);
+void em_utils_edit_vfolders (struct _GtkWidget *parent);
+
+void em_utils_composer_send_cb(struct _EMsgComposer *composer, gpointer user_data);
+void em_utils_composer_save_draft_cb(struct _EMsgComposer *composer, int quit, gpointer user_data);
+
+void em_utils_compose_new_message (struct _GtkWidget *parent);
+
+/* FIXME: mailto? url? should make up its mind what its called. imho use 'uri' */
+void em_utils_compose_new_message_with_mailto (struct _GtkWidget *parent, const char *url);
+void em_utils_post_to_url (struct _GtkWidget *parent, const char *url);
+
+void em_utils_edit_message (struct _GtkWidget *parent, struct _CamelMimeMessage *message);
+void em_utils_edit_messages (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+
+void em_utils_forward_attached (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+void em_utils_forward_inline (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+void em_utils_forward_quoted (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+
+void em_utils_forward_message(struct _GtkWidget *parent, struct _CamelMimeMessage *msg);
+void em_utils_forward_messages(struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+
+void em_utils_redirect_message (struct _GtkWidget *parent, struct _CamelMimeMessage *message);
+void em_utils_redirect_message_by_uid (struct _GtkWidget *parent, struct _CamelFolder *folder, const char *uid);
+
+enum {
+ REPLY_MODE_SENDER,
+ REPLY_MODE_ALL,
+ REPLY_MODE_LIST
+};
+
+void em_utils_reply_to_message (struct _GtkWidget *parent, struct _CamelMimeMessage *message, int mode);
+void em_utils_reply_to_message_by_uid (struct _GtkWidget *parent, struct _CamelFolder *folder, const char *uid, int mode);
+
+void em_utils_post_reply_to_message_by_uid (struct _GtkWidget *parent, struct _CamelFolder *folder, const char *uid);
+
+void em_utils_save_part(struct _GtkWidget *parent, const char *prompt, struct _CamelMimePart *part);
+void em_utils_save_messages (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+
+void em_utils_flag_for_followup (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+void em_utils_flag_for_followup_clear (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+void em_utils_flag_for_followup_completed (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids);
+
+/* This stuff that follows probably doesn't belong here, then again, the stuff above probably belongs elsewhere */
+
+void em_utils_selection_set_mailbox(struct _GtkSelectionData *data, struct _CamelFolder *folder, GPtrArray *uids);
+void em_utils_selection_get_mailbox(struct _GtkSelectionData *data, struct _CamelFolder *folder);
+/* FIXME: be nice if these also worked on struct _CamelFolder's, no easy way to get uri from folder yet tho */
+void em_utils_selection_set_uidlist(struct _GtkSelectionData *data, const char *uri, GPtrArray *uids);
+int em_utils_selection_get_uidlist(struct _GtkSelectionData *data, char **uri, GPtrArray **uidsp);
+void em_utils_selection_set_urilist(struct _GtkSelectionData *data, struct _CamelFolder *folder, GPtrArray *uids);
+
+char *em_utils_temp_save_part(struct _GtkWidget *parent, struct _CamelMimePart *part);
+
+gboolean em_utils_folder_is_drafts(struct _CamelFolder *folder, const char *uri);
+gboolean em_utils_folder_is_sent(struct _CamelFolder *folder, const char *uri);
+gboolean em_utils_folder_is_outbox(struct _CamelFolder *folder, const char *uri);
+
+void em_utils_adjustment_page(struct _GtkAdjustment *adj, gboolean down);
+
+char *em_utils_get_proxy_uri(void);
+
+/* FIXME: should this have an override charset? */
+char *em_utils_part_to_html(struct _CamelMimePart *part);
+char *em_utils_message_to_html(struct _CamelMimeMessage *msg, const char *credits, guint32 flags);
+
+void em_utils_expunge_folder (struct _GtkWidget *parent, struct _CamelFolder *folder);
+void em_utils_empty_trash (struct _GtkWidget *parent);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __EM_UTILS_H__ */
diff --git a/mail/evolution-mail.schemas b/mail/evolution-mail.schemas
index 35ec615733..a5a761d8b3 100644
--- a/mail/evolution-mail.schemas
+++ b/mail/evolution-mail.schemas
@@ -177,21 +177,7 @@
<key>/schemas/apps/evolution/mail/display/caret_mode</key>
<applyto>/apps/evolution/mail/display/caret_mode</applyto>
<owner>evolution-mail</owner>
- <type>boolean</type>
- <default>false</default>
- <locale name="C">
- <short>Enable/disable caret mode</short>
- <long>
- Enable caret mode, so that you can see a cursor when reading mail.
- </long>
- </locale>
- </schema>
-
- <schema>
- <key>/schemas/apps/evolution/mail/display/caret_mode</key>
- <applyto>/apps/evolution/mail/display/caret_mode</applyto>
- <owner>evolution-mail</owner>
- <type>boolean</type>
+ <type>bool</type>
<default>false</default>
<locale name="C">
<short>Enable/disable caret mode</short>
diff --git a/mail/folder-browser-factory.c b/mail/folder-browser-factory.c
index 35e1aeb2ab..f0157bac9a 100644
--- a/mail/folder-browser-factory.c
+++ b/mail/folder-browser-factory.c
@@ -44,16 +44,16 @@
#include "folder-browser-factory.h"
-#include "folder-browser.h"
-#include "folder-browser-ui.h"
#include "mail.h"
-#include "mail-callbacks.h"
#include "shell/Evolution.h"
#include "mail-config.h"
#include "mail-ops.h"
#include "mail-session.h"
#include "mail-folder-cache.h"
+#include "em-folder-browser.h"
+#include "em-format.h"
+
#include "evolution-shell-component-utils.h"
/* The FolderBrowser BonoboControls we have. */
@@ -85,79 +85,30 @@ fb_get_svi (BonoboControl *control)
}
static void
-control_activate (BonoboControl *control,
- BonoboUIComponent *uic,
- FolderBrowser *fb)
-{
- GtkWidget *folder_browser;
- Bonobo_UIContainer container;
- GNOME_Evolution_ShellView svi;
-
- container = bonobo_control_get_remote_ui_container (control, NULL);
- bonobo_ui_component_set_container (uic, container, NULL);
- bonobo_object_release_unref (container, NULL);
-
- g_assert (container == bonobo_ui_component_get_container (uic));
- g_return_if_fail (container != CORBA_OBJECT_NIL);
-
- folder_browser = bonobo_control_get_widget (control);
- folder_browser_set_ui_component (FOLDER_BROWSER (folder_browser), uic);
-
- /*bonobo_ui_component_freeze (uic, NULL);*/
-
- folder_browser_ui_add_global (fb);
- folder_browser_ui_add_list (fb);
- folder_browser_ui_add_message (fb);
-
- /*bonobo_ui_component_thaw (uic, NULL);*/
-
- svi = fb_get_svi (control);
- folder_browser_set_shell_view (fb, svi);
- bonobo_object_release_unref (svi, NULL);
-
- folder_browser_reload (fb);
-
- e_search_bar_set_ui_component (E_SEARCH_BAR (fb->search), uic);
-}
-
-static void
-control_deactivate (BonoboControl *control,
- BonoboUIComponent *uic,
- FolderBrowser *fb)
-{
- /*bonobo_ui_component_freeze (uic, NULL);*/
-
- folder_browser_ui_rm_list (fb);
- folder_browser_ui_rm_all (fb);
-
- /*bonobo_ui_component_thaw (uic, NULL);*/
-
- if (fb->folder)
- mail_sync_folder (fb->folder, NULL, NULL);
-
- if (fb->message_list)
- message_list_save_state (fb->message_list);
-
- folder_browser_set_ui_component (fb, NULL);
- folder_browser_set_shell_view (fb, CORBA_OBJECT_NIL);
-
- e_search_bar_set_ui_component (E_SEARCH_BAR (fb->search), NULL);
-}
-
-static void
control_activate_cb (BonoboControl *control,
gboolean activate,
gpointer user_data)
{
BonoboUIComponent *uic;
- uic = bonobo_control_get_ui_component (control);
+ uic = bonobo_control_get_ui_component(control);
g_assert (uic != NULL);
- if (activate)
- control_activate (control, uic, user_data);
- else
- control_deactivate (control, uic, user_data);
+ if (activate) {
+ Bonobo_UIContainer container;
+
+ container = bonobo_control_get_remote_ui_container(control, NULL);
+ bonobo_ui_component_set_container(uic, container, NULL);
+ bonobo_object_release_unref(container, NULL);
+
+ g_assert(container == bonobo_ui_component_get_container(uic));
+ g_return_if_fail(container != CORBA_OBJECT_NIL);
+
+ em_folder_view_activate(user_data, uic, activate);
+ } else {
+ em_folder_view_activate(user_data, uic, activate);
+ bonobo_ui_component_unset_container(uic, NULL);
+ }
}
static void
@@ -172,14 +123,18 @@ folder_browser_factory_new_control (const char *uri,
{
BonoboControl *control;
GtkWidget *fb;
-
+
+#if 0
if (!(fb = folder_browser_new (shell, uri)))
return NULL;
FOLDER_BROWSER (fb)->pref_master = TRUE; /* save UI settings changed in this FB */
-
+#endif
+ fb = em_folder_browser_new();
gtk_widget_show (fb);
-
+ em_folder_view_set_folder_uri((EMFolderView *)fb, uri);
+ em_format_set_session((EMFormat *)((EMFolderView *)fb)->preview, session);
+
control = bonobo_control_new (fb);
if (control == NULL) {
@@ -207,14 +162,16 @@ folder_browser_factory_get_control_list (void)
return control_list;
}
-FolderBrowser *
+struct _EMFolderBrowser *
folder_browser_factory_get_browser(const char *uri)
{
EList *controls;
EIterator *it;
BonoboControl *control;
- FolderBrowser *fb = NULL;
-
+ EMFolderBrowser *fb = NULL;
+
+ return NULL;
+
if (control_list == NULL)
return NULL;
@@ -223,8 +180,8 @@ folder_browser_factory_get_browser(const char *uri)
it = e_list_get_iterator (controls);
while (e_iterator_is_valid (it)) {
control = BONOBO_CONTROL (e_iterator_get (it));
- fb = FOLDER_BROWSER (bonobo_control_get_widget (control));
- if (fb->uri && strcmp (fb->uri, uri) == 0)
+ fb = (EMFolderBrowser *)bonobo_control_get_widget(control);
+ if (((EMFolderView *)fb)->folder_uri && strcmp (((EMFolderView *)fb)->folder_uri, uri) == 0)
break;
fb = NULL;
diff --git a/mail/folder-browser-factory.h b/mail/folder-browser-factory.h
index 84ebd7a97b..2c858c9c56 100644
--- a/mail/folder-browser-factory.h
+++ b/mail/folder-browser-factory.h
@@ -14,12 +14,11 @@
#include <bonobo/bonobo-control.h>
#include "Evolution.h"
#include "e-util/e-list.h"
-#include "folder-browser.h"
BonoboControl *folder_browser_factory_new_control (const char *uri,
const GNOME_Evolution_Shell shell);
EList *folder_browser_factory_get_control_list (void);
-FolderBrowser *folder_browser_factory_get_browser(const char *uri);
+struct _EMFolderBrowser *folder_browser_factory_get_browser(const char *uri);
#endif /* _FOLDER_BROWSER_FACTORY_H */
diff --git a/mail/folder-browser-ui.c b/mail/folder-browser-ui.c
deleted file mode 100644
index 9ccdb2db79..0000000000
--- a/mail/folder-browser-ui.c
+++ /dev/null
@@ -1,816 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Peter Williams <peterw@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2002 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <libgnome/gnome-util.h> /* gnome_util_prepend_user_home */
-
-#include <bonobo/bonobo-exception.h>
-#include <bonobo/bonobo-ui-component.h>
-#include <bonobo/bonobo-ui-util.h>
-
-#include "widgets/misc/e-charset-picker.h"
-#include "widgets/menus/gal-view-menus.h" /* GalView stuff */
-#include <gal/menus/gal-view-factory-etable.h>
-#include <gal/menus/gal-view-etable.h>
-
-#include "e-util/e-meta.h"
-
-#include "mail-config.h"
-#include "mail-callbacks.h" /* almost all the verbs */
-#include "mail-session.h" /* mail_session_forget_passwords */
-
-#include "folder-browser-ui.h"
-
-#include "evolution-shell-component-utils.h" /* Pixmap stuff */
-
-
-/*
- * Add with 'folder_browser'
- */
-
-static BonoboUIVerb message_verbs [] = {
- BONOBO_UI_UNSAFE_VERB ("MailNext", next_msg),
- BONOBO_UI_UNSAFE_VERB ("MailNextFlagged", next_flagged_msg),
- BONOBO_UI_UNSAFE_VERB ("MailNextUnread", next_unread_msg),
- BONOBO_UI_UNSAFE_VERB ("MailNextThread", next_thread),
- BONOBO_UI_UNSAFE_VERB ("MailPrevious", previous_msg),
- BONOBO_UI_UNSAFE_VERB ("MailPreviousFlagged", previous_flagged_msg),
- BONOBO_UI_UNSAFE_VERB ("MailPreviousUnread", previous_unread_msg),
- BONOBO_UI_UNSAFE_VERB ("AddSenderToAddressbook", add_sender_to_addrbook),
- BONOBO_UI_UNSAFE_VERB ("MessageApplyFilters", apply_filters),
- BONOBO_UI_UNSAFE_VERB ("MessageCopy", copy_msg),
- BONOBO_UI_UNSAFE_VERB ("MessageDelete", delete_msg),
- BONOBO_UI_UNSAFE_VERB ("MessageForward", forward),
- BONOBO_UI_UNSAFE_VERB ("MessageForwardAttached", forward_attached),
- BONOBO_UI_UNSAFE_VERB ("MessageForwardInline", forward_inline),
- BONOBO_UI_UNSAFE_VERB ("MessageForwardQuoted", forward_quoted),
- BONOBO_UI_UNSAFE_VERB ("MessageRedirect", redirect),
- BONOBO_UI_UNSAFE_VERB ("MessageMarkAsRead", mark_as_seen),
- BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnRead", mark_as_unseen),
- BONOBO_UI_UNSAFE_VERB ("MessageMarkAsImportant", mark_as_important),
- BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnimportant", mark_as_unimportant),
- BONOBO_UI_UNSAFE_VERB ("MessageFollowUpFlag", flag_for_followup),
- BONOBO_UI_UNSAFE_VERB ("MessageMove", move_msg),
- BONOBO_UI_UNSAFE_VERB ("MessageOpen", open_message),
- BONOBO_UI_UNSAFE_VERB ("MessagePostReply", post_reply),
- BONOBO_UI_UNSAFE_VERB ("MessageReplyAll", reply_to_all),
- BONOBO_UI_UNSAFE_VERB ("MessageReplyList", reply_to_list),
- BONOBO_UI_UNSAFE_VERB ("MessageReplySender", reply_to_sender),
- BONOBO_UI_UNSAFE_VERB ("MessageResend", resend_msg),
- BONOBO_UI_UNSAFE_VERB ("MessageSaveAs", save_msg),
- BONOBO_UI_UNSAFE_VERB ("MessageSearch", search_msg),
- BONOBO_UI_UNSAFE_VERB ("MessageUndelete", undelete_msg),
- BONOBO_UI_UNSAFE_VERB ("PrintMessage", print_msg),
- BONOBO_UI_UNSAFE_VERB ("TextZoomIn", zoom_in),
- BONOBO_UI_UNSAFE_VERB ("TextZoomOut", zoom_out),
- BONOBO_UI_UNSAFE_VERB ("TextZoomReset", zoom_reset),
- BONOBO_UI_UNSAFE_VERB ("PrintPreviewMessage", print_preview_msg),
- BONOBO_UI_UNSAFE_VERB ("ToolsFilterMailingList", filter_mlist),
- BONOBO_UI_UNSAFE_VERB ("ToolsFilterRecipient", filter_recipient),
- BONOBO_UI_UNSAFE_VERB ("ToolsFilterSender", filter_sender),
- BONOBO_UI_UNSAFE_VERB ("ToolsFilterSubject", filter_subject),
- BONOBO_UI_UNSAFE_VERB ("ToolsVFolderMailingList", vfolder_mlist),
- BONOBO_UI_UNSAFE_VERB ("ToolsVFolderRecipient", vfolder_recipient),
- BONOBO_UI_UNSAFE_VERB ("ToolsVFolderSender", vfolder_sender),
- BONOBO_UI_UNSAFE_VERB ("ToolsVFolderSubject", vfolder_subject),
- BONOBO_UI_UNSAFE_VERB ("ViewLoadImages", load_images),
- /* ViewHeaders stuff is a radio */
- /* CaretMode is a toggle */
-
- BONOBO_UI_VERB_END
-};
-
-static BonoboUIVerb list_verbs [] = {
- BONOBO_UI_UNSAFE_VERB ("EditCut", folder_browser_cut),
- BONOBO_UI_UNSAFE_VERB ("EditCopy", folder_browser_copy),
- BONOBO_UI_UNSAFE_VERB ("EditPaste", folder_browser_paste),
- BONOBO_UI_UNSAFE_VERB ("EditInvertSelection", invert_selection),
- BONOBO_UI_UNSAFE_VERB ("EditSelectAll", select_all),
- BONOBO_UI_UNSAFE_VERB ("EditSelectThread", select_thread),
- BONOBO_UI_UNSAFE_VERB ("ChangeFolderProperties", configure_folder),
- BONOBO_UI_UNSAFE_VERB ("FolderExpunge", expunge_folder),
- /* HideDeleted is a toggle */
- BONOBO_UI_UNSAFE_VERB ("MessageMarkAllAsRead", mark_all_as_seen),
- BONOBO_UI_UNSAFE_VERB ("ViewHideRead", hide_read),
- BONOBO_UI_UNSAFE_VERB ("ViewHideSelected", hide_selected),
- BONOBO_UI_UNSAFE_VERB ("ViewShowAll", hide_none),
- /* ViewThreaded is a toggle */
-
- BONOBO_UI_VERB_END
-};
-
-static BonoboUIVerb global_verbs [] = {
- BONOBO_UI_UNSAFE_VERB ("EmptyTrash", empty_trash),
- BONOBO_UI_UNSAFE_VERB ("ForgetPasswords", mail_session_forget_passwords),
- BONOBO_UI_UNSAFE_VERB ("MailCompose", compose_msg),
- BONOBO_UI_UNSAFE_VERB ("MailPost", post_message),
- BONOBO_UI_UNSAFE_VERB ("MailStop", stop_threads),
- BONOBO_UI_UNSAFE_VERB ("ToolsFilters", filter_edit),
- BONOBO_UI_UNSAFE_VERB ("ToolsSubscriptions", manage_subscriptions),
- BONOBO_UI_UNSAFE_VERB ("ToolsVFolders", vfolder_edit_vfolders),
- /* ViewPreview is a toggle */
-
- BONOBO_UI_VERB_END
-};
-
-static EPixmap message_pixcache [] = {
- E_PIXMAP ("/commands/PrintMessage", "print.xpm"),
- E_PIXMAP ("/commands/PrintPreviewMessage", "print-preview.xpm"),
- E_PIXMAP ("/commands/MessageDelete", "evolution-trash-mini.png"),
- E_PIXMAP ("/commands/MessageUndelete", "undelete_message-16.png"),
- E_PIXMAP ("/commands/MessageCopy", "copy_16_message.xpm"),
- E_PIXMAP ("/commands/MessageMove", "move_message.xpm"),
- E_PIXMAP ("/commands/MessageReplyAll", "reply_to_all.xpm"),
- E_PIXMAP ("/commands/MessageReplySender", "reply.xpm"),
- E_PIXMAP ("/commands/MessageForward", "forward.xpm"),
- E_PIXMAP ("/commands/MessageApplyFilters", "apply-filters-16.xpm"),
- E_PIXMAP ("/commands/MessageSearch", "search-16.png"),
- E_PIXMAP ("/commands/MessageSaveAs", "save-as-16.png"),
- E_PIXMAP ("/commands/MessageMarkAsRead", "mail-read.xpm"),
- E_PIXMAP ("/commands/MessageMarkAsUnRead", "mail-new.xpm"),
- E_PIXMAP ("/commands/MessageMarkAsImportant", "priority-high.xpm"),
- E_PIXMAP ("/commands/MessageFollowUpFlag", "flag-for-followup-16.png"),
-
- E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageReplySender", "buttons/reply.png"),
- E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageReplyAll", "buttons/reply-to-all.png"),
- E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageForward", "buttons/forward.png"),
- E_PIXMAP ("/Toolbar/MailMessageToolbar/PrintMessage", "buttons/print.png"),
- E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageMove", "buttons/move-message.png"),
- E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageCopy", "buttons/copy-message.png"),
- E_PIXMAP ("/Toolbar/MailMessageToolbar/MessageDelete", "buttons/delete-message.png"),
-
- E_PIXMAP ("/Toolbar/MailNextButtons/MailNext", "buttons/next-message.png"),
- E_PIXMAP ("/Toolbar/MailNextButtons/MailPrevious", "buttons/previous-message.png"),
-
- E_PIXMAP_END
-};
-
-static EPixmap list_pixcache [] = {
- E_PIXMAP ("/commands/ChangeFolderProperties", "configure_16_folder.xpm"),
- E_PIXMAP ("/commands/ViewHideRead", "hide_read_messages.xpm"),
- E_PIXMAP ("/commands/ViewHideSelected", "hide_selected_messages.xpm"),
- E_PIXMAP ("/commands/ViewShowAll", "show_all_messages.xpm"),
-
- E_PIXMAP ("/commands/EditCut", "16_cut.png"),
- E_PIXMAP ("/commands/EditCopy", "16_copy.png"),
- E_PIXMAP ("/commands/EditPaste", "16_paste.png"),
-
- E_PIXMAP_END
-};
-
-static EPixmap global_pixcache [] = {
- E_PIXMAP ("/commands/MailCompose", "new-message.xpm"),
-
- E_PIXMAP_END
-};
-
-enum {
- IS_DRAFTS_FOLDER = (1 << 0),
- IS_OUTBOX_FOLDER = (1 << 1),
- IS_SENT_FOLDER = (1 << 2),
-
- IS_OUTGOING_FOLDER = (IS_DRAFTS_FOLDER | IS_OUTBOX_FOLDER | IS_SENT_FOLDER),
- IS_INCOMING_FOLDER = (1 << 3),
-
- IS_ANY_FOLDER = (IS_OUTGOING_FOLDER | IS_INCOMING_FOLDER),
-
- SELECTION_NONE = (1 << 4),
- SELECTION_SINGLE = (1 << 5),
- SELECTION_MULTIPLE = (1 << 6),
-
- SELECTION_ANYTHING = (SELECTION_SINGLE | SELECTION_MULTIPLE),
-
- IS_THREADED = (1 << 7),
- NOT_THREADED = (1<<8),
- ANY_THREADED = (IS_THREADED|NOT_THREADED),
-
- HAS_UNDELETED = (1 << 9),
- HAS_DELETED = (1 << 10),
- HAS_UNREAD = (1 << 11),
- HAS_READ = (1 << 12),
- HAS_UNIMPORTANT = (1 << 13),
- HAS_IMPORTANT = (1 << 14)
-};
-
-#define HAS_FLAGS (HAS_UNDELETED | HAS_DELETED | \
- HAS_UNREAD | HAS_READ | \
- HAS_UNIMPORTANT | HAS_IMPORTANT)
-
-#define IS_1MESSAGE (IS_ANY_FOLDER | SELECTION_SINGLE | ANY_THREADED | HAS_FLAGS)
-#define IS_0MESSAGE (IS_ANY_FOLDER | SELECTION_ANYTHING | SELECTION_NONE | ANY_THREADED | HAS_FLAGS)
-#define IS_NMESSAGE (IS_ANY_FOLDER | SELECTION_ANYTHING | ANY_THREADED | HAS_FLAGS)
-
-struct _UINode {
- const char *name;
- guint32 enable_mask;
-};
-
-struct _UINode default_ui_nodes[] = {
- { "ViewLoadImages", IS_1MESSAGE },
- { "ViewFullHeaders", IS_0MESSAGE },
- { "ViewNormal", IS_0MESSAGE },
- { "ViewSource", IS_0MESSAGE },
- { "CaretMode", IS_0MESSAGE },
-
- { "AddSenderToAddressbook", IS_INCOMING_FOLDER | SELECTION_SINGLE | ANY_THREADED | HAS_FLAGS },
-
- { "MessageResend", IS_SENT_FOLDER | SELECTION_SINGLE | ANY_THREADED | HAS_FLAGS },
-
- /* actions that work on exactly 1 message */
- { "MessagePostReply", IS_1MESSAGE },
- { "MessageReplyAll", IS_1MESSAGE },
- { "MessageReplyList", IS_1MESSAGE },
- { "MessageReplySender", IS_1MESSAGE },
- { "MessageForwardInline", IS_1MESSAGE },
- { "MessageForwardQuoted", IS_1MESSAGE },
- { "MessageRedirect", IS_1MESSAGE },
- { "MessageSearch", IS_1MESSAGE },
-
- { "PrintMessage", IS_1MESSAGE },
- { "PrintPreviewMessage", IS_1MESSAGE },
-
- { "ToolsFilterMailingList", IS_1MESSAGE },
- { "ToolsFilterRecipient", IS_1MESSAGE },
- { "ToolsFilterSender", IS_1MESSAGE },
- { "ToolsFilterSubject", IS_1MESSAGE },
-
- { "ToolsVFolderMailingList", IS_1MESSAGE },
- { "ToolsVFolderRecipient", IS_1MESSAGE },
- { "ToolsVFolderSender", IS_1MESSAGE },
- { "ToolsVFolderSubject", IS_1MESSAGE },
-
- /* actions that work on >= 1 message */
- { "MessageApplyFilters", IS_NMESSAGE },
- { "MessageCopy", IS_NMESSAGE },
- { "MessageMove", IS_NMESSAGE },
- { "MessageDelete", IS_NMESSAGE },
- { "MessageUndelete", IS_NMESSAGE & ~HAS_DELETED },
- { "MessageMarkAsRead", IS_NMESSAGE & ~HAS_UNREAD },
- { "MessageMarkAsUnRead", IS_NMESSAGE & ~HAS_READ },
- { "MessageMarkAsImportant", IS_NMESSAGE & ~HAS_UNIMPORTANT },
- { "MessageMarkAsUnimportant", IS_NMESSAGE & ~HAS_IMPORTANT },
- { "MessageFollowUpFlag", IS_NMESSAGE },
- { "MessageOpen", IS_NMESSAGE },
- { "MessageSaveAs", IS_NMESSAGE },
- { "MessageForward", IS_NMESSAGE },
- { "MessageForwardAttached", IS_NMESSAGE },
-
- { "EditCut", IS_NMESSAGE },
- { "EditCopy", IS_NMESSAGE },
- { "EditPaste", IS_0MESSAGE },
- { "EditSelectThread", IS_ANY_FOLDER | SELECTION_ANYTHING | IS_THREADED | HAS_FLAGS},
-
- { "ViewHideSelected", IS_NMESSAGE },
-
- /* FIXME: should these be single-selection? */
- { "MailNext", IS_NMESSAGE },
- { "MailNextFlagged", IS_NMESSAGE },
- { "MailNextUnread", IS_NMESSAGE },
- { "MailNextThread", IS_NMESSAGE },
- { "MailPrevious", IS_NMESSAGE },
- { "MailPreviousFlagged", IS_NMESSAGE },
- { "MailPreviousUnread", IS_NMESSAGE },
-};
-
-static int num_default_ui_nodes = sizeof (default_ui_nodes) / sizeof (default_ui_nodes[0]);
-
-
-static void
-ui_add (FolderBrowser *fb, const char *name, BonoboUIVerb verb[], EPixmap pixcache[])
-{
- BonoboUIComponent *uic = fb->uicomp;
- char *file;
-
- bonobo_ui_component_add_verb_list_with_data (uic, verb, fb);
-
- /*bonobo_ui_component_freeze (uic, NULL);*/
-
- file = g_strconcat (EVOLUTION_UIDIR "/evolution-mail-", name, ".xml", NULL);
- bonobo_ui_util_set_ui (uic, PREFIX, file, "evolution-mail", NULL);
- g_free (file);
-
- e_pixmaps_update (uic, pixcache);
-
- /*bonobo_ui_component_thaw (uic, NULL);*/
-}
-
-/* more complex stuff */
-
-static void
-display_view (GalViewInstance *instance, GalView *view, gpointer data)
-{
- FolderBrowser *fb = data;
-
- if (GAL_IS_VIEW_ETABLE (view)) {
- gal_view_etable_attach_tree (GAL_VIEW_ETABLE (view), fb->message_list->tree);
- }
-}
-
-void
-folder_browser_ui_setup_view_menus (FolderBrowser *fb)
-{
- static GalViewCollection *collection = NULL;
- char *id;
- gboolean outgoing;
-
- if (fb->uicomp == NULL || fb->folder == NULL)
- return;
-
- g_assert (fb->view_instance == NULL);
- g_assert (fb->view_menus == NULL);
-
- outgoing = folder_browser_is_drafts (fb) ||
- folder_browser_is_sent (fb) ||
- folder_browser_is_outbox (fb);
-
- if (collection == NULL) {
- ETableSpecification *spec;
- char *local_dir;
- GalViewFactory *factory;
-
- collection = gal_view_collection_new ();
-
- gal_view_collection_set_title (collection, _("Mail"));
-
- local_dir = gnome_util_prepend_user_home ("/evolution/views/mail/");
- gal_view_collection_set_storage_directories (collection,
- EVOLUTION_GALVIEWSDIR "/mail/",
- local_dir);
- g_free (local_dir);
-
- spec = e_table_specification_new ();
- e_table_specification_load_from_file (spec, EVOLUTION_ETSPECDIR "/message-list.etspec");
-
- factory = gal_view_factory_etable_new (spec);
- g_object_unref (spec);
- gal_view_collection_add_factory (collection, factory);
- g_object_unref (factory);
-
- gal_view_collection_load (collection);
- }
-
- id = mail_config_folder_to_safe_url (fb->folder);
- fb->view_instance = gal_view_instance_new (collection, id);
- g_free (id);
-
- if (outgoing)
- gal_view_instance_set_default_view (fb->view_instance, "As_Sent_Folder");
-
- if (!gal_view_instance_exists (fb->view_instance)) {
- char *path;
- struct stat st;
-
- gal_view_instance_load (fb->view_instance);
-
- path = mail_config_folder_to_cachename (fb->folder, "et-header-");
- if (path && stat (path, &st) == 0 && st.st_size > 0 && S_ISREG (st.st_mode)) {
- ETableSpecification *spec;
- ETableState *state;
- GalView *view;
-
- spec = e_table_specification_new();
- e_table_specification_load_from_file (spec, EVOLUTION_ETSPECDIR "/message-list.etspec");
- view = gal_view_etable_new (spec, "");
- g_object_unref (spec);
-
- state = e_table_state_new ();
- e_table_state_load_from_file (state, path);
- gal_view_etable_set_state (GAL_VIEW_ETABLE (view), state);
- g_object_unref (state);
-
- gal_view_instance_set_custom_view (fb->view_instance, view);
- g_object_unref (view);
- }
- g_free (path);
- }
-
- fb->view_menus = gal_view_menus_new (fb->view_instance);
- gal_view_menus_apply (fb->view_menus, fb->uicomp, NULL);
-
- /* Due to CORBA reentrancy, the view could be gone now. */
- if (fb->view_instance == NULL)
- return;
-
- g_signal_connect (fb->view_instance, "display_view", G_CALLBACK (display_view), fb);
-
- display_view (fb->view_instance, gal_view_instance_get_current_view (fb->view_instance), fb);
-}
-
-/* Gets rid of the view instance and view menus objects */
-void
-folder_browser_ui_discard_view_menus (FolderBrowser *fb)
-{
- g_assert (fb->view_instance != NULL);
- g_assert (fb->view_menus != NULL);
-
- g_object_unref (fb->view_instance);
- fb->view_instance = NULL;
-
- g_object_unref (fb->view_menus);
- fb->view_menus = NULL;
-}
-
-void
-folder_browser_ui_message_list_focus (FolderBrowser *fb)
-{
- g_assert (fb->uicomp != NULL);
-
- bonobo_ui_component_set_prop (fb->uicomp, "/commands/EditInvertSelection",
- "sensitive", "1", NULL);
-/* bonobo_ui_component_set_prop (fb->uicomp, "/commands/EditSelectThread",
- "sensitive", "1", NULL);*/
-}
-
-void
-folder_browser_ui_message_list_unfocus (FolderBrowser *fb)
-{
- g_assert (fb->uicomp != NULL);
-
- bonobo_ui_component_set_prop (fb->uicomp, "/commands/EditInvertSelection",
- "sensitive", "0", NULL);
- /*bonobo_ui_component_set_prop (fb->uicomp, "/commands/EditSelectThread",
- "sensitive", "0", NULL);*/
-}
-
-static void
-folder_browser_setup_property_menu (FolderBrowser *fb, BonoboUIComponent *uic)
-{
- char *name, *base = NULL;
- CamelURL *url;
-
- url = camel_url_new (fb->uri, NULL);
- if (url)
- base = g_path_get_basename(url->fragment?url->fragment:url->path);
-
- if (base && base[0] != '\0')
- name = g_strdup_printf (_("Properties for \"%s\""), base);
- else
- name = g_strdup (_("Properties"));
-
- bonobo_ui_component_set_prop (
- uic, "/menu/File/Folder/ComponentPlaceholder/ChangeFolderProperties",
- "label", name, NULL);
- g_free (name);
- g_free(base);
-
- if (url)
- camel_url_free (url);
-
- fbui_sensitise_item (fb, "ChangeFolderProperties",
- (strncmp (fb->uri, "vfolder:", 8) == 0 || strncmp (fb->uri, "file:", 5) == 0));
-}
-
-/* Must be in the same order as MailConfigDisplayStyle */
-/* used in folder-browser.c as well (therefore not static) */
-char *message_display_styles[] = {
- "/commands/ViewNormal",
- "/commands/ViewFullHeaders",
- "/commands/ViewSource"
-};
-
-/* public */
-
-void
-folder_browser_ui_add_message (FolderBrowser *fb)
-{
- BonoboUIComponent *uic = fb->uicomp;
- FolderBrowserSelectionState prev_state;
- GConfClient *gconf;
- int style;
- gboolean caret_mode;
-
- gconf = mail_config_get_gconf_client ();
-
- if (fb->sensitise_state) {
- g_hash_table_destroy(fb->sensitise_state);
- fb->sensitise_state = NULL;
- }
-
- ui_add (fb, "message", message_verbs, message_pixcache);
-
- caret_mode = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/caret_mode", NULL);
- bonobo_ui_component_set_prop(uic, "/commands/CaretMode", "state", caret_mode?"1":"0", NULL);
- bonobo_ui_component_add_listener (uic, "CaretMode", folder_browser_toggle_caret_mode, fb);
-
- /* Display Style */
- style = gconf_client_get_int (gconf, "/apps/evolution/mail/display/message_style", NULL);
- style = style >= 0 && style < MAIL_CONFIG_DISPLAY_MAX ? style : 0;
- bonobo_ui_component_set_prop (uic, message_display_styles[style], "state", "1", NULL);
- bonobo_ui_component_add_listener (uic, "ViewNormal", folder_browser_set_message_display_style, fb);
- bonobo_ui_component_add_listener (uic, "ViewFullHeaders", folder_browser_set_message_display_style, fb);
- bonobo_ui_component_add_listener (uic, "ViewSource", folder_browser_set_message_display_style, fb);
- if (fb->mail_display->display_style != style) {
- fb->mail_display->display_style = style;
- mail_display_redisplay (fb->mail_display, TRUE);
- }
-
- /* Resend Message */
- if (fb->folder && !folder_browser_is_sent (fb))
- fbui_sensitise_item (fb, "MessageResend", FALSE);
-
- /* sensitivity of message-specific commands */
- prev_state = fb->selection_state;
- fb->selection_state = FB_SELSTATE_UNDEFINED;
- folder_browser_ui_set_selection_state (fb, prev_state);
-
- /* Charset picker */
- e_charset_picker_bonobo_ui_populate (uic, "/menu/View", FB_DEFAULT_CHARSET,
- folder_browser_charset_changed, fb);
-}
-
-void
-folder_browser_ui_add_list (FolderBrowser *fb)
-{
- BonoboUIComponent *uic = fb->uicomp;
- GConfClient *gconf;
- int state;
-
- gconf = mail_config_get_gconf_client ();
-
- if (fb->sensitise_state) {
- g_hash_table_destroy (fb->sensitise_state);
- fb->sensitise_state = NULL;
- }
-
- ui_add (fb, "list", list_verbs, list_pixcache);
-
- /* Hide Deleted */
- state = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL);
- bonobo_ui_component_set_prop (uic, "/commands/HideDeleted", "state", state ? "1" : "0", NULL);
- bonobo_ui_component_add_listener (uic, "HideDeleted", folder_browser_toggle_hide_deleted, fb);
- if (!(fb->folder && (fb->folder->folder_flags & CAMEL_FOLDER_IS_TRASH)))
- message_list_set_hidedeleted (fb->message_list, state);
- else
- fbui_sensitise_item (fb, "HideDeleted", FALSE);
-
- /* Threaded toggle */
- state = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/thread_list", NULL);
- if (fb->meta)
- state = e_meta_get_bool(fb->meta, "thread_list", state);
-
- bonobo_ui_component_set_prop (uic, "/commands/ViewThreaded", "state", state ? "1" : "0", NULL);
- bonobo_ui_component_add_listener (uic, "ViewThreaded", folder_browser_toggle_threads, fb);
- message_list_set_threaded (fb->message_list, state);
- state = fb->selection_state;
- fb->selection_state = FB_SELSTATE_UNDEFINED;
- folder_browser_ui_set_selection_state (fb, state);
-
- /* Property menu */
- folder_browser_setup_property_menu (fb, fb->uicomp);
-
- /* View menu */
- if (fb->view_instance == NULL)
- folder_browser_ui_setup_view_menus (fb);
-}
-
-void
-folder_browser_ui_rm_list (FolderBrowser *fb)
-{
- /* View menu */
- if (fb->view_instance != NULL)
- folder_browser_ui_discard_view_menus (fb);
-}
-
-void
-folder_browser_ui_add_global (FolderBrowser *fb)
-{
- BonoboUIComponent *uic = fb->uicomp;
- gboolean show_preview;
- GConfClient *gconf;
- int paned_size;
-
- gconf = mail_config_get_gconf_client ();
-
- if (fb->sensitise_state) {
- g_hash_table_destroy (fb->sensitise_state);
- fb->sensitise_state = NULL;
- }
-
- ui_add (fb, "global", global_verbs, global_pixcache);
-
- /* (Pre)view pane size (do this first because it affects the
- preview settings - see folder_browser_set_message_preview()
- internals for details) */
- paned_size = gconf_client_get_int (gconf, "/apps/evolution/mail/display/paned_size", NULL);
- g_signal_handler_block (fb->vpaned, fb->paned_resize_id);
- gtk_paned_set_position (GTK_PANED (fb->vpaned), paned_size);
- g_signal_handler_unblock (fb->vpaned, fb->paned_resize_id);
-
- /* (Pre)view toggle */
- show_preview = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_preview", NULL);
- if (fb->meta)
- show_preview = e_meta_get_bool(fb->meta, "show_preview", show_preview);
- bonobo_ui_component_set_prop (uic, "/commands/ViewPreview", "state", show_preview ? "1" : "0", NULL);
- folder_browser_set_message_preview (fb, show_preview);
-
- /* listen for user-changes */
- bonobo_ui_component_add_listener (uic, "ViewPreview", folder_browser_toggle_preview, fb);
-
- /* Stop button */
- /* TODO: Go through cache, but we can't becaus eof mail-mt.c:set_stop at the moment */
- bonobo_ui_component_set_prop (uic, "/commands/MailStop", "sensitive", "0", NULL);
-}
-
-void
-folder_browser_ui_rm_all (FolderBrowser *fb)
-{
- BonoboUIComponent *uic = fb->uicomp;
-
- if (bonobo_ui_component_get_container (uic) != NULL) {
- bonobo_ui_component_rm (uic, "/", NULL);
- bonobo_ui_component_unset_container (uic, NULL);
- }
-
- if (fb->sensitise_state) {
- g_hash_table_destroy (fb->sensitise_state);
- fb->sensitise_state = NULL;
- }
-}
-
-void
-fbui_sensitise_item (FolderBrowser *fb, const char *item, int state)
-{
- char *name, *key;
- gpointer val_ptr;
- int val;
-
- /* If this whole caching idea doesn't work, remove it here */
- if (fb->sensitise_state == NULL)
- fb->sensitise_state = g_hash_table_new (g_str_hash, g_str_equal);
-
- if (g_hash_table_lookup_extended (fb->sensitise_state, item, (void **)&key, &val_ptr)) {
- val = GPOINTER_TO_INT(val_ptr);
- if (val == state)
- return;
- }
-
- if (fb->uicomp) {
- name = g_alloca (strlen (item) + strlen ("/commands/") + 1);
- sprintf (name, "/commands/%s", item);
- bonobo_ui_component_set_prop (fb->uicomp, name, "sensitive", state ? "1" : "0", NULL);
- g_hash_table_insert (fb->sensitise_state, (char *) item, GINT_TO_POINTER(state));
- }
-}
-
-static void
-fbui_sensitize_items (FolderBrowser *fb, guint32 enable_mask)
-{
- gboolean enable;
- int i;
-
- for (i = 0; i < num_default_ui_nodes; i++) {
- enable = (default_ui_nodes[i].enable_mask & enable_mask) == enable_mask;
- fbui_sensitise_item (fb, default_ui_nodes[i].name, enable);
- }
-}
-
-void
-folder_browser_ui_scan_selection (FolderBrowser *fb)
-{
- gboolean outgoing = FALSE;
- guint32 enable_mask = 0;
-
- if (fb->selection_state == FB_SELSTATE_SINGLE ||
- fb->selection_state == FB_SELSTATE_MULTIPLE) {
- GPtrArray *uids;
- CamelMessageInfo *info;
- guint32 temp_mask = 0;
- int i;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- for (i = 0; i < uids->len; i++) {
- info = camel_folder_get_message_info (fb->folder, uids->pdata[i]);
- if (info == NULL)
- continue;
-
- if (info->flags & CAMEL_MESSAGE_DELETED)
- temp_mask |= HAS_DELETED;
- else
- temp_mask |= HAS_UNDELETED;
-
- if (info->flags & CAMEL_MESSAGE_SEEN)
- temp_mask |= HAS_READ;
- else
- temp_mask |= HAS_UNREAD;
-
- if (info->flags & CAMEL_MESSAGE_FLAGGED)
- temp_mask |= HAS_IMPORTANT;
- else
- temp_mask |= HAS_UNIMPORTANT;
-
- camel_folder_free_message_info (fb->folder, info);
- g_free (uids->pdata[i]);
- }
-
- g_ptr_array_free (uids, TRUE);
-
- /* yeah, the naming is a bit backwards, but we need to support
- * the case when, say, both a deleted and an undeleted message
- * are selected. Both the Delete and Undelete menu items should
- * be sensitized, but the only good way to set the flags is as
- * above. Anyway, the naming is a bit of a lie but it works out
- * so that it's sensible both above and in the definition of
- * the UI items, so deal with it.
- */
-
- enable_mask |= (~temp_mask & HAS_FLAGS);
- }
-
- if (folder_browser_is_drafts (fb)) {
- enable_mask |= IS_DRAFTS_FOLDER;
- outgoing = TRUE;
- }
-
- if (folder_browser_is_outbox (fb)) {
- enable_mask |= IS_OUTBOX_FOLDER;
- outgoing = TRUE;
- }
-
- if (folder_browser_is_sent (fb)) {
- enable_mask |= IS_SENT_FOLDER;
- outgoing = TRUE;
- }
-
- if (fb->message_list && fb->message_list->threaded)
- enable_mask |= IS_THREADED;
- else
- enable_mask |= NOT_THREADED;
-
- if (outgoing == FALSE)
- enable_mask |= IS_INCOMING_FOLDER;
-
- switch (fb->selection_state) {
- case FB_SELSTATE_SINGLE:
- enable_mask |= SELECTION_SINGLE;
- break;
- case FB_SELSTATE_MULTIPLE:
- enable_mask |= SELECTION_MULTIPLE;
- break;
- case FB_SELSTATE_NONE:
- default:
- enable_mask |= SELECTION_NONE;
- break;
- }
-
- fbui_sensitize_items (fb, enable_mask);
-}
-
-void
-folder_browser_ui_set_selection_state (FolderBrowser *fb, FolderBrowserSelectionState state)
-{
- /* the state may be the same but with
- * different messages selected, necessitating
- * a recheck of the flags of the selected
- * messages.
- */
-
- if (state == fb->selection_state &&
- state != FB_SELSTATE_SINGLE &&
- state != FB_SELSTATE_MULTIPLE)
- return;
-
- fb->selection_state = state;
- folder_browser_ui_scan_selection (fb);
-}
-
-void
-folder_browser_ui_message_loaded (FolderBrowser *fb)
-{
- BonoboUIComponent *uic = fb->uicomp;
-
- if (uic) {
- fb->selection_state = FB_SELSTATE_NONE;
- folder_browser_ui_set_selection_state (fb, FB_SELSTATE_SINGLE);
- }
-}
diff --git a/mail/folder-browser-ui.h b/mail/folder-browser-ui.h
deleted file mode 100644
index 5c2bc1fa28..0000000000
--- a/mail/folder-browser-ui.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * folder-browser-ui.c: Sets up the Bonobo UI for FolderBrowsers
- *
- * Author:
- * Peter Williams <peterw@ximian.com>
- *
- * (C) 2001 Ximian, Inc.
- */
-
-#ifndef _FOLDER_BROWSER_UI_H
-#define _FOLDER_BROWSER_UI_H
-
-#include "folder-browser.h"
-
-void folder_browser_ui_add_message (FolderBrowser *fb);
-void folder_browser_ui_add_list (FolderBrowser *fb);
-void folder_browser_ui_add_global (FolderBrowser *fb);
-
-void folder_browser_ui_rm_list (FolderBrowser *fb);
-void folder_browser_ui_rm_all (FolderBrowser *fb);
-
-/* these affect the sensitivity of UI elements */
-void folder_browser_ui_scan_selection (FolderBrowser *fb);
-void folder_browser_ui_set_selection_state (FolderBrowser *fb, FolderBrowserSelectionState state);
-void folder_browser_ui_message_loaded (FolderBrowser *fb);
-
-void folder_browser_ui_discard_view_menus (FolderBrowser *fb);
-void folder_browser_ui_setup_view_menus (FolderBrowser *fb);
-/* Set the sensitivity of a single item */
-void fbui_sensitise_item(FolderBrowser *fb, const char *item, int state);
-
-void folder_browser_ui_message_list_focus (FolderBrowser *fb);
-void folder_browser_ui_message_list_unfocus (FolderBrowser *fb);
-
-#endif /* _FOLDER_BROWSER_UI_H */
diff --git a/mail/folder-browser.c b/mail/folder-browser.c
deleted file mode 100644
index 3abdd4d065..0000000000
--- a/mail/folder-browser.c
+++ /dev/null
@@ -1,2692 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Miguel De Icaza <miguel@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2000-2003 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtkinvisible.h>
-#include <gal/e-table/e-table.h>
-#include <gal/util/e-util.h>
-#include <gal/widgets/e-gui-utils.h>
-#include <gal/widgets/e-popup-menu.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <libgnomeui/gnome-dialog-util.h>
-#include <libgnomeui/gnome-pixmap.h>
-
-#include <gtkhtml/htmlengine.h>
-#include <gtkhtml/htmlobject.h>
-#include <gtkhtml/htmlinterval.h>
-#include <gtkhtml/htmlengine-edit-cut-and-paste.h>
-
-#include "filter/vfolder-rule.h"
-#include "filter/vfolder-context.h"
-#include "filter/filter-option.h"
-#include "filter/filter-input.h"
-#include "filter/filter-label.h"
-
-#include "e-util/e-sexp.h"
-#include "e-util/e-mktemp.h"
-#include "e-util/e-meta.h"
-#include "folder-browser.h"
-#include "e-searching-tokenizer.h"
-#include "mail.h"
-#include "mail-callbacks.h"
-#include "mail-tools.h"
-#include "mail-ops.h"
-#include "mail-vfolder.h"
-#include "mail-autofilter.h"
-#include "mail-mt.h"
-#include "mail-folder-cache.h"
-#include "folder-browser-ui.h"
-
-#include "mail-local.h"
-#include "mail-config.h"
-
-#include <camel/camel-mime-message.h>
-#include <camel/camel-stream-mem.h>
-
-/* maybe this shooudlnt be private ... */
-#include "camel/camel-search-private.h"
-
-#define d(x)
-
-#define PARENT_TYPE (gtk_table_get_type ())
-
-static void folder_changed(CamelObject *o, void *event_data, void *data);
-static void main_folder_changed(CamelObject *o, void *event_data, void *data);
-
-#define X_EVOLUTION_MESSAGE_TYPE "x-evolution-message"
-#define MESSAGE_RFC822_TYPE "message/rfc822"
-#define TEXT_URI_LIST_TYPE "text/uri-list"
-#define TEXT_PLAIN_TYPE "text/plain"
-
-/* Drag & Drop types */
-enum DndTargetType {
- DND_TARGET_TYPE_X_EVOLUTION_MESSAGE,
- DND_TARGET_TYPE_MESSAGE_RFC822,
- DND_TARGET_TYPE_TEXT_URI_LIST,
-};
-
-static GtkTargetEntry drag_types[] = {
- { X_EVOLUTION_MESSAGE_TYPE, 0, DND_TARGET_TYPE_X_EVOLUTION_MESSAGE },
- { MESSAGE_RFC822_TYPE, 0, DND_TARGET_TYPE_MESSAGE_RFC822 },
- { TEXT_URI_LIST_TYPE, 0, DND_TARGET_TYPE_TEXT_URI_LIST },
-};
-
-static const int num_drag_types = sizeof (drag_types) / sizeof (drag_types[0]);
-
-enum PasteTargetType {
- PASTE_TARGET_TYPE_X_EVOLUTION_MESSAGE,
- PASTE_TARGET_TYPE_TEXT_PLAIN,
-};
-
-static GtkTargetPair paste_types[] = {
- { 0, 0, PASTE_TARGET_TYPE_X_EVOLUTION_MESSAGE },
- { GDK_SELECTION_TYPE_STRING, 0, PASTE_TARGET_TYPE_TEXT_PLAIN },
-};
-
-static const int num_paste_types = sizeof (paste_types) / sizeof (paste_types[0]);
-
-static GdkAtom clipboard_atom = GDK_NONE;
-
-static GtkTableClass *parent_class = NULL;
-
-enum {
- FOLDER_LOADED,
- MESSAGE_LOADED,
- LAST_SIGNAL
-};
-
-static guint folder_browser_signals [LAST_SIGNAL] = {0, };
-
-static void
-folder_browser_finalise (GObject *object)
-{
- FolderBrowser *folder_browser;
-
- folder_browser = FOLDER_BROWSER (object);
-
- g_free (folder_browser->loading_uid);
- g_free (folder_browser->pending_uid);
- g_free (folder_browser->new_uid);
- g_free (folder_browser->loaded_uid);
-
- g_free (folder_browser->uri);
- folder_browser->uri = NULL;
-
- if (folder_browser->clipboard_selection)
- g_byte_array_free (folder_browser->clipboard_selection, TRUE);
-
- if (folder_browser->sensitise_state) {
- g_hash_table_destroy (folder_browser->sensitise_state);
- folder_browser->sensitise_state = NULL;
- }
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-folder_browser_destroy (GtkObject *object)
-{
- FolderBrowser *folder_browser;
- CORBA_Environment ev;
-
- folder_browser = FOLDER_BROWSER (object);
-
- CORBA_exception_init (&ev);
-
- if (folder_browser->seen_id != 0) {
- gtk_timeout_remove (folder_browser->seen_id);
- folder_browser->seen_id = 0;
- }
-
- if (folder_browser->loading_id != 0) {
- gtk_timeout_remove(folder_browser->loading_id);
- folder_browser->loading_id = 0;
- }
-
- if (folder_browser->message_list) {
- gtk_widget_destroy (GTK_WIDGET (folder_browser->message_list));
- folder_browser->message_list = NULL;
- }
-
- if (folder_browser->mail_display) {
- gtk_widget_destroy (GTK_WIDGET (folder_browser->mail_display));
- folder_browser->mail_display = NULL;
- }
-
- if (folder_browser->view_instance) {
- g_object_unref (folder_browser->view_instance);
- folder_browser->view_instance = NULL;
- }
-
- if (folder_browser->view_menus) {
- g_object_unref (folder_browser->view_menus);
- folder_browser->view_menus = NULL;
- }
-
- /* wait for all outstanding async events against us */
- if (folder_browser->async_event) {
- mail_async_event_destroy (folder_browser->async_event);
- folder_browser->async_event = NULL;
- }
-
- if (folder_browser->search_full) {
- g_object_unref (folder_browser->search_full);
- folder_browser->search_full = NULL;
- }
-
- if (folder_browser->sensitize_timeout_id) {
- g_source_remove (folder_browser->sensitize_timeout_id);
- folder_browser->sensitize_timeout_id = 0;
- }
-
- if (folder_browser->shell != CORBA_OBJECT_NIL) {
- CORBA_Object_release (folder_browser->shell, &ev);
- folder_browser->shell = CORBA_OBJECT_NIL;
- }
-
- if (folder_browser->shell_view != CORBA_OBJECT_NIL) {
- CORBA_Object_release (folder_browser->shell_view, &ev);
- folder_browser->shell_view = CORBA_OBJECT_NIL;
- }
-
- if (folder_browser->uicomp) {
- bonobo_object_unref (BONOBO_OBJECT (folder_browser->uicomp));
- folder_browser->uicomp = NULL;
- }
-
- if (folder_browser->invisible) {
- g_object_unref (folder_browser->invisible);
- folder_browser->invisible = NULL;
- }
-
- if (folder_browser->get_id != -1) {
- mail_msg_cancel (folder_browser->get_id);
- folder_browser->get_id = -1;
- }
-
- if (folder_browser->folder) {
- camel_object_unhook_event (CAMEL_OBJECT (folder_browser->folder), "folder_changed",
- folder_changed, folder_browser);
- camel_object_unhook_event (CAMEL_OBJECT (folder_browser->folder), "message_changed",
- folder_changed, folder_browser);
- mail_sync_folder (folder_browser->folder, NULL, NULL);
- camel_object_unref (folder_browser->folder);
- folder_browser->folder = NULL;
- }
-
- CORBA_exception_free (&ev);
-
- GTK_OBJECT_CLASS (parent_class)->destroy (object);
-}
-
-static void
-folder_browser_class_init (FolderBrowserClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
-
- parent_class = g_type_class_ref(PARENT_TYPE);
-
- object_class->destroy = folder_browser_destroy;
- gobject_class->finalize = folder_browser_finalise;
-
- folder_browser_signals[FOLDER_LOADED] =
- g_signal_new ("folder_loaded",
- FOLDER_BROWSER_TYPE,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (FolderBrowserClass, folder_loaded),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
-
- folder_browser_signals[MESSAGE_LOADED] =
- g_signal_new ("message_loaded",
- FOLDER_BROWSER_TYPE,
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (FolderBrowserClass, message_loaded),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
-
- /* clipboard atom */
- if (!clipboard_atom)
- clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
-
- if (!paste_types[0].target)
- paste_types[0].target = gdk_atom_intern (X_EVOLUTION_MESSAGE_TYPE, FALSE);
-}
-
-static void
-add_uid (MessageList *ml, const char *uid, gpointer data)
-{
- g_ptr_array_add ((GPtrArray *) data, g_strdup (uid));
-}
-
-static void
-message_list_drag_data_get (ETree *tree, int row, ETreePath path, int col,
- GdkDragContext *context, GtkSelectionData *selection_data,
- guint info, guint time, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids = NULL;
- int i;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, add_uid, uids);
- if (uids->len == 0) {
- g_ptr_array_free (uids, TRUE);
- return;
- }
-
- switch (info) {
- case DND_TARGET_TYPE_TEXT_URI_LIST:
- {
- const char *filename, *tmpdir;
- CamelMimeMessage *message;
- CamelMimeFilter *filter;
- CamelStream *fstream;
- CamelStreamFilter *stream;
- char *uri_list;
- int fd;
-
- tmpdir = e_mkdtemp ("drag-n-drop-XXXXXX");
-
- if (!tmpdir) {
- char *msg = g_strdup_printf (_("Could not create temporary "
- "directory: %s"),
- g_strerror (errno));
- gnome_error_dialog (msg);
- /* cleanup and abort */
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
- g_free (msg);
- return;
- }
-
- message = camel_folder_get_message (fb->folder, uids->pdata[0], NULL);
- g_free (uids->pdata[0]);
-
- if (uids->len == 1) {
- filename = camel_mime_message_get_subject (message);
- if (!filename)
- filename = _("Unknown");
- } else
- filename = "mbox";
-
- uri_list = g_strdup_printf ("file://%s/%s", tmpdir, filename);
-
- fd = open (uri_list + 7, O_WRONLY | O_CREAT | O_EXCL, 0600);
- if (fd == -1) {
- /* cleanup and abort */
- camel_object_unref (message);
- for (i = 1; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
- g_free (uri_list);
- return;
- }
-
- fstream = camel_stream_fs_new_with_fd (fd);
-
- stream = camel_stream_filter_new_with_stream (fstream);
- filter = camel_mime_filter_from_new ();
- camel_stream_filter_add (stream, filter);
- camel_object_unref (filter);
-
- camel_stream_write (fstream, "From - \n", 8);
- camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), CAMEL_STREAM (stream));
- camel_object_unref (message);
- camel_stream_flush (CAMEL_STREAM (stream));
-
- for (i = 1; i < uids->len; i++) {
- message = camel_folder_get_message (fb->folder, uids->pdata[i], NULL);
- camel_stream_write (fstream, "From - \n", 8);
- camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), CAMEL_STREAM (stream));
- camel_object_unref (message);
- camel_stream_flush (CAMEL_STREAM (stream));
- g_free (uids->pdata[i]);
- }
-
- g_ptr_array_free (uids, TRUE);
-
- camel_object_unref (stream);
- camel_object_unref (fstream);
-
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- uri_list, strlen (uri_list));
- g_free (uri_list);
- }
- break;
- case DND_TARGET_TYPE_MESSAGE_RFC822:
- {
- CamelMimeFilter *filter;
- CamelStream *stream;
- CamelStream *mem;
-
- mem = camel_stream_mem_new ();
-
- stream = camel_stream_filter_new_with_stream (mem);
- filter = camel_mime_filter_from_new ();
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
- camel_object_unref (filter);
-
- for (i = 0; i < uids->len; i++) {
- CamelMimeMessage *message;
-
- message = camel_folder_get_message (fb->folder, uids->pdata[i], NULL);
- g_free (uids->pdata[i]);
-
- if (message) {
- camel_stream_write (mem, "From - \n", 8);
- camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream);
- camel_object_unref (message);
- camel_stream_flush (stream);
- }
- }
-
- g_ptr_array_free (uids, TRUE);
- camel_object_unref (stream);
-
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- CAMEL_STREAM_MEM (mem)->buffer->data,
- CAMEL_STREAM_MEM (mem)->buffer->len);
-
- camel_object_unref (mem);
- }
- break;
- case DND_TARGET_TYPE_X_EVOLUTION_MESSAGE:
- {
- GByteArray *array;
-
- /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn" */
-
- /* write the uri portion */
- array = g_byte_array_new ();
- g_byte_array_append (array, fb->uri, strlen (fb->uri));
- g_byte_array_append (array, "", 1);
-
- /* write the uids */
- for (i = 0; i < uids->len; i++) {
- g_byte_array_append (array, uids->pdata[i], strlen (uids->pdata[i]));
- g_free (uids->pdata[i]);
-
- if (i + 1 < uids->len)
- g_byte_array_append (array, "", 1);
- }
-
- g_ptr_array_free (uids, TRUE);
-
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- array->data, array->len);
-
- g_byte_array_free (array, TRUE);
- }
- break;
- default:
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
-
- g_ptr_array_free (uids, TRUE);
- break;
- }
-}
-
-static void
-message_rfc822_dnd (CamelFolder *dest, CamelStream *stream, CamelException *ex)
-{
- CamelMimeParser *mp;
-
- mp = camel_mime_parser_new ();
- camel_mime_parser_scan_from (mp, TRUE);
- camel_mime_parser_init_with_stream (mp, stream);
-
- while (camel_mime_parser_step (mp, 0, 0) == HSCAN_FROM) {
- CamelMessageInfo *info;
- CamelMimeMessage *msg;
-
- msg = camel_mime_message_new ();
- if (camel_mime_part_construct_from_parser (CAMEL_MIME_PART (msg), mp) == -1) {
- camel_object_unref (CAMEL_OBJECT (msg));
- break;
- }
-
- /* append the message to the folder... */
- info = g_new0 (CamelMessageInfo, 1);
- camel_folder_append_message (dest, msg, info, NULL, ex);
- camel_object_unref (CAMEL_OBJECT (msg));
-
- if (camel_exception_is_set (ex))
- break;
-
- /* skip over the FROM_END state */
- camel_mime_parser_step (mp, 0, 0);
- }
-
- camel_object_unref (CAMEL_OBJECT (mp));
-}
-
-static void
-message_list_drag_data_received (ETree *tree, int row, ETreePath path, int col,
- GdkDragContext *context, gint x, gint y,
- GtkSelectionData *selection_data, guint info,
- guint time, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- CamelFolder *folder = NULL;
- char *tmp, *url, **urls;
- GPtrArray *uids = NULL;
- CamelStream *stream;
- CamelException ex;
- CamelURL *uri;
- int i, fd;
-
- /* this means we are receiving no data */
- if (!selection_data->data || selection_data->length == -1)
- return;
-
- camel_exception_init (&ex);
-
- switch (info) {
- case DND_TARGET_TYPE_TEXT_URI_LIST:
- tmp = g_strndup (selection_data->data, selection_data->length);
- urls = g_strsplit (tmp, "\n", 0);
- g_free (tmp);
-
- for (i = 0; urls[i] != NULL; i++) {
- /* get the path component */
- url = g_strstrip (urls[i]);
-
- uri = camel_url_new (url, NULL);
- g_free (url);
-
- if (!uri)
- continue;
-
- url = uri->path;
- uri->path = NULL;
- camel_url_free (uri);
-
- fd = open (url, O_RDONLY);
- if (fd == -1) {
- g_free (url);
- /* FIXME: okay, what do we do in this case? */
- continue;
- }
-
- stream = camel_stream_fs_new_with_fd (fd);
- message_rfc822_dnd (fb->folder, stream, &ex);
- camel_object_unref (CAMEL_OBJECT (stream));
-
- if (context->action == GDK_ACTION_MOVE && !camel_exception_is_set (&ex))
- unlink (url);
-
- g_free (url);
- }
-
- g_free (urls);
- break;
- case DND_TARGET_TYPE_MESSAGE_RFC822:
- /* write the message(s) out to a CamelStream so we can use it */
- stream = camel_stream_mem_new ();
- camel_stream_write (stream, selection_data->data, selection_data->length);
- camel_stream_reset (stream);
-
- message_rfc822_dnd (fb->folder, stream, &ex);
- camel_object_unref (CAMEL_OBJECT (stream));
- break;
- case DND_TARGET_TYPE_X_EVOLUTION_MESSAGE:
- folder = mail_tools_x_evolution_message_parse (selection_data->data, selection_data->length, &uids);
- if (folder == NULL)
- goto fail;
-
- if (uids == NULL) {
- camel_object_unref (CAMEL_OBJECT (folder));
- goto fail;
- }
-
- mail_transfer_messages (folder, uids, context->action == GDK_ACTION_MOVE,
- fb->uri, 0, NULL, NULL);
-
- camel_object_unref (CAMEL_OBJECT (folder));
- break;
- }
-
- camel_exception_clear (&ex);
-
- gtk_drag_finish (context, TRUE, TRUE, GDK_CURRENT_TIME);
-
- fail:
- camel_exception_clear (&ex);
-
- gtk_drag_finish (context, FALSE, TRUE, GDK_CURRENT_TIME);
-}
-
-static void
-selection_get (GtkWidget *widget, GtkSelectionData *selection_data,
- guint info, guint time_stamp, FolderBrowser *fb)
-{
- if (fb->clipboard_selection == NULL)
- return;
-
- switch (info) {
- default:
- case PASTE_TARGET_TYPE_TEXT_PLAIN:
- {
- /* FIXME: this'll be fucking slow for the user... pthread this? */
- CamelFolder *source;
- CamelStream *stream;
- GByteArray *bytes;
- GPtrArray *uids;
- int i;
-
- bytes = fb->clipboard_selection;
-
- /* Note: source should == fb->folder, but we might as well use `source' instead of fb->folder */
- source = mail_tools_x_evolution_message_parse (bytes->data, bytes->len, &uids);
- if (source == NULL)
- return;
-
- if (uids == NULL) {
- camel_object_unref (CAMEL_OBJECT (source));
- return;
- }
-
- bytes = g_byte_array_new ();
- stream = camel_stream_mem_new ();
- camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), bytes);
-
- for (i = 0; i < uids->len; i++) {
- CamelMimeMessage *message;
-
- message = camel_folder_get_message (source, uids->pdata[i], NULL);
- g_free (uids->pdata[i]);
-
- if (message) {
- camel_stream_write (stream, "From - \n", 8);
- camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream);
- camel_object_unref (CAMEL_OBJECT (message));
- }
- }
-
- g_ptr_array_free (uids, TRUE);
- camel_object_unref (CAMEL_OBJECT (stream));
- camel_object_unref (CAMEL_OBJECT (source));
-
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- bytes->data, bytes->len);
-
- g_byte_array_free (bytes, FALSE);
- }
- break;
- case PASTE_TARGET_TYPE_X_EVOLUTION_MESSAGE:
- /* we already have our data in the correct form */
- gtk_selection_data_set (selection_data,
- selection_data->target, 8,
- fb->clipboard_selection->data,
- fb->clipboard_selection->len);
- break;
- }
-}
-
-static void
-selection_clear_event (GtkWidget *widget, GdkEventSelection *event, FolderBrowser *fb)
-{
- if (fb->clipboard_selection != NULL) {
- g_byte_array_free (fb->clipboard_selection, TRUE);
- fb->clipboard_selection = NULL;
- }
-}
-
-static void
-selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
- guint time, FolderBrowser *fb)
-{
- CamelFolder *source = NULL;
- GPtrArray *uids = NULL;
-
- if (selection_data == NULL || selection_data->length == -1)
- return;
-
- source = mail_tools_x_evolution_message_parse (selection_data->data, selection_data->length, &uids);
- if (source == NULL)
- return;
-
- if (uids == NULL) {
- camel_object_unref (CAMEL_OBJECT (source));
- return;
- }
-
- mail_transfer_messages (source, uids, FALSE, fb->uri, 0, NULL, NULL);
-
- camel_object_unref (CAMEL_OBJECT (source));
-}
-
-void
-folder_browser_copy (GtkWidget *menuitem, FolderBrowser *fb)
-{
- GPtrArray *uids = NULL;
- GByteArray *bytes;
- gboolean cut;
- int i;
-
- if (fb->message_list == NULL)
- return;
-
- cut = menuitem == NULL;
-
- if (GTK_WIDGET_HAS_FOCUS (fb->mail_display->html)) {
- gtk_html_copy (fb->mail_display->html);
- return;
- }
-
- if (fb->clipboard_selection) {
- g_byte_array_free (fb->clipboard_selection, TRUE);
- fb->clipboard_selection = NULL;
- }
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, add_uid, uids);
-
- /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn" */
-
- /* write the uri portion */
- bytes = g_byte_array_new ();
- g_byte_array_append (bytes, fb->uri, strlen (fb->uri));
- g_byte_array_append (bytes, "", 1);
-
- /* write the uids */
- camel_folder_freeze (fb->folder);
- for (i = 0; i < uids->len; i++) {
- if (cut) {
- camel_folder_set_message_flags (fb->folder, uids->pdata[i],
- CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED,
- CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED);
- }
- g_byte_array_append (bytes, uids->pdata[i], strlen (uids->pdata[i]));
- g_free (uids->pdata[i]);
-
- if (i + 1 < uids->len)
- g_byte_array_append (bytes, "", 1);
- }
- camel_folder_thaw (fb->folder);
-
- g_ptr_array_free (uids, TRUE);
-
- fb->clipboard_selection = bytes;
-
- gtk_selection_owner_set (fb->invisible, clipboard_atom, GDK_CURRENT_TIME);
-}
-
-void
-folder_browser_cut (GtkWidget *menuitem, FolderBrowser *fb)
-{
- folder_browser_copy (NULL, fb);
-}
-
-void
-folder_browser_paste (GtkWidget *menuitem, FolderBrowser *fb)
-{
- gtk_selection_convert (fb->invisible, clipboard_atom,
- paste_types[0].target,
- GDK_CURRENT_TIME);
-}
-
-/* all this crap so we can give the user a whoopee doo status bar */
-static void
-update_status_bar (FolderBrowser *fb)
-{
- extern CamelFolder *outbox_folder, *sent_folder;
- CORBA_Environment ev;
- int tmp, total;
- GString *work;
-
- if (fb->folder == NULL
- || fb->message_list == NULL
- || fb->shell_view == CORBA_OBJECT_NIL)
- return;
-
- if (!fb->message_list->hidedeleted || !camel_folder_has_summary_capability (fb->folder)) {
- total = camel_folder_get_message_count (fb->folder);
- } else {
- GPtrArray *sum = camel_folder_get_summary (fb->folder);
- int i;
-
- if (sum) {
- total = 0;
- for (i = 0; i < sum->len; i++) {
- CamelMessageInfo *info = sum->pdata[i];
-
- if ((info->flags & CAMEL_MESSAGE_DELETED) == 0)
- total++;
- }
- camel_folder_free_summary (fb->folder, sum);
- } else {
- total = camel_folder_get_message_count (fb->folder);
- }
- }
-
- work = g_string_new ("");
- g_string_append_printf (work, _("%d new"), camel_folder_get_unread_message_count (fb->folder));
- tmp = message_list_hidden (fb->message_list);
- if (0 < tmp && tmp < total) {
- g_string_append (work, _(", "));
- if (tmp < total / 2)
- g_string_append_printf (work, _("%d hidden"), tmp);
- else
- g_string_append_printf (work, _("%d visible"), total - tmp);
- }
- tmp = e_selection_model_selected_count (e_tree_get_selection_model (fb->message_list->tree));
- if (tmp) {
- g_string_append (work, _(", "));
- g_string_append_printf (work, _("%d selected"), tmp);
- }
- g_string_append (work, _(", "));
-
- if (fb->folder == outbox_folder)
- g_string_append_printf (work, _("%d unsent"), total);
- else if (fb->folder == sent_folder)
- g_string_append_printf (work, _("%d sent"), total);
- else
- g_string_append_printf (work, _("%d total"), total);
-
- CORBA_exception_init (&ev);
- GNOME_Evolution_ShellView_setFolderBarLabel (fb->shell_view, work->str, &ev);
- CORBA_exception_free (&ev);
-
- if (fb->update_status_bar_idle_id != 0) {
- g_source_remove (fb->update_status_bar_idle_id);
- fb->update_status_bar_idle_id = 0;
- }
-
- g_string_free (work, TRUE);
-}
-
-static gboolean
-update_status_bar_idle_cb(gpointer data)
-{
- FolderBrowser *fb = data;
-
-#if 0
- if (!GTK_OBJECT_DESTROYED (fb))
-#endif
- update_status_bar (fb);
-
- fb->update_status_bar_idle_id = 0;
- g_object_unref (fb);
-
- return FALSE;
-}
-
-static void
-update_status_bar_idle(FolderBrowser *fb)
-{
- if (fb->update_status_bar_idle_id == 0) {
- g_object_ref (fb);
- fb->update_status_bar_idle_id = g_idle_add (update_status_bar_idle_cb, fb);
- }
-}
-
-static void main_folder_changed(CamelObject *o, void *event_data, void *data)
-{
- FolderBrowser *fb = data;
-
- if (fb->message_list == NULL)
- return;
-
- /* so some corba unref doesnt blow us away while we're busy */
- g_object_ref (fb);
- update_status_bar (fb);
- folder_browser_ui_scan_selection (fb);
- g_object_unref (fb);
-}
-
-static void folder_changed (CamelObject *obj, void *event_data, void *user_data)
-{
- FolderBrowser *fb = user_data;
-
- mail_async_event_emit (fb->async_event, MAIL_ASYNC_GUI,
- (MailAsyncFunc) main_folder_changed,
- obj, NULL, user_data);
-}
-
-static void
-got_folder (char *uri, CamelFolder *folder, void *user_data)
-{
- FolderBrowser *fb = user_data;
- EMeta *meta;
-
- fb->get_id = -1;
-
- d(printf ("got folder '%s' = %p, previous folder was %p\n", uri, folder, fb->folder));
-
- if (fb->message_list == NULL)
- goto done;
-
- if (fb->folder) {
- camel_object_unhook_event (fb->folder, "folder_changed", folder_changed, fb);
- camel_object_unhook_event (fb->folder, "message_changed", folder_changed, fb);
- camel_object_unref (fb->folder);
- }
-
- if (folder) {
- fb->folder = folder;
- camel_object_ref (folder);
- meta = mail_tool_get_meta_data(fb->uri);
- if (meta != fb->meta) {
- g_object_unref(fb->meta);
- fb->meta = meta;
- } else {
- g_object_unref(meta);
- }
- } else {
- fb->folder = NULL;
- if (fb->meta) {
- g_object_unref(fb->meta);
- fb->meta = NULL;
- }
- goto done;
- }
-
-
- gtk_widget_set_sensitive (GTK_WIDGET (fb->search), camel_folder_has_search_capability (folder));
- message_list_set_folder (fb->message_list, folder,
- folder_browser_is_drafts (fb) ||
- folder_browser_is_sent (fb) ||
- folder_browser_is_outbox (fb));
-
- camel_object_hook_event (CAMEL_OBJECT (fb->folder), "folder_changed",
- folder_changed, fb);
- camel_object_hook_event (CAMEL_OBJECT (fb->folder), "message_changed",
- folder_changed, fb);
-
- if (fb->view_instance != NULL && fb->view_menus != NULL)
- folder_browser_ui_discard_view_menus (fb);
-
- folder_browser_ui_setup_view_menus (fb);
-
- /* when loading a new folder, nothing is selected initially */
-
- if (fb->uicomp)
- folder_browser_ui_set_selection_state (fb, FB_SELSTATE_NONE);
-
- done:
- g_signal_emit (fb, folder_browser_signals[FOLDER_LOADED], 0, fb->uri);
- g_object_unref (fb);
-}
-
-
-void
-folder_browser_reload (FolderBrowser *fb)
-{
- g_return_if_fail (IS_FOLDER_BROWSER (fb));
-
- if (fb->folder) {
- mail_refresh_folder (fb->folder, NULL, NULL);
- } else if (fb->uri && fb->get_id == -1) {
- g_object_ref (fb);
- fb->get_id = mail_get_folder (fb->uri, 0, got_folder, fb, mail_thread_new);
- }
-}
-
-void
-folder_browser_set_folder (FolderBrowser *fb, CamelFolder *folder, const char *uri)
-{
- g_return_if_fail (IS_FOLDER_BROWSER (fb));
- g_return_if_fail (CAMEL_IS_FOLDER (folder));
-
- if (fb->get_id != -1) {
- /* FIXME: cancel the get_folder request? */
- }
-
- g_free (fb->uri);
- fb->uri = g_strdup (uri);
-
- g_object_ref (fb);
- got_folder (NULL, folder, fb);
-}
-
-void
-folder_browser_set_ui_component (FolderBrowser *fb, BonoboUIComponent *uicomp)
-{
- g_return_if_fail (IS_FOLDER_BROWSER (fb));
-
- if (fb->sensitize_timeout_id) {
- g_source_remove (fb->sensitize_timeout_id);
- fb->sensitize_timeout_id = 0;
- }
-
- if (fb->sensitise_state) {
- g_hash_table_destroy (fb->sensitise_state);
- fb->sensitise_state = NULL;
- }
-
- if (fb->uicomp)
- bonobo_object_unref (BONOBO_OBJECT (fb->uicomp));
-
- if (uicomp)
- bonobo_object_ref (BONOBO_OBJECT (uicomp));
-
- fb->uicomp = uicomp;
-}
-
-void
-folder_browser_set_shell_view(FolderBrowser *fb, GNOME_Evolution_ShellView shell_view)
-{
- CORBA_Environment ev;
-
- CORBA_exception_init(&ev);
- if (fb->shell_view != CORBA_OBJECT_NIL)
- CORBA_Object_release (fb->shell_view, &ev);
- CORBA_exception_free (&ev);
-
- fb->shell_view = CORBA_Object_duplicate (shell_view, &ev);
- CORBA_exception_free (&ev);
-
- /* small hack, at this point we've just been activated */
- if (fb->shell_view != CORBA_OBJECT_NIL)
- update_status_bar (fb);
-}
-
-extern CamelFolder *drafts_folder, *sent_folder, *outbox_folder;
-
-/**
- * folder_browser_is_drafts:
- * @fb: a FolderBrowser
- *
- * Return value: %TRUE if @fb refers to /local/Drafts or any other
- * configured Drafts folder.
- **/
-gboolean
-folder_browser_is_drafts (FolderBrowser *fb)
-{
- gboolean is_drafts = FALSE;
- EAccountList *accounts;
- EAccount *account;
- EIterator *iter;
-
- g_return_val_if_fail (IS_FOLDER_BROWSER (fb), FALSE);
-
- if (fb->uri == NULL || fb->folder == NULL)
- return FALSE;
-
- if (fb->folder == drafts_folder)
- return TRUE;
-
- accounts = mail_config_get_accounts ();
- iter = e_list_get_iterator ((EList *) accounts);
- while (e_iterator_is_valid (iter)) {
- account = (EAccount *) e_iterator_get (iter);
- if (account->drafts_folder_uri &&
- camel_store_uri_cmp (fb->folder->parent_store, account->drafts_folder_uri, fb->uri)) {
- is_drafts = TRUE;
- break;
- }
-
- e_iterator_next (iter);
- }
-
- g_object_unref (iter);
-
- return is_drafts;
-}
-
-/**
- * folder_browser_is_sent:
- * @fb: a FolderBrowser
- *
- * Return value: %TRUE if @fb refers to /local/Sent or any other
- * configured Sent folder.
- **/
-gboolean
-folder_browser_is_sent (FolderBrowser *fb)
-{
- gboolean is_sent = FALSE;
- EAccountList *accounts;
- EAccount *account;
- EIterator *iter;
-
- g_return_val_if_fail (IS_FOLDER_BROWSER (fb), FALSE);
-
- if (fb->uri == NULL || fb->folder == NULL)
- return FALSE;
-
- if (fb->folder == sent_folder)
- return TRUE;
-
- accounts = mail_config_get_accounts ();
- iter = e_list_get_iterator ((EList *) accounts);
- while (e_iterator_is_valid (iter)) {
- account = (EAccount *) e_iterator_get (iter);
- if (account->sent_folder_uri &&
- camel_store_uri_cmp (fb->folder->parent_store, account->sent_folder_uri, fb->uri)) {
- is_sent = TRUE;
- break;
- }
-
- e_iterator_next (iter);
- }
-
- g_object_unref (iter);
-
- return is_sent;
-}
-
-/**
- * folder_browser_is_outbox:
- * @fb: a FolderBrowser
- *
- * Return value: %TRUE if @fb refers to /local/Outbox or any other
- * configured Outbox folder.
- **/
-gboolean
-folder_browser_is_outbox (FolderBrowser *fb)
-{
- /* There can be only one. */
- return fb->folder == outbox_folder;
-}
-
-static int
-save_cursor_pos (FolderBrowser *fb)
-{
- ETreePath node;
- GtkAdjustment *adj;
- int row, y, height, paned_size;
- GConfClient *gconf;
-
- node = e_tree_get_cursor (fb->message_list->tree);
- if (!node)
- return -1;
-
- row = e_tree_row_of_node (fb->message_list->tree, node);
-
- if (row == -1)
- return 0;
-
- e_tree_get_cell_geometry (fb->message_list->tree, row, 0,
- NULL, &y, NULL, &height);
-
- gconf = mail_config_get_gconf_client ();
- paned_size = gconf_client_get_int (gconf, "/apps/evolution/mail/display/paned_size", NULL);
-
- adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (fb->message_list));
- y += adj->value - ((paned_size - height) / 2);
-
- return y;
-}
-
-static void
-set_cursor_pos (FolderBrowser *fb, int y)
-{
- GtkAdjustment *adj;
-
- if (y == -1)
- return;
-
- adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (fb->message_list));
- gtk_adjustment_set_value (adj, (gfloat)y);
-}
-
-static gboolean do_message_selected(FolderBrowser *fb);
-
-void
-folder_browser_set_message_preview (FolderBrowser *folder_browser, gboolean show_preview)
-{
- GConfClient *gconf;
- int paned_size, y;
-
- if (folder_browser->preview_shown == show_preview
- || folder_browser->message_list == NULL)
- return;
-
- folder_browser->preview_shown = show_preview;
-
- gconf = mail_config_get_gconf_client ();
- paned_size = gconf_client_get_int (gconf, "/apps/evolution/mail/display/paned_size", NULL);
-
- if (show_preview) {
- y = save_cursor_pos (folder_browser);
- gtk_paned_set_position (GTK_PANED (folder_browser->vpaned), paned_size);
- gtk_widget_show (GTK_WIDGET (folder_browser->mail_display));
- do_message_selected (folder_browser);
- set_cursor_pos (folder_browser, y);
- } else {
- gtk_widget_hide (GTK_WIDGET (folder_browser->mail_display));
- mail_display_set_message (folder_browser->mail_display, NULL, NULL, NULL);
- folder_browser_ui_message_loaded (folder_browser);
- }
-}
-
-enum {
- ESB_SAVE,
-};
-
-static ESearchBarItem folder_browser_search_menu_items[] = {
- E_FILTERBAR_ADVANCED,
- { NULL, 0, NULL },
- E_FILTERBAR_SAVE,
- E_FILTERBAR_EDIT,
- { NULL, 0, NULL },
- { N_("Create _Virtual Folder From Search..."), ESB_SAVE, NULL },
- { NULL, -1, NULL }
-};
-
-static void
-folder_browser_search_menu_activated (ESearchBar *esb, int id, FolderBrowser *fb)
-{
- EFilterBar *efb = (EFilterBar *)esb;
-
- d(printf("menu activated\n"));
-
- switch (id) {
- case ESB_SAVE:
- d(printf("Save vfolder\n"));
- if (efb->current_query) {
- FilterRule *rule = vfolder_clone_rule(efb->current_query);
- char *name, *text;
-
- text = e_search_bar_get_text(esb);
- name = g_strdup_printf("%s %s", rule->name, (text&&text[0])?text:"''");
- g_free (text);
- filter_rule_set_name(rule, name);
- g_free (name);
-
- filter_rule_set_source(rule, FILTER_SOURCE_INCOMING);
- vfolder_rule_add_source((VfolderRule *)rule, fb->uri);
- vfolder_gui_add_rule((VfolderRule *)rule);
- }
- break;
- }
-}
-
-static void
-folder_browser_config_search (EFilterBar *efb, FilterRule *rule, int id, const char *query, void *data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (data);
- ESearchingTokenizer *st;
- GList *partl;
- struct _camel_search_words *words;
- int i;
-
- st = E_SEARCHING_TOKENIZER (fb->mail_display->html->engine->ht);
-
- e_searching_tokenizer_set_secondary_search_string (st, NULL);
-
- /* we scan the parts of a rule, and set all the types we know about to the query string */
- partl = rule->parts;
- while (partl) {
- FilterPart *part = partl->data;
-
- if (!strcmp(part->name, "subject")) {
- FilterInput *input = (FilterInput *)filter_part_find_element(part, "subject");
- if (input)
- filter_input_set_value(input, query);
- } else if (!strcmp(part->name, "body")) {
- FilterInput *input = (FilterInput *)filter_part_find_element(part, "word");
- if (input)
- filter_input_set_value(input, query);
-
- words = camel_search_words_split(query);
- for (i=0;i<words->len;i++)
- e_searching_tokenizer_add_secondary_search_string (st, words->words[i]->word);
- camel_search_words_free (words);
- } else if(!strcmp(part->name, "sender")) {
- FilterInput *input = (FilterInput *)filter_part_find_element(part, "sender");
- if (input)
- filter_input_set_value(input, query);
- } else if(!strcmp(part->name, "to")) {
- FilterInput *input = (FilterInput *)filter_part_find_element(part, "recipient");
- if (input)
- filter_input_set_value(input, query);
- }
-
- partl = partl->next;
- }
-
- d(printf("configuring search for search string '%s', rule is '%s'\n", query, rule->name));
-
- mail_display_redisplay (fb->mail_display, FALSE);
-}
-
-static void
-folder_browser_search_do_search (ESearchBar *esb, FolderBrowser *fb)
-{
- char *search_word;
-
- if (fb->message_list == NULL)
- return;
-
- d(printf("do search\n"));
-
- g_object_get (esb, "query", &search_word, NULL);
-
- message_list_set_search (fb->message_list, search_word);
-
- d(printf("query is %s\n", search_word));
- g_free (search_word);
-}
-
-static void
-folder_browser_query_changed (ESearchBar *esb, FolderBrowser *fb)
-{
- int id;
-
- id = e_search_bar_get_item_id (esb);
- if (id == E_FILTERBAR_ADVANCED_ID)
- folder_browser_search_do_search (esb, fb);
-}
-
-void
-folder_browser_toggle_preview (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data)
-{
- FolderBrowser *fb = user_data;
- gboolean bstate;
- GConfClient *gconf;
-
- if (type != Bonobo_UIComponent_STATE_CHANGED || fb->message_list == NULL)
- return;
-
- bstate = atoi(state);
- e_meta_set_bool(fb->meta, "show_preview", bstate);
-
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_bool (gconf, "/apps/evolution/mail/display/show_preview", bstate, NULL);
-
- folder_browser_set_message_preview (fb, bstate);
-}
-
-void
-folder_browser_toggle_threads (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data)
-{
- FolderBrowser *fb = user_data;
- int prev_state;
- gboolean bstate;
- GConfClient *gconf;
-
- if (type != Bonobo_UIComponent_STATE_CHANGED || fb->message_list == NULL)
- return;
-
- bstate = atoi(state);
- e_meta_set_bool(fb->meta, "thread_list", bstate);
-
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_bool (gconf, "/apps/evolution/mail/display/thread_list", bstate, NULL);
-
- message_list_set_threaded (fb->message_list, bstate);
-
- prev_state = fb->selection_state;
- fb->selection_state = FB_SELSTATE_UNDEFINED;
- folder_browser_ui_set_selection_state (fb, prev_state);
-}
-
-void
-folder_browser_toggle_hide_deleted (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data)
-{
- FolderBrowser *fb = user_data;
- GConfClient *gconf;
-
- if (type != Bonobo_UIComponent_STATE_CHANGED || fb->message_list == NULL)
- return;
-
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_bool (gconf, "/apps/evolution/mail/display/show_deleted",
- !atoi (state), NULL);
-
- if (!(fb->folder && (fb->folder->folder_flags & CAMEL_FOLDER_IS_TRASH)))
- message_list_set_hidedeleted (fb->message_list, atoi (state));
-}
-
-void
-folder_browser_toggle_caret_mode(BonoboUIComponent *component,
- const char * path,
- Bonobo_UIComponent_EventType type,
- const char * state,
- gpointer user_data)
-{
- GConfClient *gconf;
-
- if (type != Bonobo_UIComponent_STATE_CHANGED)
- return;
-
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_bool (gconf, "/apps/evolution/mail/display/caret_mode",
- atoi(state), NULL);
-}
-
-void
-folder_browser_set_message_display_style (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data)
-{
- extern char *message_display_styles[];
- FolderBrowser *fb = user_data;
- GConfClient *gconf;
- int i;
-
- if (type != Bonobo_UIComponent_STATE_CHANGED
- || atoi (state) == 0
- || fb->message_list == NULL)
- return;
-
- gconf = mail_config_get_gconf_client ();
-
- for (i = 0; i < MAIL_CONFIG_DISPLAY_MAX; i++) {
- if (strstr (message_display_styles[i], path)) {
- fb->mail_display->display_style = i;
- mail_display_redisplay (fb->mail_display, TRUE);
-
- if (fb->pref_master)
- gconf_client_set_int (gconf, "/apps/evolution/mail/display/message_style", i, NULL);
-
- return;
- }
- }
-}
-
-void
-folder_browser_charset_changed (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- const char *charset;
-
- if (type != Bonobo_UIComponent_STATE_CHANGED
- || fb->message_list == NULL)
- return;
-
- if (atoi (state)) {
- /* Charset menu names are "Charset-%s" where %s is the charset name */
- charset = path + strlen ("Charset-");
- if (!strcmp (charset, FB_DEFAULT_CHARSET))
- charset = NULL;
-
- mail_display_set_charset (fb->mail_display, charset);
- }
-}
-
-static void vfolder_type_uid(CamelFolder *folder, const char *uid, const char *uri, int type);
-
-static void
-vfolder_type_current(FolderBrowser *fb, int type)
-{
- GPtrArray *uids;
- int i;
-
- /* get uid */
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (uids->len == 1)
- vfolder_type_uid (fb->folder, (char *)uids->pdata[0], fb->uri, type);
-
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
-}
-
-/* external api to vfolder/filter on X, based on current message */
-void vfolder_subject (GtkWidget *w, FolderBrowser *fb) { vfolder_type_current(fb, AUTO_SUBJECT); }
-void vfolder_sender (GtkWidget *w, FolderBrowser *fb) { vfolder_type_current(fb, AUTO_FROM); }
-void vfolder_recipient (GtkWidget *w, FolderBrowser *fb) { vfolder_type_current(fb, AUTO_TO); }
-void vfolder_mlist (GtkWidget *w, FolderBrowser *fb) { vfolder_type_current(fb, AUTO_MLIST); }
-
-static void filter_type_uid (CamelFolder *folder, const char *uid, const char *source, int type);
-
-static void
-filter_type_current (FolderBrowser *fb, int type)
-{
- GPtrArray *uids;
- int i;
- const char *source;
-
- if (folder_browser_is_sent (fb) || folder_browser_is_outbox (fb))
- source = FILTER_SOURCE_OUTGOING;
- else
- source = FILTER_SOURCE_INCOMING;
-
- /* get uid */
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (uids->len == 1)
- filter_type_uid (fb->folder, (char *)uids->pdata[0], source, type);
-
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
-}
-
-void filter_subject (GtkWidget *w, FolderBrowser *fb) { filter_type_current (fb, AUTO_SUBJECT); }
-void filter_sender (GtkWidget *w, FolderBrowser *fb) { filter_type_current (fb, AUTO_FROM); }
-void filter_recipient (GtkWidget *w, FolderBrowser *fb) { filter_type_current (fb, AUTO_TO); }
-void filter_mlist (GtkWidget *w, FolderBrowser *fb) { filter_type_current (fb, AUTO_MLIST); }
-
-/* ************************************************************ */
-
-/* popup api to vfolder/filter on X, based on current selection */
-struct _filter_data {
- CamelFolder *folder;
- const char *source;
- char *uid;
- int type;
- char *uri;
- char *mlist;
-};
-
-static void
-filter_data_free (struct _filter_data *fdata)
-{
- g_free (fdata->uid);
- g_free (fdata->uri);
- if (fdata->folder)
- camel_object_unref (fdata->folder);
- g_free (fdata->mlist);
- g_free (fdata);
-}
-
-static void
-vfolder_type_got_message(CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *d)
-{
- struct _filter_data *data = d;
-
- if (msg)
- vfolder_gui_add_from_message(msg, data->type, data->uri);
-
- filter_data_free (data);
-}
-
-static void
-vfolder_type_uid(CamelFolder *folder, const char *uid, const char *uri, int type)
-{
- struct _filter_data *data;
-
- data = g_malloc0(sizeof(*data));
- data->type = type;
- data->uri = g_strdup(uri);
- mail_get_message(folder, uid, vfolder_type_got_message, data, mail_thread_new);
-}
-
-static void vfolder_subject_uid (GtkWidget *w, struct _filter_data *fdata) { vfolder_type_uid(fdata->folder, fdata->uid, fdata->uri, AUTO_SUBJECT); }
-static void vfolder_sender_uid(GtkWidget *w, struct _filter_data *fdata) { vfolder_type_uid(fdata->folder, fdata->uid, fdata->uri, AUTO_FROM); }
-static void vfolder_recipient_uid(GtkWidget *w, struct _filter_data *fdata) { vfolder_type_uid(fdata->folder, fdata->uid, fdata->uri, AUTO_TO); }
-static void vfolder_mlist_uid(GtkWidget *w, struct _filter_data *fdata) { vfolder_type_uid(fdata->folder, fdata->uid, fdata->uri, AUTO_MLIST); }
-
-static void
-filter_type_got_message(CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *d)
-{
- struct _filter_data *data = d;
-
- if (msg)
- filter_gui_add_from_message(msg, data->source, data->type);
-
- filter_data_free (data);
-}
-
-static void
-filter_type_uid(CamelFolder *folder, const char *uid, const char *source, int type)
-{
- struct _filter_data *data;
-
- data = g_malloc0(sizeof(*data));
- data->type = type;
- data->source = source;
- mail_get_message(folder, uid, filter_type_got_message, data, mail_thread_new);
-}
-
-static void filter_subject_uid (GtkWidget *w, struct _filter_data *fdata) { filter_type_uid(fdata->folder, fdata->uid, fdata->source, AUTO_SUBJECT); }
-static void filter_sender_uid(GtkWidget *w, struct _filter_data *fdata) { filter_type_uid(fdata->folder, fdata->uid, fdata->source, AUTO_FROM); }
-static void filter_recipient_uid(GtkWidget *w, struct _filter_data *fdata) { filter_type_uid(fdata->folder, fdata->uid, fdata->source, AUTO_TO); }
-static void filter_mlist_uid(GtkWidget *w, struct _filter_data *fdata) { filter_type_uid(fdata->folder, fdata->uid, fdata->source, AUTO_MLIST); }
-
-void
-hide_none(GtkWidget *w, FolderBrowser *fb)
-{
- message_list_hide_clear (fb->message_list);
-}
-
-void
-hide_selected(GtkWidget *w, FolderBrowser *fb)
-{
- GPtrArray *uids;
- int i;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
- message_list_hide_uids (fb->message_list, uids);
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
-}
-
-void
-hide_deleted(GtkWidget *w, FolderBrowser *fb)
-{
- MessageList *ml = fb->message_list;
-
- message_list_hide_add(ml, "(match-all (system-flag \"deleted\"))", ML_HIDE_SAME, ML_HIDE_SAME);
-}
-
-void
-hide_read(GtkWidget *w, FolderBrowser *fb)
-{
- MessageList *ml = fb->message_list;
-
- message_list_hide_add(ml, "(match-all (system-flag \"seen\"))", ML_HIDE_SAME, ML_HIDE_SAME);
-}
-
-/* dum de dum, about the 3rd copy of this function throughout the mailer/camel */
-static const char *
-strip_re(const char *subject)
-{
- const unsigned char *s, *p;
-
- s = (unsigned char *) subject;
-
- while (*s) {
- while(isspace (*s))
- s++;
- if (s[0] == 0)
- break;
- if ((s[0] == 'r' || s[0] == 'R')
- && (s[1] == 'e' || s[1] == 'E')) {
- p = s+2;
- while (isdigit(*p) || (ispunct(*p) && (*p != ':')))
- p++;
- if (*p == ':') {
- s = p + 1;
- } else
- break;
- } else
- break;
- }
- return (char *) s;
-}
-
-void
-hide_subject(GtkWidget *w, FolderBrowser *fb)
-{
- const char *subject;
- GString *expr;
-
- if (fb->mail_display->current_message) {
- subject = camel_mime_message_get_subject(fb->mail_display->current_message);
- if (subject) {
- subject = strip_re(subject);
- if (subject && subject[0]) {
- expr = g_string_new ("(match-all (header-contains \"subject\" ");
- e_sexp_encode_string (expr, subject);
- g_string_append (expr, "))");
- message_list_hide_add (fb->message_list, expr->str, ML_HIDE_SAME, ML_HIDE_SAME);
- g_string_free (expr, TRUE);
- return;
- }
- }
- }
-}
-
-void
-hide_sender (GtkWidget *w, FolderBrowser *fb)
-{
- const CamelInternetAddress *from;
- const char *real, *addr;
- GString *expr;
-
- if (fb->mail_display->current_message) {
- from = camel_mime_message_get_from (fb->mail_display->current_message);
- if (camel_internet_address_get (from, 0, &real, &addr)) {
- expr = g_string_new ("(match-all (header-contains \"from\" ");
- e_sexp_encode_string (expr, addr);
- g_string_append (expr, "))");
- message_list_hide_add (fb->message_list, expr->str, ML_HIDE_SAME, ML_HIDE_SAME);
- g_string_free (expr, TRUE);
- return;
- }
- }
-}
-
-struct _label_data {
- FolderBrowser *fb;
- const char *label;
-};
-
-static void
-set_msg_label (GtkWidget *widget, gpointer user_data)
-{
- struct _label_data *data = user_data;
- GPtrArray *uids;
- int i;
-
- uids = g_ptr_array_new ();
- message_list_foreach (data->fb->message_list, enumerate_msg, uids);
- for (i = 0; i < uids->len; i++)
- camel_folder_set_message_user_tag (data->fb->folder, uids->pdata[i], "label", data->label);
- g_ptr_array_free (uids, TRUE);
-}
-
-static void
-label_closures_free (GPtrArray *closures)
-{
- struct _label_data *data;
- int i;
-
- for (i = 0; i < closures->len; i++) {
- data = closures->pdata[i];
- g_object_unref (data->fb);
- g_free (data);
- }
- g_ptr_array_free (closures, TRUE);
-}
-
-static void
-mark_as_seen_cb (GtkWidget *widget, void *user_data)
-{
- mark_as_seen (NULL, user_data, NULL);
-}
-
-static void
-mark_as_unseen_cb (GtkWidget *widget, void *user_data)
-{
- mark_as_unseen (NULL, user_data, NULL);
-}
-
-static void
-mark_as_important_cb (GtkWidget *widget, void *user_data)
-{
- mark_as_important (NULL, user_data, NULL);
-}
-
-static void
-mark_as_unimportant_cb (GtkWidget *widget, void *user_data)
-{
- mark_as_unimportant (NULL, user_data, NULL);
-}
-
-enum {
- SELECTION_SET = 1<<1,
- CAN_MARK_READ = 1<<2,
- CAN_MARK_UNREAD = 1<<3,
- CAN_DELETE = 1<<4,
- CAN_UNDELETE = 1<<5,
- IS_MAILING_LIST = 1<<6,
- CAN_RESEND = 1<<7,
- CAN_MARK_IMPORTANT = 1<<8,
- CAN_MARK_UNIMPORTANT = 1<<9,
- CAN_FLAG_FOR_FOLLOWUP = 1<<10,
- CAN_FLAG_COMPLETED = 1<<11,
- CAN_CLEAR_FLAG = 1<<12,
- CAN_ADD_SENDER = 1<<13
-};
-
-#define MLIST_VFOLDER (3)
-#define MLIST_FILTER (8)
-
-static EPopupMenu filter_menu[] = {
- E_POPUP_ITEM_CC (N_("VFolder on _Subject"), G_CALLBACK (vfolder_subject_uid), NULL, SELECTION_SET),
- E_POPUP_ITEM_CC (N_("VFolder on Se_nder"), G_CALLBACK (vfolder_sender_uid), NULL, SELECTION_SET),
- E_POPUP_ITEM_CC (N_("VFolder on _Recipients"), G_CALLBACK (vfolder_recipient_uid), NULL, SELECTION_SET),
- E_POPUP_ITEM_CC (N_("VFolder on Mailing _List"), G_CALLBACK (vfolder_mlist_uid), NULL, SELECTION_SET | IS_MAILING_LIST),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM_CC (N_("Filter on Sub_ject"), G_CALLBACK (filter_subject_uid), NULL, SELECTION_SET),
- E_POPUP_ITEM_CC (N_("Filter on Sen_der"), G_CALLBACK (filter_sender_uid), NULL, SELECTION_SET),
- E_POPUP_ITEM_CC (N_("Filter on Re_cipients"), G_CALLBACK (filter_recipient_uid), NULL, SELECTION_SET),
- E_POPUP_ITEM_CC (N_("Filter on _Mailing List"), G_CALLBACK (filter_mlist_uid), NULL, SELECTION_SET | IS_MAILING_LIST),
-
- E_POPUP_TERMINATOR
-};
-
-static EPopupMenu label_menu[] = {
- E_POPUP_PIXMAP_WIDGET_ITEM_CC (N_("None"), NULL, G_CALLBACK (set_msg_label), NULL, 0),
- E_POPUP_SEPARATOR,
- E_POPUP_PIXMAP_WIDGET_ITEM_CC (NULL, NULL, G_CALLBACK (set_msg_label), NULL, 0),
- E_POPUP_PIXMAP_WIDGET_ITEM_CC (NULL, NULL, G_CALLBACK (set_msg_label), NULL, 0),
- E_POPUP_PIXMAP_WIDGET_ITEM_CC (NULL, NULL, G_CALLBACK (set_msg_label), NULL, 0),
- E_POPUP_PIXMAP_WIDGET_ITEM_CC (NULL, NULL, G_CALLBACK (set_msg_label), NULL, 0),
- E_POPUP_PIXMAP_WIDGET_ITEM_CC (NULL, NULL, G_CALLBACK (set_msg_label), NULL, 0),
- E_POPUP_TERMINATOR
-};
-
-static EPopupMenu context_menu[] = {
- E_POPUP_ITEM (N_("_Open"), G_CALLBACK (open_msg), 0),
- E_POPUP_ITEM (N_("_Edit as New Message..."), G_CALLBACK (resend_msg), CAN_RESEND),
- E_POPUP_ITEM (N_("_Save As..."), G_CALLBACK (save_msg), 0),
- E_POPUP_ITEM (N_("_Print"), G_CALLBACK (print_msg), 0),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM (N_("_Reply to Sender"), G_CALLBACK (reply_to_sender), 0),
- E_POPUP_ITEM (N_("Reply to _List"), G_CALLBACK (reply_to_list), 0),
- E_POPUP_ITEM (N_("Reply to _All"), G_CALLBACK (reply_to_all), 0),
- E_POPUP_ITEM (N_("_Forward"), G_CALLBACK (forward), 0),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM (N_("Follo_w Up..."), G_CALLBACK (flag_for_followup), CAN_FLAG_FOR_FOLLOWUP),
- E_POPUP_ITEM (N_("Fla_g Completed"), G_CALLBACK (flag_followup_completed), CAN_FLAG_COMPLETED),
- E_POPUP_ITEM (N_("Cl_ear Flag"), G_CALLBACK (flag_followup_clear), CAN_CLEAR_FLAG),
-
- /* separator here? */
-
- E_POPUP_ITEM (N_("Mar_k as Read"), G_CALLBACK (mark_as_seen_cb), CAN_MARK_READ),
- E_POPUP_ITEM (N_("Mark as _Unread"), G_CALLBACK (mark_as_unseen_cb), CAN_MARK_UNREAD),
- E_POPUP_ITEM (N_("Mark as _Important"), G_CALLBACK (mark_as_important_cb), CAN_MARK_IMPORTANT),
- E_POPUP_ITEM (N_("_Mark as Unimportant"), G_CALLBACK (mark_as_unimportant_cb), CAN_MARK_UNIMPORTANT),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM (N_("_Delete"), G_CALLBACK (delete_msg), CAN_DELETE),
- E_POPUP_ITEM (N_("U_ndelete"), G_CALLBACK (undelete_msg), CAN_UNDELETE),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM (N_("Mo_ve to Folder..."), G_CALLBACK (move_msg_cb), 0),
- E_POPUP_ITEM (N_("_Copy to Folder..."), G_CALLBACK (copy_msg_cb), 0),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_SUBMENU (N_("Label"), label_menu, 0),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM (N_("Add Sender to Address_book"), G_CALLBACK (addrbook_sender), SELECTION_SET | CAN_ADD_SENDER),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_ITEM (N_("Appl_y Filters"), G_CALLBACK (apply_filters), 0),
-
- E_POPUP_SEPARATOR,
-
- E_POPUP_SUBMENU (N_("Crea_te Rule From Message"), filter_menu, SELECTION_SET),
-
- E_POPUP_TERMINATOR
-};
-
-/* Note: this must be kept in sync with the context_menu!!! */
-static char *context_pixmaps[] = {
- NULL, /* Open */
- NULL, /* Edit */
- "save-as-16.png",
- "print.xpm",
- NULL,
- "reply.xpm",
- NULL, /* Reply to List */
- "reply_to_all.xpm",
- "forward.xpm",
- NULL,
- "flag-for-followup-16.png",
- NULL, /* Flag */
- NULL, /* Clear */
- "mail-read.xpm",
- "mail-new.xpm",
- "priority-high.xpm",
- NULL, /* Mark as Unimportant */
- NULL,
- "evolution-trash-mini.png",
- "undelete_message-16.png",
- NULL,
- "move_message.xpm",
- "copy_16_message.xpm",
- NULL,
- NULL, /* Label */
- NULL,
- NULL, /* Add Sender to Addressbook */
- NULL,
- NULL, /* Apply Filters */
- NULL,
- NULL, /* Create Rule from Message */
-};
-
-struct cmpf_data {
- ETree *tree;
- int row, col;
-};
-
-static void
-context_menu_position_func (GtkMenu *menu, gint *x, gint *y,
- gboolean *push_in, gpointer user_data)
-{
- int tx, ty, tw, th;
- struct cmpf_data *closure = user_data;
-
- gdk_window_get_origin (GTK_WIDGET (closure->tree)->window, x, y);
- e_tree_get_cell_geometry (closure->tree, closure->row, closure->col,
- &tx, &ty, &tw, &th);
- *x += tx + tw / 2;
- *y += ty + th / 2;
-}
-
-static void
-setup_popup_icons (void)
-{
- int i;
-
- for (i = 0; context_menu[i].name; i++) {
- if (context_pixmaps[i]) {
- char *filename;
-
- filename = g_strdup_printf ("%s/%s", EVOLUTION_IMAGES, context_pixmaps[i]);
- context_menu[i].pixmap_widget = gtk_image_new_from_file (filename);
- g_free (filename);
- }
- }
-}
-
-/* handle context menu over message-list */
-static int
-on_right_click (ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, FolderBrowser *fb)
-{
- struct _filter_data *fdata = NULL;
- GPtrArray *uids, *closures;
- CamelMessageInfo *info;
- GSList *labels;
- int enable_mask = 0;
- int hide_mask = 0;
- char *mlist = NULL;
- GtkMenu *menu;
- int i;
-
- if (!folder_browser_is_sent (fb)) {
- enable_mask |= CAN_RESEND;
- hide_mask |= CAN_RESEND;
- } else {
- enable_mask |= CAN_ADD_SENDER;
- hide_mask |= CAN_ADD_SENDER;
- }
-
- if (folder_browser_is_drafts (fb)) {
- enable_mask |= CAN_ADD_SENDER;
- hide_mask |= CAN_ADD_SENDER;
- }
-
- if (fb->folder == outbox_folder) {
- enable_mask |= CAN_ADD_SENDER;
- hide_mask |= CAN_ADD_SENDER;
- }
-
- enable_mask |= SELECTION_SET;
-
- /* get a list of uids */
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
- if (uids->len >= 1) {
- /* gray-out any items we don't need */
- gboolean have_deleted = FALSE;
- gboolean have_undeleted = FALSE;
- gboolean have_seen = FALSE;
- gboolean have_unseen = FALSE;
- gboolean have_important = FALSE;
- gboolean have_unimportant = FALSE;
- gboolean have_flag_for_followup = FALSE;
- gboolean have_flag_completed = FALSE;
- gboolean have_flag_incomplete = FALSE;
- gboolean have_unflagged = FALSE;
- const char *tag;
-
- for (i = 0; i < uids->len; i++) {
- info = camel_folder_get_message_info (fb->folder, uids->pdata[i]);
- if (info == NULL)
- continue;
-
- if (i == 0 && uids->len == 1) {
- const char *mname, *p;
- char c, *o;
-
- /* used by filter/vfolder from X callbacks */
- fdata = g_malloc0(sizeof(*fdata));
- fdata->uid = g_strdup(uids->pdata[i]);
- fdata->uri = g_strdup(fb->uri);
- fdata->folder = fb->folder;
- camel_object_ref((CamelObject *)fdata->folder);
- if (folder_browser_is_sent (fb) || folder_browser_is_outbox (fb))
- fdata->source = FILTER_SOURCE_OUTGOING;
- else
- fdata->source = FILTER_SOURCE_INCOMING;
-
- enable_mask &= ~SELECTION_SET;
- mname = camel_message_info_mlist(info);
- if (mname && mname[0]) {
- fdata->mlist = g_strdup(mname);
-
- /* Escape the mailing list name before showing it */
- mlist = g_alloca ((strlen (mname) * 2) + 1);
- p = mname;
- o = mlist;
- while ((c = *p++)) {
- if (c == '_')
- *o++ = '_';
- *o++ = c;
- }
- *o = 0;
- }
- }
-
- if (info->flags & CAMEL_MESSAGE_SEEN)
- have_seen = TRUE;
- else
- have_unseen = TRUE;
-
- if (info->flags & CAMEL_MESSAGE_DELETED)
- have_deleted = TRUE;
- else
- have_undeleted = TRUE;
-
- if (info->flags & CAMEL_MESSAGE_FLAGGED)
- have_important = TRUE;
- else
- have_unimportant = TRUE;
-
- tag = camel_tag_get (&info->user_tags, "follow-up");
- if (tag && *tag) {
- have_flag_for_followup = TRUE;
- tag = camel_tag_get (&info->user_tags, "completed-on");
- if (tag && *tag)
- have_flag_completed = TRUE;
- else
- have_flag_incomplete = TRUE;
- } else
- have_unflagged = TRUE;
-
- camel_folder_free_message_info (fb->folder, info);
-
- if (have_seen && have_unseen && have_deleted && have_undeleted)
- break;
- }
-
- if (!have_unseen)
- enable_mask |= CAN_MARK_READ;
- if (!have_seen)
- enable_mask |= CAN_MARK_UNREAD;
-
- if (!have_undeleted)
- enable_mask |= CAN_DELETE;
- if (!have_deleted)
- enable_mask |= CAN_UNDELETE;
-
- if (!have_unimportant)
- enable_mask |= CAN_MARK_IMPORTANT;
- if (!have_important)
- enable_mask |= CAN_MARK_UNIMPORTANT;
-
- if (!have_flag_for_followup)
- enable_mask |= CAN_CLEAR_FLAG;
- if (!have_unflagged)
- enable_mask |= CAN_FLAG_FOR_FOLLOWUP;
- if (!have_flag_incomplete)
- enable_mask |= CAN_FLAG_COMPLETED;
-
- /*
- * Hide items that wont get used.
- */
- if (!(have_unseen && have_seen)) {
- if (have_seen)
- hide_mask |= CAN_MARK_READ;
- else
- hide_mask |= CAN_MARK_UNREAD;
- }
-
- if (!(have_undeleted && have_deleted)) {
- if (have_deleted)
- hide_mask |= CAN_DELETE;
- else
- hide_mask |= CAN_UNDELETE;
- }
-
- if (!(have_important && have_unimportant)) {
- if (have_important)
- hide_mask |= CAN_MARK_IMPORTANT;
- else
- hide_mask |= CAN_MARK_UNIMPORTANT;
- }
-
- if (!have_flag_for_followup)
- hide_mask |= CAN_CLEAR_FLAG;
- if (!have_unflagged)
- hide_mask |= CAN_FLAG_FOR_FOLLOWUP;
- if (!have_flag_incomplete)
- hide_mask |= CAN_FLAG_COMPLETED;
- }
-
- /* free uids */
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
-
- /* generate the "Filter on Mailing List" menu item name */
- if (mlist == NULL) {
- enable_mask |= IS_MAILING_LIST;
- filter_menu[MLIST_FILTER].name = g_strdup (_("Filter on _Mailing List"));
- filter_menu[MLIST_VFOLDER].name = g_strdup (_("VFolder on M_ailing List"));
- } else {
- filter_menu[MLIST_FILTER].name = g_strdup_printf (_("Filter on _Mailing List (%s)"), mlist);
- filter_menu[MLIST_VFOLDER].name = g_strdup_printf (_("VFolder on M_ailing List (%s)"), mlist);
- }
-
- /* create the label/colour menu */
- closures = g_ptr_array_new ();
- label_menu[0].closure = g_new (struct _label_data, 1);
- g_ptr_array_add (closures, label_menu[0].closure);
- g_object_ref (fb);
- ((struct _label_data *) label_menu[0].closure)->fb = fb;
- ((struct _label_data *) label_menu[0].closure)->label = NULL;
-
- i = 0;
- labels = mail_config_get_labels ();
- while (labels != NULL && i < 5) {
- struct _label_data *closure;
- MailConfigLabel *label;
- GdkPixmap *pixmap;
- GdkColormap *map;
- GdkColor colour;
- GdkGC *gc;
-
- label = labels->data;
- gdk_color_parse (label->colour, &colour);
- map = gdk_colormap_get_system ();
- gdk_color_alloc (map, &colour);
-
- pixmap = gdk_pixmap_new (GTK_WIDGET (fb)->window, 16, 16, -1);
- gc = gdk_gc_new (GTK_WIDGET (fb)->window);
- gdk_gc_set_foreground (gc, &colour);
- gdk_draw_rectangle (pixmap, gc, TRUE, 0, 0, 16, 16);
- gdk_gc_unref (gc);
-
- closure = g_new (struct _label_data, 1);
- g_object_ref (fb);
- closure->fb = fb;
- closure->label = label->tag;
-
- g_ptr_array_add (closures, closure);
-
- label_menu[i + 2].name = label->name;
- label_menu[i + 2].pixmap_widget = gtk_image_new_from_pixmap (pixmap, NULL);
- label_menu[i + 2].closure = closure;
-
- i++;
- labels = labels->next;
- }
-
- setup_popup_icons ();
-
- for (i = 0; i < sizeof (filter_menu) / sizeof (filter_menu[0]); i++)
- filter_menu[i].closure = fdata;
-
- menu = e_popup_menu_create (context_menu, enable_mask, hide_mask, fb);
- e_auto_kill_popup_menu_on_selection_done (menu);
-
- g_object_set_data_full ((GObject *) menu, "label_closures", closures, (GtkDestroyNotify) label_closures_free);
-
- if (fdata)
- g_object_set_data_full ((GObject *) menu, "filter_data", fdata, (GtkDestroyNotify) filter_data_free);
-
- if (event->type == GDK_KEY_PRESS) {
- struct cmpf_data closure;
-
- closure.tree = tree;
- closure.row = row;
- closure.col = col;
- gtk_menu_popup (menu, NULL, NULL, context_menu_position_func,
- &closure, 0, event->key.time);
- } else {
- gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
- event->button.button, event->button.time);
- }
-
- g_free (filter_menu[MLIST_FILTER].name);
- g_free (filter_menu[MLIST_VFOLDER].name);
-
- return TRUE;
-}
-
-static int
-html_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
-{
- FolderBrowser *fb = data;
- HTMLEngine *engine;
- HTMLPoint *point;
- ETreePath *path;
- int row;
-
- if (event->type != GDK_BUTTON_PRESS || event->button != 3)
- return FALSE;
-
- engine = GTK_HTML (widget)->engine;
- point = html_engine_get_point_at (engine, event->x, event->y, FALSE);
-
- if (point) {
- /* don't popup a menu if the mouse is hovering over a
- url or a source image because those situations are
- handled in mail-display.c's button_press_event
- callback */
- const char *src, *url;
-
- url = html_object_get_url (point->object, point->offset);
- src = html_object_get_src (point->object);
-
- if (url || src) {
- html_point_destroy (point);
- return FALSE;
- }
-
- html_point_destroy (point);
- }
-
- path = e_tree_get_cursor (fb->message_list->tree);
- row = e_tree_row_of_node (fb->message_list->tree, path);
-
- on_right_click (fb->message_list->tree, row, path, 2,
- (GdkEvent *) event, fb);
-
- return TRUE;
-}
-
-static int
-on_key_press (GtkWidget *widget, GdkEventKey *key, gpointer data)
-{
- FolderBrowser *fb = data;
- ETreePath *path;
- int row;
-
- if (key->state & GDK_CONTROL_MASK)
- return FALSE;
-
- path = e_tree_get_cursor (fb->message_list->tree);
- row = e_tree_row_of_node (fb->message_list->tree, path);
-
- switch (key->keyval) {
- case GDK_Delete:
- case GDK_KP_Delete:
- delete_msg (NULL, fb);
- return TRUE;
-
- case GDK_Menu:
- on_right_click (fb->message_list->tree, row, path, 2,
- (GdkEvent *)key, fb);
- return TRUE;
- case '!':
- toggle_as_important (NULL, fb, NULL);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static int
-etree_key (ETree *tree, int row, ETreePath path, int col, GdkEvent *ev, FolderBrowser *fb)
-{
- GtkAdjustment *vadj;
- gfloat page_size;
-
- if ((ev->key.state & GDK_CONTROL_MASK) != 0)
- return FALSE;
-
- vadj = gtk_scrolled_window_get_vadjustment (fb->mail_display->scroll);
- page_size = vadj->page_size - vadj->step_increment;
-
- switch (ev->key.keyval) {
- case GDK_space:
- /* Work around Ximian 4939 */
- if (vadj->upper < vadj->page_size)
- break;
- if (vadj->value < vadj->upper - vadj->page_size - page_size)
- vadj->value += page_size;
- else
- vadj->value = vadj->upper - vadj->page_size;
- gtk_adjustment_value_changed (vadj);
- break;
- case GDK_BackSpace:
- if (vadj->value > vadj->lower + page_size)
- vadj->value -= page_size;
- else
- vadj->value = vadj->lower;
- gtk_adjustment_value_changed (vadj);
- break;
- case GDK_Return:
- case GDK_KP_Enter:
- case GDK_ISO_Enter:
- open_msg (NULL, fb);
- break;
- default:
- return on_key_press ((GtkWidget *)tree, (GdkEventKey *)ev, fb);
- }
-
- return TRUE;
-}
-
-static void
-on_double_click (ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, FolderBrowser *fb)
-{
- /* Ignore double-clicks on columns where single-click doesn't
- * just select.
- */
- if (MESSAGE_LIST_COLUMN_IS_ACTIVE (col))
- return;
-
- open_msg (NULL, fb);
-}
-
-static void
-on_selection_changed (GtkObject *obj, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- FolderBrowserSelectionState state;
-
- /* we can get this signal at strange times...
- * if no uicomp, don't even bother */
-
- if (fb->uicomp == NULL)
- return;
-
- switch (e_selection_model_selected_count (E_SELECTION_MODEL (obj))) {
- case 0:
- state = FB_SELSTATE_NONE;
- break;
- case 1:
- state = FB_SELSTATE_SINGLE;
- break;
- default:
- state = FB_SELSTATE_MULTIPLE;
- break;
- }
-
- folder_browser_ui_set_selection_state (fb, state);
-
- update_status_bar_idle (fb);
-}
-
-
-static void
-on_cursor_activated (ETree *tree, int row, ETreePath path, gpointer user_data)
-{
- on_selection_changed ((GtkObject *)tree, user_data);
-}
-
-static gboolean
-fb_resize_cb (GtkWidget *w, GdkEventButton *e, FolderBrowser *fb)
-{
- GConfClient *gconf;
-
- if (GTK_WIDGET_REALIZED (w) && fb->preview_shown) {
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_int (gconf, "/apps/evolution/mail/display/paned_size", gtk_paned_get_position (GTK_PANED (w)), NULL);
- }
-
- return FALSE;
-}
-
-/* hack to get around the fact setting the paned size doesn't work */
-static void
-paned_realised(GtkWidget *w, FolderBrowser *fb)
-{
- GConfClient *gconf;
- int size;
-
- gconf = mail_config_get_gconf_client ();
- size = gconf_client_get_int (gconf, "/apps/evolution/mail/display/paned_size", NULL);
- gtk_paned_set_position (GTK_PANED (fb->vpaned), size);
-}
-
-static void
-folder_browser_gui_init (FolderBrowser *fb)
-{
- extern RuleContext *search_context;
- ESelectionModel *esm;
-
- /* The panned container */
- fb->vpaned = gtk_vpaned_new ();
- g_signal_connect(fb->vpaned, "realize", G_CALLBACK(paned_realised), fb);
- gtk_widget_show (fb->vpaned);
-
- gtk_table_attach (GTK_TABLE (fb), fb->vpaned,
- 0, 1, 1, 3,
- GTK_FILL | GTK_EXPAND,
- GTK_FILL | GTK_EXPAND,
- 0, 0);
-
- /* quick-search bar */
- if (search_context) {
- const char *systemrules = g_object_get_data (G_OBJECT (search_context), "system");
- const char *userrules = g_object_get_data (G_OBJECT (search_context), "user");
-
- fb->search = e_filter_bar_new (search_context, systemrules, userrules,
- folder_browser_config_search, fb);
- e_search_bar_set_menu ((ESearchBar *)fb->search, folder_browser_search_menu_items);
- }
-
- gtk_widget_show (GTK_WIDGET (fb->search));
-
- g_signal_connect (fb->search, "menu_activated",
- G_CALLBACK (folder_browser_search_menu_activated), fb);
- g_signal_connect (fb->search, "search_activated",
- G_CALLBACK (folder_browser_search_do_search), fb);
- g_signal_connect (fb->search, "query_changed",
- G_CALLBACK (folder_browser_query_changed), fb);
-
- gtk_table_attach (GTK_TABLE (fb), GTK_WIDGET (fb->search),
- 0, 1, 0, 1,
- GTK_FILL | GTK_EXPAND,
- 0,
- 0, 0);
-
- esm = e_tree_get_selection_model (E_TREE (fb->message_list->tree));
- g_signal_connect (esm, "selection_changed", G_CALLBACK (on_selection_changed), fb);
- g_signal_connect (esm, "cursor_activated", G_CALLBACK (on_cursor_activated), fb);
- fb->selection_state = FB_SELSTATE_NONE; /* default to none */
-
- gtk_paned_add1 (GTK_PANED (fb->vpaned), GTK_WIDGET (fb->message_list));
- gtk_widget_show (GTK_WIDGET (fb->message_list));
-
- fb->paned_resize_id = g_signal_connect (fb->vpaned, "button_release_event",
- G_CALLBACK (fb_resize_cb), fb);
-
- gtk_paned_add2 (GTK_PANED (fb->vpaned), GTK_WIDGET (fb->mail_display));
- gtk_widget_show (GTK_WIDGET (fb->mail_display));
- gtk_widget_show (GTK_WIDGET (fb));
-}
-
-/* mark the message seen if the current message still matches */
-static gint
-do_mark_seen (gpointer data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (data);
-
- if (fb->new_uid && fb->loaded_uid && !strcmp (fb->new_uid, fb->loaded_uid)) {
- camel_folder_set_message_flags (fb->folder, fb->new_uid,
- CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
- }
-
- return FALSE;
-}
-
-/* callback when we have the message to display, after async loading it (see below) */
-/* if we have pending uid's, it means another was selected before we finished displaying
- the last one - so we cycle through and start loading the pending one immediately now */
-static void
-done_message_selected (CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *data)
-{
- FolderBrowser *fb = data;
- CamelMessageInfo *info;
- GConfClient *gconf;
- int timeout;
-
- if (folder != fb->folder || fb->mail_display == NULL)
- return;
-
- gconf = mail_config_get_gconf_client ();
- timeout = gconf_client_get_int (gconf, "/apps/evolution/mail/display/mark_seen_timeout", NULL);
-
- info = camel_folder_get_message_info (fb->folder, uid);
- mail_display_set_message (fb->mail_display, (CamelMedium *) msg, fb->folder, info);
- if (info)
- camel_folder_free_message_info (fb->folder, info);
-
- /* FIXME: should this signal be emitted here?? */
- g_signal_emit (fb, folder_browser_signals[MESSAGE_LOADED], 0, uid);
-
- /* pain, if we have pending stuff, re-run */
- if (fb->pending_uid) {
- g_free (fb->loading_uid);
- fb->loading_uid = fb->pending_uid;
- fb->pending_uid = NULL;
-
- mail_get_message (fb->folder, fb->loading_uid, done_message_selected, fb, mail_thread_new);
- return;
- }
-
- g_free (fb->loaded_uid);
- fb->loaded_uid = fb->loading_uid;
- fb->loading_uid = NULL;
-
- folder_browser_ui_message_loaded (fb);
-
- /* if we are still on the same message, do the 'idle read' thing */
- if (fb->seen_id)
- gtk_timeout_remove (fb->seen_id);
-
- if (msg && gconf_client_get_bool (gconf, "/apps/evolution/mail/display/mark_seen", NULL)) {
- if (timeout > 0)
- fb->seen_id = gtk_timeout_add (timeout, do_mark_seen, fb);
- else
- do_mark_seen (fb);
- }
-}
-
-/* ok we waited enough, display it anyway (see below) */
-static gboolean
-do_message_selected (FolderBrowser *fb)
-{
- d(printf ("%p: selecting uid %s (delayed)\n", fb, fb->new_uid ? fb->new_uid : "NONE"));
-
- fb->loading_id = 0;
-
- /* if we are loading, then set a pending, but leave the loading, coudl cancel here (?) */
- if (fb->loading_uid) {
- if (fb->new_uid == NULL || fb->pending_uid == NULL || strcmp(fb->pending_uid, fb->new_uid) != 0) {
- g_free (fb->pending_uid);
- fb->pending_uid = g_strdup (fb->new_uid);
- }
- } else {
- if (fb->new_uid) {
- if (fb->loaded_uid == NULL || strcmp(fb->new_uid, fb->loaded_uid) != 0) {
- fb->loading_uid = g_strdup (fb->new_uid);
- mail_get_message (fb->folder, fb->loading_uid, done_message_selected, fb, mail_thread_new);
- }
- } else {
- mail_display_set_message (fb->mail_display, NULL, NULL, NULL);
- }
- }
-
- return FALSE;
-}
-
-/* when a message is selected, wait a while before trying to display it */
-static void
-on_message_selected (MessageList *ml, const char *uid, FolderBrowser *fb)
-{
- d(printf ("%p: selecting uid %s (direct)\n", fb, uid ? uid : "NONE"));
-
- if (fb->loading_id != 0)
- gtk_timeout_remove (fb->loading_id);
-
- g_free (fb->new_uid);
- fb->new_uid = g_strdup (uid);
-
- if (fb->preview_shown)
- fb->loading_id = gtk_timeout_add (100, (GtkFunction)do_message_selected, fb);
-}
-
-static gboolean
-on_message_list_focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
-{
- FolderBrowser *fb = (FolderBrowser *) user_data;
-
- d(printf ("got focus!\n"));
- folder_browser_ui_message_list_focus (fb);
-
- return FALSE;
-}
-
-static gboolean
-on_message_list_focus_out (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
-{
- FolderBrowser *fb = (FolderBrowser *) user_data;
-
- d(printf ("got unfocus!\n"));
- folder_browser_ui_message_list_unfocus (fb);
-
- return FALSE;
-}
-
-static void
-folder_browser_init (FolderBrowser *fb)
-{
- fb->async_event = mail_async_event_new ();
- fb->get_id = -1;
-}
-
-static void
-my_folder_browser_init (FolderBrowser *fb)
-{
- int i;
-
- fb->view_instance = NULL;
- fb->view_menus = NULL;
-
- fb->pref_master = FALSE;
-
- /*
- * Setup parent class fields.
- */
- GTK_TABLE (fb)->homogeneous = FALSE;
- gtk_table_resize (GTK_TABLE (fb), 1, 2);
-
- /*
- * Our instance data
- */
- fb->message_list = (MessageList *)message_list_new ();
- fb->mail_display = (MailDisplay *)mail_display_new ();
-
- fb->preview_shown = TRUE;
-
- g_signal_connect (fb->mail_display->html, "key_press_event",
- G_CALLBACK (on_key_press), fb);
- g_signal_connect (fb->mail_display->html, "button_press_event",
- G_CALLBACK (html_button_press_event), fb);
-
- g_signal_connect (fb->message_list->tree, "key_press",
- G_CALLBACK (etree_key), fb);
-
- g_signal_connect (fb->message_list->tree, "right_click",
- G_CALLBACK (on_right_click), fb);
-
- g_signal_connect (fb->message_list->tree, "double_click",
- G_CALLBACK (on_double_click), fb);
-
- g_signal_connect (fb->message_list, "focus_in_event",
- G_CALLBACK (on_message_list_focus_in), fb);
-
- g_signal_connect (fb->message_list, "focus_out_event",
- G_CALLBACK (on_message_list_focus_out), fb);
-
- g_signal_connect (fb->message_list, "message_selected",
- G_CALLBACK (on_message_selected), fb);
-
- /* drag & drop */
- e_tree_drag_source_set (fb->message_list->tree, GDK_BUTTON1_MASK,
- drag_types, num_drag_types, GDK_ACTION_MOVE | GDK_ACTION_COPY);
-
- g_signal_connect (fb->message_list->tree, "tree_drag_data_get",
- G_CALLBACK (message_list_drag_data_get), fb);
-
- e_tree_drag_dest_set (fb->message_list->tree, GTK_DEST_DEFAULT_ALL,
- drag_types, num_drag_types, GDK_ACTION_MOVE | GDK_ACTION_COPY);
-
- g_signal_connect (fb->message_list->tree, "tree_drag_data_received",
- G_CALLBACK (message_list_drag_data_received), fb);
-
- /* cut, copy & paste */
- fb->invisible = gtk_invisible_new ();
- g_object_ref (fb->invisible);
- gtk_object_sink ((GtkObject *) fb->invisible);
-
- for (i = 0; i < num_paste_types; i++)
- gtk_selection_add_target (fb->invisible, clipboard_atom,
- paste_types[i].target,
- paste_types[i].info);
-
- g_signal_connect (fb->invisible, "selection_get",
- G_CALLBACK (selection_get), fb);
- g_signal_connect (fb->invisible, "selection_clear_event",
- G_CALLBACK (selection_clear_event), fb);
- g_signal_connect (fb->invisible, "selection_received",
- G_CALLBACK (selection_received), fb);
-
- folder_browser_gui_init (fb);
-}
-
-GtkWidget *
-folder_browser_new (const GNOME_Evolution_Shell shell, const char *uri)
-{
- CORBA_Environment ev;
- FolderBrowser *folder_browser;
-
- CORBA_exception_init (&ev);
-
- folder_browser = g_object_new (folder_browser_get_type (), NULL);
-
- my_folder_browser_init (folder_browser);
-
- folder_browser->shell = CORBA_Object_duplicate (shell, &ev);
- if (ev._major != CORBA_NO_EXCEPTION) {
- folder_browser->shell = CORBA_OBJECT_NIL;
- gtk_widget_destroy (GTK_WIDGET (folder_browser));
- CORBA_exception_free (&ev);
- return NULL;
- }
-
- CORBA_exception_free (&ev);
-
- if (uri) {
- folder_browser->uri = g_strdup (uri);
- folder_browser->meta = mail_tool_get_meta_data(uri);
- g_object_ref (folder_browser);
- folder_browser->get_id = mail_get_folder (folder_browser->uri, 0, got_folder,
- folder_browser, mail_thread_new);
- }
-
- return GTK_WIDGET (folder_browser);
-}
-
-
-E_MAKE_TYPE (folder_browser, "FolderBrowser", FolderBrowser, folder_browser_class_init, folder_browser_init, PARENT_TYPE);
diff --git a/mail/folder-browser.h b/mail/folder-browser.h
deleted file mode 100644
index 12d21cd526..0000000000
--- a/mail/folder-browser.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-
-#ifndef _FOLDER_BROWSER_H_
-#define _FOLDER_BROWSER_H_
-
-#include <gtk/gtktable.h>
-#include "camel/camel-stream.h"
-#include <bonobo/bonobo-property-bag.h>
-#include <bonobo/bonobo-ui-component.h>
-#include <widgets/misc/e-filter-bar.h>
-#include "widgets/menus/gal-view-menus.h"
-#include "filter/filter-rule.h"
-#include "filter/filter-context.h" /*eek*/
-#include "message-list.h"
-#include "mail-display.h"
-#include "mail-types.h"
-#include "shell/Evolution.h"
-
-#define FOLDER_BROWSER_TYPE (folder_browser_get_type ())
-#define FOLDER_BROWSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FOLDER_BROWSER_TYPE, FolderBrowser))
-#define FOLDER_BROWSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), FOLDER_BROWSER_TYPE, FolderBrowserClass))
-#define IS_FOLDER_BROWSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FOLDER_BROWSER_TYPE))
-#define IS_FOLDER_BROWSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), FOLDER_BROWSER_TYPE))
-
-#define FB_DEFAULT_CHARSET _("Default")
-
-#define FOLDER_BROWSER_IS_DESTROYED(fb) (!fb || !fb->message_list || !fb->mail_display || !fb->folder)
-
-typedef enum _FolderBrowserSelectionState {
- FB_SELSTATE_NONE,
- FB_SELSTATE_SINGLE,
- FB_SELSTATE_MULTIPLE,
- FB_SELSTATE_UNDEFINED
-} FolderBrowserSelectionState;
-
-struct _FolderBrowser {
- GtkTable parent;
-
- BonoboPropertyBag *properties;
-
- GNOME_Evolution_Shell shell;
- GNOME_Evolution_ShellView shell_view;
-
- BonoboUIComponent *uicomp;
-
- /*
- * The current URI being displayed by the FolderBrowser
- */
- char *uri;
- CamelFolder *folder;
- int unread_count; /* last known unread message count */
-
- /* async loading stuff */
- char *loading_uid; /* what uid am i loading now */
- char *pending_uid; /* what uid should i load next */
- char *new_uid; /* place to save the next uid during idle timeout */
- char *loaded_uid; /* what we have loaded */
- guint loading_id;
- guint seen_id;
-
- gulong paned_resize_id;
-
- /* a folder we are expunging, dont use other than to compare the pointer value */
- CamelFolder *expunging;
- int expunge_mlfocussed; /* true if the ml was focussed before we expunged */
-
- MessageList *message_list;
- MailDisplay *mail_display;
- GtkWidget *vpaned;
-
- EFilterBar *search;
- FilterRule *search_full; /* if we have a full search active */
-
- struct _EMeta *meta; /* various per-folder meta-data */
-
- guint32 preview_shown : 1;
- guint32 threaded : 1;
- guint32 pref_master : 1;
-
- FolderBrowserSelectionState selection_state;
- GSList *sensitize_changes;
- GHashTable *sensitise_state; /* the last sent sensitise state, to avoid much bonobo overhead */
- int sensitize_timeout_id;
- int update_status_bar_idle_id;
-
- /* View instance and the menu handler object */
- GalViewInstance *view_instance;
- GalViewMenus *view_menus;
-
- GtkWidget *invisible;
- GByteArray *clipboard_selection;
-
- /* for async events */
- struct _MailAsyncEvent *async_event;
-
- int get_id; /* for getting folder op */
-
- /* info used by popup for filter/vfolder */
- struct _popup_filter_data *popup;
-};
-
-typedef struct {
- GtkTableClass parent_class;
-
- /* signals */
- void (*folder_loaded) (FolderBrowser *fb, const char *uri);
- void (*message_loaded) (FolderBrowser *fb, const char *uid);
-} FolderBrowserClass;
-
-struct fb_ondemand_closure {
- FilterRule *rule;
- FolderBrowser *fb;
- gchar *path;
-};
-
-GtkType folder_browser_get_type (void);
-GtkWidget *folder_browser_new (const GNOME_Evolution_Shell shell,
- const char *uri);
-
-void folder_browser_set_folder (FolderBrowser *fb, CamelFolder *folder, const char *uri);
-
-void folder_browser_set_ui_component (FolderBrowser *fb, BonoboUIComponent *uicomp);
-void folder_browser_set_shell_view (FolderBrowser *fb, GNOME_Evolution_ShellView shell_view);
-
-void folder_browser_set_message_preview (FolderBrowser *folder_browser,
- gboolean show_message_preview);
-void folder_browser_clear_search (FolderBrowser *fb);
-
-void folder_browser_cut (GtkWidget *widget, FolderBrowser *fb);
-void folder_browser_copy (GtkWidget *widget, FolderBrowser *fb);
-void folder_browser_paste (GtkWidget *widget, FolderBrowser *fb);
-
-void folder_browser_reload (FolderBrowser *fb);
-
-/* callbacks for functions on the folder-browser */
-void vfolder_subject (GtkWidget *w, FolderBrowser *fb);
-void vfolder_sender (GtkWidget *w, FolderBrowser *fb);
-void vfolder_recipient (GtkWidget *w, FolderBrowser *fb);
-void vfolder_mlist (GtkWidget *w, FolderBrowser *fb);
-
-void filter_subject (GtkWidget *w, FolderBrowser *fb);
-void filter_sender (GtkWidget *w, FolderBrowser *fb);
-void filter_recipient (GtkWidget *w, FolderBrowser *fb);
-void filter_mlist (GtkWidget *w, FolderBrowser *fb);
-
-void hide_read(GtkWidget *w, FolderBrowser *fb);
-void hide_deleted(GtkWidget *w, FolderBrowser *fb);
-void hide_selected(GtkWidget *w, FolderBrowser *fb);
-void hide_none(GtkWidget *w, FolderBrowser *fb);
-void hide_subject(GtkWidget *w, FolderBrowser *fb);
-void hide_sender(GtkWidget *w, FolderBrowser *fb);
-
-void folder_browser_toggle_preview (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data);
-
-void folder_browser_toggle_threads (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data);
-
-void folder_browser_toggle_hide_deleted (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data);
-
-void folder_browser_toggle_caret_mode (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data);
-
-void folder_browser_set_message_display_style (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data);
-
-void folder_browser_charset_changed (BonoboUIComponent *component,
- const char *path,
- Bonobo_UIComponent_EventType type,
- const char *state,
- gpointer user_data);
-
-gboolean folder_browser_is_drafts (FolderBrowser *fb);
-gboolean folder_browser_is_sent (FolderBrowser *fb);
-gboolean folder_browser_is_outbox (FolderBrowser *fb);
-
-#endif /* _FOLDER_BROWSER_H_ */
diff --git a/mail/mail-account-gui.c b/mail/mail-account-gui.c
index 2bba8944e6..6d965ece6b 100644
--- a/mail/mail-account-gui.c
+++ b/mail/mail-account-gui.c
@@ -1862,7 +1862,6 @@ mail_account_gui_save (MailAccountGui *gui)
{
EAccount *account, *new;
CamelProvider *provider = NULL;
- CamelURL *source_url = NULL, *url;
gboolean is_new = FALSE;
const char *new_name;
gboolean is_storage;
@@ -1907,10 +1906,8 @@ mail_account_gui_save (MailAccountGui *gui)
/* source */
save_service (&gui->source, gui->extra_config, new->source);
- if (new->source->url) {
+ if (new->source->url)
provider = camel_session_get_provider (session, new->source->url, NULL);
- source_url = provider ? camel_url_new (new->source->url, NULL) : NULL;
- }
new->source->auto_check = gtk_toggle_button_get_active (gui->source_auto_check);
if (new->source->auto_check)
@@ -1924,34 +1921,21 @@ mail_account_gui_save (MailAccountGui *gui)
save_service (&gui->transport, NULL, new->transport);
/* Check to make sure that the Drafts folder uri is "valid" before assigning it */
- url = source_url && gui->drafts_folder_uri ? camel_url_new (gui->drafts_folder_uri, NULL) : NULL;
- if (mail_config_get_account_by_source_url (gui->drafts_folder_uri) ||
- (url && provider->url_equal (source_url, url))) {
+ if (mail_config_get_account_by_source_url (gui->drafts_folder_uri)) {
new->drafts_folder_uri = g_strdup (gui->drafts_folder_uri);
} else {
/* assign defaults - the uri is unknown to us (probably pointed to an old source url) */
new->drafts_folder_uri = g_strdup (default_drafts_folder_uri);
}
- if (url)
- camel_url_free (url);
-
/* Check to make sure that the Sent folder uri is "valid" before assigning it */
- url = source_url && gui->sent_folder_uri ? camel_url_new (gui->sent_folder_uri, NULL) : NULL;
- if (mail_config_get_account_by_source_url (gui->sent_folder_uri) ||
- (url && provider->url_equal (source_url, url))) {
+ if (mail_config_get_account_by_source_url (gui->sent_folder_uri)) {
new->sent_folder_uri = g_strdup (gui->sent_folder_uri);
} else {
/* assign defaults - the uri is unknown to us (probably pointed to an old source url) */
new->sent_folder_uri = g_strdup (default_sent_folder_uri);
}
- if (url)
- camel_url_free (url);
-
- if (source_url)
- camel_url_free (source_url);
-
new->always_cc = gtk_toggle_button_get_active (gui->always_cc);
new->cc_addrs = g_strdup (gtk_entry_get_text (gui->cc_addrs));
new->always_bcc = gtk_toggle_button_get_active (gui->always_bcc);
diff --git a/mail/mail-callbacks.c b/mail/mail-callbacks.c
deleted file mode 100644
index 21690c55ce..0000000000
--- a/mail/mail-callbacks.c
+++ /dev/null
@@ -1,3239 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* mail-ops.c: callbacks for the mail toolbar/menus */
-
-/*
- * Authors:
- * Dan Winship <danw@ximian.com>
- * Peter Williams <peterw@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2000 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <time.h>
-#include <errno.h>
-
-#include <gtkhtml/gtkhtml.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <gtk/gtkmessagedialog.h>
-
-#include <libgnomeprint/gnome-print-job.h>
-#include <libgnomeprintui/gnome-print-dialog.h>
-#include <libgnomeprintui/gnome-print-job-preview.h>
-
-#include <bonobo/bonobo-widget.h>
-#include <bonobo/bonobo-socket.h>
-#include <gal/e-table/e-table.h>
-#include <e-util/e-dialog-utils.h>
-#include <filter/filter-editor.h>
-
-#include "mail.h"
-#include "message-browser.h"
-#include "mail-callbacks.h"
-#include "mail-config.h"
-#include "mail-accounts.h"
-#include "mail-config-druid.h"
-#include "mail-mt.h"
-#include "mail-tools.h"
-#include "mail-ops.h"
-#include "mail-local.h"
-#include "mail-search.h"
-#include "mail-send-recv.h"
-#include "mail-vfolder.h"
-#include "mail-folder-cache.h"
-#include "folder-browser.h"
-#include "subscribe-dialog.h"
-#include "message-tag-editor.h"
-#include "message-tag-followup.h"
-
-#include "Evolution.h"
-#include "evolution-storage.h"
-
-#include "evolution-shell-client.h"
-
-#define d(x) x
-
-#define FB_WINDOW(fb) GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (fb), GTK_TYPE_WINDOW))
-
-
-/* default is default gtk response
- if again is != NULL, a checkbox "dont show this again" will appear, and the result stored in *again
-*/
-static gboolean
-e_question (GtkWindow *parent, int def, gboolean *again, const char *fmt, ...)
-{
- GtkWidget *mbox, *check = NULL;
- va_list ap;
- int button;
- char *str;
-
- va_start (ap, fmt);
- str = g_strdup_vprintf (fmt, ap);
- va_end (ap);
- mbox = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
- "%s", str);
- g_free (str);
- gtk_dialog_set_default_response ((GtkDialog *) mbox, def);
- if (again) {
- check = gtk_check_button_new_with_label (_("Don't show this message again."));
- gtk_box_pack_start ((GtkBox *)((GtkDialog *) mbox)->vbox, check, TRUE, TRUE, 10);
- gtk_widget_show (check);
- }
-
- button = gtk_dialog_run ((GtkDialog *) mbox);
- if (again)
- *again = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
- gtk_widget_destroy (mbox);
-
- return button == GTK_RESPONSE_YES;
-}
-
-
-struct _composer_callback_data {
- unsigned int ref_count;
-
- CamelFolder *drafts_folder;
- char *drafts_uid;
-
- CamelFolder *folder;
- guint32 flags, set;
- char *uid;
-};
-
-static struct _composer_callback_data *
-ccd_new (void)
-{
- struct _composer_callback_data *ccd;
-
- ccd = g_new (struct _composer_callback_data, 1);
- ccd->ref_count = 1;
- ccd->drafts_folder = NULL;
- ccd->drafts_uid = NULL;
- ccd->folder = NULL;
- ccd->flags = 0;
- ccd->set = 0;
- ccd->uid = NULL;
-
- return ccd;
-}
-
-static void
-free_ccd (struct _composer_callback_data *ccd)
-{
- if (ccd->drafts_folder)
- camel_object_unref (ccd->drafts_folder);
- g_free (ccd->drafts_uid);
-
- if (ccd->folder)
- camel_object_unref (ccd->folder);
- g_free (ccd->uid);
- g_free (ccd);
-}
-
-static void
-ccd_ref (struct _composer_callback_data *ccd)
-{
- ccd->ref_count++;
-}
-
-static void
-ccd_unref (struct _composer_callback_data *ccd)
-{
- ccd->ref_count--;
- if (ccd->ref_count == 0)
- free_ccd (ccd);
-}
-
-
-static void
-composer_destroy_cb (gpointer user_data, GObject *deadbeef)
-{
- ccd_unref (user_data);
-}
-
-
-static void
-druid_destroy_cb (gpointer user_data, GObject *deadbeef)
-{
- gtk_main_quit ();
-}
-
-static gboolean
-configure_mail (FolderBrowser *fb)
-{
- MailConfigDruid *druid;
-
- if (e_question (FB_WINDOW (fb), GTK_RESPONSE_YES, NULL,
- _("You have not configured the mail client.\n"
- "You need to do this before you can send,\n"
- "receive or compose mail.\n"
- "Would you like to configure it now?"))) {
- druid = mail_config_druid_new ();
- g_object_weak_ref ((GObject *) druid, (GWeakNotify) druid_destroy_cb, NULL);
- gtk_widget_show ((GtkWidget *) druid);
- gtk_grab_add ((GtkWidget *) druid);
- gtk_main ();
- }
-
- return mail_config_is_configured ();
-}
-
-static gboolean
-check_send_configuration (FolderBrowser *fb)
-{
- EAccount *account;
-
- if (!mail_config_is_configured ()) {
- if (fb == NULL) {
- e_notice (NULL, GTK_MESSAGE_WARNING,
- _("You need to configure an account\nbefore you can compose mail."));
- return FALSE;
- }
-
- if (!configure_mail (fb))
- return FALSE;
- }
-
- /* Get the default account */
- account = mail_config_get_default_account ();
-
- /* Check for an identity */
- if (!account) {
- e_notice (FB_WINDOW (fb), GTK_MESSAGE_WARNING,
- _("You need to configure an identity\nbefore you can compose mail."));
- return FALSE;
- }
-
- /* Check for a transport */
- if (!account->transport->url) {
- e_notice (FB_WINDOW (fb), GTK_MESSAGE_WARNING,
- _("You need to configure a mail transport\n"
- "before you can compose mail."));
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EDestination **recipients)
-{
- gboolean show_again, res;
- GConfClient *gconf;
- GString *str;
- int i;
-
- gconf = mail_config_get_gconf_client ();
-
- if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL))
- return TRUE;
-
- /* FIXME: this wording sucks */
- str = g_string_new (_("You are sending an HTML-formatted message. Please make sure that\n"
- "the following recipients are willing and able to receive HTML mail:\n"));
- for (i = 0; recipients[i] != NULL; ++i) {
- if (!e_destination_get_html_mail_pref (recipients[i])) {
- const char *name;
-
- name = e_destination_get_textrep (recipients[i], FALSE);
-
- g_string_append_printf (str, " %s\n", name);
- }
- }
-
- g_string_append (str, _("Send anyway?"));
- res = e_question ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again, "%s", str->str);
- g_string_free (str, TRUE);
-
- gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", show_again, NULL);
-
- return res;
-}
-
-static gboolean
-ask_confirm_for_empty_subject (EMsgComposer *composer)
-{
- gboolean show_again, res;
- GConfClient *gconf;
-
- gconf = mail_config_get_gconf_client ();
-
- if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", NULL))
- return TRUE;
-
- res = e_question ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again,
- _("This message has no subject.\nReally send?"));
-
- gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", show_again, NULL);
-
- return res;
-}
-
-static gboolean
-ask_confirm_for_only_bcc (EMsgComposer *composer, gboolean hidden_list_case)
-{
- gboolean show_again, res;
- const char *first_text;
- GConfClient *gconf;
-
- gconf = mail_config_get_gconf_client ();
-
- if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", NULL))
- return TRUE;
-
- /* If the user is mailing a hidden contact list, it is possible for
- them to create a message with only Bcc recipients without really
- realizing it. To try to avoid being totally confusing, I've changed
- this dialog to provide slightly different text in that case, to
- better explain what the hell is going on. */
-
- if (hidden_list_case) {
- first_text = _("Since the contact list you are sending to "
- "is configured to hide the list's addresses, "
- "this message will contain only Bcc recipients.");
- } else {
- first_text = _("This message contains only Bcc recipients.");
- }
-
- res = e_question ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again,
- "%s\n%s", first_text,
- _("It is possible that the mail server may reveal the recipients "
- "by adding an Apparently-To header.\nSend anyway?"));
-
- gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", show_again, NULL);
-
- return res;
-}
-
-
-struct _send_data {
- struct _composer_callback_data *ccd;
- EMsgComposer *composer;
- gboolean send;
-};
-
-static void
-composer_send_queued_cb (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info,
- int queued, const char *appended_uid, void *data)
-{
- struct _composer_callback_data *ccd;
- struct _send_data *send = data;
-
- ccd = send->ccd;
-
- if (queued) {
- if (ccd && ccd->drafts_folder) {
- /* delete the old draft message */
- camel_folder_set_message_flags (ccd->drafts_folder, ccd->drafts_uid,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
- camel_object_unref (ccd->drafts_folder);
- ccd->drafts_folder = NULL;
- g_free (ccd->drafts_uid);
- ccd->drafts_uid = NULL;
- }
-
- if (ccd && ccd->folder) {
- /* set any replied flags etc */
- camel_folder_set_message_flags (ccd->folder, ccd->uid, ccd->flags, ccd->set);
- camel_object_unref (ccd->folder);
- ccd->folder = NULL;
- g_free (ccd->uid);
- ccd->uid = NULL;
- }
-
- gtk_widget_destroy (GTK_WIDGET (send->composer));
-
- if (send->send && camel_session_is_online (session)) {
- /* queue a message send */
- mail_send ();
- }
- } else {
- if (!ccd) {
- ccd = ccd_new ();
-
- /* disconnect the previous signal handlers */
- g_signal_handlers_disconnect_matched(send->composer, G_SIGNAL_MATCH_FUNC, 0,
- 0, NULL, composer_send_cb, NULL);
- g_signal_handlers_disconnect_matched(send->composer, G_SIGNAL_MATCH_FUNC, 0,
- 0, NULL, composer_save_draft_cb, NULL);
-
- /* reconnect to the signals using a non-NULL ccd for the callback data */
- g_signal_connect (send->composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (send->composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) send->composer, (GWeakNotify) composer_destroy_cb, ccd);
- }
-
- e_msg_composer_set_enable_autosave (send->composer, TRUE);
- gtk_widget_show (GTK_WIDGET (send->composer));
- }
-
- camel_message_info_free (info);
-
- if (send->ccd)
- ccd_unref (send->ccd);
-
- g_object_unref(send->composer);
- g_free (send);
-}
-
-static CamelMimeMessage *
-composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_object_data)
-{
- CamelMimeMessage *message = NULL;
- EDestination **recipients, **recipients_bcc;
- gboolean send_html, confirm_html;
- CamelInternetAddress *cia;
- int hidden = 0, shown = 0;
- int num = 0, num_bcc = 0;
- const char *subject;
- GConfClient *gconf;
- EAccount *account;
- int i;
-
- gconf = mail_config_get_gconf_client ();
-
- /* We should do all of the validity checks based on the composer, and not on
- the created message, as extra interaction may occur when we get the message
- (e.g. to get a passphrase to sign a message) */
-
- /* get the message recipients */
- recipients = e_msg_composer_get_recipients (composer);
-
- cia = camel_internet_address_new ();
-
- /* see which ones are visible/present, etc */
- if (recipients) {
- for (i = 0; recipients[i] != NULL; i++) {
- const char *addr = e_destination_get_address (recipients[i]);
-
- if (addr && addr[0]) {
- camel_address_decode ((CamelAddress *) cia, addr);
- if (camel_address_length ((CamelAddress *) cia) > 0) {
- camel_address_remove ((CamelAddress *) cia, -1);
- num++;
- if (e_destination_is_evolution_list (recipients[i])
- && !e_destination_list_show_addresses (recipients[i])) {
- hidden++;
- } else {
- shown++;
- }
- }
- }
- }
- }
-
- recipients_bcc = e_msg_composer_get_bcc (composer);
- if (recipients_bcc) {
- for (i = 0; recipients_bcc[i] != NULL; i++) {
- const char *addr = e_destination_get_address (recipients_bcc[i]);
-
- if (addr && addr[0]) {
- camel_address_decode ((CamelAddress *) cia, addr);
- if (camel_address_length ((CamelAddress *) cia) > 0) {
- camel_address_remove ((CamelAddress *) cia, -1);
- num_bcc++;
- }
- }
- }
-
- e_destination_freev (recipients_bcc);
- }
-
- camel_object_unref (cia);
-
- /* I'm sensing a lack of love, er, I mean recipients. */
- if (num == 0 && !post) {
- e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING,
- _("You must specify recipients in order to send this message."));
- goto finished;
- }
-
- if (num > 0 && (num == num_bcc || shown == 0)) {
- /* this means that the only recipients are Bcc's */
- if (!ask_confirm_for_only_bcc (composer, shown == 0))
- goto finished;
- }
-
- send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL);
- confirm_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL);
-
- /* Only show this warning if our default is to send html. If it isn't, we've
- manually switched into html mode in the composer and (presumably) had a good
- reason for doing this. */
- if (e_msg_composer_get_send_html (composer) && send_html && confirm_html) {
- gboolean html_problem = FALSE;
-
- if (recipients) {
- for (i = 0; recipients[i] != NULL && !html_problem; i++) {
- if (!e_destination_get_html_mail_pref (recipients[i]))
- html_problem = TRUE;
- }
- }
-
- if (html_problem) {
- html_problem = !ask_confirm_for_unwanted_html_mail (composer, recipients);
- if (html_problem)
- goto finished;
- }
- }
-
- /* Check for no subject */
- subject = e_msg_composer_get_subject (composer);
- if (subject == NULL || subject[0] == '\0') {
- if (!ask_confirm_for_empty_subject (composer))
- goto finished;
- }
-
- /* actually get the message now, this will sign/encrypt etc */
- message = e_msg_composer_get_message (composer, save_html_object_data);
- if (message == NULL)
- goto finished;
-
- /* Add info about the sending account */
- account = e_msg_composer_get_preferred_account (composer);
-
- if (account) {
- camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Account", account->name);
- camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Transport", account->transport->url);
- camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Fcc", account->sent_folder_uri);
- if (account->id->organization && *account->id->organization)
- camel_medium_set_header (CAMEL_MEDIUM (message), "Organization", account->id->organization);
- }
-
- /* Get the message recipients and 'touch' them, boosting their use scores */
- if (recipients)
- e_destination_touchv (recipients);
-
- finished:
-
- if (recipients)
- e_destination_freev (recipients);
-
- return message;
-}
-
-static void
-got_post_folder (char *uri, CamelFolder *folder, void *data)
-{
- CamelFolder **fp = data;
-
- *fp = folder;
-
- if (folder)
- camel_object_ref (folder);
-}
-
-void
-composer_send_cb (EMsgComposer *composer, gpointer user_data)
-{
- extern CamelFolder *outbox_folder;
- CamelMimeMessage *message;
- CamelMessageInfo *info;
- struct _send_data *send;
- gboolean post = FALSE;
- CamelFolder *folder;
- XEvolution *xev;
- char *url;
-
- url = e_msg_composer_hdrs_get_post_to ((EMsgComposerHdrs *) composer->hdrs);
- if (url && *url) {
- post = TRUE;
-
- mail_msg_wait (mail_get_folder (url, 0, got_post_folder, &folder, mail_thread_new));
-
- if (!folder) {
- g_free (url);
- return;
- }
- } else {
- folder = outbox_folder;
- camel_object_ref (folder);
- }
-
- g_free (url);
-
- message = composer_get_message (composer, post, FALSE);
- if (!message)
- return;
-
- if (post) {
- /* Remove the X-Evolution* headers if we are in Post-To mode */
- xev = mail_tool_remove_xevolution_headers (message);
- mail_tool_destroy_xevolution (xev);
- }
-
- info = camel_message_info_new ();
- info->flags = CAMEL_MESSAGE_SEEN;
-
- send = g_malloc (sizeof (*send));
- send->ccd = user_data;
- if (send->ccd)
- ccd_ref (send->ccd);
- send->send = !post;
- send->composer = composer;
- g_object_ref (composer);
- gtk_widget_hide (GTK_WIDGET (composer));
-
- e_msg_composer_set_enable_autosave (composer, FALSE);
-
- mail_append_mail (folder, message, info, composer_send_queued_cb, send);
- camel_object_unref (message);
- camel_object_unref (folder);
-}
-
-struct _save_draft_info {
- struct _composer_callback_data *ccd;
- EMsgComposer *composer;
- int quit;
-};
-
-static void
-save_draft_done (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, int ok,
- const char *appended_uid, void *user_data)
-{
- struct _save_draft_info *sdi = user_data;
- struct _composer_callback_data *ccd;
- CORBA_Environment ev;
-
- if (!ok)
- goto done;
- CORBA_exception_init (&ev);
- GNOME_GtkHTML_Editor_Engine_runCommand (sdi->composer->editor_engine, "saved", &ev);
- CORBA_exception_free (&ev);
-
- if ((ccd = sdi->ccd) == NULL) {
- ccd = ccd_new ();
-
- /* disconnect the previous signal handlers */
- g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (composer_send_cb), NULL);
- g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (composer_save_draft_cb), NULL);
-
- /* reconnect to the signals using a non-NULL ccd for the callback data */
- g_signal_connect (sdi->composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (sdi->composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) sdi->composer, (GWeakNotify) composer_destroy_cb, ccd);
- }
-
- if (ccd->drafts_folder) {
- /* delete the original draft message */
- camel_folder_set_message_flags (ccd->drafts_folder, ccd->drafts_uid,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
- camel_object_unref (ccd->drafts_folder);
- ccd->drafts_folder = NULL;
- g_free (ccd->drafts_uid);
- ccd->drafts_uid = NULL;
- }
-
- if (ccd->folder) {
- /* set the replied flags etc */
- camel_folder_set_message_flags (ccd->folder, ccd->uid, ccd->flags, ccd->set);
- camel_object_unref (ccd->folder);
- ccd->folder = NULL;
- g_free (ccd->uid);
- ccd->uid = NULL;
- }
-
- if (appended_uid) {
- camel_object_ref (folder);
- ccd->drafts_folder = folder;
- ccd->drafts_uid = g_strdup (appended_uid);
- }
-
- if (sdi->quit)
- gtk_widget_destroy (GTK_WIDGET (sdi->composer));
-
- done:
- g_object_unref (sdi->composer);
- if (sdi->ccd)
- ccd_unref (sdi->ccd);
- g_free (info);
- g_free (sdi);
-}
-
-static void
-use_default_drafts_cb (int reply, gpointer data)
-{
- extern CamelFolder *drafts_folder;
- CamelFolder **folder = data;
-
- if (reply == 0) {
- *folder = drafts_folder;
- camel_object_ref (drafts_folder);
- }
-}
-
-static void
-save_draft_folder (char *uri, CamelFolder *folder, gpointer data)
-{
- CamelFolder **save = data;
-
- if (folder) {
- *save = folder;
- camel_object_ref (folder);
- }
-}
-
-void
-composer_save_draft_cb (EMsgComposer *composer, int quit, gpointer user_data)
-{
- extern char *default_drafts_folder_uri;
- extern CamelFolder *drafts_folder;
- struct _save_draft_info *sdi;
- CamelFolder *folder = NULL;
- CamelMimeMessage *msg;
- CamelMessageInfo *info;
- EAccount *account;
-
- account = e_msg_composer_get_preferred_account (composer);
- if (account && account->drafts_folder_uri &&
- strcmp (account->drafts_folder_uri, default_drafts_folder_uri) != 0) {
- int id;
-
- id = mail_get_folder (account->drafts_folder_uri, 0, save_draft_folder, &folder, mail_thread_new);
- mail_msg_wait (id);
-
- if (!folder) {
- if (!e_question ((GtkWindow *) composer, GTK_RESPONSE_YES, NULL,
- _("Unable to open the drafts folder for this account.\n"
- "Would you like to use the default drafts folder?")))
- return;
-
- folder = drafts_folder;
- camel_object_ref (drafts_folder);
- }
- } else {
- folder = drafts_folder;
- camel_object_ref (folder);
- }
-
- msg = e_msg_composer_get_message_draft (composer);
-
- info = g_new0 (CamelMessageInfo, 1);
- info->flags = CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN;
-
- sdi = g_malloc (sizeof (struct _save_draft_info));
- sdi->composer = composer;
- g_object_ref (composer);
- sdi->ccd = user_data;
- if (sdi->ccd)
- ccd_ref (sdi->ccd);
- sdi->quit = quit;
-
- mail_append_mail (folder, msg, info, save_draft_done, sdi);
- camel_object_unref (folder);
- camel_object_unref (msg);
-}
-
-static GtkWidget *
-create_msg_composer (EAccount *account, gboolean post, const char *url)
-{
- EMsgComposer *composer;
- GConfClient *gconf;
- gboolean send_html;
-
- /* Make sure that we've actually been passed in an account. If one has
- * not been passed in, grab the default account.
- */
- if (account == NULL)
- account = mail_config_get_default_account ();
-
- gconf = mail_config_get_gconf_client ();
- send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL);
-
- if (post)
- composer = e_msg_composer_new_post ();
- else if (url)
- composer = e_msg_composer_new_from_url (url);
- else
- composer = e_msg_composer_new ();
-
- if (composer) {
- e_msg_composer_hdrs_set_from_account (E_MSG_COMPOSER_HDRS (composer->hdrs), account->name);
- e_msg_composer_set_send_html (composer, send_html);
- e_msg_composer_unset_changed (composer);
- e_msg_composer_drop_editor_undo (composer);
- return GTK_WIDGET (composer);
- } else
- return NULL;
-}
-
-void
-compose_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- struct _composer_callback_data *ccd;
- GtkWidget *composer;
- EAccount *account;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- /* Figure out which account we want to initially compose from */
- account = mail_config_get_account_by_source_url (fb->uri);
-
- composer = create_msg_composer (account, FALSE, NULL);
- if (!composer)
- return;
-
- ccd = ccd_new ();
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
-
- gtk_widget_show (composer);
-}
-
-/* Send according to a mailto (RFC 2368) URL. */
-void
-send_to_url (const char *url, const char *parent_uri)
-{
- struct _composer_callback_data *ccd;
- GtkWidget *composer;
- EAccount *account = NULL;
-
- /* FIXME: no way to get folder browser? Not without
- * big pain in the ass, as far as I can tell */
- if (!check_send_configuration (NULL))
- return;
-
- if (parent_uri)
- account = mail_config_get_account_by_source_url (parent_uri);
-
- composer = create_msg_composer (account, FALSE, url);
-
- if (!composer)
- return;
-
- ccd = ccd_new ();
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
-
- gtk_widget_show (composer);
-}
-
-static GList *
-list_add_addresses (GList *list, const CamelInternetAddress *cia, GHashTable *account_hash,
- GHashTable *rcpt_hash, EAccount **me)
-{
- const char *name, *addr;
- EAccount *account;
- int i;
-
- for (i = 0; camel_internet_address_get (cia, i, &name, &addr); i++) {
- /* Here I'll check to see if the cc:'d address is the address
- of the sender, and if so, don't add it to the cc: list; this
- is to fix Bugzilla bug #455. */
- account = g_hash_table_lookup (account_hash, addr);
- if (account && me && !*me)
- *me = account;
-
- if (!account && !g_hash_table_lookup (rcpt_hash, addr)) {
- EDestination *dest;
-
- dest = e_destination_new ();
- e_destination_set_name (dest, name);
- e_destination_set_email (dest, addr);
-
- list = g_list_append (list, dest);
- g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1));
- }
- }
-
- return list;
-}
-
-static EAccount *
-guess_me (const CamelInternetAddress *to, const CamelInternetAddress *cc, GHashTable *account_hash)
-{
- EAccount *account = NULL;
- const char *addr;
- int i;
-
- /* "optimization" */
- if (!to && !cc)
- return NULL;
-
- if (to) {
- for (i = 0; camel_internet_address_get (to, i, NULL, &addr); i++) {
- account = g_hash_table_lookup (account_hash, addr);
- if (account)
- goto found;
- }
- }
-
- if (cc) {
- for (i = 0; camel_internet_address_get (cc, i, NULL, &addr); i++) {
- account = g_hash_table_lookup (account_hash, addr);
- if (account)
- goto found;
- }
- }
-
- found:
-
- return account;
-}
-
-static EAccount *
-guess_me_from_accounts (const CamelInternetAddress *to, const CamelInternetAddress *cc, EAccountList *accounts)
-{
- EAccount *account, *def;
- GHashTable *account_hash;
- EIterator *iter;
-
- account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
-
- /* add the default account to the hash first */
- if ((def = mail_config_get_default_account ())) {
- if (def->id->address)
- g_hash_table_insert (account_hash, (char *) def->id->address, (void *) def);
- }
-
- iter = e_list_get_iterator ((EList *) accounts);
- while (e_iterator_is_valid (iter)) {
- account = (EAccount *) e_iterator_get (iter);
-
- if (account->id->address) {
- EAccount *acnt;
-
- /* Accounts with identical email addresses that are enabled
- * take precedence over the accounts that aren't. If all
- * accounts with matching email addresses are disabled, then
- * the first one in the list takes precedence. The default
- * account always takes precedence no matter what.
- */
- acnt = g_hash_table_lookup (account_hash, account->id->address);
- if (acnt && acnt != def && !acnt->enabled && account->enabled) {
- g_hash_table_remove (account_hash, acnt->id->address);
- acnt = NULL;
- }
-
- if (!acnt)
- g_hash_table_insert (account_hash, (char *) account->id->address, (void *) account);
- }
-
- e_iterator_next (iter);
- }
-
- g_object_unref (iter);
-
- account = guess_me (to, cc, account_hash);
-
- g_hash_table_destroy (account_hash);
-
- return account;
-}
-
-inline static void
-mail_ignore (EMsgComposer *composer, const char *name, const char *address)
-{
- e_msg_composer_ignore (composer, name && *name ? name : address);
-}
-
-static void
-mail_ignore_address (EMsgComposer *composer, const CamelInternetAddress *addr)
-{
- const char *name, *address;
- int i, max;
-
- max = camel_address_length (CAMEL_ADDRESS (addr));
- for (i = 0; i < max; i++) {
- camel_internet_address_get (addr, i, &name, &address);
- mail_ignore (composer, name, address);
- }
-}
-
-#define MAX_SUBJECT_LEN 1024
-
-static EMsgComposer *
-mail_generate_reply (CamelFolder *folder, CamelMimeMessage *message, const char *uid, int mode)
-{
- const CamelInternetAddress *reply_to, *sender, *to_addrs, *cc_addrs;
- const char *name = NULL, *address = NULL, *source = NULL;
- const char *message_id, *references, *mlist = NULL;
- char *text = NULL, *subject, format[256];
- EAccount *def, *account, *me = NULL;
- EAccountList *accounts = NULL;
- GHashTable *account_hash = NULL;
- CamelMessageInfo *info = NULL;
- GList *to = NULL, *cc = NULL;
- EDestination **tov, **ccv;
- EMsgComposer *composer;
- CamelMimePart *part;
- GConfClient *gconf;
- EIterator *iter;
- time_t date;
- int date_ofs;
- char *url;
-
- gconf = mail_config_get_gconf_client ();
-
- if (mode == REPLY_POST) {
- composer = e_msg_composer_new_post ();
- if (composer != NULL) {
- url = mail_tools_folder_to_url (folder);
- e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) composer->hdrs, url);
- g_free (url);
- }
- } else
- composer = e_msg_composer_new ();
-
- if (!composer)
- return NULL;
-
- e_msg_composer_add_message_attachments (composer, message, TRUE);
-
- /* Set the recipients */
- accounts = mail_config_get_accounts ();
- account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
-
- /* add the default account to the hash first */
- if ((def = mail_config_get_default_account ())) {
- if (def->id->address)
- g_hash_table_insert (account_hash, (char *) def->id->address, (void *) def);
- }
-
- iter = e_list_get_iterator ((EList *) accounts);
- while (e_iterator_is_valid (iter)) {
- account = (EAccount *) e_iterator_get (iter);
-
- if (account->id->address) {
- EAccount *acnt;
-
- /* Accounts with identical email addresses that are enabled
- * take precedence over the accounts that aren't. If all
- * accounts with matching email addresses are disabled, then
- * the first one in the list takes precedence. The default
- * account always takes precedence no matter what.
- */
- acnt = g_hash_table_lookup (account_hash, account->id->address);
- if (acnt && acnt != def && !acnt->enabled && account->enabled) {
- g_hash_table_remove (account_hash, acnt->id->address);
- acnt = NULL;
- }
-
- if (!acnt)
- g_hash_table_insert (account_hash, (char *) account->id->address, (void *) account);
- }
-
- e_iterator_next (iter);
- }
-
- g_object_unref (iter);
-
- to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
- cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
- mail_ignore_address (composer, to_addrs);
- mail_ignore_address (composer, cc_addrs);
-
- if (mode == REPLY_LIST) {
- /* make sure we can reply to an mlist */
- info = camel_folder_get_message_info (folder, uid);
- if (!(mlist = camel_message_info_mlist (info)) || *mlist == '\0') {
- camel_folder_free_message_info (folder, info);
- mode = REPLY_ALL;
- info = NULL;
- }
- }
-
- determine_recipients:
- if (mode == REPLY_LIST) {
- EDestination *dest;
- int i, max;
-
- /* look through the recipients to find the *real* mailing list address */
- d(printf ("we are looking for the mailing list called: %s\n", mlist));
-
- max = camel_address_length (CAMEL_ADDRESS (to_addrs));
- for (i = 0; i < max; i++) {
- camel_internet_address_get (to_addrs, i, &name, &address);
- if (!strcasecmp (address, mlist))
- break;
- }
-
- if (i == max) {
- max = camel_address_length (CAMEL_ADDRESS (cc_addrs));
- for (i = 0; i < max; i++) {
- camel_internet_address_get (cc_addrs, i, &name, &address);
- if (!strcasecmp (address, mlist))
- break;
- }
- }
-
- if (address && i != max) {
- dest = e_destination_new ();
- e_destination_set_name (dest, name);
- e_destination_set_email (dest, address);
-
- to = g_list_append (to, dest);
- } else {
- /* mailing list address wasn't found */
- if (strchr (mlist, '@')) {
- /* mlist string has an @, maybe it's valid? */
- dest = e_destination_new ();
- e_destination_set_email (dest, mlist);
-
- to = g_list_append (to, dest);
- } else {
- /* give up and just reply to all recipients? */
- mode = REPLY_ALL;
- camel_folder_free_message_info (folder, info);
- goto determine_recipients;
- }
- }
-
- me = guess_me (to_addrs, cc_addrs, account_hash);
- camel_folder_free_message_info (folder, info);
- } else {
- GHashTable *rcpt_hash;
- EDestination *dest;
-
- rcpt_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
-
- reply_to = camel_mime_message_get_reply_to (message);
- if (!reply_to)
- reply_to = camel_mime_message_get_from (message);
-
- if (reply_to) {
- int i;
-
- for (i = 0; camel_internet_address_get (reply_to, i, &name, &address); i++) {
- /* ignore references to the Reply-To address in the To and Cc lists */
- if (address && !(mode == REPLY_ALL && g_hash_table_lookup (account_hash, address))) {
- /* In the case that we are doing a Reply-To-All, we do not want
- to include the user's email address because replying to oneself
- is kinda silly. */
- dest = e_destination_new ();
- e_destination_set_name (dest, name);
- e_destination_set_email (dest, address);
- to = g_list_append (to, dest);
- g_hash_table_insert (rcpt_hash, (char *) address, GINT_TO_POINTER (1));
- mail_ignore (composer, name, address);
- }
- }
- }
-
- if (mode == REPLY_ALL) {
- cc = list_add_addresses (cc, to_addrs, account_hash, rcpt_hash, me ? NULL : &me);
- cc = list_add_addresses (cc, cc_addrs, account_hash, rcpt_hash, me ? NULL : &me);
-
- /* promote the first Cc: address to To: if To: is empty */
- if (to == NULL && cc != NULL) {
- to = cc;
- cc = g_list_remove_link (cc, to);
- }
- } else {
- me = guess_me (to_addrs, cc_addrs, account_hash);
- }
-
- g_hash_table_destroy (rcpt_hash);
- }
-
- g_hash_table_destroy (account_hash);
-
- if (!me) {
- /* default 'me' to the source account... */
- if ((source = camel_mime_message_get_source (message)))
- me = mail_config_get_account_by_source_url (source);
- }
-
- /* set body text here as we want all ignored words to take effect */
- switch (gconf_client_get_int (gconf, "/apps/evolution/mail/format/reply_style", NULL)) {
- case MAIL_CONFIG_REPLY_DO_NOT_QUOTE:
- /* do nothing */
- break;
- case MAIL_CONFIG_REPLY_ATTACH:
- /* attach the original message as an attachment */
- part = mail_tool_make_message_attachment (message);
- e_msg_composer_attach (composer, part);
- camel_object_unref (part);
- break;
- case MAIL_CONFIG_REPLY_QUOTED:
- default:
- /* do what any sane user would want when replying... */
- sender = camel_mime_message_get_from (message);
- if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) {
- camel_internet_address_get (sender, 0, &name, &address);
- } else {
- name = _("an unknown sender");
- }
-
- date = camel_mime_message_get_date (message, &date_ofs);
- /* Convert to UTC */
- date += (date_ofs / 100) * 60 * 60;
- date += (date_ofs % 100) * 60;
-
- /* translators: attribution string used when quoting messages */
- e_utf8_strftime (format, sizeof (format), _("On %a, %Y-%m-%d at %H:%M %%+05d, %%s wrote:"), gmtime (&date));
- text = mail_tool_quote_message (message, format, date_ofs, name && *name ? name : address);
- mail_ignore (composer, name, address);
- if (text) {
- e_msg_composer_set_body_text (composer, text);
- g_free (text);
- }
- break;
- }
-
- /* Set the subject of the new message. */
- subject = (char *) camel_mime_message_get_subject (message);
- if (!subject)
- subject = g_strdup ("");
- else {
- if (!strncasecmp (subject, "Re: ", 4))
- subject = g_strndup (subject, MAX_SUBJECT_LEN);
- else {
- if (strlen (subject) < MAX_SUBJECT_LEN) {
- subject = g_strdup_printf ("Re: %s", subject);
- } else {
- /* We can't use %.*s because it depends on the locale being C/POSIX
- or UTF-8 to work correctly in glibc */
- char *sub;
-
- /*subject = g_strdup_printf ("Re: %.*s...", MAX_SUBJECT_LEN, subject);*/
- sub = g_malloc (MAX_SUBJECT_LEN + 8);
- memcpy (sub, "Re: ", 4);
- memcpy (sub + 4, subject, MAX_SUBJECT_LEN);
- memcpy (sub + 4 + MAX_SUBJECT_LEN, "...", 4);
- subject = sub;
- }
- }
- }
-
- tov = e_destination_list_to_vector (to);
- ccv = e_destination_list_to_vector (cc);
-
- g_list_free (to);
- g_list_free (cc);
-
- e_msg_composer_set_headers (composer, me ? me->name : NULL, tov, ccv, NULL, subject);
-
- e_destination_freev (tov);
- e_destination_freev (ccv);
-
- g_free (subject);
-
- /* Add In-Reply-To and References. */
- message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id");
- references = camel_medium_get_header (CAMEL_MEDIUM (message), "References");
- if (message_id) {
- char *reply_refs;
-
- e_msg_composer_add_header (composer, "In-Reply-To", message_id);
-
- if (references)
- reply_refs = g_strdup_printf ("%s %s", references, message_id);
- else
- reply_refs = g_strdup (message_id);
-
- e_msg_composer_add_header (composer, "References", reply_refs);
- g_free (reply_refs);
- } else if (references) {
- e_msg_composer_add_header (composer, "References", references);
- }
-
- e_msg_composer_drop_editor_undo (composer);
-
- return composer;
-}
-
-static void
-requeue_mail_reply (CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *data)
-{
- int mode = GPOINTER_TO_INT (data);
-
- if (msg != NULL)
- mail_reply (folder, msg, uid, mode);
-}
-
-void
-mail_reply (CamelFolder *folder, CamelMimeMessage *msg, const char *uid, int mode)
-{
- struct _composer_callback_data *ccd;
- EMsgComposer *composer;
-
- g_return_if_fail (CAMEL_IS_FOLDER (folder));
- g_return_if_fail (uid != NULL);
-
- if (!msg) {
- mail_get_message (folder, uid, requeue_mail_reply,
- GINT_TO_POINTER (mode), mail_thread_new);
- return;
- }
-
- composer = mail_generate_reply (folder, msg, uid, mode);
- if (!composer)
- return;
-
- ccd = ccd_new ();
-
- camel_object_ref (folder);
- ccd->folder = folder;
- ccd->uid = g_strdup (uid);
- ccd->flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
- if (mode == REPLY_LIST || mode == REPLY_ALL)
- ccd->flags |= CAMEL_MESSAGE_ANSWERED_ALL;
- ccd->set = ccd->flags;
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
-
- gtk_widget_show (GTK_WIDGET (composer));
- e_msg_composer_unset_changed (composer);
-}
-
-void
-reply_to_sender (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- mail_reply (fb->folder, NULL, fb->message_list->cursor_uid, REPLY_SENDER);
-}
-
-void
-reply_to_list (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- mail_reply (fb->folder, NULL, fb->message_list->cursor_uid, REPLY_LIST);
-}
-
-void
-reply_to_all (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- mail_reply(fb->folder, NULL, fb->message_list->cursor_uid, REPLY_ALL);
-}
-
-void
-enumerate_msg (MessageList *ml, const char *uid, gpointer data)
-{
- g_return_if_fail (ml != NULL);
-
- g_ptr_array_add ((GPtrArray *) data, g_strdup (uid));
-}
-
-
-static EMsgComposer *
-forward_get_composer (CamelMimeMessage *message, const char *subject)
-{
- struct _composer_callback_data *ccd;
- EAccount *account = NULL;
- EMsgComposer *composer;
-
- if (message) {
- const CamelInternetAddress *to_addrs, *cc_addrs;
- EAccountList *accounts;
-
- accounts = mail_config_get_accounts ();
- to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
- cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
-
- account = guess_me_from_accounts (to_addrs, cc_addrs, accounts);
-
- if (!account) {
- const char *source;
-
- source = camel_mime_message_get_source (message);
- account = mail_config_get_account_by_source_url (source);
- }
- }
-
- if (!account)
- account = mail_config_get_default_account ();
-
- composer = e_msg_composer_new ();
- if (composer) {
- ccd = ccd_new ();
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
-
- e_msg_composer_set_headers (composer, account->name, NULL, NULL, NULL, subject);
- } else {
- g_warning ("Could not create composer");
- }
-
- return composer;
-}
-
-static void
-do_forward_non_attached (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *data)
-{
- MailConfigForwardStyle style = GPOINTER_TO_INT (data);
- CamelMimeMessage *message;
- char *subject, *text;
- int i;
-
- if (messages->len == 0)
- return;
-
- for (i = 0; i < messages->len; i++) {
- message = messages->pdata[i];
- subject = mail_tool_generate_forward_subject (message);
- text = mail_tool_forward_message (message, style == MAIL_CONFIG_FORWARD_QUOTED);
-
- if (text) {
- EMsgComposer *composer = forward_get_composer (message, subject);
- if (composer) {
- CamelDataWrapper *wrapper;
-
- e_msg_composer_set_body_text (composer, text);
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message));
- if (CAMEL_IS_MULTIPART (wrapper))
- e_msg_composer_add_message_attachments (composer, message, FALSE);
-
- gtk_widget_show (GTK_WIDGET (composer));
- e_msg_composer_unset_changed (composer);
- e_msg_composer_drop_editor_undo (composer);
- }
- g_free (text);
- }
-
- g_free (subject);
- }
-}
-
-static void
-forward_message (FolderBrowser *fb, MailConfigForwardStyle style)
-{
- GPtrArray *uids;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (!check_send_configuration (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- mail_get_messages (fb->folder, uids, do_forward_non_attached, GINT_TO_POINTER (style));
-}
-
-void
-forward_inline (GtkWidget *widget, gpointer user_data)
-{
- forward_message (user_data, MAIL_CONFIG_FORWARD_INLINE);
-}
-
-void
-forward_quoted (GtkWidget *widget, gpointer user_data)
-{
- forward_message (user_data, MAIL_CONFIG_FORWARD_QUOTED);
-}
-
-static void
-do_forward_attach (CamelFolder *folder, GPtrArray *messages, CamelMimePart *part, char *subject, void *data)
-{
- CamelMimeMessage *message = data;
-
- if (part) {
- EMsgComposer *composer = forward_get_composer (message, subject);
- if (composer) {
- e_msg_composer_attach (composer, part);
- gtk_widget_show (GTK_WIDGET (composer));
- e_msg_composer_unset_changed (composer);
- e_msg_composer_drop_editor_undo (composer);
- }
- }
-}
-
-void
-forward_attached (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = (FolderBrowser *) user_data;
- GPtrArray *uids;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
- mail_build_attachment (fb->folder, uids, do_forward_attach,
- uids->len == 1 ? fb->mail_display->current_message : NULL);
-}
-
-void
-forward (GtkWidget *widget, gpointer user_data)
-{
- MailConfigForwardStyle style;
- GConfClient *gconf;
-
- gconf = mail_config_get_gconf_client ();
- style = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL);
-
- if (style == MAIL_CONFIG_FORWARD_ATTACHED)
- forward_attached (widget, user_data);
- else
- forward_message (user_data, style);
-}
-
-
-void
-post_to_url (const char *url)
-{
- struct _composer_callback_data *ccd;
- GtkWidget *composer;
- EAccount *account = NULL;
-
- /* FIXME: no way to get folder browser? Not without
- * big pain in the ass, as far as I can tell */
- if (!check_send_configuration (NULL))
- return;
-
- if (url)
- account = mail_config_get_account_by_source_url (url);
-
- composer = create_msg_composer (account, TRUE, NULL);
- if (!composer)
- return;
-
- e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url);
-
- ccd = ccd_new ();
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
-
- gtk_widget_show (composer);
-}
-
-void
-post_message (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- char *url;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- url = mail_tools_folder_to_url (fb->folder);
- post_to_url (url);
- g_free (url);
-}
-
-void
-post_reply (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb) || !check_send_configuration (fb))
- return;
-
- mail_reply (fb->folder, NULL, fb->message_list->cursor_uid, REPLY_POST);
-}
-
-
-static EMsgComposer *
-redirect_get_composer (CamelMimeMessage *message)
-{
- const CamelInternetAddress *to_addrs, *cc_addrs;
- struct _composer_callback_data *ccd;
- EAccountList *accounts = NULL;
- EAccount *account = NULL;
- EMsgComposer *composer;
-
- g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
-
- accounts = mail_config_get_accounts ();
- to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
- cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
-
- account = guess_me_from_accounts (to_addrs, cc_addrs, accounts);
-
- if (!account) {
- const char *source;
-
- source = camel_mime_message_get_source (message);
- account = mail_config_get_account_by_source_url (source);
- }
-
- if (!account)
- account = mail_config_get_default_account ();
-
- /* QMail will refuse to send a message if it finds one of
- it's Delivered-To headers in the message, so remove all
- Delivered-To headers. Fixes bug #23635. */
- while (camel_medium_get_header (CAMEL_MEDIUM (message), "Delivered-To"))
- camel_medium_remove_header (CAMEL_MEDIUM (message), "Delivered-To");
-
- composer = e_msg_composer_new_redirect (message, account->name);
- if (composer) {
- ccd = ccd_new ();
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
- } else {
- g_warning ("Could not create composer");
- }
-
- return composer;
-}
-
-static void
-do_redirect (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *data)
-{
- EMsgComposer *composer;
-
- if (!message)
- return;
-
- composer = redirect_get_composer (message);
- if (composer) {
- CamelDataWrapper *wrapper;
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message));
- if (CAMEL_IS_MULTIPART (wrapper))
- e_msg_composer_add_message_attachments (composer, message, FALSE);
-
- gtk_widget_show (GTK_WIDGET (composer));
- e_msg_composer_unset_changed (composer);
- e_msg_composer_drop_editor_undo (composer);
- }
-}
-
-void
-redirect (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = (FolderBrowser *) user_data;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (!check_send_configuration (fb))
- return;
-
- mail_get_message (fb->folder, fb->message_list->cursor_uid,
- do_redirect, NULL, mail_thread_new);
-}
-
-static void
-transfer_msg_done (gboolean ok, void *data)
-{
- FolderBrowser *fb = data;
- gboolean hide_deleted;
- GConfClient *gconf;
- int row;
-
- if (ok && !FOLDER_BROWSER_IS_DESTROYED (fb)) {
- gconf = mail_config_get_gconf_client ();
- hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL);
-
- row = e_tree_row_of_node (fb->message_list->tree,
- e_tree_get_cursor (fb->message_list->tree));
-
- /* If this is the last message and deleted messages
- are hidden, select the previous */
- if ((row + 1 == e_tree_row_count (fb->message_list->tree)) && hide_deleted)
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_PREVIOUS,
- 0, CAMEL_MESSAGE_DELETED, FALSE);
- else
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_NEXT,
- 0, 0, FALSE);
- }
-
- g_object_unref (fb);
-}
-
-static void
-transfer_msg (FolderBrowser *fb, gboolean delete_from_source)
-{
- static const char *allowed_types[] = { "mail/*", "vtrash", NULL };
- extern EvolutionShellClient *global_shell_client;
- GNOME_Evolution_Folder *folder;
- static char *last_uri = NULL;
- GPtrArray *uids;
- char *desc;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (last_uri == NULL)
- last_uri = g_strdup (fb->uri);
-
- if (delete_from_source)
- desc = _("Move message(s) to");
- else
- desc = _("Copy message(s) to");
-
- evolution_shell_client_user_select_folder (global_shell_client,
- GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (fb))),
- desc, last_uri, allowed_types,
- &folder);
- if (!folder)
- return;
-
- if (strcmp (last_uri, folder->evolutionUri) != 0) {
- g_free (last_uri);
- last_uri = g_strdup (folder->evolutionUri);
- }
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (delete_from_source) {
- g_object_ref (fb);
- mail_transfer_messages (fb->folder, uids, delete_from_source,
- folder->physicalUri, 0,
- transfer_msg_done, fb);
- } else {
- mail_transfer_messages (fb->folder, uids, delete_from_source,
- folder->physicalUri, 0, NULL, NULL);
- }
-
- CORBA_free (folder);
-}
-
-void
-move_msg_cb (GtkWidget *widget, gpointer user_data)
-{
- transfer_msg (user_data, TRUE);
-}
-
-void
-move_msg (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- transfer_msg (user_data, TRUE);
-}
-
-void
-copy_msg_cb (GtkWidget *widget, gpointer user_data)
-{
- transfer_msg (user_data, FALSE);
-}
-
-void
-copy_msg (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- transfer_msg (user_data, FALSE);
-}
-
-/* Copied from e-shell-view.c */
-static GtkWidget *
-find_socket (GtkContainer *container)
-{
- GList *children, *tmp;
-
- children = gtk_container_get_children (container);
- while (children) {
- if (BONOBO_IS_SOCKET (children->data))
- return children->data;
- else if (GTK_IS_CONTAINER (children->data)) {
- GtkWidget *socket = find_socket (children->data);
- if (socket)
- return socket;
- }
- tmp = children->next;
- g_list_free_1 (children);
- children = tmp;
- }
-
- return NULL;
-}
-
-static void
-popup_listener_cb (BonoboListener *listener,
- const char *event_name,
- const CORBA_any *any,
- CORBA_Environment *ev,
- gpointer user_data)
-{
- char *type = bonobo_event_subtype (event_name);
-
- if (!strcmp (type, "Destroy")) {
- gtk_widget_destroy (GTK_WIDGET (user_data));
- }
-
- g_free (type);
-}
-
-void
-addrbook_sender (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- const char *addr_str;
- CamelMessageInfo *info;
- GtkWidget *win;
- GtkWidget *control;
- GtkWidget *socket;
- GPtrArray *uids;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
- if (uids->len != 1)
- goto done;
-
- info = camel_folder_get_message_info (fb->folder, uids->pdata[0]);
- if (info == NULL || (addr_str = camel_message_info_from (info)) == NULL)
- goto done;
-
- win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_window_set_title (GTK_WINDOW (win), _("Sender"));
-
- control = bonobo_widget_new_control ("OAFIID:GNOME_Evolution_Addressbook_AddressPopup",
- CORBA_OBJECT_NIL);
- bonobo_widget_set_property (BONOBO_WIDGET (control),
- "email", TC_CORBA_string, addr_str,
- NULL);
-
- bonobo_event_source_client_add_listener (bonobo_widget_get_objref (BONOBO_WIDGET (control)),
- popup_listener_cb, NULL, NULL, win);
-
- socket = find_socket (GTK_CONTAINER (control));
-
- g_object_weak_ref ((GObject *) socket, (GWeakNotify) gtk_widget_destroy, win);
-
- gtk_container_add (GTK_CONTAINER (win), control);
- gtk_widget_show_all (win);
-
-done:
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
-}
-
-void
-add_sender_to_addrbook (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- addrbook_sender (NULL, user_data);
-}
-
-void
-apply_filters (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- mail_filter_on_demand (fb->folder, uids);
-}
-
-void
-select_all (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- ESelectionModel *etsm;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (GTK_WIDGET_HAS_FOCUS (fb->mail_display->html)) {
- gtk_html_select_all (fb->mail_display->html);
- } else {
- etsm = e_tree_get_selection_model (fb->message_list->tree);
-
- e_selection_model_select_all (etsm);
- }
-}
-
-/* Thread selection */
-
-typedef struct thread_select_info {
- MessageList *ml;
- GPtrArray *paths;
-} thread_select_info_t;
-
-static gboolean
-select_node (ETreeModel *tm, ETreePath path, gpointer user_data)
-{
- thread_select_info_t *tsi = (thread_select_info_t *) user_data;
-
- g_ptr_array_add (tsi->paths, path);
- return FALSE; /*not done yet*/
-}
-
-static void
-thread_select_foreach (ETreePath path, gpointer user_data)
-{
- thread_select_info_t *tsi = (thread_select_info_t *) user_data;
- ETreeModel *tm = tsi->ml->model;
- ETreePath node;
-
- /* @path part of the initial selection. If it has children,
- * we select them as well. If it doesn't, we select its siblings and
- * their children (ie, the current node must be inside the thread
- * that the user wants to mark.
- */
-
- if (e_tree_model_node_get_first_child (tm, path))
- node = path;
- else {
- node = e_tree_model_node_get_parent (tm, path);
-
- /* Let's make an exception: if no parent, then we're about
- * to mark the whole tree. No. */
- if (e_tree_model_node_is_root (tm, node))
- node = path;
- }
-
- e_tree_model_node_traverse (tm, node, select_node, tsi);
-}
-
-void
-select_thread (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- ETreeSelectionModel *selection_model;
- thread_select_info_t tsi;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- /* For every selected item, select the thread containing it.
- * We can't alter the selection while iterating through it,
- * so build up a list of paths.
- */
-
- tsi.ml = fb->message_list;
- tsi.paths = g_ptr_array_new ();
-
- e_tree_selected_path_foreach (fb->message_list->tree, thread_select_foreach, &tsi);
-
- selection_model = E_TREE_SELECTION_MODEL (e_tree_get_selection_model (fb->message_list->tree));
-
- for (i = 0; i < tsi.paths->len; i++)
- e_tree_selection_model_add_to_selection (selection_model,
- tsi.paths->pdata[i]);
- g_ptr_array_free (tsi.paths, TRUE);
-}
-
-void
-invert_selection (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- ESelectionModel *etsm;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- etsm = e_tree_get_selection_model (fb->message_list->tree);
-
- e_selection_model_invert_selection (etsm);
-}
-
-/* flag all selected messages. Return number flagged */
-static int
-flag_messages (FolderBrowser *fb, guint32 mask, guint32 set)
-{
- GPtrArray *uids;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return 0;
-
- /* could just use specific callback but i'm lazy */
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
- camel_folder_freeze (fb->folder);
- for (i = 0; i < uids->len; i++) {
- camel_folder_set_message_flags (fb->folder, uids->pdata[i], mask, set);
- g_free (uids->pdata[i]);
- }
-
- camel_folder_thaw (fb->folder);
-
- g_ptr_array_free (uids, TRUE);
-
- return i;
-}
-
-static int
-toggle_flags (FolderBrowser *fb, guint32 mask)
-{
- GPtrArray *uids;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return 0;
-
- /* could just use specific callback but i'm lazy */
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
- camel_folder_freeze (fb->folder);
- for (i = 0; i < uids->len; i++) {
- guint32 flags;
-
- flags = ~(camel_folder_get_message_flags (fb->folder, uids->pdata[i]));
-
- /* if we're flagging a message important, always undelete it too */
- if (mask & flags & CAMEL_MESSAGE_FLAGGED) {
- flags &= ~CAMEL_MESSAGE_DELETED;
- mask |= CAMEL_MESSAGE_DELETED;
- }
-
- /* if we're flagging a message deleted, mark it seen. If
- * we're undeleting it, we also want it to be seen, so always do this.
- */
- if (mask & CAMEL_MESSAGE_DELETED) {
- flags |= CAMEL_MESSAGE_SEEN;
- mask |= CAMEL_MESSAGE_SEEN;
- }
-
- camel_folder_set_message_flags (fb->folder, uids->pdata[i], mask, flags);
-
- g_free (uids->pdata[i]);
- }
- camel_folder_thaw (fb->folder);
-
- g_ptr_array_free (uids, TRUE);
-
- return i;
-}
-
-void
-mark_as_seen (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- flag_messages (FOLDER_BROWSER (user_data), CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
-}
-
-void
-mark_as_unseen (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- /* Remove the automatic mark-as-read timer first */
- if (fb->seen_id) {
- g_source_remove (fb->seen_id);
- fb->seen_id = 0;
- }
-
- flag_messages (fb, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED, 0);
-}
-
-void
-mark_all_as_seen (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = camel_folder_get_uids (fb->folder);
- camel_folder_freeze (fb->folder);
- for (i = 0; i < uids->len; i++)
- camel_folder_set_message_flags (fb->folder, uids->pdata[i], CAMEL_MESSAGE_SEEN, ~0);
- camel_folder_free_uids (fb->folder, uids);
- camel_folder_thaw (fb->folder);
-}
-
-void
-mark_as_important (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- flag_messages (FOLDER_BROWSER (user_data), CAMEL_MESSAGE_FLAGGED|CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_FLAGGED);
-}
-
-void
-mark_as_unimportant (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- flag_messages (FOLDER_BROWSER (user_data), CAMEL_MESSAGE_FLAGGED, 0);
-}
-
-void
-toggle_as_important (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- toggle_flags (FOLDER_BROWSER (user_data), CAMEL_MESSAGE_FLAGGED);
-}
-
-
-struct _tag_editor_data {
- MessageTagEditor *editor;
- FolderBrowser *fb;
- GPtrArray *uids;
-};
-
-static void
-tag_editor_response(GtkWidget *gd, int button, struct _tag_editor_data *data)
-{
- CamelFolder *folder;
- CamelTag *tags, *t;
- GPtrArray *uids;
- int i;
-
- /*if (FOLDER_BROWSER_IS_DESTROYED (data->fb))
- goto done;*/
-
- if (button == GTK_RESPONSE_OK
- && (tags = message_tag_editor_get_tag_list (data->editor))) {
- folder = data->fb->folder;
- uids = data->uids;
-
- camel_folder_freeze (folder);
- for (i = 0; i < uids->len; i++) {
- for (t = tags; t; t = t->next)
- camel_folder_set_message_user_tag (folder, uids->pdata[i], t->name, t->value);
- }
- camel_folder_thaw (folder);
- camel_tag_list_free (&tags);
- }
-
- gtk_widget_destroy(gd);
-
- g_object_unref (data->fb);
- g_ptr_array_free (data->uids, TRUE);
- g_free (data);
-}
-
-void
-flag_for_followup (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- struct _tag_editor_data *data;
- GtkWidget *editor;
- GPtrArray *uids;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- editor = (GtkWidget *) message_tag_followup_new ();
-
- data = g_new (struct _tag_editor_data, 1);
- data->editor = MESSAGE_TAG_EDITOR (editor);
- gtk_widget_ref (GTK_WIDGET (fb));
- data->fb = fb;
- data->uids = uids;
-
- for (i = 0; i < uids->len; i++) {
- CamelMessageInfo *info;
-
- info = camel_folder_get_message_info (fb->folder, uids->pdata[i]);
- message_tag_followup_append_message (MESSAGE_TAG_FOLLOWUP (editor),
- camel_message_info_from (info),
- camel_message_info_subject (info));
- }
-
- g_signal_connect(editor, "response", G_CALLBACK(tag_editor_response), data);
-
- /* special-case... */
- if (uids->len == 1) {
- CamelMessageInfo *info;
-
- info = camel_folder_get_message_info (fb->folder, uids->pdata[0]);
- if (info) {
- if (info->user_tags)
- message_tag_editor_set_tag_list (MESSAGE_TAG_EDITOR (editor), info->user_tags);
- camel_folder_free_message_info (fb->folder, info);
- }
- }
-
- gtk_widget_show (editor);
-}
-
-void
-flag_followup_completed (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids;
- char *now;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- now = header_format_date (time (NULL), 0);
-
- camel_folder_freeze (fb->folder);
- for (i = 0; i < uids->len; i++) {
- const char *tag;
-
- tag = camel_folder_get_message_user_tag (fb->folder, uids->pdata[i], "follow-up");
- if (tag == NULL || *tag == '\0')
- continue;
-
- camel_folder_set_message_user_tag (fb->folder, uids->pdata[i], "completed-on", now);
- }
- camel_folder_thaw (fb->folder);
-
- g_free (now);
-
- g_ptr_array_free (uids, TRUE);
-}
-
-void
-flag_followup_clear (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- camel_folder_freeze (fb->folder);
- for (i = 0; i < uids->len; i++) {
- camel_folder_set_message_user_tag (fb->folder, uids->pdata[i], "follow-up", "");
- camel_folder_set_message_user_tag (fb->folder, uids->pdata[i], "due-by", "");
- camel_folder_set_message_user_tag (fb->folder, uids->pdata[i], "completed-on", "");
- }
- camel_folder_thaw (fb->folder);
-
- g_ptr_array_free (uids, TRUE);
-}
-
-void
-zoom_in (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- gtk_html_zoom_in (fb->mail_display->html);
-}
-
-void
-zoom_out (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- gtk_html_zoom_out (fb->mail_display->html);
-}
-
-void
-zoom_reset (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- gtk_html_zoom_reset (fb->mail_display->html);
-}
-
-static void
-do_edit_messages (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *data)
-{
- FolderBrowser *fb = (FolderBrowser *) data;
- int i;
-
- if (messages == NULL)
- return;
-
- for (i = 0; i < messages->len; i++) {
- struct _composer_callback_data *ccd;
- EMsgComposer *composer;
-
- camel_medium_remove_header (CAMEL_MEDIUM (messages->pdata[i]), "X-Mailer");
-
- composer = e_msg_composer_new_with_message (messages->pdata[i]);
- e_msg_composer_unset_changed (composer);
- e_msg_composer_drop_editor_undo (composer);
-
- if (composer) {
- ccd = ccd_new ();
- if (folder_browser_is_drafts (fb)) {
- camel_object_ref (folder);
- ccd->drafts_folder = folder;
- ccd->drafts_uid = g_strdup (uids->pdata[i]);
- }
-
- g_signal_connect (composer, "send", G_CALLBACK (composer_send_cb), ccd);
- g_signal_connect (composer, "save-draft", G_CALLBACK (composer_save_draft_cb), ccd);
-
- g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, ccd);
-
- gtk_widget_show (GTK_WIDGET (composer));
- }
- }
-}
-
-static gboolean
-are_you_sure (const char *msg, GPtrArray *uids, FolderBrowser *fb)
-{
- GtkWidget *dialog;
- int button, i;
-
- dialog = gtk_message_dialog_new (FB_WINDOW (fb), GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
- msg, uids->len);
- button = gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
-
- if (button != GTK_RESPONSE_OK) {
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
- g_ptr_array_free (uids, TRUE);
- }
-
- return button == GTK_RESPONSE_OK;
-}
-
-static void
-edit_msg_internal (FolderBrowser *fb)
-{
- GPtrArray *uids;
-
- if (!check_send_configuration (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (uids->len > 10 && !are_you_sure (_("Are you sure you want to edit all %d messages?"), uids, fb))
- return;
-
- mail_get_messages (fb->folder, uids, do_edit_messages, fb);
-}
-
-void
-edit_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (!folder_browser_is_drafts (fb)) {
- e_notice(FB_WINDOW(fb), GTK_MESSAGE_ERROR,
- _("You may only edit messages saved\nin the Drafts folder."));
- return;
- }
-
- edit_msg_internal (fb);
-}
-
-static void
-do_resend_messages (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *data)
-{
- int i;
-
- for (i = 0; i < messages->len; i++) {
- /* generate a new Message-Id because they need to be unique */
- camel_mime_message_set_message_id (messages->pdata[i], NULL);
- }
-
- /* "Resend" should open up the composer to let the user edit the message */
- do_edit_messages (folder, uids, messages, data);
-}
-
-
-void
-resend_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (!folder_browser_is_sent (fb)) {
- e_notice (FB_WINDOW (fb), GTK_MESSAGE_ERROR,
- _("You may only resend messages\nin the Sent folder."));
- return;
- }
-
- if (!check_send_configuration (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (uids->len > 10 && !are_you_sure (_("Are you sure you want to resend all %d messages?"), uids, fb))
- return;
-
- mail_get_messages (fb->folder, uids, do_resend_messages, fb);
-}
-
-
-void
-search_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GtkWidget *w;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (fb->mail_display->current_message == NULL) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (FB_WINDOW(fb), GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
- _("No Message Selected"));
- g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
- gtk_widget_show (dialog);
- return;
- }
-
- w = mail_search_new (fb->mail_display);
- gtk_widget_show_all (w);
-}
-
-void
-load_images (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- mail_display_load_images (fb->mail_display);
-}
-
-static void
-save_msg_ok (GtkWidget *widget, gpointer user_data)
-{
- CamelFolder *folder;
- GPtrArray *uids;
- const char *path;
- struct stat st;
- gboolean ret = TRUE;
-
- path = gtk_file_selection_get_filename (GTK_FILE_SELECTION (user_data));
- if (path[0] == '\0')
- return;
-
- /* make sure we can actually save to it... */
- if (stat (path, &st) != -1 && !S_ISREG (st.st_mode))
- return;
-
- if (access(path, F_OK) == 0) {
- if (access(path, W_OK) != 0) {
- e_notice(GTK_WINDOW(user_data), GTK_MESSAGE_ERROR,
- _("Cannot save to `%s'\n %s"), path, g_strerror(errno));
- return;
- }
-
- ret = e_question(GTK_WINDOW(user_data), GTK_RESPONSE_NO, NULL,
- _("`%s' already exists.\nOverwrite it?"), path);
- }
-
- if (ret) {
- folder = g_object_get_data ((GObject *) user_data, "folder");
- uids = g_object_steal_data (G_OBJECT (user_data), "uids");
- mail_save_messages (folder, uids, path, NULL, NULL);
- gtk_widget_destroy (GTK_WIDGET (user_data));
- }
-}
-
-static void
-save_msg_destroy (gpointer user_data)
-{
- GPtrArray *uids = user_data;
-
- if (uids) {
- int i;
-
- for (i = 0; i < uids->len; i++)
- g_free (uids->pdata[i]);
-
- g_ptr_array_free (uids, TRUE);
- }
-}
-
-void
-save_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GtkFileSelection *filesel;
- GPtrArray *uids;
- char *title, *path;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (uids->len == 1)
- title = _("Save Message As...");
- else
- title = _("Save Messages As...");
-
- filesel = GTK_FILE_SELECTION (gtk_file_selection_new (title));
- path = g_strdup_printf ("%s/", g_get_home_dir ());
- gtk_file_selection_set_filename (filesel, path);
- g_free (path);
-
- g_object_set_data_full ((GObject *) filesel, "uids", uids, save_msg_destroy);
- g_object_set_data ((GObject *) filesel, "folder", fb->folder);
-
- g_signal_connect (filesel->ok_button, "clicked", G_CALLBACK (save_msg_ok), filesel);
- g_signal_connect_swapped (filesel->cancel_button, "clicked",
- G_CALLBACK (gtk_widget_destroy), filesel);
-
- gtk_widget_show (GTK_WIDGET (filesel));
-}
-
-void
-colour_msg (GtkWidget *widget, gpointer user_data)
-{
- /* FIXME: implement me? */
-}
-
-void
-delete_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- gboolean hide_deleted;
- GConfClient *gconf;
- int deleted, row;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- deleted = flag_messages (fb, CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
-
- /* Select the next message if we are only deleting one message */
- if (deleted == 1) {
- row = e_tree_row_of_node (fb->message_list->tree,
- e_tree_get_cursor (fb->message_list->tree));
-
- gconf = mail_config_get_gconf_client ();
- hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL);
-
- /* If this is the last message and deleted messages
- are hidden, select the previous */
- if ((row + 1 == e_tree_row_count (fb->message_list->tree)) && hide_deleted)
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_PREVIOUS,
- 0, CAMEL_MESSAGE_DELETED, FALSE);
- else
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_NEXT,
- 0, 0, FALSE);
- }
-}
-
-void
-undelete_msg (GtkWidget *button, gpointer user_data)
-{
- flag_messages (FOLDER_BROWSER (user_data), CAMEL_MESSAGE_DELETED, 0);
-}
-
-void
-next_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_NEXT, 0, 0, FALSE);
-}
-
-void
-next_unread_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_NEXT, 0, CAMEL_MESSAGE_SEEN, TRUE);
-}
-
-void
-next_flagged_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_NEXT,
- CAMEL_MESSAGE_FLAGGED, CAMEL_MESSAGE_FLAGGED, FALSE);
-}
-
-void
-next_thread (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select_next_thread (fb->message_list);
-}
-
-void
-previous_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_PREVIOUS,
- 0, 0, FALSE);
-}
-
-void
-previous_unread_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_PREVIOUS,
- 0, CAMEL_MESSAGE_SEEN, TRUE);
-}
-
-void
-previous_flagged_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- message_list_select (fb->message_list, MESSAGE_LIST_SELECT_PREVIOUS,
- CAMEL_MESSAGE_FLAGGED, CAMEL_MESSAGE_FLAGGED, TRUE);
-}
-
-static void
-expunged_folder (CamelFolder *f, void *data)
-{
- FolderBrowser *fb = data;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- fb->expunging = NULL;
- gtk_widget_set_sensitive (GTK_WIDGET (fb->message_list), TRUE);
-
- /* FIXME: we should check that the focus hasn't changed in the
- * mean time, otherwise we steal the focus unecessarily.
- * Check :get_toplevel()->focus_widget? */
- if (fb->expunge_mlfocussed)
- gtk_widget_grab_focus((GtkWidget *)fb->message_list);
-}
-
-static gboolean
-confirm_expunge (FolderBrowser *fb)
-{
- gboolean res, show_again;
- GConfClient *gconf;
-
- gconf = mail_config_get_gconf_client ();
-
- if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/expunge", NULL))
- return TRUE;
-
- res = e_question (FB_WINDOW (fb), GTK_RESPONSE_NO, &show_again,
- _("This operation will permanently erase all messages marked as\n"
- "deleted. If you continue, you will not be able to recover these messages.\n"
- "\nReally erase these messages?"));
-
- gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/expunge", show_again, NULL);
-
- return res;
-}
-
-void
-expunge_folder (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (fb->folder && (fb->expunging == NULL || fb->folder != fb->expunging) && confirm_expunge (fb)) {
- CamelMessageInfo *info;
- GtkWindow *top;
- GtkWidget *focus;
-
- /* disable the message list so user can't click on them while we expunge */
-
- /* nasty hack to find out if some widget inside the message list is focussed ... */
- top = GTK_WINDOW (gtk_widget_get_toplevel((GtkWidget *)fb->message_list));
- focus = top?top->focus_widget:NULL;
- while (focus && focus != (GtkWidget *)fb->message_list)
- focus = focus->parent;
- fb->expunge_mlfocussed = focus == (GtkWidget *)fb->message_list;
- gtk_widget_set_sensitive (GTK_WIDGET (fb->message_list), FALSE);
-
- /* Only blank the mail display if the message being
- viewed is one of those to be expunged */
- if (fb->loaded_uid) {
- info = camel_folder_get_message_info (fb->folder, fb->loaded_uid);
-
- if (!info || info->flags & CAMEL_MESSAGE_DELETED)
- mail_display_set_message (fb->mail_display, NULL, NULL, NULL);
- }
-
- fb->expunging = fb->folder;
- mail_expunge_folder (fb->folder, expunged_folder, fb);
- }
-}
-
-/********************** Begin Filter Editor ********************/
-
-static GtkWidget *filter_editor = NULL;
-
-static void
-filter_editor_response (GtkWidget *dialog, int button, FolderBrowser *fb)
-{
- FilterContext *fc;
-
- if (button == GTK_RESPONSE_ACCEPT) {
- char *user;
-
- fc = g_object_get_data(G_OBJECT(dialog), "context");
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
- rule_context_save ((RuleContext *)fc, user);
- g_free (user);
- }
-
- gtk_widget_destroy(dialog);
-
- filter_editor = NULL;
-}
-
-static const char *filter_source_names[] = {
- "incoming",
- "outgoing",
- NULL,
-};
-
-void
-filter_edit (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- FilterContext *fc;
- char *user, *system;
-
- if (filter_editor) {
- gdk_window_raise (GTK_WIDGET (filter_editor)->window);
- return;
- }
-
- fc = filter_context_new ();
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
- system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
- rule_context_load ((RuleContext *)fc, system, user);
- g_free (user);
-
- if (((RuleContext *)fc)->error) {
- e_notice(FB_WINDOW (fb), GTK_MESSAGE_ERROR,
- _("Error loading filter information:\n%s"),
- ((RuleContext *)fc)->error);
- return;
- }
-
- filter_editor = (GtkWidget *)filter_editor_new (fc, filter_source_names);
- /* FIXME: maybe this needs destroy func? */
- gtk_window_set_transient_for ((GtkWindow *) filter_editor, FB_WINDOW (fb));
- gtk_window_set_title (GTK_WINDOW (filter_editor), _("Filters"));
- g_object_set_data_full ((GObject *) filter_editor, "context", fc, (GtkDestroyNotify) g_object_unref);
- g_signal_connect (filter_editor, "response", G_CALLBACK (filter_editor_response), fb);
- gtk_widget_show (GTK_WIDGET (filter_editor));
-}
-
-/********************** End Filter Editor ********************/
-
-void
-vfolder_edit_vfolders (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- vfolder_edit ();
-}
-
-
-/* static void
-header_print_cb (GtkHTML *html, GnomePrintContext *print_context,
- double x, double y, double width, double height, gpointer user_data)
-{
- printf ("header_print_cb %f,%f x %f,%f\n", x, y, width, height);
-
- gnome_print_newpath (print_context);
- gnome_print_setlinewidth (print_context, 12.0);
- gnome_print_setrgbcolor (print_context, 1.0, 0.0, 0.0);
- gnome_print_moveto (print_context, x, y);
- gnome_print_lineto (print_context, x+width, y-height);
- gnome_print_strokepath (print_context);
-} */
-
-struct footer_info {
- GnomeFont *local_font;
- gint page_num, pages;
-};
-
-static void
-footer_print_cb (GtkHTML *html, GnomePrintContext *print_context,
- double x, double y, double width, double height, gpointer user_data)
-{
- struct footer_info *info = (struct footer_info *) user_data;
-
- if (info->local_font) {
- char *text = g_strdup_printf (_("Page %d of %d"), info->page_num, info->pages);
- /*gdouble tw = gnome_font_get_width_string (info->local_font, text);*/
- /* FIXME: work out how to measure this */
- gdouble tw = strlen (text) * 8;
-
- gnome_print_gsave (print_context);
- gnome_print_newpath (print_context);
- gnome_print_setrgbcolor (print_context, .0, .0, .0);
- gnome_print_moveto (print_context, x + width - tw, y - gnome_font_get_ascender (info->local_font));
- gnome_print_setfont (print_context, info->local_font);
- gnome_print_show (print_context, text);
- gnome_print_grestore (print_context);
-
- g_free (text);
- info->page_num++;
- }
-}
-
-static void
-footer_info_free (struct footer_info *info)
-{
- if (info->local_font)
- gnome_font_unref (info->local_font);
- g_free (info);
-}
-
-static struct footer_info *
-footer_info_new (GtkHTML *html, GnomePrintContext *pc, gdouble *line)
-{
- struct footer_info *info;
-
- info = g_new (struct footer_info, 1);
- info->local_font = gnome_font_find_closest ("Helvetica", 10.0);
-
- if (info->local_font)
- *line = gnome_font_get_ascender (info->local_font) - gnome_font_get_descender (info->local_font);
-
- info->page_num = 1;
- info->pages = gtk_html_print_get_pages_num (html, pc, 0.0, *line);
-
- return info;
-}
-
-static void
-do_mail_print (FolderBrowser *fb, gboolean preview)
-{
- GtkHTML *html;
- GtkWidget *w = NULL;
- GnomePrintContext *print_context;
- GnomePrintJob *print_master;
- GnomePrintConfig *config = NULL;
- GtkDialog *dialog;
- gdouble line = 0.0;
- struct footer_info *info;
-
- if (!preview) {
- dialog = (GtkDialog *) gnome_print_dialog_new (NULL, _("Print Message"), GNOME_PRINT_DIALOG_COPIES);
- gtk_dialog_set_default_response (dialog, GNOME_PRINT_DIALOG_RESPONSE_PRINT);
- gtk_window_set_transient_for ((GtkWindow *) dialog, (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) fb));
-
- switch (gtk_dialog_run (dialog)) {
- case GNOME_PRINT_DIALOG_RESPONSE_PRINT:
- break;
- case GNOME_PRINT_DIALOG_RESPONSE_PREVIEW:
- preview = TRUE;
- break;
- default:
- gtk_widget_destroy ((GtkWidget *) dialog);
- return;
- }
-
- config = gnome_print_dialog_get_config ((GnomePrintDialog *) dialog);
- gtk_widget_destroy ((GtkWidget *)dialog);
- }
-
- if (config) {
- print_master = gnome_print_job_new (config);
- gnome_print_config_unref (config);
- } else
- print_master = gnome_print_job_new (NULL);
-
- /* paper size settings? */
- /*gnome_print_master_set_paper (print_master, paper);*/
- print_context = gnome_print_job_get_context (print_master);
-
- html = GTK_HTML (gtk_html_new ());
- gtk_widget_set_name (GTK_WIDGET (html), "EvolutionMailPrintHTMLWidget");
- mail_display_initialize_gtkhtml (fb->mail_display, html);
-
- /* Set our 'printing' flag to true and render. This causes us
- to ignoring any adjustments we made to accomodate the
- user's theme. */
- fb->mail_display->printing = TRUE;
-
- if (!GTK_WIDGET_REALIZED (GTK_WIDGET (html))) {
- /* gtk widgets don't like to be realized outside top level widget
- so we put new html widget into gtk window */
- w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (html));
- gtk_widget_realize (GTK_WIDGET (html));
- }
- mail_display_render (fb->mail_display, html, TRUE);
- gtk_html_print_set_master (html, print_master);
-
- info = footer_info_new (html, print_context, &line);
- gtk_html_print_with_header_footer (html, print_context, 0.0, line, NULL, footer_print_cb, info);
- footer_info_free (info);
-
- fb->mail_display->printing = FALSE;
-
- gnome_print_job_close (print_master);
- gtk_widget_destroy (GTK_WIDGET (html));
- if (w)
- gtk_widget_destroy (w);
-
- if (preview){
- GtkWidget *pw;
-
- pw = gnome_print_job_preview_new (print_master, _("Print Preview"));
- gtk_widget_show (pw);
- } else {
- int result = gnome_print_job_print (print_master);
-
- if (result == -1)
- e_notice (FB_WINDOW (fb), GTK_MESSAGE_ERROR, _("Printing of message failed"));
- }
-
- g_object_unref (print_master);
-}
-
-/* This is pretty evil. FolderBrowser's API should be extended to allow these sorts of
- things to be done in a more natural way. */
-
-/* <evil_code> */
-
-struct blarg_this_sucks {
- FolderBrowser *fb;
- gboolean preview;
-};
-
-static void
-done_message_selected (CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *data)
-{
- struct blarg_this_sucks *blarg = data;
- FolderBrowser *fb = blarg->fb;
- gboolean preview = blarg->preview;
- CamelMessageInfo *info;
-
- g_free (blarg);
-
- info = camel_folder_get_message_info (fb->folder, uid);
- mail_display_set_message (fb->mail_display, (CamelMedium *) msg, fb->folder, info);
- if (info)
- camel_folder_free_message_info (fb->folder, info);
-
- g_free (fb->loaded_uid);
- fb->loaded_uid = fb->loading_uid;
- fb->loading_uid = NULL;
-
- if (msg)
- do_mail_print (fb, preview);
-}
-
-/* Ack! Most of this is copied from folder-browser.c */
-static void
-do_mail_fetch_and_print (FolderBrowser *fb, gboolean preview)
-{
- if (!fb->preview_shown || fb->mail_display->current_message == NULL) {
- /* If the preview pane is closed, we have to do some
- extra magic to load the message. */
- struct blarg_this_sucks *blarg = g_new (struct blarg_this_sucks, 1);
-
- blarg->fb = fb;
- blarg->preview = preview;
-
- fb->loading_id = 0;
-
- /* if we are loading, then set a pending, but leave the loading, coudl cancel here (?) */
- if (fb->loading_uid) {
- g_free (fb->pending_uid);
- fb->pending_uid = g_strdup (fb->new_uid);
- } else {
- if (fb->new_uid) {
- fb->loading_uid = g_strdup (fb->new_uid);
- mail_get_message (fb->folder, fb->loading_uid, done_message_selected, blarg, mail_thread_new);
- } else {
- mail_display_set_message (fb->mail_display, NULL, NULL, NULL);
- g_free (blarg);
- }
- }
- } else {
- do_mail_print (fb, preview);
- }
-}
-
-/* </evil_code> */
-
-
-void
-print_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- do_mail_fetch_and_print (fb, FALSE);
-}
-
-void
-print_preview_msg (GtkWidget *button, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- do_mail_fetch_and_print (fb, TRUE);
-}
-
-/******************** Begin Subscription Dialog ***************************/
-
-static GtkObject *subscribe_dialog = NULL;
-
-static void
-subscribe_dialog_destroy (GtkObject *dialog, GObject *deadbeef)
-{
- if (subscribe_dialog) {
- g_object_unref (subscribe_dialog);
- subscribe_dialog = NULL;
- }
-}
-
-void
-manage_subscriptions (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- if (!subscribe_dialog) {
- subscribe_dialog = subscribe_dialog_new ();
-
- g_object_weak_ref ((GObject *) SUBSCRIBE_DIALOG (subscribe_dialog)->app,
- (GWeakNotify) subscribe_dialog_destroy, subscribe_dialog);
- g_object_ref(subscribe_dialog);
- gtk_object_sink((GtkObject *)subscribe_dialog);
- subscribe_dialog_show (subscribe_dialog);
- } else {
- gdk_window_raise (SUBSCRIBE_DIALOG (subscribe_dialog)->app->window);
- }
-}
-
-/******************** End Subscription Dialog ***************************/
-
-static void
-local_configure_done(const char *uri, CamelFolder *folder, void *data)
-{
- FolderBrowser *fb = data;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb)) {
- g_object_unref(fb);
- return;
- }
-
- if (folder == NULL)
- folder = fb->folder;
-
- message_list_set_folder(fb->message_list, folder, FALSE);
- g_object_unref(fb);
-}
-
-void
-configure_folder (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (fb->uri) {
- if (strncmp (fb->uri, "vfolder:", 8) == 0) {
- vfolder_edit_rule (fb->uri);
- } else {
- message_list_set_folder(fb->message_list, NULL, FALSE);
- g_object_ref((GtkObject *)fb);
- mail_local_reconfigure_folder(fb->uri, local_configure_done, fb);
- }
- }
-}
-
-static void
-do_view_messages(CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, void *data)
-{
- FolderBrowser *fb = data;
- int i;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- for (i = 0; i < uids->len && i < msgs->len; i++) {
- char *uid = uids->pdata[i];
- CamelMimeMessage *msg = msgs->pdata[i];
- GtkWidget *mb;
-
- camel_folder_set_message_flags(folder, uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
- mb = message_browser_new(fb->shell, fb->uri, uid);
- gtk_widget_show (mb);
- }
-}
-
-void
-view_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- GPtrArray *uids;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- uids = g_ptr_array_new ();
- message_list_foreach (fb->message_list, enumerate_msg, uids);
-
- if (uids->len > 10 && !are_you_sure (_("Are you sure you want to open all %d messages in separate windows?"), uids, fb))
- return;
-
- mail_get_messages(fb->folder, uids, do_view_messages, fb);
-}
-
-void
-open_msg (GtkWidget *widget, gpointer user_data)
-{
- FolderBrowser *fb = FOLDER_BROWSER (user_data);
- extern CamelFolder *outbox_folder;
-
- if (FOLDER_BROWSER_IS_DESTROYED (fb))
- return;
-
- if (folder_browser_is_drafts (fb) || fb->folder == outbox_folder)
- edit_msg_internal (fb);
- else
- view_msg (NULL, user_data);
-}
-
-void
-open_message (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- open_msg (NULL, user_data);
-}
-
-void
-edit_message (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- edit_msg (NULL, user_data);
-}
-
-void
-stop_threads (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- camel_operation_cancel (NULL);
-}
-
-void
-empty_trash (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- CamelProvider *provider;
- EAccountList *accounts;
- FolderBrowser *fb;
- CamelException ex;
- EAccount *account;
- EIterator *iter;
-
- fb = user_data ? FOLDER_BROWSER (user_data) : NULL;
-
- if (fb && !confirm_expunge (fb))
- return;
-
- camel_exception_init (&ex);
-
- /* expunge all remote stores */
- accounts = mail_config_get_accounts ();
- iter = e_list_get_iterator ((EList *) accounts);
- while (e_iterator_is_valid (iter)) {
- account = (EAccount *) e_iterator_get (iter);
-
- /* make sure this is a valid source */
- if (account->enabled && account->source->url) {
- provider = camel_session_get_provider (session, account->source->url, &ex);
- if (provider) {
- /* make sure this store is a remote store */
- if (provider->flags & CAMEL_PROVIDER_IS_STORAGE &&
- provider->flags & CAMEL_PROVIDER_IS_REMOTE) {
- mail_empty_trash (account, NULL, NULL);
- }
- }
-
- /* clear the exception for the next round */
- camel_exception_clear (&ex);
- }
-
- e_iterator_next (iter);
- }
-
- g_object_unref (iter);
-
- /* Now empty the local trash folder */
- mail_empty_trash (NULL, NULL, NULL);
-}
diff --git a/mail/mail-callbacks.h b/mail/mail-callbacks.h
deleted file mode 100644
index 6b2c4573c9..0000000000
--- a/mail/mail-callbacks.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2000 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef MAIL_CALLBACKS_H
-#define MAIL_CALLBACKS_H
-
-#include <camel/camel.h>
-#include "composer/e-msg-composer.h"
-#include <mail/mail-types.h>
-#include "evolution-storage.h"
-
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus */
-
-/* these are the possible modes for replying */
-enum {
- REPLY_SENDER,
- REPLY_LIST,
- REPLY_ALL,
- REPLY_POST,
- REPLY_NO_QUOTE = 0x80 /* dont quote reply */
-};
-
-void enumerate_msg (MessageList *ml, const char *uid, gpointer data);
-
-void fetch_mail (GtkWidget *widget, gpointer user_data);
-void send_queued_mail (GtkWidget *widget, gpointer user_data);
-
-void compose_msg (GtkWidget *widget, gpointer user_data);
-void send_to_url (const char *url, const char *parent_uri);
-
-void forward_inline (GtkWidget *widget, gpointer user_data);
-void forward_quoted (GtkWidget *widget, gpointer user_data);
-void forward_attached (GtkWidget *widget, gpointer user_data);
-void forward (GtkWidget *widget, gpointer user_data);
-
-void post_to_url (const char *url);
-void post_message (GtkWidget *widget, gpointer user_data);
-void post_reply (GtkWidget *widget, gpointer user_data);
-
-void redirect (GtkWidget *widget, gpointer user_data);
-
-void reply_to_sender (GtkWidget *widget, gpointer user_data);
-void reply_to_list (GtkWidget *widget, gpointer user_data);
-void reply_to_all (GtkWidget *widget, gpointer user_data);
-
-void colour_msg (GtkWidget *widget, gpointer user_data);
-void delete_msg (GtkWidget *widget, gpointer user_data);
-void undelete_msg (GtkWidget *widget, gpointer user_data);
-void move_msg_cb (GtkWidget *widget, gpointer user_data);
-void copy_msg_cb (GtkWidget *widget, gpointer user_data);
-void addrbook_sender (GtkWidget *widget, gpointer user_data);
-void apply_filters (GtkWidget *widget, gpointer user_data);
-void print_msg (GtkWidget *widget, gpointer user_data);
-void print_preview_msg (GtkWidget *widget, gpointer user_data);
-void edit_msg (GtkWidget *widget, gpointer user_data);
-void open_msg (GtkWidget *widget, gpointer user_data);
-void save_msg (GtkWidget *widget, gpointer user_data);
-void view_msg (GtkWidget *widget, gpointer user_data);
-void view_digest (GtkWidget *widget, gpointer user_data);
-void view_source (GtkWidget *widget, gpointer user_data);
-void next_msg (GtkWidget *widget, gpointer user_data);
-void next_unread_msg (GtkWidget *widget, gpointer user_data);
-void next_flagged_msg (GtkWidget *widget, gpointer user_data);
-void next_thread (GtkWidget *widget, gpointer user_data);
-void previous_msg (GtkWidget *widget, gpointer user_data);
-void previous_unread_msg (GtkWidget *widget, gpointer user_data);
-void previous_flagged_msg (GtkWidget *widget, gpointer user_data);
-void resend_msg (GtkWidget *widget, gpointer user_data);
-void search_msg (GtkWidget *widget, gpointer user_data);
-void load_images (GtkWidget *widget, gpointer user_data);
-
-void add_sender_to_addrbook (BonoboUIComponent *uih, void *user_data, const char *path);
-void move_msg (BonoboUIComponent *uih, void *user_data, const char *path);
-void copy_msg (BonoboUIComponent *uih, void *user_data, const char *path);
-void select_all (BonoboUIComponent *uih, void *user_data, const char *path);
-void select_thread (BonoboUIComponent *uih, void *user_data, const char *path);
-void invert_selection (BonoboUIComponent *uih, void *user_data, const char *path);
-void mark_as_seen (BonoboUIComponent *uih, void *user_data, const char *path);
-void mark_all_as_seen (BonoboUIComponent *uih, void *user_data, const char *path);
-void mark_as_unseen (BonoboUIComponent *uih, void *user_data, const char *path);
-void mark_as_important (BonoboUIComponent *uih, void *user_data, const char *path);
-void mark_as_unimportant (BonoboUIComponent *uih, void *user_data, const char *path);
-void toggle_as_important (BonoboUIComponent *uih, void *user_data, const char *path);
-void flag_for_followup (BonoboUIComponent *uih, void *user_data, const char *path);
-void flag_followup_completed (BonoboUIComponent *uih, void *user_data, const char *path);
-void flag_followup_clear (BonoboUIComponent *uih, void *user_data, const char *path);
-
-void zoom_in (BonoboUIComponent *uih, void *user_data, const char *path);
-void zoom_out (BonoboUIComponent *uih, void *user_data, const char *path);
-void zoom_reset (BonoboUIComponent *uih, void *user_data, const char *path);
-
-void edit_message (BonoboUIComponent *uih, void *user_data, const char *path);
-void open_message (BonoboUIComponent *uih, void *user_data, const char *path);
-void expunge_folder (BonoboUIComponent *uih, void *user_data, const char *path);
-void filter_edit (BonoboUIComponent *uih, void *user_data, const char *path);
-void vfolder_edit_vfolders (BonoboUIComponent *uih, void *user_data, const char *path);
-void manage_subscriptions (BonoboUIComponent *uih, void *user_data, const char *path);
-
-void configure_folder (BonoboUIComponent *uih, void *user_data, const char *path);
-
-void stop_threads (BonoboUIComponent *uih, void *user_data, const char *path);
-
-void empty_trash (BonoboUIComponent *uih, void *user_data, const char *path);
-
-void mail_reply (CamelFolder *folder, CamelMimeMessage *msg, const char *uid, int mode);
-
-void composer_send_cb (EMsgComposer *composer, gpointer data);
-void composer_save_draft_cb (EMsgComposer *composer, int quit, gpointer data);
-
-void forward_messages (CamelFolder *folder, GPtrArray *uids, gboolean inline);
-
-/* CamelStore callbacks */
-void folder_created (CamelStore *store, const char *prefix, CamelFolderInfo *fi);
-void folder_deleted (CamelStore *store, CamelFolderInfo *fi);
-
-void mail_storage_create_folder (EvolutionStorage *storage, CamelStore *store, CamelFolderInfo *fi);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* ! MAIL_CALLBACKS_H */
diff --git a/mail/mail-display-stream.c b/mail/mail-display-stream.c
deleted file mode 100644
index ae81401680..0000000000
--- a/mail/mail-display-stream.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2001 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "mail-display-stream.h"
-
-
-static void mail_display_stream_class_init (MailDisplayStreamClass *klass);
-static void mail_display_stream_init (CamelObject *object);
-static void mail_display_stream_finalize (CamelObject *object);
-
-static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
-
-
-static CamelStreamClass *parent_class = NULL;
-
-
-CamelType
-mail_display_stream_get_type (void)
-{
- static CamelType type = CAMEL_INVALID_TYPE;
-
- if (type == CAMEL_INVALID_TYPE) {
- type = camel_type_register (CAMEL_STREAM_TYPE,
- "MailDisplayStream",
- sizeof (MailDisplayStream),
- sizeof (MailDisplayStreamClass),
- (CamelObjectClassInitFunc) mail_display_stream_class_init,
- NULL,
- (CamelObjectInitFunc) mail_display_stream_init,
- (CamelObjectFinalizeFunc) mail_display_stream_finalize);
- }
-
- return type;
-}
-
-static void
-mail_display_stream_class_init (MailDisplayStreamClass *klass)
-{
- CamelStreamClass *stream_class = CAMEL_STREAM_CLASS (klass);
-
- parent_class = (CamelStreamClass *) CAMEL_STREAM_TYPE;
-
- /* virtual method overload */
- stream_class->write = stream_write;
-}
-
-static void
-mail_display_stream_init (CamelObject *object)
-{
- ;
-}
-
-static void
-mail_display_stream_finalize (CamelObject *object)
-{
- ;
-}
-
-static ssize_t
-stream_write (CamelStream *stream, const char *buffer, size_t n)
-{
- MailDisplayStream *dstream = MAIL_DISPLAY_STREAM (stream);
-
- gtk_html_write (dstream->html, dstream->html_stream, buffer, n);
-
- return (ssize_t) n;
-}
-
-
-CamelStream *
-mail_display_stream_new (GtkHTML *html, GtkHTMLStream *html_stream)
-{
- MailDisplayStream *new;
-
- new = MAIL_DISPLAY_STREAM (camel_object_new (MAIL_DISPLAY_STREAM_TYPE));
- new->html = html;
- new->html_stream = html_stream;
-
- return CAMEL_STREAM (new);
-}
diff --git a/mail/mail-display.c b/mail/mail-display.c
deleted file mode 100644
index 4793d0741a..0000000000
--- a/mail/mail-display.c
+++ /dev/null
@@ -1,2995 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Dan Winship <danw@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- * Michael Zucchi <notzed@ximian.com>
- * Miguel de Icaza <miguel@ximian.com>
- * Larry Ewing <lewing@ximian.com>
- *
- * Copyright 2002 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <gtk/gtkinvisible.h>
-#include <libgnome/gnome-program.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <libgnomevfs/gnome-vfs.h>
-#include <libgnome/gnome-url.h>
-#include <bonobo/bonobo-exception.h>
-#include <bonobo/bonobo-control-frame.h>
-#include <bonobo/bonobo-stream-memory.h>
-#include <bonobo/bonobo-widget.h>
-#include <bonobo/bonobo-socket.h>
-
-#include <gdk/gdkkeysyms.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <gdk-pixbuf/gdk-pixbuf-loader.h>
-#include <gal/util/e-util.h>
-#include <gal/widgets/e-gui-utils.h>
-#include <gal/widgets/e-popup-menu.h>
-
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-embedded.h>
-#include <gtkhtml/htmlengine.h>
-#include <gtkhtml/htmlobject.h>
-#include <gtkhtml/htmltext.h>
-#include <gtkhtml/htmlinterval.h>
-#include <gtkhtml/gtkhtml-stream.h>
-
-#include <libsoup/soup-message.h>
-
-#include "e-util/e-gui-utils.h"
-#include "e-util/e-mktemp.h"
-#include "addressbook/backend/ebook/e-book-util.h"
-
-#include "e-searching-tokenizer.h"
-#include "folder-browser-factory.h"
-#include "mail-display-stream.h"
-#include "folder-browser.h"
-#include "mail-config.h"
-#include "mail-display.h"
-#include "mail-format.h"
-#include "mail-ops.h"
-#include "mail-mt.h"
-#include "mail.h"
-
-#include "camel/camel-data-cache.h"
-
-#define d(x)
-
-struct _MailDisplayPrivate {
-
- /* because we want to control resource usage, we need our own queues, etc */
- EDList fetch_active;
- EDList fetch_queue;
-
- /* used to try and make some sense with progress reporting */
- int fetch_total;
- int fetch_total_done;
-
- /* bit hackish, 'fake' an async message and processing,
- so we can use that to get cancel and report progress */
- struct _mail_msg *fetch_msg ;
- GIOChannel *fetch_cancel_channel;
- guint fetch_cancel_watch;
-
- guint display_notify_id;
-};
-
-/* max number of connections to download images */
-#define FETCH_MAX_CONNECTIONS (4)
-
-/* path to http cache in fetch_cache */
-#define FETCH_HTTP_CACHE "http"
-
-/* for asynchronously downloading remote content */
-struct _remote_data {
- struct _remote_data *next;
- struct _remote_data *prev;
-
- MailDisplay *md; /* not ref'd */
-
- SoupMessage *msg;
- char *uri;
- GtkHTML *html;
- GtkHTMLStream *stream;
- CamelStream *cstream; /* cache stream */
- size_t length;
- size_t total;
-};
-
-static void fetch_remote(MailDisplay *md, const char *uri, GtkHTML *html, GtkHTMLStream *stream);
-static void fetch_cancel(MailDisplay *md);
-static void fetch_next(MailDisplay *md);
-static void fetch_data(SoupMessage *req, void *data);
-static void fetch_free(struct _remote_data *rd);
-static void fetch_done(SoupMessage *req, void *data);
-
-/* global http cache, relies on external evolution_dir as well */
-static CamelDataCache *fetch_cache;
-
-#define PARENT_TYPE (gtk_vbox_get_type ())
-
-static GtkObjectClass *mail_display_parent_class;
-
-struct _PixbufLoader {
- CamelDataWrapper *wrapper; /* The data */
- CamelStream *mstream;
- GdkPixbufLoader *loader;
- GtkHTMLEmbedded *eb;
- char *type; /* Type of data, in case the conversion fails */
- char *cid; /* Strdupped on creation, but not freed until
- the hashtable is destroyed */
- GtkWidget *pixmap;
- guint32 destroy_id;
-};
-static GHashTable *thumbnail_cache = NULL;
-
-/* Drag & Drop types */
-#define TEXT_URI_LIST_TYPE "text/uri-list"
-
-enum DndTargetType {
- DND_TARGET_TYPE_TEXT_URI_LIST,
- DND_TARGET_TYPE_PART_MIME_TYPE
-};
-
-static GtkTargetEntry drag_types[] = {
- { TEXT_URI_LIST_TYPE, 0, DND_TARGET_TYPE_TEXT_URI_LIST },
- { NULL, 0, DND_TARGET_TYPE_PART_MIME_TYPE }
-};
-
-static const int num_drag_types = sizeof (drag_types) / sizeof (drag_types[0]);
-
-/*----------------------------------------------------------------------*
- * Callbacks
- *----------------------------------------------------------------------*/
-
-static void
-write_data_written(CamelMimePart *part, char *name, int done, void *data)
-{
- int *ret = data;
-
- /* should we popup a dialogue to say its done too? */
- *ret = done;
-}
-
-static gboolean
-write_data_to_file (CamelMimePart *part, const char *name, gboolean unique)
-{
- int fd, ret = FALSE;
-
- g_return_val_if_fail (CAMEL_IS_MIME_PART (part), FALSE);
-
- fd = open (name, O_WRONLY | O_CREAT | O_EXCL, 0666);
- if (fd == -1 && errno == EEXIST && !unique) {
- GtkWidget *dialog;
- int button;
-
- dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
- _("File `%s' already exists.\nOverwrite it?"),
- name);
-
- g_object_set (dialog, "title", _("Overwrite file?"), "allow_grow", TRUE, NULL);
- button = gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
-
- if (button != GTK_RESPONSE_YES)
- return FALSE;
- }
-
- if (fd != -1)
- close (fd);
-
- /* should this have progress of what its doing? */
- mail_msg_wait (mail_save_part (part, name, write_data_written, &ret));
-
- return ret;
-}
-
-static char *
-make_safe_filename (const char *prefix, CamelMimePart *part)
-{
- const char *name;
- char *safe, *p;
-
- name = part ? camel_mime_part_get_filename (part) : NULL;
-
- if (!name) {
- /* This is a filename. Translators take note. */
- name = _("attachment");
- }
-
- p = strrchr (name, '/');
- if (p)
- safe = g_strdup_printf ("%s%s", prefix, p);
- else
- safe = g_strdup_printf ("%s/%s", prefix, name);
-
- p = strrchr (safe, '/');
- if (p)
- e_filename_make_safe (p + 1);
-
- return safe;
-}
-
-static void
-save_data_cb (GtkWidget *widget, gpointer user_data)
-{
- GtkFileSelection *file_select;
- GConfClient *gconf;
- char *dir;
-
- file_select = (GtkFileSelection *) gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_SELECTION);
-
- /* uh, this doesn't really feel right, but i dont know what to do better */
- gtk_widget_hide (GTK_WIDGET (file_select));
- write_data_to_file (user_data, gtk_file_selection_get_filename (file_select), FALSE);
-
- /* preserve the pathname */
- dir = g_path_get_dirname (gtk_file_selection_get_filename (file_select));
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_string (gconf, "/apps/evolution/mail/save_dir", dir, NULL);
- g_free (dir);
-
- gtk_widget_destroy (GTK_WIDGET (file_select));
-}
-
-static void
-save_destroy_cb (CamelMimePart *part, GObject *deadbeef)
-{
- camel_object_unref (part);
-}
-
-static gboolean
-idle_redisplay (gpointer data)
-{
- MailDisplay *md = data;
-
- md->idle_id = 0;
- mail_display_redisplay (md, FALSE);
-
- return FALSE;
-}
-
-void
-mail_display_queue_redisplay (MailDisplay *md)
-{
- if (!md->idle_id) {
- md->idle_id = g_idle_add_full (G_PRIORITY_LOW, idle_redisplay,
- md, NULL);
- }
-}
-
-static void
-mail_display_jump_to_anchor (MailDisplay *md, const char *url)
-{
- char *anchor = strstr (url, "#");
-
- g_return_if_fail (anchor != NULL);
-
- if (anchor)
- gtk_html_jump_to_anchor (md->html, anchor + 1);
-}
-
-static void
-on_link_clicked (GtkHTML *html, const char *url, MailDisplay *md)
-{
- if (!strncasecmp (url, "mailto:", 7)) {
- send_to_url (url, NULL);
- } else if (*url == '#') {
- mail_display_jump_to_anchor (md, url);
- } else {
- GError *err = NULL;
-
- gnome_url_show (url, &err);
-
- if (err) {
- g_warning ("gnome_url_show: %s", err->message);
- g_error_free (err);
- }
- }
-}
-
-static void
-save_part (CamelMimePart *part)
-{
- char *filename, *dir, *home, *base;
- GtkFileSelection *file_select;
- GConfClient *gconf;
-
- camel_object_ref (part);
-
- home = getenv ("HOME");
- gconf = mail_config_get_gconf_client ();
- dir = gconf_client_get_string (gconf, "/apps/evolution/mail/save_dir", NULL);
- filename = make_safe_filename (dir ? dir : (home ? home : ""), part);
- g_free (dir);
-
- file_select = GTK_FILE_SELECTION (gtk_file_selection_new (_("Save Attachment")));
- gtk_file_selection_set_filename (file_select, filename);
- /* set the GtkEntry with the locale filename by breaking abstraction */
- base = g_path_get_basename (filename);
- gtk_entry_set_text (GTK_ENTRY (file_select->selection_entry), base);
- g_free (filename);
- g_free (base);
-
- g_signal_connect (file_select->ok_button, "clicked",
- G_CALLBACK (save_data_cb), part);
-
- g_signal_connect_swapped (file_select->cancel_button, "clicked",
- G_CALLBACK (gtk_widget_destroy), file_select);
-
- g_object_weak_ref ((GObject *) file_select, (GWeakNotify) save_destroy_cb, part);
-
- gtk_widget_show (GTK_WIDGET (file_select));
-}
-
-static void
-save_cb (GtkWidget *widget, gpointer user_data)
-{
- CamelMimePart *part = g_object_get_data ((GObject *) user_data, "CamelMimePart");
-
- save_part (part);
-}
-
-static void
-launch_cb (GtkWidget *widget, gpointer user_data)
-{
- CamelMimePart *part = g_object_get_data(user_data, "CamelMimePart");
- MailMimeHandler *handler;
- GList *apps, *children, *c;
- GnomeVFSMimeApplication *app;
- char *command, *filename;
- const char *tmpdir;
-
- handler = mail_lookup_handler (g_object_get_data(user_data, "mime_type"));
- g_return_if_fail (handler != NULL && handler->applications != NULL);
-
- /* Yum. Too bad EPopupMenu doesn't allow per-item closures. */
- children = gtk_container_get_children (GTK_CONTAINER (widget->parent));
- /* We need to bypass the first 2 menu items */
- g_return_if_fail (children != NULL && children->next != NULL
- && children->next->next != NULL && children->next->next->next != NULL);
-
- for (c = children->next->next->next, apps = handler->applications; c && apps; c = c->next, apps = apps->next) {
- if (c->data == widget)
- break;
- }
- g_list_free (children);
- g_return_if_fail (c != NULL && apps != NULL);
- app = apps->data;
-
- tmpdir = e_mkdtemp ("app-launcher-XXXXXX");
-
- if (!tmpdir) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_RESPONSE_CLOSE,
- _("Could not create temporary directory: %s"),
- g_strerror (errno));
-
- /* FIXME: this should be async */
- gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
- return;
- }
-
- filename = make_safe_filename (tmpdir, part);
-
- if (!write_data_to_file (part, filename, TRUE)) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_RESPONSE_CLOSE,
- _("Could not create temporary file '%s': %s"),
- filename, g_strerror (errno));
-
- /* FIXME: this should be async */
- gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
- g_free (filename);
- return;
- }
-
- command = g_strdup_printf ("%s %s%s &", app->command,
- app->expects_uris == GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS ?
- "file://" : "", filename);
- g_free (filename);
-
- system (command);
- g_free (command);
-}
-
-static void
-inline_cb (GtkWidget *widget, gpointer user_data)
-{
- MailDisplay *md = g_object_get_data (user_data, "MailDisplay");
- CamelMimePart *part = g_object_get_data (user_data, "CamelMimePart");
-
- mail_part_toggle_displayed (part, md);
- mail_display_queue_redisplay (md);
-}
-
-static void
-save_all_parts_cb (GtkWidget *widget, gpointer user_data)
-{
- GtkFileSelection *dir_select = (GtkFileSelection *)
- gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_SELECTION);
- const char *filename;
- char *save_filename, *dir;
- struct stat st;
- int i;
- GPtrArray *attachment_array;
- CamelMimePart *part;
- GConfClient *gconf;
-
- gtk_widget_hide (GTK_WIDGET (dir_select));
-
- /* Get the selected directory name */
- filename = gtk_file_selection_get_filename (dir_select);
- if (stat (filename, &st) == -1 || !S_ISDIR (st.st_mode)) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
- _("%s is not a valid directory name."), filename);
-
- /* FIXME: this should be async */
- gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
- gtk_widget_destroy (GTK_WIDGET (dir_select));
- return;
- } else {
- dir = g_strdup (filename);
- }
-
- /* Now save the attachment one by one */
- attachment_array = (GPtrArray *)user_data;
- for (i = 0; i < attachment_array->len; i++) {
- part = g_ptr_array_index (attachment_array, i);
- save_filename = make_safe_filename (dir, part);
- write_data_to_file (part, save_filename, FALSE);
- g_free (save_filename);
- }
-
- /* preserve the pathname */
- gconf = mail_config_get_gconf_client ();
- gconf_client_set_string (gconf, "/apps/evolution/mail/save_dir", dir, NULL);
- g_free (dir);
-
- gtk_widget_destroy (GTK_WIDGET (dir_select));
-}
-
-static void
-save_all_parts (GPtrArray *attachment_array)
-{
- GtkFileSelection *dir_select;
- char *dir, *home, *dir2;
- GConfClient *gconf;
-
- g_return_if_fail (attachment_array != NULL);
-
- home = getenv ("HOME");
- gconf = mail_config_get_gconf_client ();
- dir = gconf_client_get_string (gconf, "/apps/evolution/mail/save_dir", NULL);
- dir = dir ? dir : (home ? g_strdup (home) : g_strdup (""));
-
- /* Make sure dir2 has a '/' as its tail */
- dir2 = g_strdup_printf ("%s/", dir);
- g_free (dir);
-
- dir_select = GTK_FILE_SELECTION (
- gtk_file_selection_new (_("Select Directory for Attachments")));
- gtk_file_selection_set_filename (dir_select, dir2);
- gtk_widget_set_sensitive (dir_select->file_list, FALSE);
- gtk_widget_hide (dir_select->selection_entry);
- g_free (dir2);
-
- g_signal_connect (dir_select->ok_button, "clicked",
- G_CALLBACK (save_all_parts_cb), attachment_array);
- g_signal_connect_swapped (dir_select->cancel_button,
- "clicked",
- G_CALLBACK (gtk_widget_destroy),
- dir_select);
-
- gtk_widget_show (GTK_WIDGET (dir_select));
-}
-
-static void
-save_all_cb (GtkWidget *widget, gpointer user_data)
-{
- MailDisplay *md = g_object_get_data (user_data, "MailDisplay");
- GPtrArray *attachment_array;
-
- if (md == NULL) {
- g_warning ("No MailDisplay!");
- return;
- }
-
- attachment_array = g_datalist_get_data (md->data, "attachment_array");
- save_all_parts (attachment_array);
-}
-
-static void
-inline_toggle(MailDisplay *md, CamelMimePart *part)
-{
- g_return_if_fail(md != NULL);
-
- mail_part_toggle_displayed (part, md);
- mail_display_queue_redisplay (md);
-}
-
-static void
-inline_button_clicked(GtkWidget *widget, CamelMimePart *part)
-{
- inline_toggle((MailDisplay *)g_object_get_data((GObject *)widget, "MailDisplay"), part);
-}
-
-static gboolean
-inline_button_press(GtkWidget *widget, GdkEventKey *event, CamelMimePart *part)
-{
- if (event->keyval != GDK_Return)
- return FALSE;
-
- inline_toggle((MailDisplay *)g_object_get_data((GObject *)widget, "MailDisplay"), part);
-
- return TRUE;
-}
-
-static void
-popup_menu_placement_callback(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
-{
- GtkWidget *widget = (GtkWidget*) user_data;
-
- gdk_window_get_origin (gtk_widget_get_parent_window (widget), x, y);
- *x += widget->allocation.x + widget->allocation.width;
- *y += widget->allocation.y;
-
- return;
-}
-
-static gboolean
-pixmap_press (GtkWidget *widget, GdkEvent *event, gpointer user_data)
-{
- EPopupMenu *menu;
- GtkMenu *gtk_menu;
- EPopupMenu save_item = E_POPUP_ITEM (N_("Save Attachment..."), G_CALLBACK (save_cb), 0);
- EPopupMenu save_all_item = E_POPUP_ITEM (N_("Save all attachments..."), G_CALLBACK (save_all_cb), 0);
- EPopupMenu view_item = E_POPUP_ITEM (N_("View Inline"), G_CALLBACK (inline_cb), 2);
- EPopupMenu open_item = E_POPUP_ITEM (N_("Open in %s..."), G_CALLBACK (launch_cb), 1);
- MailDisplay *md;
- CamelMimePart *part;
- MailMimeHandler *handler;
- int mask = 0, i, nitems;
- int current_item = 0;
-
- if (event->type == GDK_BUTTON_PRESS) {
-#ifdef USE_OLD_DISPLAY_STYLE
- if (event->button.button != 3) {
- gtk_propagate_event (GTK_WIDGET (user_data),
- (GdkEvent *)event);
- return TRUE;
- }
-#endif
-
- if (event->button.button != 1 && event->button.button != 3) {
- gtk_propagate_event (GTK_WIDGET (user_data),
- (GdkEvent *)event);
- return TRUE;
- }
- /* Stop the signal, since we don't want the button's class method to
- mess up our popup. */
- g_signal_stop_emission_by_name (widget, "button_press_event");
- } else {
- if (event->key.keyval != GDK_Return)
- return FALSE;
- }
-
- part = g_object_get_data ((GObject *) widget, "CamelMimePart");
- handler = mail_lookup_handler (g_object_get_data ((GObject *) widget, "mime_type"));
-
- if (handler && handler->applications)
- nitems = g_list_length (handler->applications) + 3;
- else
- nitems = 4;
- menu = g_new0 (EPopupMenu, nitems + 1);
-
- /* Save item */
- memcpy (&menu[current_item], &save_item, sizeof (menu[current_item]));
- menu[current_item].name = g_strdup (_(menu[current_item].name));
- current_item++;
-
- /* Save All item */
- memcpy (&menu[current_item], &save_all_item, sizeof (menu[current_item]));
- menu[current_item].name = g_strdup (_(menu[current_item].name));
- current_item++;
-
- /* Inline view item */
- memcpy (&menu[current_item], &view_item, sizeof (menu[current_item]));
- if (handler && handler->builtin) {
- md = g_object_get_data ((GObject *) widget, "MailDisplay");
-
- if (!mail_part_is_displayed_inline (part, md)) {
- if (handler->component) {
- Bonobo_ActivationProperty *prop;
- char *name;
-
- prop = bonobo_server_info_prop_find (handler->component, "name");
- if (!prop) {
- prop = bonobo_server_info_prop_find (handler->component,
- "description");
- }
- if (prop && prop->v._d == Bonobo_ACTIVATION_P_STRING)
- name = prop->v._u.value_string;
- else
- name = "bonobo";
- menu[current_item].name = g_strdup_printf (_("View Inline (via %s)"), name);
- } else
- menu[current_item].name = g_strdup (_(menu[current_item].name));
- } else
- menu[current_item].name = g_strdup (_("Hide"));
- } else {
- menu[current_item].name = g_strdup (_(menu[current_item].name));
- mask |= 2;
- }
- current_item++;
-
- /* External views */
- if (handler && handler->applications) {
- GnomeVFSMimeApplication *app;
- GList *apps;
- int i;
-
- apps = handler->applications;
- for (i = current_item; i < nitems; i++, apps = apps->next) {
- app = apps->data;
- memcpy (&menu[i], &open_item, sizeof (menu[i]));
- menu[i].name = g_strdup_printf (_(menu[i].name), app->name);
- current_item++;
- }
- } else {
- memcpy (&menu[current_item], &open_item, sizeof (menu[current_item]));
- menu[current_item].name = g_strdup_printf (_(menu[current_item].name), _("External Viewer"));
- mask |= 1;
- }
-
- gtk_menu = e_popup_menu_create (menu, mask, 0, widget);
- e_auto_kill_popup_menu_on_selection_done (gtk_menu);
-
- if (event->type == GDK_BUTTON_PRESS)
- gtk_menu_popup (gtk_menu, NULL, NULL, NULL, (gpointer)widget, event->button.button, event->button.time);
- else
- gtk_menu_popup (gtk_menu, NULL, NULL, popup_menu_placement_callback, (gpointer)widget, 0, event->key.time);
-
- for (i = 1; i < nitems; i++)
- g_free (menu[i].name);
- g_free (menu);
-
- return TRUE;
-}
-
-static gboolean
-pixbuf_uncache (gpointer key)
-{
- GdkPixbuf *pixbuf;
-
- pixbuf = g_hash_table_lookup (thumbnail_cache, key);
- g_object_unref (pixbuf);
- g_hash_table_remove (thumbnail_cache, key);
- g_free (key);
- return FALSE;
-}
-
-static gint
-pixbuf_gen_idle (struct _PixbufLoader *pbl)
-{
- GdkPixbuf *pixbuf, *mini;
- gboolean error = FALSE;
- char tmp[4096];
- int len, width, height, ratio;
- gpointer orig_key;
-
- /* Get the pixbuf from the cache */
- if (g_hash_table_lookup_extended (thumbnail_cache, pbl->cid,
- &orig_key, (gpointer *)&mini)) {
- width = gdk_pixbuf_get_width (mini);
- height = gdk_pixbuf_get_height (mini);
-
- gtk_image_set_from_pixbuf ((GtkImage *) pbl->pixmap, mini);
- gtk_widget_set_size_request (pbl->pixmap, width, height);
-
- /* Restart the cache-cleaning timer */
- g_source_remove_by_user_data (orig_key);
- g_timeout_add (5 * 60 * 1000, pixbuf_uncache, orig_key);
-
- if (pbl->loader) {
- gdk_pixbuf_loader_close (pbl->loader, NULL);
- g_object_unref (pbl->loader);
- camel_object_unref (pbl->mstream);
- }
-
- g_signal_handler_disconnect (pbl->eb, pbl->destroy_id);
- g_free (pbl->type);
- g_free (pbl->cid);
- g_free (pbl);
-
- return FALSE;
- }
-
- /* Not in cache, so get a pixbuf from the wrapper */
-
- if (!GTK_IS_WIDGET (pbl->pixmap)) {
- /* Widget has died */
- if (pbl->mstream)
- camel_object_unref (pbl->mstream);
-
- if (pbl->loader) {
- gdk_pixbuf_loader_close (pbl->loader, NULL);
- g_object_unref (pbl->loader);
- }
-
- g_signal_handler_disconnect (pbl->eb, pbl->destroy_id);
- g_free (pbl->type);
- g_free (pbl->cid);
- g_free (pbl);
-
- return FALSE;
- }
-
- if (pbl->mstream) {
- if (pbl->loader == NULL)
- pbl->loader = gdk_pixbuf_loader_new ();
-
- len = camel_stream_read (pbl->mstream, tmp, 4096);
- if (len > 0) {
- error = !gdk_pixbuf_loader_write (pbl->loader, tmp, len, NULL);
- if (!error)
- return TRUE;
- } else if (!camel_stream_eos (pbl->mstream))
- error = TRUE;
- }
-
- if (error || !pbl->mstream) {
- if (pbl->type)
- pixbuf = e_icon_for_mime_type (pbl->type, 24);
- else
- pixbuf = gdk_pixbuf_new_from_file (EVOLUTION_ICONSDIR "/pgp-signature-nokey.png", NULL);
- } else
- pixbuf = gdk_pixbuf_loader_get_pixbuf (pbl->loader);
-
- if (pixbuf == NULL) {
- /* pixbuf is non-existant */
- if (pbl->mstream)
- camel_object_unref (pbl->mstream);
-
- if (pbl->loader) {
- gdk_pixbuf_loader_close (pbl->loader, NULL);
- g_object_unref (pbl->loader);
- }
-
- g_signal_handler_disconnect (pbl->eb, pbl->destroy_id);
- g_free (pbl->type);
- g_free (pbl->cid);
- g_free (pbl);
-
- return FALSE;
- }
-
- width = gdk_pixbuf_get_width (pixbuf);
- height = gdk_pixbuf_get_height (pixbuf);
-
- if (width >= height) {
- if (width > 24) {
- ratio = width / 24;
- width = 24;
- height /= ratio;
- }
- } else {
- if (height > 24) {
- ratio = height / 24;
- height = 24;
- width /= ratio;
- }
- }
-
- mini = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
- if (error || !pbl->mstream)
- g_object_unref (pixbuf);
-
- gtk_image_set_from_pixbuf ((GtkImage *) pbl->pixmap, mini);
-
- /* Add the pixbuf to the cache */
- g_hash_table_insert (thumbnail_cache, pbl->cid, mini);
- g_timeout_add (5 * 60 * 1000, pixbuf_uncache, pbl->cid);
-
- g_signal_handler_disconnect (pbl->eb, pbl->destroy_id);
- if (pbl->loader) {
- gdk_pixbuf_loader_close (pbl->loader, NULL);
- g_object_unref (pbl->loader);
- camel_object_unref (pbl->mstream);
- }
-
- g_free (pbl->type);
- g_free (pbl);
-
- return FALSE;
-}
-
-/* Stop the idle function and free the pbl structure
- as the widget that the pixbuf was to be rendered to
- has died on us. */
-static void
-embeddable_destroy_cb (GtkObject *embeddable, struct _PixbufLoader *pbl)
-{
- g_idle_remove_by_data (pbl);
- if (pbl->mstream)
- camel_object_unref (pbl->mstream);
-
- if (pbl->loader) {
- gdk_pixbuf_loader_close (pbl->loader, NULL);
- g_object_unref (pbl->loader);
- }
-
- g_free (pbl->type);
- g_free (pbl->cid);
- g_free (pbl);
-};
-
-static GtkWidget *
-get_embedded_for_component (const char *iid, MailDisplay *md)
-{
- GtkWidget *embedded;
- BonoboControlFrame *control_frame;
- Bonobo_PropertyBag prop_bag;
-
- /*
- * First try a control.
- */
- embedded = bonobo_widget_new_control (iid, NULL);
- if (embedded == NULL) {
-#warning "what about bonobo_widget_new_subdoc?"
-#if 0
- /*
- * No control, try an embeddable instead.
- */
- embedded = bonobo_widget_new_subdoc (iid, NULL);
- if (embedded != NULL) {
- /* FIXME: as of bonobo 0.18, there's an extra
- * client_site dereference in the BonoboWidget
- * destruction path that we have to balance out to
- * prevent problems.
- */
- bonobo_object_ref (BONOBO_OBJECT (bonobo_widget_get_client_site (
- BONOBO_WIDGET (embedded))));
-
- return embedded;
- }
-#endif
- }
-
- if (embedded == NULL)
- return NULL;
-
- control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (embedded));
-
- prop_bag = bonobo_control_frame_get_control_property_bag (control_frame, NULL);
-
- if (prop_bag != CORBA_OBJECT_NIL) {
- CORBA_Environment ev;
- /*
- * Now we can take care of business. Currently, the only control
- * that needs something passed to it through a property bag is
- * the iTip control, and it needs only the From email address,
- * but perhaps in the future we can generalize this section of code
- * to pass a bunch of useful things to all embedded controls.
- */
- const CamelInternetAddress *from;
- char *from_address;
-
- CORBA_exception_init (&ev);
-
- from = camel_mime_message_get_from (md->current_message);
- from_address = camel_address_encode ((CamelAddress *) from);
- bonobo_property_bag_client_set_value_string (
- prop_bag, "from_address",
- from_address, &ev);
- g_free (from_address);
-
- Bonobo_Unknown_unref (prop_bag, &ev);
- CORBA_exception_free (&ev);
- }
-
- return embedded;
-}
-
-static void *
-save_url (MailDisplay *md, const char *url)
-{
- GHashTable *urls;
- CamelMimePart *part;
-
- urls = g_datalist_get_data (md->data, "part_urls");
- g_return_val_if_fail (url != NULL, NULL);
- g_return_val_if_fail (urls != NULL, NULL);
-
- part = g_hash_table_lookup (urls, url);
- if (part == NULL) {
- CamelDataWrapper *wrapper;
- CamelStream *stream = NULL;
- const char *name;
-
- /* See if it's some piece of cached data if it is then pretend it
- * is a mime part so that we can use the mime part saving routines.
- * It is gross but it keeps duplicated code to a minimum and helps
- * out with ref counting and the like.
- */
- name = strrchr (url, '/');
- name = name ? name : url;
-
- if (fetch_cache) {
- /* look in the soup cache */
- stream = camel_data_cache_get(fetch_cache, FETCH_HTTP_CACHE, url, NULL);
- } else {
- GByteArray *ba = NULL;
-
- urls = g_datalist_get_data (md->data, "data_urls");
- g_return_val_if_fail (urls != NULL, NULL);
-
- ba = g_hash_table_lookup (urls, url);
- if (ba) {
- /* we have to copy the data here since the ba may be long gone
- * by the time the user actually saves the file
- */
- stream = camel_stream_mem_new_with_buffer (ba->data, ba->len);
- }
- }
-
- if (stream) {
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_object_unref (stream);
- part = camel_mime_part_new ();
- camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
- camel_object_unref (wrapper);
- camel_mime_part_set_filename (part, name);
- }
- } else {
- camel_object_ref (part);
- }
-
- if (part) {
- CamelDataWrapper *data;
-
- g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
-
- data = camel_medium_get_content_object ((CamelMedium *)part);
- if (!mail_content_loaded (data, md, TRUE, NULL, NULL, NULL)) {
- return NULL;
- }
-
- save_part (part);
- camel_object_unref (part);
- return NULL;
- }
-
- g_warning ("Data for url: \"%s\" not found", url);
-
- return NULL;
-}
-
-static void
-drag_data_get_cb (GtkWidget *widget,
- GdkDragContext *drag_context,
- GtkSelectionData *selection_data,
- guint info,
- guint time,
- gpointer user_data)
-{
- CamelMimePart *part = user_data;
- const char *filename, *tmpdir;
- char *uri_list;
-
- switch (info) {
- case DND_TARGET_TYPE_TEXT_URI_LIST:
- /* Kludge around Nautilus requesting the same data many times */
- uri_list = g_object_get_data ((GObject *) widget, "uri-list");
- if (uri_list) {
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- uri_list, strlen (uri_list));
- return;
- }
-
- tmpdir = e_mkdtemp ("drag-n-drop-XXXXXX");
- if (!tmpdir) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_RESPONSE_CLOSE,
- _("Could not create temporary directory: %s"),
- g_strerror (errno));
-
- /* FIXME: this should be async */
- gtk_dialog_run ((GtkDialog *) dialog);
- gtk_widget_destroy (dialog);
- }
-
- filename = camel_mime_part_get_filename (part);
- /* This is the default filename used for dnd temporary target of attachment */
- if (!filename)
- filename = _("Unknown");
-
- uri_list = g_strdup_printf ("file://%s/%s", tmpdir, filename);
-
- if (!write_data_to_file (part, uri_list + 7, TRUE)) {
- g_free (uri_list);
- return;
- }
-
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- uri_list, strlen (uri_list));
-
- g_object_set_data_full ((GObject *) widget, "uri-list", uri_list, g_free);
- break;
- case DND_TARGET_TYPE_PART_MIME_TYPE:
- if (header_content_type_is (((CamelDataWrapper *) part)->mime_type, "text", "*")) {
- GByteArray *ba;
-
- ba = mail_format_get_data_wrapper_text ((CamelDataWrapper *) part, NULL);
- if (ba) {
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- ba->data, ba->len);
- g_byte_array_free (ba, TRUE);
- }
- } else {
- CamelDataWrapper *wrapper;
- CamelStreamMem *mem;
-
- mem = (CamelStreamMem *) camel_stream_mem_new ();
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mem);
-
- gtk_selection_data_set (selection_data, selection_data->target, 8,
- mem->buffer->data, mem->buffer->len);
-
- camel_object_unref (mem);
- }
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
-static void
-drag_data_delete_cb (GtkWidget *widget,
- GdkDragContext *drag_context,
- gpointer user_data)
-{
- char *uri_list;
-
- uri_list = g_object_get_data ((GObject *) widget, "uri-list");
- if (uri_list) {
- unlink (uri_list + 7);
- g_object_set_data ((GObject *) widget, "uri-list", NULL);
- }
-}
-
-/* This is a wrapper function */
-void ptr_array_free_notify (gpointer array)
-{
- g_ptr_array_free ((GPtrArray *) array, TRUE);
-}
-
-static gboolean
-do_attachment_header (GtkHTML *html, GtkHTMLEmbedded *eb,
- CamelMimePart *part, MailDisplay *md)
-{
- GtkWidget *button, *mainbox, *hbox, *arrow, *popup;
- MailMimeHandler *handler;
- struct _PixbufLoader *pbl;
- GPtrArray *attachment_array;
-
- pbl = g_new0 (struct _PixbufLoader, 1);
- if (strncasecmp (eb->type, "image/", 6) == 0) {
- CamelDataWrapper *content;
-
- content = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- if (!camel_data_wrapper_is_offline (content)) {
- pbl->mstream = camel_stream_mem_new ();
- camel_data_wrapper_decode_to_stream (content, pbl->mstream);
- camel_stream_reset (pbl->mstream);
- }
- }
-
- pbl->type = g_strdup (eb->type);
- pbl->cid = g_strdup (eb->classid + 6);
- pbl->pixmap = gtk_image_new();
- gtk_widget_set_size_request (pbl->pixmap, 24, 24);
- pbl->eb = eb;
- pbl->destroy_id = g_signal_connect (eb, "destroy", G_CALLBACK (embeddable_destroy_cb), pbl);
-
- g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) pixbuf_gen_idle, pbl, NULL);
-
- mainbox = gtk_hbox_new (FALSE, 0);
-
- button = gtk_button_new ();
- g_object_set_data ((GObject *) button, "MailDisplay", md);
-
- handler = mail_lookup_handler (eb->type);
- if (handler && handler->builtin) {
- g_signal_connect (button, "clicked", G_CALLBACK (inline_button_clicked), part);
- g_signal_connect (button, "key_press_event", G_CALLBACK (inline_button_press), part);
- } else {
- gtk_widget_set_sensitive (button, FALSE);
- GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
- }
-
- /* Drag & Drop */
- drag_types[DND_TARGET_TYPE_PART_MIME_TYPE].target = header_content_type_simple (((CamelDataWrapper *) part)->mime_type);
- camel_strdown (drag_types[DND_TARGET_TYPE_PART_MIME_TYPE].target);
-
- gtk_drag_source_set (button, GDK_BUTTON1_MASK,
- drag_types, num_drag_types,
- GDK_ACTION_COPY);
- g_signal_connect (button, "drag-data-get", G_CALLBACK (drag_data_get_cb), part);
- g_signal_connect (button, "drag-data-delete", G_CALLBACK (drag_data_delete_cb), part);
-
- g_free (drag_types[DND_TARGET_TYPE_PART_MIME_TYPE].target);
- drag_types[DND_TARGET_TYPE_PART_MIME_TYPE].target = NULL;
-
- hbox = gtk_hbox_new (FALSE, 2);
- gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
-
- /* should this be a gtk_arrow? */
- if (handler && mail_part_is_displayed_inline (part, md))
- arrow = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON);
- else
- arrow = gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_BUTTON);
- gtk_box_pack_start (GTK_BOX (hbox), arrow, TRUE, TRUE, 0);
- gtk_box_pack_start (GTK_BOX (hbox), pbl->pixmap, TRUE, TRUE, 0);
- gtk_container_add (GTK_CONTAINER (button), hbox);
-
- popup = gtk_button_new ();
- gtk_container_add (GTK_CONTAINER (popup),
- gtk_arrow_new (GTK_ARROW_DOWN,
- GTK_SHADOW_ETCHED_IN));
-
- g_object_set_data ((GObject *) popup, "MailDisplay", md);
- g_object_set_data ((GObject *) popup, "CamelMimePart", part);
- g_object_set_data_full ((GObject *) popup, "mime_type", g_strdup (eb->type), (GDestroyNotify) g_free);
-
- /* Save attachment pointer in an array for "save all attachment" use */
- attachment_array = g_datalist_get_data (md->data, "attachment_array");
- if (!attachment_array) {
- attachment_array = g_ptr_array_new ();
- g_datalist_set_data_full (md->data, "attachment_array",
- attachment_array, (GDestroyNotify) ptr_array_free_notify);
- }
- /* Since the attachment pointer might have been added to the array before,
- remove it first anyway to avoide duplication */
- g_ptr_array_remove (attachment_array, part);
- g_ptr_array_add (attachment_array, part);
-
-
- g_signal_connect (popup, "button_press_event", G_CALLBACK (pixmap_press), md->scroll);
- g_signal_connect (popup, "key_press_event", G_CALLBACK (pixmap_press), md->scroll);
-
- gtk_box_pack_start (GTK_BOX (mainbox), button, TRUE, TRUE, 0);
- gtk_box_pack_start (GTK_BOX (mainbox), popup, TRUE, TRUE, 0);
- gtk_widget_show_all (mainbox);
-
- gtk_container_add (GTK_CONTAINER (eb), mainbox);
-
- return TRUE;
-}
-
-static gboolean
-do_external_viewer (GtkHTML *html, GtkHTMLEmbedded *eb,
- CamelMimePart *part, MailDisplay *md)
-{
- CamelDataWrapper *wrapper;
- Bonobo_ServerInfo *component;
- GtkWidget *embedded;
- Bonobo_PersistStream persist;
- CORBA_Environment ev;
- CamelStreamMem *cstream;
- BonoboStream *bstream;
- MailMimeHandler *handler;
-
- handler = mail_lookup_handler (eb->type);
- if (!handler || !handler->is_bonobo)
- return FALSE;
-
- component = gnome_vfs_mime_get_default_component (eb->type);
- if (!component)
- return FALSE;
-
- embedded = get_embedded_for_component (component->iid, md);
- CORBA_free (component);
- if (!embedded)
- return FALSE;
-
- persist = (Bonobo_PersistStream) Bonobo_Unknown_queryInterface (
- bonobo_widget_get_objref (BONOBO_WIDGET (embedded)),
- "IDL:Bonobo/PersistStream:1.0", NULL);
-
- if (persist == CORBA_OBJECT_NIL) {
- gtk_object_sink (GTK_OBJECT (embedded));
- return FALSE;
- }
-
- /* Write the data to a CamelStreamMem... */
- cstream = (CamelStreamMem *) camel_stream_mem_new ();
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *)cstream);
-
- /* ...convert the CamelStreamMem to a BonoboStreamMem... */
- bstream = bonobo_stream_mem_create (cstream->buffer->data, cstream->buffer->len, TRUE, FALSE);
- camel_object_unref (cstream);
-
- /* ...and hydrate the PersistStream from the BonoboStream. */
- CORBA_exception_init (&ev);
- Bonobo_PersistStream_load (persist,
- bonobo_object_corba_objref (BONOBO_OBJECT (bstream)),
- eb->type, &ev);
- bonobo_object_unref (BONOBO_OBJECT (bstream));
- Bonobo_Unknown_unref (persist, &ev);
- CORBA_Object_release (persist, &ev);
-
- if (ev._major != CORBA_NO_EXCEPTION) {
- gtk_object_sink (GTK_OBJECT (embedded));
- CORBA_exception_free (&ev);
- return FALSE;
- }
- CORBA_exception_free (&ev);
-
- gtk_widget_show (embedded);
- gtk_container_add (GTK_CONTAINER (eb), embedded);
-
- return TRUE;
-}
-
-static gboolean
-do_signature (GtkHTML *html, GtkHTMLEmbedded *eb,
- CamelMimePart *part, MailDisplay *md)
-{
- GtkWidget *button;
- struct _PixbufLoader *pbl;
-
- pbl = g_new0 (struct _PixbufLoader, 1);
- pbl->type = NULL;
- pbl->cid = g_strdup (eb->classid);
- pbl->pixmap = gtk_image_new ();
- gtk_widget_set_size_request (pbl->pixmap, 24, 24);
- pbl->eb = eb;
- pbl->destroy_id = g_signal_connect (eb, "destroy", G_CALLBACK (embeddable_destroy_cb), pbl);
-
- g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) pixbuf_gen_idle, pbl, NULL);
-
- button = gtk_button_new ();
- g_object_set_data ((GObject *) button, "MailDisplay", md);
- g_signal_connect (button, "clicked", G_CALLBACK (inline_button_clicked), part);
- g_signal_connect (button, "key_press_event", G_CALLBACK (inline_button_press), part);
-
- gtk_container_add (GTK_CONTAINER (button), pbl->pixmap);
- gtk_widget_show_all (button);
- gtk_container_add (GTK_CONTAINER (eb), button);
-
- return TRUE;
-}
-
-static gboolean
-on_object_requested (GtkHTML *html, GtkHTMLEmbedded *eb, gpointer data)
-{
- MailDisplay *md = data;
- GHashTable *urls;
- CamelMimePart *part;
-
- if (!eb->classid)
- return FALSE;
-
- urls = g_datalist_get_data (md->data, "part_urls");
- if (!urls)
- return FALSE;
-
- if (!strncmp (eb->classid, "popup:", 6) && eb->type) {
- part = g_hash_table_lookup (urls, eb->classid + 6);
- if (!CAMEL_IS_MIME_PART (part))
- return FALSE;
- return do_attachment_header (html, eb, part, md);
- } else if (!strncmp (eb->classid, "signature:", 10)) {
- part = g_hash_table_lookup (urls, eb->classid);
- if (!CAMEL_IS_MIME_PART (part))
- return FALSE;
- return do_signature (html, eb, part, md);
- } else if (!strncmp (eb->classid, "cid:", 4) && eb->type) {
- part = g_hash_table_lookup (urls, eb->classid);
- if (!CAMEL_IS_MIME_PART (part))
- return FALSE;
- return do_external_viewer (html, eb, part, md);
- }
-
- return FALSE;
-}
-
-static void
-ebook_callback (EBook *book, const gchar *addr, ECard *card, gpointer data)
-{
- MailDisplay *md = data;
-
- if (card && md->current_message) {
- const CamelInternetAddress *from = camel_mime_message_get_from (md->current_message);
- const char *md_name = NULL, *md_addr = NULL;
-
- /* We are extra anal, in case we are dealing with some sort of pathological message
- w/o a From: header. */
- if (from != NULL && camel_internet_address_get (from, 0, &md_name, &md_addr)) {
- if (md_addr != NULL && !strcmp (addr, md_addr))
- mail_display_load_images (md);
- }
- }
-}
-
-static void
-on_url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle,
- gpointer user_data)
-{
- MailDisplay *md = user_data;
- GConfClient *gconf;
- GHashTable *urls;
- CamelMedium *medium;
- GByteArray *ba;
-
- gconf = mail_config_get_gconf_client ();
-
- urls = g_datalist_get_data (md->data, "part_urls");
- g_return_if_fail (urls != NULL);
-
- /* See if it refers to a MIME part (cid: or http:) */
- medium = g_hash_table_lookup (urls, url);
- if (medium) {
- CamelContentType *content_type;
- CamelDataWrapper *wrapper;
- CamelStream *html_stream;
-
- g_return_if_fail (CAMEL_IS_MEDIUM (medium));
-
- if (md->related)
- g_hash_table_remove (md->related, medium);
-
- wrapper = camel_medium_get_content_object (medium);
- if (!mail_content_loaded (wrapper, md, FALSE, url, html, handle))
- return;
-
- content_type = camel_data_wrapper_get_mime_type_field (wrapper);
-
- html_stream = mail_display_stream_new (html, handle);
-
- if (header_content_type_is (content_type, "text", "*")) {
- mail_format_data_wrapper_write_to_stream (wrapper, TRUE, md, html_stream);
- } else {
- camel_data_wrapper_decode_to_stream (wrapper, html_stream);
- }
-
- camel_object_unref (html_stream);
-
- gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
- return;
- }
-
- urls = g_datalist_get_data (md->data, "data_urls");
- g_return_if_fail (urls != NULL);
-
- /* See if it's some piece of cached data */
- ba = g_hash_table_lookup (urls, url);
- if (ba) {
- if (ba->len) {
- gtk_html_write (html, handle, ba->data, ba->len);
- /* printf ("-- begin --\n");
- printf (ba->data);
- printf ("-- end --\n"); */
- }
- gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
- return;
- }
-
- /* See if it's something we can load. */
- if (strncmp (url, "http:", 5) == 0 || strncmp (url, "https:", 6) == 0) {
- int http_mode;
-
- http_mode = gconf_client_get_int (gconf, "/apps/evolution/mail/display/load_http_images", NULL);
- if (http_mode == MAIL_CONFIG_HTTP_ALWAYS ||
- g_datalist_get_data (md->data, "load_images")) {
- fetch_remote (md, url, html, handle);
- } else if (http_mode == MAIL_CONFIG_HTTP_SOMETIMES &&
- !g_datalist_get_data (md->data, "checking_from")) {
- const CamelInternetAddress *from;
- const char *name, *addr;
-
- from = camel_mime_message_get_from (md->current_message);
- g_datalist_set_data (md->data, "checking_from", GINT_TO_POINTER (1));
-
- /* Make sure we aren't deal w/ some sort of a
- pathological message w/o a From: header */
- if (from != NULL && camel_internet_address_get (from, 0, &name, &addr))
- e_book_query_address_default (addr, ebook_callback, md);
- else
- gtk_html_end (html, handle, GTK_HTML_STREAM_ERROR);
- }
- }
-}
-
-/* for processing asynchronous url fetch cancels */
-static struct _mail_msg_op fetch_fake_op = {
- NULL, NULL, NULL, NULL,
-};
-
-static gboolean
-fetch_cancelled (GIOChannel *source, GIOCondition cond, void *user_data)
-{
- fetch_cancel ((MailDisplay *) user_data);
-
- return FALSE;
-}
-
-static void
-fetch_next (MailDisplay *md)
-{
- struct _remote_data *rd;
- struct _MailDisplayPrivate *p = md->priv;
- SoupMessage *msg;
- SoupContext *ctx;
-
- /* if we're called and no more work to do, clean up, otherwise, setup */
- if (e_dlist_empty(&p->fetch_active) && e_dlist_empty(&p->fetch_queue)) {
- if (p->fetch_msg) {
- p->fetch_total = 0;
- mail_disable_stop();
- camel_operation_end(p->fetch_msg->cancel);
- camel_operation_unregister(p->fetch_msg->cancel);
- mail_msg_free(p->fetch_msg);
- p->fetch_msg = NULL;
- g_source_remove(p->fetch_cancel_watch);
- g_io_channel_unref(p->fetch_cancel_channel);
- }
- } else {
- if (p->fetch_msg == NULL) {
- p->fetch_total_done = 0;
- p->fetch_msg = mail_msg_new(&fetch_fake_op, NULL, sizeof(*p->fetch_msg));
- camel_operation_register(p->fetch_msg->cancel);
- camel_operation_start(p->fetch_msg->cancel, _("Downloading images"));
- p->fetch_cancel_channel = g_io_channel_unix_new(camel_operation_cancel_fd(p->fetch_msg->cancel));
- p->fetch_cancel_watch = g_io_add_watch(p->fetch_cancel_channel, G_IO_IN, fetch_cancelled, md);
- mail_enable_stop();
- }
- }
-
- while (e_dlist_length(&p->fetch_active) < FETCH_MAX_CONNECTIONS
- && (rd = (struct _remote_data *)e_dlist_remhead(&p->fetch_queue))) {
-
- ctx = soup_context_get(rd->uri);
- rd->msg = msg = soup_message_new(ctx, SOUP_METHOD_GET);
-
- if (ctx)
- soup_context_unref(ctx);
-
- soup_message_set_flags(msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
- soup_message_add_handler(msg, SOUP_HANDLER_BODY_CHUNK, fetch_data, rd);
- e_dlist_addtail(&p->fetch_active, (EDListNode *)rd);
- soup_message_queue(msg, fetch_done, rd);
- }
-}
-
-static void fetch_remote(MailDisplay *md, const char *uri, GtkHTML *html, GtkHTMLStream *stream)
-{
- struct _remote_data *rd;
- CamelStream *cstream = NULL;
-
- if (fetch_cache) {
- cstream = camel_data_cache_get(fetch_cache, FETCH_HTTP_CACHE, uri, NULL);
- if (cstream) {
- char buf[1024];
- ssize_t len;
-
- /* need to verify header? */
-
- while (!camel_stream_eos(cstream)) {
- len = camel_stream_read(cstream, buf, 1024);
- if (len > 0) {
- gtk_html_write(html, stream, buf, len);
- } else if (len < 0) {
- gtk_html_end(html, stream, GTK_HTML_STREAM_ERROR);
- camel_object_unref(cstream);
- return;
- }
- }
- gtk_html_end(html, stream, GTK_HTML_STREAM_OK);
- camel_object_unref(cstream);
- return;
- }
- cstream = camel_data_cache_add(fetch_cache, FETCH_HTTP_CACHE, uri, NULL);
- }
-
- rd = g_malloc0(sizeof(*rd));
- rd->md = md; /* dont ref */
- rd->uri = g_strdup(uri);
- rd->html = html;
- g_object_ref(html);
- rd->stream = stream;
- rd->cstream = cstream;
-
- md->priv->fetch_total++;
- e_dlist_addtail(&md->priv->fetch_queue, (EDListNode *)rd);
-
- fetch_next(md);
-}
-
-static void fetch_data(SoupMessage *req, void *data)
-{
- struct _remote_data *rd = data, *wd;
- struct _MailDisplayPrivate *p = rd->md->priv;
- int count;
- double complete;
-
- /* we could just hook into the header function for this, but i'm lazy today */
- if (rd->total == 0) {
- const char *cl = soup_message_get_header(req->response_headers, "content-length");
- if (cl)
- rd->total = strtoul(cl, 0, 10);
- else
- rd->total = 0;
- }
- rd->length += req->response.length;
-
- gtk_html_write(rd->html, rd->stream, req->response.body, req->response.length);
-
- /* copy to cache, clear cache if we get a cache failure */
- if (rd->cstream) {
- if (camel_stream_write(rd->cstream, req->response.body, req->response.length) == -1) {
- camel_data_cache_remove(fetch_cache, FETCH_HTTP_CACHE, rd->uri, NULL);
- camel_object_unref(rd->cstream);
- rd->cstream = NULL;
- }
- }
-
- /* update based on total active + finished totals */
- complete = 0.0;
- wd = (struct _remote_data *)p->fetch_active.head;
- count = e_dlist_length(&p->fetch_active);
- while (wd->next) {
- if (wd->total)
- complete += (double)wd->length / wd->total / count;
- wd = wd->next;
- }
-
- d(printf("%s: %f total %f (%d,%d)\n", rd->uri, complete, (p->fetch_total_done + complete ) * 100.0 / p->fetch_total, p->fetch_total, p->fetch_total_done));
-
- camel_operation_progress(p->fetch_msg->cancel, (p->fetch_total_done + complete ) * 100.0 / p->fetch_total);
-}
-
-static void fetch_free(struct _remote_data *rd)
-{
- g_object_unref(rd->html);
- if (rd->cstream)
- camel_object_unref(rd->cstream);
- g_free(rd->uri);
- g_free(rd);
-}
-
-static void fetch_done(SoupMessage *req, void *data)
-{
- struct _remote_data *rd = data;
- MailDisplay *md = rd->md;
-
- if (SOUP_MESSAGE_IS_ERROR(req)) {
- d(printf("Loading '%s' failed!\n", rd->uri));
- gtk_html_end(rd->html, rd->stream, GTK_HTML_STREAM_ERROR);
- if (fetch_cache)
- camel_data_cache_remove(fetch_cache, FETCH_HTTP_CACHE, rd->uri, NULL);
- } else {
- d(printf("Loading '%s' complete!\n", rd->uri));
- gtk_html_end(rd->html, rd->stream, GTK_HTML_STREAM_OK);
- }
-
- e_dlist_remove((EDListNode *)rd);
- fetch_free(rd);
- md->priv->fetch_total_done++;
-
- fetch_next(md);
-}
-
-static void fetch_cancel(MailDisplay *md)
-{
- struct _remote_data *rd;
-
- /* first, clean up all the ones we haven't finished yet */
- while ((rd = (struct _remote_data *)e_dlist_remhead(&md->priv->fetch_queue))) {
- gtk_html_end(rd->html, rd->stream, GTK_HTML_STREAM_ERROR);
- if (fetch_cache)
- camel_data_cache_remove(fetch_cache, FETCH_HTTP_CACHE, rd->uri, NULL);
- fetch_free(rd);
- }
-
- /* cancel the rest, cancellation will free it/etc */
- while (!e_dlist_empty(&md->priv->fetch_active)) {
- rd = (struct _remote_data *)md->priv->fetch_active.head;
- soup_message_cancel(rd->msg);
- }
-}
-
-struct _load_content_msg {
- struct _mail_msg msg;
-
- MailDisplay *display;
- GtkHTML *html;
-
- GtkHTMLStream *handle;
- int redisplay_counter;
- char *url;
- CamelMimeMessage *message;
- void (*callback)(MailDisplay *, gpointer);
- gpointer data;
-};
-
-static char *
-load_content_desc (struct _mail_msg *mm, int done)
-{
- return g_strdup (_("Loading message content"));
-}
-
-static void
-load_content_load (struct _mail_msg *mm)
-{
- struct _load_content_msg *m = (struct _load_content_msg *)mm;
-
- m->callback (m->display, m->data);
-}
-
-static gboolean
-try_part_urls (struct _load_content_msg *m)
-{
- GHashTable *urls;
- CamelMedium *medium;
-
- urls = g_datalist_get_data (m->display->data, "part_urls");
- g_return_val_if_fail (urls != NULL, FALSE);
-
- /* See if it refers to a MIME part (cid: or http:) */
- medium = g_hash_table_lookup (urls, m->url);
- if (medium) {
- CamelDataWrapper *data;
- CamelStream *html_stream;
-
- g_return_val_if_fail (CAMEL_IS_MEDIUM (medium), FALSE);
-
- data = camel_medium_get_content_object (medium);
- if (!mail_content_loaded (data, m->display, FALSE, m->url, m->html, m->handle)) {
- g_warning ("This code should not be reached\n");
- return TRUE;
- }
-
- html_stream = mail_display_stream_new (m->html, m->handle);
- camel_data_wrapper_decode_to_stream (data, html_stream);
- camel_object_unref (html_stream);
-
- gtk_html_end (m->html, m->handle, GTK_HTML_STREAM_OK);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-try_data_urls (struct _load_content_msg *m)
-{
- GHashTable *urls;
- GByteArray *ba;
-
- urls = g_datalist_get_data (m->display->data, "data_urls");
- ba = g_hash_table_lookup (urls, m->url);
-
- if (ba) {
- if (ba->len)
- gtk_html_write (m->html, m->handle, ba->data, ba->len);
- gtk_html_end (m->html, m->handle, GTK_HTML_STREAM_OK);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-load_content_loaded (struct _mail_msg *mm)
-{
- struct _load_content_msg *m = (struct _load_content_msg *)mm;
-
- if (m->display->destroyed)
- return;
-
- if (m->display->current_message == m->message) {
- if (m->handle) {
- if (m->redisplay_counter == m->display->redisplay_counter) {
- if (!try_part_urls (m) && !try_data_urls (m))
- gtk_html_end (m->html, m->handle, GTK_HTML_STREAM_ERROR);
- }
- } else {
- mail_display_redisplay (m->display, FALSE);
- }
- }
-}
-
-static void
-load_content_free (struct _mail_msg *mm)
-{
- struct _load_content_msg *m = (struct _load_content_msg *)mm;
-
- g_free (m->url);
- g_object_unref (m->html);
- g_object_unref (m->display);
- camel_object_unref (m->message);
-}
-
-static struct _mail_msg_op load_content_op = {
- load_content_desc,
- load_content_load,
- load_content_loaded,
- load_content_free,
-};
-
-static void
-stream_write_or_redisplay_when_loaded (MailDisplay *md,
- GtkHTML *html,
- gconstpointer key,
- const gchar *url,
- void (*callback)(MailDisplay *, gpointer),
- GtkHTMLStream *handle,
- gpointer data)
-{
- struct _load_content_msg *m;
- GHashTable *loading;
-
- if (md->destroyed)
- return;
-
- loading = g_datalist_get_data (md->data, "loading");
- if (loading) {
- if (g_hash_table_lookup (loading, key))
- return;
- } else {
- loading = g_hash_table_new (NULL, NULL);
- g_datalist_set_data_full (md->data, "loading", loading,
- (GDestroyNotify) g_hash_table_destroy);
- }
- g_hash_table_insert (loading, (gpointer) key, GINT_TO_POINTER (1));
-
- m = mail_msg_new (&load_content_op, NULL, sizeof (*m));
- m->display = md;
- g_object_ref((m->display));
- m->html = html;
- g_object_ref((html));
- m->handle = handle;
- m->url = g_strdup (url);
- m->redisplay_counter = md->redisplay_counter;
- m->message = md->current_message;
- camel_object_ref (m->message);
- m->callback = callback;
- m->data = data;
-
- e_thread_put (mail_thread_queued, (EMsg *)m);
- return;
-}
-
-void
-mail_display_stream_write_when_loaded (MailDisplay *md,
- gconstpointer key,
- const char *url,
- void (*callback)(MailDisplay *, gpointer),
- GtkHTML *html,
- GtkHTMLStream *handle,
- gpointer data)
-{
- stream_write_or_redisplay_when_loaded (md, html, key, url, callback, handle, data);
-}
-
-void
-mail_display_redisplay_when_loaded (MailDisplay *md,
- gconstpointer key,
- void (*callback)(MailDisplay *, gpointer),
- GtkHTML *html,
- gpointer data)
-{
- stream_write_or_redisplay_when_loaded (md, html, key, NULL, callback, NULL, data);
-}
-
-void
-mail_text_write (MailDisplayStream *stream, MailDisplay *md, CamelMimePart *part,
- int idx, gboolean printing, const char *text)
-{
- CamelStreamFilter *filtered_stream;
- CamelMimeFilter *html_filter;
- GConfClient *gconf;
- guint32 flags, rgb;
- GdkColor colour;
- char *buf;
-
- gconf = mail_config_get_gconf_client ();
-
- flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
-
- if (!printing)
- flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
-
- if (!printing && gconf_client_get_bool (gconf, "/apps/evolution/mail/display/mark_citations", NULL))
- flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
-
- buf = gconf_client_get_string (gconf, "/apps/evolution/mail/display/citation_colour", NULL);
- gdk_color_parse (buf ? buf : "#737373", &colour);
- g_free (buf);
-
- rgb = ((colour.red & 0xff00) << 8) | (colour.green & 0xff00) | ((colour.blue & 0xff00) >> 8);
- html_filter = camel_mime_filter_tohtml_new (flags, rgb);
- filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
- camel_stream_filter_add (filtered_stream, html_filter);
- camel_object_unref (html_filter);
-
- camel_stream_write ((CamelStream *) stream, "<tt>\n", 5);
- camel_stream_write ((CamelStream *) filtered_stream, text, strlen (text));
- camel_stream_flush ((CamelStream *) filtered_stream);
- camel_stream_write ((CamelStream *) stream, "</tt>\n", 6);
- camel_object_unref (filtered_stream);
-
-#if 0
- /* this was the old way of doing it, I don't understand why we need iframes... */
- GByteArray *ba;
- char *xed, *iframe;
- char *btt = "<tt>\n";
- char *ett = "</tt>\n";
- char *htmltext;
- guint32 flags;
-
- flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
-
- if (!printing)
- flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
-
- if (!printing && mail_config_get_citation_highlight ())
- flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
-
- htmltext = camel_text_to_html (text, flags, mail_config_get_citation_color ());
-
- ba = g_byte_array_new ();
- g_byte_array_append (ba, (const guint8 *) btt, strlen (btt) + 1);
- g_byte_array_append (ba, (const guint8 *) htmltext, strlen (htmltext) + 1);
- g_byte_array_append (ba, (const guint8 *) ett, strlen (ett) + 1);
- g_free (htmltext);
-
- xed = g_strdup_printf ("x-evolution-data:%p-%d", part, idx);
- iframe = g_strdup_printf ("<iframe src=\"%s\" frameborder=0 scrolling=no>could not get %s</iframe>", xed, xed);
- mail_display_add_url (md, "data_urls", xed, ba);
- camel_stream_write ((CamelStream *) stream, iframe, strlen (iframe));
- g_free (iframe);
-#endif
-}
-
-void
-mail_error_printf (MailDisplayStream *stream, const char *format, ...)
-{
- /* FIXME: it'd be nice if camel-stream had a vprintf method... */
- char *buf, *htmltext;
- va_list ap;
-
- va_start (ap, format);
- buf = g_strdup_vprintf (format, ap);
- va_end (ap);
-
- htmltext = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- g_free (buf);
-
- camel_stream_printf ((CamelStream *) stream, "<em><font color=red>");
- camel_stream_write ((CamelStream *) stream, htmltext, strlen (htmltext));
- camel_stream_printf ((CamelStream *) stream, "</font></em>");
-
- g_free (htmltext);
-}
-
-
-#define COLOR_IS_LIGHT(r, g, b) ((r + g + b) > (128 * 3))
-
-#define HTML_HEADER "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n" \
- "<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\">\n</head>\n"
-
-void
-mail_display_render (MailDisplay *md, GtkHTML *html, gboolean reset_scroll)
-{
- const char *flag, *completed;
- GtkHTMLStream *html_stream;
- MailDisplayStream *stream;
-
- g_return_if_fail (IS_MAIL_DISPLAY (md));
- g_return_if_fail (GTK_IS_HTML (html));
-
- if (!md->html) {
- /* we've been destroyed */
- return;
- }
-
- html_stream = gtk_html_begin (html);
- if (!reset_scroll) {
- /* This is a hack until there's a clean way to do this. */
- GTK_HTML (md->html)->engine->newPage = FALSE;
- }
-
- gtk_html_stream_write (html_stream, HTML_HEADER, sizeof (HTML_HEADER) - 1);
-
- if (md->current_message && md->display_style == MAIL_CONFIG_DISPLAY_SOURCE)
- gtk_html_stream_write (html_stream, "<body>\n", 7);
- else
- gtk_html_stream_write (html_stream, "<body marginwidth=0 marginheight=0>\n", 36);
-
- flag = md->info ? camel_tag_get (&md->info->user_tags, "follow-up") : NULL;
- completed = md->info ? camel_tag_get (&md->info->user_tags, "completed-on") : NULL;
- if ((flag && *flag) && !(completed && *completed)) {
- const char *due_by, *overdue = "";
- char bgcolor[7], fontcolor[7];
- time_t target_date, now;
- GtkStyle *style = NULL;
- char due_date[256];
- struct tm due;
- int offset;
-
- /* my favorite thing to do... muck around with colors so we respect people's stupid themes. */
- /* FIXME: this is also in mail-format.c */
- style = gtk_widget_get_style (GTK_WIDGET (html));
- if (style && !md->printing) {
- int state = GTK_WIDGET_STATE (GTK_WIDGET (html));
- gushort r, g, b;
-
- r = style->base[state].red / 256;
- g = style->base[state].green / 256;
- b = style->base[state].blue / 256;
-
- if (COLOR_IS_LIGHT (r, g, b)) {
- r *= 1.0;
- g *= 0.97;
- b *= 0.75;
- } else {
- r = 255 - (1.0 * (255 - r));
- g = 255 - (0.97 * (255 - g));
- b = 255 - (0.75 * (255 - b));
- }
-
- sprintf (bgcolor, "%.2X%.2X%.2X", r, g, b);
-
- r = style->text[state].red / 256;
- g = style->text[state].green / 256;
- b = style->text[state].blue / 256;
-
- sprintf (fontcolor, "%.2X%.2X%.2X", r, g, b);
- } else {
- strcpy (bgcolor, "EEEEEE");
- strcpy (fontcolor, "000000");
- }
-
- due_by = camel_tag_get (&md->info->user_tags, "due-by");
- if (due_by && *due_by) {
- target_date = header_decode_date (due_by, &offset);
- now = time (NULL);
- if (now >= target_date)
- overdue = _("Overdue:");
-
- localtime_r (&target_date, &due);
-
- e_utf8_strftime_fix_am_pm (due_date, sizeof (due_date), _("by %B %d, %Y, %l:%M %p"), &due);
- } else {
- due_date[0] = '\0';
- }
-
- gtk_html_stream_printf (html_stream, "<font color=\"#%s\">"
- "<table width=\"100%%\" cellpadding=0 cellspacing=0><tr><td colspan=3 height=10></td></tr>"
- "<tr><td width=10></td><td>"
- "<table cellspacing=1 cellpadding=1 bgcolor=\"#000000\" width=\"100%%\"><tr><td>"
- "<table cellspacing=0 bgcolor=\"#%s\" cellpadding=2 cellspacing=2 width=\"100%%\">"
- "<tr><td align=\"left\" width=20><img src=\"%s\" align=\"middle\"></td>"
- "<td>%s%s%s%s %s</td></table></td></tr></table>"
- "</td><td width=10></td></tr></table></font>", fontcolor, bgcolor,
- mail_display_get_url_for_icon (md, EVOLUTION_IMAGES "/flag-for-followup-16.png"),
- overdue ? "<b>" : "", overdue, overdue ? "</b>&nbsp;" : "",
- flag, due_date);
- }
-
- if (md->current_message) {
- stream = (MailDisplayStream *) mail_display_stream_new (html, html_stream);
-
- if (md->display_style == MAIL_CONFIG_DISPLAY_SOURCE)
- mail_format_raw_message (md->current_message, md, stream);
- else
- mail_format_mime_message (md->current_message, md, stream);
-
- camel_object_unref (stream);
- }
-
- gtk_html_stream_write (html_stream, "</body></html>\n", 15);
- gtk_html_end (html, html_stream, GTK_HTML_STREAM_OK);
-}
-
-/**
- * mail_display_redisplay:
- * @mail_display: the mail display object
- * @reset_scroll: specifies whether or not to reset current scroll
- *
- * Force a redraw of the message display.
- **/
-void
-mail_display_redisplay (MailDisplay *md, gboolean reset_scroll)
-{
- if (md->destroyed)
- return;
-
- /* we're in effect stealing the queued redisplay */
- if (md->idle_id) {
- g_source_remove(md->idle_id);
- md->idle_id = 0;
- }
-
- fetch_cancel(md);
-
- md->last_active = NULL;
- md->redisplay_counter++;
- /* printf ("md %p redisplay %d\n", md, md->redisplay_counter); */
-
- mail_display_render (md, md->html, reset_scroll);
-}
-
-
-/**
- * mail_display_set_message:
- * @mail_display: the mail display object
- * @medium: the input camel medium, or %NULL
- * @folder: CamelFolder
- * @info: message info
- *
- * Makes the mail_display object show the contents of the medium
- * param.
- **/
-void
-mail_display_set_message (MailDisplay *md, CamelMedium *medium, CamelFolder *folder, CamelMessageInfo *info)
-{
- /* For the moment, we deal only with CamelMimeMessage, but in
- * the future, we should be able to deal with any medium.
- */
- if (md->destroyed
- || (medium && !CAMEL_IS_MIME_MESSAGE (medium)))
- return;
-
- /* Clean up from previous message. */
- if (md->current_message) {
- fetch_cancel (md);
- camel_object_unref (md->current_message);
- g_datalist_clear (md->data);
- }
-
- if (medium) {
- camel_object_ref (medium);
- md->current_message = (CamelMimeMessage *) medium;
- } else
- md->current_message = NULL;
-
- if (md->folder && md->info) {
- camel_folder_free_message_info (md->folder, md->info);
- camel_object_unref (md->folder);
- }
-
- if (folder && info) {
- md->info = info;
- md->folder = folder;
- camel_object_ref (folder);
- camel_folder_ref_message_info (folder, info);
- } else {
- md->info = NULL;
- md->folder = NULL;
- }
-
- g_datalist_init (md->data);
- mail_display_redisplay (md, TRUE);
-}
-
-/**
- * mail_display_set_charset:
- * @mail_display: the mail display object
- * @charset: charset or %NULL
- *
- * Makes the mail_display object show the contents of the medium
- * param.
- **/
-void
-mail_display_set_charset (MailDisplay *mail_display, const char *charset)
-{
- g_free (mail_display->charset);
- mail_display->charset = g_strdup (charset);
-
- mail_display_queue_redisplay (mail_display);
-}
-
-/**
- * mail_display_load_images:
- * @md: the mail display object
- *
- * Load all HTTP images in the current message
- **/
-void
-mail_display_load_images (MailDisplay *md)
-{
- g_datalist_set_data (md->data, "load_images", GINT_TO_POINTER (1));
- mail_display_redisplay (md, FALSE);
-}
-
-/*----------------------------------------------------------------------*
- * Standard Gtk+ Class functions
- *----------------------------------------------------------------------*/
-
-static void
-mail_display_init (GObject *object)
-{
- MailDisplay *mail_display = MAIL_DISPLAY (object);
- GConfClient *gconf;
- int style;
-
- mail_display->scroll = NULL;
- mail_display->html = NULL;
- mail_display->redisplay_counter = 0;
- mail_display->last_active = NULL;
- mail_display->idle_id = 0;
- mail_display->selection = NULL;
- mail_display->charset = NULL;
- mail_display->current_message = NULL;
- mail_display->folder = NULL;
- mail_display->info = NULL;
- mail_display->data = NULL;
-
- mail_display->invisible = gtk_invisible_new ();
- g_object_ref (mail_display->invisible);
- gtk_object_sink ((GtkObject *) mail_display->invisible);
-
- gconf = mail_config_get_gconf_client ();
- style = gconf_client_get_int (gconf, "/apps/evolution/mail/format/message_display_style", NULL);
- mail_display->display_style = style;
-
- mail_display->printing = FALSE;
-
- mail_display->priv = g_malloc0(sizeof(*mail_display->priv));
- e_dlist_init(&mail_display->priv->fetch_active);
- e_dlist_init(&mail_display->priv->fetch_queue);
-}
-
-static void
-mail_display_destroy (GtkObject *object)
-{
- MailDisplay *mail_display = MAIL_DISPLAY (object);
-
- if (mail_display->html) {
- g_object_unref (mail_display->html);
- mail_display->html = NULL;
- }
-
- if (mail_display->current_message) {
- camel_object_unref (mail_display->current_message);
- g_datalist_clear (mail_display->data);
- fetch_cancel(mail_display);
- mail_display->current_message = NULL;
- }
-
- g_free (mail_display->charset);
- mail_display->charset = NULL;
- g_free (mail_display->selection);
- mail_display->selection = NULL;
-
- if (mail_display->folder) {
- if (mail_display->info)
- camel_folder_free_message_info (mail_display->folder, mail_display->info);
- camel_object_unref (mail_display->folder);
- mail_display->folder = NULL;
- }
-
- g_free (mail_display->data);
- mail_display->data = NULL;
-
- if (mail_display->idle_id) {
- gtk_timeout_remove (mail_display->idle_id);
- mail_display->idle_id = 0;
- }
-
- if (mail_display->invisible) {
- g_object_unref (mail_display->invisible);
- mail_display->invisible = NULL;
- }
-
- if (mail_display->priv && mail_display->priv->display_notify_id) {
- GConfClient *gconf = mail_config_get_gconf_client ();
- gconf_client_notify_remove (gconf, mail_display->priv->display_notify_id);
- mail_display->priv->display_notify_id = 0;
- }
-
- g_free (mail_display->priv);
- mail_display->priv = NULL;
-
- mail_display->destroyed = TRUE;
-
- mail_display_parent_class->destroy (object);
-}
-
-static void
-invisible_selection_get_callback (GtkWidget *widget,
- GtkSelectionData *selection_data,
- guint info,
- guint time,
- void *data)
-{
- MailDisplay *display;
-
- display = MAIL_DISPLAY (data);
-
- if (!display->selection)
- return;
-
- g_assert (info == 1);
-
- gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8,
- display->selection, strlen (display->selection));
-}
-
-static gint
-invisible_selection_clear_event_callback (GtkWidget *widget,
- GdkEventSelection *event,
- void *data)
-{
- MailDisplay *display;
-
- display = MAIL_DISPLAY (data);
-
- g_free (display->selection);
- display->selection = NULL;
-
- return TRUE;
-}
-
-static void
-mail_display_class_init (GtkObjectClass *object_class)
-{
- object_class->destroy = mail_display_destroy;
-
- if (mail_display_parent_class == NULL) {
- /* blah, this is an unecessary dependency ... */
- extern char *evolution_dir;
- char *path;
-
- path = g_alloca (strlen (evolution_dir) + 16);
- sprintf (path, "%s/cache", evolution_dir);
-
- /* cache expiry - 2 hour access, 1 day max */
- fetch_cache = camel_data_cache_new(path, 0, NULL);
- camel_data_cache_set_expire_age(fetch_cache, 24*60*60);
- camel_data_cache_set_expire_access(fetch_cache, 2*60*60);
-
- mail_display_parent_class = g_type_class_ref (PARENT_TYPE);
- thumbnail_cache = g_hash_table_new (g_str_hash, g_str_equal);
- }
-}
-
-static void
-link_open_in_browser (GtkWidget *w, MailDisplay *mail_display)
-{
- if (!mail_display->html->pointer_url)
- return;
-
- on_link_clicked (mail_display->html, mail_display->html->pointer_url,
- mail_display);
-}
-
-#if 0
-static void
-link_save_as (GtkWidget *w, MailDisplay *mail_display)
-{
- g_print ("FIXME save %s\n", mail_display->html->pointer_url);
-}
-#endif
-
-static void
-link_copy_location (GtkWidget *w, MailDisplay *mail_display)
-{
- GdkAtom clipboard_atom;
-
- g_free (mail_display->selection);
- mail_display->selection = g_strdup (mail_display->html->pointer_url);
-
- clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
- if (clipboard_atom == GDK_NONE)
- return; /* failed */
-
- /* We don't check the return values of the following since there is not
- * much we can do if we cannot assert the selection.
- */
-
- gtk_selection_owner_set (GTK_WIDGET (mail_display->invisible),
- GDK_SELECTION_PRIMARY,
- GDK_CURRENT_TIME);
- gtk_selection_owner_set (GTK_WIDGET (mail_display->invisible),
- clipboard_atom,
- GDK_CURRENT_TIME);
-}
-
-static void
-image_save_as (GtkWidget *w, MailDisplay *mail_display)
-{
- const char *src;
-
- src = g_object_get_data ((GObject *) mail_display, "current_src_uri");
-
- save_url (mail_display, src);
-}
-
-enum {
- /*
- * This is used to mask the link specific menu items.
- */
- MASK_URL = 1,
-
- /*
- * This is used to mask src specific menu items.
- */
- MASK_SRC = 2
-};
-
-#define SEPARATOR { "", NULL, (NULL), NULL, 0 }
-#define TERMINATOR { NULL, NULL, (NULL), NULL, 0 }
-
-static EPopupMenu link_menu [] = {
- E_POPUP_ITEM (N_("Open Link in Browser"), G_CALLBACK (link_open_in_browser), MASK_URL),
- E_POPUP_ITEM (N_("Copy Link Location"), G_CALLBACK (link_copy_location), MASK_URL),
-#if 0
- E_POPUP_ITEM (N_("Save Link as (FIXME)"), G_CALLBACK (link_save_as), MASK_URL),
-#endif
- E_POPUP_ITEM (N_("Save Image as..."), G_CALLBACK (image_save_as), MASK_SRC),
-
- TERMINATOR
-};
-
-
-/*
- * Create a window and popup our widget, with reasonable semantics for the popup
- * disappearing, etc.
- */
-
-typedef struct _PopupInfo PopupInfo;
-struct _PopupInfo {
- GtkWidget *w;
- GtkWidget *win;
- guint destroy_timeout;
- guint widget_destroy_handle;
- Bonobo_Listener listener;
- gboolean hidden;
-};
-
-/* Aiieee! Global Data! */
-static GtkWidget *the_popup = NULL;
-
-static void
-popup_window_destroy_cb (PopupInfo *pop, GObject *deadbeef)
-{
- the_popup = NULL;
-
- if (pop->destroy_timeout != 0)
- g_source_remove(pop->destroy_timeout);
-
- bonobo_event_source_client_remove_listener (bonobo_widget_get_objref (BONOBO_WIDGET (pop->w)),
- pop->listener,
- NULL);
- CORBA_Object_release (pop->listener, NULL);
- g_object_unref(pop->w);
- g_free (pop);
-}
-
-static int
-popup_timeout_cb (gpointer user_data)
-{
- PopupInfo *pop = (PopupInfo *) user_data;
-
- pop->destroy_timeout = 0;
- gtk_widget_destroy (pop->win);
-
- return 0;
-}
-
-static int
-popup_enter_cb (GtkWidget *w, GdkEventCrossing *ev, gpointer user_data)
-{
- PopupInfo *pop = (PopupInfo *) user_data;
-
- if (pop->destroy_timeout)
- gtk_timeout_remove (pop->destroy_timeout);
- pop->destroy_timeout = 0;
-
- return 0;
-}
-
-static int
-popup_leave_cb (GtkWidget *w, GdkEventCrossing *ev, gpointer user_data)
-{
- PopupInfo *pop = (PopupInfo *) user_data;
-
- if (pop->destroy_timeout)
- gtk_timeout_remove (pop->destroy_timeout);
-
- if (!pop->hidden)
- pop->destroy_timeout = gtk_timeout_add (500, popup_timeout_cb, pop);
-
- return 0;
-}
-
-static void
-popup_realize_cb (GtkWidget *widget, gpointer user_data)
-{
- PopupInfo *pop = (PopupInfo *) user_data;
-
- gtk_widget_add_events (pop->win, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
-
- if (pop->destroy_timeout == 0) {
- if (!pop->hidden) {
- pop->destroy_timeout = gtk_timeout_add (5000, popup_timeout_cb, pop);
- } else {
- pop->destroy_timeout = 0;
- }
- }
-}
-
-static void
-popup_size_allocate_cb (GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
-{
- gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_MOUSE);
-}
-
-static PopupInfo *
-make_popup_window (GtkWidget *w)
-{
- PopupInfo *pop = g_new0 (PopupInfo, 1);
- GtkWidget *fr;
-
- /* Only allow for one popup at a time. Ugly. */
- if (the_popup)
- gtk_widget_destroy (the_popup);
-
- pop->w = w;
- g_object_ref(w);
- the_popup = pop->win = gtk_window_new (GTK_WINDOW_POPUP);
- fr = gtk_frame_new (NULL);
-
- gtk_container_add (GTK_CONTAINER (pop->win), fr);
- gtk_container_add (GTK_CONTAINER (fr), w);
-
- gtk_window_set_resizable (GTK_WINDOW (pop->win), FALSE);
-
- g_signal_connect (pop->win, "enter_notify_event", G_CALLBACK (popup_enter_cb), pop);
- g_signal_connect (pop->win, "leave_notify_event", G_CALLBACK (popup_leave_cb), pop);
- g_signal_connect_after (pop->win, "realize", G_CALLBACK (popup_realize_cb), pop);
- g_signal_connect (pop->win, "size_allocate", G_CALLBACK (popup_size_allocate_cb), pop);
-
- g_object_weak_ref ((GObject *) pop->win, (GWeakNotify) popup_window_destroy_cb, pop);
-
- gtk_widget_show (w);
- gtk_widget_show (fr);
- gtk_widget_show (pop->win);
-
- return pop;
-}
-
-static void
-listener_cb (BonoboListener *listener,
- char *event_name,
- CORBA_any *any,
- CORBA_Environment *ev,
- gpointer user_data)
-{
- PopupInfo *pop;
- char *type;
-
- pop = user_data;
-
- if (pop->destroy_timeout)
- gtk_timeout_remove (pop->destroy_timeout);
- pop->destroy_timeout = 0;
-
- type = bonobo_event_subtype (event_name);
-
- if (!strcmp (type, "Destroy")) {
- gtk_widget_destroy (GTK_WIDGET (pop->win));
- } else if (!strcmp (type, "Hide")) {
- pop->hidden = TRUE;
- gtk_widget_hide (GTK_WIDGET (pop->win));
- }
-
- g_free (type);
-}
-
-static int
-html_button_press_event (GtkWidget *widget, GdkEventButton *event, MailDisplay *mail_display)
-{
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- if (event->type == GDK_BUTTON_PRESS) {
- if (event->button == 3) {
- HTMLEngine *e;
- HTMLPoint *point;
- GtkWidget *popup_thing;
-
- e = GTK_HTML (widget)->engine;
- point = html_engine_get_point_at (e, event->x, event->y, FALSE);
-
- if (point) {
- const char *url, *src;
-
- url = html_object_get_url (point->object, point->offset);
- src = html_object_get_src (point->object);
-
- if (url && !strncasecmp (url, "mailto:", 7)) {
- PopupInfo *pop;
- char *url_decoded;
-
- url_decoded = gtk_html_get_url_object_relative (GTK_HTML (widget),
- point->object,
- url);
- camel_url_decode (url_decoded);
-
- popup_thing = bonobo_widget_new_control ("OAFIID:GNOME_Evolution_Addressbook_AddressPopup",
- CORBA_OBJECT_NIL);
-
- bonobo_widget_set_property (BONOBO_WIDGET (popup_thing),
- "email", TC_CORBA_string, url_decoded+7,
- NULL);
- g_free (url_decoded);
-
- pop = make_popup_window (popup_thing);
-
- pop->listener = bonobo_event_source_client_add_listener_full(
- bonobo_widget_get_objref (BONOBO_WIDGET (popup_thing)),
- g_cclosure_new (G_CALLBACK (listener_cb), pop, NULL),
- NULL, NULL);
- } else if (url || src) {
- int hide_mask = 0;
-
- if (!url)
- hide_mask |= MASK_URL;
-
- if (!src)
- hide_mask |= MASK_SRC;
-
- g_free (g_object_get_data ((GObject *) mail_display, "current_src_uri"));
- g_object_set_data ((GObject *) mail_display, "current_src_uri",
- gtk_html_get_url_object_relative (GTK_HTML (widget),
- point->object,
- src));
-
- e_popup_menu_run (link_menu, (GdkEvent *) event, 0, hide_mask, mail_display);
- }
-
- html_point_destroy (point);
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-static inline void
-set_underline (HTMLEngine *e, HTMLObject *o, gboolean underline)
-{
- HTMLText *text = HTML_TEXT (o);
-
- html_text_set_font_style (text, e, underline
- ? html_text_get_font_style (text) | GTK_HTML_FONT_STYLE_UNDERLINE
- : html_text_get_font_style (text) & ~GTK_HTML_FONT_STYLE_UNDERLINE);
- html_engine_queue_draw (e, o);
-}
-
-static void
-update_active (GtkWidget *widget, gint x, gint y, MailDisplay *mail_display)
-{
- HTMLEngine *e;
- HTMLPoint *point;
- const gchar *email;
-
- e = GTK_HTML (widget)->engine;
-
- point = html_engine_get_point_at (e, x, y, FALSE);
- if (mail_display->last_active && (!point || mail_display->last_active != point->object)) {
- set_underline (e, HTML_OBJECT (mail_display->last_active), FALSE);
- mail_display->last_active = NULL;
- }
- if (point) {
- email = (const gchar *) html_object_get_data (point->object, "email");
- if (email && html_object_is_text (point->object)) {
- set_underline (e, point->object, TRUE);
- mail_display->last_active = point->object;
- }
- html_point_destroy (point);
- }
-}
-
-static int
-html_enter_notify_event (GtkWidget *widget, GdkEventCrossing *event, MailDisplay *mail_display)
-{
- update_active (widget, event->x, event->y, mail_display);
-
- return FALSE;
-}
-
-static int
-html_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, MailDisplay *mail_display)
-{
- int x, y;
-
- g_return_val_if_fail (widget != NULL, 0);
- g_return_val_if_fail (GTK_IS_HTML (widget), 0);
- g_return_val_if_fail (event != NULL, 0);
-
- if (event->is_hint)
- gdk_window_get_pointer (GTK_LAYOUT (widget)->bin_window, &x, &y, NULL);
- else {
- x = event->x;
- y = event->y;
- }
-
- update_active (widget, x, y, mail_display);
-
- return FALSE;
-}
-
-static void
-html_iframe_created (GtkWidget *w, GtkHTML *iframe, MailDisplay *mail_display)
-{
- g_signal_connect (iframe, "button_press_event",
- G_CALLBACK (html_button_press_event), mail_display);
- g_signal_connect (iframe, "motion_notify_event",
- G_CALLBACK (html_motion_notify_event), mail_display);
- g_signal_connect (iframe, "enter_notify_event",
- G_CALLBACK (html_enter_notify_event), mail_display);
-}
-
-static GNOME_Evolution_ShellView
-retrieve_shell_view_interface_from_control (BonoboControl *control)
-{
- Bonobo_ControlFrame control_frame;
- GNOME_Evolution_ShellView shell_view_interface;
- CORBA_Environment ev;
-
- control_frame = bonobo_control_get_control_frame (control, NULL);
-
- if (control_frame == NULL)
- return CORBA_OBJECT_NIL;
-
- CORBA_exception_init (&ev);
- shell_view_interface = Bonobo_Unknown_queryInterface (control_frame,
- "IDL:GNOME/Evolution/ShellView:1.0",
- &ev);
-
- if (BONOBO_EX (&ev))
- shell_view_interface = CORBA_OBJECT_NIL;
-
- CORBA_exception_free (&ev);
-
- return shell_view_interface;
-}
-
-static void
-set_status_message (const char *message, int busy)
-{
- EList *controls;
- EIterator *it;
-
- controls = folder_browser_factory_get_control_list ();
- for (it = e_list_get_iterator (controls); e_iterator_is_valid (it); e_iterator_next (it)) {
- BonoboControl *control;
- GNOME_Evolution_ShellView shell_view_interface;
- CORBA_Environment ev;
-
- control = BONOBO_CONTROL (e_iterator_get (it));
-
- shell_view_interface = retrieve_shell_view_interface_from_control (control);
-
- CORBA_exception_init (&ev);
-
- if (shell_view_interface != CORBA_OBJECT_NIL) {
- if (message != NULL)
- GNOME_Evolution_ShellView_setMessage (shell_view_interface,
- message[0] ? message: "",
- busy,
- &ev);
- }
-
- CORBA_exception_free (&ev);
-
- bonobo_object_release_unref (shell_view_interface, NULL);
-
- /* yeah we only set the first one. Why? Because it seems to leave
- random ones lying around otherwise. Shrug. */
- break;
- }
-
- g_object_unref (it);
-}
-
-/* For now show every url but possibly limit it to showing only http:
- or ftp: urls */
-static void
-html_on_url (GtkHTML *html, const char *url, MailDisplay *mail_display)
-{
- static char *previous_url = NULL;
-
- /* This all looks silly but yes, this is the proper way to mix
- GtkHTML's on_url with BonoboUIComponent statusbar */
- if (!url || (previous_url && (strcmp (url, previous_url) != 0)))
- set_status_message ("", FALSE);
- if (url) {
- set_status_message (url, FALSE);
- g_free (previous_url);
- previous_url = g_strdup (url);
- }
-}
-
-/* If if a gconf setting for the mail display has changed redisplay to pick up the changes */
-static void
-display_notify (GConfClient *gconf, guint cnxn_id, GConfEntry *entry, gpointer data)
-{
- MailDisplay *md = data;
- gchar *tkey;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (gconf_entry_get_key (entry) != NULL);
- g_return_if_fail (gconf_entry_get_value (entry) != NULL);
-
- tkey = strrchr (entry->key, '/');
-
- g_return_if_fail (tkey != NULL);
-
- if (!strcmp (tkey, "/animate_images")) {
- gtk_html_set_animate (md->html, gconf_value_get_bool (gconf_entry_get_value(entry)));
- } else if (!strcmp (tkey, "/citation_color")
- || !strcmp (tkey, "/mark_citations")) {
- mail_display_queue_redisplay (md);
- } else if (!strcmp (tkey, "/caret_mode")) {
- gtk_html_set_caret_mode(md->html, gconf_value_get_bool (gconf_entry_get_value(entry)));
- }
-}
-
-GtkWidget *
-mail_display_new (void)
-{
- MailDisplay *mail_display = g_object_new (mail_display_get_type (), NULL);
- GtkWidget *scroll, *html;
- GdkAtom clipboard_atom;
- HTMLTokenizer *tok;
- GConfClient *gconf;
-
- gtk_box_set_homogeneous (GTK_BOX (mail_display), FALSE);
- gtk_widget_show (GTK_WIDGET (mail_display));
-
- scroll = gtk_scrolled_window_new (NULL, NULL);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
- gtk_box_pack_start_defaults (GTK_BOX (mail_display), scroll);
- gtk_widget_show (scroll);
-
- html = gtk_html_new ();
- tok = e_searching_tokenizer_new ();
- html_engine_set_tokenizer (GTK_HTML (html)->engine, tok);
- g_object_unref (tok);
-
- mail_display_initialize_gtkhtml (mail_display, GTK_HTML (html));
-
- gtk_container_add (GTK_CONTAINER (scroll), html);
- gtk_widget_show (GTK_WIDGET (html));
-
- g_signal_connect (mail_display->invisible, "selection_get",
- G_CALLBACK (invisible_selection_get_callback), mail_display);
- g_signal_connect (mail_display->invisible, "selection_clear_event",
- G_CALLBACK (invisible_selection_clear_event_callback), mail_display);
-
- gtk_selection_add_target (mail_display->invisible,
- GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 1);
-
- clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
- if (clipboard_atom != GDK_NONE)
- gtk_selection_add_target (mail_display->invisible,
- clipboard_atom, GDK_SELECTION_TYPE_STRING, 1);
-
- gconf = mail_config_get_gconf_client ();
- gtk_html_set_animate (GTK_HTML (html), gconf_client_get_bool (gconf, "/apps/evolution/mail/display/animate_images", NULL));
- gtk_html_set_caret_mode (GTK_HTML (html), gconf_client_get_bool (gconf, "/apps/evolution/mail/display/caret_mode", NULL));
-
- gconf_client_add_dir (gconf, "/apps/evolution/mail/display",GCONF_CLIENT_PRELOAD_NONE, NULL);
- mail_display->priv->display_notify_id = gconf_client_notify_add (gconf, "/apps/evolution/mail/display",
- display_notify, mail_display, NULL, NULL);
-
- mail_display->scroll = GTK_SCROLLED_WINDOW (scroll);
- mail_display->html = GTK_HTML (html);
- g_object_ref (mail_display->html);
- mail_display->last_active = NULL;
- mail_display->data = g_new0 (GData *, 1);
- g_datalist_init (mail_display->data);
-
- return GTK_WIDGET (mail_display);
-}
-
-void
-mail_display_initialize_gtkhtml (MailDisplay *mail_display, GtkHTML *html)
-{
- gtk_html_set_default_content_type (GTK_HTML (html), "text/html; charset=utf-8");
-
- gtk_html_set_editable (GTK_HTML (html), FALSE);
-
- g_signal_connect (html, "url_requested",
- G_CALLBACK (on_url_requested),
- mail_display);
- g_signal_connect (html, "object_requested",
- G_CALLBACK (on_object_requested),
- mail_display);
- g_signal_connect (html, "link_clicked",
- G_CALLBACK (on_link_clicked),
- mail_display);
- g_signal_connect (html, "button_press_event",
- G_CALLBACK (html_button_press_event), mail_display);
- g_signal_connect (html, "motion_notify_event",
- G_CALLBACK (html_motion_notify_event), mail_display);
- g_signal_connect (html, "enter_notify_event",
- G_CALLBACK (html_enter_notify_event), mail_display);
- g_signal_connect (html, "iframe_created",
- G_CALLBACK (html_iframe_created), mail_display);
- g_signal_connect (html, "on_url",
- G_CALLBACK (html_on_url), mail_display);
-}
-
-static void
-free_url (gpointer key, gpointer value, gpointer data)
-{
- g_free (key);
- if (data)
- g_byte_array_free (value, TRUE);
-}
-
-static void
-free_data_urls (gpointer urls)
-{
- g_hash_table_foreach (urls, free_url, GINT_TO_POINTER (1));
- g_hash_table_destroy (urls);
-}
-
-char *
-mail_display_add_url (MailDisplay *md, const char *kind, char *url, gpointer data)
-{
- GHashTable *urls;
- gpointer old_key, old_value;
-
- urls = g_datalist_get_data (md->data, kind);
- if (!urls) {
- urls = g_hash_table_new (g_str_hash, g_str_equal);
- g_datalist_set_data_full (md->data, "data_urls", urls,
- free_data_urls);
- }
-
- if (g_hash_table_lookup_extended (urls, url, &old_key, &old_value)) {
- g_free (url);
- url = old_key;
- }
-
- g_hash_table_insert (urls, url, data);
-
- return url;
-}
-
-const char *
-mail_display_get_url_for_icon (MailDisplay *md, const char *icon_name)
-{
- char *icon_path, buf[1024], *url;
- int fd, nread;
- GByteArray *ba;
-
- /* FIXME: cache */
-
- if (*icon_name == '/')
- icon_path = g_strdup (icon_name);
- else {
- icon_path = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP,
- icon_name, TRUE, NULL);
- if (!icon_path)
- return "file:///dev/null";
- }
-
- fd = open (icon_path, O_RDONLY);
- g_free (icon_path);
- if (fd == -1)
- return "file:///dev/null";
-
- ba = g_byte_array_new ();
- while (1) {
- nread = read (fd, buf, sizeof (buf));
- if (nread < 1)
- break;
- g_byte_array_append (ba, buf, nread);
- }
- close (fd);
-
- url = g_strdup_printf ("x-evolution-data:%p", ba);
-
- return mail_display_add_url (md, "data_urls", url, ba);
-}
-
-
-struct _location_url_stack {
- struct _location_url_stack *parent;
- CamelURL *url;
-};
-
-void
-mail_display_push_content_location (MailDisplay *md, const char *location)
-{
- struct _location_url_stack *node;
- CamelURL *url;
-
- url = camel_url_new (location, NULL);
- node = g_new (struct _location_url_stack, 1);
- node->parent = md->urls;
- node->url = url;
- md->urls = node;
-}
-
-CamelURL *
-mail_display_get_content_location (MailDisplay *md)
-{
- return md->urls ? md->urls->url : NULL;
-}
-
-void
-mail_display_pop_content_location (MailDisplay *md)
-{
- struct _location_url_stack *node;
-
- if (!md->urls) {
- g_warning ("content-location stack underflow!");
- return;
- }
-
- node = md->urls;
- md->urls = node->parent;
-
- if (node->url)
- camel_url_free (node->url);
-
- g_free (node);
-}
-
-E_MAKE_TYPE (mail_display, "MailDisplay", MailDisplay, mail_display_class_init, mail_display_init, PARENT_TYPE);
diff --git a/mail/mail-display.h b/mail/mail-display.h
deleted file mode 100644
index fe95c95490..0000000000
--- a/mail/mail-display.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2002 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifndef _MAIL_DISPLAY_H_
-#define _MAIL_DISPLAY_H_
-
-#include <gtk/gtkvbox.h>
-#include <gtk/gtkscrolledwindow.h>
-#include <gtkhtml/gtkhtml.h>
-#include <gtkhtml/gtkhtml-stream.h>
-
-#include <camel/camel-stream.h>
-#include <camel/camel-mime-message.h>
-#include <camel/camel-medium.h>
-#include <camel/camel-folder.h>
-
-#include "mail-types.h"
-#include "mail-config.h" /*display_style*/
-#include "mail-display-stream.h"
-
-#define MAIL_DISPLAY_TYPE (mail_display_get_type ())
-#define MAIL_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MAIL_DISPLAY_TYPE, MailDisplay))
-#define MAIL_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MAIL_DISPLAY_TYPE, MailDisplayClass))
-#define IS_MAIL_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MAIL_DISPLAY_TYPE))
-#define IS_MAIL_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MAIL_DISPLAY_TYPE))
-
-struct _MailDisplay {
- GtkVBox parent;
-
- struct _MailDisplayPrivate *priv;
-
- GtkScrolledWindow *scroll;
- GtkHTML *html;
- /* GtkHTMLStream *stream; */
- gint redisplay_counter;
- gpointer last_active;
- guint idle_id;
-
- char *charset;
-
- char *selection;
-
- CamelMimeMessage *current_message;
- CamelMessageInfo *info;
- CamelFolder *folder;
- GData **data;
-
- /* stack of Content-Location URLs used for combining with a
- relative URL Content-Location on a leaf part in order to
- construct the full URL */
- struct _location_url_stack *urls;
-
- GHashTable *related; /* related parts not displayed yet */
-
- /* Sigh. This shouldn't be needed. I haven't figured out why it is
- though. */
- GtkWidget *invisible;
-
- MailConfigDisplayStyle display_style;
-
- guint printing : 1;
- guint destroyed: 1;
-};
-
-typedef struct {
- GtkVBoxClass parent_class;
-} MailDisplayClass;
-
-GtkType mail_display_get_type (void);
-GtkWidget * mail_display_new (void);
-
-void mail_display_initialize_gtkhtml (MailDisplay *mail_display, GtkHTML *html);
-
-void mail_display_queue_redisplay (MailDisplay *mail_display);
-void mail_display_render (MailDisplay *mail_display, GtkHTML *html, gboolean reset_scroll);
-void mail_display_redisplay (MailDisplay *mail_display, gboolean reset_scroll);
-void mail_display_redisplay_when_loaded (MailDisplay *md,
- gconstpointer key,
- void (*callback)(MailDisplay *, gpointer),
- GtkHTML *html,
- gpointer data);
-void mail_display_stream_write_when_loaded (MailDisplay *md,
- gconstpointer key,
- const gchar *url,
- void (*callback)(MailDisplay *, gpointer),
- GtkHTML *html,
- GtkHTMLStream *handle,
- gpointer data);
-
-void mail_display_set_message (MailDisplay *mail_display,
- CamelMedium *medium,
- CamelFolder *folder,
- CamelMessageInfo *info);
-
-void mail_display_set_charset (MailDisplay *mail_display,
- const char *charset);
-
-void mail_display_load_images (MailDisplay *mail_display);
-
-void mail_text_write (MailDisplayStream *stream,
- MailDisplay *md,
- CamelMimePart *part,
- gint idx,
- gboolean printing,
- const char *text);
-void mail_error_printf (MailDisplayStream *stream,
- const char *format, ...);
-
-char *mail_display_add_url (MailDisplay *md, const char *kind, char *url, gpointer data);
-
-const char *mail_display_get_url_for_icon (MailDisplay *md, const char *icon_name);
-
-void mail_display_push_content_location (MailDisplay *md, const char *location);
-CamelURL *mail_display_get_content_location (MailDisplay *md);
-void mail_display_pop_content_location (MailDisplay *md);
-
-#endif /* _MAIL_DISPLAY_H_ */
diff --git a/mail/mail-font-prefs.c b/mail/mail-font-prefs.c
deleted file mode 100644
index 0d26f1ccbd..0000000000
--- a/mail/mail-font-prefs.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Larry Ewing <lewing@ximian.com>
- *
- * Copyright 2002 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml-propmanager.h>
-
-#include "mail-font-prefs.h"
-
-static GtkVBoxClass *parent_class = NULL;
-
-GtkWidget *
-mail_font_prefs_new (void)
-{
- MailFontPrefs *new;
-
- new = MAIL_FONT_PREFS (g_object_new (mail_font_prefs_get_type ()), NULL);
-
- return GTK_WIDGET (new);
-}
-
-void
-mail_font_prefs_apply (MailFontPrefs *prefs)
-{
- gtk_html_propmanager_apply (prefs->pman);
-}
-
-static void
-font_prefs_changed (GtkHTMLPropmanager *pman, MailFontPrefs *prefs)
-{
- if (prefs->control)
- evolution_config_control_changed (prefs->control);
-}
-
-static void
-mail_font_prefs_destroy (GtkObject *object)
-{
- MailFontPrefs *prefs = (MailFontPrefs *) object;
-
- if (prefs->pman) {
- g_object_unref(prefs->pman);
- g_object_unref(prefs->gui);
- prefs->pman = NULL;
- }
-
- if (GTK_OBJECT_CLASS (parent_class)->finalize)
- (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
-}
-
-static void
-mail_font_prefs_init (MailFontPrefs *prefs)
-{
- GtkWidget *toplevel;
- GladeXML *gui;
-
- gui = glade_xml_new (EVOLUTION_GLADEDIR "/mail-config.glade", "font_tab", NULL);
- prefs->gui = gui;
-
- prefs->pman = GTK_HTML_PROPMANAGER (gtk_html_propmanager_new (NULL));
- gtk_html_propmanager_set_gui (prefs->pman, gui, NULL);
- g_object_ref(prefs->pman);
- gtk_object_sink (GTK_OBJECT (prefs->pman));
-
- g_signal_connect(prefs->pman, "changed", font_prefs_changed, prefs);
-
- /* get our toplevel widget */
- toplevel = glade_xml_get_widget (gui, "toplevel");
-
- /* reparent */
- g_object_ref (toplevel);
- gtk_container_remove (GTK_CONTAINER (toplevel->parent), toplevel);
- gtk_container_add (GTK_CONTAINER (prefs), toplevel);
- g_object_unref (toplevel);
-}
-
-static void
-mail_font_prefs_class_init (MailFontPrefsClass *klass)
-{
- GtkObjectClass *object_class;
-
- object_class = (GtkObjectClass *) klass;
- parent_class = g_type_class_ref(gtk_vbox_get_type ());
-
- object_class->destroy = mail_font_prefs_destroy;
-}
-
-GtkType
-mail_font_prefs_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- GTypeInfo type_info = {
- sizeof (MailFontPrefsClass),
- NULL, NULL,
- (GClassInitFunc) mail_font_prefs_class_init,
- NULL, NULL,
- sizeof (MailFontPrefs),
- 0,
- (GInstanceInitFunc) mail_font_prefs_init,
- };
-
- type = g_type_register_static (gtk_vbox_get_type (), "MailFontPrefs", &type_info, 0);
- }
-
- return type;
-}
-
-
diff --git a/mail/mail-font-prefs.h b/mail/mail-font-prefs.h
deleted file mode 100644
index 14a1d20b40..0000000000
--- a/mail/mail-font-prefs.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2002 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-#ifndef __MAIL_FONT_PREFS_H__
-#define __MAIL_FONT_PREFS_H__
-
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif
-
-#include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml-propmanager.h>
-
-#include <shell/Evolution.h>
-#include "evolution-config-control.h"
-
-#define MAIL_FONT_PREFS_TYPE (mail_font_prefs_get_type())
-#define MAIL_FONT_PREFS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MAIL_FONT_PREFS_TYPE, MailFontPrefs))
-#define MAIL_FONT_PREFS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MAIL_FONT_PREFS_TYPE, MailFontPrefsClass))
-#define IS_MAIL_FONT_PREFS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MAIL_FONT_PREFS_TYPE))
-#define IS_MAIL_FONT_PREFS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MAIL_FONT_PREFS_TYPE))
-
-typedef struct _MailFontPrefs MailFontPrefs;
-typedef struct _MailFontPrefsClass MailFontPrefsClass;
-
-struct _MailFontPrefs {
- GtkVBox parent_object;
-
- GtkHTMLPropmanager *pman;
- GladeXML *gui;
- EvolutionConfigControl *control;
-};
-
-struct _MailFontPrefsClass {
- GtkVBoxClass parent_object;
-};
-
-GtkType mail_font_prefs_get_type (void);
-GtkWidget * mail_font_prefs_new (void);
-void mail_font_prefs_apply (MailFontPrefs *prefs);
-
-#define MAIL_FONT_PREFS_CONTROL_ID "OAFIID:GNOME_Evolution_Mail_FontPrefs_ConfigControl"
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* __MAIL_FONT_PREFS_H__ */
diff --git a/mail/mail-format.c b/mail/mail-format.c
deleted file mode 100644
index 92428af592..0000000000
--- a/mail/mail-format.c
+++ /dev/null
@@ -1,2131 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Dan Winship <danw@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2000-2003 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h> /* for strstr */
-#include <ctype.h>
-#include <fcntl.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <libgnome/gnome-util.h>
-#include <libgnomevfs/gnome-vfs-mime-handlers.h>
-#include <shell/e-setup.h>
-
-#include <gal/util/e-iconv.h>
-
-#include <camel/camel-mime-utils.h>
-#include <camel/camel-stream-null.h>
-#include <camel/camel-stream-filter.h>
-#include <camel/camel-multipart-signed.h>
-#include <camel/camel-mime-filter-enriched.h>
-#include <camel/camel-mime-filter-tohtml.h>
-#include <camel/camel-mime-filter-windows.h>
-
-#include <e-util/e-trie.h>
-#include <e-util/e-time-utils.h>
-
-#include "mail.h"
-#include "mail-tools.h"
-#include "mail-display.h"
-#include "mail-format.h"
-#include "mail-mt.h"
-#include "mail-crypto.h"
-
-
-#define STANDARD_ISSUE_TABLE_OPEN "<table cellspacing=0 cellpadding=10 width=\"100%\">"
-
-
-static gboolean handle_text_plain (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_text_enriched (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_text_html (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_image (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_multipart_mixed (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_multipart_related (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_multipart_alternative (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_multipart_appledouble (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_multipart_encrypted (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_multipart_signed (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_message_rfc822 (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-static gboolean handle_message_external_body (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-
-static gboolean handle_via_bonobo (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-
-/* writes the header info for a mime message into an html stream */
-static void write_headers (MailDisplayStream *stream, MailDisplay *md, CamelMimeMessage *message);
-
-/* dispatch html printing via mimetype */
-static gboolean format_mime_part (CamelMimePart *part, MailDisplay *md, MailDisplayStream *stream);
-
-static void
-free_url (gpointer key, gpointer value, gpointer data)
-{
- g_free (key);
- if (data)
- g_byte_array_free (value, TRUE);
-}
-
-static void
-free_part_urls (gpointer urls)
-{
- g_hash_table_foreach (urls, free_url, NULL);
- g_hash_table_destroy (urls);
-}
-
-static void
-free_data_urls (gpointer urls)
-{
- g_hash_table_foreach (urls, free_url, GINT_TO_POINTER (1));
- g_hash_table_destroy (urls);
-}
-
-/**
- * mail_format_mime_message:
- * @mime_message: the input mime message
- * @md: the MailDisplay to render into
- *
- * Writes a CamelMimeMessage out into a MailDisplay
- **/
-void
-mail_format_mime_message (CamelMimeMessage *mime_message, MailDisplay *md,
- MailDisplayStream *stream)
-{
- GHashTable *hash;
-
- g_return_if_fail (CAMEL_IS_MIME_MESSAGE (mime_message));
-
- hash = g_datalist_get_data (md->data, "part_urls");
- if (!hash) {
- hash = g_hash_table_new (g_str_hash, g_str_equal);
- g_datalist_set_data_full (md->data, "part_urls", hash,
- free_part_urls);
- }
- hash = g_datalist_get_data (md->data, "data_urls");
- if (!hash) {
- hash = g_hash_table_new (g_str_hash, g_str_equal);
- g_datalist_set_data_full (md->data, "data_urls", hash,
- free_data_urls);
- }
-
- hash = g_datalist_get_data (md->data, "attachment_states");
- if (!hash) {
- hash = g_hash_table_new (NULL, NULL);
- g_datalist_set_data_full (md->data, "attachment_states", hash,
- (GDestroyNotify) g_hash_table_destroy);
- }
- hash = g_datalist_get_data (md->data, "fake_parts");
- if (!hash) {
- hash = g_hash_table_new (NULL, NULL);
- g_datalist_set_data_full (md->data, "fake_parts", hash,
- (GDestroyNotify) g_hash_table_destroy);
- }
-
- write_headers (stream, md, mime_message);
- format_mime_part (CAMEL_MIME_PART (mime_message), md, stream);
-}
-
-
-/**
- * mail_format_raw_message:
- * @mime_message: the input mime message
- * @md: the MailDisplay to render into
- *
- * Writes a CamelMimeMessage source out into a MailDisplay
- **/
-void
-mail_format_raw_message (CamelMimeMessage *mime_message, MailDisplay *md,
- MailDisplayStream *stream)
-{
- CamelStreamFilter *filtered_stream;
- CamelMimeFilter *html_filter;
- CamelDataWrapper *wrapper;
- guint32 flags;
-
- g_return_if_fail (CAMEL_IS_MIME_MESSAGE (mime_message));
-
- wrapper = CAMEL_DATA_WRAPPER (mime_message);
- if (!mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- return;
-
- filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
-
- flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
- CAMEL_MIME_FILTER_TOHTML_ESCAPE_8BIT;
- html_filter = camel_mime_filter_tohtml_new (flags, 0);
- camel_stream_filter_add (filtered_stream, html_filter);
- camel_object_unref (html_filter);
-
- camel_stream_write_string ((CamelStream *) stream, STANDARD_ISSUE_TABLE_OPEN "<tr><td><tt>");
-
- mail_format_data_wrapper_write_to_stream (wrapper, FALSE, md, (CamelStream *) filtered_stream);
- camel_object_unref (filtered_stream);
-
- camel_stream_write_string ((CamelStream *) stream, "</tt></td></tr></table>");
-}
-
-static const char *
-get_cid (CamelMimePart *part, MailDisplay *md)
-{
- static int fake_cid_counter = 0;
- char *cid;
-
- /* If we have a real Content-ID, use it. If we don't,
- * make a (syntactically invalid, unique) fake one.
- */
- if (camel_mime_part_get_content_id (part)) {
- cid = g_strdup_printf ("cid:%s", camel_mime_part_get_content_id (part));
- } else
- cid = g_strdup_printf ("cid:@@@%d", fake_cid_counter++);
-
- return mail_display_add_url (md, "part_urls", cid, part);
-}
-
-static const char *
-get_location (CamelMimePart *part, MailDisplay *md)
-{
- CamelURL *base;
- const char *loc;
- char *location;
-
- base = mail_display_get_content_location (md);
-
- loc = camel_mime_part_get_content_location (part);
- if (!loc) {
- if (!base)
- return NULL;
-
- location = camel_url_to_string (base, 0);
- return mail_display_add_url (md, "part_urls", location, part);
- }
-
- /* kludge: If the multipart/related does not have a
- Content-Location header and the HTML part doesn't contain a
- Content-Location header either, then we will end up
- generating a invalid unique identifier in the form of
- "cid:@@@%d" for use in GtkHTML's iframe src url. This means
- that when GtkHTML requests a relative URL, it will request
- "cid:/%s" */
- mail_display_add_url (md, "part_urls", g_strdup_printf ("cid:/%s", loc), part);
-
- if (!strchr (loc, ':') && base) {
- CamelURL *url;
-
- mail_display_add_url (md, "part_urls", g_strdup (loc), part);
-
- url = camel_url_new_with_base (base, loc);
- location = camel_url_to_string (url, 0);
- camel_url_free (url);
- } else {
- location = g_strdup (loc);
- }
-
- return mail_display_add_url (md, "part_urls", location, part);
-}
-
-
-static GHashTable *mime_handler_table, *mime_function_table;
-
-static void
-setup_mime_tables (void)
-{
- mime_handler_table = g_hash_table_new (g_str_hash, g_str_equal);
- mime_function_table = g_hash_table_new (g_str_hash, g_str_equal);
-
- g_hash_table_insert (mime_function_table, "text/plain",
- handle_text_plain);
- g_hash_table_insert (mime_function_table, "text/richtext",
- handle_text_enriched);
- g_hash_table_insert (mime_function_table, "text/enriched",
- handle_text_enriched);
- g_hash_table_insert (mime_function_table, "text/html",
- handle_text_html);
-
- g_hash_table_insert (mime_function_table, "image/gif",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/jpeg",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/png",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-png",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/tiff",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-bmp",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/bmp",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-cmu-raster",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-ico",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-portable-anymap",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-portable-bitmap",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-portable-graymap",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-portable-pixmap",
- handle_image);
- g_hash_table_insert (mime_function_table, "image/x-xpixmap",
- handle_image);
-
- g_hash_table_insert (mime_function_table, "message/rfc822",
- handle_message_rfc822);
- g_hash_table_insert (mime_function_table, "message/news",
- handle_message_rfc822);
- g_hash_table_insert (mime_function_table, "message/external-body",
- handle_message_external_body);
-
- g_hash_table_insert (mime_function_table, "multipart/alternative",
- handle_multipart_alternative);
- g_hash_table_insert (mime_function_table, "multipart/related",
- handle_multipart_related);
- g_hash_table_insert (mime_function_table, "multipart/mixed",
- handle_multipart_mixed);
- g_hash_table_insert (mime_function_table, "multipart/appledouble",
- handle_multipart_appledouble);
- g_hash_table_insert (mime_function_table, "multipart/encrypted",
- handle_multipart_encrypted);
- g_hash_table_insert (mime_function_table, "multipart/signed",
- handle_multipart_signed);
-
- /* RFC 2046 says unrecognized text subtypes can be treated
- * as text/plain (as long as you recognize the character set),
- * and unrecognized multipart subtypes as multipart/mixed. */
- g_hash_table_insert (mime_function_table, "text/*",
- handle_text_plain);
- g_hash_table_insert (mime_function_table, "multipart/*",
- handle_multipart_mixed);
-}
-
-static gboolean
-component_supports (Bonobo_ServerInfo *component, const char *mime_type)
-{
- Bonobo_ActivationProperty *prop;
- CORBA_sequence_CORBA_string stringv;
- int i;
-
- prop = bonobo_server_info_prop_find (component, "repo_ids");
- if (!prop || prop->v._d != Bonobo_ACTIVATION_P_STRINGV)
- return FALSE;
-
- stringv = prop->v._u.value_stringv;
- for (i = 0; i < stringv._length; i++) {
- if (!strcasecmp ("IDL:Bonobo/PersistStream:1.0", stringv._buffer[i]))
- break;
- }
-
- /* got to end of list with no persist stream? */
-
- if (i >= stringv._length)
- return FALSE;
-
- prop = bonobo_server_info_prop_find (component,
- "bonobo:supported_mime_types");
- if (!prop || prop->v._d != Bonobo_ACTIVATION_P_STRINGV)
- return FALSE;
-
- stringv = prop->v._u.value_stringv;
- for (i = 0; i < stringv._length; i++) {
- if (!strcasecmp (mime_type, stringv._buffer[i]))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-mime_type_uses_evolution_component (const char *mime_type)
-{
- return (!strcmp (mime_type, "text/x-vcard") || !strcmp (mime_type, "text/calendar"));
-}
-
-static gboolean
-mime_type_can_use_component (const char *mime_type)
-{
- const char **mime_types;
- int i;
-
- mime_types = mail_config_get_allowable_mime_types ();
- for (i = 0; mime_types[i]; i++) {
- if (!strcmp (mime_types[i], mime_type))
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- * mail_lookup_handler:
- * @mime_type: a MIME type
- *
- * Looks up the MIME type in its own tables and GNOME-VFS's and returns
- * a MailMimeHandler structure detailing the component, application,
- * and built-in handlers (if any) for that MIME type. (If the component
- * is non-%NULL, the built-in handler will always be handle_via_bonobo().)
- * The MailMimeHandler's @generic field is set if the match was for the
- * MIME supertype rather than the exact type.
- *
- * Return value: a MailMimeHandler (which should not be freed), or %NULL
- * if no handlers are available.
- **/
-MailMimeHandler *
-mail_lookup_handler (const char *mime_type)
-{
- MailMimeHandler *handler;
- char *mime_type_main;
- const char *p;
- GList *components, *iter;
-
- if (mime_handler_table == NULL)
- setup_mime_tables ();
-
- /* See if we've already found it. */
- handler = g_hash_table_lookup (mime_handler_table, mime_type);
- if (handler)
- return handler;
-
- /* Special case MIME type: application/octet-stream
- * The point of this type is that there isn't a handler.
- */
- if (strcmp (mime_type, "application/octet-stream") == 0)
- return NULL;
-
- /* No. Create a new one and look up application and full type
- * handler. If we find a builtin, create the handler and
- * register it.
- */
- handler = g_new0 (MailMimeHandler, 1);
- handler->applications =
- gnome_vfs_mime_get_short_list_applications (mime_type);
- handler->builtin =
- g_hash_table_lookup (mime_function_table, mime_type);
-
- if (handler->builtin) {
- handler->generic = FALSE;
- handler->is_bonobo = FALSE;
- goto reg;
- }
-
- /* only allow using a bonobo component if it is an evo-component or the user has
- * specified that we can use a bonobo-component by setting the gconf key */
- if (mime_type_uses_evolution_component (mime_type) || mime_type_can_use_component (mime_type)) {
- /* Try for the first matching component. (we don't use get_short_list_comps
- * as that will return NULL if the oaf files don't have the short_list properties
- * defined). */
- components = gnome_vfs_mime_get_all_components (mime_type);
- for (iter = components; iter; iter = iter->next) {
- if (component_supports (iter->data, mime_type)) {
- handler->generic = FALSE;
- handler->is_bonobo = TRUE;
- handler->builtin = handle_via_bonobo;
- handler->component = Bonobo_ServerInfo_duplicate (iter->data);
- gnome_vfs_mime_component_list_free (components);
- goto reg;
- }
- }
-
- gnome_vfs_mime_component_list_free (components);
- }
-
- /* Try for a generic builtin match. */
- p = strchr (mime_type, '/');
- if (p == NULL)
- p = mime_type + strlen (mime_type);
- mime_type_main = g_alloca ((p - mime_type) + 3);
- memcpy (mime_type_main, mime_type, p - mime_type);
- memcpy (mime_type_main + (p - mime_type), "/*", 3);
-
- handler->builtin = g_hash_table_lookup (mime_function_table,
- mime_type_main);
-
- if (handler->builtin) {
- handler->generic = TRUE;
- handler->is_bonobo = FALSE;
- if (handler->component) {
- CORBA_free (handler->component);
- handler->component = NULL;
- }
- goto reg;
- }
-
- /* Try for a generic component match. */
- if (handler->component) {
- handler->generic = TRUE;
- handler->is_bonobo = TRUE;
- handler->builtin = handle_via_bonobo;
- goto reg;
- }
-
- /* If we at least got an application list, use that. */
- if (handler->applications) {
- handler->generic = TRUE;
- handler->is_bonobo = FALSE;
- goto reg;
- }
-
- /* Nada. */
- g_free (handler);
- return NULL;
-
- reg:
- g_hash_table_insert (mime_handler_table, g_strdup (mime_type), handler);
-
- return handler;
-}
-
-/* An "anonymous" MIME part is one that we shouldn't call attention
- * to the existence of, but simply display.
- */
-static gboolean
-is_anonymous (CamelMimePart *part, const char *mime_type)
-{
- /* FIXME: should use CamelContentType stuff */
- if (!strncasecmp (mime_type, "multipart/", 10) ||
- !strncasecmp (mime_type, "message/", 8))
- return TRUE;
-
- if (!strncasecmp (mime_type, "text/", 5) &&
- !camel_mime_part_get_filename (part))
- return TRUE;
-
- return FALSE;
-}
-
-/**
- * mail_part_is_inline:
- * @part: a CamelMimePart
- *
- * Return value: whether or not the part should/will be displayed inline.
- **/
-gboolean
-mail_part_is_inline (CamelMimePart *part)
-{
- const char *disposition;
- CamelContentType *content_type;
- gboolean anon;
- char *type;
-
- /* If it has an explicit disposition, return that. */
- disposition = camel_mime_part_get_disposition (part);
- if (disposition)
- return strcasecmp (disposition, "inline") == 0;
-
- /* Certain types should default to inline. FIXME: this should
- * be customizable.
- */
- content_type = camel_mime_part_get_content_type (part);
- if (!header_content_type_is (content_type, "message", "*"))
- return TRUE;
-
- /* Otherwise, display it inline if it's "anonymous", and
- * as an attachment otherwise.
- */
- type = header_content_type_simple (content_type);
- anon = is_anonymous (part, type);
- g_free (type);
-
- return anon;
-}
-
-enum inline_states {
- I_VALID = (1 << 0),
- I_ACTUALLY = (1 << 1),
- I_DISPLAYED = (1 << 2)
-};
-
-static int
-get_inline_flags (CamelMimePart *part, MailDisplay *md)
-{
- GHashTable *asht;
- int val;
-
- /* check if we already know. */
-
- asht = g_datalist_get_data (md->data, "attachment_states");
- val = GPOINTER_TO_INT (g_hash_table_lookup (asht, part));
- if (val)
- return val;
-
- /* ok, we don't know. Figure it out. */
-
- if (mail_part_is_inline (part))
- val = (I_VALID | I_ACTUALLY | I_DISPLAYED);
- else
- val = (I_VALID);
-
- g_hash_table_insert (asht, part, GINT_TO_POINTER (val));
-
- return val;
-}
-
-gboolean
-mail_part_is_displayed_inline (CamelMimePart *part, MailDisplay *md)
-{
- return (gboolean) (get_inline_flags (part, md) & I_DISPLAYED);
-}
-
-void
-mail_part_toggle_displayed (CamelMimePart *part, MailDisplay *md)
-{
- GHashTable *asht = g_datalist_get_data (md->data, "attachment_states");
- gpointer ostate, opart;
- int state;
-
- if (g_hash_table_lookup_extended (asht, part, &opart, &ostate)) {
- g_hash_table_remove (asht, part);
-
- state = GPOINTER_TO_INT (ostate);
-
- if (state & I_DISPLAYED)
- state &= ~I_DISPLAYED;
- else
- state |= I_DISPLAYED;
- } else {
- state = I_VALID | I_DISPLAYED;
- }
-
- g_hash_table_insert (asht, part, GINT_TO_POINTER (state));
-}
-
-static void
-mail_part_set_default_displayed_inline (CamelMimePart *part, MailDisplay *md,
- gboolean displayed)
-{
- GHashTable *asht = g_datalist_get_data (md->data, "attachment_states");
- int state;
-
- if (g_hash_table_lookup (asht, part))
- return;
-
- state = I_VALID | (displayed ? I_DISPLAYED : 0);
- g_hash_table_insert (asht, part, GINT_TO_POINTER (state));
-}
-
-static void
-attachment_header (CamelMimePart *part, const char *mime_type, MailDisplay *md,
- MailDisplayStream *stream)
-{
- char *htmlinfo;
- const char *info;
-
- /* Start the table, create the pop-up object. */
- camel_stream_write_string ((CamelStream *) stream, "<table cellspacing=0 cellpadding=0><tr><td>"
- "<table width=10 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr></table></td>");
-
- if (!md->printing) {
- camel_stream_printf ((CamelStream *) stream, "<td><object classid=\"popup:%s\""
- "type=\"%s\"></object></td>", get_cid (part, md), mime_type);
- }
-
- camel_stream_write_string ((CamelStream *) stream, "<td><table width=3 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr></table></td><td><font size=-1>");
-
- /* Write the MIME type */
- info = gnome_vfs_mime_get_description (mime_type);
- htmlinfo = camel_text_to_html (info ? info : mime_type, 0, 0);
- camel_stream_printf ((CamelStream *) stream, _("%s attachment"), htmlinfo);
- g_free (htmlinfo);
-
- /* Write the name, if we have it. */
- info = camel_mime_part_get_filename (part);
- if (info) {
- htmlinfo = camel_text_to_html (info, 0, 0);
- camel_stream_printf ((CamelStream *) stream, " (%s)", htmlinfo);
- g_free (htmlinfo);
- }
-
- /* Write a description, if we have one. */
- info = camel_mime_part_get_description (part);
- if (info) {
- htmlinfo = camel_text_to_html (info, md->printing ? 0 : CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- camel_stream_printf ((CamelStream *) stream, ", \"%s\"", htmlinfo);
- g_free (htmlinfo);
- }
-
- camel_stream_write_string ((CamelStream *) stream, "</font></td></tr><tr><td height=10>"
- "<table cellspacing=0 cellpadding=0><tr><td height=10>"
- "<a name=\"glue\"></td></tr></table></td></tr></table>\n");
-}
-
-static gboolean
-format_mime_part (CamelMimePart *part, MailDisplay *md,
- MailDisplayStream *stream)
-{
- CamelDataWrapper *wrapper;
- MailMimeHandler *handler;
- gboolean output;
- int inline_flags;
- char *mime_type;
-
- /* Record URLs associated with this part */
- get_cid (part, md);
- get_location (part, md);
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-
- if (CAMEL_IS_MULTIPART (wrapper) &&
- camel_multipart_get_number (CAMEL_MULTIPART (wrapper)) == 0) {
- mail_error_printf (stream, "\n%s\n", _("Could not parse MIME message. Displaying as source."));
- if (mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- handle_text_plain (part, "text/plain", md, stream);
- return TRUE;
- }
-
- mime_type = camel_data_wrapper_get_mime_type (wrapper);
- camel_strdown (mime_type);
-
- handler = mail_lookup_handler (mime_type);
- if (!handler) {
- char *id_type;
-
- /* Special case MIME types that we know that we can't
- * display but are some kind of plain text to prevent
- * evil infinite recursion.
- */
-
- if (!strcmp (mime_type, "application/mac-binhex40")) {
- handler = NULL;
- } else if (!strcmp (mime_type, "application/octet-stream")) {
- /* only sniff application/octet-stream parts */
- id_type = mail_identify_mime_part (part, md);
- if (id_type) {
- g_free (mime_type);
- mime_type = id_type;
- handler = mail_lookup_handler (id_type);
- }
- }
- }
-
- inline_flags = get_inline_flags (part, md);
-
- /* No header for anonymous inline parts. */
- if (!((inline_flags & I_ACTUALLY) && is_anonymous (part, mime_type)))
- attachment_header (part, mime_type, md, stream);
-
- if (handler && handler->builtin && inline_flags & I_DISPLAYED &&
- mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- output = (*handler->builtin) (part, mime_type, md, stream);
- else
- output = TRUE;
-
- g_free (mime_type);
- return output;
-}
-
-/* flags for write_field_to_stream */
-enum {
- WRITE_BOLD=1,
- WRITE_NOCOLUMNS=2,
-};
-
-static void
-write_field_row_begin (MailDisplayStream *stream, const char *name, int flags)
-{
- gboolean bold = (flags & WRITE_BOLD);
- gboolean nocolumns = (flags & WRITE_NOCOLUMNS);
-
- if (nocolumns) {
- camel_stream_printf ((CamelStream *) stream, "<tr><td>%s%s:%s ",
- bold ? "<b>" : "", name, bold ? "</b>" : "");
- } else {
- camel_stream_printf ((CamelStream *) stream,
- "<tr><%s align=\"right\" valign=\"top\">%s:"
- "<b>&nbsp;</%s><td>", bold ? "th" : "td",
- name, bold ? "th" : "td");
- }
-}
-
-static void
-write_date (MailDisplayStream *stream, CamelMimeMessage *message, int flags)
-{
- const char *datestr;
-
- datestr = camel_medium_get_header (CAMEL_MEDIUM (message), "Date");
-
- if (datestr) {
- int msg_offset;
- time_t msg_date;
- struct tm local;
- int local_tz;
-
- msg_date = header_decode_date(datestr, &msg_offset);
- e_localtime_with_offset(msg_date, &local, &local_tz);
-
- write_field_row_begin(stream, _("Date"), flags);
- camel_stream_printf((CamelStream *)stream, "%s", datestr);
-
- /* Convert message offset to minutes (e.g. -0400 --> -240) */
- msg_offset = ((msg_offset / 100) * 60) + (msg_offset % 100);
- /* Turn into offset from localtime, not UTC */
- msg_offset -= local_tz / 60;
-
- if (msg_offset) {
- /* Message timezone different from local. Show both */
- char buf[30];
-
- msg_offset += (local.tm_hour * 60) + local.tm_min;
-
- if (msg_offset >= (24 * 60) || msg_offset < 0) {
- /* Timezone conversion crossed midnight. Show day */
- /* translators: strftime format for local time equivalent in Date header display */
- e_utf8_strftime(buf, 29, _("<I> (%a, %R %Z)</I>"), &local);
- } else {
- e_utf8_strftime(buf, 29, _("<I> (%R %Z)</I>"), &local);
- }
-
- /* I doubt any locales put '%' in time representation
- but just in case... */
- camel_stream_printf((CamelStream *)stream, "%s", buf);
- }
-
- camel_stream_printf ((CamelStream *) stream, "</td> </tr>");
- }
-}
-
-static void
-write_text_header (MailDisplayStream *stream, const char *name, const char *value, int flags)
-{
- char *encoded;
-
- if (value && *value)
- encoded = camel_text_to_html (value, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- else
- encoded = "";
-
- write_field_row_begin (stream, name, flags);
-
- camel_stream_printf ((CamelStream *) stream, "%s</td></tr>", encoded);
-
- if (value && *value)
- g_free (encoded);
-}
-
-static void
-write_address (MailDisplay *md, MailDisplayStream *stream,
- const CamelInternetAddress *addr, const char *field_name, int flags)
-{
- const char *name, *email;
- int i;
-
- if (addr == NULL || !camel_internet_address_get (addr, 0, NULL, NULL))
- return;
-
- write_field_row_begin (stream, field_name, flags);
-
- i = 0;
- while (camel_internet_address_get (addr, i, &name, &email)) {
- CamelInternetAddress *subaddr;
- char *addr_txt, *addr_url;
- gboolean have_name = name && *name;
- gboolean have_email = email && *email;
- char *name_disp = NULL;
- char *email_disp = NULL;
-
- subaddr = camel_internet_address_new ();
- camel_internet_address_add (subaddr, name, email);
- addr_txt = camel_address_format (CAMEL_ADDRESS (subaddr));
- addr_url = camel_url_encode (addr_txt, NULL);
- camel_object_unref (subaddr);
-
- if (have_name) {
- name_disp = camel_text_to_html (name, 0, 0);
- }
-
- if (have_email) {
- email_disp = camel_text_to_html (email, 0, 0);
- }
-
- if (i)
- camel_stream_write_string ((CamelStream *) stream, ", ");
-
- if (have_email || have_name) {
- if (!have_email)
- email_disp = g_strdup ("???");
-
- if (have_name) {
- if (md->printing) {
- camel_stream_printf ((CamelStream *) stream,
- "%s &lt;%s&gt;", name_disp, email_disp);
- } else {
- camel_stream_printf ((CamelStream *) stream,
- "%s &lt;<a href=\"mailto:%s\">%s</a>&gt;",
- name_disp, addr_url, email_disp);
- }
- } else {
- if (md->printing) {
- camel_stream_write_string ((CamelStream *) stream, email_disp);
- } else {
- camel_stream_printf ((CamelStream *) stream,
- "<a href=\"mailto:%s\">%s</a>",
- addr_url, email_disp);
- }
- }
- } else {
- camel_stream_printf ((CamelStream *) stream, "<i>%s</i>", _("Bad Address"));
- }
-
- g_free (name_disp);
- g_free (email_disp);
- g_free (addr_txt);
- g_free (addr_url);
-
- i++;
- }
-
- camel_stream_write_string ((CamelStream *) stream, "</td></tr>");
-}
-
-/* order of these must match write_header code */
-static char *default_headers[] = {
- "From", "Reply-To", "To", "Cc", "Bcc", "Subject", "Date",
-};
-
-/* return index of header in default_headers array */
-static int
-default_header_index (const char *name)
-{
- int i;
-
- for (i = 0; i < sizeof (default_headers) / sizeof (default_headers[0]); i++)
- if (!strcasecmp (name, default_headers[i]))
- return i;
-
- return -1;
-}
-
-/* index is index of header in default_headers array */
-static void
-write_default_header (CamelMimeMessage *message, MailDisplay *md,
- MailDisplayStream *stream,
- int index, int flags)
-{
- switch (index) {
- case 0:
- write_address (md, stream,
- camel_mime_message_get_from (message), _("From"), flags | WRITE_BOLD);
- break;
- case 1:
- write_address (md, stream,
- camel_mime_message_get_reply_to (message), _("Reply-To"), flags | WRITE_BOLD);
- break;
- case 2:
- write_address (md, stream,
- camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO),
- _("To"), flags | WRITE_BOLD);
- break;
- case 3:
- write_address (md, stream,
- camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC),
- _("Cc"), flags | WRITE_BOLD);
- break;
- case 4:
- write_address (md, stream,
- camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC),
- _("Bcc"), flags | WRITE_BOLD);
- break;
- case 5:
- write_text_header (stream, _("Subject"), camel_mime_message_get_subject (message),
- flags | WRITE_BOLD);
- break;
- case 6:
- write_date (stream, message, flags | WRITE_BOLD);
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
-static gboolean
-write_xmailer_header (CamelMimeMessage *message, MailDisplay *md,
- MailDisplayStream *stream, int xmask)
-{
- const char *xmailer, *evolution;
-
- xmailer = camel_medium_get_header (CAMEL_MEDIUM (message), "X-Mailer");
- if (!xmailer) {
- xmailer = camel_medium_get_header (CAMEL_MEDIUM (message), "User-Agent");
- if (!xmailer)
- return FALSE;
- }
- while (isspace ((unsigned char)*xmailer))
- xmailer++;
-
- evolution = strstr (xmailer, "Evolution");
- if ((xmask & MAIL_CONFIG_XMAILER_OTHER) ||
- (evolution && (xmask & MAIL_CONFIG_XMAILER_EVO)))
- write_text_header (stream, _("Mailer"), xmailer, WRITE_BOLD);
-
- return evolution != NULL && (xmask & MAIL_CONFIG_XMAILER_RUPERT_APPROVED);
-}
-
-#define COLOR_IS_LIGHT(r, g, b) ((r + g + b) > (128 * 3))
-
-static void
-write_headers (MailDisplayStream *stream, MailDisplay *md, CamelMimeMessage *message)
-{
- gboolean full = (md->display_style == MAIL_CONFIG_DISPLAY_FULL_HEADERS);
- char bgcolor[7], fontcolor[7];
- GtkStyle *style = NULL;
- gboolean evo_icon = FALSE;
- GConfClient *gconf;
- int xmask, i;
-
- gconf = mail_config_get_gconf_client ();
- xmask = gconf_client_get_int (gconf, "/apps/evolution/mail/display/xmailer_mask", NULL);
-
- /* My favorite thing to do... muck around with colors so we respect people's stupid themes.
- However, we only do this if we are rendering to the screen -- we ignore the theme
- when we are printing. */
- style = gtk_widget_get_style (GTK_WIDGET (md->html));
- if (style && !md->printing) {
- int state = GTK_WIDGET_STATE (GTK_WIDGET (md->html));
- gushort r, g, b;
-
- r = style->base[state].red / 256;
- g = style->base[state].green / 256;
- b = style->base[state].blue / 256;
-
- if (COLOR_IS_LIGHT (r, g, b)) {
- r *= 0.92;
- g *= 0.92;
- b *= 0.92;
- } else {
- r = 255 - (0.92 * (255 - r));
- g = 255 - (0.92 * (255 - g));
- b = 255 - (0.92 * (255 - b));
- }
-
- sprintf (bgcolor, "%.2X%.2X%.2X", r, g, b);
-
- r = style->text[state].red / 256;
- g = style->text[state].green / 256;
- b = style->text[state].blue / 256;
-
- sprintf (fontcolor, "%.2X%.2X%.2X", r, g, b);
- } else {
- strcpy (bgcolor, "EEEEEE");
- strcpy (fontcolor, "000000");
- }
-
- camel_stream_write_string ((CamelStream *) stream,
- "<table width=\"100%\" cellpadding=0 cellspacing=0>");
-
- /* Top margin */
- camel_stream_write_string ((CamelStream *) stream, "<tr><td colspan=3 height=10>"
- "<table cellpadding=0 cellspacing=0><tr><td height=10>"
- "<a name=\"glue\"></td></tr></table></td></tr>");
-
- /* Left margin */
- camel_stream_write_string ((CamelStream *) stream, "<tr><td><table width=10 "
- "cellpadding=0 cellspacing=0><tr><td></td></tr></table></td>");
-
- /* Black border */
- camel_stream_write_string ((CamelStream *) stream, "<td width=\"100%\"><table bgcolor=\"#000000\" "
- "width=\"100%\" cellspacing=0 cellpadding=1>");
-
- /* Main header box */
- camel_stream_printf ((CamelStream *) stream, "<tr><td><table bgcolor=\"#%s\" width=\"100%%\" "
- "cellpadding=0 cellspacing=0>"
- /* Internal header table */
- "<tr valign=top><td><table><font color=\"#%s\">\n",
- bgcolor, fontcolor);
-
- if (full) {
- struct _header_raw *header;
- const char *charset;
- CamelContentType *ct;
- char *value;
-
- ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (message));
- charset = header_content_type_param (ct, "charset");
- charset = e_iconv_charset_name (charset);
-
- header = CAMEL_MIME_PART (message)->headers;
- while (header) {
- i = default_header_index (header->name);
- if (i == -1) {
- value = header_decode_string (header->value, charset);
- write_text_header (stream, header->name, value, WRITE_NOCOLUMNS);
- g_free (value);
- } else
- write_default_header (message, md, stream, i, WRITE_NOCOLUMNS);
- header = header->next;
- }
- } else {
- for (i = 0; i < sizeof (default_headers) / sizeof (default_headers[0]); i++)
- write_default_header (message, md, stream, i, 0);
- if (xmask != MAIL_CONFIG_XMAILER_NONE)
- evo_icon = write_xmailer_header (message, md, stream, xmask);
- }
-
- /* Close off the internal header table */
- camel_stream_write_string ((CamelStream *) stream, "</font></table></td>");
-
- if (!md->printing && evo_icon) {
- camel_stream_printf ((CamelStream *) stream, "<td align=right><table><tr><td width=16>"
- "<img src=\"%s\"></td></tr></table></td>",
- mail_display_get_url_for_icon (md, EVOLUTION_ICONSDIR "/monkey-16.png"));
- }
-
- camel_stream_write_string ((CamelStream *) stream,
- /* Main header box */
- "</tr></table>"
- /* Black border */
- "</td></tr></table></td>"
- /* Right margin */
- "<td><table width=10 cellpadding=0 cellspacing=0>"
- "<tr><td></td></tr></table></td>"
- "</tr></table>\n");
-}
-
-static void
-load_offline_content (MailDisplay *md, gpointer data)
-{
- CamelDataWrapper *wrapper = data;
- CamelStream *stream;
-
- stream = camel_stream_null_new ();
- camel_data_wrapper_write_to_stream (wrapper, stream);
- camel_object_unref (stream);
- camel_object_unref (wrapper);
-}
-
-gboolean
-mail_content_loaded (CamelDataWrapper *wrapper, MailDisplay *md, gboolean redisplay, const char *url,
- GtkHTML *html, GtkHTMLStream *stream)
-{
- if (!camel_data_wrapper_is_offline (wrapper))
- return TRUE;
-
- camel_object_ref (wrapper);
- if (redisplay) {
- mail_display_redisplay_when_loaded (md, wrapper, load_offline_content,
- html, wrapper);
- } else {
- mail_display_stream_write_when_loaded (md, wrapper, url, load_offline_content,
- html, stream, wrapper);
- }
-
- return FALSE;
-}
-
-
-ssize_t
-mail_format_data_wrapper_write_to_stream (CamelDataWrapper *wrapper, gboolean decode, MailDisplay *mail_display, CamelStream *stream)
-{
- CamelStreamFilter *filter_stream;
- CamelMimeFilterCharset *filter;
- CamelContentType *content_type;
- GConfClient *gconf;
- ssize_t nwritten;
- char *charset;
-
- gconf = mail_config_get_gconf_client ();
-
- content_type = camel_data_wrapper_get_mime_type_field (wrapper);
-
- /* find out the charset the user wants to override to */
- if (mail_display && mail_display->charset) {
- /* user override charset */
- charset = g_strdup (mail_display->charset);
- } else if (content_type && (charset = (char *) header_content_type_param (content_type, "charset"))) {
- /* try to use the charset declared in the Content-Type header */
- if (!strncasecmp (charset, "iso-8859-", 9)) {
- /* Since a few Windows mailers like to claim they sent
- * out iso-8859-# encoded text when they really sent
- * out windows-cp125#, do some simple sanity checking
- * before we move on... */
- CamelMimeFilterWindows *windows;
- CamelStream *null;
-
- null = camel_stream_null_new ();
- filter_stream = camel_stream_filter_new_with_stream (null);
- camel_object_unref (null);
-
- windows = (CamelMimeFilterWindows *) camel_mime_filter_windows_new (charset);
- camel_stream_filter_add (filter_stream, (CamelMimeFilter *) windows);
-
- if (decode)
- camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) filter_stream);
- else
- camel_data_wrapper_write_to_stream (wrapper, (CamelStream *) filter_stream);
- camel_stream_flush ((CamelStream *) filter_stream);
- camel_object_unref (filter_stream);
-
- charset = g_strdup (camel_mime_filter_windows_real_charset (windows));
- camel_object_unref (windows);
- } else {
- charset = g_strdup (charset);
- }
- } else {
- /* default to user's locale charset? */
- charset = gconf_client_get_string (gconf, "/apps/evolution/mail/format/charset", NULL);
- }
-
- filter_stream = camel_stream_filter_new_with_stream (stream);
-
- if ((filter = camel_mime_filter_charset_new_convert (charset, "UTF-8"))) {
- camel_stream_filter_add (filter_stream, (CamelMimeFilter *) filter);
- camel_object_unref (filter);
- }
-
- g_free (charset);
-
- if (decode)
- nwritten = camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) filter_stream);
- else
- nwritten = camel_data_wrapper_write_to_stream (wrapper, (CamelStream *) filter_stream);
- camel_stream_flush ((CamelStream *) filter_stream);
- camel_object_unref (filter_stream);
-
- return nwritten;
-}
-
-/* Return the contents of a data wrapper, or %NULL if it contains only
- * whitespace.
- */
-GByteArray *
-mail_format_get_data_wrapper_text (CamelDataWrapper *wrapper, MailDisplay *mail_display)
-{
- CamelStream *memstream;
- GByteArray *ba;
- char *text, *end;
-
- memstream = camel_stream_mem_new ();
- ba = g_byte_array_new ();
- camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (memstream), ba);
-
- mail_format_data_wrapper_write_to_stream (wrapper, TRUE, mail_display, memstream);
- camel_object_unref (memstream);
-
- for (text = ba->data, end = text + ba->len; text < end; text++) {
- if (!isspace ((unsigned char) *text))
- break;
- }
-
- if (text >= end) {
- g_byte_array_free (ba, TRUE);
- return NULL;
- }
-
- return ba;
-}
-
-static void
-write_hr (MailDisplayStream *stream)
-{
- camel_stream_write_string ((CamelStream *) stream, STANDARD_ISSUE_TABLE_OPEN
- "<tr><td width=\"100%\"><hr noshadow size=1>"
- "</td></tr></table>\n");
-}
-
-/*----------------------------------------------------------------------*
- * Mime handling functions
- *----------------------------------------------------------------------*/
-
-static gboolean
-handle_text_plain (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelStreamFilter *filtered_stream;
- CamelMimeFilter *html_filter;
- CamelDataWrapper *wrapper;
- CamelContentType *type;
- const char *format;
- GConfClient *gconf;
- guint32 flags, rgb = 0;
- GdkColor colour;
- char *buf;
-
- gconf = mail_config_get_gconf_client ();
-
- flags = CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES;
- if (!md->printing) {
- flags |= CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES;
- if (gconf_client_get_bool (gconf, "/apps/evolution/mail/display/mark_citations", NULL)) {
- flags |= CAMEL_MIME_FILTER_TOHTML_MARK_CITATION;
-
- buf = gconf_client_get_string (gconf, "/apps/evolution/mail/display/citation_colour", NULL);
- gdk_color_parse (buf ? buf : "#737373", &colour);
- g_free (buf);
-
- rgb = ((colour.red & 0xff00) << 8) | (colour.green & 0xff00) | ((colour.blue & 0xff00) >> 8);
- }
- }
-
- /* Check for RFC 2646 flowed text. */
- type = camel_mime_part_get_content_type (part);
- if (header_content_type_is (type, "text", "plain")) {
- format = header_content_type_param (type, "format");
-
- if (format && !strcasecmp (format, "flowed"))
- flags |= CAMEL_MIME_FILTER_TOHTML_FORMAT_FLOWED;
- }
-
- html_filter = camel_mime_filter_tohtml_new (flags, rgb);
- filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
- camel_stream_filter_add (filtered_stream, html_filter);
- camel_object_unref (html_filter);
-
- camel_stream_write_string ((CamelStream *) stream, STANDARD_ISSUE_TABLE_OPEN "<tr><td><tt>\n");
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- mail_format_data_wrapper_write_to_stream (wrapper, TRUE, md, (CamelStream *) filtered_stream);
-
- camel_stream_write_string ((CamelStream *) stream, "</tt></td></tr></table>\n");
-
- camel_object_unref (filtered_stream);
-
- return TRUE;
-}
-
-/* text/enriched (RFC 1896) or text/richtext (included in RFC 1341) */
-static gboolean
-handle_text_enriched (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelStreamFilter *filtered_stream;
- CamelMimeFilter *enriched;
- CamelDataWrapper *wrapper;
- guint32 flags = 0;
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-
- if (!strcasecmp (mime_type, "text/richtext")) {
- flags = CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT;
- camel_stream_write_string ((CamelStream *) stream, "\n<!-- text/richtext -->\n");
- } else {
- camel_stream_write_string ((CamelStream *) stream, "\n<!-- text/enriched -->\n");
- }
-
- enriched = camel_mime_filter_enriched_new (flags);
- filtered_stream = camel_stream_filter_new_with_stream ((CamelStream *) stream);
- camel_stream_filter_add (filtered_stream, enriched);
- camel_object_unref (enriched);
-
- camel_stream_write_string ((CamelStream *) stream, STANDARD_ISSUE_TABLE_OPEN "<tr><td><tt>\n");
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- mail_format_data_wrapper_write_to_stream (wrapper, TRUE, md, (CamelStream *) filtered_stream);
-
- camel_stream_write_string ((CamelStream *) stream, "</tt></td></tr></table>\n");
- camel_object_unref (filtered_stream);
-
- return TRUE;
-}
-
-static gboolean
-handle_text_html (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- const char *location, *base;
-
- camel_stream_write_string ((CamelStream *) stream, "\n<!-- text/html -->\n");
-
- if ((base = camel_medium_get_header (CAMEL_MEDIUM (part), "Content-Base"))) {
- char *base_url;
- size_t len;
-
- len = strlen (base);
-
- if (*base == '"' && *(base + len - 1) == '"') {
- len -= 2;
- base_url = g_alloca (len + 1);
- memcpy (base_url, base + 1, len);
- base_url[len] = '\0';
- base = base_url;
- }
-
- gtk_html_set_base (md->html, base);
- }
-
- location = get_location (part, md);
- if (!location)
- location = get_cid (part, md);
-
- camel_stream_printf ((CamelStream *) stream, "<iframe src=\"%s\" frameborder=0 "
- "scrolling=no>could not get %s</iframe>", location, location);
-
- return TRUE;
-}
-
-static gboolean
-handle_image (CamelMimePart *part, const char *mime_type, MailDisplay *md, MailDisplayStream *stream)
-{
- camel_stream_printf ((CamelStream *) stream, "<img hspace=10 vspace=10 src=\"%s\">",
- get_cid (part, md));
- return TRUE;
-}
-
-static gboolean
-handle_multipart_mixed (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelDataWrapper *wrapper =
- camel_medium_get_content_object (CAMEL_MEDIUM (part));
- CamelMultipart *mp;
- int i, nparts;
- gboolean output = FALSE;
-
- if (!CAMEL_IS_MULTIPART (wrapper)) {
- mail_error_printf (stream, "\n%s\n", _("Could not parse MIME message. Displaying as source."));
- if (mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- handle_text_plain (part, "text/plain", md, stream);
- return TRUE;
- }
-
- mp = CAMEL_MULTIPART (wrapper);
-
- nparts = camel_multipart_get_number (mp);
- for (i = 0; i < nparts; i++) {
- if (i != 0 && output)
- write_hr (stream);
-
- part = camel_multipart_get_part (mp, i);
-
- output = format_mime_part (part, md, stream);
- }
-
- return TRUE;
-}
-
-static gboolean
-handle_multipart_encrypted (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelMultipartEncrypted *mpe;
- CamelMimePart *mime_part;
- CamelCipherContext *cipher;
- CamelDataWrapper *wrapper;
- const char *protocol;
- CamelException ex;
- gboolean handled;
-
- /* Currently we only handle RFC2015-style PGP encryption. */
- protocol = header_content_type_param (((CamelDataWrapper *) part)->mime_type, "protocol");
- if (!protocol || strcmp (protocol, "application/pgp-encrypted") != 0)
- return handle_multipart_mixed (part, mime_type, md, stream);
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-
- mpe = CAMEL_MULTIPART_ENCRYPTED (wrapper);
-
- camel_exception_init (&ex);
- cipher = camel_gpg_context_new (session);
- mime_part = camel_multipart_encrypted_decrypt (mpe, cipher, &ex);
- camel_object_unref (cipher);
-
- if (camel_exception_is_set (&ex)) {
- mail_error_printf (stream, "\n%s\n", camel_exception_get_description (&ex));
- camel_exception_clear (&ex);
- return TRUE;
- }
-
- handled = format_mime_part (mime_part, md, stream);
- camel_object_unref (mime_part);
-
- return handled;
-}
-
-static gboolean
-handle_multipart_signed (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelMimePart *subpart;
- CamelDataWrapper *wrapper;
- CamelMultipartSigned *mps;
- gboolean output = FALSE;
-
- wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
-
- if (!CAMEL_IS_MULTIPART_SIGNED (wrapper)) {
- mail_error_printf (stream, "\n%s\n", _("Could not parse MIME message. Displaying as source."));
- if (mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- handle_text_plain (part, "text/plain", md, stream);
- return TRUE;
- }
-
- mps = CAMEL_MULTIPART_SIGNED (wrapper);
-
- /* if subpart & signature is null, what do we do? just write it out raw?
- multipart_signed will, if it cannot parse properly, put everything in the first part
- this includes: more or less than 2 parts */
-
- /* output the content */
- subpart = camel_multipart_get_part ((CamelMultipart *) mps, CAMEL_MULTIPART_SIGNED_CONTENT);
- if (subpart == NULL)
- return FALSE;
-
- output = format_mime_part (subpart, md, stream);
-
- /* now handle the signature */
- subpart = camel_multipart_get_part ((CamelMultipart *) mps, CAMEL_MULTIPART_SIGNED_SIGNATURE);
- if (subpart == NULL)
- return FALSE;
-
- mail_part_set_default_displayed_inline (subpart, md, FALSE);
-
- if (!mail_part_is_displayed_inline (subpart, md) && !md->printing) {
- char *url;
-
- /* Write out the click-for-info object */
- url = g_strdup_printf ("signature:%p/%lu", subpart,
- (unsigned long)time (NULL));
- camel_stream_printf ((CamelStream *) stream,
- "<br><table cellspacing=0 cellpadding=0>"
- "<tr><td><table width=10 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr></table></td>"
- "<td><object classid=\"%s\"></object></td>"
- "<td><table width=3 cellspacing=0 cellpadding=0>"
- "<tr><td></td></tr></table></td>"
- "<td><font size=-1>", url);
- mail_display_add_url (md, "part_urls", url, subpart);
-
- camel_stream_write_string ((CamelStream *) stream,
- _("This message is digitally signed. "
- "Click the lock icon for more information."));
-
- camel_stream_write_string ((CamelStream *) stream, "</font></td></tr><tr><td height=10>"
- "<table cellspacing=0 cellpadding=0><tr>"
- "<td height=10><a name=\"glue\"></td></tr>"
- "</table></td></tr></table>\n");
- } else {
- CamelCipherValidity *valid = NULL;
- CamelException ex;
- const char *message = NULL;
- gboolean good = FALSE;
- CamelCipherContext *cipher;
-
- /* Write out the verification results */
- /* TODO: use the right context for the right message ... */
- camel_exception_init (&ex);
- cipher = camel_gpg_context_new (session);
- if (cipher) {
- valid = camel_multipart_signed_verify (mps, cipher, &ex);
- camel_object_unref (cipher);
- if (valid) {
- good = camel_cipher_validity_get_valid (valid);
- message = camel_cipher_validity_get_description (valid);
- } else {
- message = camel_exception_get_description (&ex);
- }
- } else {
- message = _("Could not create a PGP verfication context");
- }
-
- if (good) {
- camel_stream_printf ((CamelStream *) stream, "<table><tr valign=top><td>"
- "<img src=\"%s\"></td><td>%s<br><br>",
- mail_display_get_url_for_icon (md, EVOLUTION_ICONSDIR
- "/pgp-signature-ok.png"),
- _("This message is digitally signed and "
- "has been found to be authentic."));
- } else {
- camel_stream_printf ((CamelStream *) stream, "<table><tr valign=top><td>"
- "<img src=\"%s\"></td><td>%s<br><br>",
- mail_display_get_url_for_icon (md, EVOLUTION_ICONSDIR
- "/pgp-signature-bad.png"),
- _("This message is digitally signed but can "
- "not be proven to be authentic."));
- }
-
- if (message) {
- camel_stream_printf ((CamelStream *) stream, "<font size=-1%s>", good ||
- md->printing ? "" : " color=red");
- mail_text_write (stream, md, part, 0, md->printing, message);
- camel_stream_write_string ((CamelStream *) stream, "</font>");
- }
-
- camel_stream_write_string ((CamelStream *) stream, "</td></tr></table>");
- camel_exception_clear (&ex);
- camel_cipher_validity_free (valid);
- }
-
- return TRUE;
-}
-
-/* As seen in RFC 2387! */
-static gboolean
-handle_multipart_related (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelDataWrapper *wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- CamelMultipart *mp;
- CamelMimePart *body_part, *display_part = NULL;
- CamelContentType *content_type;
- const char *location, *start;
- int i, nparts;
- GHashTable *related_save;
- int ret;
-
- if (!CAMEL_IS_MULTIPART (wrapper)) {
- mail_error_printf (stream, "\n%s\n", _("Could not parse MIME message. Displaying as source."));
- if (mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- handle_text_plain (part, "text/plain", md, stream);
- return TRUE;
- }
-
- mp = CAMEL_MULTIPART (wrapper);
- nparts = camel_multipart_get_number (mp);
-
- content_type = camel_mime_part_get_content_type (part);
- start = header_content_type_param (content_type, "start");
- if (start) {
- int len;
-
- /* The "start" parameter includes <>s, which Content-Id
- * does not.
- */
- len = strlen (start) - 2;
-
- for (i = 0; i < nparts; i++) {
- const char *cid;
-
- body_part = camel_multipart_get_part (mp, i);
- cid = camel_mime_part_get_content_id (body_part);
-
- if (cid && !strncmp (cid, start + 1, len) && strlen (cid) == len) {
- display_part = body_part;
- break;
- }
- }
- } else {
- /* No start parameter, so it defaults to the first part. */
- display_part = camel_multipart_get_part (mp, 0);
- }
-
- if (!display_part) {
- /* Oops. Hrmph. */
- return handle_multipart_mixed (part, mime_type, md, stream);
- }
-
- /* setup a 'stack' of related parts */
- related_save = md->related;
- md->related = g_hash_table_new(NULL, NULL);
-
- location = camel_mime_part_get_content_location (part);
- if (location)
- mail_display_push_content_location (md, location);
-
- /* Record the Content-ID/Content-Location of any non-displayed parts. */
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- if (body_part == display_part)
- continue;
-
- get_cid (body_part, md);
- get_location (body_part, md);
- g_hash_table_insert (md->related, body_part, body_part);
- }
-
- /* Now, display the displayed part. */
- ret = format_mime_part (display_part, md, stream);
-
- /* FIXME: flush html stream via gtkhtml_stream_flush which doens't exist yet ... */
- while (gtk_events_pending ())
- gtk_main_iteration ();
-
- /* Now check for related parts which didn't display, display them as attachments */
- for (i = 0; i < nparts; i++) {
- body_part = camel_multipart_get_part (mp, i);
- if (body_part == display_part)
- continue;
-
- if (g_hash_table_lookup (md->related, body_part)) {
- if (ret)
- write_hr (stream);
- ret |= format_mime_part (body_part, md, stream);
- }
- }
-
- g_hash_table_destroy (md->related);
- md->related = related_save;
-
- if (location)
- mail_display_pop_content_location (md);
-
- return ret;
-}
-
-/* RFC 2046 says "display the last part that you are able to display". */
-static CamelMimePart *
-find_preferred_alternative (CamelMultipart *multipart, gboolean want_plain)
-{
- int i, nparts;
- CamelMimePart *preferred_part = NULL;
- MailMimeHandler *handler;
-
- nparts = camel_multipart_get_number (multipart);
- for (i = 0; i < nparts; i++) {
- CamelMimePart *part = camel_multipart_get_part (multipart, i);
- CamelContentType *type = camel_mime_part_get_content_type (part);
- char *mime_type = header_content_type_simple (type);
-
- camel_strdown (mime_type);
- if (want_plain && !strcmp (mime_type, "text/plain"))
- return part;
- handler = mail_lookup_handler (mime_type);
- if (handler && (!preferred_part || !handler->generic))
- preferred_part = part;
- g_free (mime_type);
- }
-
- return preferred_part;
-}
-
-static gboolean
-handle_multipart_alternative (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelDataWrapper *wrapper =
- camel_medium_get_content_object (CAMEL_MEDIUM (part));
- CamelMultipart *multipart;
- CamelMimePart *mime_part;
-
- if (!CAMEL_IS_MULTIPART (wrapper)) {
- mail_error_printf (stream, "\n%s\n", _("Could not parse MIME message. Displaying as source."));
- if (mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- handle_text_plain (part, "text/plain", md, stream);
- return TRUE;
- }
-
- multipart = CAMEL_MULTIPART (wrapper);
-
- mime_part = find_preferred_alternative (multipart, FALSE);
- if (mime_part)
- return format_mime_part (mime_part, md, stream);
- else
- return handle_multipart_mixed (part, mime_type, md, stream);
-}
-
-/* RFC 1740 */
-static gboolean
-handle_multipart_appledouble (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelDataWrapper *wrapper =
- camel_medium_get_content_object (CAMEL_MEDIUM (part));
- CamelMultipart *multipart;
-
- if (!CAMEL_IS_MULTIPART (wrapper)) {
- mail_error_printf (stream, "\n%s\n", _("Could not parse MIME message. Displaying as source."));
- if (mail_content_loaded (wrapper, md, TRUE, NULL, md->html, NULL))
- handle_text_plain (part, "text/plain", md, stream);
- return TRUE;
- }
-
- multipart = CAMEL_MULTIPART (wrapper);
-
- /* The first part is application/applefile and is not useful
- * to us. The second part _may_ be displayable data. Most
- * likely it's application/octet-stream though.
- */
- part = camel_multipart_get_part (multipart, 1);
- return format_mime_part (part, md, stream);
-}
-
-static gboolean
-handle_message_rfc822 (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelDataWrapper *wrapper =
- camel_medium_get_content_object (CAMEL_MEDIUM (part));
-
- g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (wrapper), FALSE);
-
- camel_stream_write_string ((CamelStream *) stream, "<blockquote>");
- mail_format_mime_message (CAMEL_MIME_MESSAGE (wrapper), md, stream);
- camel_stream_write_string ((CamelStream *) stream, "</blockquote>");
-
- return TRUE;
-}
-
-static gboolean
-handle_message_external_body (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- CamelContentType *type;
- const char *access_type;
- char *url = NULL, *desc = NULL;
-
- type = camel_mime_part_get_content_type (part);
- access_type = header_content_type_param (type, "access-type");
- if (!access_type)
- goto fallback;
-
- if (!strcasecmp (access_type, "ftp") ||
- !strcasecmp (access_type, "anon-ftp")) {
- const char *name, *site, *dir, *mode, *ftype;
- char *path;
-
- name = header_content_type_param (type, "name");
- site = header_content_type_param (type, "site");
- if (name == NULL || site == NULL)
- goto fallback;
- dir = header_content_type_param (type, "directory");
- mode = header_content_type_param (type, "mode");
-
- /* Generate the path. */
- if (dir) {
- const char *p = dir + strlen (dir);
-
- path = g_strdup_printf ("%s%s%s%s",
- *dir == '/' ? "" : "/",
- dir,
- *p == '/' ? "" : "/",
- name);
- } else {
- path = g_strdup_printf ("%s%s",
- *name == '/' ? "" : "/",
- name);
- }
-
- if (mode && *mode == 'A')
- ftype = ";type=A";
- else if (mode && *mode == 'I')
- ftype = ";type=I";
- else
- ftype = "";
-
- url = g_strdup_printf ("ftp://%s%s%s", site, path, ftype);
- g_free (path);
- desc = g_strdup_printf (_("Pointer to FTP site (%s)"), url);
- } else if (!g_ascii_strcasecmp (access_type, "local-file")) {
- const char *name, *site;
-
- name = header_content_type_param (type, "name");
- if (name == NULL)
- goto fallback;
- site = header_content_type_param (type, "site");
-
- url = g_strdup_printf ("file://%s%s", *name == '/' ? "" : "/", name);
- if (site) {
- desc = g_strdup_printf(_("Pointer to local file (%s) "
- "valid at site \"%s\""), name, site);
- } else {
- desc = g_strdup_printf(_("Pointer to local file (%s)"), name);
- }
- } else if (!strcasecmp (access_type, "URL")) {
- const char *urlparam;
- char *s, *d;
-
- /* RFC 2017 */
-
- urlparam = header_content_type_param (type, "url");
- if (urlparam == NULL)
- goto fallback;
-
- /* For obscure MIMEy reasons, the URL may be split into
- * multiple words, and needs to be rejoined. (The URL
- * must have any real whitespace %-encoded, so we just
- * get rid of all of it.
- */
- url = g_strdup (urlparam);
- s = d = url;
-
- while (*s) {
- if (!isspace ((unsigned char)*s))
- *d++ = *s;
- s++;
- }
- *d = *s;
-
- desc = g_strdup_printf (_("Pointer to remote data (%s)"), url);
- }
-
- fallback:
- if (!desc) {
- if (access_type)
- desc = g_strdup_printf (_("Pointer to unknown external data (\"%s\" type)"), access_type);
- else
- desc = g_strdup (_("Malformed external-body part."));
- }
-
-#if 0 /* FIXME */
- handle_mystery (part, md, url, "gnome-globe.png", desc,
- url ? "open it in a browser" : NULL);
-#endif
-
- g_free (desc);
- g_free (url);
-
- return TRUE;
-}
-
-static gboolean
-handle_via_bonobo (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream)
-{
- if (!md->printing) {
- camel_stream_printf ((CamelStream *) stream,
- "<object classid=\"%s\" type=\"%s\"></object>",
- get_cid (part, md), mime_type);
- }
-
- return TRUE;
-}
-
-/**
- * mail_get_message_rfc822:
- * @message: the message
- * @want_plain: whether the caller prefers plain to html
- * @cite: whether or not to cite the message text
- *
- * See mail_get_message_body() below for more details.
- *
- * Return value: an HTML string representing the text parts of @message.
- **/
-static char *
-mail_get_message_rfc822 (CamelMimeMessage *message, gboolean want_plain, gboolean cite)
-{
- CamelDataWrapper *contents;
- GString *retval;
- const CamelInternetAddress *cia;
- char *text, *citation, *buf, *html;
- time_t date_val;
- int offset;
-
- contents = camel_medium_get_content_object (CAMEL_MEDIUM (message));
- text = mail_get_message_body (contents, want_plain, cite);
- if (!text)
- text = g_strdup ("");
- citation = cite ? "&gt; " : "";
- retval = g_string_new (NULL);
-
- /* Kludge: if text starts with "<PRE>", wrap it around the
- * headers too so we won't get a blank line between them for the
- * <P> to <PRE> switch.
- */
- if (!strncasecmp (text, "<pre>", 5))
- g_string_append_printf (retval, "<PRE>");
-
- /* create credits */
- cia = camel_mime_message_get_from (message);
- buf = camel_address_format (CAMEL_ADDRESS (cia));
- if (buf) {
- html = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL, 0);
- g_string_append_printf (retval, "%s<b>From:</b> %s<br>",
- citation, html);
- g_free (html);
- g_free (buf);
- }
-
- cia = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
- buf = camel_address_format (CAMEL_ADDRESS (cia));
- if (buf) {
- html = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL, 0);
- g_string_append_printf (retval, "%s<b>To:</b> %s<br>",
- citation, html);
- g_free (html);
- g_free (buf);
- }
-
- cia = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
- buf = camel_address_format (CAMEL_ADDRESS (cia));
- if (buf) {
- html = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL, 0);
- g_string_append_printf (retval, "%s<b>Cc:</b> %s<br>",
- citation, html);
- g_free (html);
- g_free (buf);
- }
-
- buf = (char *) camel_mime_message_get_subject (message);
- if (buf) {
- html = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS, 0);
- g_string_append_printf (retval, "%s<b>Subject:</b> %s<br>",
- citation, html);
- g_free (html);
- }
-
- date_val = camel_mime_message_get_date (message, &offset);
- buf = header_format_date (date_val, offset);
- html = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_CONVERT_NL, 0);
- g_string_append_printf (retval, "%s<b>Date:</b> %s<br>", citation, html);
- g_free (html);
- g_free (buf);
-
- if (!strncasecmp (text, "<pre>", 5))
- g_string_append_printf (retval, "%s<br>%s", citation, text + 5);
- else
- g_string_append_printf (retval, "%s<br>%s", citation, text);
- g_free (text);
-
- buf = retval->str;
- g_string_free (retval, FALSE);
-
- return buf;
-}
-
-/**
- * mail_get_message_body:
- * @data: the message or mime part content
- * @want_plain: whether the caller prefers plain to html
- * @cite: whether or not to cite the message text
- *
- * This creates an HTML string representing @data. If @want_plain is %TRUE,
- * it will be an HTML string that looks like a text/plain representation
- * of @data (but it will still be HTML).
- *
- * If @cite is %TRUE, the message will be cited as a reply, using "> "s.
- *
- * Return value: the HTML string, which the caller must free, or
- * %NULL if @data doesn't include any data which should be forwarded or
- * replied to.
- **/
-char *
-mail_get_message_body (CamelDataWrapper *data, gboolean want_plain, gboolean cite)
-{
- char *subtext, *old, *div, *text = NULL;
- CamelContentType *mime_type;
- CamelCipherContext *cipher;
- GByteArray *bytes = NULL;
- CamelMimePart *subpart;
- CamelMultipart *mp;
- int i, nparts;
-
- mime_type = camel_data_wrapper_get_mime_type_field (data);
-
- /* If it is message/rfc822 or message/news, extract the
- * important headers and recursively process the body.
- */
- if (header_content_type_is (mime_type, "message", "rfc822") ||
- header_content_type_is (mime_type, "message", "news"))
- return mail_get_message_rfc822 (CAMEL_MIME_MESSAGE (data), want_plain, cite);
-
- /* If it's a vcard or icalendar, ignore it. */
- if (header_content_type_is (mime_type, "text", "x-vcard") ||
- header_content_type_is (mime_type, "text", "calendar"))
- return NULL;
-
- /* Get the body data for other text/ or message/ types */
- if (header_content_type_is (mime_type, "text", "*") ||
- header_content_type_is (mime_type, "message", "*")) {
- bytes = mail_format_get_data_wrapper_text (data, NULL);
-
- if (bytes) {
- g_byte_array_append (bytes, "", 1);
- text = bytes->data;
- g_byte_array_free (bytes, FALSE);
- }
-
- if (text && !header_content_type_is(mime_type, "text", "html")) {
- char *html;
-
- if (header_content_type_is(mime_type, "text", "richtext"))
- html = camel_enriched_to_html(text, CAMEL_MIME_FILTER_ENRICHED_IS_RICHTEXT);
- else if (header_content_type_is(mime_type, "text", "enriched"))
- html = camel_enriched_to_html(text, 0);
- else
- html = camel_text_to_html (text, CAMEL_MIME_FILTER_TOHTML_PRE |
- CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
- (cite ? CAMEL_MIME_FILTER_TOHTML_CITE : 0), 0);
- g_free(text);
- text = html;
- }
- return text;
- }
-
- /* If it's not message and it's not text, and it's not
- * multipart, we don't want to deal with it.
- */
- if (!header_content_type_is (mime_type, "multipart", "*"))
- return NULL;
-
- mp = CAMEL_MULTIPART (data);
-
- if (CAMEL_IS_MULTIPART_ENCRYPTED (mp)) {
- cipher = camel_gpg_context_new (session);
- subpart = camel_multipart_encrypted_decrypt (CAMEL_MULTIPART_ENCRYPTED (mp),
- cipher, NULL);
- if (!subpart)
- return NULL;
-
- data = camel_medium_get_content_object (CAMEL_MEDIUM (subpart));
- return mail_get_message_body (data, want_plain, cite);
- } else if (header_content_type_is (mime_type, "multipart", "alternative")) {
- /* Pick our favorite alternative and reply to it. */
-
- subpart = find_preferred_alternative (mp, want_plain);
- if (!subpart)
- return NULL;
-
- data = camel_medium_get_content_object (CAMEL_MEDIUM (subpart));
- return mail_get_message_body (data, want_plain, cite);
- }
-
- /* Otherwise, concatenate all the parts that we can. */
- if (want_plain) {
- if (cite)
- div = "<br>\n&gt; ----<br>\n&gt; <br>\n";
- else
- div = "<br>\n----<br>\n<br>\n";
- } else
- div = "<br><hr><br>";
-
- nparts = camel_multipart_get_number (mp);
- for (i = 0; i < nparts; i++) {
- subpart = camel_multipart_get_part (mp, i);
-
- /* only add to the body contents if it is marked as "inline" */
- if (!mail_part_is_inline (subpart))
- continue;
-
- data = camel_medium_get_content_object (CAMEL_MEDIUM (subpart));
- subtext = mail_get_message_body (data, want_plain, cite);
- if (!subtext)
- continue;
-
- if (text) {
- old = text;
- text = g_strdup_printf ("%s%s%s", old, div, subtext);
- g_free (subtext);
- g_free (old);
- } else
- text = subtext;
- }
-
- return text;
-}
diff --git a/mail/mail-format.h b/mail/mail-format.h
deleted file mode 100644
index e39148ca83..0000000000
--- a/mail/mail-format.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2003 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifndef __MAIL_FORMAT_H__
-#define __MAIL_FORMAT_H__
-
-#include <camel/camel.h>
-#include <gtkhtml/gtkhtml.h>
-
-#include "mail-display.h"
-#include "mail-display-stream.h"
-
-GByteArray *mail_format_get_data_wrapper_text (CamelDataWrapper *data,
- MailDisplay *mail_display);
-
-ssize_t mail_format_data_wrapper_write_to_stream (CamelDataWrapper *wrapper,
- gboolean decode,
- MailDisplay *mail_display,
- CamelStream *stream);
-
-void mail_format_mime_message (CamelMimeMessage *mime_message,
- MailDisplay *md, MailDisplayStream *stream);
-void mail_format_raw_message (CamelMimeMessage *mime_message,
- MailDisplay *md, MailDisplayStream *stream);
-
-gboolean mail_content_loaded (CamelDataWrapper *wrapper,
- MailDisplay *display,
- gboolean redisplay,
- const char *url,
- GtkHTML *html,
- GtkHTMLStream *handle);
-
-typedef gboolean (*MailMimeHandlerFn) (CamelMimePart *part, const char *mime_type,
- MailDisplay *md, MailDisplayStream *stream);
-typedef struct {
- Bonobo_ServerInfo *component;
- GList *applications;
- MailMimeHandlerFn builtin;
- guint generic : 1;
- guint is_bonobo : 1;
-} MailMimeHandler;
-
-MailMimeHandler *mail_lookup_handler (const char *mime_type);
-
-gboolean mail_part_is_inline (CamelMimePart *part);
-gboolean mail_part_is_displayed_inline (CamelMimePart *part, MailDisplay *md);
-void mail_part_toggle_displayed (CamelMimePart *part, MailDisplay *md);
-
-char *mail_get_message_body (CamelDataWrapper *data, gboolean want_plain, gboolean cite);
-
-#endif /* __MAIL_FORMAT_H__ */
diff --git a/mail/mail-identify.c b/mail/mail-identify.c
deleted file mode 100644
index 2e3f517145..0000000000
--- a/mail/mail-identify.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-/*
- * Author :
- * Dan Winship <danw@ximian.com>
- *
- * Copyright 2000, Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <glib.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <libgnomevfs/gnome-vfs-mime.h>
-#include <libgnomevfs/gnome-vfs-mime-utils.h>
-#include "mail.h"
-
-static const char *identify_by_magic (CamelDataWrapper *data, MailDisplay *md);
-
-/**
- * mail_identify_mime_part:
- * @part: a CamelMimePart
- * @md: the MailDisplay @part is being shown in
- *
- * Try to identify the MIME type of the data in @part (which presumably
- * doesn't have a useful Content-Type).
- *
- * Return value: the MIME type, which the caller must free, or %NULL
- * if it could not be identified.
- **/
-char *
-mail_identify_mime_part (CamelMimePart *part, MailDisplay *md)
-{
- const char *filename, *name_type = NULL, *magic_type = NULL;
- CamelDataWrapper *data;
-
- filename = camel_mime_part_get_filename (part);
- if (filename) {
- /* GNOME-VFS will misidentify TNEF attachments as MPEG */
- if (!strcmp (filename, "winmail.dat"))
- return g_strdup ("application/vnd.ms-tnef");
-
- name_type = gnome_vfs_mime_type_from_name (filename);
- }
-
- data = camel_medium_get_content_object (CAMEL_MEDIUM (part));
- if (!camel_data_wrapper_is_offline (data))
- magic_type = identify_by_magic (data, md);
-
- if (magic_type && name_type) {
- /* If GNOME-VFS doesn't recognize the data by magic, but it
- * contains English words, it will call it text/plain. If the
- * filename-based check came up with something different, use
- * that instead.
- */
- if (!strcmp (magic_type, "text/plain"))
- return g_strdup (name_type);
-
- /* If if returns "application/octet-stream" try to
- * do better with the filename check.
- */
- if (!strcmp (magic_type, "application/octet-stream"))
- return g_strdup (name_type);
- }
-
- /* If the MIME part data was online, and the magic check
- * returned something, use that, since it's more reliable.
- */
- if (magic_type)
- return g_strdup (magic_type);
-
- /* Otherwise try guessing based on the filename */
- if (name_type)
- return g_strdup (name_type);
-
- /* Another possibility to try is the x-mac-type / x-mac-creator
- * parameter to Content-Type used by some Mac email clients. That
- * would require a Mac type to mime type conversion table.
- */
-
-#if 0
- /* If the data part is offline, then we didn't try magic
- * before, so force it to be loaded so we can try again later.
- * FIXME: In a perfect world, we would not load the content
- * just to identify the MIME type.
- */
- /* This is disabled as it just frustrates users more than it helps,
- see discussion in bug #11778 */
- if (camel_data_wrapper_is_offline (data))
- mail_content_loaded (data, md, TRUE, NULL, NULL, NULL);
-#endif
-
- return NULL;
-}
-
-static const char *
-identify_by_magic (CamelDataWrapper *data, MailDisplay *md)
-{
- CamelStreamMem *memstream;
- const char *type;
-
- memstream = (CamelStreamMem *)camel_stream_mem_new();
- if (camel_data_wrapper_write_to_stream (data, (CamelStream *)memstream) > 0)
- type = gnome_vfs_get_mime_type_for_data(memstream->buffer->data, memstream->buffer->len);
- else
- type = NULL;
- camel_object_unref(memstream);
-
- return type;
-}
diff --git a/mail/mail-local.c b/mail/mail-local.c
index 26de1dbc2a..0ec4f5a8e9 100644
--- a/mail/mail-local.c
+++ b/mail/mail-local.c
@@ -558,13 +558,50 @@ mlf_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
return 0;
}
+static gboolean
+mlf_meta_set(CamelObject *obj, const char *name, const char *value)
+{
+ MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(obj);
+ CamelFolder *f;
+ gboolean res;
+
+ LOCAL_FOLDER_LOCK(mlf);
+ f = mlf->real_folder;
+ camel_object_ref(f);
+ LOCAL_FOLDER_UNLOCK(mlf);
+
+ /* We must write this ourselves, since MailLocalFolder is not persistent itself */
+ if ((res = camel_object_meta_set(f, name, value)))
+ camel_object_state_write(f);
+ camel_object_unref(f);
+
+ return res;
+}
+
+static char *
+mlf_meta_get(CamelObject *obj, const char *name)
+{
+ MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(obj);
+ CamelFolder *f;
+ char * res;
+
+ LOCAL_FOLDER_LOCK(mlf);
+ f = mlf->real_folder;
+ camel_object_ref(f);
+ LOCAL_FOLDER_UNLOCK(mlf);
+
+ res = camel_object_meta_get(f, name);
+ camel_object_unref(f);
+
+ return res;
+}
+
static void
mlf_class_init (CamelObjectClass *camel_object_class)
{
CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_object_class);
/* override all the functions subclassed in providers/local/ */
-
camel_folder_class->refresh_info = mlf_refresh_info;
camel_folder_class->sync = mlf_sync;
camel_folder_class->expunge = mlf_expunge;
@@ -581,6 +618,9 @@ mlf_class_init (CamelObjectClass *camel_object_class)
camel_folder_class->rename = mlf_rename;
camel_object_class->getv = mlf_getv;
+
+ camel_object_class->meta_get = mlf_meta_get;
+ camel_object_class->meta_set = mlf_meta_set;
}
static void
diff --git a/mail/mail-mt.c b/mail/mail-mt.c
index 0cef1f2b30..66b8d68130 100644
--- a/mail/mail-mt.c
+++ b/mail/mail-mt.c
@@ -529,21 +529,28 @@ void mail_msg_cleanup(void)
e_msgport_destroy(mail_gui_reply_port);
}
-void mail_msg_init(void)
+static guint
+em_channel_setup(EMsgPort **port, GIOChannel **channel, GIOFunc func)
{
- mail_gui_reply_port = e_msgport_new();
- mail_gui_reply_channel = g_io_channel_unix_new(e_msgport_fd(mail_gui_reply_port));
- g_io_add_watch(mail_gui_reply_channel, G_IO_IN, mail_msgport_replied, mail_gui_reply_port);
+ GSource *source;
+ guint id;
- mail_gui_port = e_msgport_new();
- mail_gui_channel = g_io_channel_unix_new(e_msgport_fd(mail_gui_port));
- mail_gui_watch = g_io_add_watch(mail_gui_channel, G_IO_IN, mail_msgport_received, mail_gui_port);
+ *port = e_msgport_new();
+ *channel = g_io_channel_unix_new(e_msgport_fd(*port));
+ source = g_io_create_watch(*channel, G_IO_IN);
+ g_source_set_callback(source, (GSourceFunc)func, *port, NULL);
+ g_source_set_can_recurse(source, FALSE);
+ id = g_source_attach(source, NULL);
+ g_source_unref(source);
- /* experimental temporary */
- mail_gui_port2 = e_msgport_new();
- mail_gui_channel2 = g_io_channel_unix_new(e_msgport_fd(mail_gui_port2));
- mail_gui_watch2 = g_io_add_watch(mail_gui_channel2, G_IO_IN, mail_msgport_received2, mail_gui_port2);
+ return id;
+}
+void mail_msg_init(void)
+{
+ em_channel_setup(&mail_gui_reply_port, &mail_gui_reply_channel, mail_msgport_replied);
+ mail_gui_watch = em_channel_setup(&mail_gui_port, &mail_gui_channel, mail_msgport_received);
+ mail_gui_watch2 = em_channel_setup(&mail_gui_port2, &mail_gui_channel2, mail_msgport_received2);
mail_thread_queued = e_thread_new(E_THREAD_QUEUE);
e_thread_set_msg_destroy(mail_thread_queued, mail_msg_destroy, 0);
diff --git a/mail/mail-ops.c b/mail/mail-ops.c
index 489dda4402..41fe74df55 100644
--- a/mail/mail-ops.c
+++ b/mail/mail-ops.c
@@ -43,13 +43,14 @@
#include "mail-vfolder.h"
#include "mail-session.h"
#include "composer/e-msg-composer.h"
-#include "folder-browser.h"
#include "filter/filter-filter.h"
#include "mail-mt.h"
#include "mail-folder-cache.h"
+#include "em-utils.h"
+
#define w(x)
#define d(x)
@@ -150,17 +151,12 @@ static void
filter_folder_free (struct _mail_msg *mm)
{
struct _filter_mail_msg *m = (struct _filter_mail_msg *)mm;
- int i;
if (m->source_folder)
camel_object_unref (m->source_folder);
- if (m->source_uids) {
- for (i = 0; i < m->source_uids->len; i++)
- g_free (m->source_uids->pdata[i]);
-
- g_ptr_array_free (m->source_uids, TRUE);
- }
+ if (m->source_uids)
+ em_utils_uids_free (m->source_uids);
if (m->cancel)
camel_operation_unref (m->cancel);
@@ -1001,14 +997,10 @@ static void
transfer_messages_free (struct _mail_msg *mm)
{
struct _transfer_msg *m = (struct _transfer_msg *)mm;
- int i;
-
+
camel_object_unref (m->source);
g_free (m->dest_uri);
- for (i = 0; i < m->uids->len; i++)
- g_free (m->uids->pdata[i]);
- g_ptr_array_free (m->uids, TRUE);
-
+ em_utils_uids_free (m->uids);
}
static struct _mail_msg_op transfer_messages_op = {
@@ -1854,10 +1846,8 @@ static void get_messages_free(struct _mail_msg *mm)
{
struct _get_messages_msg *m = (struct _get_messages_msg *)mm;
int i;
-
- for (i=0;i<m->uids->len;i++)
- g_free(m->uids->pdata[i]);
- g_ptr_array_free(m->uids, TRUE);
+
+ em_utils_uids_free (m->uids);
for (i=0;i<m->messages->len;i++) {
if (m->messages->pdata[i])
camel_object_unref(m->messages->pdata[i]);
@@ -2007,11 +1997,8 @@ static void save_messages_saved(struct _mail_msg *mm)
static void save_messages_free(struct _mail_msg *mm)
{
struct _save_messages_msg *m = (struct _save_messages_msg *)mm;
- int i;
-
- for (i=0;i<m->uids->len;i++)
- g_free(m->uids->pdata[i]);
- g_ptr_array_free(m->uids, TRUE);
+
+ em_utils_uids_free (m->uids);
camel_object_unref(m->folder);
g_free(m->path);
}
diff --git a/mail/mail-ops.h b/mail/mail-ops.h
index f81829db45..e15d7729c2 100644
--- a/mail/mail-ops.h
+++ b/mail/mail-ops.h
@@ -30,6 +30,7 @@ extern "C" {
#pragma }
#endif /* __cplusplus */
+#include "camel/camel-store.h"
#include "camel/camel-folder.h"
#include "camel/camel-filter-driver.h"
#include "camel/camel-mime-message.h"
diff --git a/mail/mail-search.c b/mail/mail-search.c
deleted file mode 100644
index 424ccee991..0000000000
--- a/mail/mail-search.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jon Trowbridge <trow@ximian.com>
- *
- * Copyright 2001-2003 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include "mail-search.h"
-#include "e-searching-tokenizer.h"
-#include <gtkhtml/gtkhtml-search.h>
-#include <gtkhtml/htmlengine.h>
-#include <libgnomeui/gnome-window-icon.h>
-#include <gdk/gdkkeysyms.h>
-
-
-static ESearchingTokenizer *mail_search_tokenizer (MailSearch *ms);
-static void mail_search_redisplay_message (MailSearch *ms);
-
-
-static GtkObjectClass *parent_class = NULL;
-
-
-static void
-mail_search_finalise (GObject *obj)
-{
- MailSearch *ms = MAIL_SEARCH (obj);
-
- g_free (ms->last_search);
- g_object_weak_unref ((GObject *) ms->mail, (GWeakNotify) gtk_widget_destroy, ms);
- g_object_unref (ms->mail);
-
- G_OBJECT_CLASS (parent_class)->finalize (obj);
-}
-
-static void
-mail_search_destroy (GtkObject *obj)
-{
- MailSearch *ms = (MailSearch *) obj;
- ESearchingTokenizer *st = mail_search_tokenizer (ms);
-
- if (ms->begin_handler) {
- g_signal_handler_disconnect (ms->mail->html->engine->ht, ms->begin_handler);
- ms->begin_handler = 0;
- g_signal_handler_disconnect (ms->mail->html->engine->ht, ms->match_handler);
- ms->match_handler = 0;
-
- e_searching_tokenizer_set_primary_search_string (st, NULL);
- mail_search_redisplay_message (ms);
- }
-
- GTK_OBJECT_CLASS (parent_class)->destroy (obj);
-}
-
-static void
-mail_search_class_init (MailSearchClass *klass)
-{
- GObjectClass *object_class = (GObjectClass *) klass;
- GtkObjectClass *gtk_object_class = (GtkObjectClass *) klass;
-
- parent_class = (GtkObjectClass *) g_type_class_ref (GTK_TYPE_DIALOG);
-
- object_class->finalize = mail_search_finalise;
-
- gtk_object_class->destroy = mail_search_destroy;
-}
-
-static void
-mail_search_init (MailSearch *ms)
-{
-
-}
-
-GtkType
-mail_search_get_type (void)
-{
- static GType type = 0;
-
- if (!type) {
- static const GTypeInfo info = {
- sizeof (MailSearchClass),
- NULL, NULL,
- (GClassInitFunc) mail_search_class_init,
- NULL, NULL,
- sizeof (MailSearch),
- 0,
- (GInstanceInitFunc) mail_search_init,
- };
-
- type = g_type_register_static (GTK_TYPE_DIALOG, "MailSearch", &info, 0);
- }
-
- return type;
-}
-
-/*
- * Convenience
- */
-
-static ESearchingTokenizer *
-mail_search_tokenizer (MailSearch *ms)
-{
- return E_SEARCHING_TOKENIZER (ms->mail->html->engine->ht);
-}
-
-static void
-mail_search_redisplay_message (MailSearch *ms)
-{
- mail_display_redisplay (ms->mail, FALSE);
-}
-
-static void
-mail_search_set_subject (MailSearch *ms, const char *subject)
-{
- char *utf8_subject = NULL;
-
- if (subject && *subject) {
- utf8_subject = g_strdup (subject);
-
- if (g_utf8_validate (utf8_subject, -1, NULL)) {
-#define ARBITRARY_CUTOFF 40
- if (g_utf8_strlen (utf8_subject, -1) > ARBITRARY_CUTOFF + 3) {
- char *p = g_utf8_offset_to_pointer (utf8_subject, ARBITRARY_CUTOFF);
-
- strcpy (p, "...");
- }
- } else {
- /* If the subject contains bad utf8, don't show anything in the frame label. */
- g_free (utf8_subject);
- utf8_subject = NULL;
- }
- } else {
- utf8_subject = g_strdup (_("(Untitled Message)"));
- }
-
- gtk_frame_set_label (GTK_FRAME (ms->msg_frame), utf8_subject);
-
- g_free (utf8_subject);
-}
-
-/*
- * Construct Objects
- */
-
-static void
-toggled_case_cb (GtkToggleButton *b, MailSearch *ms)
-{
- ms->case_sensitive = gtk_toggle_button_get_active (b);
-
- e_searching_tokenizer_set_primary_case_sensitivity (mail_search_tokenizer (ms),
- ms->case_sensitive);
- mail_search_redisplay_message (ms);
-
-}
-
-#if 0
-static void
-toggled_fwd_cb (GtkToggleButton *b, MailSearch *ms)
-{
- ms->search_forward = gtk_toggle_button_get_active (b);
- gtk_html_engine_search_set_forward (ms->mail->html, ms->search_forward);
-}
-#endif
-
-static void
-dialog_response_cb (GtkWidget *widget, int button, MailSearch *ms)
-{
- ESearchingTokenizer *st = mail_search_tokenizer (ms);
-
- if (button == GTK_RESPONSE_ACCEPT) {
- char *search_text;
-
- search_text = gtk_editable_get_chars (GTK_EDITABLE (ms->entry), 0, -1);
- g_strstrip (search_text);
-
- if (search_text && *search_text) {
- if (ms->last_search && !strcmp (ms->last_search, search_text)) {
-
- if (!gtk_html_engine_search_next (ms->mail->html)) {
- g_free (ms->last_search);
- ms->last_search = NULL;
- }
- } else {
- g_free (ms->last_search);
- ms->last_search = NULL;
-
- e_searching_tokenizer_set_primary_search_string (st, search_text);
- e_searching_tokenizer_set_primary_case_sensitivity (st, ms->case_sensitive);
-
- mail_search_redisplay_message (ms);
-
- if (gtk_html_engine_search (ms->mail->html, search_text,
- ms->case_sensitive, ms->search_forward,
- FALSE)) {
- ms->last_search = g_strdup (search_text);
- }
- }
- }
-
- g_free (search_text);
- } else if (button == GTK_RESPONSE_CLOSE) {
- gtk_widget_destroy (widget);
- }
-}
-
-static void
-begin_cb (ESearchingTokenizer *st, char *foo, MailSearch *ms)
-{
- const char *subject;
-
- if (ms && ms->mail && ms->mail->current_message) {
- subject = ms->mail->current_message->subject;
-
- if (subject == NULL)
- subject = _("Untitled Message");
- } else {
- subject = _("Empty Message");
- }
-
- gtk_label_set_text (GTK_LABEL (ms->count_label), "0");
- mail_search_set_subject (ms, subject);
-}
-
-static void
-match_cb (ESearchingTokenizer *st, MailSearch *ms)
-{
- char buf[16];
-
- g_snprintf (buf, 16, "%d", e_searching_tokenizer_match_count (st));
- gtk_label_set_text (GTK_LABEL (ms->count_label), buf);
-}
-
-static void
-entry_run_search (GtkWidget *w, MailSearch *ms)
-{
- /* run search when enter pressed on widget */
- gtk_dialog_response ((GtkDialog *) ms, GTK_RESPONSE_ACCEPT);
-}
-
-void
-mail_search_construct (MailSearch *ms, MailDisplay *mail)
-{
- GtkWidget *find_hbox;
- GtkWidget *matches_hbox;
- GtkWidget *toggles_hbox;
- GtkWidget *frame_vbox;
- GtkWidget *entry;
- GtkWidget *count_label;
- GtkWidget *case_check;
-#if 0
- GtkWidget *fwd_check;
-#endif
- GtkWidget *button;
- GtkWidget *msg_hbox;
- GtkWidget *msg_frame;
- GtkAccelGroup *accel_group;
-
- g_return_if_fail (ms != NULL && IS_MAIL_SEARCH (ms));
- g_return_if_fail (mail != NULL && IS_MAIL_DISPLAY (mail));
-
- /* Basic set-up */
-
- ms->mail = mail;
- g_object_ref (mail);
-
- gtk_window_set_title ((GtkWindow *) ms, _("Find in Message"));
-
- button = gtk_dialog_add_button ((GtkDialog *) ms, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
- gtk_dialog_set_default_response ((GtkDialog *) ms, GTK_RESPONSE_ACCEPT);
- accel_group = gtk_accel_group_new ();
- gtk_window_add_accel_group (GTK_WINDOW (ms), accel_group);
- gtk_widget_add_accelerator (button, "activate", accel_group, GDK_Escape, 0, GTK_ACCEL_LOCKED);
-
- gtk_dialog_add_button ((GtkDialog *) ms, GTK_STOCK_FIND, GTK_RESPONSE_ACCEPT);
-
- ms->search_forward = TRUE;
- ms->case_sensitive = FALSE;
-
- ms->begin_handler = g_signal_connect (ms->mail->html->engine->ht, "begin",
- G_CALLBACK (begin_cb), ms);
- ms->match_handler = g_signal_connect (ms->mail->html->engine->ht, "match",
- G_CALLBACK (match_cb), ms);
-
- /* Construct the dialog contents. */
-
- msg_hbox = gtk_hbox_new (FALSE, 3);
- find_hbox = gtk_hbox_new (FALSE, 3);
- matches_hbox = gtk_hbox_new (FALSE, 3);
- toggles_hbox = gtk_hbox_new (FALSE, 3);
- frame_vbox = gtk_vbox_new (FALSE, 3);
- gtk_container_set_border_width ((GtkContainer *) frame_vbox, 3);
-
- entry = gtk_entry_new ();
- count_label = gtk_label_new ("0");
-
- msg_frame = gtk_frame_new (NULL);
- gtk_container_set_border_width ((GtkContainer *) msg_frame, 6);
-
- case_check = gtk_check_button_new_with_label (_("Case Sensitive"));
-#if 0
- fwd_check = gtk_check_button_new_with_label (_("Search Forward"));
-#endif
-
- ms->entry = entry;
- ms->count_label = count_label;
-
- ms->msg_frame = msg_frame;
-
- if (mail->current_message->subject && *mail->current_message->subject)
- mail_search_set_subject (ms, mail->current_message->subject);
- else
- mail_search_set_subject (ms, NULL);
-
-#if 0
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fwd_check), ms->search_forward);
-#endif
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (case_check), ms->case_sensitive);
-
- gtk_box_pack_start (GTK_BOX (msg_hbox), GTK_WIDGET (msg_frame), TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (find_hbox), gtk_label_new (_("Find:")), FALSE, FALSE, 3);
- gtk_box_pack_start (GTK_BOX (find_hbox), entry, TRUE, TRUE, 3);
-
- gtk_box_pack_start (GTK_BOX (matches_hbox), gtk_hbox_new (FALSE, 0), TRUE, TRUE, 0);
- gtk_box_pack_start (GTK_BOX (matches_hbox), gtk_label_new (_("Matches:")), FALSE, FALSE, 3);
- gtk_box_pack_start (GTK_BOX (matches_hbox), count_label, FALSE, FALSE, 3);
- gtk_box_pack_start (GTK_BOX (matches_hbox), gtk_hbox_new (FALSE, 0), TRUE, TRUE, 0);
-
- gtk_box_pack_start (GTK_BOX (toggles_hbox), case_check, FALSE, FALSE, 3);
-
- /*
- * Disabling the forward/backward search button because there are problems with it
- * (related to how gtkhtml handles searches), the GUI freeze is upon us, and I
- * don't know if they'll get resolved for 1.0. Hopefully getting this fixed can
- * be a 1.1 item.
- */
-
-#if 0
- gtk_box_pack_start (GTK_BOX (toggles_hbox), fwd_check, FALSE, FALSE, 3);
-#endif
-
- gtk_box_pack_start (GTK_BOX (frame_vbox), find_hbox, FALSE, FALSE, 3);
- gtk_box_pack_start (GTK_BOX (frame_vbox), matches_hbox, FALSE, FALSE, 3);
- gtk_box_pack_start (GTK_BOX (frame_vbox), toggles_hbox, FALSE, FALSE, 3);
-
- gtk_container_add (GTK_CONTAINER (msg_frame), GTK_WIDGET (frame_vbox));
-
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ms)->vbox), msg_hbox, TRUE, TRUE, 0);
-
- gtk_widget_grab_focus (entry); /* Give focus to entry by default */
- g_signal_connect (entry, "activate", G_CALLBACK (entry_run_search), ms);
- gnome_window_icon_set_from_file (GTK_WINDOW (ms), EVOLUTION_ICONSDIR "/find-message.xpm");
-
- gtk_widget_show_all (msg_hbox);
- gtk_widget_show_all (find_hbox);
- gtk_widget_show_all (matches_hbox);
- gtk_widget_show_all (toggles_hbox);
-
- /* Hook up signals */
-
- g_signal_connect (case_check, "toggled", G_CALLBACK (toggled_case_cb), ms);
-#if 0
- g_signal_connect (fwd_check, "toggled", G_CALLBACK (toggled_fwd_cb), ms);
-#endif
- g_signal_connect (ms, "response", G_CALLBACK (dialog_response_cb), ms);
-
- g_object_weak_ref ((GObject *) ms->mail, (GWeakNotify) gtk_widget_destroy, ms);
-}
-
-GtkWidget *
-mail_search_new (MailDisplay *mail)
-{
- GtkWidget *widget;
-
- g_return_val_if_fail (IS_MAIL_DISPLAY (mail), NULL);
-
- widget = g_object_new (mail_search_get_type (), NULL);
- mail_search_construct (MAIL_SEARCH (widget), mail);
-
- return widget;
-}
-
diff --git a/mail/mail-search.glade b/mail/mail-search.glade
new file mode 100644
index 0000000000..10b84e99a2
--- /dev/null
+++ b/mail/mail-search.glade
@@ -0,0 +1,197 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="search_message_dialog">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">Find in Message</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">False</property>
+ <property name="resizable">False</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="has_separator">True</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-find</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="response_id">-3</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="border_width">6</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Find:</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkEntry" id="search_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">True</property>
+ <property name="max_length">0</property>
+ <property name="text" translatable="yes"></property>
+ <property name="has_frame">True</property>
+ <property name="invisible_char" translatable="yes">*</property>
+ <property name="activates_default">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="search_matches_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="search_case_check">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Case Sensitive</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="search_subject_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
diff --git a/mail/mail-search.h b/mail/mail-search.h
deleted file mode 100644
index cbcf563636..0000000000
--- a/mail/mail-search.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jon Trowbridge <trow@ximian.com>
- *
- * Copyright 2001-2003 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifndef _MAIL_SEARCH_H_
-#define _MAIL_SEARCH_H_
-
-#ifdef _cplusplus
-extern "C" {
-#pragma }
-#endif /* _cplusplus */
-
-#include <gtk/gtkdialog.h>
-#include "mail-display.h"
-
-#define MAIL_SEARCH_TYPE (mail_search_get_type ())
-#define MAIL_SEARCH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MAIL_SEARCH_TYPE, MailSearch))
-#define MAIL_SEARCH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MAIL_SEARCH_TYPE, MailSearch))
-#define IS_MAIL_SEARCH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MAIL_SEARCH_TYPE))
-#define IS_MAIL_SEARCH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MAIL_SEARCH_TYPE))
-
-typedef struct _MailSearch MailSearch;
-typedef struct _MailSearchClass MailSearchClass;
-
-struct _MailSearch {
- GtkDialog parent;
-
- MailDisplay *mail;
-
- GtkWidget *entry;
- GtkWidget *msg_frame;
- GtkWidget *count_label;
-
- gboolean search_forward, case_sensitive;
- char *last_search;
-
- guint begin_handler;
- guint match_handler;
-};
-
-struct _MailSearchClass {
- GtkDialogClass parent_class;
-
-};
-
-GtkType mail_search_get_type (void);
-
-void mail_search_construct (MailSearch *, MailDisplay *);
-GtkWidget *mail_search_new (MailDisplay *);
-
-
-#ifdef _cplusplus
-}
-#endif /* _cplusplus */
-
-#endif /* _MAIL_SEARCH_H_ */
diff --git a/mail/mail-send-recv.c b/mail/mail-send-recv.c
index 469f0494c2..890434daa5 100644
--- a/mail/mail-send-recv.c
+++ b/mail/mail-send-recv.c
@@ -36,6 +36,7 @@
#include "e-util/e-gtk-utils.h"
+#include "widgets/misc/e-clipped-label.h"
#include "filter/filter-filter.h"
#include "camel/camel-filter-driver.h"
#include "camel/camel-folder.h"
@@ -110,7 +111,7 @@ struct _send_info {
send_state_t state;
GtkProgressBar *bar;
GtkButton *stop;
- GtkLabel *status;
+ EClippedLabel *status;
int timeout_id;
char *what;
@@ -143,7 +144,7 @@ receive_cancel(GtkButton *button, struct _send_info *info)
if (info->state == SEND_ACTIVE) {
camel_operation_cancel(info->cancel);
if (info->status)
- gtk_label_set_text(info->status, _("Cancelling..."));
+ e_clipped_label_set_text(info->status, _("Cancelling..."));
info->state = SEND_CANCELLED;
}
if (info->stop)
@@ -294,7 +295,8 @@ build_dialogue (EAccountList *accounts, CamelFolder *outbox, const char *destina
GList *list = NULL;
struct _send_data *data;
GtkWidget *send_icon, *recv_icon;
- GtkLabel *label, *status_label;
+ GtkLabel *label;
+ EClippedLabel *status_label;
GtkProgressBar *bar;
GtkButton *stop;
GtkHSeparator *line;
@@ -386,9 +388,9 @@ build_dialogue (EAccountList *accounts, CamelFolder *outbox, const char *destina
stop = (GtkButton *)e_gtk_button_new_with_icon(_("Cancel"), GTK_STOCK_CANCEL);
- status_label = (GtkLabel *)gtk_label_new ((info->type == SEND_UPDATE) ? _("Updating...") :
- _("Waiting..."));
-
+ status_label = (EClippedLabel *)e_clipped_label_new((info->type == SEND_UPDATE)?_("Updating..."):_("Waiting..."),
+ PANGO_WEIGHT_NORMAL, 1.0);
+
/* g_object_set(data->label, "bold", TRUE, NULL); */
gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
gtk_misc_set_alignment (GTK_MISC (status_label), 0, .5);
@@ -443,8 +445,8 @@ build_dialogue (EAccountList *accounts, CamelFolder *outbox, const char *destina
bar = (GtkProgressBar *)gtk_progress_bar_new ();
stop = (GtkButton *)e_gtk_button_new_with_icon(_("Cancel"), GTK_STOCK_CANCEL);
- status_label = (GtkLabel *)gtk_label_new (_("Waiting..."));
-
+ status_label = (EClippedLabel *)e_clipped_label_new(_("Waiting..."), PANGO_WEIGHT_NORMAL, 1.0);
+
gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
gtk_misc_set_alignment (GTK_MISC (status_label), 0, .5);
@@ -538,7 +540,8 @@ static int operation_status_timeout(void *data)
if (info->bar) {
gtk_progress_bar_set_fraction((GtkProgressBar *)info->bar, (gfloat)(info->pc/100.0));
- gtk_label_set_text(info->status, info->what);
+ if (info->what)
+ e_clipped_label_set_text(info->status, info->what);
return TRUE;
}
@@ -574,11 +577,11 @@ receive_done (char *uri, void *data)
switch(info->state) {
case SEND_CANCELLED:
- gtk_label_set_text(info->status, _("Cancelled."));
+ e_clipped_label_set_text(info->status, _("Cancelled."));
break;
default:
info->state = SEND_COMPLETE;
- gtk_label_set_text(info->status, _("Complete."));
+ e_clipped_label_set_text(info->status, _("Complete"));
}
}
diff --git a/mail/mail-tools.c b/mail/mail-tools.c
index 758a4b512b..99fd749d78 100644
--- a/mail/mail-tools.c
+++ b/mail/mail-tools.c
@@ -48,12 +48,12 @@
#include "mail.h" /*session*/
#include "mail-config.h"
#include "mail-vfolder.h"
-#include "mail-format.h"
#include "mail-tools.h"
#include "mail-local.h"
#include "mail-mt.h"
#include "mail-folder-cache.h"
+
/* **************************************** */
CamelFolder *
@@ -345,135 +345,6 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex)
return folder;
}
-/**
- * mail_tool_quote_message:
- * @message: mime message to quote
- * @fmt: credits format - example: "On %s, %s wrote:\n"
- * @Varargs: arguments
- *
- * Returns an allocated buffer containing the quoted message.
- */
-gchar *
-mail_tool_quote_message (CamelMimeMessage *message, const char *fmt, ...)
-{
- CamelDataWrapper *contents;
- gboolean want_plain;
- char *text, *colour;
- GConfClient *gconf;
-
- gconf = mail_config_get_gconf_client ();
-
- contents = camel_medium_get_content_object (CAMEL_MEDIUM (message));
- /* We pass "want_plain" for "cite", since if it's HTML, we'll
- * do the citing ourself below.
- */
- /* FIXME the citing logic has changed and we basically never want_plain
- * to be true now, but I don't want to remove all that logic until I
- * am sure --Larry
- */
- want_plain = FALSE;
- text = mail_get_message_body (contents, want_plain, FALSE);
-
- /* Set the quoted reply text. */
- if (text) {
- char *sig, *p, *ret_text, *credits = NULL;
-
- /* look for the signature and strip it off */
- sig = text;
- while ((p = strstr (sig, "\n-- \n")))
- sig = p + 1;
-
- if (sig != text)
- *sig = '\0';
-
- /* create credits */
- if (fmt) {
- va_list ap;
-
- va_start (ap, fmt);
- credits = g_strdup_vprintf (fmt, ap);
- va_end (ap);
- }
-
- colour = gconf_client_get_string (gconf, "/apps/evolution/mail/display/citation_colour", NULL);
-
- ret_text = g_strdup_printf ("%s<!--+GtkHTML:<DATA class=\"ClueFlow\" key=\"orig\" value=\"1\">-->"
- "<font color=\"%s\">\n%s%s%s</font>"
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" clear=\"orig\">-->",
- credits ? credits : "",
- colour ? colour : "#737373",
- want_plain ? "" : "<blockquote type=cite><i>",
- text,
- want_plain ? "" : "</i></blockquote>");
-
- g_free (text);
- g_free (colour);
- g_free (credits);
-
- return ret_text;
- }
-
- return NULL;
-}
-
-
-/**
- * mail_tool_forward_message:
- * @message: mime message to forward
- * @quoted: whether to forwarded it quoted (%TRUE) or inline (%FALSE)
- *
- * Returns an allocated buffer containing the forwarded message.
- */
-gchar *
-mail_tool_forward_message (CamelMimeMessage *message, gboolean quoted)
-{
- GConfClient *gconf;
- char *text;
-
- gconf = mail_config_get_gconf_client ();
-
- text = mail_get_message_body (CAMEL_DATA_WRAPPER (message), FALSE, FALSE);
-
- if (text != NULL) {
- char *sig, *p, *ret_text;
-
- /* FIXME: this code should be merged with the quote_message() code above somehow... */
-
- /* look for the signature and strip it off */
- sig = text;
- while ((p = strstr (sig, "\n-- \n")))
- sig = p + 1;
-
- if (sig != text)
- *sig = '\0';
-
- if (quoted) {
- char *colour;
-
- colour = gconf_client_get_string (gconf, "/apps/evolution/mail/display/citation_colour", NULL);
-
- ret_text = g_strdup_printf ("-----%s-----<br>"
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" key=\"orig\" value=\"1\">-->"
- "<font color=\"%s\">\n%s%s%s</font>"
- "<!--+GtkHTML:<DATA class=\"ClueFlow\" clear=\"orig\">-->",
- _("Forwarded Message"),
- colour ? colour : "#737373",
- "<blockquote type=cite><i>", text,
- "</i></blockquote>");
-
- g_free (colour);
- } else {
- ret_text = g_strdup_printf ("-----%s-----<br>%s", _("Forwarded Message"), text ? text : "");
- }
-
- g_free (text);
-
- return ret_text;
- }
-
- return NULL;
-}
-
/**
* mail_tools_x_evolution_message_parse:
diff --git a/mail/mail-tools.h b/mail/mail-tools.h
index eaca223a2d..2f4b61123c 100644
--- a/mail/mail-tools.h
+++ b/mail/mail-tools.h
@@ -65,10 +65,6 @@ CamelFolder *mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelExcep
GHashTable *mail_lookup_url_table (CamelMimeMessage *mime_message);
-gchar *mail_tool_quote_message (CamelMimeMessage *message, const char *fmt, ...);
-
-gchar *mail_tool_forward_message (CamelMimeMessage *message, gboolean quoted);
-
CamelFolder *mail_tools_x_evolution_message_parse (char *in, unsigned int inlen, GPtrArray **uids);
char *mail_tools_folder_to_url (CamelFolder *folder);
diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c
index 05eaa174c6..001a0ae57e 100644
--- a/mail/mail-vfolder.c
+++ b/mail/mail-vfolder.c
@@ -32,7 +32,6 @@
#include "evolution-storage.h"
#include "evolution-shell-component.h"
-#include "folder-browser.h"
#include "mail-vfolder.h"
#include "mail-tools.h"
#include "mail-autofilter.h"
diff --git a/mail/mail.h b/mail/mail.h
index 1c40b52107..81832cd2a5 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -26,10 +26,8 @@
#include <shell/evolution-storage.h>
#include "mail-accounts.h"
#include "mail-account-editor.h"
-#include "mail-callbacks.h"
#include "mail-config.h"
#include "mail-config-druid.h"
-#include "mail-display-stream.h"
#include "mail-session.h"
#include "mail-types.h"
diff --git a/mail/message-browser.c b/mail/message-browser.c
deleted file mode 100644
index 9949f0ff0c..0000000000
--- a/mail/message-browser.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2001 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
-#include <gal/util/e-util.h>
-
-#include <bonobo/bonobo-exception.h>
-#include <bonobo/bonobo-ui-component.h>
-#include <bonobo/bonobo-ui-container.h>
-#include <bonobo/bonobo-ui-util.h>
-
-#include "message-browser.h"
-
-#include "mail.h"
-#include "mail-callbacks.h"
-#include "mail-tools.h"
-#include "message-list.h"
-#include "mail-ops.h"
-#include "mail-vfolder.h"
-#include "mail-autofilter.h"
-#include "mail-mt.h"
-
-#include "mail-local.h"
-#include "mail-config.h"
-
-#include "folder-browser-ui.h"
-
-#define d(x)
-
-#define MINIMUM_WIDTH 600
-#define MINIMUM_HEIGHT 400
-
-#define PARENT_TYPE BONOBO_TYPE_WINDOW
-
-/* Size of the window last time it was changed. */
-static GtkAllocation last_allocation = { 0, 0 };
-
-static BonoboWindowClass *message_browser_parent_class;
-
-static void
-message_browser_destroy (GtkObject *object)
-{
- MessageBrowser *message_browser;
-
- message_browser = MESSAGE_BROWSER (object);
-
- if (message_browser->ml_built_id) {
- g_signal_handler_disconnect (message_browser->fb->message_list, message_browser->ml_built_id);
- message_browser->ml_built_id = 0;
- }
-
- if (message_browser->loaded_id) {
- g_signal_handler_disconnect (message_browser->fb, message_browser->loaded_id);
- message_browser->loaded_id = 0;
- }
-
- if (message_browser->fb) {
- g_object_unref (message_browser->fb);
- message_browser->fb = NULL;
- }
-
- if (GTK_OBJECT_CLASS (message_browser_parent_class)->destroy)
- (GTK_OBJECT_CLASS (message_browser_parent_class)->destroy) (object);
-}
-
-static void
-message_browser_class_init (GObjectClass *object_class)
-{
- ((GtkObjectClass *)object_class)->destroy = message_browser_destroy;
-
- message_browser_parent_class = g_type_class_ref (PARENT_TYPE);
-}
-
-static void
-message_browser_init (GtkObject *object)
-{
-
-}
-
-static void
-transfer_msg_done (gboolean ok, void *data)
-{
- MessageBrowser *mb = data;
-
- gtk_widget_destroy ((GtkWidget *) mb);
-
- g_object_unref (mb);
-}
-
-static void
-transfer_msg (MessageBrowser *mb, int del)
-{
- const char *allowed_types[] = { "mail/*", "vtrash", NULL };
- extern EvolutionShellClient *global_shell_client;
- GNOME_Evolution_Folder *folder;
- static char *last_uri = NULL;
- GPtrArray *uids;
- char *desc;
-
-/* if (GTK_OBJECT_DESTROYED(mb))
- return;*/
-
- if (last_uri == NULL)
- last_uri = g_strdup ("");
-
- if (del)
- desc = _("Move message(s) to");
- else
- desc = _("Copy message(s) to");
-
- evolution_shell_client_user_select_folder (global_shell_client,
- GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (mb))),
- desc, last_uri, allowed_types, &folder);
- if (!folder)
- return;
-
- if (strcmp (last_uri, folder->evolutionUri) != 0) {
- g_free (last_uri);
- last_uri = g_strdup (folder->evolutionUri);
- }
-
- uids = g_ptr_array_new ();
- message_list_foreach (mb->fb->message_list, enumerate_msg, uids);
-
- if (del) {
- g_object_ref (mb);
- mail_transfer_messages (mb->fb->folder, uids, del,
- folder->physicalUri, 0, transfer_msg_done, mb);
- } else {
- mail_transfer_messages (mb->fb->folder, uids, del,
- folder->physicalUri, 0, NULL, NULL);
- }
-
- CORBA_free (folder);
-}
-
-
-/* UI callbacks */
-
-static void
-message_browser_close (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- gtk_widget_destroy (GTK_WIDGET (user_data));
-}
-
-static void
-message_browser_move (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- transfer_msg (user_data, TRUE);
-}
-
-static void
-message_browser_copy (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- transfer_msg (user_data, FALSE);
-}
-
-static void
-message_browser_delete (BonoboUIComponent *uih, void *user_data, const char *path)
-{
- MessageBrowser *mb = user_data;
- GPtrArray *uids;
- int i;
-
- uids = g_ptr_array_new ();
- message_list_foreach (mb->fb->message_list, enumerate_msg, uids);
- camel_folder_freeze (mb->fb->folder);
- for (i = 0; i < uids->len; i++) {
- camel_folder_set_message_flags (mb->fb->folder, uids->pdata[i],
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
- g_free (uids->pdata[i]);
- }
-
- camel_folder_thaw (mb->fb->folder);
-
- g_ptr_array_free (uids, TRUE);
-
- gtk_widget_destroy ((GtkWidget *) mb);
-}
-
-static BonoboUIVerb
-browser_verbs [] = {
- BONOBO_UI_UNSAFE_VERB ("MessageBrowserClose", message_browser_close),
- BONOBO_UI_UNSAFE_VERB ("MessageMove", message_browser_move),
- BONOBO_UI_UNSAFE_VERB ("MessageCopy", message_browser_copy),
- BONOBO_UI_UNSAFE_VERB ("MessageDelete", message_browser_delete),
- BONOBO_UI_VERB_END
-};
-
-/* FB message loading hookups */
-
-static void
-message_browser_message_loaded (FolderBrowser *fb, const char *uid, MessageBrowser *mb)
-{
- CamelMimeMessage *message;
- char *subject = NULL;
- char *title;
-
- folder_browser_ui_message_loaded(fb);
-
- message = fb->mail_display->current_message;
-
- if (message)
- subject = (char *) camel_mime_message_get_subject (message);
-
- if (subject == NULL)
- subject = _("(No subject)");
-
- title = g_strdup_printf (_("%s - Message"), subject);
-
- gtk_window_set_title (GTK_WINDOW (mb), title);
-
- g_free (title);
-}
-
-static void
-message_browser_message_list_built (MessageList *ml, MessageBrowser *mb)
-{
- const char *uid = g_object_get_data (G_OBJECT (mb), "uid");
-
- g_signal_handler_disconnect (ml, mb->ml_built_id);
- mb->ml_built_id = 0;
-
- message_list_select_uid (ml, uid);
-}
-
-static void
-message_browser_folder_loaded (FolderBrowser *fb, const char *uri, MessageBrowser *mb)
-{
- g_signal_handler_disconnect (fb, mb->loaded_id);
- mb->loaded_id = 0;
-
- mb->ml_built_id = g_signal_connect (fb->message_list, "message_list_built",
- G_CALLBACK (message_browser_message_list_built), mb);
-}
-
-static void
-message_browser_size_allocate_cb (GtkWidget *widget,
- GtkAllocation *allocation)
-{
- last_allocation = *allocation;
-}
-
-/* Construction */
-
-static void
-set_default_size (GtkWidget *widget)
-{
- int width, height;
-
- width = MAX (MINIMUM_WIDTH, last_allocation.width);
- height = MAX (MINIMUM_HEIGHT, last_allocation.height);
-
- gtk_window_set_default_size (GTK_WINDOW (widget), width, height);
-}
-
-static void
-set_bonobo_ui (GtkWidget *widget, FolderBrowser *fb)
-{
- BonoboUIContainer *uicont;
- BonoboUIComponent *uic;
- CORBA_Environment ev;
-
- uicont = bonobo_window_get_ui_container (BONOBO_WINDOW (widget));
-
- uic = bonobo_ui_component_new_default ();
- bonobo_ui_component_set_container (uic, BONOBO_OBJREF (uicont), NULL);
- folder_browser_set_ui_component (fb, uic);
-
- /* Load our UI */
-
- /*bonobo_ui_component_freeze (uic, NULL);*/
- bonobo_ui_util_set_ui (uic, PREFIX,
- EVOLUTION_UIDIR "/evolution-mail-messagedisplay.xml",
- "evolution-mail", NULL);
-
- /* Load the appropriate UI stuff from the folder browser */
-
- folder_browser_ui_add_message (fb);
-
- /* We just opened the message! We don't need to open it again. */
-
- CORBA_exception_init (&ev);
- /* remove the broken menus and toolbar items */
- bonobo_ui_component_rm (uic, "/menu/File/FileOps/MessageOpen", &ev);
- bonobo_ui_component_rm (uic, "/menu/Actions/ComponentActionsPlaceholder/MailMessageActions/GoTo", &ev);
- bonobo_ui_component_rm (uic, "/menu/Tools", &ev);
- bonobo_ui_component_rm (uic, "/Toolbar/MailNextButtons", &ev);
- CORBA_exception_free (&ev);
-
- /* Hack around the move/copy/delete commands api's */
- bonobo_ui_component_remove_listener (uic, "MessageCopy");
- bonobo_ui_component_remove_listener (uic, "MessageMove");
- bonobo_ui_component_remove_listener (uic, "MessageDelete");
-
- /* Add the Close & Move/Copy/Delete items */
-
- bonobo_ui_component_add_verb_list_with_data (uic, browser_verbs, widget);
-
- /* Done */
-
- /*bonobo_ui_component_thaw (uic, NULL);*/
-}
-
-static int
-on_key_press (GtkWidget *widget, GdkEventKey *key, gpointer data)
-{
- MessageBrowser *mb = data;
-
- if (key->state & GDK_CONTROL_MASK)
- return FALSE;
-
- switch (key->keyval) {
- case GDK_Delete:
- case GDK_KP_Delete:
- message_browser_delete (NULL, mb, NULL);
- return TRUE;
- case GDK_Escape:
- message_browser_close (NULL, mb, NULL);
- return TRUE;
- default:
- }
-
- return FALSE;
-}
-
-GtkWidget *
-message_browser_new (const GNOME_Evolution_Shell shell, const char *uri, const char *uid)
-{
- GtkWidget *vbox;
- MessageBrowser *new;
- FolderBrowser *fb;
-
- new = g_object_new (MESSAGE_BROWSER_TYPE, "title", "Ximian Evolution", NULL);
-
- g_object_set_data_full (G_OBJECT (new), "uid", g_strdup (uid), g_free);
-
- fb = FOLDER_BROWSER (folder_browser_new (shell, uri));
- g_object_ref (fb);
- gtk_object_sink ((GtkObject *) fb);
-
- new->fb = fb;
-
- set_bonobo_ui (GTK_WIDGET (new), fb);
-
- /* some evil hackery action... */
- vbox = gtk_vbox_new (TRUE, 0);
- gtk_widget_ref (GTK_WIDGET (fb->mail_display));
- gtk_widget_reparent (GTK_WIDGET (fb->mail_display), vbox);
- /* Note: normally we'd unref the fb->mail_display now, except
- that if we do then our refcounts will not be in
- harmony... both the fb *and* the message-browser need to
- own a ref on the mail_display. */
- gtk_widget_show (GTK_WIDGET (fb->mail_display));
- gtk_widget_show (vbox);
-
- g_signal_connect (new, "size-allocate", G_CALLBACK (message_browser_size_allocate_cb), NULL);
-
- bonobo_window_set_contents (BONOBO_WINDOW (new), vbox);
- gtk_widget_grab_focus (GTK_WIDGET (MAIL_DISPLAY (fb->mail_display)->html));
-
- set_default_size (GTK_WIDGET (new));
-
- /* more evil hackery... */
- new->loaded_id = g_signal_connect (fb, "folder_loaded", G_CALLBACK (message_browser_folder_loaded), new);
- g_signal_connect (fb, "message_loaded", G_CALLBACK (message_browser_message_loaded), new);
-
- g_signal_connect (new, "key_press_event", G_CALLBACK (on_key_press), new);
-
- return GTK_WIDGET (new);
-}
-
-/* Fin */
-
-E_MAKE_TYPE (message_browser, "MessageBrowser", MessageBrowser, message_browser_class_init,
- message_browser_init, PARENT_TYPE);
diff --git a/mail/message-browser.h b/mail/message-browser.h
deleted file mode 100644
index 297c90c895..0000000000
--- a/mail/message-browser.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Jeffrey Stedfast <fejj@ximian.com>
- *
- * Copyright 2001 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef _MESSAGE_BROWSER_H_
-#define _MESSAGE_BROWSER_H_
-
-#include <gnome.h>
-#include <bonobo/bonobo-window.h>
-
-#include <camel/camel-folder.h>
-#include "folder-browser.h"
-#include "mail-display.h"
-#include "mail-types.h"
-
-#define MESSAGE_BROWSER_TYPE (message_browser_get_type ())
-#define MESSAGE_BROWSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGE_BROWSER_TYPE, MessageBrowser))
-#define MESSAGE_BROWSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MESSAGE_BROWSER_TYPE, MessageBrowserClass))
-#define IS_MESSAGE_BROWSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MESSAGE_BROWSER_TYPE))
-#define IS_MESSAGE_BROWSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MESSAGE_BROWSER_TYPE))
-
-struct _MessageBrowser {
- BonoboWindow parent;
-
- /*
- * The current URI being displayed by the MessageBrowser
- */
- FolderBrowser *fb;
- gulong ml_built_id;
- gulong loaded_id;
-};
-
-
-typedef struct {
- BonoboWindowClass parent_class;
-
-} MessageBrowserClass;
-
-GtkType message_browser_get_type (void);
-
-GtkWidget *message_browser_new (const GNOME_Evolution_Shell shell,
- const char *uri, const char *uid);
-
-#endif /* _MESSAGE_BROWSER_H_ */
-
diff --git a/mail/message-list.c b/mail/message-list.c
index 77e03b656e..80bedfb406 100644
--- a/mail/message-list.c
+++ b/mail/message-list.c
@@ -61,6 +61,8 @@
#include "mail-tools.h"
#include "mail-ops.h"
+#include "em-utils.h"
+
#include "art/mail-new.xpm"
#include "art/mail-read.xpm"
#include "art/mail-replied.xpm"
@@ -86,6 +88,13 @@
#define d(x)
#define t(x)
+struct _MessageListPrivate {
+ GtkWidget *invisible; /* 4 selection */
+
+ GPtrArray *primary_uids; /* uids in primary selection */
+ GPtrArray *clipboard_uids; /* uids in clipboard selection */
+};
+
/*
* Default sizes for the ETable display
*
@@ -508,19 +517,20 @@ void
message_list_select_uid (MessageList *message_list, const char *uid)
{
ETreePath node;
+
+ if (message_list->regen) {
+ g_free(message_list->pending_select_uid);
+ message_list->pending_select_uid = g_strdup(uid);
+ }
node = g_hash_table_lookup (message_list->uid_nodemap, uid);
if (node) {
CamelMessageInfo *info;
-
+
info = get_message_info (message_list, node);
+
+ /* This will emit a changed signal that we'll pick up */
e_tree_set_cursor (message_list->tree, node);
-
- g_free (message_list->cursor_uid);
- message_list->cursor_uid = g_strdup (camel_message_info_uid (info));
-
- g_signal_emit (GTK_OBJECT (message_list), message_list_signals[MESSAGE_SELECTED], 0,
- camel_message_info_uid (info));
} else {
g_free (message_list->cursor_uid);
message_list->cursor_uid = NULL;
@@ -562,6 +572,155 @@ message_list_select_next_thread (MessageList *message_list)
}
+/**
+ * message_list_select_all:
+ * @message_list: Message List widget
+ *
+ * Selects all messages in the message list.
+ **/
+void
+message_list_select_all (MessageList *message_list)
+{
+ ESelectionModel *etsm;
+
+ etsm = e_tree_get_selection_model (message_list->tree);
+
+ e_selection_model_select_all (etsm);
+}
+
+
+typedef struct thread_select_info {
+ MessageList *ml;
+ GPtrArray *paths;
+} thread_select_info_t;
+
+static gboolean
+select_node (ETreeModel *model, ETreePath path, gpointer user_data)
+{
+ thread_select_info_t *tsi = (thread_select_info_t *) user_data;
+
+ g_ptr_array_add (tsi->paths, path);
+ return FALSE; /*not done yet*/
+}
+
+static void
+thread_select_foreach (ETreePath path, gpointer user_data)
+{
+ thread_select_info_t *tsi = (thread_select_info_t *) user_data;
+ ETreeModel *model = tsi->ml->model;
+ ETreePath node;
+
+ /* @path part of the initial selection. If it has children,
+ * we select them as well. If it doesn't, we select its siblings and
+ * their children (ie, the current node must be inside the thread
+ * that the user wants to mark.
+ */
+
+ if (e_tree_model_node_get_first_child (model, path)) {
+ node = path;
+ } else {
+ node = e_tree_model_node_get_parent (model, path);
+
+ /* Let's make an exception: if no parent, then we're about
+ * to mark the whole tree. No. */
+ if (e_tree_model_node_is_root (model, node))
+ node = path;
+ }
+
+ e_tree_model_node_traverse (model, node, select_node, tsi);
+}
+
+/**
+ * message_list_select_thread:
+ * @message_list: Message List widget
+ *
+ * Selects all messages in the current thread (based on cursor).
+ **/
+void
+message_list_select_thread (MessageList *message_list)
+{
+ ETreeSelectionModel *etsm;
+ thread_select_info_t tsi;
+ int i;
+
+ tsi.ml = message_list;
+ tsi.paths = g_ptr_array_new ();
+
+ etsm = (ETreeSelectionModel *) e_tree_get_selection_model (message_list->tree);
+
+ e_tree_selected_path_foreach (message_list->tree, thread_select_foreach, &tsi);
+
+ for (i = 0; i < tsi.paths->len; i++)
+ e_tree_selection_model_add_to_selection (etsm, tsi.paths->pdata[i]);
+
+ g_ptr_array_free (tsi.paths, TRUE);
+}
+
+
+/**
+ * message_list_invert_selection:
+ * @message_list: Message List widget
+ *
+ * Invert the current selection in the message-list.
+ **/
+void
+message_list_invert_selection (MessageList *message_list)
+{
+ ESelectionModel *etsm;
+
+ etsm = e_tree_get_selection_model (message_list->tree);
+
+ e_selection_model_invert_selection (etsm);
+}
+
+void
+message_list_copy(MessageList *ml, gboolean cut)
+{
+ struct _MessageListPrivate *p = ml->priv;
+ GPtrArray *uids;
+
+ if (p->clipboard_uids) {
+ message_list_free_uids(ml, p->clipboard_uids);
+ p->clipboard_uids = NULL;
+ }
+
+ uids = message_list_get_selected(ml);
+
+ if (uids->len > 0) {
+ if (cut) {
+ int i;
+
+ camel_folder_freeze(ml->folder);
+ for (i=0;i<uids->len;i++)
+ camel_folder_set_message_flags(ml->folder, uids->pdata[i],
+ CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED,
+ CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DELETED);
+
+ camel_folder_thaw(ml->folder);
+ }
+
+ p->clipboard_uids = uids;
+ gtk_selection_owner_set(p->invisible, GDK_SELECTION_CLIPBOARD, gtk_get_current_event_time());
+ } else {
+ message_list_free_uids(ml, uids);
+ gtk_selection_owner_set(NULL, GDK_SELECTION_CLIPBOARD, gtk_get_current_event_time());
+ }
+}
+
+gboolean
+message_list_has_primary_selection(MessageList *ml)
+{
+ return ml->priv->primary_uids != NULL;
+}
+
+void
+message_list_paste(MessageList *ml)
+{
+ gtk_selection_convert(ml->priv->invisible, GDK_SELECTION_CLIPBOARD,
+ gdk_atom_intern("x-evolution-message", FALSE),
+ GDK_CURRENT_TIME);
+}
+
/*
* SimpleTableModel::col_count
*/
@@ -1252,6 +1411,141 @@ message_list_setup_etree (MessageList *message_list, gboolean outgoing)
}
}
+static void
+ml_selection_get(GtkWidget *widget, GtkSelectionData *data, guint info, guint time_stamp, MessageList *ml)
+{
+ GPtrArray *uids;
+
+ if (info & 1)
+ uids = ml->priv->primary_uids;
+ else
+ uids = ml->priv->clipboard_uids;
+
+ if (uids == NULL)
+ return;
+
+ if (info & 2) {
+ /* text_plain */
+ printf("setting text/plain selection for uids\n");
+ em_utils_selection_set_mailbox(data, ml->folder, uids);
+ } else {
+ /* x-evolution-message */
+ printf("setting x-evolution-message selection for uids\n");
+ em_utils_selection_set_uidlist(data, ml->folder_uri, uids);
+ }
+}
+
+static void
+ml_selection_clear_event(GtkWidget *widget, GdkEventSelection *event, MessageList *ml)
+{
+ struct _MessageListPrivate *p = ml->priv;
+
+ if (event->selection == GDK_SELECTION_PRIMARY) {
+ if (p->primary_uids) {
+ message_list_free_uids(ml, p->primary_uids);
+ p->primary_uids = NULL;
+ }
+ } else if (event->selection == GDK_SELECTION_CLIPBOARD) {
+ if (p->clipboard_uids) {
+ message_list_free_uids(ml, p->clipboard_uids);
+ p->clipboard_uids = NULL;
+ }
+ }
+}
+
+static void
+ml_selection_received_uidlist(MessageList *ml, GtkSelectionData *data)
+{
+ CamelFolder *folder;
+ GPtrArray *uids;
+ char *uri;
+
+ if (em_utils_selection_get_uidlist(data, &uri, &uids) == 0)
+ return;
+
+ folder = mail_tool_uri_to_folder(uri, 0, NULL);
+ if (folder) {
+ mail_transfer_messages(folder, uids, FALSE, ml->folder_uri, 0, NULL, NULL);
+ camel_object_unref(folder);
+ } else {
+ /* FIXME: error box? */
+ g_warning("could not open paste source uri '%s'", uri);
+ em_utils_uids_free(uids);
+ }
+
+ g_free(uri);
+}
+
+static void
+ml_selection_received(GtkWidget *widget, GtkSelectionData *data, guint time, MessageList *ml)
+{
+ if (data->target != gdk_atom_intern("x-evolution-message", FALSE)) {
+ printf("Unknown selection received by message-list\n");
+
+ return;
+ }
+
+ ml_selection_received_uidlist(ml, data);
+}
+
+static GtkTargetEntry ml_drag_types[] = {
+ { "x-evolution-message", 0, 0 },
+ { "message/rfc822", 0, 1 },
+ /* not included in dest types */
+ { "text/uri-list", 0, 2 },
+};
+
+static void
+ml_tree_drag_data_get (ETree *tree, int row, ETreePath path, int col,
+ GdkDragContext *context, GtkSelectionData *data,
+ guint info, guint time, MessageList *ml)
+{
+ GPtrArray *uids;
+
+ uids = message_list_get_selected(ml);
+
+ if (uids->len > 0) {
+ switch (info) {
+ case 0 /*DND_TARGET_TYPE_X_EVOLUTION_MESSAGE*/:
+ em_utils_selection_set_uidlist(data, ml->folder_uri, uids);
+ break;
+ case 1 /*DND_TARGET_TYPE_MESSAGE_RFC822*/:
+ em_utils_selection_set_mailbox(data, ml->folder, uids);
+ break;
+ case 2 /*DND_TARGET_TYPE_TEXT_URI_LIST*/:
+ em_utils_selection_set_urilist(data, ml->folder, uids);
+ break;
+ }
+ }
+
+ message_list_free_uids(ml, uids);
+}
+
+static void
+ml_tree_drag_data_received (ETree *tree, int row, ETreePath path, int col,
+ GdkDragContext *context, gint x, gint y,
+ GtkSelectionData *data, guint info,
+ guint time, MessageList *ml)
+{
+ /* this means we are receiving no data */
+ if (data->data == NULL || data->length == -1)
+ return;
+
+ /* Note: we don't receive text/uri-list, since we have no
+ guarantee on what the content would be */
+
+ switch (info) {
+ case 1 /*DND_TARGET_TYPE_MESSAGE_RFC822*/:
+ em_utils_selection_get_mailbox(data, ml->folder);
+ break;
+ case 0 /*DND_TARGET_TYPE_X_EVOLUTION_MESSAGE*/:
+ ml_selection_received_uidlist(ml, data);
+ break;
+ }
+
+ gtk_drag_finish(context, TRUE, TRUE, time);
+}
+
/*
* GtkObject::init
*/
@@ -1259,6 +1553,8 @@ static void
message_list_init (GtkObject *object)
{
MessageList *message_list = MESSAGE_LIST (object);
+ struct _MessageListPrivate *p;
+ GdkAtom matom;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (message_list), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
@@ -1275,6 +1571,22 @@ message_list_init (GtkObject *object)
message_list->uid_nodemap = g_hash_table_new (g_str_hash, g_str_equal);
message_list->async_event = mail_async_event_new();
+
+ /* TODO: Should this only get the selection if we're realised? */
+ p = message_list->priv = g_malloc0(sizeof(*message_list->priv));
+ p->invisible = gtk_invisible_new();
+ g_object_ref(p->invisible);
+ gtk_object_sink((GtkObject *)p->invisible);
+
+ matom = gdk_atom_intern("x-evolution-message", FALSE);
+ gtk_selection_add_target(p->invisible, GDK_SELECTION_CLIPBOARD, matom, 0);
+ gtk_selection_add_target(p->invisible, GDK_SELECTION_PRIMARY, matom, 1);
+ gtk_selection_add_target(p->invisible, GDK_SELECTION_CLIPBOARD, GDK_SELECTION_TYPE_STRING, 2);
+ gtk_selection_add_target(p->invisible, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 3);
+
+ g_signal_connect(p->invisible, "selection_get", G_CALLBACK(ml_selection_get), message_list);
+ g_signal_connect(p->invisible, "selection_clear_event", G_CALLBACK(ml_selection_clear_event), message_list);
+ g_signal_connect(p->invisible, "selection_received", G_CALLBACK(ml_selection_received), message_list);
}
static void
@@ -1287,7 +1599,8 @@ static void
message_list_destroy(GtkObject *object)
{
MessageList *message_list = MESSAGE_LIST (object);
-
+ struct _MessageListPrivate *p = message_list->priv;
+
if (message_list->async_event) {
mail_async_event_destroy(message_list->async_event);
message_list->async_event = NULL;
@@ -1307,7 +1620,12 @@ message_list_destroy(GtkObject *object)
camel_object_unref (message_list->folder);
message_list->folder = NULL;
}
-
+
+ if (p->invisible) {
+ g_object_unref(p->invisible);
+ p->invisible = NULL;
+ }
+
if (message_list->extras) {
g_object_unref (message_list->extras);
message_list->extras = NULL;
@@ -1337,6 +1655,7 @@ static void
message_list_finalise (GObject *object)
{
MessageList *message_list = MESSAGE_LIST (object);
+ struct _MessageListPrivate *p = message_list->priv;
g_hash_table_foreach (message_list->normalised_hash, normalised_free, NULL);
g_hash_table_destroy (message_list->normalised_hash);
@@ -1355,6 +1674,16 @@ message_list_finalise (GObject *object)
g_mutex_free(message_list->hide_lock);
+ g_free(message_list->folder_uri);
+ message_list->folder_uri = NULL;
+
+ if (p->primary_uids)
+ message_list_free_uids(message_list, p->primary_uids);
+ if (p->clipboard_uids)
+ message_list_free_uids(message_list, p->clipboard_uids);
+
+ g_free(p);
+
G_OBJECT_CLASS (message_list_parent_class)->finalize (object);
}
@@ -1446,8 +1775,30 @@ message_list_construct (MessageList *message_list)
g_signal_connect((message_list->tree), "selection_change",
G_CALLBACK (on_selection_changed_cmd), message_list);
+
+ e_tree_drag_source_set(message_list->tree, GDK_BUTTON1_MASK,
+ ml_drag_types, sizeof(ml_drag_types)/sizeof(ml_drag_types[0]),
+ GDK_ACTION_MOVE|GDK_ACTION_COPY);
+
+ g_signal_connect(message_list->tree, "tree_drag_data_get",
+ G_CALLBACK(ml_tree_drag_data_get), message_list);
+
+ /* note, we only include 2 types, we don't include text/uri-list as a receiver */
+ e_tree_drag_dest_set(message_list->tree, GTK_DEST_DEFAULT_ALL,
+ ml_drag_types, 2,
+ GDK_ACTION_MOVE|GDK_ACTION_COPY);
+
+ g_signal_connect(message_list->tree, "tree_drag_data_received",
+ G_CALLBACK(ml_tree_drag_data_received), message_list);
}
+/**
+ * message_list_new:
+ *
+ * Creates a new message-list widget.
+ *
+ * Returns a new message-list widget.
+ **/
GtkWidget *
message_list_new (void)
{
@@ -2128,17 +2479,28 @@ message_changed (CamelObject *o, gpointer event_data, gpointer user_data)
mail_async_event_emit(ml->async_event, MAIL_ASYNC_GUI, (MailAsyncFunc)main_folder_changed, o, changes, user_data);
}
+
+/**
+ * message_list_set_folder:
+ * @message_list: Message List widget
+ * @folder: folder backend to be set
+ * @uri: uri of @folder.
+ * @outgoing: whether this is an outgoing folder
+ *
+ * Sets @folder to be the backend folder for @message_list. If
+ * @outgoing is %TRUE, then the message-list UI changes to default to
+ * the "Outgoing folder" column view.
+ **/
void
-message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, gboolean outgoing)
+message_list_set_folder (MessageList *message_list, CamelFolder *folder, const char *uri, gboolean outgoing)
{
gboolean hide_deleted;
GConfClient *gconf;
CamelException ex;
- g_return_if_fail (message_list != NULL);
g_return_if_fail (IS_MESSAGE_LIST (message_list));
- if (message_list->folder == camel_folder)
+ if (message_list->folder == folder)
return;
camel_exception_init (&ex);
@@ -2171,8 +2533,12 @@ message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, g
camel_folder_thread_messages_unref(message_list->thread_tree);
message_list->thread_tree = NULL;
}
-
- message_list->folder = camel_folder;
+
+ if (message_list->folder_uri != uri) {
+ g_free(message_list->folder_uri);
+ message_list->folder_uri = g_strdup(uri);
+ message_list->folder = folder;
+ }
if (message_list->cursor_uid) {
g_free(message_list->cursor_uid);
@@ -2180,9 +2546,9 @@ message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, g
g_signal_emit(message_list, message_list_signals[MESSAGE_SELECTED], 0, NULL);
}
- if (camel_folder) {
+ if (folder) {
/* Setup the strikeout effect for non-trash folders */
- if (!(camel_folder->folder_flags & CAMEL_FOLDER_IS_TRASH)) {
+ if (!(folder->folder_flags & CAMEL_FOLDER_IS_TRASH)) {
ECell *cell;
cell = e_table_extras_get_cell (message_list->extras, "render_date");
@@ -2204,16 +2570,16 @@ message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, g
/* Build the etree suitable for this folder */
message_list_setup_etree (message_list, outgoing);
- camel_object_hook_event (camel_folder, "folder_changed",
+ camel_object_hook_event (folder, "folder_changed",
folder_changed, message_list);
- camel_object_hook_event (camel_folder, "message_changed",
+ camel_object_hook_event (folder, "message_changed",
message_changed, message_list);
- camel_object_ref (camel_folder);
+ camel_object_ref (folder);
gconf = mail_config_get_gconf_client ();
hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL);
- message_list->hidedeleted = hide_deleted && !(camel_folder->folder_flags & CAMEL_FOLDER_IS_TRASH);
+ message_list->hidedeleted = hide_deleted && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH);
hide_load_state (message_list);
mail_regen_list (message_list, message_list->search, NULL, NULL);
@@ -2266,26 +2632,33 @@ on_cursor_activated_cmd (ETree *tree, int row, ETreePath path, gpointer user_dat
}
static void
-get_selected_cb(ETreePath path, MessageList *ml)
-{
- g_free(ml->cursor_uid);
- ml->cursor_uid = g_strdup(get_message_uid(ml, path));
-}
-
-static void
on_selection_changed_cmd(ETree *tree, MessageList *ml)
{
- ESelectionModel *esm = e_tree_get_selection_model (ml->tree);
- int selected = e_selection_model_selected_count (esm);
+ GPtrArray *uids;
+ uids = message_list_get_selected(ml);
g_free(ml->cursor_uid);
- ml->cursor_uid = NULL;
-
- if (selected == 1)
- e_tree_selected_path_foreach(ml->tree, (ETreeForeachFunc)get_selected_cb, ml);
+ if (uids->len == 1)
+ ml->cursor_uid = g_strdup(uids->pdata[0]);
+ else
+ ml->cursor_uid = NULL;
- if ((selected == 1 || selected == 0) && !ml->idle_id)
+ if (uids->len <= 1 && !ml->idle_id)
ml->idle_id = g_idle_add_full (G_PRIORITY_LOW, on_cursor_activated_idle, ml, NULL);
+
+ if (ml->priv->primary_uids) {
+ message_list_free_uids(ml, ml->priv->primary_uids);
+ ml->priv->primary_uids = NULL;
+ }
+
+ if (uids->len > 0) {
+ ml->priv->primary_uids = uids;
+ gtk_selection_owner_set(ml->priv->invisible, GDK_SELECTION_PRIMARY, gtk_get_current_event_time());
+ } else {
+ message_list_free_uids(ml, uids);
+ gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, gtk_get_current_event_time());
+ }
+
}
static gint
@@ -2368,6 +2741,47 @@ message_list_foreach (MessageList *message_list,
mlfe_callback, &mlfe_data);
}
+struct _ml_selected_data {
+ MessageList *ml;
+ GPtrArray *uids;
+};
+
+static void
+ml_getselected_cb(ETreePath path, void *user_data)
+{
+ struct _ml_selected_data *data = user_data;
+ const char *uid;
+
+ if (e_tree_model_node_is_root (data->ml->model, path))
+ return;
+
+ uid = get_message_uid(data->ml, path);
+ g_assert(uid != NULL);
+ g_ptr_array_add(data->uids, g_strdup(uid));
+}
+
+GPtrArray *
+message_list_get_selected(MessageList *ml)
+{
+ struct _ml_selected_data data = {
+ ml,
+ g_ptr_array_new()
+ };
+
+ e_tree_selected_path_foreach(ml->tree, ml_getselected_cb, &data);
+
+ return data.uids;
+}
+
+void message_list_free_uids(MessageList *ml, GPtrArray *uids)
+{
+ int i;
+
+ for (i=0;i<uids->len;i++)
+ g_free(uids->pdata[i]);
+ g_ptr_array_free(uids, TRUE);
+}
+
/* set whether we are in threaded view or flat view */
void
message_list_set_threaded (MessageList *ml, gboolean threaded)
@@ -2845,6 +3259,14 @@ regen_list_free (struct _mail_msg *mm)
However, since we have a received function, this will always be called in gui thread */
m->ml->regen = g_list_remove(m->ml->regen, m);
+ if (m->ml->regen == NULL && m->ml->pending_select_uid) {
+ char *uid = m->ml->pending_select_uid;
+
+ m->ml->pending_select_uid = NULL;
+ message_list_select_uid(m->ml, uid);
+ g_free(uid);
+ }
+
g_object_unref(m->ml);
}
diff --git a/mail/message-list.h b/mail/message-list.h
index de2ddc199f..a5c88cf308 100644
--- a/mail/message-list.h
+++ b/mail/message-list.h
@@ -1,4 +1,26 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
#ifndef _MESSAGE_LIST_H_
#define _MESSAGE_LIST_H_
@@ -9,6 +31,11 @@
#include <gal/e-table/e-tree-scrolled.h>
#include "mail-types.h"
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
#define MESSAGE_LIST_TYPE (message_list_get_type ())
#define MESSAGE_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGE_LIST_TYPE, MessageList))
#define MESSAGE_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MESSAGE_LIST_TYPE, MessageListClass))
@@ -55,14 +82,17 @@ enum {
struct _MessageList {
ETreeScrolled parent;
+ struct _MessageListPrivate *priv;
+
/* The table */
ETreeModel *model;
ETree *tree;
ETreePath tree_root;
ETableExtras *extras;
- /* The folder */
+ /* The folder & matching uri */
CamelFolder *folder;
+ char *folder_uri;
GHashTable *uid_nodemap; /* uid (from info) -> tree node mapping */
@@ -99,6 +129,7 @@ struct _MessageList {
/* list of outstanding regeneration requests */
GList *regen;
+ char *pending_select_uid; /* set if we were busy regnerating while we had a select come in */
/* the current camel folder thread tree, if any */
struct _CamelFolderThread *thread_tree;
@@ -126,14 +157,16 @@ typedef enum {
GtkType message_list_get_type (void);
GtkWidget *message_list_new (void);
-void message_list_set_folder (MessageList *message_list,
- CamelFolder *camel_folder,
- gboolean outgoing);
+void message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, const char *uri, gboolean outgoing);
void message_list_foreach (MessageList *message_list,
MessageListForeachFunc callback,
gpointer user_data);
+GPtrArray *message_list_get_selected(MessageList *ml);
+void message_list_free_uids(MessageList *ml, GPtrArray *uids);
+
+/* select next/prev message helpers */
gboolean message_list_select (MessageList *message_list,
MessageListSelectDirection direction,
guint32 flags,
@@ -143,24 +176,38 @@ gboolean message_list_select (MessageList *message_list,
void message_list_select_uid (MessageList *message_list,
const char *uid);
-void message_list_select_next_thread (MessageList *messageList);
+void message_list_select_next_thread (MessageList *ml);
+
+/* selection manipulation */
+void message_list_select_all (MessageList *ml);
+void message_list_select_thread (MessageList *ml);
+void message_list_invert_selection (MessageList *ml);
+
+/* clipboard stuff */
+void message_list_copy(MessageList *ml, gboolean cut);
+gboolean message_list_has_primary_selection(MessageList *ml);
+void message_list_paste (MessageList *ml);
/* info */
-unsigned int message_list_length(MessageList *ml);
-unsigned int message_list_hidden(MessageList *ml);
+unsigned int message_list_length (MessageList *ml);
+unsigned int message_list_hidden (MessageList *ml);
/* hide specific messages */
-void message_list_hide_add(MessageList *ml, const char *expr, unsigned int lower, unsigned int upper);
-void message_list_hide_uids(MessageList *ml, GPtrArray *uids);
-void message_list_hide_clear(MessageList *ml);
+void message_list_hide_add (MessageList *ml, const char *expr, unsigned int lower, unsigned int upper);
+void message_list_hide_uids (MessageList *ml, GPtrArray *uids);
+void message_list_hide_clear (MessageList *ml);
-void message_list_set_threaded(MessageList *ml, gboolean threaded);
-void message_list_set_hidedeleted(MessageList *ml, gboolean hidedeleted);
-void message_list_set_search(MessageList *ml, const char *search);
+void message_list_set_threaded (MessageList *ml, gboolean threaded);
+void message_list_set_hidedeleted (MessageList *ml, gboolean hidedeleted);
+void message_list_set_search (MessageList *ml, const char *search);
void message_list_save_state (MessageList *ml);
#define MESSAGE_LIST_LOCK(m, l) g_mutex_lock(((MessageList *)m)->l)
#define MESSAGE_LIST_UNLOCK(m, l) g_mutex_unlock(((MessageList *)m)->l)
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
#endif /* _MESSAGE_LIST_H_ */
diff --git a/mail/subscribe-dialog.c b/mail/subscribe-dialog.c
deleted file mode 100644
index cf9768aff0..0000000000
--- a/mail/subscribe-dialog.c
+++ /dev/null
@@ -1,1663 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/* subscribe-dialog.c: Subscribe dialog */
-/*
- * Authors: Chris Toshok <toshok@ximian.com>
- * Peter Williams <peterw@ximian.com>
- *
- * Copyright 2000 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-/* This doens't do what it's supposed to do ...
- I think etree changed so it just fills out the whole tree always anyway */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-
-#include <gal/util/e-util.h>
-
-#include <gal/e-table/e-cell-toggle.h>
-#include <gal/e-table/e-cell-text.h>
-#include <gal/e-table/e-cell-tree.h>
-
-#include <gal/e-table/e-tree-scrolled.h>
-#include <gal/e-table/e-tree-memory-callbacks.h>
-#include <gal/e-table/e-tree.h>
-
-#include <pthread.h>
-
-#include "evolution-shell-component-utils.h"
-#include "mail.h"
-#include "mail-tools.h"
-#include "mail-ops.h"
-#include "mail-mt.h"
-#include "mail-folder-cache.h"
-#include "camel/camel-exception.h"
-#include "camel/camel-store.h"
-#include "camel/camel-session.h"
-#include "subscribe-dialog.h"
-
-#include "art/empty.xpm"
-#include "art/mark.xpm"
-
-#define d(x)
-
-/* Things to test.
- * - Feature
- * + How to check that it works.
- *
- * - Proper stores displayed
- * + Skip stores that don't support subscriptions
- * + Skip disabled stores
- * - Changing subscription status
- * + Select single folder, double-click row -> toggled
- * + Select multiple folders, press subscribe -> all selected folders end up subscribed
- * - (un)Subscribing from/to already (un)subscribed folder
- * + Check that no IMAP command is sent
- * - Switching views between stores
- * + Proper tree shown
- * - No crashes when buttons are pressed with "No store" screen
- * + obvious
- * - Restoring filter settings when view switched
- * + Enter search, change view, change back -> filter checked and search entry set
- * + Clear search, change view, change back -> "all" checked
- * - Cancelling in middle of get_store
- * + Enter invalid hostname, open dialog, click Close
- * - Cancelling in middle if listing
- * + Open large directory, click Close
- * - Cancelling in middle of subscription op
- * + How to test?
- * - Test with both IMAP and NNTP
- * + obvious
- * - Verify that refresh view works
- * + obvious
- * - No unnecessary tree rebuilds
- * + Show All folders, change filter with empty search -> no tree rebuild
- * + Converse
- * - No out of date tree
- * + Show All Folders, change to filter with a search -> tree rebuild
- * - Tree construction logic (mostly IMAP-specific terminology)
- * + Tree is created progressively
- * + No wasted LIST responses
- * + No extraneous LIST commands
- * + Specifying "folder names begin with" works
- * + Always show folders below IMAP namespace (no escaping the namespace)
- * + Don't allow subscription to NoSelect folders
- * + IMAP accounts always show INBOX
- * - Shell interactions
- * + Folders are properly created / delete from folder tree when subscribed / unsubscribed
- * + Folders with spaces in names / 8bit chars
- * + Toplevel as well as subfolders
- * + Mail Folder Cache doesn't complain
- * - No ETable wackiness
- * + Verify columns cannot be DnD'd
- * + Alphabetical order always
- * - UI cleanliness
- * + Keybindings work
- * + Some widget has focus by default
- * + Escape / enter work
- * + Close button works
- */
-
-/* FIXME: we should disable/enable the subscribe/unsubscribe buttons as
- * appropriate when only a single folder is selected. We need a
- * mechanism to learn when the selected folder's subscription status
- * changes, so when the user double-clicks it (eg) the buttons can
- * (de)sensitize appropriately. See Ximian bug #7673.
- */
-
-/*#define NEED_TOGGLE_SELECTION*/
-
-typedef struct _FolderETree FolderETree;
-typedef struct _FolderETreeClass FolderETreeClass;
-
-typedef void (*FolderETreeActivityCallback) (int level, gpointer user_data);
-
-struct _FolderETree {
- ETreeMemory parent;
- ETreePath root;
-
- GHashTable *scan_ops;
- GHashTable *subscribe_ops;
-
- GHashTable *node_full_name;
-
- CamelStore *store;
- EvolutionStorage *e_storage;
- char *service_name;
-
- FolderETreeActivityCallback activity_cb;
- gpointer activity_data;
- int activity_level;
-};
-
-struct _FolderETreeClass {
- ETreeMemoryClass parent;
-};
-
-static GtkObjectClass *folder_etree_parent_class = NULL;
-
-typedef struct _FolderETreeExtras FolderETreeExtras;
-typedef struct _FolderETreeExtrasClass FolderETreeExtrasClass;
-
-enum {
- FOLDER_COL_SUBSCRIBED,
- FOLDER_COL_NAME,
- FOLDER_COL_LAST
-};
-
-struct _FolderETreeExtras {
- ETableExtras parent;
- GdkPixbuf *toggles[2];
-};
-
-struct _FolderETreeExtrasClass {
- ETableExtrasClass parent;
-};
-
-static GtkObjectClass *ftree_extras_parent_class = NULL;
-
-/* util */
-
-static void
-recursive_add_folder (EvolutionStorage *storage, const char *path, const char *name, const char *url)
-{
- char *parent, *pname, *p;
-
- p = strrchr (path, '/');
- if (p && p != path) {
- parent = g_strndup (path, p - path);
- if (!evolution_storage_folder_exists (storage, parent)) {
- p = strrchr (parent, '/');
- if (p)
- pname = g_strdup (p + 1);
- else
- pname = g_strdup ("");
- recursive_add_folder (storage, parent, pname, "");
- g_free (pname);
- }
- g_free (parent);
- }
-
- evolution_storage_new_folder (storage, path, name, "mail", url, name, NULL, FALSE, TRUE, 0);
-}
-
-/* ** Get one level of folderinfo ****************************************** */
-
-typedef void (*SubscribeShortFolderinfoFunc) (CamelStore *store, char *prefix, CamelFolderInfo *info, gpointer data);
-
-int subscribe_get_short_folderinfo (FolderETree *ftree, const char *prefix,
- SubscribeShortFolderinfoFunc func, gpointer user_data);
-
-struct _get_short_folderinfo_msg {
- struct _mail_msg msg;
-
- char *prefix;
-
- FolderETree *ftree;
- CamelFolderInfo *info;
-
- SubscribeShortFolderinfoFunc func;
- gpointer user_data;
-};
-
-static char *
-get_short_folderinfo_desc (struct _mail_msg *mm, int done)
-{
- struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
- char *ret, *name;
-
- name = camel_service_get_name (CAMEL_SERVICE (m->ftree->store), TRUE);
-
- if (m->prefix)
- ret = g_strdup_printf (_("Scanning folders under %s on \"%s\""), m->prefix, name);
- else
- ret = g_strdup_printf (_("Scanning root-level folders on \"%s\""), name);
-
- g_free (name);
- return ret;
-}
-
-static void
-get_short_folderinfo_get (struct _mail_msg *mm)
-{
- struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
-
- m->info = camel_store_get_folder_info (m->ftree->store, m->prefix, CAMEL_STORE_FOLDER_INFO_FAST, &mm->ex);
-
- d(printf("%d: getted folderinfo '%s'\n", mm->seq, m->prefix));
-}
-
-static void
-get_short_folderinfo_got (struct _mail_msg *mm)
-{
- struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
-
- d(printf("%d: got folderinfo '%s'\n", mm->seq, m->prefix));
-
- if (camel_exception_is_set (&mm->ex) && camel_exception_get_id(&mm->ex) != CAMEL_EXCEPTION_USER_CANCEL) {
- g_warning ("Error getting folder info from store at %s: %s",
- camel_service_get_url (CAMEL_SERVICE (m->ftree->store)),
- camel_exception_get_description (&mm->ex));
- }
-
- if (m->func)
- m->func (m->ftree->store, m->prefix, m->info, m->user_data);
-}
-
-static void
-get_short_folderinfo_free (struct _mail_msg *mm)
-{
- struct _get_short_folderinfo_msg *m = (struct _get_short_folderinfo_msg *) mm;
-
- camel_store_free_folder_info (m->ftree->store, m->info);
- g_object_unref((m->ftree));
-
- g_free (m->prefix); /* may be NULL but that's ok */
-}
-
-static struct _mail_msg_op get_short_folderinfo_op = {
- get_short_folderinfo_desc,
- get_short_folderinfo_get,
- get_short_folderinfo_got,
- get_short_folderinfo_free,
-};
-
-int
-subscribe_get_short_folderinfo (FolderETree *ftree,
- const char *prefix,
- SubscribeShortFolderinfoFunc func,
- gpointer user_data)
-{
- struct _get_short_folderinfo_msg *m;
- int id;
-
- m = mail_msg_new (&get_short_folderinfo_op, NULL, sizeof(*m));
-
- m->ftree = ftree;
- g_object_ref((ftree));
- m->prefix = g_strdup (prefix);
- m->func = func;
- m->user_data = user_data;
-
- d(printf("%d: get folderinfo '%s'\n", m->msg.seq, m->prefix));
-
- id = m->msg.seq;
- e_thread_put (mail_thread_queued, (EMsg *)m);
- return id;
-}
-
-/* ** Subscribe folder operation **************************************** */
-
-typedef void (*SubscribeFolderCallback) (const char *, const char *, gboolean, gboolean, gpointer);
-
-struct _subscribe_msg {
- struct _mail_msg msg;
-
- CamelStore *store;
- gboolean subscribe;
- char *full_name;
- char *name;
-
- SubscribeFolderCallback cb;
- gpointer cb_data;
-};
-
-static char *
-subscribe_folder_desc (struct _mail_msg *mm, int done)
-{
- struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
-
- if (m->subscribe)
- return g_strdup_printf (_("Subscribing to folder \"%s\""), m->name);
- else
- return g_strdup_printf (_("Unsubscribing to folder \"%s\""), m->name);
-}
-
-static void
-subscribe_folder_subscribe (struct _mail_msg *mm)
-{
- struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
-
- if (m->subscribe)
- camel_store_subscribe_folder (m->store, m->full_name, &mm->ex);
- else
- camel_store_unsubscribe_folder (m->store, m->full_name, &mm->ex);
-}
-
-static void
-subscribe_folder_subscribed (struct _mail_msg *mm)
-{
- struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
-
- if (m->cb)
- (m->cb) (m->full_name, m->name, m->subscribe,
- !camel_exception_is_set (&mm->ex), m->cb_data);
-}
-
-static void
-subscribe_folder_free (struct _mail_msg *mm)
-{
- struct _subscribe_msg *m = (struct _subscribe_msg *) mm;
-
- g_free (m->name);
- g_free (m->full_name);
-
- camel_object_unref (m->store);
-}
-
-static struct _mail_msg_op subscribe_folder_op = {
- subscribe_folder_desc,
- subscribe_folder_subscribe,
- subscribe_folder_subscribed,
- subscribe_folder_free,
-};
-
-static int
-subscribe_do_subscribe_folder (CamelStore *store, const char *full_name, const char *name,
- gboolean subscribe, SubscribeFolderCallback cb, gpointer cb_data)
-{
- struct _subscribe_msg *m;
- int id;
-
- g_return_val_if_fail (CAMEL_IS_STORE (store), 0);
- g_return_val_if_fail (full_name, 0);
-
- m = mail_msg_new (&subscribe_folder_op, NULL, sizeof(*m));
- m->store = store;
- m->subscribe = subscribe;
- m->name = g_strdup (name);
- m->full_name = g_strdup (full_name);
- m->cb = cb;
- m->cb_data = cb_data;
-
- camel_object_ref(store);
-
- id = m->msg.seq;
- e_thread_put (mail_thread_queued, (EMsg *)m);
- return id;
-}
-
-/* ** FolderETree Extras *************************************************** */
-
-static void
-fete_finalise (GObject *object)
-{
- FolderETreeExtras *extras = (FolderETreeExtras *) object;
-
- g_object_unref (extras->toggles[0]);
- g_object_unref (extras->toggles[1]);
-
- ((GObjectClass *)ftree_extras_parent_class)->finalize (object);
-}
-
-static void
-fete_class_init (GObjectClass *object_class)
-{
- object_class->finalize = fete_finalise;
-
- ftree_extras_parent_class = g_type_class_ref (E_TABLE_EXTRAS_TYPE);
-}
-
-static void
-fete_init (GtkObject *object)
-{
- FolderETreeExtras *extras = (FolderETreeExtras *) object;
- ECell *cell;
- ECell *text_cell;
-
- /* text column */
-
- cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
- text_cell = cell;
- g_object_set (G_OBJECT (cell),
- "bold_column", FOLDER_COL_SUBSCRIBED,
- NULL);
- e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_text", cell);
-
- /* toggle column */
-
- extras->toggles[0] = gdk_pixbuf_new_from_xpm_data ((const char **)empty_xpm);
- extras->toggles[1] = gdk_pixbuf_new_from_xpm_data ((const char **)mark_xpm);
- cell = e_cell_toggle_new (0, 2, extras->toggles);
- e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_toggle", cell);
-
- /* tree cell */
-
- cell = e_cell_tree_new (NULL, NULL, TRUE, text_cell);
- e_table_extras_add_cell (E_TABLE_EXTRAS (extras), "cell_tree", cell);
-
- /* misc */
-
- e_table_extras_add_pixbuf (E_TABLE_EXTRAS (extras), "subscribed-image", extras->toggles[1]);
-}
-
-/* naughty! */
-static
-E_MAKE_TYPE (fete, "FolderETreeExtras", FolderETreeExtras, fete_class_init, fete_init, E_TABLE_EXTRAS_TYPE);
-
-/* ** Global Extras ******************************************************** */
-
-static FolderETreeExtras *global_extras = NULL;
-
-static void
-global_extras_destroyed (void *user_data, GObject *obj)
-{
- global_extras = NULL;
-}
-
-static ETableExtras *
-subscribe_get_global_extras (void)
-{
- if (global_extras == NULL) {
- global_extras = g_object_new (fete_get_type(), NULL);
- /*g_object_ref(global_extras);
- gtk_object_sink((GtkObject *)global_extras);*/
- g_object_weak_ref(G_OBJECT(global_extras), global_extras_destroyed, NULL);
- } else {
- g_object_ref(global_extras);
- }
-
- return E_TABLE_EXTRAS (global_extras);
-}
-
-/* ** Folder Tree Node ***************************************************** */
-
-typedef struct _ftree_node ftree_node;
-
-struct _ftree_node {
- guint8 flags;
- char *cache;
- int uri_offset;
- int full_name_offset;
-
- /* format: {name}{\0}{uri}{\0}{full_name}{\0}
- * (No braces). */
- char data[1];
-};
-
-#define FTREE_NODE_GOT_CHILDREN (1 << 0)
-#define FTREE_NODE_SUBSCRIBABLE (1 << 1)
-#define FTREE_NODE_SUBSCRIBED (1 << 2)
-#define FTREE_NODE_ROOT (1 << 3)
-
-static ftree_node *
-ftree_node_new_root (void)
-{
- ftree_node *node;
-
- node = g_malloc (sizeof (ftree_node));
- node->flags = FTREE_NODE_ROOT;
- node->uri_offset = 0;
- node->full_name_offset = 0;
- node->data[0] = '\0';
-
- return node;
-}
-
-static ftree_node *
-ftree_node_new (CamelStore *store, CamelFolderInfo *fi)
-{
- ftree_node *node;
- int uri_offset, full_name_offset;
- size_t size;
-
- uri_offset = strlen (fi->name) + 1;
- full_name_offset = uri_offset + strlen (fi->url) + 1;
- size = full_name_offset + strlen (fi->full_name);
-
- /* - 1 for sizeof(node.data) but +1 for terminating \0 */
- node = g_malloc (sizeof (*node) + size);
-
- node->cache = NULL;
-
- node->flags = FTREE_NODE_SUBSCRIBABLE;
-
- /* subscribed? */
-
- if (camel_store_folder_subscribed (store, fi->full_name))
- node->flags |= FTREE_NODE_SUBSCRIBED;
-
- /* Copy strings */
-
- node->uri_offset = uri_offset;
- node->full_name_offset = full_name_offset;
-
- strcpy (node->data, fi->name);
- strcpy (node->data + uri_offset, fi->url);
- strcpy (node->data + full_name_offset, fi->full_name);
-
- /* Done */
-
- return node;
-}
-
-#define ftree_node_subscribable(node) ( ((ftree_node *) (node))->flags & FTREE_NODE_SUBSCRIBABLE )
-#define ftree_node_subscribed(node) ( ((ftree_node *) (node))->flags & FTREE_NODE_SUBSCRIBED )
-#define ftree_node_get_name(node) ( ((ftree_node *) (node))->data )
-#define ftree_node_get_full_name(node) ( ((ftree_node *) (node))->data + ((ftree_node *) (node))->full_name_offset )
-#define ftree_node_get_uri(node) ( ((ftree_node *) (node))->data + ((ftree_node *) (node))->uri_offset )
-
-/* ** Folder Tree Model **************************************************** */
-
-/* A subscribe or scan operation */
-
-typedef struct _ftree_op_data ftree_op_data;
-
-struct _ftree_op_data {
- FolderETree *ftree;
- ETreePath path;
- ftree_node *data;
- int handle;
-};
-
-
-/* ETreeModel functions */
-
-static int
-fe_column_count (ETreeModel *etm)
-{
- return FOLDER_COL_LAST;
-}
-
-static void *
-fe_duplicate_value (ETreeModel *etm, int col, const void *val)
-{
- return g_strdup (val);
-}
-
-static void
-fe_free_value (ETreeModel *etm, int col, void *val)
-{
- g_free (val);
-}
-
-static void*
-fe_init_value (ETreeModel *etm, int col)
-{
- return g_strdup ("");
-}
-
-static gboolean
-fe_value_is_empty (ETreeModel *etm, int col, const void *val)
-{
- return !(val && *(char *)val);
-}
-
-static char *
-fe_value_to_string (ETreeModel *etm, int col, const void *val)
-{
- return g_strdup (val);
-}
-
-static GdkPixbuf *
-fe_icon_at (ETreeModel *etree, ETreePath path)
-{
- return NULL; /* XXX no icons for now */
-}
-
-static gpointer
-fe_root_value_at (FolderETree *ftree, int col)
-{
- switch (col) {
- case FOLDER_COL_NAME:
- return ftree->service_name;
- case FOLDER_COL_SUBSCRIBED:
- return GINT_TO_POINTER (0);
- default:
- printf ("Oh no, unimplemented column %d in subscribe dialog\n", col);
- }
-
- return NULL;
-}
-
-static gpointer
-fe_real_value_at (FolderETree *ftree, int col, gpointer data)
-{
- switch (col) {
- case FOLDER_COL_NAME:
- return ftree_node_get_name (data);
- case FOLDER_COL_SUBSCRIBED:
- if (ftree_node_subscribed (data))
- return GINT_TO_POINTER (1);
- return GINT_TO_POINTER (0);
- default:
- printf ("Oh no, unimplemented column %d in subscribe dialog\n", col);
- }
-
- return NULL;
-}
-
-static void *
-fe_value_at (ETreeModel *etree, ETreePath path, int col)
-{
- FolderETree *ftree = (FolderETree *) etree;
- gpointer node_data;
-
- if (path == ftree->root)
- return fe_root_value_at (ftree, col);
-
- node_data = e_tree_memory_node_get_data (E_TREE_MEMORY (etree), path);
- return fe_real_value_at (ftree, col, node_data);
-}
-
-static void
-fe_set_value_at (ETreeModel *etree, ETreePath path, int col, const void *val)
-{
- /* nothing */
-}
-
-static gboolean
-fe_return_false (void)
-{
- return FALSE;
-}
-
-static gint
-fe_sort_folder (ETreeMemory *etmm, ETreePath left, ETreePath right, gpointer user_data)
-{
- ftree_node *n_left, *n_right;
-
- n_left = e_tree_memory_node_get_data (etmm, left);
- n_right = e_tree_memory_node_get_data (etmm, right);
-
- /* if in utf8 locale ? */
- return strcasecmp (ftree_node_get_name (n_left), ftree_node_get_name (n_right));
-}
-
-
-static void fe_check_for_children (FolderETree *ftree, ETreePath path);
-
-/* scanning */
-static void
-fe_got_children (CamelStore *store, char *prefix, CamelFolderInfo *info, gpointer data)
-{
- ftree_op_data *closure = (ftree_op_data *) data;
-
- if (!info) /* cancelled */
- goto done;
-
- /* also cancelled, but camel returned data, might leak */
- if (closure->handle == -1)
- goto done;
-
- if (!prefix)
- prefix = "";
-
- for ( ; info; info = info->sibling) {
- ETreePath child_path;
- ftree_node *node;
-
- if (g_hash_table_lookup(closure->ftree->node_full_name, info->full_name))
- continue;
-
- node = ftree_node_new (store, info);
- child_path = e_tree_memory_node_insert (E_TREE_MEMORY (closure->ftree),
- closure->path,
- 0,
- node);
- g_hash_table_insert(closure->ftree->node_full_name, ftree_node_get_full_name(node), child_path);
-
- if (!(info->flags & CAMEL_FOLDER_NOINFERIORS))
- fe_check_for_children (closure->ftree, child_path);
- }
-
- /* FIXME: this needs to be added back to sort the tree */
- e_tree_memory_sort_node (E_TREE_MEMORY (closure->ftree),
- closure->path,
- fe_sort_folder,
- NULL);
-
- if (closure->data)
- closure->data->flags |= FTREE_NODE_GOT_CHILDREN;
-
- g_hash_table_remove (closure->ftree->scan_ops, closure->path);
-
-done:
- /* finish off the activity of this task */
- /* hack, we know activity_data is an object */
- closure->ftree->activity_level--;
- (closure->ftree->activity_cb) (closure->ftree->activity_level, closure->ftree->activity_data);
- g_object_unref(closure->ftree->activity_data);
-
- g_free (closure);
-}
-
-static void
-fe_check_for_children (FolderETree *ftree, ETreePath path)
-{
- ftree_op_data *closure;
- ftree_node *node;
- char *prefix;
-
- node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path);
-
- /* have we already gotten these children? */
- if (node->flags & FTREE_NODE_GOT_CHILDREN)
- return;
-
- /* or we're loading them right now? */
- if (g_hash_table_lookup (ftree->scan_ops, path))
- return;
-
- /* figure out our search prefix */
- if (path == ftree->root)
- prefix = "";
- else
- prefix = ftree_node_get_full_name (node);
-
- closure = g_new (ftree_op_data, 1);
- closure->ftree = ftree;
- closure->path = path;
- closure->data = node;
- closure->handle = -1;
-
- g_hash_table_insert (ftree->scan_ops, path, closure);
-
- /* hack, we know this is an object ... infact the subscribe dialog */
- g_object_ref(ftree->activity_data);
- ftree->activity_level++;
- (ftree->activity_cb) (ftree->activity_level, ftree->activity_data);
-
- /* FIXME. Tiny race possiblity I guess. */
- closure->handle = subscribe_get_short_folderinfo (ftree, prefix, fe_got_children, closure);
-}
-
-static void
-fe_create_root_node (FolderETree *ftree)
-{
- ftree_node *node;
-
- node = ftree_node_new_root ();
- ftree->root = e_tree_memory_node_insert (E_TREE_MEMORY(ftree), NULL, 0, node);
- fe_check_for_children (ftree, ftree->root);
-}
-
-static ETreePath
-fe_get_first_child (ETreeModel *model, ETreePath path)
-{
- ETreePath child_path;
-
- child_path = E_TREE_MODEL_CLASS (folder_etree_parent_class)->get_first_child (model, path);
- if (child_path)
- fe_check_for_children ((FolderETree *) model, child_path);
- else
- fe_check_for_children ((FolderETree *) model, path);
- return child_path;
-}
-
-/* subscribing */
-static void
-fe_done_subscribing (const char *full_name, const char *name, gboolean subscribe, gboolean success, gpointer user_data)
-{
- ftree_op_data *closure = (ftree_op_data *) user_data;
-
- if (success && closure->handle != -1) {
- char *path;
-
- path = g_strdup_printf ("/%s", full_name);
-
- if (subscribe) {
- closure->data->flags |= FTREE_NODE_SUBSCRIBED;
- recursive_add_folder (closure->ftree->e_storage,
- path, name,
- ftree_node_get_uri (closure->data));
- } else {
- closure->data->flags &= ~FTREE_NODE_SUBSCRIBED;
-
- /* FIXME: recursively remove folder as well? Possible? */
- }
-
- g_free (path);
- e_tree_model_node_data_changed (E_TREE_MODEL (closure->ftree), closure->path);
- }
-
- if (closure->handle != -1)
- g_hash_table_remove (closure->ftree->subscribe_ops, closure->path);
-
- g_free (closure);
-}
-
-/* cleanup */
-
-static gboolean
-fe_cancel_op_foreach (gpointer key, gpointer value, gpointer user_data)
-{
- /*FolderETree *ftree = (FolderETree *) user_data;*/
- ftree_op_data *closure = (ftree_op_data *) value;
-
- if (closure->handle != -1) {
- d(printf("%d: cancel get messageinfo\n", closure->handle));
- mail_msg_cancel (closure->handle);
- }
-
- closure->handle = -1;
-
- return TRUE;
-}
-
-static void
-fe_kill_current_tree (FolderETree *ftree)
-{
- g_hash_table_foreach_remove (ftree->scan_ops, fe_cancel_op_foreach, ftree);
- g_assert (g_hash_table_size (ftree->scan_ops) == 0);
-}
-
-static void
-fe_finalise (GObject *obj)
-{
- FolderETree *ftree = (FolderETree *) (obj);
-
- d(printf("fe finalise!?\n"));
-
- fe_kill_current_tree (ftree);
-
- g_hash_table_foreach_remove (ftree->subscribe_ops, fe_cancel_op_foreach, ftree);
-
- g_hash_table_destroy (ftree->scan_ops);
- g_hash_table_destroy (ftree->subscribe_ops);
- g_hash_table_destroy(ftree->node_full_name);
-
- camel_object_unref (ftree->store);
- bonobo_object_unref (BONOBO_OBJECT (ftree->e_storage));
-
- g_free (ftree->service_name);
-
- ((GObjectClass *)folder_etree_parent_class)->finalize(obj);
-}
-
-typedef gboolean (*bool_func_1) (ETreeModel *, ETreePath, int);
-typedef gboolean (*bool_func_2) (ETreeModel *);
-
-static void
-folder_etree_class_init (GObjectClass *klass)
-{
- ETreeModelClass *etree_model_class = E_TREE_MODEL_CLASS (klass);
-
- folder_etree_parent_class = g_type_class_ref (E_TREE_MEMORY_TYPE);
-
- klass->finalize = fe_finalise;
-
- etree_model_class->value_at = fe_value_at;
- etree_model_class->set_value_at = fe_set_value_at;
- etree_model_class->column_count = fe_column_count;
- etree_model_class->duplicate_value = fe_duplicate_value;
- etree_model_class->free_value = fe_free_value;
- etree_model_class->initialize_value = fe_init_value;
- etree_model_class->value_is_empty = fe_value_is_empty;
- etree_model_class->value_to_string = fe_value_to_string;
- etree_model_class->icon_at = fe_icon_at;
- etree_model_class->is_editable = (bool_func_1) fe_return_false;
- etree_model_class->has_save_id = (bool_func_2) fe_return_false;
- etree_model_class->has_get_node_by_id = (bool_func_2) fe_return_false;
- etree_model_class->get_first_child = fe_get_first_child;
-}
-
-static void
-folder_etree_init (GtkObject *object)
-{
- FolderETree *ftree = (FolderETree *) object;
-
- e_tree_memory_set_node_destroy_func (E_TREE_MEMORY (ftree), (GFunc) g_free, ftree);
-
- ftree->scan_ops = g_hash_table_new (g_direct_hash, g_direct_equal);
- ftree->subscribe_ops = g_hash_table_new (g_direct_hash, g_direct_equal);
-
- ftree->activity_level = 0;
- ftree->node_full_name = g_hash_table_new(g_str_hash, g_str_equal);
-}
-
-static FolderETree *
-folder_etree_construct (FolderETree *ftree,
- CamelStore *store,
- FolderETreeActivityCallback activity_cb,
- gpointer activity_data)
-{
- e_tree_memory_construct (E_TREE_MEMORY (ftree));
-
- ftree->store = store;
- camel_object_ref (store);
-
- ftree->service_name = camel_service_get_name (CAMEL_SERVICE (store), FALSE);
-
- ftree->e_storage = mail_lookup_storage (store); /* this gives us a ref */
-
- ftree->activity_cb = activity_cb;
- ftree->activity_data = activity_data;
-
- fe_create_root_node (ftree);
-
- return ftree;
-}
-
-static
-E_MAKE_TYPE (folder_etree, "FolderETree", FolderETree, folder_etree_class_init, folder_etree_init, E_TREE_MEMORY_TYPE);
-
-/* public */
-
-static FolderETree *
-folder_etree_new (CamelStore *store,
- FolderETreeActivityCallback activity_cb,
- gpointer activity_data)
-{
- FolderETree *ftree;
-
- ftree = g_object_new (folder_etree_get_type(), NULL);
- ftree = folder_etree_construct (ftree, store, activity_cb, activity_data);
- return ftree;
-}
-
-static void
-folder_etree_clear_tree (FolderETree *ftree)
-{
- e_tree_memory_freeze (E_TREE_MEMORY (ftree));
- e_tree_memory_node_remove (E_TREE_MEMORY (ftree), ftree->root);
- fe_create_root_node (ftree);
- g_hash_table_destroy(ftree->node_full_name);
- ftree->node_full_name = g_hash_table_new(g_str_hash, g_str_equal);
- e_tree_memory_thaw (E_TREE_MEMORY (ftree));
-}
-
-static int
-folder_etree_path_set_subscription (FolderETree *ftree, ETreePath path, gboolean subscribe)
-{
- ftree_op_data *closure;
- ftree_node *node;
-
- /* already in progress? */
-
- if (g_hash_table_lookup (ftree->subscribe_ops, path))
- return 0;
-
- /* noselect? */
-
- node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path);
-
- if (!ftree_node_subscribable (node))
- return -1;
-
- /* noop? */
-
- /* uh, this should be a not XOR or something */
- if ((ftree_node_subscribed (node) && subscribe) ||
- (!ftree_node_subscribed (node) && !subscribe))
- return 0;
-
- closure = g_new (ftree_op_data, 1);
- closure->ftree = ftree;
- closure->path = path;
- closure->data = node;
- closure->handle = -1;
-
- g_hash_table_insert (ftree->subscribe_ops, path, closure);
-
- closure->handle = subscribe_do_subscribe_folder (ftree->store,
- ftree_node_get_full_name (node),
- ftree_node_get_name (node),
- subscribe,
- fe_done_subscribing,
- closure);
- return 0;
-}
-
-static int
-folder_etree_path_toggle_subscription (FolderETree *ftree, ETreePath path)
-{
- ftree_node *node = e_tree_memory_node_get_data (E_TREE_MEMORY (ftree), path);
-
- if (ftree_node_subscribed (node))
- return folder_etree_path_set_subscription (ftree, path, FALSE);
- else
- return folder_etree_path_set_subscription (ftree, path, TRUE);
-}
-
-static void
-folder_etree_cancel_all(FolderETree *ftree)
-{
- g_hash_table_foreach_remove (ftree->scan_ops, fe_cancel_op_foreach, ftree);
- g_hash_table_foreach_remove (ftree->subscribe_ops, fe_cancel_op_foreach, ftree);
-}
-
-/* ** StoreData ************************************************************ */
-
-typedef struct _StoreData StoreData;
-
-typedef void (*StoreDataStoreFunc) (StoreData *, CamelStore *, gpointer);
-
-struct _StoreData {
- int refcount;
- char *uri;
-
- FolderETree *ftree;
- CamelStore *store;
-
- int request_id;
-
- GtkWidget *widget;
- StoreDataStoreFunc store_func;
- gpointer store_data;
-};
-
-static StoreData *
-store_data_new (const char *uri)
-{
- StoreData *sd;
-
- sd = g_new0 (StoreData, 1);
- sd->refcount = 1;
- sd->uri = g_strdup (uri);
-
- return sd;
-}
-
-static void
-store_data_free (StoreData *sd)
-{
- d(printf("store data free?\n"));
-
- if (sd->request_id)
- mail_msg_cancel (sd->request_id);
-
- if (sd->ftree) {
- folder_etree_cancel_all(sd->ftree);
- g_object_unref(sd->ftree);
- }
-
- if (sd->store)
- camel_object_unref (sd->store);
-
- g_free (sd->uri);
- g_free (sd);
-}
-
-static void
-store_data_ref (StoreData *sd)
-{
- sd->refcount++;
-}
-
-static void
-store_data_unref (StoreData *sd)
-{
- if (sd->refcount <= 1) {
- store_data_free (sd);
- } else {
- sd->refcount--;
- }
-}
-
-static void
-sd_got_store (char *uri, CamelStore *store, gpointer user_data)
-{
- StoreData *sd = (StoreData *) user_data;
-
- sd->store = store;
-
- if (store) /* we can have exceptions getting the store... server is down, eg */
- camel_object_ref (sd->store);
-
- /* uh, so we might have a problem if this operation is cancelled. Unsure. */
- sd->request_id = 0;
-
- if (sd->store_func)
- (sd->store_func) (sd, sd->store, sd->store_data);
-
- store_data_unref (sd);
-}
-
-static void
-store_data_async_get_store (StoreData *sd, StoreDataStoreFunc func, gpointer user_data)
-{
- if (sd->request_id) {
- d(printf ("Already loading store, nooping\n"));
- return;
- }
-
- if (sd->store) {
- /* um, is this the best behavior? */
- func (sd, sd->store, user_data);
- return;
- }
-
- sd->store_func = func;
- sd->store_data = user_data;
- store_data_ref (sd);
- sd->request_id = mail_get_store (sd->uri, NULL, sd_got_store, sd);
-}
-
-static void
-store_data_cancel_get_store (StoreData *sd)
-{
- g_return_if_fail (sd->request_id);
-
- mail_msg_cancel (sd->request_id);
- sd->request_id = 0;
-}
-
-static void
-sd_toggle_cb (ETree *tree, int row, ETreePath path, int col, GdkEvent *event, gpointer user_data)
-{
- StoreData *sd = (StoreData *) user_data;
-
- folder_etree_path_toggle_subscription (sd->ftree, path);
-}
-
-static GtkWidget *
-store_data_get_widget (StoreData *sd,
- FolderETreeActivityCallback activity_cb,
- gpointer activity_data)
-{
- GtkWidget *tree;
-
- if (!sd->store) {
- d(printf ("store data can't get widget before getting store.\n"));
- return NULL;
- }
-
- if (sd->widget)
- return sd->widget;
-
- sd->ftree = folder_etree_new (sd->store, activity_cb, activity_data);
-
- /* You annoy me, etree! */
- tree = gtk_widget_new (E_TREE_SCROLLED_TYPE,
- "hadjustment", NULL,
- "vadjustment", NULL,
- NULL);
-
- tree = (GtkWidget *) e_tree_scrolled_construct_from_spec_file (E_TREE_SCROLLED (tree),
- E_TREE_MODEL (sd->ftree),
- subscribe_get_global_extras (),
- EVOLUTION_ETSPECDIR "/subscribe-dialog.etspec",
- NULL);
- e_tree_root_node_set_visible (e_tree_scrolled_get_tree(E_TREE_SCROLLED(tree)), TRUE);
- g_signal_connect(e_tree_scrolled_get_tree(E_TREE_SCROLLED (tree)),
- "double_click", G_CALLBACK (sd_toggle_cb), sd);
-
- g_object_unref(global_extras);
-
- sd->widget = tree;
-
- return sd->widget;
-}
-
-typedef struct _selection_closure {
- StoreData *sd;
- enum { SET, CLEAR, TOGGLE } mode;
-} selection_closure;
-
-static void
-sd_subscribe_folder_foreach (int model_row, gpointer closure)
-{
- selection_closure *sc = (selection_closure *) closure;
- StoreData *sd = sc->sd;
- ETree *tree = e_tree_scrolled_get_tree(E_TREE_SCROLLED(sd->widget));
- ETreePath path = e_tree_node_at_row (tree, model_row);
-
- /* ignore results */
- switch (sc->mode) {
- case SET:
- folder_etree_path_set_subscription (sd->ftree, path, TRUE);
- break;
- case CLEAR:
- folder_etree_path_set_subscription (sd->ftree, path, FALSE);
- break;
- case TOGGLE:
- folder_etree_path_toggle_subscription (sd->ftree, path);
- break;
- }
-}
-
-static void
-store_data_selection_set_subscription (StoreData *sd, gboolean subscribe)
-{
- selection_closure sc;
- ETree *tree;
-
- sc.sd = sd;
- if (subscribe)
- sc.mode = SET;
- else
- sc.mode = CLEAR;
-
- tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (sd->widget));
- e_tree_selected_row_foreach (tree, sd_subscribe_folder_foreach, &sc);
-}
-
-#ifdef NEED_TOGGLE_SELECTION
-static void
-store_data_selection_toggle_subscription (StoreData *sd)
-{
- selection_closure sc;
- ETree *tree;
-
- sc.sd = sd;
- sc.mode = TOGGLE;
-
- tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (sd->widget));
- e_tree_selected_row_foreach (tree, sd_subscribe_folder_foreach, &sc);
-}
-#endif
-
-static gboolean
-store_data_mid_request (StoreData *sd)
-{
- return (gboolean) sd->request_id;
-}
-
-/* ** yaay, SubscribeDialog ******************************************************* */
-
-#define PARENT_TYPE (gtk_object_get_type ())
-
-#ifdef JUST_FOR_TRANSLATORS
-static char *str = N_("Folder");
-#endif
-
-#define STORE_DATA_KEY "store-data"
-
-struct _SubscribeDialogPrivate {
- GladeXML *xml;
- GList *store_list;
-
- StoreData *current_store;
- GtkWidget *current_widget;
-
- GtkWidget *default_widget;
- GtkWidget *none_item;
- GtkWidget *search_entry;
- GtkWidget *hbox;
- GtkWidget *filter_radio, *all_radio;
- GtkWidget *sub_button, *unsub_button, *refresh_button, *close_button;
- GtkWidget *progress;
-
- int cancel; /* have we been cancelled? */
- guint activity_timeout_id;
-};
-
-static GtkObjectClass *subscribe_dialog_parent_class;
-
-static void
-sc_refresh_pressed (GtkWidget *widget, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
-
- if (sc->priv->current_store)
- folder_etree_clear_tree (sc->priv->current_store->ftree);
-}
-
-static void
-sc_close_pressed (GtkWidget *widget, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
-
- /* order important here */
- gtk_object_destroy (GTK_OBJECT (sc));
- gtk_widget_destroy (GTK_WIDGET (sc->app));
-}
-
-static void
-sc_subscribe_pressed (GtkWidget *widget, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
- StoreData *store = sc->priv->current_store;
-
- if (!store)
- return;
-
- store_data_selection_set_subscription (store, TRUE);
-}
-
-static void
-sc_unsubscribe_pressed (GtkWidget *widget, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
- StoreData *store = sc->priv->current_store;
-
- if (!store)
- return;
-
- store_data_selection_set_subscription (store, FALSE);
-}
-
-static void
-kill_default_view (SubscribeDialog *sc)
-{
- gtk_widget_hide (sc->priv->none_item);
-
- gtk_widget_set_sensitive (sc->priv->sub_button, TRUE);
- gtk_widget_set_sensitive (sc->priv->unsub_button, TRUE);
- gtk_widget_set_sensitive (sc->priv->refresh_button, TRUE);
-}
-
-static void
-sc_selection_changed (GtkObject *obj, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
- gboolean sensitive;
-
- if (e_selection_model_selected_count (E_SELECTION_MODEL (obj)))
- sensitive = TRUE;
- else
- sensitive = FALSE;
-
- gtk_widget_set_sensitive (sc->priv->sub_button, sensitive);
- gtk_widget_set_sensitive (sc->priv->unsub_button, sensitive);
-}
-
-static gboolean
-sc_activity_timeout (SubscribeDialog *sc)
-{
- gtk_progress_bar_pulse(GTK_PROGRESS_BAR(sc->priv->progress));
-
- return TRUE;
-}
-
-static void
-sc_activity_cb (int level, SubscribeDialog *sc)
-{
- g_assert (pthread_self() == mail_gui_thread);
-
- if (sc->priv->cancel)
- return;
-
- if (level) {
- if (sc->priv->activity_timeout_id)
- return;
-
- sc->priv->activity_timeout_id = g_timeout_add(50, (GSourceFunc)sc_activity_timeout, sc);
- gtk_widget_show(sc->priv->progress);
- } else {
- if (sc->priv->activity_timeout_id) {
- g_source_remove (sc->priv->activity_timeout_id);
- sc->priv->activity_timeout_id = 0;
- }
-
- gtk_widget_hide(sc->priv->progress);
- }
-}
-
-static void
-menu_item_selected (GtkMenuItem *item, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
- StoreData *sd = g_object_get_data (G_OBJECT (item), STORE_DATA_KEY);
-
- g_return_if_fail (sd);
-
- if (sd->widget == NULL) {
- GtkWidget *widget;
- ESelectionModel *esm;
- ETree *tree;
-
- widget = store_data_get_widget (sd, (FolderETreeActivityCallback) sc_activity_cb, sc);
- gtk_box_pack_start (GTK_BOX (sc->priv->hbox), widget, TRUE, TRUE, 0);
-
- tree = e_tree_scrolled_get_tree (E_TREE_SCROLLED (widget));
- esm = e_tree_get_selection_model (tree);
- g_signal_connect(esm, "selection_changed", G_CALLBACK(sc_selection_changed), sc);
- sc_selection_changed ((GtkObject *)esm, sc);
- }
-
- if (sc->priv->current_widget == sc->priv->default_widget)
- kill_default_view (sc);
-
- gtk_widget_hide (sc->priv->current_widget);
- gtk_widget_show (sd->widget);
- sc->priv->current_widget = sd->widget;
- sc->priv->current_store = sd;
-}
-
-static void
-dummy_item_selected (GtkMenuItem *item, gpointer user_data)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data);
-
- gtk_widget_hide (sc->priv->current_widget);
- gtk_widget_show (sc->priv->default_widget);
- sc->priv->current_widget = sc->priv->default_widget;
- sc->priv->current_store = NULL;
-
- gtk_entry_set_text (GTK_ENTRY (sc->priv->search_entry), "");
-}
-
-/* wonderful */
-
-static void
-got_sd_store (StoreData *sd, CamelStore *store, gpointer data)
-{
- if (store && camel_store_supports_subscriptions (store))
- gtk_widget_show (GTK_WIDGET (data));
-}
-
-/* FIXME: if there aren't any stores that are subscribable, the option
- * menu will only have the "No server selected" item and the user will
- * be confused. */
-
-static void
-populate_store_list (SubscribeDialog *sc)
-{
- EAccountList *accounts;
- EAccount *account;
- EIterator *iter;
- GtkWidget *menu;
- GtkWidget *omenu;
- GList *l;
-
- accounts = mail_config_get_accounts ();
- iter = e_list_get_iterator ((EList *) accounts);
- while (e_iterator_is_valid (iter)) {
- StoreData *sd;
-
- account = (EAccount *) e_iterator_get (iter);
-
- if (account->enabled && account->source->url) {
- sd = store_data_new (account->source->url);
- sc->priv->store_list = g_list_prepend (sc->priv->store_list, sd);
- }
-
- e_iterator_next (iter);
- }
-
- g_object_unref (iter);
-
- menu = gtk_menu_new ();
-
- for (l = sc->priv->store_list; l; l = l->next) {
- GtkWidget *item;
- CamelURL *url;
- char *string;
-
- url = camel_url_new (((StoreData *) l->data)->uri, NULL);
- string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
- camel_url_free (url);
- item = gtk_menu_item_new_with_label (string);
- store_data_async_get_store (l->data, got_sd_store, item);
- g_object_set_data (G_OBJECT (item), STORE_DATA_KEY, l->data);
- g_signal_connect (item, "activate", G_CALLBACK (menu_item_selected), sc);
- g_free (string);
-
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
- }
-
- sc->priv->none_item = gtk_menu_item_new_with_label (_("No server has been selected"));
- g_signal_connect (sc->priv->none_item, "activate", G_CALLBACK (dummy_item_selected), sc);
- gtk_widget_show (sc->priv->none_item);
- gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), sc->priv->none_item);
-
- gtk_widget_show (menu);
-
- omenu = glade_xml_get_widget (sc->priv->xml, "store_menu");
- gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
-}
-
-static void
-subscribe_dialog_finalise (GObject *object)
-{
- SubscribeDialog *sc;
- GList *iter;
-
- sc = SUBSCRIBE_DIALOG (object);
-
- if (sc->priv->store_list) {
- for (iter = sc->priv->store_list; iter; iter = iter->next) {
- StoreData *data = iter->data;
- store_data_unref (data);
- }
-
- g_list_free (sc->priv->store_list);
- sc->priv->store_list = NULL;
- }
-
- g_free (sc->priv);
- sc->priv = NULL;
-
- ((GObjectClass *)subscribe_dialog_parent_class)->finalize (object);
-}
-
-static void
-subscribe_dialog_destroy (GtkObject *object)
-{
- SubscribeDialog *sc;
- GList *iter;
-
- sc = SUBSCRIBE_DIALOG (object);
-
- d(printf("subscribe_dialog_destroy\n"));
-
- if (!sc->priv->cancel) {
- sc->priv->cancel = 1;
-
- if (sc->priv->activity_timeout_id) {
- g_source_remove (sc->priv->activity_timeout_id);
- sc->priv->activity_timeout_id = 0;
- }
-
- if (sc->priv->store_list) {
- for (iter = sc->priv->store_list; iter; iter = iter->next) {
- StoreData *data = iter->data;
-
- if (store_data_mid_request (data))
- store_data_cancel_get_store (data);
-
- if (data->ftree)
- folder_etree_cancel_all(data->ftree);
-
- data->store_func = NULL;
- }
- }
-
- if (sc->priv->xml) {
- g_object_unref(sc->priv->xml);
- sc->priv->xml = NULL;
- }
- }
-
- subscribe_dialog_parent_class->destroy (object);
-}
-
-static void
-subscribe_dialog_class_init (GtkObjectClass *object_class)
-{
- object_class->destroy = subscribe_dialog_destroy;
- ((GObjectClass *)object_class)->finalize = subscribe_dialog_finalise;
-
- subscribe_dialog_parent_class = g_type_class_ref (PARENT_TYPE);
-}
-
-static void
-subscribe_dialog_init (GtkObject *object)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (object);
-
- sc->priv = g_new0 (SubscribeDialogPrivate, 1);
-}
-
-static GtkWidget *
-sc_create_default_widget (void)
-{
- GtkWidget *label;
- GtkWidget *viewport;
-
- label = gtk_label_new (_("Please select a server."));
- gtk_widget_show (label);
-
- viewport = gtk_viewport_new (NULL, NULL);
- gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_IN);
- gtk_container_add (GTK_CONTAINER (viewport), label);
-
- return viewport;
-}
-
-static void
-subscribe_dialog_construct (GtkObject *object)
-{
- SubscribeDialog *sc = SUBSCRIBE_DIALOG (object);
-
- /* Load the XML */
- /* "app2" */
- sc->priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/subscribe-dialog.glade", "subscribe_dialog", NULL);
-
- sc->app = glade_xml_get_widget (sc->priv->xml, "subscribe_dialog");
- sc->priv->hbox = glade_xml_get_widget (sc->priv->xml, "tree_box");
- sc->priv->close_button = glade_xml_get_widget (sc->priv->xml, "close_button");
- sc->priv->sub_button = glade_xml_get_widget (sc->priv->xml, "subscribe_button");
- sc->priv->unsub_button = glade_xml_get_widget (sc->priv->xml, "unsubscribe_button");
- sc->priv->refresh_button = glade_xml_get_widget (sc->priv->xml, "refresh_button");
- sc->priv->progress = glade_xml_get_widget(sc->priv->xml, "progress_bar");
-
- /* create default view */
- sc->priv->default_widget = sc_create_default_widget();
- sc->priv->current_widget = sc->priv->default_widget;
- gtk_box_pack_start (GTK_BOX (sc->priv->hbox), sc->priv->default_widget, TRUE, TRUE, 0);
- gtk_widget_show (sc->priv->default_widget);
-
- gtk_widget_set_sensitive (sc->priv->sub_button, FALSE);
- gtk_widget_set_sensitive (sc->priv->unsub_button, FALSE);
- gtk_widget_set_sensitive (sc->priv->refresh_button, FALSE);
-
- /* hook up some signals */
- g_signal_connect(sc->priv->close_button, "clicked", G_CALLBACK(sc_close_pressed), sc);
- g_signal_connect(sc->priv->sub_button, "clicked", G_CALLBACK(sc_subscribe_pressed), sc);
- g_signal_connect(sc->priv->unsub_button, "clicked", G_CALLBACK(sc_unsubscribe_pressed), sc);
- g_signal_connect(sc->priv->refresh_button, "clicked", G_CALLBACK(sc_refresh_pressed), sc);
-
- /* progress */
- gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(sc->priv->progress), 0.1);
- gtk_widget_hide(sc->priv->progress);
-
- /* reasonable starting point */
- gtk_window_set_default_size((GtkWindow *)sc->app, 350, 400);
-
- /* Get the list of stores */
- populate_store_list (sc);
-}
-
-GtkObject *
-subscribe_dialog_new (void)
-{
- SubscribeDialog *subscribe_dialog;
-
- subscribe_dialog = g_object_new (SUBSCRIBE_DIALOG_TYPE, NULL);
- subscribe_dialog_construct (GTK_OBJECT (subscribe_dialog));
-
- return GTK_OBJECT (subscribe_dialog);
-}
-
-E_MAKE_TYPE (subscribe_dialog, "SubscribeDialog", SubscribeDialog, subscribe_dialog_class_init, subscribe_dialog_init, PARENT_TYPE);
diff --git a/mail/subscribe-dialog.glade b/mail/subscribe-dialog.glade
index 276ac3f947..c38505df4d 100644
--- a/mail/subscribe-dialog.glade
+++ b/mail/subscribe-dialog.glade
@@ -82,7 +82,7 @@
<property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
<property name="fraction">0</property>
<property name="pulse_step">0.1</property>
- <property name="text" translatable="yes">Scanning folders ...</property>
+ <property name="text" translatable="yes"></property>
</widget>
<packing>
<property name="padding">0</property>
diff --git a/mail/subscribe-dialog.h b/mail/subscribe-dialog.h
deleted file mode 100644
index c2e6b3d46f..0000000000
--- a/mail/subscribe-dialog.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Authors: Chris Toshok <toshok@ximian.com>
- * Peter Williams <peterw@ximian.com>
- *
- * Copyright 2000 Ximian, Inc. (www.ximian.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-
-#ifndef _SUBSCRIBE_DIALOG_H_
-#define _SUBSCRIBE_DIALOG_H_
-
-#include <gtk/gtktable.h>
-#include <bonobo/bonobo-control.h>
-#include <bonobo/bonobo-property-bag.h>
-#include <gal/e-table/e-tree-model.h>
-#include <gal/e-table/e-table-model.h>
-#include "shell/evolution-storage.h"
-#include "mail-types.h"
-#include "camel/camel-store.h"
-
-#define SUBSCRIBE_DIALOG_TYPE (subscribe_dialog_get_type ())
-#define SUBSCRIBE_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUBSCRIBE_DIALOG_TYPE, SubscribeDialog))
-#define SUBSCRIBE_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUBSCRIBE_DIALOG_TYPE, SubscribeDialogClass))
-#define IS_SUBSCRIBE_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUBSCRIBE_DIALOG_TYPE))
-#define IS_SUBSCRIBE_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUBSCRIBE_DIALOG_TYPE))
-
-typedef struct _SubscribeDialogPrivate SubscribeDialogPrivate;
-
-struct _SubscribeDialog {
- GtkObject parent;
-
- GtkWidget *app;
- SubscribeDialogPrivate *priv;
-};
-
-typedef struct {
- GtkObjectClass parent_class;
-} SubscribeDialogClass;
-
-GtkType subscribe_dialog_get_type (void);
-GtkObject *subscribe_dialog_new (void);
-
-/* helper macro */
-#define subscribe_dialog_show(dialog) gtk_widget_show (SUBSCRIBE_DIALOG (dialog)->app)
-
-#endif /* _SUBSCRIBE_DIALOG_H_ */