aboutsummaryrefslogtreecommitdiffstats
path: root/mail
diff options
context:
space:
mode:
Diffstat (limited to 'mail')
-rw-r--r--mail/ChangeLog362
-rw-r--r--mail/GNOME_Evolution_Mail.server.in.in26
-rw-r--r--mail/Makefile.am269
-rw-r--r--mail/component-factory.c153
-rw-r--r--mail/component-factory.h3
-rw-r--r--mail/em-composer-utils.c24
-rw-r--r--mail/em-folder-browser.c3
-rw-r--r--mail/em-folder-selection-button.c242
-rw-r--r--mail/em-folder-selection-button.h59
-rw-r--r--mail/em-folder-selection.c170
-rw-r--r--mail/em-folder-selection.h41
-rw-r--r--mail/em-folder-selector.c349
-rw-r--r--mail/em-folder-selector.h96
-rw-r--r--mail/em-folder-view.c40
-rw-r--r--mail/em-format-html.c8
-rw-r--r--mail/em-marshal.list1
-rw-r--r--mail/em-popup.c3
-rw-r--r--mail/em-popup.h11
-rw-r--r--mail/em-utils.c25
-rw-r--r--mail/em-utils.h1
-rw-r--r--mail/folder-browser-factory.c5
-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.c2679
-rw-r--r--mail/folder-browser.h192
-rw-r--r--mail/mail-account-gui.c53
-rw-r--r--mail/mail-accounts.c20
-rw-r--r--mail/mail-accounts.etspec12
-rw-r--r--mail/mail-autofilter.c8
-rw-r--r--mail/mail-callbacks.c3228
-rw-r--r--mail/mail-callbacks.h144
-rw-r--r--mail/mail-component-factory.c104
-rw-r--r--mail/mail-component.c1625
-rw-r--r--mail/mail-component.h99
-rw-r--r--mail/mail-config-factory.c2
-rw-r--r--mail/mail-config.c32
-rw-r--r--mail/mail-display-stream.c104
-rw-r--r--mail/mail-display-stream.h62
-rw-r--r--mail/mail-display.c2995
-rw-r--r--mail/mail-display.h137
-rw-r--r--mail/mail-folder-cache.c66
-rw-r--r--mail/mail-folder-cache.h9
-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-importer.c5
-rw-r--r--mail/mail-local.c3
-rw-r--r--mail/mail-mt.c7
-rw-r--r--mail/mail-offline-handler.c14
-rw-r--r--mail/mail-ops.c8
-rw-r--r--mail/mail-search.c403
-rw-r--r--mail/mail-search.h75
-rw-r--r--mail/mail-send-recv.c25
-rw-r--r--mail/mail-session.c11
-rw-r--r--mail/mail-session.h2
-rw-r--r--mail/mail-signature-editor.c4
-rw-r--r--mail/mail-summary.c3
-rw-r--r--mail/mail-tools.c15
-rw-r--r--mail/mail-vfolder.c38
-rw-r--r--mail/mail-vfolder.h2
-rw-r--r--mail/mail.h16
-rw-r--r--mail/main.c161
-rw-r--r--mail/message-browser.c402
-rw-r--r--mail/message-browser.h63
-rw-r--r--mail/subscribe-dialog.c1669
-rw-r--r--mail/subscribe-dialog.etspec9
-rw-r--r--mail/subscribe-dialog.h62
-rw-r--r--mail/upgrade-mailer.c1169
71 files changed, 20395 insertions, 616 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog
index 1ca3dae2d2..a5548f1232 100644
--- a/mail/ChangeLog
+++ b/mail/ChangeLog
@@ -1,3 +1,85 @@
+2003-10-21 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.c (emc_popup_properties): implement.
+ (emc_popup_properties_got_folder): builds dynamic
+ folder-properties dialogue.
+ (emc_popup_properties_response): set the properties on the folder
+ on an ok response.
+ (emc_popup_properties_free): free the properties working data.
+
+ * mail-folder-cache.c (unset_folder_info): unhook from the right
+ function for message_changed.
+
+2003-10-20 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.c (emc_popup_new_folder): pass the right object
+ to set_selected(). Fixes a new real bug. Undid reformatting.
+
+2003-10-17 Jeffrey Stedfast <fejj@ximian.com>
+
+ * mail-component.c: General compile fixes.
+ (emc_popup_new_folder): Fixed to not shadow a parameter. Fixes a
+ real bug.
+
+ * mail-component.h: Added some prototypes.
+
+2003-10-13 Not Zed <NotZed@Ximian.com>
+
+ * em-popup.c (em_popup_create_menu_once): only hookup target free
+ if we have a target set.
+
+ * mail-component.c (load_accounts): removed debug i accidentally
+ left in.
+ (emc_tree_right_click): handle right-click context menu, using an
+ EMPopup table.
+ (emc_popup_*): setup empty popup handlers.
+
+2003-10-13 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-selection.c (em_select_folder): asynchornous folder
+ selection call.
+ (emfs_folder_selected): callback for folder selected.
+
+ * em-folder-view.c (emfv_popup_move): implement.
+ (emfv_popup_copy): "
+ (emfv_popup_move_cb): async folder select callback to run it.
+
+2003-10-10 Not Zed <NotZed@Ximian.com>
+
+ * mail-account-gui.c
+ (mail_account_gui_folder_selector_button_new): use
+ em_folder_selection_button.
+ (mail_account_gui_new): "
+ (folder_selected): "
+
+ * em-folder-selection-button.c: Make this use camel uri's rather
+ than camelfolders.
+ (set_selection): removed, redundant.
+ (impl_dispose): removed, not needed.
+
+ * em-folder-selection-button.h: change the selected signal not to
+ actually return the selection, which must get retrieved later.
+
+ * mail-component.c (em_uri_from_camel): create an evo mail uri
+ from a camel one.
+ (em_uri_to_camel): the reverse.
+
+ * mail-signature-editor.c (mail_signature_editor): up the version
+ of the gtkhtml editor.
+
+2003-10-09 Not Zed <NotZed@Ximian.com>
+
+ * em-folder-selection-button.c (set_selection): always set
+ selected_folder, otherwise we don't unset it properly.
+
+ * em-folder-selection.c (em_folder_selection_run_dialog): fix a
+ small memleak.
+ (em_folder_selection_run_dialog_uri): do the same as run_dialog
+ but take, and return physical uri's.
+
+ * mail-component-factory.c (factory): removed some fixme's, and
+ re-hookedup the composer.
+
2003-10-09 Frederic Crozat <fcrozat@mandrakesoft.com>
* em-icon-stream.c: (emis_sync_close):
@@ -10,6 +92,38 @@
create-rule-from-message bars so that we don't segfault when we
right click with a multi-selection.
+2003-10-08 Chris Toshok <toshok@ximian.com>
+
+ * em-utils.c (em_utils_camel_address_to_destination): EDestination
+ => EABDestination, and e_destination => eab_destination.
+ (reply_get_composer): same.
+ (post_reply_to_message): same.
+
+ * em-composer-utils.c (ask_confirm_for_unwanted_html_mail)
+ EDestination => EABDestination, and e_destination =>
+ eab_destination.
+ (composer_get_message):same.
+
+2003-10-08 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.c (mail_component_peek): setup vfolders once we
+ hve the component, since its setup will call mail_component_peek,
+ fun recursion.
+
+2003-10-08 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.c (setup_local_folder): removed.
+ (setup_local_store): setup various needed globals properly.
+ (setup_account_storages): renamed to load_accounts.
+ (go_online): turn on interactivity as well as onlinedness.
+
+ * GNOME_Evolution_Mail.server.in.in: point the preferences pages
+ to the right factory.
+
+2003-10-07 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.[ch]: Fix copyrights.
+
2003-10-06 Jeffrey Stedfast <fejj@ximian.com>
* mail-config-druid.c (identity_prepare): Fixed.
@@ -28,6 +142,22 @@
the first unread message for now. This is actually annoying the
fuck out of me, Radek, and a few other people.
+2003-10-02 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.c (add_storage): Add the storage to the hash
+ after we've initialised it.
+ (mail_component_evomail_uri_from_folder): hardcode "local" account
+ pseudo-id for local folders.
+ (mail_component_get_folder_from_evomail_uri): handle the "local"
+ account case.
+
+2003-10-02 Not Zed <NotZed@Ximian.com>
+
+ * mail-component.c (setup_local_store): use mbox:/path rather than
+ mbox:///path - the mbox code is 'wrong', but this is easier to
+ fix. fixes local unread counts. maybe the provider url-compare
+ should address this too.
+
2003-10-02 Suresh Chandrasekharan <suresh.chandrasekharan@sun.com>
* mail-config-druid.c: Fix for 40917 "Backspace shouldn't
@@ -101,6 +231,27 @@
* em-format.c (emf_init): Oops, put the arguments in the right order.
+2003-09-29 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-component.c: New member local_store in
+ MailComponentPrivate.
+ (impl_dispose): Unref.
+ (mail_component_load_storage_by_uri): Return the CamelStore.
+ (setup_local_folder): New.
+ (setup_local_store): New.
+ (mail_component_init): Call it.
+ (mail_component_peek_storage_set): New.
+ (mail_component_get_folder_from_evomail_uri): New.
+ (mail_component_evomail_uri_from_folder): New.
+
+ * em-folder-selection-button.c: New.
+ * em-folder-selection-button.h: New.
+
+ * em-folder-selection.c: New.
+ * em-folder-selection.h: New.
+
+ * em-marshal.list: Add NONE:POINTER.
+
2003-09-25 Jeffrey Stedfast <fejj@ximian.com>
* mail-account-gui.c (mail_account_gui_save): Allow any file: uri
@@ -120,6 +271,33 @@
charset string is empty, default the charset to the user's locale
charset. Partial fix for bug #47638.
+2003-09-23 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-component.c (add_storage): Remove unused arg "uri".
+ (mail_component_add_store): Likewise.
+ (add_storage): Don't set the "Connecting..." node.
+ (mail_component_init): Set up local store at
+ ~/.evolution/mail/local.
+
+ * evolution-mbox-upgrade.c (get_local_store): Remove a double
+ xmlFree() that was causing it to crash.
+
+
+2003-09-23 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-component.c (add_storage): Note the new store.
+
+ * mail-component-factory.c: Don't include "mail-callbacks.h"
+ anymore.
+
+ * em-format-html.c (em_format_html_get_type): Get the base
+ directory with mail_component_peek_base_directory().
+ * em-utils.c (filter_editor_response): Likewise.
+ (em_utils_edit_filters): Likewise.
+
+ * em-folder-browser.c (emfb_init): Get the search context through
+ mail_component_peek_search_context().
+
2003-09-23 Jeffrey Stedfast <fejj@ximian.com>
* evolution-mbox-upgrade.c (get_local_store): Don't xmlFree (name)
@@ -273,6 +451,14 @@
* evolution-mbox-upgrade.c: New source file to migrate from the
old mbox structure to the new mbox structure.
+2003-09-08 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-folder-cache.c (mail_note_store): Allow NULL storage in
+ precondition.
+
+ * mail-component.c (mail_component_init): Remove debugging
+ message.
+
2003-08-22 Not Zed <NotZed@Ximian.com>
* mail-format.c (write_date): translate the local time format.
@@ -284,14 +470,13 @@
day names, and the autoconf magic which made Not Zed dislike the
inclusion of the timezone name.
-2003-08-14 Jeffrey Stedfast <fejj@ximian.com>
+2003-08-18 Ettore Perazzoli <ettore@ximian.com>
- * mail-ops.c (mail_send_message): Don't abort at the first failure
- after sending (filtering, appending to Sent, syncing). Instead,
- keep a running tab of exceptions and then set a culmulative
- exception at the end to report to our caller. Also, if we fail to
- append to the account Sent folder, try again with the local Sent
- folder. Fixes bug #46512.
+ * GNOME_Evolution_Mail.server.in.in: Rename
+ GNOME_Evolution_Mail_Component2 to
+ GNOME_Evolution_Mail_Component_2 and GNOME_Evolution_Mail_Factory2
+ to GNOME_Evolution_Mail_Factory_2.
+ * mail-component-factory.c: Update accordingly.
2003-08-18 Jeffrey Stedfast <fejj@ximian.com>
@@ -336,6 +521,15 @@
* mail-display.c (mail_display_render): Change "%P" to "%p" so
that strftime() can work under solaris.
+2003-08-14 Jeffrey Stedfast <fejj@ximian.com>
+
+ * mail-ops.c (mail_send_message): Don't abort at the first failure
+ after sending (filtering, appending to Sent, syncing). Instead,
+ keep a running tab of exceptions and then set a culmulative
+ exception at the end to report to our caller. Also, if we fail to
+ append to the account Sent folder, try again with the local Sent
+ folder. Fixes bug #46512.
+
2003-08-13 Suresh Chandrasekharan <suresh.chandrasekharan@sun.com>
* e-searching-tokenizer.c (searcher_next_token): Fix for 45818 (
@@ -353,6 +547,12 @@
* mail-session.c (remove_timeout): Removed.
(register_timeout): Removed.
+2003-08-09 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-component.c (storage_go_online): Pass NULL for the
+ operation pointer to mail_note_store(), to sync with Michael's
+ changes.
+
2003-08-05 Jeffrey Stedfast <fejj@ximian.com>
* mail-format.c (handle_multipart_encrypted): Updated for
@@ -1308,6 +1508,154 @@
* message-browser.c (message_browser_new): Handle our own Delete
key presses. Fixes bug #45597.
+2003-07-25 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-callbacks.c (do_view_message): No need to pass a shell
+ argument to message_browser_new() anymore.
+
+ * message-browser.c (message_browser_new): Removed arg shell. No
+ need to pass it to folder_browser_new() either.
+
+ * mail-component.c (create_view_callback): No need to pass a shell
+ arg to folder_browser_factory_new_control() anymore.
+
+ * folder-browser-factory.c (folder_browser_factory_new_control):
+ Removed arg shell; folder_browser_browser_new() doesn't need it
+ anymore.
+
+ * folder-browser.c (folder_browser_destroy): No need to unref
+ ->shell anymore.
+ (folder_browser_new): Removed shell arg.
+ (folder_browser_gui_init): Removed a const qualifier that was not
+ supposed to be there.
+
+ * folder-browser.h: Removed member shell from struct
+ FolderBrowser.
+
+2003-07-25 Ettore Perazzoli <ettore@ximian.com>
+
+ * folder-browser.c (folder_browser_gui_init): Get the search
+ context through mail_component_peek_search_context(), since it's
+ no longer a global variable.
+ (folder_browser_gui_init): Cleaned up an extra unneeded if()
+ statement.
+
+ * mail-component.c: New member search_context in struct
+ MailComponentPrivate.
+ (mail_component_peek_search_context): New.
+ (setup_search_context): New function to initialize the
+ search_context, based on the old code in component-factory.c.
+ (mail_component_init): Call it here.
+ (impl_dispose): Unref the rule_context.
+
+ * mail-component-factory.c: Removed global variable
+ search_context.
+
+2003-07-25 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-component.c (browser_page_switched_callback): New callback
+ for the "page_switched" signal on EStorageBrowser; deactivate the
+ previous page, activate the new one.
+ (impl_createControls): Connect.
+
+2003-07-24 Ettore Perazzoli <ettore@ximian.com>
+
+ * mail-mt.c (do_op_status): Pass "evolution-mail" as the ID to
+ evolution_activity_client_new(). [This is just a temporary thing
+ to avoid the fact that we don't have component-factory.h anymore.
+ Eventually we'll just get rid of the activity client stuff.]
+
+ * mail-component-factory.c: Added to the build. Also, finished
+ implementing and moving the factory over from component-factory.c.
+
+ * component-factory.c: Removed from the build.
+ * component-factory.h: Removed from the build.
+
+ * mail-component.c: Removed some debugging messages.
+
+2003-07-23 Ettore Perazzoli <ettore@ximian.com>
+
+ * subscribe-dialog.c: Converted to use EStorages instead of
+ EvolutionStorages and the new MailComponent object.
+
+ * mail.h: Nuked a bunch of stuff. This will go away when I am
+ done refactoring.
+
+ * mail-offline-handler.c: Use the new MailComponent object.
+
+ * mail-folder-cache.c, mail-folder-cache.h: Converted to use
+ EStorages instead of EvolutionStorages.
+
+ * mail-display.c: Use g_timeout and g_source functions instead of
+ gtk_timeout functions.
+
+ * mail-send-recv.c: Use g_timeout and g_source functions instead
+ of gtk_timeout functions.
+ (receive_update_got_store): Updated for the new mail_note_store().
+
+ * mail-session.c: Use g_timeout and g_source functions instead of
+ gtk_timeout functions.
+
+ * mail-config-factory.c (factory): Removed.
+
+ * folder-browser.c (folder_browser_destroy): Use GLib
+ timeout/source functions instead of the deprecated GTK ones.
+ (done_message_selected): Likewise.
+ (folder_browser_gui_init): Protect against fb->search being NULL.
+
+ * mail-account-gui.c (add_new_store): Use new MailComponent object
+ and EStorages instead of EvolutionStorages.
+ (mail_account_gui_save): Likewise.
+
+ * mail-accounts.c (account_delete_clicked): Use new MailComponent
+ object and EStorages instead of EvolutionStorages.
+ (account_able_clicked): Likewise.
+ (account_able_toggled): Likewise.
+
+ * mail-autofilter.c: Use mail_component_peek_base_directory()
+ instead of the evolution_dir global.
+ * mail-callbacks.c: Likewise.
+ * mail-config.c (uri_to_evname): Likewise.
+ (mail_config_get_signature_list): Likewise.
+ (delete_unused_signature_file): Likewise.
+ * mail-display.c (mail_display_class_init): Likewise.
+ * mail-importer.c (mail_importer_make_local_folder): Likewise.
+ * mail-local.c (mlf_getv): Likewise.
+ * mail-ops.c (uid_cachename_hack): Likewise.
+ * mail-summary.c (generate_folder_summaries): Likewise.
+ * mail-tools.c (mail_tool_get_local_inbox): Likewise.
+ (mail_tools_folder_to_url): Likewise.
+ * mail-vfolder.c (mail_vfolder_delete_uri): Likewise.
+ (mail_vfolder_rename_uri): Likewise.
+ (context_rule_removed): Likewise.
+ (store_folder_deleted): Likewise.
+ (store_folder_renamed): Likewise.
+ (vfolder_load_storage): Likewise.
+ (vfolder_editor_response): Likewise.
+ (edit_rule_response): Likewise.
+ (new_rule_clicked): Likewise.
+ (vfolder_gui_add_rule): Likewise.
+ * mail-session.c (main_get_filter_driver): Likewise.
+ (mail_session_forget_password): Likewise.
+ (mail_session_init): Get a base_directory arg.
+
+ * component-factory.c, component-factory.h: Disabled a bunch of
+ stuff to get it to compile in the new configuration. These files
+ will eventually go away when I am done refactoring this.
+
+ * Makefile.am: Do not build importers, compile generate
+ skels/stubs for Evolution.
+
+ * GNOME_Evolution_Mail.server.in.in: Rename control factory to
+ OAFIID:GNOME_Evolution_Mail_Factory2. Add new component
+ GNOME_Evolution_Mail_Component2.
+
+ * mail-component-factory.c: New file implementing the Bonobo
+ factory.
+
+ * mail-component.c, mail-component.h: New files implementing the
+ new mail component, using the new Evolution::Component IDL.
+
2003-07-23 Jeffrey Stedfast <fejj@ximian.com>
* mail-format.c (mail_format_data_wrapper_write_to_stream): Revert
diff --git a/mail/GNOME_Evolution_Mail.server.in.in b/mail/GNOME_Evolution_Mail.server.in.in
index 9d0391d0be..30fcd5b070 100644
--- a/mail/GNOME_Evolution_Mail.server.in.in
+++ b/mail/GNOME_Evolution_Mail.server.in.in
@@ -3,7 +3,7 @@
<!-- Folder display control -->
<!-- (factory) -->
- <oaf_server iid="OAFIID:GNOME_Evolution_Mail_ControlFactory"
+ <oaf_server iid="OAFIID:GNOME_Evolution_Mail_Factory_2"
type="shlib"
location="@COMPONENTDIR@/libevolution-mail.so">
@@ -29,6 +29,22 @@
_value="Evolution Mail folder viewer"/>
</oaf_server>
+ <!-- Component Interface -->
+
+ <oaf_server iid="OAFIID:GNOME_Evolution_Mail_Component_2"
+ type="factory"
+ location="OAFIID:GNOME_Evolution_Mail_Factory_2">
+
+ <oaf_attribute name="repo_ids" type="stringv">
+ <item value="IDL:GNOME/Evolution/Component:1.0"/>
+ </oaf_attribute>
+
+ <oaf_attribute name="name" type="string" _value="Evolution Mail component"/>
+
+ <oaf_attribute name="evolution:component_icon" type="string" value="evolution-inbox.png"/>
+ <oaf_attribute name="evolution:component_display_order" type="number" value="1"/>
+ </oaf_server>
+
<!-- Shell Component -->
<oaf_server iid="OAFIID:GNOME_Evolution_Mail_ShellComponent"
@@ -53,7 +69,7 @@
<!-- (composer) -->
<oaf_server iid="OAFIID:GNOME_Evolution_Mail_Composer"
type="factory"
- location="OAFIID:GNOME_Evolution_Mail_ControlFactory">
+ location="OAFIID:GNOME_Evolution_Mail_Factory_2">
<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:GNOME/Evolution:Composer:1.0"/>
@@ -109,7 +125,7 @@
<!-- Account Editor -->
<oaf_server iid="OAFIID:GNOME_Evolution_Mail_Accounts_ConfigControl"
type="factory"
- location="OAFIID:GNOME_Evolution_Mail_ControlFactory">
+ location="OAFIID:GNOME_Evolution_Mail_Factory_2">
<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:GNOME/Evolution/ConfigControl:1.0"/>
@@ -138,7 +154,7 @@
<!-- Mail Preferences -->
<oaf_server iid="OAFIID:GNOME_Evolution_Mail_Preferences_ConfigControl"
type="factory"
- location="OAFIID:GNOME_Evolution_Mail_ControlFactory">
+ location="OAFIID:GNOME_Evolution_Mail_Factory_2">
<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:GNOME/Evolution/ConfigControl:1.0"/>
@@ -163,7 +179,7 @@
<!-- Composer Preferences -->
<oaf_server iid="OAFIID:GNOME_Evolution_Mail_ComposerPrefs_ConfigControl"
type="factory"
- location="OAFIID:GNOME_Evolution_Mail_ControlFactory">
+ location="OAFIID:GNOME_Evolution_Mail_Factory_2">
<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:GNOME/Evolution/ConfigControl:1.0"/>
diff --git a/mail/Makefile.am b/mail/Makefile.am
index 75802f396e..ac0b6827bd 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = importers
+SUBDIRS = # importers FIXME
libexec_PROGRAMS = \
evolution-mbox-upgrade
@@ -39,128 +39,153 @@ INCLUDES = \
component_LTLIBRARIES = libevolution-mail.la
-EVOLUTION_MAIL_CORBA_GENERATED_H = \
+
+# Code generation for Mailer.idl
+
+MAILER_IDL = Mailer.idl
+
+MAILER_IDL_GENERATED_H = \
Mailer.h
-EVOLUTION_MAIL_CORBA_GENERATED_C = \
+MAILER_IDL_GENERATED_C = \
Mailer-common.c \
Mailer-skels.c \
Mailer-stubs.c
-EVOLUTION_MAIL_CORBA_GENERATED = $(EVOLUTION_MAIL_CORBA_GENERATED_C) $(EVOLUTION_MAIL_CORBA_GENERATED_H)
+MAILER_IDL_GENERATED = $(MAILER_IDL_GENERATED_C) $(MAILER_IDL_GENERATED_H)
+
+$(MAILER_IDL_GENERATED_H): Mailer.idl
+ $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl $(IDL_INCLUDES) $(srcdir)/Mailer.idl
+$(MAILER_IDL_GENERATED_C): $(MAILER_IDL_GENERATED_H)
+
+
+# Code generation for Spell.idl
SPELL_IDL = Spell.idl
-IDL_GENERATED_H = \
+SPELL_IDL_GENERATED_H = \
Spell.h
-IDL_GENERATED_C = \
+SPELL_IDL_GENERATED_C = \
Spell-common.c \
Spell-skels.c \
Spell-stubs.c
-IDL_GENERATED = $(IDL_GENERATED_C) $(IDL_GENERATED_H)
+SPELL_IDL_GENERATED = $(SPELL_IDL_GENERATED_C) $(SPELL_IDL_GENERATED_H)
-$(IDL_GENERATED_H): $(SPELL_IDL)
+$(SPELL_IDL_GENERATED_H): $(SPELL_IDL)
$(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl $(IDL_INCLUDES) $(srcdir)/Spell.idl
-$(IDL_GENERATED_C): $(IDL_GENERATED_H)
+$(SPELL_IDL_GENERATED_C): $(SPELL_IDL_GENERATED_H)
Spell-impl.o: Spell.h
-libevolution_mail_la_SOURCES = \
- $(EVOLUTION_MAIL_CORBA_GENERATED) \
- $(IDL_GENERATED) \
- component-factory.c \
- component-factory.h \
- e-searching-tokenizer.c \
- e-searching-tokenizer.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-info.c \
- folder-info.h \
- mail-account-editor.c \
- mail-account-editor.h \
- mail-account-gui.c \
- mail-account-gui.h \
- mail-accounts.c \
- mail-accounts.h \
- mail-autofilter.c \
- mail-autofilter.h \
- mail-composer-prefs.c \
- mail-composer-prefs.h \
- mail-config.c \
- mail-config.h \
- mail-config-druid.c \
- mail-config-druid.h \
- mail-crypto.c \
- mail-crypto.h \
- mail-config-factory.c \
- mail-config-factory.h \
- mail-preferences.c \
- mail-preferences.h \
- mail-folder-cache.c \
- mail-folder-cache.h \
- mail-importer.c \
- mail-importer.h \
- mail-local.c \
- mail-local.h \
- mail-mt.c \
- mail-mt.h \
- mail-offline-handler.c \
- mail-offline-handler.h \
- mail-ops.c \
- mail-ops.h \
- mail-send-recv.c \
- mail-send-recv.h \
- mail-session.c \
- mail-session.h \
- mail-signature-editor.c \
- mail-signature-editor.h \
- mail-tools.c \
- mail-tools.h \
- mail-types.h \
- mail-vfolder.c \
- mail-vfolder.h \
- message-list.c \
- message-list.h \
- message-tag-editor.c \
- message-tag-editor.h \
- message-tag-followup.c \
- message-tag-followup.h \
+
+# libevolution-mail
+
+libevolution_mail_la_SOURCES = \
+ $(SPELL_IDL_GENERATED) \
+ $(MAILER_IDL_GENERATED) \
+ e-searching-tokenizer.c \
+ e-searching-tokenizer.h \
+ em-inline-filter.c \
+ em-inline-filter.h \
+ em-folder-selection.c \
+ em-folder-selection.h \
+ em-folder-selection-button.c \
+ em-folder-selection-button.h \
+ em-folder-selector.c \
+ em-folder-selector.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-info.c \
+ folder-info.h \
+ mail-account-editor.c \
+ mail-account-editor.h \
+ mail-account-gui.c \
+ mail-account-gui.h \
+ mail-accounts.c \
+ mail-accounts.h \
+ mail-autofilter.c \
+ mail-autofilter.h \
+ mail-component-factory.c \
+ mail-component.c \
+ mail-component.h \
+ mail-composer-prefs.c \
+ mail-composer-prefs.h \
+ mail-config.c \
+ mail-config.h \
+ mail-config-druid.c \
+ mail-config-druid.h \
+ mail-crypto.c \
+ mail-crypto.h \
+ mail-config-factory.c \
+ mail-config-factory.h \
+ mail-preferences.c \
+ mail-preferences.h \
+ mail-folder-cache.c \
+ mail-folder-cache.h \
+ mail-importer.c \
+ mail-importer.h \
+ mail-mt.c \
+ mail-mt.h \
+ mail-offline-handler.c \
+ mail-offline-handler.h \
+ mail-ops.c \
+ mail-ops.h \
+ mail-send-recv.c \
+ mail-send-recv.h \
+ mail-session.c \
+ mail-session.h \
+ mail-signature-editor.c \
+ mail-signature-editor.h \
+ mail-tools.c \
+ mail-tools.h \
+ mail-types.h \
+ mail-vfolder.c \
+ mail-vfolder.h \
+ message-list.c \
+ message-list.h \
+ message-tag-editor.c \
+ message-tag-editor.h \
+ message-tag-followup.c \
+ message-tag-followup.h \
mail.h
+# EPFIXME: Functionality to be brought back to life.
+#
+# mail-local.c
+# mail-local.h
+
libevolution_mail_la_LIBADD = \
$(top_builddir)/shell/importer/libevolution-importer.la \
$(top_builddir)/camel/libcamel.la \
@@ -177,6 +202,9 @@ libevolution_mail_la_LIBADD = \
libevolution_mail_la_LDFLAGS = \
-avoid-version -module
+
+# .server files
+
evolution_mbox_upgrade_SOURCES = evolution-mbox-upgrade.c
evolution_mbox_upgrade_LDADD = \
$(top_builddir)/camel/libcamel.la \
@@ -190,21 +218,16 @@ server_DATA = $(server_in_files:.server.in.in=.server)
sed -e "s|\@COMPONENTDIR\@|$(componentdir)|" $< > $@
@INTLTOOL_SERVER_RULE@
-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 = message-list.etspec
+# Misc data to install
-schemadir = $(GCONF_SCHEMA_FILE_DIR)
-schema_DATA = evolution-mail.schemas
+glade_DATA = mail-config.glade local-config.glade subscribe-dialog.glade message-tags.glade mail-search.glade
+MARSHAL_GENERATED = em-marshal.c em-marshal.h
+@EVO_MARSHAL_RULE@
-idl_DATA = Mailer.idl
+etspec_DATA = mail-accounts.etspec message-list.etspec subscribe-dialog.etspec
-$(EVOLUTION_MAIL_CORBA_GENERATED_H): Mailer.idl
- $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl $(IDL_INCLUDES) $(srcdir)/Mailer.idl
-$(EVOLUTION_MAIL_CORBA_GENERATED_C): $(EVOLUTION_MAIL_CORBA_GENERATED_H)
+idl_DATA = $(MAILER_IDL)
EXTRA_DIST = \
ChangeLog.pre-1-4 \
@@ -217,6 +240,9 @@ EXTRA_DIST = \
$(server_DATA) \
$(etspec_DATA)
+
+# Purify support
+
if ENABLE_PURIFY
PLINK = $(LIBTOOL) --mode=link $(PURIFY) $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
@@ -228,6 +254,12 @@ evolution-mail.pure: evolution-mail
endif
+
+# GConf
+
+schemadir = $(GCONF_SCHEMA_FILE_DIR)
+schema_DATA = evolution-mail.schemas
+
install-data-local:
if test -z "$(DESTDIR)" ; then \
for p in $(schema_DATA) ; do \
@@ -235,8 +267,11 @@ install-data-local:
done \
fi
+
+# Prologue
+
dist-hook:
cd $(distdir); rm -f $(BUILT_SOURCES)
-BUILT_SOURCES = $(EVOLUTION_MAIL_CORBA_GENERATED) $(IDL_GENERATED) $(MARSHAL_GENERATED) $(server_DATA)
+BUILT_SOURCES = $(MAILER_IDL_GENERATED) $(SPELL_IDL_GENERATED) $(MARSHAL_GENERATED) $(server_DATA)
CLEANFILES = $(BUILT_SOURCES)
diff --git a/mail/component-factory.c b/mail/component-factory.c
index a1d0c322eb..9f361ec5fc 100644
--- a/mail/component-factory.c
+++ b/mail/component-factory.c
@@ -20,6 +20,8 @@
* Boston, MA 02111-1307, USA.
*/
+/* EPFIXME: This file should go away. */
+
#ifdef HAVE_CONFIG_H
#include <config.h>
@@ -54,6 +56,7 @@
#include "mail-config.h"
#include "mail-config-factory.h"
#include "mail-preferences.h"
+#include "mail-component.h"
#include "mail-composer-prefs.h"
#include "mail-tools.h"
#include "mail-ops.h"
@@ -79,7 +82,6 @@ char *default_sent_folder_uri;
CamelFolder *sent_folder = NULL;
char *default_outbox_folder_uri;
CamelFolder *outbox_folder = NULL;
-char *evolution_dir;
EvolutionShellClient *global_shell_client = NULL;
@@ -136,6 +138,14 @@ type_is_vtrash (const char *type)
return !strcmp (type, "vtrash");
}
+
+/* Forward decls just to get it to compile without warnings. EPFIXME: This is
+ all junk at this point in the refactoring anyways, so it will all die. */
+static void mail_load_storage_by_uri (GNOME_Evolution_Shell shell, const char *uri, const char *name);
+static void mail_load_storages (GNOME_Evolution_Shell shell, EAccountList *accounts);
+static void mail_hash_storage (CamelService *store, EvolutionStorage *storage);
+
+
/* EvolutionShellComponent methods and signals. */
static BonoboControl *
@@ -766,6 +776,7 @@ owner_set_cb (EvolutionShellComponent *shell_component,
const char *evolution_homedir,
gpointer user_data)
{
+#if 0
GNOME_Evolution_Shell corba_shell;
EAccountList *accounts;
int i;
@@ -774,7 +785,6 @@ owner_set_cb (EvolutionShellComponent *shell_component,
global_shell_client = shell_client;
g_object_weak_ref ((GObject *) shell_client, (GWeakNotify) shell_client_destroy, NULL);
- evolution_dir = g_strdup (evolution_homedir);
mail_session_init ();
async_event = mail_async_event_new();
@@ -831,6 +841,7 @@ owner_set_cb (EvolutionShellComponent *shell_component,
/* Everything should be ready now */
evolution_folder_info_notify_ready ();
+#endif
}
static void
@@ -1037,7 +1048,7 @@ request_quit (EvolutionShellComponent *shell_component,
}
static BonoboObject *
-create_component (void)
+create_shell_component (void)
{
EvolutionShellComponentDndDestinationFolder *destination_interface;
MailOfflineHandler *offline_handler;
@@ -1336,12 +1347,14 @@ storage_xfer_folder (EvolutionStorage *storage,
camel_exception_clear (&ex);
}
+#if 0 /* EPFIXME */
static void
storage_connected (CamelStore *store, CamelFolderInfo *info, void *listener)
{
notify_listener (listener, (info ? GNOME_Evolution_Storage_OK :
GNOME_Evolution_Storage_GENERIC_ERROR));
}
+#endif
static void
storage_connect (EvolutionStorage *storage,
@@ -1349,8 +1362,10 @@ storage_connect (EvolutionStorage *storage,
const char *path,
CamelStore *store)
{
+#if 0 /* EPFIXME */
mail_note_store (CAMEL_STORE (store), NULL, storage, CORBA_OBJECT_NIL,
storage_connected, listener);
+#endif
}
static void
@@ -1373,7 +1388,7 @@ add_storage (const char *name, const char *uri, CamelService *store,
evolution_storage_has_subfolders (storage, "/", _("Connecting..."));
mail_hash_storage (store, storage);
/*if (auto_connect)*/
- mail_note_store ((CamelStore *) store, NULL, storage, CORBA_OBJECT_NIL, NULL, NULL);
+ /* EPFIXME mail_note_store ((CamelStore *) store, NULL, storage, CORBA_OBJECT_NIL, NULL, NULL); */
/* falllll */
case EVOLUTION_STORAGE_ERROR_ALREADYREGISTERED:
case EVOLUTION_STORAGE_ERROR_EXISTS:
@@ -1386,34 +1401,7 @@ add_storage (const char *name, const char *uri, CamelService *store,
}
}
-void
-mail_add_storage (CamelStore *store, const char *name, const char *uri)
-{
- EvolutionShellClient *shell_client;
- GNOME_Evolution_Shell shell;
- CamelException ex;
-
- g_return_if_fail (CAMEL_IS_STORE (store));
-
- shell_client = evolution_shell_component_get_owner (shell_component);
- shell = evolution_shell_client_corba_objref (shell_client);
-
- camel_exception_init (&ex);
-
- if (name == NULL) {
- char *service_name;
-
- service_name = camel_service_get_name ((CamelService *) store, TRUE);
- add_storage (service_name, uri, (CamelService *) store, shell, &ex);
- g_free (service_name);
- } else {
- add_storage (name, uri, (CamelService *) store, shell, &ex);
- }
-
- camel_exception_clear (&ex);
-}
-
-void
+static void
mail_load_storage_by_uri (GNOME_Evolution_Shell shell, const char *uri, const char *name)
{
CamelException ex;
@@ -1468,7 +1456,7 @@ mail_load_storage_by_uri (GNOME_Evolution_Shell shell, const char *uri, const ch
camel_object_unref (CAMEL_OBJECT (store));
}
-void
+static void
mail_load_storages (GNOME_Evolution_Shell shell, EAccountList *accounts)
{
CamelException ex;
@@ -1500,103 +1488,23 @@ mail_load_storages (GNOME_Evolution_Shell shell, EAccountList *accounts)
g_object_unref (iter);
}
-void
+static void
mail_hash_storage (CamelService *store, EvolutionStorage *storage)
{
camel_object_ref (CAMEL_OBJECT (store));
g_hash_table_insert (storages_hash, store, storage);
}
-EvolutionStorage *
-mail_lookup_storage (CamelStore *store)
-{
- EvolutionStorage *storage;
-
- /* Because the storages_hash holds a reference to each store
- * used as a key in it, none of them will ever be gc'ed, meaning
- * any call to camel_session_get_{service,store} with the same
- * URL will always return the same object. So this works.
- */
-
- storage = g_hash_table_lookup (storages_hash, store);
- if (storage)
- bonobo_object_ref (BONOBO_OBJECT (storage));
-
- return storage;
-}
-
+#if 0
static void
store_disconnect(CamelStore *store, void *event_data, void *data)
{
camel_service_disconnect (CAMEL_SERVICE (store), TRUE, NULL);
camel_object_unref (CAMEL_OBJECT (store));
}
+#endif
-void
-mail_remove_storage (CamelStore *store)
-{
- EvolutionStorage *storage;
- EvolutionShellClient *shell_client;
- GNOME_Evolution_Shell corba_shell;
-
- /* Because the storages_hash holds a reference to each store
- * used as a key in it, none of them will ever be gc'ed, meaning
- * any call to camel_session_get_{service,store} with the same
- * URL will always return the same object. So this works.
- */
-
- storage = g_hash_table_lookup (storages_hash, store);
- if (!storage)
- return;
-
- g_hash_table_remove (storages_hash, store);
-
- /* so i guess potentially we could have a race, add a store while one
- being removed. ?? */
- mail_note_store_remove(store);
-
- shell_client = evolution_shell_component_get_owner (shell_component);
- corba_shell = evolution_shell_client_corba_objref(shell_client);
-
- evolution_storage_deregister_on_shell (storage, corba_shell);
-
- mail_async_event_emit(async_event, MAIL_ASYNC_THREAD, (MailAsyncFunc)store_disconnect, store, NULL, NULL);
-}
-
-void
-mail_remove_storage_by_uri (const char *uri)
-{
- CamelProvider *prov;
- CamelService *store;
-
- prov = camel_session_get_provider (session, uri, NULL);
- if (!prov)
- return;
- if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) ||
- (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL))
- return;
-
- store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, NULL);
- if (store != NULL) {
- mail_remove_storage (CAMEL_STORE (store));
- camel_object_unref (CAMEL_OBJECT (store));
- }
-}
-
-int
-mail_storages_count (void)
-{
- return g_hash_table_size (storages_hash);
-}
-
-void
-mail_storages_foreach (GHFunc func, gpointer data)
-{
- g_hash_table_foreach (storages_hash, func, data);
-}
-
-
-#define FACTORY_ID "OAFIID:GNOME_Evolution_Mail_ControlFactory"
+#define FACTORY_ID "OAFIID:GNOME_Evolution_Mail_Factory_2"
#define MAIL_CONFIG_IID "OAFIID:GNOME_Evolution_MailConfig"
#define WIZARD_IID "OAFIID:GNOME_Evolution_Mail_Wizard"
@@ -1608,9 +1516,14 @@ factory (BonoboGenericFactory *factory,
const char *component_id,
void *closure)
{
- if (strcmp (component_id, COMPONENT_ID) == 0)
- return create_component();
- else if (strcmp(component_id, MAIL_CONFIG_IID) == 0)
+ if (strcmp (component_id, SHELL_COMPONENT_ID) == 0)
+ return create_shell_component();
+ else if (strcmp (component_id, COMPONENT_ID) == 0) {
+ MailComponent *component = mail_component_peek ();
+
+ bonobo_object_ref (BONOBO_OBJECT (component));
+ return BONOBO_OBJECT (component);
+ } else if (strcmp(component_id, MAIL_CONFIG_IID) == 0)
return (BonoboObject *)g_object_new (evolution_mail_config_get_type (), NULL);
else if (strcmp(component_id, FOLDER_INFO_IID) == 0)
return evolution_folder_info_new();
diff --git a/mail/component-factory.h b/mail/component-factory.h
index 3abd058556..fdc7c4056f 100644
--- a/mail/component-factory.h
+++ b/mail/component-factory.h
@@ -23,7 +23,8 @@
#ifndef COMPONENT_FACTORY_H
#define COMPONENT_FACTORY_H
-#define COMPONENT_ID "OAFIID:GNOME_Evolution_Mail_ShellComponent"
+#define COMPONENT_ID "OAFIID:GNOME_Evolution_Mail_Component2"
+#define SHELL_COMPONENT_ID "OAFIID:GNOME_Evolution_Mail_ShellComponent"
#define SUMMARY_FACTORY_ID "OAFIID:GNOME_Evolution_Mail_ExecutiveSummaryComponentFactory"
#endif
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index e181c2cb33..75166a19b4 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -101,7 +101,7 @@ composer_destroy_cb (gpointer user_data, GObject *deadbeef)
}
static gboolean
-ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EDestination **recipients)
+ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EABDestination **recipients)
{
gboolean show_again, res;
GConfClient *gconf;
@@ -117,10 +117,10 @@ ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EDestination **recip
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])) {
+ if (!eab_destination_get_html_mail_pref (recipients[i])) {
const char *name;
- name = e_destination_get_textrep (recipients[i], FALSE);
+ name = eab_destination_get_textrep (recipients[i], FALSE);
g_string_append_printf (str, " %s\n", name);
}
@@ -262,7 +262,7 @@ static CamelMimeMessage *
composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_object_data)
{
CamelMimeMessage *message = NULL;
- EDestination **recipients, **recipients_bcc;
+ EABDestination **recipients, **recipients_bcc;
gboolean send_html, confirm_html;
CamelInternetAddress *cia;
int hidden = 0, shown = 0;
@@ -286,15 +286,15 @@ composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_
/* 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]);
+ const char *addr = eab_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])) {
+ if (eab_destination_is_evolution_list (recipients[i])
+ && !eab_destination_list_show_addresses (recipients[i])) {
hidden++;
} else {
shown++;
@@ -307,7 +307,7 @@ composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_
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]);
+ const char *addr = eab_destination_get_address (recipients_bcc[i]);
if (addr && addr[0]) {
camel_address_decode ((CamelAddress *) cia, addr);
@@ -318,7 +318,7 @@ composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_
}
}
- e_destination_freev (recipients_bcc);
+ eab_destination_freev (recipients_bcc);
}
camel_object_unref (cia);
@@ -347,7 +347,7 @@ composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_
if (recipients) {
for (i = 0; recipients[i] != NULL && !html_problem; i++) {
- if (!e_destination_get_html_mail_pref (recipients[i]))
+ if (!eab_destination_get_html_mail_pref (recipients[i]))
html_problem = TRUE;
}
}
@@ -384,12 +384,12 @@ composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_
/* Get the message recipients and 'touch' them, boosting their use scores */
if (recipients)
- e_destination_touchv (recipients);
+ eab_destination_touchv (recipients);
finished:
if (recipients)
- e_destination_freev (recipients);
+ eab_destination_freev (recipients);
return message;
}
diff --git a/mail/em-folder-browser.c b/mail/em-folder-browser.c
index 243ea449eb..03ab55800f 100644
--- a/mail/em-folder-browser.c
+++ b/mail/em-folder-browser.c
@@ -156,9 +156,8 @@ static void
emfb_init(GObject *o)
{
EMFolderBrowser *emfb = (EMFolderBrowser *)o;
+ RuleContext *search_context = mail_component_peek_search_context (mail_component_peek ());
struct _EMFolderBrowserPrivate *p;
- /* FIXME ... */
- extern RuleContext *search_context;
p = emfb->priv = g_malloc0(sizeof(struct _EMFolderBrowserPrivate));
diff --git a/mail/em-folder-selection-button.c b/mail/em-folder-selection-button.c
new file mode 100644
index 0000000000..ac5a6e1d06
--- /dev/null
+++ b/mail/em-folder-selection-button.c
@@ -0,0 +1,242 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* em-folder-selection-button.c
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * 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.
+ *
+ * Author: Ettore Perazzoli <ettore@ximian.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "em-folder-selection-button.h"
+
+#include "mail-component.h"
+#include "em-folder-selector.h"
+
+#include <gal/util/e-util.h>
+
+#include <gtk/gtkimage.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkhbox.h>
+
+
+#define PARENT_TYPE gtk_button_get_type ()
+static GtkButtonClass *parent_class = NULL;
+
+
+struct _EMFolderSelectionButtonPrivate {
+ GtkWidget *icon;
+ GtkWidget *label;
+
+ char *uri;
+
+ char *title;
+ char *caption;
+};
+
+enum {
+ SELECTED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+/* Utility functions. */
+
+static void
+set_contents_unselected (EMFolderSelectionButton *button)
+{
+ gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->icon), NULL);
+ gtk_label_set_text (GTK_LABEL (button->priv->label), _("<click here to select a folder>"));
+}
+
+static void
+set_contents (EMFolderSelectionButton *button)
+{
+ EMFolderSelectionButtonPrivate *priv = button->priv;
+ char *path, *tmp, *label;
+
+ if (priv->uri == NULL)
+ goto unset;
+
+ /* We set the button name directly from the storage set path, which is /accountname/path/foldername */
+ path = e_storage_set_get_path_for_physical_uri(mail_component_peek_storage_set(mail_component_peek()), priv->uri);
+
+ if (path == NULL)
+ goto unknown;
+
+ tmp = strchr(path+1, '/');
+ if (tmp == NULL)
+ goto unknown;
+ *tmp++ = 0;
+
+ label = g_strdup_printf(_("\"%s\" in \"%s\""), tmp, path+1);
+ gtk_label_set_text (GTK_LABEL (priv->label), label);
+ g_free (label);
+
+ g_free(path);
+ return;
+
+unknown:
+ g_free(path);
+unset:
+ set_contents_unselected(button);
+}
+
+static void
+impl_finalize (GObject *object)
+{
+ EMFolderSelectionButtonPrivate *priv = EM_FOLDER_SELECTION_BUTTON (object)->priv;
+
+ g_free (priv->title);
+ g_free (priv->caption);
+ g_free(priv->uri);
+ g_free (priv);
+
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+static void
+emfsb_selector_response(EMFolderSelector *emfs, int response, EMFolderSelectionButton *button)
+{
+ if (response == GTK_RESPONSE_OK) {
+ const char *uri = em_folder_selector_get_selected_uri(emfs);
+
+ em_folder_selection_button_set_selection(button, uri);
+ g_signal_emit(button, signals[SELECTED], 0);
+ }
+
+ gtk_widget_destroy((GtkWidget *)emfs);
+}
+
+static void
+impl_clicked (GtkButton *button)
+{
+ EMFolderSelectionButtonPrivate *priv = EM_FOLDER_SELECTION_BUTTON (button)->priv;
+ EStorageSet *ess;
+ GtkWidget *w;
+ GtkWidget *toplevel;
+
+ if (GTK_BUTTON_CLASS (parent_class)->clicked != NULL)
+ (* GTK_BUTTON_CLASS (parent_class)->clicked) (button);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ ess = mail_component_peek_storage_set(mail_component_peek());
+ w = em_folder_selector_new(ess, EM_FOLDER_SELECTOR_CAN_CREATE, priv->title, priv->caption);
+ em_folder_selector_set_selected_uri((EMFolderSelector *)w, priv->uri);
+ g_signal_connect(w, "response", G_CALLBACK(emfsb_selector_response), button);
+ gtk_widget_show(w);
+}
+#if 0
+{
+ uri = em_folder_selection_run_dialog_uri((GtkWindow *)toplevel,
+ priv->title,
+ priv->caption,
+ priv->uri);
+
+ em_folder_selection_button_set_selection (EM_FOLDER_SELECTION_BUTTON (button), uri);
+ g_free(uri);
+
+ g_signal_emit (button, signals[SELECTED], 0);
+}
+#endif
+
+static void
+class_init (EMFolderSelectionButtonClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkButtonClass *button_class = GTK_BUTTON_CLASS (class);
+
+ object_class->finalize = impl_finalize;
+
+ button_class->clicked = impl_clicked;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ signals[SELECTED] = g_signal_new ("selected",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EMFolderSelectionButtonClass, selected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+init (EMFolderSelectionButton *folder_selection_button)
+{
+ EMFolderSelectionButtonPrivate *priv;
+ GtkWidget *box;
+
+ priv = g_new0 (EMFolderSelectionButtonPrivate, 1);
+ folder_selection_button->priv = priv;
+
+ box = gtk_hbox_new (FALSE, 4);
+
+ priv->icon = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (box), priv->icon, FALSE, TRUE, 0);
+
+ priv->label = gtk_label_new ("");
+ gtk_label_set_justify (GTK_LABEL (priv->label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (box);
+ gtk_container_add (GTK_CONTAINER (folder_selection_button), box);
+
+ set_contents (folder_selection_button);
+}
+
+GtkWidget *
+em_folder_selection_button_new(const char *title, const char *caption)
+{
+ EMFolderSelectionButton *button = g_object_new (EM_TYPE_FOLDER_SELECTION_BUTTON, NULL);
+
+ button->priv->title = g_strdup (title);
+ button->priv->caption = g_strdup (caption);
+
+ return GTK_WIDGET (button);
+}
+
+
+void
+em_folder_selection_button_set_selection(EMFolderSelectionButton *button, const char *uri)
+{
+ EMFolderSelectionButtonPrivate *p = button->priv;
+
+ g_return_if_fail(EM_IS_FOLDER_SELECTION_BUTTON(button));
+
+ if (p->uri != uri) {
+ g_free(p->uri);
+ p->uri = g_strdup(uri);
+ }
+
+ set_contents(button);
+}
+
+
+const char *
+em_folder_selection_button_get_selection(EMFolderSelectionButton *button)
+{
+ g_return_val_if_fail (EM_IS_FOLDER_SELECTION_BUTTON (button), NULL);
+
+ return button->priv->uri;
+}
+
+E_MAKE_TYPE (em_folder_selection_button, "EMFolderSelectionButton", EMFolderSelectionButton, class_init, init, PARENT_TYPE)
diff --git a/mail/em-folder-selection-button.h b/mail/em-folder-selection-button.h
new file mode 100644
index 0000000000..1ab4eb461c
--- /dev/null
+++ b/mail/em-folder-selection-button.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* em-folder-selection-button.h
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * 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.
+ *
+ * Author: Ettore Perazzoli <ettore@ximian.com>
+ */
+
+#ifndef _EM_FOLDER_SELECTION_BUTTON_H_
+#define _EM_FOLDER_SELECTION_BUTTON_H_
+
+#include <gtk/gtkbutton.h>
+
+#define EM_TYPE_FOLDER_SELECTION_BUTTON (em_folder_selection_button_get_type ())
+#define EM_FOLDER_SELECTION_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EM_TYPE_FOLDER_SELECTION_BUTTON, EMFolderSelectionButton))
+#define EM_FOLDER_SELECTION_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EM_TYPE_FOLDER_SELECTION_BUTTON, EMFolderSelectionButtonClass))
+#define EM_IS_FOLDER_SELECTION_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EM_TYPE_FOLDER_SELECTION_BUTTON))
+#define EM_IS_FOLDER_SELECTION_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EM_TYPE_FOLDER_SELECTION_BUTTON))
+
+typedef struct _EMFolderSelectionButton EMFolderSelectionButton;
+typedef struct _EMFolderSelectionButtonPrivate EMFolderSelectionButtonPrivate;
+typedef struct _EMFolderSelectionButtonClass EMFolderSelectionButtonClass;
+
+struct _EMFolderSelectionButton {
+ GtkButton parent;
+
+ EMFolderSelectionButtonPrivate *priv;
+};
+
+struct _EMFolderSelectionButtonClass {
+ GtkButtonClass parent_class;
+
+ /* Signals. */
+
+ void (* selected) (EMFolderSelectionButton *button);
+};
+
+GType em_folder_selection_button_get_type (void);
+
+GtkWidget *em_folder_selection_button_new(const char *title, const char *caption);
+
+void em_folder_selection_button_set_selection(EMFolderSelectionButton *button, const char *uri);
+const char *em_folder_selection_button_get_selection(EMFolderSelectionButton *button);
+
+#endif /* _EM_FOLDER_SELECTION_BUTTON_H_ */
diff --git a/mail/em-folder-selection.c b/mail/em-folder-selection.c
new file mode 100644
index 0000000000..c65ad80fc7
--- /dev/null
+++ b/mail/em-folder-selection.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* em-folder-selection.c - UI for selecting folders.
+ *
+ * Copyright (C) 2002 Ximian, Inc.
+ *
+ * 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.
+ *
+ * Author: Ettore Perazzoli <ettore@ximian.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "em-folder-selection.h"
+
+#include "mail-component.h"
+#include "mail-tools.h"
+
+#include "shell/e-folder-selection-dialog.h"
+
+
+CamelFolder *
+em_folder_selection_run_dialog (GtkWindow *parent_window,
+ const char *title,
+ const char *caption,
+ CamelFolder *default_folder)
+{
+ EStorageSet *storage_set = mail_component_peek_storage_set (mail_component_peek ());
+ char *default_path = NULL;
+ CamelStore *default_store;
+ GtkWidget *dialog;
+ EFolder *selected_e_folder;
+ CamelFolder *selected_camel_folder;
+ int response;
+
+ default_store = camel_folder_get_parent_store (default_folder);
+ if (default_store != NULL) {
+ EStorage *storage = mail_component_lookup_storage (mail_component_peek (), default_store);
+
+ if (storage != NULL) {
+ default_path = g_strconcat ("/",
+ e_storage_get_name (storage),
+ "/",
+ camel_folder_get_full_name (default_folder),
+ NULL);
+ }
+ }
+
+ /* EPFIXME: Allowed types? */
+ dialog = e_folder_selection_dialog_new (storage_set, title, caption, default_path, NULL, FALSE);
+ g_free(default_path);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ return NULL;
+ }
+
+ selected_e_folder = e_storage_set_get_folder (storage_set,
+ e_folder_selection_dialog_get_selected_path (E_FOLDER_SELECTION_DIALOG (dialog)));
+ if (selected_e_folder == NULL) {
+ gtk_widget_destroy (dialog);
+ return NULL;
+ }
+
+ selected_camel_folder = mail_tool_uri_to_folder (e_folder_get_physical_uri (selected_e_folder), 0, NULL);
+ gtk_widget_destroy (dialog);
+
+ return selected_camel_folder;
+}
+
+/* FIXME: This isn't the way to do it, but then neither is the above, really ... */
+char *
+em_folder_selection_run_dialog_uri(GtkWindow *parent_window,
+ const char *title,
+ const char *caption,
+ const char *default_folder_uri)
+{
+ EStorageSet *storage_set = mail_component_peek_storage_set (mail_component_peek ());
+ char *default_path;
+ GtkWidget *dialog;
+ EFolder *selected_e_folder;
+ int response;
+
+ default_path = e_storage_set_get_path_for_physical_uri(storage_set, default_folder_uri);
+ dialog = e_folder_selection_dialog_new (storage_set, title, caption, default_path, NULL, FALSE);
+ g_free(default_path);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (response != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (dialog);
+ return NULL;
+ }
+
+ selected_e_folder = e_storage_set_get_folder (storage_set,
+ e_folder_selection_dialog_get_selected_path (E_FOLDER_SELECTION_DIALOG (dialog)));
+ gtk_widget_destroy (dialog);
+ if (selected_e_folder == NULL)
+ return NULL;
+
+ return g_strdup(e_folder_get_physical_uri(selected_e_folder));
+}
+
+
+struct _select_folder_data {
+ void (*done)(const char *uri, void *data);
+ void *data;
+};
+
+static void
+emfs_folder_selected(GtkWidget *w, const char *path, struct _select_folder_data *d)
+{
+ const char *uri = NULL;
+ EStorageSet *storage_set = mail_component_peek_storage_set (mail_component_peek ());
+ EFolder *folder;
+
+ folder = e_storage_set_get_folder(storage_set, path);
+ if (folder)
+ uri = e_folder_get_physical_uri(folder);
+
+ gtk_widget_hide(w);
+
+ d->done(uri, d->data);
+
+ gtk_widget_destroy(w);
+}
+
+static void
+emfs_folder_cancelled(GtkWidget *w, struct _select_folder_data *d)
+{
+ gtk_widget_destroy(w);
+}
+
+void
+em_select_folder(GtkWindow *parent_window, const char *title, const char *text, const char *default_folder_uri, void (*done)(const char *uri, void *data), void *data)
+{
+ EStorageSet *storage_set = mail_component_peek_storage_set (mail_component_peek ());
+ char *path;
+ GtkWidget *dialog;
+ struct _select_folder_data *d;
+
+ d = g_malloc0(sizeof(*d));
+ d->data = data;
+ d->done = done;
+
+ if (default_folder_uri)
+ path = e_storage_set_get_path_for_physical_uri(storage_set, default_folder_uri);
+ else
+ path = NULL;
+ dialog = e_folder_selection_dialog_new(storage_set, title, text, path, NULL, TRUE);
+ g_free(path);
+ /* ugh, painful api ... */
+ g_signal_connect(dialog, "folder_selected", G_CALLBACK(emfs_folder_selected), d);
+ g_signal_connect(dialog, "cancelled", G_CALLBACK(emfs_folder_cancelled), d);
+ g_object_set_data_full((GObject *)dialog, "emfs_data", d, g_free);
+ gtk_widget_show(dialog);
+}
diff --git a/mail/em-folder-selection.h b/mail/em-folder-selection.h
new file mode 100644
index 0000000000..374e1eec3f
--- /dev/null
+++ b/mail/em-folder-selection.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* em-folder-selection.h - UI for selecting folders.
+ *
+ * Copyright (C) 2002 Ximian, Inc.
+ *
+ * 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.
+ *
+ * Author: Ettore Perazzoli <ettore@ximian.com>
+ */
+
+#ifndef EM_FOLDER_SELECTION_H
+#define EM_FOLDER_SELECTION_H
+
+#include <camel/camel-folder.h>
+
+#include <gtk/gtkwindow.h>
+
+CamelFolder *em_folder_selection_run_dialog (GtkWindow *parent_window,
+ const char *title,
+ const char *caption,
+ CamelFolder *default_folder);
+char *em_folder_selection_run_dialog_uri(GtkWindow *parent_window,
+ const char *title,
+ const char *caption,
+ const char *default_folder_uri);
+
+void em_select_folder(GtkWindow *parent_window, const char *title, const char *text, const char *default_folder_uri, void (*done)(const char *uri, void *data), void *data);
+
+#endif /* EM_FOLDER_SELECTION_H */
diff --git a/mail/em-folder-selector.c b/mail/em-folder-selector.c
new file mode 100644
index 0000000000..3ccf00e497
--- /dev/null
+++ b/mail/em-folder-selector.c
@@ -0,0 +1,349 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright(C) 2000, 2001, 2002, 2003 Ximian, Inc.
+ *
+ * Authors: Ettore Perazzoli
+ * Michael Zucchi
+ *
+ * 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 "em-folder-selector.h"
+
+#include "shell/e-storage-set-view.h"
+#include "shell/e-storage-set.h"
+
+#include <libgnome/gnome-i18n.h>
+
+#include <gal/util/e-util.h>
+#include <gal/widgets/e-gui-utils.h>
+
+#include <gtk/gtkentry.h>
+#include <gtk/gtkbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkstock.h>
+
+#include <camel/camel-url.h>
+
+#include <string.h>
+
+#define PARENT_TYPE (gtk_dialog_get_type())
+static GtkDialogClass *parent_class = NULL;
+
+static gboolean
+check_folder_type_valid(EMFolderSelector *emfs)
+{
+ const char *selected;
+ EFolder *folder;
+
+ selected = e_storage_set_view_get_current_folder(emfs->essv);
+ if (selected == NULL)
+ return FALSE;
+
+ folder = e_storage_set_get_folder(emfs->ess, selected);
+ if (folder == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+#if 0 /* EPFIXME */
+static void
+folder_creation_dialog_result_cb(EShell *shell,
+ EShellFolderCreationDialogResult result,
+ const char *path,
+ void *data)
+{
+ EMFolderSelector *dialog;
+
+ dialog = EM_FOLDER_SELECTOR(data);
+
+ if (result == E_SHELL_FOLDER_CREATION_DIALOG_RESULT_SUCCESS)
+ e_storage_set_view_set_current_folder(E_STORAGE_SET_VIEW(priv->storage_set_view),
+ path);
+}
+#endif
+
+static void
+emfs_dispose(GObject *object)
+{
+ EMFolderSelector *emfs = (EMFolderSelector *)object;
+
+ if (emfs->ess != NULL) {
+ g_object_unref(emfs->ess);
+ emfs->ess = NULL;
+ emfs->essv = NULL;
+ }
+
+ (* G_OBJECT_CLASS(parent_class)->dispose)(object);
+}
+
+static void
+emfs_finalize(GObject *object)
+{
+ /*EMFolderSelector *emfs = (EMFolderSelector *)object;*/
+
+ (* G_OBJECT_CLASS(parent_class)->finalize)(object);
+}
+
+static void
+emfs_response(GtkDialog *dialog, int response)
+{
+ EMFolderSelector *emfs = (EMFolderSelector *)dialog;
+ const char *path;
+
+ switch (response) {
+ case EM_FOLDER_SELECTOR_RESPONSE_NEW:
+ path = e_storage_set_view_get_current_folder(emfs->essv);
+
+ printf("create new folder, default parent '%s'\n", path);
+ break;
+ }
+}
+
+static void
+emfs_class_init(EMFolderSelectorClass *klass)
+{
+ GObjectClass *object_class;
+ GtkDialogClass *dialog_class;
+
+ parent_class = g_type_class_ref(PARENT_TYPE);
+ object_class = G_OBJECT_CLASS(klass);
+ dialog_class = GTK_DIALOG_CLASS(klass);
+
+ object_class->dispose = emfs_dispose;
+ object_class->finalize = emfs_finalize;
+
+ dialog_class->response = emfs_response;
+}
+
+static void
+emfs_init(EMFolderSelector *emfs)
+{
+ emfs->flags = 0;
+}
+
+static void
+folder_selected_cb(EStorageSetView *essv, const char *path, EMFolderSelector *emfs)
+{
+ if (check_folder_type_valid(emfs))
+ gtk_dialog_set_response_sensitive(GTK_DIALOG(emfs), GTK_RESPONSE_OK, TRUE);
+ else
+ gtk_dialog_set_response_sensitive(GTK_DIALOG(emfs), GTK_RESPONSE_OK, FALSE);
+}
+
+static void
+double_click_cb(EStorageSetView *essv, int row, ETreePath path, int col, GdkEvent *event, EMFolderSelector *emfs)
+{
+ if (check_folder_type_valid(emfs)) {
+ /*g_signal_emit(emfs, signals[FOLDER_SELECTED], 0,
+ em_folder_selector_get_selected(emfs));*/
+ printf("double clicked!\n");
+ }
+}
+
+void
+em_folder_selector_construct(EMFolderSelector *emfs, EStorageSet *ess, guint32 flags, const char *title, const char *text)
+{
+ GtkWidget *scrolled_window;
+ GtkWidget *text_label;
+
+ gtk_window_set_default_size(GTK_WINDOW(emfs), 350, 300);
+ gtk_window_set_modal(GTK_WINDOW(emfs), TRUE);
+ gtk_window_set_title(GTK_WINDOW(emfs), title);
+ gtk_container_set_border_width(GTK_CONTAINER(emfs), 6);
+
+ emfs->flags = flags;
+ if (flags & EM_FOLDER_SELECTOR_CAN_CREATE)
+ gtk_dialog_add_buttons(GTK_DIALOG(emfs), GTK_STOCK_NEW, EM_FOLDER_SELECTOR_RESPONSE_NEW, NULL);
+
+ gtk_dialog_add_buttons(GTK_DIALOG(emfs),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_response_sensitive(GTK_DIALOG(emfs), GTK_RESPONSE_OK, FALSE);
+ gtk_dialog_set_default_response(GTK_DIALOG(emfs), GTK_RESPONSE_OK);
+
+ emfs->ess = ess;
+ g_object_ref(ess);
+
+ emfs->essv = (EStorageSetView *)e_storage_set_create_new_view(ess, NULL);
+ e_storage_set_view_set_allow_dnd(emfs->essv, FALSE);
+ e_storage_set_view_enable_search(emfs->essv, TRUE);
+
+ g_signal_connect(emfs->essv, "double_click", G_CALLBACK(double_click_cb), emfs);
+ g_signal_connect(emfs->essv, "folder_selected", G_CALLBACK(folder_selected_cb), emfs);
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_container_add(GTK_CONTAINER(scrolled_window), (GtkWidget *)emfs->essv);
+
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(emfs)->vbox), scrolled_window, TRUE, TRUE, 6);
+ gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(emfs)->vbox), 6);
+
+ gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(emfs)->vbox), 6);
+
+ gtk_widget_show((GtkWidget *)emfs->essv);
+ gtk_widget_show(scrolled_window);
+
+ if (text != NULL) {
+ text_label = gtk_label_new(text);
+ gtk_label_set_justify(GTK_LABEL(text_label), GTK_JUSTIFY_LEFT);
+ gtk_widget_show(text_label);
+
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(emfs)->vbox), text_label, FALSE, TRUE, 6);
+ gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(emfs)->vbox), 6);
+ }
+
+ GTK_WIDGET_SET_FLAGS((GtkWidget *)emfs->essv, GTK_CAN_FOCUS);
+ gtk_widget_grab_focus((GtkWidget *)emfs->essv);
+}
+
+GtkWidget *
+em_folder_selector_new(EStorageSet *ess, guint32 flags, const char *title, const char *text)
+{
+ EMFolderSelector *emfs;
+
+ g_return_val_if_fail(E_IS_STORAGE_SET(ess), NULL);
+
+ emfs = g_object_new(em_folder_selector_get_type(), NULL);
+ em_folder_selector_construct(emfs, ess, flags, title, text);
+
+ return GTK_WIDGET(emfs);
+}
+
+static void
+emfs_create_name_changed(GtkEntry *entry, EMFolderSelector *emfs)
+{
+ int active;
+
+ active = e_storage_set_view_get_current_folder(emfs->essv) != NULL
+ && emfs->name_entry->text_length > 0;
+
+ gtk_dialog_set_response_sensitive((GtkDialog *)emfs, GTK_RESPONSE_OK, active);
+}
+
+static void
+emfs_create_name_activate(GtkEntry *entry, EMFolderSelector *emfs)
+{
+ printf("entry activated, woop\n");
+}
+
+GtkWidget *
+em_folder_selector_create_new(EStorageSet *ess, guint32 flags, const char *title, const char *text)
+{
+ EMFolderSelector *emfs;
+ GtkWidget *hbox, *w;
+
+ g_return_val_if_fail(E_IS_STORAGE_SET(ess), NULL);
+
+ emfs = g_object_new(em_folder_selector_get_type(), NULL);
+ em_folder_selector_construct(emfs, ess, flags, title, text);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ w = gtk_label_new_with_mnemonic(_("Folder _name"));
+ gtk_box_pack_start((GtkBox *)hbox, w, FALSE, FALSE, 6);
+ emfs->name_entry = (GtkEntry *)gtk_entry_new();
+ g_signal_connect(emfs->name_entry, "changed", G_CALLBACK(emfs_create_name_changed), emfs);
+ g_signal_connect(emfs->name_entry, "activate", G_CALLBACK(emfs_create_name_activate), emfs);
+ gtk_box_pack_start((GtkBox *)hbox, (GtkWidget *)emfs->name_entry, TRUE, FALSE, 6);
+ gtk_widget_show_all(hbox);
+
+ gtk_box_pack_start((GtkBox *)((GtkDialog *)emfs)->vbox, hbox, FALSE, TRUE, 0);
+
+ return GTK_WIDGET(emfs);
+}
+
+void
+em_folder_selector_set_selected(EMFolderSelector *emfs, const char *path)
+{
+ e_storage_set_view_set_current_folder(emfs->essv, path);
+}
+
+void
+em_folder_selector_set_selected_uri(EMFolderSelector *emfs, const char *uri)
+{
+ const char *path;
+
+ path = e_storage_set_get_path_for_physical_uri(emfs->ess, uri);
+ if (path)
+ e_storage_set_view_set_current_folder(emfs->essv, path);
+}
+
+const char *
+em_folder_selector_get_selected(EMFolderSelector *emfs)
+{
+ const char *path;
+
+ path = e_storage_set_view_get_current_folder(emfs->essv);
+ if (emfs->name_entry) {
+ g_free(emfs->selected);
+ emfs->selected = g_strdup_printf("%s/%s", path, gtk_entry_get_text(emfs->name_entry));
+ path = emfs->selected;
+ }
+
+ return path;
+}
+
+const char *
+em_folder_selector_get_selected_uri(EMFolderSelector *emfs)
+{
+ const char *path;
+ EFolder *folder;
+
+ path = e_storage_set_view_get_current_folder(emfs->essv);
+ if (path == NULL) {
+ printf("current folder is null?\n");
+ return NULL;
+ }
+
+ folder = e_storage_set_get_folder(emfs->ess, path);
+ if (folder == NULL) {
+ printf("path ok, but can't get folder?\n");
+ return NULL;
+ }
+
+ path = e_folder_get_physical_uri(folder);
+ if (path && emfs->name_entry) {
+ CamelURL *url;
+ char *newpath;
+
+ url = camel_url_new(path, NULL);
+ newpath = g_strdup_printf("%s/%s", url->fragment?url->fragment:url->path, gtk_entry_get_text(emfs->name_entry));
+ if (url->fragment)
+ camel_url_set_fragment(url, newpath);
+ else
+ camel_url_set_path(url, newpath);
+ g_free(emfs->selected_uri);
+ emfs->selected_uri = camel_url_to_string(url, 0);
+ camel_url_free(url);
+ path = emfs->selected_uri;
+ }
+
+ return path;
+}
+
+E_MAKE_TYPE(em_folder_selector, "EMFolderSelector", EMFolderSelector, emfs_class_init, emfs_init, PARENT_TYPE)
diff --git a/mail/em-folder-selector.h b/mail/em-folder-selector.h
new file mode 100644
index 0000000000..48fc6758b5
--- /dev/null
+++ b/mail/em-folder-selector.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* e-folder-selection-dialog.h
+ *
+ * Copyright (C) 2000, 2001, 2002, 2003 Ximian, Inc.
+ *
+ * Authors: Ettore Perazzoli
+ * Michael Zucchi
+ *
+ * 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_FOLDER_SELECTOR_H
+#define EM_FOLDER_SELECTOR_H
+
+#include <gtk/gtkdialog.h>
+
+#ifdef cplusplus
+extern "C" {
+#pragma }
+#endif /* cplusplus */
+
+#define EM_TYPE_FOLDER_SELECTOR (em_folder_selector_get_type ())
+#define EM_FOLDER_SELECTOR(obj) (GTK_CHECK_CAST ((obj), E_TYPEM_FOLDER_SELECTOR, EMFolderSelector))
+#define EM_FOLDER_SELECTOR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPEM_FOLDER_SELECTOR, EMFolderSelectorClass))
+#define EM_IS_FOLDER_SELECTOR(obj) (GTK_CHECK_TYPE ((obj), E_TYPEM_FOLDER_SELECTOR))
+#define EM_IS_FOLDER_SELECTOR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPEM_FOLDER_SELECTOR))
+
+typedef struct _EMFolderSelector EMFolderSelector;
+typedef struct _EMFolderSelectorPrivate EMFolderSelectorPrivate;
+typedef struct _EMFolderSelectorClass EMFolderSelectorClass;
+
+struct _EStorageSet;
+struct _EStorageSetView;
+
+struct _EMFolderSelector {
+ GtkDialog parent;
+
+ guint32 flags;
+ struct _EStorageSet *ess;
+ struct _EStorageSetView *essv;
+
+ struct _GtkEntry *name_entry;
+ char *selected;
+ char *selected_uri;
+};
+
+struct _EMFolderSelectorClass {
+ GtkDialogClass parent_class;
+
+#if 0
+ void (* folder_selected) (EMFolderSelector *folder_selection_dialog,
+ const char *path);
+ void (* cancelled) (EMFolderSelector *folder_selection_dialog);
+#endif
+};
+
+enum {
+ EM_FOLDER_SELECTOR_CAN_CREATE = 1,
+};
+
+enum {
+ EM_FOLDER_SELECTOR_RESPONSE_NEW = 1,
+};
+
+GtkType em_folder_selector_get_type (void);
+void em_folder_selector_construct(EMFolderSelector *, struct _EStorageSet *, guint32, const char *, const char *);
+/* for selecting folders */
+GtkWidget *em_folder_selector_new (struct _EStorageSet *, guint32, const char *, const char *);
+
+/* for creating folders */
+GtkWidget *em_folder_selector_create_new(struct _EStorageSet *ess, guint32 flags, const char *title, const char *text);
+
+void em_folder_selector_set_selected (EMFolderSelector *emfs, const char *path);
+void em_folder_selector_set_selected_uri(EMFolderSelector *emfs, const char *uri);
+
+const char *em_folder_selector_get_selected (EMFolderSelector *emfs);
+const char *em_folder_selector_get_selected_uri(EMFolderSelector *emfs);
+
+#ifdef cplusplus
+}
+#endif /* cplusplus */
+
+#endif /* EM_FOLDER_SELECTOR_H */
diff --git a/mail/em-folder-view.c b/mail/em-folder-view.c
index 8099191b01..fac7d155d3 100644
--- a/mail/em-folder-view.c
+++ b/mail/em-folder-view.c
@@ -542,16 +542,52 @@ emfv_popup_undelete(GtkWidget *w, EMFolderView *emfv)
em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_DELETED, 0);
}
+struct _move_data {
+ EMFolderView *emfv;
+ GPtrArray *uids;
+ int delete;
+};
+
+static void
+emfv_popup_move_cb(const char *uri, void *data)
+{
+ struct _move_data *d = data;
+
+ if (uri)
+ mail_transfer_messages(d->emfv->folder, d->uids, d->delete, uri, 0, NULL, NULL);
+ else
+ em_utils_uids_free(d->uids);
+
+ g_object_unref(d->emfv);
+ g_free(d);
+}
+
static void
emfv_popup_move(GtkWidget *w, EMFolderView *emfv)
{
- /* FIXME */
+ struct _move_data *d;
+
+ d = g_malloc(sizeof(*d));
+ d->emfv = emfv;
+ g_object_ref(emfv);
+ d->uids = message_list_get_selected(emfv->list);
+ d->delete = TRUE;
+
+ em_select_folder((GtkWidget *)emfv, _("Select folder"), NULL, NULL, emfv_popup_move_cb, d);
}
static void
emfv_popup_copy(GtkWidget *w, EMFolderView *emfv)
{
- /* FIXME */
+ struct _move_data *d;
+
+ d = g_malloc(sizeof(*d));
+ d->emfv = emfv;
+ g_object_ref(emfv);
+ d->uids = message_list_get_selected(emfv->list);
+ d->delete = FALSE;
+
+ em_select_folder((GtkWidget *)emfv, _("Select folder"), NULL, NULL, emfv_popup_move_cb, d);
}
static void
diff --git a/mail/em-format-html.c b/mail/em-format-html.c
index 6d38eda3f5..f95624e655 100644
--- a/mail/em-format-html.c
+++ b/mail/em-format-html.c
@@ -62,6 +62,8 @@
#include <camel/camel-file-utils.h>
#include <e-util/e-msgport.h>
+
+#include "mail-component.h"
#include "mail-mt.h"
#include "em-format-html.h"
@@ -214,15 +216,15 @@ em_format_html_get_type(void)
sizeof(EMFormatHTML), 0,
(GInstanceInitFunc)efh_init
};
- extern char *evolution_dir;
+ const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
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);
+ path = alloca(strlen(base_directory)+16);
+ sprintf(path, "%s/cache", base_directory);
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);
diff --git a/mail/em-marshal.list b/mail/em-marshal.list
index 910bfb1b3d..0c8bfbbbfb 100644
--- a/mail/em-marshal.list
+++ b/mail/em-marshal.list
@@ -1 +1,2 @@
BOOLEAN:BOXED,POINTER,POINTER
+NONE:POINTER
diff --git a/mail/em-popup.c b/mail/em-popup.c
index 69fd1d0b6d..74ba86c33b 100644
--- a/mail/em-popup.c
+++ b/mail/em-popup.c
@@ -353,7 +353,8 @@ em_popup_create_menu_once(EMPopup *emp, EMPopupTarget *target, guint32 hide_mask
menu = em_popup_create_menu(emp, hide_mask, disable_mask);
- g_signal_connect_swapped(menu, "selection_done", G_CALLBACK(em_popup_target_free), target);
+ if (target)
+ 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;
diff --git a/mail/em-popup.h b/mail/em-popup.h
index 877b28fc28..0ad1d0015c 100644
--- a/mail/em-popup.h
+++ b/mail/em-popup.h
@@ -69,6 +69,7 @@ enum _em_popup_target_t {
EM_POPUP_TARGET_SELECT,
EM_POPUP_TARGET_URI,
EM_POPUP_TARGET_PART,
+ EM_POPUP_TARGET_FOLDER,
};
/* Flags that describe a TARGET_SELECT */
@@ -103,6 +104,13 @@ enum {
EM_POPUP_PART_IMAGE = 1<<1,
};
+/* Flags that describe TARGET_FOLDER */
+enum {
+ EM_POPUP_FOLDER_LOCAL = 1<<0,
+ EM_POPUP_FOLDER_REMOTE = 1<<1,
+ EM_POPUP_FOLDER_VFOLDER = 1<<2,
+};
+
struct _EMPopupTarget {
enum _em_popup_target_t type;
guint32 mask; /* depends on type, see above */
@@ -118,6 +126,9 @@ struct _EMPopupTarget {
char *mime_type;
struct _CamelMimePart *part;
} part;
+ struct {
+ char *folder_uri;
+ } folder;
} data;
};
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 49ef9af792..5b16d0cd7b 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -36,6 +36,7 @@
#include <filter/filter-editor.h>
+#include "mail-component.h"
#include "mail-mt.h"
#include "mail-ops.h"
#include "mail-tools.h"
@@ -215,14 +216,14 @@ 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);
+ user = g_strdup_printf ("%s/filters.xml",
+ mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save ((RuleContext *) fc, user);
g_free (user);
}
@@ -249,7 +250,7 @@ static const char *filter_source_names[] = {
void
em_utils_edit_filters (GtkWidget *parent)
{
- extern char *evolution_dir;
+ const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
char *user, *system;
FilterContext *fc;
@@ -259,7 +260,7 @@ em_utils_edit_filters (GtkWidget *parent)
}
fc = filter_context_new ();
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ user = g_strdup_printf ("%s/filters.xml", base_directory);
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *) fc, system, user);
g_free (user);
@@ -773,10 +774,10 @@ generate_account_hash (void)
return account_hash;
}
-static EDestination **
+static EABDestination **
em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
{
- EDestination *dest, **destv;
+ EABDestination *dest, **destv;
int n, i, j;
if (iaddr == NULL)
@@ -785,14 +786,14 @@ em_utils_camel_address_to_destination (CamelInternetAddress *iaddr)
if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0)
return NULL;
- destv = g_malloc (sizeof (EDestination *) * (n + 1));
+ destv = g_malloc (sizeof (EABDestination *) * (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);
+ dest = eab_destination_new ();
+ eab_destination_set_name (dest, name);
+ eab_destination_set_email (dest, addr);
destv[j++] = dest;
}
@@ -813,7 +814,7 @@ reply_get_composer (GtkWidget *parent, CamelMimeMessage *message, EAccount *acco
CamelInternetAddress *to, CamelInternetAddress *cc)
{
const char *message_id, *references;
- EDestination **tov, **ccv;
+ EABDestination **tov, **ccv;
EMsgComposer *composer;
char *subject;
@@ -1211,7 +1212,7 @@ post_reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *m
const char *message_id, *references;
CamelInternetAddress *to = NULL;
GtkWidget *parent = user_data;
- EDestination **tov = NULL;
+ EABDestination **tov = NULL;
EMsgComposer *composer;
char *subject, *url;
EAccount *account;
diff --git a/mail/em-utils.h b/mail/em-utils.h
index 0114c86ae2..a30c9109fb 100644
--- a/mail/em-utils.h
+++ b/mail/em-utils.h
@@ -36,6 +36,7 @@ struct _GtkWindow;
struct _CamelFolder;
struct _CamelStream;
struct _CamelMimeMessage;
+struct _CamelMimePart;
struct _GtkSelectionData;
struct _GtkAdjustment;
struct _EMsgComposer;
diff --git a/mail/folder-browser-factory.c b/mail/folder-browser-factory.c
index f0157bac9a..194396b062 100644
--- a/mail/folder-browser-factory.c
+++ b/mail/folder-browser-factory.c
@@ -118,14 +118,13 @@ control_destroy_cb (GtkObject *fb, GObject *control)
}
BonoboControl *
-folder_browser_factory_new_control (const char *uri,
- const GNOME_Evolution_Shell shell)
+folder_browser_factory_new_control (const char *uri)
{
BonoboControl *control;
GtkWidget *fb;
#if 0
- if (!(fb = folder_browser_new (shell, uri)))
+ if (!(fb = folder_browser_new (uri)))
return NULL;
FOLDER_BROWSER (fb)->pref_master = TRUE; /* save UI settings changed in this FB */
diff --git a/mail/folder-browser-factory.h b/mail/folder-browser-factory.h
index 2c858c9c56..109938c27e 100644
--- a/mail/folder-browser-factory.h
+++ b/mail/folder-browser-factory.h
@@ -15,8 +15,7 @@
#include "Evolution.h"
#include "e-util/e-list.h"
-BonoboControl *folder_browser_factory_new_control (const char *uri,
- const GNOME_Evolution_Shell shell);
+BonoboControl *folder_browser_factory_new_control (const char *uri);
EList *folder_browser_factory_get_control_list (void);
struct _EMFolderBrowser *folder_browser_factory_get_browser(const char *uri);
diff --git a/mail/folder-browser-ui.c b/mail/folder-browser-ui.c
new file mode 100644
index 0000000000..9ccdb2db79
--- /dev/null
+++ b/mail/folder-browser-ui.c
@@ -0,0 +1,816 @@
+/* -*- 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
new file mode 100644
index 0000000000..5c2bc1fa28
--- /dev/null
+++ b/mail/folder-browser-ui.h
@@ -0,0 +1,36 @@
+/* -*- 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
new file mode 100644
index 0000000000..6d7deb2bdb
--- /dev/null
+++ b/mail/folder-browser.c
@@ -0,0 +1,2679 @@
+/* -*- 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-component.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) {
+ g_source_remove (folder_browser->seen_id);
+ folder_browser->seen_id = 0;
+ }
+
+ if (folder_browser->loading_id != 0) {
+ g_source_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_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);
+ 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)
+{
+ RuleContext *search_context = mail_component_peek_search_context (mail_component_peek ());
+ 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)
+ g_source_remove (fb->seen_id);
+
+ if (msg && gconf_client_get_bool (gconf, "/apps/evolution/mail/display/mark_seen", NULL)) {
+ if (timeout > 0)
+ fb->seen_id = g_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)
+ g_source_remove (fb->loading_id);
+
+ g_free (fb->new_uid);
+ fb->new_uid = g_strdup (uid);
+
+ if (fb->preview_shown)
+ fb->loading_id = g_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 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);
+
+ 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
new file mode 100644
index 0000000000..1b03ec9efb
--- /dev/null
+++ b/mail/folder-browser.h
@@ -0,0 +1,192 @@
+/* -*- 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_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 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 62ddfc4d06..6e6ce85d2c 100644
--- a/mail/mail-account-gui.c
+++ b/mail/mail-account-gui.c
@@ -36,17 +36,21 @@
#include <e-util/e-account-list.h>
#include <e-util/e-dialog-utils.h>
-#include "evolution-folder-selector-button.h"
+#include "em-folder-selection-button.h"
#include "mail-account-gui.h"
#include "mail-session.h"
#include "mail-send-recv.h"
#include "mail-signature-editor.h"
+#include "mail-component.h"
#include "mail-composer-prefs.h"
#include "mail-config.h"
#include "mail-ops.h"
#include "mail-mt.h"
#include "mail.h"
+#include "e-storage.h"
+
+
#define d(x)
extern char *default_drafts_folder_uri, *default_sent_folder_uri;
@@ -1060,16 +1064,13 @@ extract_values (MailAccountGuiService *source, GHashTable *extra_config, CamelUR
}
}
-
static void
-folder_selected (EvolutionFolderSelectorButton *button,
- GNOME_Evolution_Folder *corba_folder,
- gpointer user_data)
+folder_selected (EMFolderSelectionButton *button, gpointer user_data)
{
char **folder_name = user_data;
g_free (*folder_name);
- *folder_name = g_strdup (corba_folder->physicalUri);
+ *folder_name = g_strdup(em_folder_selection_button_get_selection(button));
}
static void
@@ -1080,14 +1081,12 @@ default_folders_clicked (GtkButton *button, gpointer user_data)
/* Drafts folder */
g_free (gui->drafts_folder_uri);
gui->drafts_folder_uri = g_strdup (default_drafts_folder_uri);
- evolution_folder_selector_button_set_uri (EVOLUTION_FOLDER_SELECTOR_BUTTON (gui->drafts_folder_button),
- gui->drafts_folder_uri);
+ em_folder_selection_button_set_selection((EMFolderSelectionButton *)gui->drafts_folder_button, gui->drafts_folder_uri);
/* Sent folder */
g_free (gui->sent_folder_uri);
gui->sent_folder_uri = g_strdup (default_sent_folder_uri);
- evolution_folder_selector_button_set_uri (EVOLUTION_FOLDER_SELECTOR_BUTTON (gui->sent_folder_button),
- gui->sent_folder_uri);
+ em_folder_selection_button_set_selection((EMFolderSelectionButton *)gui->sent_folder_button, gui->sent_folder_uri);
}
GtkWidget *mail_account_gui_folder_selector_button_new (char *widget_name, char *string1, char *string2, int int1, int int2);
@@ -1097,7 +1096,7 @@ mail_account_gui_folder_selector_button_new (char *widget_name,
char *string1, char *string2,
int int1, int int2)
{
- return (GtkWidget *)g_object_new (EVOLUTION_TYPE_FOLDER_SELECTOR_BUTTON, NULL);
+ return (GtkWidget *)em_folder_selection_button_new(_("Select Folder"), NULL);
}
static gboolean
@@ -1410,7 +1409,6 @@ prepare_signatures (MailAccountGui *gui)
MailAccountGui *
mail_account_gui_new (EAccount *account, MailAccountsTab *dialog)
{
- const char *allowed_types[] = { "mail/*", NULL };
MailAccountGui *gui;
GtkWidget *button;
@@ -1499,31 +1497,21 @@ mail_account_gui_new (EAccount *account, MailAccountsTab *dialog)
/* Drafts folder */
gui->drafts_folder_button = GTK_BUTTON (glade_xml_get_widget (gui->xml, "drafts_button"));
- g_signal_connect (gui->drafts_folder_button, "selected",
- G_CALLBACK (folder_selected), &gui->drafts_folder_uri);
+ g_signal_connect (gui->drafts_folder_button, "selected", G_CALLBACK (folder_selected), &gui->drafts_folder_uri);
if (account->drafts_folder_uri)
gui->drafts_folder_uri = g_strdup (account->drafts_folder_uri);
else
gui->drafts_folder_uri = g_strdup (default_drafts_folder_uri);
- evolution_folder_selector_button_construct (EVOLUTION_FOLDER_SELECTOR_BUTTON (gui->drafts_folder_button),
- global_shell_client,
- _("Select Folder"),
- gui->drafts_folder_uri,
- allowed_types);
+ em_folder_selection_button_set_selection((EMFolderSelectionButton *)gui->drafts_folder_button, gui->drafts_folder_uri);
/* Sent folder */
gui->sent_folder_button = GTK_BUTTON (glade_xml_get_widget (gui->xml, "sent_button"));
- g_signal_connect (gui->sent_folder_button, "selected",
- G_CALLBACK (folder_selected), &gui->sent_folder_uri);
+ g_signal_connect (gui->sent_folder_button, "selected", G_CALLBACK (folder_selected), &gui->sent_folder_uri);
if (account->sent_folder_uri)
gui->sent_folder_uri = g_strdup (account->sent_folder_uri);
else
gui->sent_folder_uri = g_strdup (default_sent_folder_uri);
- evolution_folder_selector_button_construct (EVOLUTION_FOLDER_SELECTOR_BUTTON (gui->sent_folder_button),
- global_shell_client,
- _("Select Folder"),
- gui->sent_folder_uri,
- allowed_types);
+ em_folder_selection_button_set_selection((EMFolderSelectionButton *)gui->sent_folder_button, gui->sent_folder_uri);
/* Special Folders "Reset Defaults" button */
button = glade_xml_get_widget (gui->xml, "default_folders_button");
@@ -1842,18 +1830,21 @@ static void
add_new_store (char *uri, CamelStore *store, void *user_data)
{
EAccount *account = user_data;
- EvolutionStorage *storage;
+ MailComponent *component = mail_component_peek ();
+ EStorage *storage;
if (store == NULL)
return;
+
+ /* EPFIXME: Strange refcounting semantics here?! */
- storage = mail_lookup_storage (store);
+ storage = mail_component_lookup_storage (component, store);
if (storage) {
/* store is already in the folder tree, so do nothing */
- bonobo_object_unref (BONOBO_OBJECT (storage));
+ g_object_unref (storage);
} else {
/* store is *not* in the folder tree, so lets add it. */
- mail_add_storage (store, account->name, account->source->url);
+ mail_component_add_store (component, store, account->name);
}
}
@@ -1968,7 +1959,7 @@ mail_account_gui_save (MailAccountGui *gui)
#define sources_equal(old,new) (new->url && !strcmp (old->url, new->url))
if (!sources_equal (account->source, new->source)) {
/* Remove the old storage from the folder-tree */
- mail_remove_storage_by_uri (account->source->url);
+ mail_component_remove_storage_by_uri (mail_component_peek (), account->source->url);
}
}
diff --git a/mail/mail-accounts.c b/mail/mail-accounts.c
index fb06efd6ba..b17652bb66 100644
--- a/mail/mail-accounts.c
+++ b/mail/mail-accounts.c
@@ -33,9 +33,11 @@
#include <gtk/gtktreeselection.h>
#include "mail.h"
+#include "mail-component.h"
#include "mail-config.h"
#include "mail-config-druid.h"
#include "mail-account-editor.h"
+#include "mail-ops.h"
#include "mail-send-recv.h"
#include "art/mark.xpm"
@@ -248,8 +250,8 @@ account_delete_clicked (GtkButton *button, gpointer user_data)
/* remove it from the folder-tree in the shell */
if (account->enabled && account->source && account->source->url)
- mail_remove_storage_by_uri (account->source->url);
-
+ mail_component_remove_storage_by_uri (mail_component_peek (), account->source->url);
+
/* remove it from the config file */
mail_config_remove_account (account);
accounts = mail_config_get_accounts ();
@@ -297,6 +299,7 @@ account_default_clicked (GtkButton *button, gpointer user_data)
static void
account_able_clicked (GtkButton *button, gpointer user_data)
{
+ MailComponent *component = mail_component_peek ();
MailAccountsTab *prefs = user_data;
GtkTreeSelection *selection;
EAccount *account = NULL;
@@ -317,9 +320,11 @@ account_able_clicked (GtkButton *button, gpointer user_data)
folder-tree, otherwise add it to the folder-tree */
if (account->source->url) {
if (account->enabled)
- mail_load_storage_by_uri (prefs->shell, account->source->url, account->name);
+ mail_component_load_storage_by_uri (component,
+ account->source->url,
+ account->name);
else
- mail_remove_storage_by_uri (account->source->url);
+ mail_component_remove_storage_by_uri (component, account->source->url);
}
mail_autoreceive_setup ();
@@ -354,17 +359,18 @@ account_able_toggled (GtkCellRendererToggle *renderer, char *arg1, gpointer user
gtk_tree_path_free (path);
if (account) {
+ MailComponent *component = mail_component_peek ();
+
/* if the account got disabled, remove it from the
folder-tree, otherwise add it to the folder-tree */
if (account->source->url) {
if (account->enabled)
- mail_load_storage_by_uri (prefs->shell, account->source->url, account->name);
+ mail_component_load_storage_by_uri (component, account->source->url, account->name);
else
- mail_remove_storage_by_uri (account->source->url);
+ mail_component_remove_storage_by_uri (component, account->source->url);
}
mail_autoreceive_setup ();
-
mail_config_write ();
}
}
diff --git a/mail/mail-accounts.etspec b/mail/mail-accounts.etspec
new file mode 100644
index 0000000000..22c09370b1
--- /dev/null
+++ b/mail/mail-accounts.etspec
@@ -0,0 +1,12 @@
+<ETableSpecification cursor-mode="line" draw-grid="false" draw-focus="true" selection-mode="single">
+ <ETableColumn model_col= "0" _title="Enabled" pixbuf="enabled" expansion="0.0" minimum_width="18" resizable="false" cell="render_message_status" compare="integer" sortable="false"/>
+
+ <ETableColumn model_col= "1" _title="Account name" expansion="1.6" minimum_width="32" resizable="true" cell="render_text" compare="string"/>
+
+ <ETableColumn model_col= "2" _title="Protocol" expansion="0.8" minimum_width="32" resizable="true" cell="render_text" compare="string"/>
+
+ <ETableState>
+ <column source="0"/> <column source="1"/> <column source="2"/>
+ <grouping> </grouping>
+ </ETableState>
+</ETableSpecification>
diff --git a/mail/mail-autofilter.c b/mail/mail-autofilter.c
index ac116aea7d..9e98758700 100644
--- a/mail/mail-autofilter.c
+++ b/mail/mail-autofilter.c
@@ -51,7 +51,6 @@
#include "filter/filter-editor.h"
#include "filter/filter-option.h"
-extern char *evolution_dir;
static void
rule_match_recipients (RuleContext *context, FilterRule *rule, CamelInternetAddress *iaddr)
@@ -313,7 +312,8 @@ filter_gui_add_from_message (CamelMimeMessage *msg, const char *source, int flag
g_return_if_fail (msg != NULL);
fc = filter_context_new ();
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ user = g_strdup_printf ("%s/filters.xml",
+ mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *)fc, system, user);
rule = filter_rule_from_message (fc, msg, flags);
@@ -334,7 +334,7 @@ mail_filter_rename_uri(CamelStore *store, const char *olduri, const char *newuri
GList *changed;
fc = filter_context_new ();
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ user = g_strdup_printf ("%s/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *)fc, system, user);
@@ -359,7 +359,7 @@ mail_filter_delete_uri(CamelStore *store, const char *uri)
GList *deleted;
fc = filter_context_new ();
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ user = g_strdup_printf ("%s/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
rule_context_load ((RuleContext *)fc, system, user);
diff --git a/mail/mail-callbacks.c b/mail/mail-callbacks.c
new file mode 100644
index 0000000000..cfb657b542
--- /dev/null
+++ b/mail/mail-callbacks.c
@@ -0,0 +1,3228 @@
+/* -*- 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-component.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
+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", mail_component_peek_base_directory (mail_component_peek ()));
+ 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", mail_component_peek_base_directory (mail_component_peek ()));
+ 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->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
new file mode 100644
index 0000000000..6b2c4573c9
--- /dev/null
+++ b/mail/mail-callbacks.h
@@ -0,0 +1,144 @@
+/* -*- 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-component-factory.c b/mail/mail-component-factory.c
new file mode 100644
index 0000000000..00087b7439
--- /dev/null
+++ b/mail/mail-component-factory.c
@@ -0,0 +1,104 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* mail-component-factory.c
+ *
+ * Authors: Ettore Perazzoli <ettore@ximian.com>
+ *
+ * Copyright (C) 2003 Ximian, Inc.
+ *
+ * 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.
+ */
+
+#include "em-utils.h"
+#include "evolution-composer.h"
+#include "mail-accounts.h"
+#include "mail-component.h"
+#include "mail-composer-prefs.h"
+#include "mail-config-druid.h"
+#include "mail-config-factory.h"
+#include "mail-config.h"
+#include "mail-mt.h"
+#include "mail-preferences.h"
+
+#include <bonobo-activation/bonobo-activation.h>
+#include <bonobo/bonobo-shlib-factory.h>
+
+#include <string.h>
+
+
+#define FACTORY_ID "OAFIID:GNOME_Evolution_Mail_Factory_2"
+
+#define COMPONENT_ID "OAFIID:GNOME_Evolution_Mail_Component_2"
+#define COMPOSER_ID "OAFIID:GNOME_Evolution_Mail_Composer"
+#define FOLDER_INFO_ID "OAFIID:GNOME_Evolution_FolderInfo"
+#define MAIL_CONFIG_ID "OAFIID:GNOME_Evolution_MailConfig"
+#define WIZARD_ID "OAFIID:GNOME_Evolution_Mail_Wizard"
+
+
+/* EPFIXME: This stuff is here just to get it to compile, it should be moved
+ out of the way (was originally in component-factory.c). */
+EvolutionShellClient *global_shell_client = NULL;
+
+static BonoboObject *
+factory (BonoboGenericFactory *factory,
+ const char *component_id,
+ void *closure)
+{
+ /* EPFIXME this is messy. The IDs are defined all over the place
+ without a logic... */
+
+ if (strcmp (component_id, COMPONENT_ID) == 0) {
+ MailComponent *component = mail_component_peek ();
+
+ bonobo_object_ref (BONOBO_OBJECT (component));
+ return BONOBO_OBJECT (component);
+ } else if (strcmp(component_id, MAIL_CONFIG_ID) == 0) {
+ return (BonoboObject *)g_object_new (evolution_mail_config_get_type (), NULL);
+ } else if (strcmp(component_id, WIZARD_ID) == 0) {
+ return evolution_mail_config_wizard_new();
+ } else if (strcmp (component_id, MAIL_ACCOUNTS_CONTROL_ID) == 0
+ || strcmp (component_id, MAIL_PREFERENCES_CONTROL_ID) == 0
+ || strcmp (component_id, MAIL_COMPOSER_PREFS_CONTROL_ID) == 0) {
+ return mail_config_control_factory_cb (factory, component_id, CORBA_OBJECT_NIL);
+ } else if (strcmp(component_id, COMPOSER_ID) == 0) {
+ /* FIXME: how to remove need for callbacks, probably make the composer more tightly integrated with mail */
+ 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;
+}
+
+static Bonobo_Unknown
+make_factory (PortableServer_POA poa, const char *iid, gpointer impl_ptr, CORBA_Environment *ev)
+{
+ static int init = 0;
+
+ if (!init) {
+ mail_config_init ();
+ mail_msg_init ();
+ init = 1;
+ }
+
+ return bonobo_shlib_factory_std (FACTORY_ID, poa, impl_ptr, factory, NULL, ev);
+}
+
+static BonoboActivationPluginObject plugin_list[] = {
+ { FACTORY_ID, make_factory},
+ { NULL }
+};
+
+const BonoboActivationPlugin Bonobo_Plugin_info = {
+ plugin_list, "Evolution Mail component factory"
+};
diff --git a/mail/mail-component.c b/mail/mail-component.c
new file mode 100644
index 0000000000..e4289eda19
--- /dev/null
+++ b/mail/mail-component.c
@@ -0,0 +1,1625 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* mail-component.c
+ *
+ * Copyright (C) 2003 Ximian Inc.
+ *
+ * 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.
+ *
+ * Author: Ettore Perazzoli <ettore@ximian.com>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "e-storage.h"
+#include "e-storage-set.h"
+#include "e-storage-browser.h"
+#include "e-storage-set-view.h"
+#include "em-folder-selector.h"
+#include "em-folder-selection.h"
+
+#include "folder-browser-factory.h"
+#include "mail-config.h"
+#include "mail-component.h"
+#include "mail-folder-cache.h"
+#include "mail-vfolder.h"
+#include "mail-mt.h"
+#include "mail-ops.h"
+#include "mail-send-recv.h"
+#include "mail-session.h"
+
+#include "em-popup.h"
+
+#include <gtk/gtklabel.h>
+
+#include <gal/e-table/e-tree.h>
+#include <gal/e-table/e-tree-memory.h>
+
+#include <camel/camel.h>
+
+#include <bonobo/bonobo-control.h>
+#include <bonobo/bonobo-widget.h>
+
+
+#define PARENT_TYPE bonobo_object_get_type ()
+static BonoboObjectClass *parent_class = NULL;
+
+struct _MailComponentPrivate {
+ char *base_directory;
+
+ MailAsyncEvent *async_event;
+ GHashTable *storages_hash; /* storage by store */
+
+ EFolderTypeRegistry *folder_type_registry;
+ EStorageSet *storage_set;
+
+ RuleContext *search_context;
+
+ char *context_path; /* current path for right-click menu */
+
+ CamelStore *local_store;
+};
+
+static int emc_tree_right_click(ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, MailComponent *component);
+
+/* Utility functions. */
+
+/* EPFIXME: Eeek, this totally sucks. See comment in e-storage.h,
+ async_open_folder() should NOT be a signal. */
+
+struct _StorageConnectedData {
+ EStorage *storage;
+ char *path;
+ EStorageDiscoveryCallback callback;
+ void *callback_data;
+};
+typedef struct _StorageConnectedData StorageConnectedData;
+
+static void
+storage_connected_callback (CamelStore *store,
+ CamelFolderInfo *info,
+ StorageConnectedData *data)
+{
+ EStorageResult result;
+
+ if (info != NULL)
+ result = E_STORAGE_OK;
+ else
+ result = E_STORAGE_GENERICERROR;
+
+ (* data->callback) (data->storage, result, data->path, data->callback_data);
+
+ g_object_unref (data->storage);
+ g_free (data->path);
+ g_free (data);
+}
+
+static void
+storage_async_open_folder_callback (EStorage *storage,
+ const char *path,
+ EStorageDiscoveryCallback callback,
+ void *callback_data,
+ CamelStore *store)
+{
+ StorageConnectedData *storage_connected_data = g_new0 (StorageConnectedData, 1);
+
+ g_object_ref (storage);
+
+ storage_connected_data->storage = storage;
+ storage_connected_data->path = g_strdup (path);
+ storage_connected_data->callback = callback;
+ storage_connected_data->callback_data = callback_data;
+
+ mail_note_store (store, NULL, storage,
+ (void *) storage_connected_callback, storage_connected_data);
+}
+
+static void
+add_storage (MailComponent *component,
+ const char *name,
+ CamelService *store,
+ CamelException *ex)
+{
+ EStorage *storage;
+ EFolder *root_folder;
+
+ root_folder = e_folder_new (name, "noselect", "");
+ storage = e_storage_new (name, root_folder);
+ e_storage_declare_has_subfolders(storage, "/", _("Connecting..."));
+
+ camel_object_ref(store);
+
+ g_object_set_data((GObject *)storage, "em-store", store);
+ g_hash_table_insert (component->priv->storages_hash, store, storage);
+
+ g_signal_connect(storage, "async_open_folder",
+ G_CALLBACK (storage_async_open_folder_callback), store);
+
+#if 0
+ /* EPFIXME these are not needed anymore. */
+ g_signal_connect(storage, "create_folder", G_CALLBACK(storage_create_folder), store);
+ g_signal_connect(storage, "remove_folder", G_CALLBACK(storage_remove_folder), store);
+ g_signal_connect(storage, "xfer_folder", G_CALLBACK(storage_xfer_folder), store);
+#endif
+
+ e_storage_set_add_storage (component->priv->storage_set, storage);
+
+ mail_note_store ((CamelStore *) store, NULL, storage, NULL, NULL);
+
+ g_object_unref (storage);
+}
+
+static void
+load_accounts(MailComponent *component, EAccountList *accounts)
+{
+ EIterator *iter;
+
+ /* Load each service (don't connect!). Check its provider and
+ * see if this belongs in the shell's folder list. If so, add
+ * it.
+ */
+
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ EAccountService *service;
+ EAccount *account;
+ const char *name;
+
+ account = (EAccount *) e_iterator_get (iter);
+ service = account->source;
+ name = account->name;
+
+ if (account->enabled && service->url != NULL)
+ mail_component_load_storage_by_uri (component, service->url, name);
+
+ e_iterator_next (iter);
+ }
+
+ g_object_unref (iter);
+}
+
+static inline gboolean
+type_is_mail (const char *type)
+{
+ return !strcmp (type, "mail") || !strcmp (type, "mail/public");
+}
+
+static inline gboolean
+type_is_vtrash (const char *type)
+{
+ return !strcmp (type, "vtrash");
+}
+
+static void
+storage_go_online (gpointer key, gpointer value, gpointer data)
+{
+ CamelStore *store = key;
+ CamelService *service = CAMEL_SERVICE (store);
+
+ if (! (service->provider->flags & CAMEL_PROVIDER_IS_REMOTE)
+ || (service->provider->flags & CAMEL_PROVIDER_IS_EXTERNAL))
+ return;
+
+ if ((CAMEL_IS_DISCO_STORE (service)
+ && camel_disco_store_status (CAMEL_DISCO_STORE (service)) == CAMEL_DISCO_STORE_OFFLINE)
+ || service->status != CAMEL_SERVICE_DISCONNECTED) {
+ mail_store_set_offline (store, FALSE, NULL, NULL);
+ mail_note_store (store, NULL, NULL, NULL, NULL);
+ }
+}
+
+static void
+go_online (MailComponent *component)
+{
+ camel_session_set_online(session, TRUE);
+ mail_session_set_interactive(TRUE);
+ mail_component_storages_foreach(component, storage_go_online, NULL);
+}
+
+static void
+setup_search_context (MailComponent *component)
+{
+ MailComponentPrivate *priv = component->priv;
+ char *user = g_strdup_printf ("%s/evolution/searches.xml", g_get_home_dir ()); /* EPFIXME should be somewhere else. */
+ char *system = g_strdup (EVOLUTION_PRIVDATADIR "/vfoldertypes.xml");
+
+ priv->search_context = rule_context_new ();
+ g_object_set_data_full (G_OBJECT (priv->search_context), "user", user, g_free);
+ g_object_set_data_full (G_OBJECT (priv->search_context), "system", system, g_free);
+
+ rule_context_add_part_set (priv->search_context, "partset", filter_part_get_type (),
+ rule_context_add_part, rule_context_next_part);
+
+ rule_context_add_rule_set (priv->search_context, "ruleset", filter_rule_get_type (),
+ rule_context_add_rule, rule_context_next_rule);
+
+ rule_context_load (priv->search_context, system, user);
+}
+
+/* Local store setup. */
+char *default_drafts_folder_uri;
+CamelFolder *drafts_folder = NULL;
+char *default_sent_folder_uri;
+CamelFolder *sent_folder = NULL;
+char *default_outbox_folder_uri;
+CamelFolder *outbox_folder = NULL;
+char *default_inbox_folder_uri;
+CamelFolder *inbox_folder = NULL;
+
+static struct {
+ char *base;
+ char **uri;
+ CamelFolder **folder;
+} default_folders[] = {
+ { "Inbox", &default_inbox_folder_uri, &inbox_folder },
+ { "Drafts", &default_drafts_folder_uri, &drafts_folder },
+ { "Outbox", &default_outbox_folder_uri, &outbox_folder },
+ { "Sent", &default_sent_folder_uri, &sent_folder },
+};
+
+static void
+setup_local_store(MailComponent *component)
+{
+ MailComponentPrivate *p = component->priv;
+ CamelException ex;
+ char *store_uri;
+ int i;
+
+ g_assert(p->local_store == NULL);
+
+ /* EPFIXME It should use base_directory once we have moved it. */
+ store_uri = g_strconcat("mbox:", g_get_home_dir(), "/.evolution/mail/local", NULL);
+ p->local_store = mail_component_load_storage_by_uri(component, store_uri, _("On this Computer"));
+ camel_object_ref(p->local_store);
+
+ camel_exception_init (&ex);
+ for (i=0;i<sizeof(default_folders)/sizeof(default_folders[0]);i++) {
+ /* FIXME: should this uri be account relative? */
+ *default_folders[i].uri = g_strdup_printf("%s#%s", store_uri, default_folders[i].base);
+ *default_folders[i].folder = camel_store_get_folder(p->local_store, default_folders[i].base,
+ CAMEL_STORE_FOLDER_CREATE, &ex);
+ camel_exception_clear(&ex);
+ }
+
+ g_free(store_uri);
+}
+
+/* EStorageBrowser callbacks. */
+
+static BonoboControl *
+create_noselect_control (void)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new (_("This folder cannot contain messages."));
+ gtk_widget_show (label);
+ return bonobo_control_new (label);
+}
+
+static GtkWidget *
+create_view_callback (EStorageBrowser *browser,
+ const char *path,
+ void *unused_data)
+{
+ BonoboControl *control;
+ EFolder *folder;
+ const char *folder_type;
+ const char *physical_uri;
+
+ folder = e_storage_set_get_folder (e_storage_browser_peek_storage_set (browser), path);
+ if (folder == NULL) {
+ g_warning ("No folder at %s", path);
+ return gtk_label_new ("(You should not be seeing this label)");
+ }
+
+ folder_type = e_folder_get_type_string (folder);
+ physical_uri = e_folder_get_physical_uri (folder);
+
+ if (type_is_mail (folder_type)) {
+ const char *noselect;
+ CamelURL *url;
+
+ url = camel_url_new (physical_uri, NULL);
+ noselect = url ? camel_url_get_param (url, "noselect") : NULL;
+ if (noselect && !strcasecmp (noselect, "yes"))
+ control = create_noselect_control ();
+ else
+ control = folder_browser_factory_new_control (physical_uri);
+ camel_url_free (url);
+ } else if (type_is_vtrash (folder_type)) {
+ if (!strncasecmp (physical_uri, "file:", 5))
+ control = folder_browser_factory_new_control ("vtrash:file:/");
+ else
+ control = folder_browser_factory_new_control (physical_uri);
+ } else
+ return NULL;
+
+ if (!control)
+ return NULL;
+
+ /* EPFIXME: This leaks the control. */
+ return bonobo_widget_new_control_from_objref (BONOBO_OBJREF (control), CORBA_OBJECT_NIL);
+}
+
+static void
+browser_page_switched_callback (EStorageBrowser *browser,
+ GtkWidget *old_page,
+ GtkWidget *new_page,
+ BonoboControl *parent_control)
+{
+ if (BONOBO_IS_WIDGET (old_page)) {
+ BonoboControlFrame *control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (old_page));
+
+ bonobo_control_frame_control_deactivate (control_frame);
+ }
+
+ if (BONOBO_IS_WIDGET (new_page)) {
+ BonoboControlFrame *control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (new_page));
+ Bonobo_UIContainer ui_container = bonobo_control_get_remote_ui_container (parent_control, NULL);
+
+ /* This is necessary because we are not embedding the folder browser control
+ directly; we are putting the folder browser control into a notebook which
+ is then exported to the shell as a control. So we need to forward the
+ notebook's UIContainer to the folder browser. */
+ bonobo_control_frame_set_ui_container (control_frame, ui_container, NULL);
+
+ bonobo_control_frame_control_activate (control_frame);
+ }
+}
+
+/* GObject methods. */
+
+static void
+impl_dispose (GObject *object)
+{
+ MailComponentPrivate *priv = MAIL_COMPONENT (object)->priv;
+
+ if (priv->storage_set != NULL) {
+ g_object_unref (priv->storage_set);
+ priv->storage_set = NULL;
+ }
+
+ if (priv->folder_type_registry != NULL) {
+ g_object_unref (priv->folder_type_registry);
+ priv->folder_type_registry = NULL;
+ }
+
+ if (priv->search_context != NULL) {
+ g_object_unref (priv->search_context);
+ priv->search_context = NULL;
+ }
+
+ if (priv->local_store != NULL) {
+ camel_object_unref (CAMEL_OBJECT (priv->local_store));
+ priv->local_store = NULL;
+ }
+
+ (* G_OBJECT_CLASS (parent_class)->dispose) (object);
+}
+
+static void
+impl_finalize (GObject *object)
+{
+ MailComponentPrivate *priv = MAIL_COMPONENT (object)->priv;
+
+ g_free (priv->base_directory);
+
+ mail_async_event_destroy (priv->async_event);
+
+ g_hash_table_destroy (priv->storages_hash); /* EPFIXME free the data within? */
+
+ if (mail_async_event_destroy (priv->async_event) == -1) {
+ g_warning("Cannot destroy async event: would deadlock");
+ g_warning(" system may be unstable at exit");
+ }
+
+ g_free(priv->context_path);
+ g_free (priv);
+
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+/* Evolution::Component CORBA methods. */
+
+static void
+impl_createControls (PortableServer_Servant servant,
+ Bonobo_Control *corba_sidebar_control,
+ Bonobo_Control *corba_view_control,
+ CORBA_Environment *ev)
+{
+ MailComponent *mail_component = MAIL_COMPONENT (bonobo_object_from_servant (servant));
+ MailComponentPrivate *priv = mail_component->priv;
+ EStorageBrowser *browser;
+ GtkWidget *tree_widget;
+ GtkWidget *view_widget;
+ BonoboControl *sidebar_control;
+ BonoboControl *view_control;
+
+ browser = e_storage_browser_new (priv->storage_set, "/", create_view_callback, NULL);
+
+ tree_widget = e_storage_browser_peek_tree_widget (browser);
+ view_widget = e_storage_browser_peek_view_widget (browser);
+
+ gtk_widget_show (tree_widget);
+ gtk_widget_show (view_widget);
+
+ sidebar_control = bonobo_control_new (tree_widget);
+ view_control = bonobo_control_new (view_widget);
+
+ *corba_sidebar_control = CORBA_Object_duplicate (BONOBO_OBJREF (sidebar_control), ev);
+ *corba_view_control = CORBA_Object_duplicate (BONOBO_OBJREF (view_control), ev);
+
+ g_signal_connect_object (browser, "page_switched",
+ G_CALLBACK (browser_page_switched_callback), view_control, 0);
+
+ g_signal_connect(tree_widget, "right_click", G_CALLBACK(emc_tree_right_click), mail_component);
+}
+
+
+/* Initialization. */
+
+static void
+mail_component_class_init (MailComponentClass *class)
+{
+ POA_GNOME_Evolution_Component__epv *epv = &class->epv;
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = impl_dispose;
+ object_class->finalize = impl_finalize;
+
+ epv->createControls = impl_createControls;
+}
+
+static void
+mail_component_init (MailComponent *component)
+{
+ MailComponentPrivate *priv;
+ EAccountList *accounts;
+
+ priv = g_new0 (MailComponentPrivate, 1);
+ component->priv = priv;
+
+ /* EPFIXME: Move to a private directory. */
+ /* EPFIXME: Create the directory. */
+ priv->base_directory = g_build_filename (g_get_home_dir (), "evolution", NULL);
+
+ /* EPFIXME: Turn into an object? */
+ mail_session_init (priv->base_directory);
+
+ priv->async_event = mail_async_event_new();
+ priv->storages_hash = g_hash_table_new (NULL, NULL);
+
+ priv->folder_type_registry = e_folder_type_registry_new ();
+ priv->storage_set = e_storage_set_new (priv->folder_type_registry);
+
+#if 0 /* EPFIXME TODO somehow */
+ for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++)
+ *standard_folders[i].uri = g_strdup_printf ("file://%s/local/%s", evolution_dir, standard_folders[i].name);
+#endif
+ setup_local_store (component);
+
+ accounts = mail_config_get_accounts ();
+ load_accounts(component, accounts);
+
+#if 0
+ /* EPFIXME? */
+ mail_local_storage_startup (shell_client, evolution_dir);
+ mail_importer_init (shell_client);
+
+ for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++) {
+ mail_msg_wait (mail_get_folder (*standard_folders[i].uri, CAMEL_STORE_FOLDER_CREATE,
+ got_folder, standard_folders[i].folder, mail_thread_new));
+ }
+#endif
+
+ /* mail_autoreceive_setup (); EPFIXME keep it off for testing */
+
+ setup_search_context (component);
+
+#if 0
+ /* EPFIXME this shouldn't be here. */
+ if (mail_config_is_corrupt ()) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
+ _("Some of your mail settings seem corrupt, "
+ "please check that everything is in order."));
+ g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
+ gtk_widget_show (dialog);
+ }
+#endif
+
+#if 0
+ /* EPFIXME if we nuke the summary this is not necessary anymore. */
+
+ /* Everything should be ready now */
+ evolution_folder_info_notify_ready ();
+#endif
+
+ /* EPFIXME not sure about this. */
+ go_online (component);
+}
+
+
+/* Public API. */
+
+MailComponent *
+mail_component_peek (void)
+{
+ static MailComponent *component = NULL;
+
+ if (component == NULL) {
+ component = g_object_new (mail_component_get_type (), NULL);
+
+ /* FIXME: this should all be initialised in a starutp routine, not from the peek function,
+ this covers much of the ::init method's content too */
+ vfolder_load_storage();
+ }
+
+ return component;
+}
+
+
+const char *
+mail_component_peek_base_directory (MailComponent *component)
+{
+ return component->priv->base_directory;
+}
+
+RuleContext *
+mail_component_peek_search_context (MailComponent *component)
+{
+ return component->priv->search_context;
+}
+
+
+void
+mail_component_add_store (MailComponent *component,
+ CamelStore *store,
+ const char *name)
+{
+ CamelException ex;
+
+ camel_exception_init (&ex);
+
+ if (name == NULL) {
+ char *service_name;
+
+ service_name = camel_service_get_name ((CamelService *) store, TRUE);
+ add_storage (component, service_name, (CamelService *) store, &ex);
+ g_free (service_name);
+ } else {
+ add_storage (component, name, (CamelService *) store, &ex);
+ }
+
+ camel_exception_clear (&ex);
+}
+
+
+/**
+ * mail_component_load_storage_by_uri:
+ * @component:
+ * @uri:
+ * @name:
+ *
+ *
+ *
+ * Return value: Pointer to the newly added CamelStore. The caller is supposed
+ * to ref the object if it wants to store it.
+ **/
+CamelStore *
+mail_component_load_storage_by_uri (MailComponent *component,
+ const char *uri,
+ const char *name)
+{
+ CamelException ex;
+ CamelService *store;
+ CamelProvider *prov;
+
+ camel_exception_init (&ex);
+
+ /* Load the service (don't connect!). Check its provider and
+ * see if this belongs in the shell's folder list. If so, add
+ * it.
+ */
+
+ prov = camel_session_get_provider (session, uri, &ex);
+ if (prov == NULL) {
+ /* EPFIXME: real error dialog */
+ g_warning ("couldn't get service %s: %s\n", uri,
+ camel_exception_get_description (&ex));
+ camel_exception_clear (&ex);
+ return NULL;
+ }
+
+ if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) ||
+ (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL))
+ return NULL;
+
+ store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex);
+ if (store == NULL) {
+ /* EPFIXME: real error dialog */
+ g_warning ("couldn't get service %s: %s\n", uri,
+ camel_exception_get_description (&ex));
+ camel_exception_clear (&ex);
+ return NULL;
+ }
+
+ if (name != NULL) {
+ add_storage (component, name, store, &ex);
+ } else {
+ char *service_name;
+
+ service_name = camel_service_get_name (store, TRUE);
+ add_storage (component, service_name, store, &ex);
+ g_free (service_name);
+ }
+
+ if (camel_exception_is_set (&ex)) {
+ /* EPFIXME: real error dialog */
+ g_warning ("Cannot load storage: %s",
+ camel_exception_get_description (&ex));
+ camel_exception_clear (&ex);
+ }
+
+ camel_object_unref (CAMEL_OBJECT (store));
+ return CAMEL_STORE (store); /* (Still has one ref in the hash.) */
+}
+
+
+static void
+store_disconnect (CamelStore *store,
+ void *event_data,
+ void *data)
+{
+ camel_service_disconnect (CAMEL_SERVICE (store), TRUE, NULL);
+ camel_object_unref (CAMEL_OBJECT (store));
+}
+
+void
+mail_component_remove_storage (MailComponent *component,
+ CamelStore *store)
+{
+ MailComponentPrivate *priv = component->priv;
+ EStorage *storage;
+
+ /* Because the storages_hash holds a reference to each store
+ * used as a key in it, none of them will ever be gc'ed, meaning
+ * any call to camel_session_get_{service,store} with the same
+ * URL will always return the same object. So this works.
+ */
+
+ storage = g_hash_table_lookup (priv->storages_hash, store);
+ if (!storage)
+ return;
+
+ g_hash_table_remove (priv->storages_hash, store);
+
+ /* so i guess potentially we could have a race, add a store while one
+ being removed. ?? */
+ mail_note_store_remove (store);
+
+ e_storage_set_remove_storage (priv->storage_set, storage);
+
+ mail_async_event_emit(priv->async_event, MAIL_ASYNC_THREAD, (MailAsyncFunc) store_disconnect, store, NULL, NULL);
+}
+
+
+void
+mail_component_remove_storage_by_uri (MailComponent *component,
+ const char *uri)
+{
+ CamelProvider *prov;
+ CamelService *store;
+
+ prov = camel_session_get_provider (session, uri, NULL);
+ if (!prov)
+ return;
+ if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) ||
+ (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL))
+ return;
+
+ store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, NULL);
+ if (store != NULL) {
+ mail_component_remove_storage (component, CAMEL_STORE (store));
+ camel_object_unref (CAMEL_OBJECT (store));
+ }
+}
+
+
+EStorage *
+mail_component_lookup_storage (MailComponent *component,
+ CamelStore *store)
+{
+ EStorage *storage;
+
+ /* Because the storages_hash holds a reference to each store
+ * used as a key in it, none of them will ever be gc'ed, meaning
+ * any call to camel_session_get_{service,store} with the same
+ * URL will always return the same object. So this works.
+ */
+
+ storage = g_hash_table_lookup (component->priv->storages_hash, store);
+ if (storage)
+ g_object_ref (storage);
+
+ return storage;
+}
+
+
+int
+mail_component_get_storage_count (MailComponent *component)
+{
+ return g_hash_table_size (component->priv->storages_hash);
+}
+
+
+EStorageSet *
+mail_component_peek_storage_set (MailComponent *component)
+{
+ return component->priv->storage_set;
+}
+
+
+void
+mail_component_storages_foreach (MailComponent *component,
+ GHFunc func,
+ void *data)
+{
+ g_hash_table_foreach (component->priv->storages_hash, func, data);
+}
+
+extern struct _CamelSession *session;
+
+char *em_uri_from_camel(const char *curi)
+{
+ CamelURL *curl;
+ EAccount *account;
+ const char *uid, *path;
+ char *euri;
+ CamelProvider *provider;
+
+ provider = camel_session_get_provider(session, curi, NULL);
+ if (provider == NULL)
+ return g_strdup(curi);
+
+ curl = camel_url_new(curi, NULL);
+ if (curl == NULL)
+ return g_strdup(curi);
+
+ account = mail_config_get_account_by_source_url(curi);
+ uid = (account == NULL)?"local@local":account->uid;
+ path = (provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)?curl->fragment:curl->path;
+ if (path[0] == '/')
+ path++;
+ euri = g_strdup_printf("email://%s/%s", uid, path);
+ printf("em uri from camel '%s' -> '%s'\n", curi, euri);
+
+ return euri;
+}
+
+char *em_uri_to_camel(const char *euri)
+{
+ EAccountList *accounts;
+ const EAccount *account;
+ EAccountService *service;
+ CamelProvider *provider;
+ CamelURL *eurl, *curl;
+ char *uid, *curi;
+
+ eurl = camel_url_new(euri, NULL);
+ if (eurl == NULL)
+ return g_strdup(euri);
+
+ if (strcmp(eurl->protocol, "email") != 0) {
+ camel_url_free(eurl);
+ return g_strdup(euri);
+ }
+
+ g_assert(eurl->user != NULL);
+ g_assert(eurl->host != NULL);
+
+ if (strcmp(eurl->user, "local") == 0 && strcmp(eurl->host, "local") == 0) {
+ /* FIXME: needs to track real local store location */
+ curi = g_strdup_printf("mbox:%s/.evolution/mail/local#%s", g_get_home_dir(), eurl->path);
+ camel_url_free(eurl);
+ return curi;
+ }
+
+ uid = g_strdup_printf("%s@%s", eurl->user, eurl->host);
+
+ accounts = mail_config_get_accounts();
+ account = e_account_list_find(accounts, E_ACCOUNT_FIND_UID, uid);
+ g_free(uid);
+
+ if (account == NULL) {
+ camel_url_free(eurl);
+ return g_strdup(euri);
+ }
+
+ service = account->source;
+ provider = camel_session_get_provider(session, service->url, NULL);
+
+ curl = camel_url_new(service->url, NULL);
+ if (provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
+ camel_url_set_fragment(curl, eurl->path);
+ else
+ camel_url_set_path(curl, eurl->path);
+
+ curi = camel_url_to_string(curl, 0);
+
+ camel_url_free(eurl);
+ camel_url_free(curl);
+
+ printf("em uri to camel '%s' -> '%s'\n", euri, curi);
+
+ return curi;
+}
+
+
+CamelFolder *
+mail_component_get_folder_from_evomail_uri (MailComponent *component,
+ guint32 flags,
+ const char *evomail_uri,
+ CamelException *ex)
+{
+ CamelException local_ex;
+ EAccountList *accounts;
+ EIterator *iter;
+ const char *p;
+ const char *q;
+ const char *folder_name;
+ char *uid;
+
+ camel_exception_init (&local_ex);
+
+ if (strncmp (evomail_uri, "evomail:", 8) != 0)
+ return NULL;
+
+ p = evomail_uri + 8;
+ while (*p == '/')
+ p ++;
+
+ q = strchr (p, '/');
+ if (q == NULL)
+ return NULL;
+
+ uid = g_strndup (p, q - p);
+ folder_name = q + 1;
+
+ /* since we have no explicit account for 'local' folders, make one up */
+ if (strcmp(uid, "local") == 0) {
+ g_free(uid);
+ return camel_store_get_folder(component->priv->local_store, folder_name, flags, ex);
+ }
+
+ accounts = mail_config_get_accounts ();
+ iter = e_list_get_iterator ((EList *) accounts);
+ while (e_iterator_is_valid (iter)) {
+ EAccount *account = (EAccount *) e_iterator_get (iter);
+ EAccountService *service = account->source;
+ CamelProvider *provider;
+ CamelStore *store;
+
+ if (strcmp (account->uid, uid) != 0)
+ continue;
+
+ provider = camel_session_get_provider (session, service->url, &local_ex);
+ if (provider == NULL)
+ goto fail;
+
+ store = (CamelStore *) camel_session_get_service (session, service->url, CAMEL_PROVIDER_STORE, &local_ex);
+ if (store == NULL)
+ goto fail;
+
+ g_free (uid);
+ return camel_store_get_folder (store, folder_name, flags, ex);
+ }
+
+ fail:
+ camel_exception_clear (&local_ex);
+ g_free (uid);
+ return NULL;
+}
+
+
+char *
+mail_component_evomail_uri_from_folder (MailComponent *component,
+ CamelFolder *folder)
+{
+ CamelStore *store = camel_folder_get_parent_store (folder);
+ EAccount *account;
+ char *service_url;
+ char *evomail_uri;
+ const char *uid;
+
+ if (store == NULL)
+ return NULL;
+
+ service_url = camel_service_get_url (CAMEL_SERVICE (store));
+ account = mail_config_get_account_by_source_url (service_url);
+
+ if (account == NULL) {
+ /* since we have no explicit account for 'local' folders, make one up */
+ /* TODO: check the folder is really a local one, folder->parent_store == local_store? */
+ uid = "local";
+ /*g_free (service_url);
+ return NULL;*/
+ } else {
+ uid = account->uid;
+ }
+
+ evomail_uri = g_strconcat ("evomail:///", uid, "/", camel_folder_get_full_name (folder), NULL);
+ g_free (service_url);
+
+ return evomail_uri;
+}
+
+
+BONOBO_TYPE_FUNC_FULL (MailComponent, GNOME_Evolution_Component, PARENT_TYPE, mail_component)
+
+
+/* ********************************************************************** */
+#if 0
+static void
+emc_popup_view(GtkWidget *w, MailComponent *mc)
+{
+
+}
+
+static void
+emc_popup_open_new(GtkWidget *w, MailComponent *mc)
+{
+}
+#endif
+
+static void
+em_copy_folders(CamelStore *tostore, const char *tobase, CamelStore *fromstore, const char *frombase, int delete)
+{
+ GString *toname, *fromname;
+ CamelFolderInfo *fi;
+ GList *pending = NULL;
+ guint32 flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE;
+ CamelException *ex = camel_exception_new();
+ int fromlen;
+ const char *tmp;
+
+ if (camel_store_supports_subscriptions(fromstore))
+ flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
+
+ fi = camel_store_get_folder_info(fromstore, frombase, flags, ex);
+ if (camel_exception_is_set(ex))
+ goto done;
+
+ pending = g_list_append(pending, fi);
+
+ toname = g_string_new("");
+ fromname = g_string_new("");
+
+ tmp = strrchr(frombase, '/');
+ if (tmp == NULL)
+ fromlen = 0;
+ else
+ fromlen = tmp-frombase;
+
+ printf("top name is '%s'\n", fi->full_name);
+
+ while (pending) {
+ CamelFolderInfo *info = pending->data;
+
+ pending = g_list_remove_link(pending, pending);
+ while (info) {
+ CamelFolder *fromfolder, *tofolder;
+ GPtrArray *uids;
+
+ if (info->child)
+ pending = g_list_append(pending, info->child);
+ g_string_printf(toname, "%s/%s", tobase, info->full_name + fromlen);
+
+ printf("Copying from '%s' to '%s'\n", info->full_name, toname->str);
+
+ /* This makes sure we create the same tree, e.g. from a nonselectable source */
+ /* Not sure if this is really the 'right thing', e.g. for spool stores, but it makes the ui work */
+ fromfolder = camel_store_get_folder(fromstore, info->full_name, 0, ex);
+ tofolder = camel_store_get_folder(tostore, toname->str, CAMEL_STORE_FOLDER_CREATE, ex);
+ if (tofolder == NULL) {
+ if (fromfolder)
+ camel_object_unref(fromfolder);
+ goto exception;
+ }
+
+ if (fromfolder) {
+ uids = camel_folder_get_uids(fromfolder);
+ camel_folder_transfer_messages_to(fromfolder, uids, tofolder, NULL, FALSE, ex);
+ camel_folder_free_uids(fromfolder, uids);
+
+ camel_object_unref(fromfolder);
+ }
+ camel_object_unref(tofolder);
+ if (camel_exception_is_set(ex))
+ goto exception;
+ info = info->sibling;
+ }
+ }
+
+ camel_store_free_folder_info(fromstore, fi);
+
+exception:
+ g_string_free(toname, TRUE);
+ g_string_free(fromname, TRUE);
+done:
+ printf("exception: %s\n", ex->desc?ex->desc:"<none>");
+ camel_exception_free(ex);
+}
+
+struct _copy_folder_data {
+ MailComponent *mc;
+ int delete;
+};
+
+static void
+emc_popup_copy_folder_selected(const char *uri, void *data)
+{
+ struct _copy_folder_data *d = data;
+
+ if (uri == NULL) {
+ g_free(d);
+ return;
+ }
+
+ if (uri) {
+ EFolder *folder = e_storage_set_get_folder(d->mc->priv->storage_set, d->mc->priv->context_path);
+ CamelException *ex = camel_exception_new();
+ CamelStore *fromstore, *tostore;
+ char *tobase, *frombase;
+ CamelURL *url;
+
+ printf("copying folder '%s' to '%s'\n", d->mc->priv->context_path, uri);
+
+ fromstore = camel_session_get_store(session, e_folder_get_physical_uri(folder), ex);
+ frombase = strchr(d->mc->priv->context_path+1, '/')+1;
+
+ tostore = camel_session_get_store(session, uri, ex);
+ url = camel_url_new(uri, NULL);
+ if (url->fragment)
+ tobase = url->fragment;
+ else
+ tobase = url->path;
+
+ em_copy_folders(tostore, tobase, fromstore, frombase, d->delete);
+
+ camel_url_free(url);
+ camel_exception_free(ex);
+ }
+ g_free(d);
+}
+
+static void
+emc_popup_copy(GtkWidget *w, MailComponent *mc)
+{
+ struct _copy_folder_data *d;
+
+ d = g_malloc(sizeof(*d));
+ d->mc = mc;
+ d->delete = 0;
+ em_select_folder(NULL, _("Select folder"), _("Select destination to copy folder into"), NULL, emc_popup_copy_folder_selected, d);
+}
+
+static void
+emc_popup_move(GtkWidget *w, MailComponent *mc)
+{
+ struct _copy_folder_data *d;
+
+ d = g_malloc(sizeof(*d));
+ d->mc = mc;
+ d->delete = 1;
+ em_select_folder(NULL, _("Select folder"), _("Select destination to move folder into"), NULL, emc_popup_copy_folder_selected, d);
+}
+static void
+emc_popup_new_folder_create(EStorageSet *ess, EStorageResult result, void *data)
+{
+ printf("folder created %s\n", result == E_STORAGE_OK?"ok":"failed");
+}
+
+static void
+emc_popup_new_folder_response(EMFolderSelector *emfs, guint response, MailComponent *mc)
+{
+ if (response == GTK_RESPONSE_OK) {
+ char *path, *tmp, *name, *full;
+ EStorage *storage;
+ CamelStore *store;
+ CamelException *ex;
+
+ printf("Creating folder: %s (%s)\n", em_folder_selector_get_selected(emfs),
+ em_folder_selector_get_selected_uri(emfs));
+
+ path = g_strdup(em_folder_selector_get_selected(emfs));
+ tmp = strchr(path+1, '/');
+ *tmp++ = 0;
+ /* FIXME: camel_store_create_folder should just take full path names */
+ full = g_strdup(tmp);
+ name = strrchr(tmp, '/');
+ if (name == NULL) {
+ name = tmp;
+ tmp = "";
+ } else
+ *name++ = 0;
+
+ storage = e_storage_set_get_storage(mc->priv->storage_set, path+1);
+ store = g_object_get_data((GObject *)storage, "em-store");
+
+ printf("creating folder '%s' / '%s' on '%s'\n", tmp, name, path+1);
+
+ ex = camel_exception_new();
+ camel_store_create_folder(store, tmp, name, ex);
+ if (camel_exception_is_set(ex)) {
+ printf("Create failed: %s\n", ex->desc);
+ } else if (camel_store_supports_subscriptions(store)) {
+ camel_store_subscribe_folder(store, full, ex);
+ if (camel_exception_is_set(ex)) {
+ printf("Subscribe failed: %s\n", ex->desc);
+ }
+ }
+
+ camel_exception_free(ex);
+
+ g_free(full);
+ g_free(path);
+
+ /* Blah, this should just use camel, we get better error reporting if we do too */
+ /*e_storage_set_async_create_folder(mc->priv->storage_set, path, "mail", "", emc_popup_new_folder_create, mc);*/
+ }
+ gtk_widget_destroy((GtkWidget *)emfs);
+}
+
+static void
+emc_popup_new_folder (GtkWidget *w, MailComponent *mc)
+{
+ GtkWidget *dialog;
+
+ dialog = em_folder_selector_create_new(mc->priv->storage_set, 0, _("Create folder"), _("Specify where to create the folder:"));
+ em_folder_selector_set_selected((EMFolderSelector *)dialog, mc->priv->context_path);
+ g_signal_connect(dialog, "response", G_CALLBACK(emc_popup_new_folder_response), mc);
+ gtk_widget_show(dialog);
+}
+
+static void
+em_delete_rec(CamelStore *store, CamelFolderInfo *fi, CamelException *ex)
+{
+ while (fi) {
+ CamelFolder *folder;
+
+ if (fi->child)
+ em_delete_rec(store, fi->child, ex);
+ if (camel_exception_is_set(ex))
+ return;
+
+ printf("deleting folder '%s'\n", fi->full_name);
+
+ if (camel_store_supports_subscriptions(store))
+ camel_store_unsubscribe_folder(store, fi->full_name, NULL);
+
+ folder = camel_store_get_folder(store, fi->full_name, 0, NULL);
+ if (folder) {
+ GPtrArray *uids = camel_folder_get_uids(folder);
+ int i;
+
+ camel_folder_freeze(folder);
+ for (i = 0; i < uids->len; i++)
+ camel_folder_delete_message(folder, uids->pdata[i]);
+ camel_folder_sync(folder, TRUE, NULL);
+ camel_folder_thaw(folder);
+ camel_folder_free_uids(folder, uids);
+ }
+
+ camel_store_delete_folder(store, fi->full_name, ex);
+ if (camel_exception_is_set(ex))
+ return;
+ fi = fi->sibling;
+ }
+}
+
+static void
+em_delete_folders(CamelStore *store, const char *base, CamelException *ex)
+{
+ CamelFolderInfo *fi;
+ guint32 flags = CAMEL_STORE_FOLDER_INFO_RECURSIVE;
+
+ if (camel_store_supports_subscriptions(store))
+ flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
+
+ fi = camel_store_get_folder_info(store, base, flags, ex);
+ if (camel_exception_is_set(ex))
+ return;
+
+ em_delete_rec(store, fi, ex);
+ camel_store_free_folder_info(store, fi);
+}
+
+static void
+emc_popup_delete_response(GtkWidget *w, guint response, MailComponent *mc)
+{
+ gtk_widget_destroy(w);
+
+ if (response == GTK_RESPONSE_OK) {
+ const char *path = strchr(mc->priv->context_path+1, '/')+1;
+ EFolder *folder = e_storage_set_get_folder(mc->priv->storage_set, mc->priv->context_path);
+ CamelException *ex = camel_exception_new();
+ CamelStore *store;
+
+ /* FIXME: need to hook onto store changed event and delete view as well, somewhere else tho */
+ store = camel_session_get_store(session, e_folder_get_physical_uri(folder), ex);
+ if (camel_exception_is_set(ex))
+ goto exception;
+
+ em_delete_folders(store, path, ex);
+ if (!camel_exception_is_set(ex))
+ goto noexception;
+ exception:
+ e_notice(NULL, GTK_MESSAGE_ERROR,
+ _("Could not delete folder: %s"), ex->desc);
+ noexception:
+ camel_exception_free(ex);
+ if (store)
+ camel_object_unref(store);
+ }
+}
+
+static void
+emc_popup_delete_folder(GtkWidget *w, MailComponent *mc)
+{
+ GtkWidget *dialog;
+ char *title;
+ const char *path = strchr(mc->priv->context_path+1, '/')+1;
+ EFolder *folder;
+
+ folder = e_storage_set_get_folder(mc->priv->storage_set, mc->priv->context_path);
+ if (folder == NULL)
+ return;
+
+ dialog = gtk_message_dialog_new(NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("Really delete folder \"%s\" and all of its subfolders?"), path);
+
+ gtk_dialog_add_button((GtkDialog *)dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+ gtk_dialog_add_button((GtkDialog *)dialog, GTK_STOCK_DELETE, GTK_RESPONSE_OK);
+
+ gtk_dialog_set_default_response((GtkDialog *)dialog, GTK_RESPONSE_OK);
+ gtk_container_set_border_width((GtkContainer *)dialog, 6);
+ gtk_box_set_spacing((GtkBox *)((GtkDialog *)dialog)->vbox, 6);
+
+ title = g_strdup_printf(_("Delete \"%s\""), path);
+ gtk_window_set_title((GtkWindow *)dialog, title);
+ g_free(title);
+
+ g_signal_connect(dialog, "response", G_CALLBACK(emc_popup_delete_response), mc);
+ gtk_widget_show(dialog);
+}
+
+static void
+emc_popup_rename_folder(GtkWidget *w, MailComponent *mc)
+{
+ char *prompt, *new;
+ EFolder *folder;
+ const char *old, *why;
+ int done = 0;
+
+ folder = e_storage_set_get_folder(mc->priv->storage_set, mc->priv->context_path);
+ if (folder == NULL)
+ return;
+
+ old = e_folder_get_name(folder);
+ prompt = g_strdup_printf (_("Rename the \"%s\" folder to:"), e_folder_get_name(folder));
+ while (!done) {
+ new = e_request_string(NULL, _("Rename Folder"), prompt, old);
+ if (new == NULL || strcmp(old, new) == 0)
+ done = 1;
+#if 0
+ else if (!e_shell_folder_name_is_valid(new, &why))
+ e_notice(NULL, GTK_MESSAGE_ERROR, _("The specified folder name is not valid: %s"), why);
+#endif
+ else {
+ char *base, *path;
+
+ /* FIXME: we can't use the os independent path crap here, since we want to control the format */
+ base = g_path_get_dirname(mc->priv->context_path);
+ path = g_build_filename(base, new, NULL);
+
+ if (e_storage_set_get_folder(mc->priv->storage_set, path) != NULL) {
+ e_notice(NULL, GTK_MESSAGE_ERROR,
+ _("A folder named \"%s\" already exists. Please use a different name."), new);
+ } else {
+ CamelStore *store;
+ CamelException *ex = camel_exception_new();
+ const char *oldpath, *newpath;
+
+ oldpath = strchr(mc->priv->context_path+1, '/');
+ g_assert(oldpath);
+ newpath = strchr(path+1, '/');
+ g_assert(newpath);
+ oldpath++;
+ newpath++;
+
+ printf("renaming %s to %s\n", oldpath, newpath);
+
+ store = camel_session_get_store(session, e_folder_get_physical_uri(folder), ex);
+ if (camel_exception_is_set(ex))
+ goto exception;
+
+ camel_store_rename_folder(store, oldpath, newpath, ex);
+ if (!camel_exception_is_set(ex))
+ goto noexception;
+
+ exception:
+ e_notice(NULL, GTK_MESSAGE_ERROR,
+ _("Could not rename folder: %s"), ex->desc);
+ noexception:
+ if (store)
+ camel_object_unref(store);
+ camel_exception_free(ex);
+
+ done = 1;
+ }
+ g_free(path);
+ g_free(base);
+ }
+ g_free(new);
+ }
+}
+
+struct _prop_data {
+ void *object;
+ CamelArgV *argv;
+ GtkWidget **widgets;
+};
+
+static void
+emc_popup_properties_response(GtkWidget *dialog, int response, struct _prop_data *prop_data)
+{
+ int i;
+ CamelArgV *argv = prop_data->argv;
+
+ if (response != GTK_RESPONSE_OK) {
+ gtk_widget_destroy(dialog);
+ return;
+ }
+
+ for (i=0;i<argv->argc;i++) {
+ CamelArg *arg = &argv->argv[i];
+
+ switch (arg->tag & CAMEL_ARG_TYPE) {
+ case CAMEL_ARG_BOO:
+ arg->ca_int = gtk_toggle_button_get_active(prop_data->widgets[i]);
+ break;
+ case CAMEL_ARG_STR:
+ g_free(arg->ca_str);
+ arg->ca_str = gtk_entry_get_text(prop_data->widgets[i]);
+ break;
+ default:
+ printf("unknown property type set\n");
+ }
+ }
+
+ camel_object_setv(prop_data->object, NULL, argv);
+ gtk_widget_destroy(dialog);
+}
+
+static void
+emc_popup_properties_free(void *data)
+{
+ struct _prop_data *prop_data = data;
+ int i;
+
+ for (i=0; i<prop_data->argv->argc; i++) {
+ if ((prop_data->argv->argv[i].tag & CAMEL_ARG_TYPE) == CAMEL_ARG_STR)
+ g_free(prop_data->argv->argv[i].ca_str);
+ }
+ camel_object_unref(prop_data->object);
+ g_free(prop_data->argv);
+ g_free(prop_data);
+}
+
+static void
+emc_popup_properties_got_folder(const char *uri, CamelFolder *folder, void *data)
+{
+ MailComponent *mc = data;
+
+ if (folder) {
+ GtkWidget *dialog, *w, *table, *label;
+ GSList *list, *l;
+ char *name, *txt;
+ int row = 1;
+ gint32 count, i;
+ struct _prop_data *prop_data;
+ CamelArgV *argv;
+ CamelArgGetV *arggetv;
+
+ camel_object_get(folder, NULL, CAMEL_FOLDER_PROPERTIES, &list, CAMEL_FOLDER_NAME, &name, NULL);
+
+ dialog = gtk_dialog_new_with_buttons(_("Folder properties"),
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+
+ /* TODO: maybe we want some basic properties here, like message counts/approximate size/etc */
+ w = gtk_frame_new(_("Properties"));
+ gtk_box_pack_start(((GtkDialog *)dialog)->vbox, w, TRUE, TRUE, 6);
+ table = gtk_table_new(g_slist_length(list)+1, 2, FALSE);
+ gtk_container_add((GtkContainer *)w, table);
+ label = gtk_label_new(_("Folder Name"));
+ gtk_misc_set_alignment(label, 1.0, 0.5);
+ gtk_table_attach(table, label, 0, 1, 0, 1, GTK_FILL|GTK_EXPAND, 0, 3, 0);
+ label = gtk_label_new(name);
+ gtk_misc_set_alignment(label, 0.0, 0.5);
+ gtk_table_attach(table, label, 1, 2, 0, 1, GTK_FILL|GTK_EXPAND, 0, 3, 0);
+
+ /* build an arggetv/argv to retrieve/store the results */
+ count = g_slist_length(list);
+ arggetv = g_malloc0(sizeof(*arggetv) + (count - CAMEL_ARGV_MAX) * sizeof(arggetv->argv[0]));
+ arggetv->argc = count;
+ argv = g_malloc0(sizeof(*argv) + (count - CAMEL_ARGV_MAX) * sizeof(argv->argv[0]));
+ argv->argc = count;
+ i = 0;
+ l = list;
+ while (l) {
+ CamelProperty *prop = l->data;
+
+ argv->argv[i].tag = prop->tag;
+ arggetv->argv[i].tag = prop->tag;
+ arggetv->argv[i].ca_ptr = &argv->argv[i].ca_ptr;
+
+ l = l->next;
+ i++;
+ }
+ camel_object_getv(folder, NULL, arggetv);
+ g_free(arggetv);
+
+ prop_data = g_malloc0(sizeof(*prop_data));
+ prop_data->widgets = g_malloc0(sizeof(prop_data->widgets[0]) * count);
+ prop_data->argv = argv;
+
+ /* setup the ui with the values retrieved */
+ l = list;
+ i = 0;
+ while (l) {
+ CamelProperty *prop = l->data;
+
+ switch (prop->tag & CAMEL_ARG_TYPE) {
+ case CAMEL_ARG_BOO:
+ w = gtk_check_button_new_with_label(prop->description);
+ gtk_toggle_button_set_active((GtkToggleButton *)w, argv->argv[i].ca_int != 0);
+ gtk_table_attach(table, w, 0, 2, row, row+1, 0, 0, 3, 3);
+ prop_data->widgets[i] = w;
+ break;
+ case CAMEL_ARG_STR:
+ label = gtk_label_new(prop->description);
+ gtk_misc_set_alignment(label, 1.0, 0.5);
+ gtk_table_attach(table, label, 0, 1, row, row+1, GTK_FILL|GTK_EXPAND, 0, 3, 3);
+
+ w = gtk_entry_new();
+ if (argv->argv[i].ca_str) {
+ gtk_entry_set_text((GtkEntry *)w, txt);
+ camel_object_free(folder, argv->argv[i].tag, argv->argv[i].ca_str);
+ argv->argv[i].ca_str = NULL;
+ }
+ gtk_table_attach(table, w, 1, 2, row, row+1, GTK_FILL, 0, 3, 3);
+ prop_data->widgets[i] = w;
+ break;
+ default:
+ w = gtk_label_new("CamelFolder error: unsupported propery type");
+ gtk_table_attach(table, w, 0, 2, row, row+1, 0, 0, 3, 3);
+ break;
+ }
+
+ row++;
+ l = l->next;
+ }
+
+ prop_data->object = folder;
+ camel_object_ref(folder);
+
+ camel_object_free(folder, CAMEL_FOLDER_PROPERTIES, list);
+ camel_object_free(folder, CAMEL_FOLDER_NAME, name);
+
+ /* we do 'apply on ok' ... since instant apply may apply some very long running tasks */
+
+ g_signal_connect(dialog, "response", G_CALLBACK(emc_popup_properties_response), prop_data);
+ g_object_set_data_full((GObject *)dialog, "e-prop-data", prop_data, emc_popup_properties_free);
+ gtk_widget_show_all(dialog);
+ }
+}
+
+static void
+emc_popup_properties(GtkWidget *w, MailComponent *mc)
+{
+ EFolder *efolder;
+
+ /* TODO: Make sure we only have one dialog open for any given folder */
+
+ efolder = e_storage_set_get_folder(mc->priv->storage_set, mc->priv->context_path);
+ if (efolder == NULL)
+ return;
+
+ mail_get_folder(e_folder_get_physical_uri(efolder), 0, emc_popup_properties_got_folder, mc, mail_thread_new);
+}
+
+static EMPopupItem emc_popup_menu[] = {
+#if 0
+ { EM_POPUP_ITEM, "00.emc.00", N_("_View"), G_CALLBACK(emc_popup_view), NULL, NULL, 0 },
+ { EM_POPUP_ITEM, "00.emc.01", N_("Open in _New Window"), G_CALLBACK(emc_popup_open_new), NULL, NULL, 0 },
+
+ { EM_POPUP_BAR, "10.emc" },
+#endif
+ { EM_POPUP_ITEM, "10.emc.00", N_("_Copy"), G_CALLBACK(emc_popup_copy), NULL, "folder-copy-16.png", 0 },
+ { EM_POPUP_ITEM, "10.emc.01", N_("_Move"), G_CALLBACK(emc_popup_move), NULL, "folder-move-16.png", 0 },
+
+ { EM_POPUP_BAR, "20.emc" },
+ { EM_POPUP_ITEM, "20.emc.00", N_("_New Folder..."), G_CALLBACK(emc_popup_new_folder), NULL, "folder-mini.png", 0 },
+ { EM_POPUP_ITEM, "20.emc.01", N_("_Delete"), G_CALLBACK(emc_popup_delete_folder), NULL, "evolution-trash-mini.png", 0 },
+ { EM_POPUP_ITEM, "20.emc.01", N_("_Rename"), G_CALLBACK(emc_popup_rename_folder), NULL, NULL, 0 },
+
+ { EM_POPUP_BAR, "80.emc" },
+ { EM_POPUP_ITEM, "80.emc.00", N_("_Properties..."), G_CALLBACK(emc_popup_properties), NULL, "configure_16_folder.xpm", 0 },
+};
+
+
+static int
+emc_tree_right_click(ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, MailComponent *component)
+{
+ char *name;
+ ETreeModel *model = e_tree_get_model(tree);
+ EMPopup *emp;
+ int i;
+ GSList *menus = NULL;
+ struct _GtkMenu *menu;
+
+ name = e_tree_memory_node_get_data((ETreeMemory *)model, path);
+ g_free(component->priv->context_path);
+ component->priv->context_path = g_strdup(name);
+ printf("right click, path = '%s'\n", name);
+
+ emp = em_popup_new("com.ximian.mail.storageset.popup.select");
+
+ for (i=0;i<sizeof(emc_popup_menu)/sizeof(emc_popup_menu[0]);i++) {
+ EMPopupItem *item = &emc_popup_menu[i];
+
+ item->activate_data = component;
+ menus = g_slist_prepend(menus, item);
+ }
+
+ em_popup_add_items(emp, menus, (GDestroyNotify)g_slist_free);
+
+ menu = em_popup_create_menu_once(emp, NULL, 0, 0);
+
+ 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);
+ }
+
+ return TRUE;
+}
diff --git a/mail/mail-component.h b/mail/mail-component.h
new file mode 100644
index 0000000000..3e832752df
--- /dev/null
+++ b/mail/mail-component.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* mail-component.h
+ *
+ * Copyright (C) 2003 Ximian Inc.
+ *
+ * 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.
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ * Ettore Perazzoli <ettore@ximian.com>
+ */
+
+#ifndef _MAIL_COMPONENT_H_
+#define _MAIL_COMPONENT_H_
+
+#include <bonobo/bonobo-object.h>
+
+#include <camel/camel-store.h>
+
+#include "e-storage-set.h"
+#include "Evolution.h"
+
+#include "filter/rule-context.h"
+
+
+#define MAIL_TYPE_COMPONENT (mail_component_get_type ())
+#define MAIL_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAIL_TYPE_COMPONENT, MailComponent))
+#define MAIL_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAIL_TYPE_COMPONENT, MailComponentClass))
+#define MAIL_IS_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAIL_TYPE_COMPONENT))
+#define MAIL_IS_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MAIL_TYPE_COMPONENT))
+
+
+typedef struct _MailComponent MailComponent;
+typedef struct _MailComponentPrivate MailComponentPrivate;
+typedef struct _MailComponentClass MailComponentClass;
+
+struct _MailComponent {
+ BonoboObject parent;
+
+ MailComponentPrivate *priv;
+};
+
+struct _MailComponentClass {
+ BonoboObjectClass parent_class;
+
+ POA_GNOME_Evolution_Component__epv epv;
+};
+
+
+GType mail_component_get_type (void);
+
+MailComponent *mail_component_peek (void);
+
+const char *mail_component_peek_base_directory (MailComponent *component);
+RuleContext *mail_component_peek_search_context (MailComponent *component);
+
+void mail_component_add_store (MailComponent *component,
+ CamelStore *store,
+ const char *name);
+CamelStore *mail_component_load_storage_by_uri (MailComponent *component,
+ const char *uri,
+ const char *name);
+void mail_component_remove_storage (MailComponent *component,
+ CamelStore *store);
+void mail_component_remove_storage_by_uri (MailComponent *component,
+ const char *uri);
+EStorage *mail_component_lookup_storage (MailComponent *component,
+ CamelStore *store);
+
+int mail_component_get_storage_count (MailComponent *component);
+EStorageSet *mail_component_peek_storage_set (MailComponent *component);
+void mail_component_storages_foreach (MailComponent *component,
+ GHFunc func,
+ void *data);
+
+char *em_uri_from_camel (const char *curi);
+char *em_uri_to_camel (const char *euri);
+
+CamelFolder *mail_component_get_folder_from_evomail_uri (MailComponent *component,
+ guint32 flags,
+ const char *evomail_uri,
+ CamelException *ex);
+char *mail_component_evomail_uri_from_folder (MailComponent *component,
+ CamelFolder *folder);
+
+#endif /* _MAIL_COMPONENT_H_ */
diff --git a/mail/mail-config-factory.c b/mail/mail-config-factory.c
index 5a6db2cb47..50fddb9be9 100644
--- a/mail/mail-config-factory.c
+++ b/mail/mail-config-factory.c
@@ -34,8 +34,6 @@
#define CONFIG_CONTROL_FACTORY_ID "OAFIID:GNOME_Evolution_Mail_ConfigControlFactory"
-static BonoboGenericFactory *factory = NULL;
-
typedef void (*ApplyFunc) (GtkWidget *prefs);
diff --git a/mail/mail-config.c b/mail/mail-config.c
index a357f74498..f75babcf36 100644
--- a/mail/mail-config.c
+++ b/mail/mail-config.c
@@ -53,7 +53,9 @@
#include <gal/widgets/e-gui-utils.h>
#include <e-util/e-url.h>
#include <e-util/e-passwords.h>
+
#include "mail.h"
+#include "mail-component.h"
#include "mail-config.h"
#include "mail-mt.h"
#include "mail-tools.h"
@@ -448,6 +450,8 @@ config_write_style (void)
* may not have been set yet
*
* filename = g_build_filename (evolution_dir, MAIL_CONFIG_RC, NULL);
+ *
+ * EPFIXME this kludge needs to go away.
*/
filename = g_build_filename (g_get_home_dir (), "evolution", MAIL_CONFIG_RC, NULL);
@@ -528,6 +532,7 @@ mail_config_init (void)
mail_config_clear ();
/*
+ EPFIXME: This kludge needs to go away.
filename = g_build_filename (evolution_dir, MAIL_CONFIG_RC, NULL);
*/
filename = g_build_filename (g_get_home_dir (), "evolution", MAIL_CONFIG_RC, NULL);
@@ -933,6 +938,7 @@ mail_config_get_default_transport (void)
static char *
uri_to_evname (const char *uri, const char *prefix)
{
+ const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
char *safe;
char *tmp;
@@ -940,9 +946,9 @@ uri_to_evname (const char *uri, const char *prefix)
e_filename_make_safe (safe);
/* blah, easiest thing to do */
if (prefix[0] == '*')
- tmp = g_strdup_printf ("%s/%s%s.xml", evolution_dir, prefix + 1, safe);
+ tmp = g_strdup_printf ("%s/%s%s.xml", base_directory, prefix + 1, safe);
else
- tmp = g_strdup_printf ("%s/%s%s", evolution_dir, prefix, safe);
+ tmp = g_strdup_printf ("%s/%s%s", base_directory, prefix, safe);
g_free (safe);
return tmp;
}
@@ -1057,7 +1063,10 @@ mail_config_folder_to_cachename (CamelFolder *folder, const char *prefix)
char *url, *filename;
url = mail_config_folder_to_safe_url (folder);
- filename = g_strdup_printf ("%s/config/%s%s", evolution_dir, prefix, url);
+ filename = g_strdup_printf ("%s/config/%s%s",
+ mail_component_peek_base_directory (mail_component_peek ()),
+ prefix,
+ url);
g_free (url);
return filename;
@@ -1328,22 +1337,24 @@ mail_config_get_signature_list (void)
static char *
get_new_signature_filename (void)
{
+ const char *base_directory;
char *filename, *id;
struct stat st;
int i;
-
- filename = g_build_filename (evolution_dir, "/signatures", NULL);
+
+ base_directory = mail_component_peek_base_directory (mail_component_peek ());
+ filename = g_build_filename (base_directory, "signatures", NULL);
if (lstat (filename, &st)) {
if (errno == ENOENT) {
if (mkdir (filename, 0700))
- g_warning ("Fatal problem creating %s/signatures directory.", evolution_dir);
+ g_warning ("Fatal problem creating %s directory.", filename);
} else
- g_warning ("Fatal problem with %s/signatures directory.", evolution_dir);
+ g_warning ("Fatal problem with %s directory.", filename);
}
g_free (filename);
- filename = g_malloc (strlen (evolution_dir) + sizeof ("/signatures/signature-") + 12);
- id = g_stpcpy (filename, evolution_dir);
+ filename = g_malloc (strlen (base_directory) + sizeof ("/signatures/signature-") + 12);
+ id = g_stpcpy (filename, base_directory);
id = g_stpcpy (id, "/signatures/signature-");
for (i = 0; i < (INT_MAX - 1); i++) {
@@ -1400,7 +1411,8 @@ delete_unused_signature_file (const char *filename)
char *signatures_dir;
int len;
- signatures_dir = g_strconcat (evolution_dir, "/signatures", NULL);
+ signatures_dir = g_strconcat (mail_component_peek_base_directory (mail_component_peek ()),
+ "/signatures", NULL);
/* remove signature file if it's in evolution dir and no other signature uses it */
len = strlen (signatures_dir);
diff --git a/mail/mail-display-stream.c b/mail/mail-display-stream.c
new file mode 100644
index 0000000000..ae81401680
--- /dev/null
+++ b/mail/mail-display-stream.c
@@ -0,0 +1,104 @@
+/* -*- 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-stream.h b/mail/mail-display-stream.h
new file mode 100644
index 0000000000..943398c49a
--- /dev/null
+++ b/mail/mail-display-stream.h
@@ -0,0 +1,62 @@
+/* -*- 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 MAIL_DISPLAY_STREAM_H
+#define MAIL_DISPLAY_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <camel/camel-stream.h>
+#include <gtkhtml/gtkhtml.h>
+
+#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))
+
+typedef struct _MailDisplayStream {
+ CamelStream parent_stream;
+
+ GtkHTML *html;
+ GtkHTMLStream *html_stream;
+} MailDisplayStream;
+
+typedef struct {
+ CamelStreamClass parent_class;
+
+} MailDisplayStreamClass;
+
+
+CamelType mail_display_stream_get_type (void);
+
+/* Note: stream does not ref these objects! */
+CamelStream *mail_display_stream_new (GtkHTML *html, GtkHTMLStream *html_stream);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* MAIL_DISPLAY_STREAM_H */
diff --git a/mail/mail-display.c b/mail/mail-display.c
new file mode 100644
index 0000000000..6c6d9aaf2a
--- /dev/null
+++ b/mail/mail-display.c
@@ -0,0 +1,2995 @@
+/* -*- 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-component.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) {
+ g_source_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) {
+ const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
+ char *path;
+
+ path = g_alloca (strlen (base_directory) + 16);
+ sprintf (path, "%s/cache", base_directory);
+
+ /* 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)
+ g_source_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)
+ g_source_remove (pop->destroy_timeout);
+
+ if (!pop->hidden)
+ pop->destroy_timeout = g_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 = g_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)
+ g_source_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);
+ 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
new file mode 100644
index 0000000000..fe95c95490
--- /dev/null
+++ b/mail/mail-display.h
@@ -0,0 +1,137 @@
+/* -*- 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-folder-cache.c b/mail/mail-folder-cache.c
index c22d3011b0..3d4eb96d53 100644
--- a/mail/mail-folder-cache.c
+++ b/mail/mail-folder-cache.c
@@ -98,9 +98,7 @@ struct _store_info {
CamelStore *store; /* the store for these folders */
- /* only 1 should be set */
- EvolutionStorage *storage;
- GNOME_Evolution_Storage corba_storage;
+ EStorage *storage;
/* Outstanding folderinfo requests */
EDList folderinfo_updates;
@@ -109,7 +107,7 @@ struct _store_info {
static void folder_changed(CamelObject *o, gpointer event_data, gpointer user_data);
static void folder_renamed(CamelObject *o, gpointer event_data, gpointer user_data);
static void folder_finalised(CamelObject *o, gpointer event_data, gpointer user_data);
-
+static void message_changed (CamelObject *o, gpointer event_data, gpointer user_data);
static guint ping_id = 0;
static gboolean ping_cb (gpointer user_data);
@@ -188,9 +186,7 @@ real_flush_updates(void *o, void *event_data, void *data)
{
struct _folder_update *up;
struct _store_info *si;
- EvolutionStorage *storage;
- GNOME_Evolution_Storage corba_storage;
- CORBA_Environment ev;
+ EStorage *storage;
time_t now;
LOCK(info_lock);
@@ -199,11 +195,9 @@ real_flush_updates(void *o, void *event_data, void *data)
if (si) {
storage = si->storage;
if (storage)
- bonobo_object_ref((BonoboObject *)storage);
- corba_storage = si->corba_storage;
+ g_object_ref (storage);
} else {
storage = NULL;
- corba_storage = CORBA_OBJECT_NIL;
}
UNLOCK(info_lock);
@@ -214,7 +208,7 @@ real_flush_updates(void *o, void *event_data, void *data)
mail_filter_delete_uri(up->store, up->uri);
mail_config_uri_deleted(CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(up->store))->compare_folder_name, up->uri);
if (up->unsub)
- evolution_storage_removed_folder (storage, up->path);
+ e_storage_removed_folder (storage, up->path);
} else
mail_vfolder_add_uri(up->store, up->uri, TRUE);
} else {
@@ -222,7 +216,7 @@ real_flush_updates(void *o, void *event_data, void *data)
if (up->oldpath) {
if (storage != NULL) {
d(printf("Removing old folder (rename?) '%s'\n", up->oldpath));
- evolution_storage_removed_folder(storage, up->oldpath);
+ e_storage_removed_folder(storage, up->oldpath);
}
/* ELSE? Shell supposed to handle the local snot case */
}
@@ -237,24 +231,28 @@ real_flush_updates(void *o, void *event_data, void *data)
}
if (up->name == NULL) {
- if (storage != NULL) {
- d(printf("Updating existing folder: %s (%d unread)\n", up->path, up->unread));
- evolution_storage_update_folder(storage, up->path, up->unread);
- } else if (corba_storage != CORBA_OBJECT_NIL) {
- d(printf("Updating existing (local) folder: %s (%d unread)\n", up->path, up->unread));
- CORBA_exception_init(&ev);
- GNOME_Evolution_Storage_updateFolder(corba_storage, up->path, up->unread, &ev);
- CORBA_exception_free(&ev);
+ EFolder *folder = e_storage_get_folder (storage, up->path);
+
+ if (folder != NULL) {
+ d(printf("updating unread count to '%s' to %d\n", up->path, up->unread));
+ e_folder_set_unread_count (folder, up->unread);
+ } else {
+ g_warning ("No folder at %s ?!", up->path);
}
} else if (storage != NULL) {
char *type = (strncmp(up->uri, "vtrash:", 7)==0)?"vtrash":"mail";
-
+ EFolder *new_folder = e_folder_new (up->name, type, NULL);
+
d(printf("Adding new folder: %s\n", up->path));
- evolution_storage_new_folder(storage,
- up->path, up->name, type, up->uri, up->name, NULL,
- up->unread,
- CAMEL_IS_DISCO_STORE(up->store)
- && camel_disco_store_can_work_offline((CamelDiscoStore *)up->store), 0);
+
+ e_folder_set_physical_uri (new_folder, up->uri);
+ e_folder_set_unread_count (new_folder, up->unread);
+ if (CAMEL_IS_DISCO_STORE(up->store) && camel_disco_store_can_work_offline((CamelDiscoStore *)up->store))
+ e_folder_set_can_sync_offline (new_folder, TRUE);
+ else
+ e_folder_set_can_sync_offline (new_folder, FALSE);
+
+ e_storage_new_folder(storage, up->path, new_folder);
}
if (!up->olduri && up->add)
@@ -279,9 +277,9 @@ real_flush_updates(void *o, void *event_data, void *data)
notify_idle_id = g_idle_add_full (G_PRIORITY_LOW, notify_idle_cb, NULL, NULL);
free_update(up);
-
- if (storage)
- bonobo_object_unref((BonoboObject *)storage);
+
+ if (storage != NULL)
+ g_object_unref (storage);
LOCK(info_lock);
}
@@ -307,7 +305,7 @@ unset_folder_info(struct _folder_info *mfi, int delete, int unsub)
CamelFolder *folder = mfi->folder;
camel_object_unhook_event(folder, "folder_changed", folder_changed, mfi);
- camel_object_unhook_event(folder, "message_changed", folder_changed, mfi);
+ camel_object_unhook_event(folder, "message_changed", message_changed, mfi);
camel_object_unhook_event(folder, "renamed", folder_renamed, mfi);
camel_object_unhook_event(folder, "finalize", folder_finalised, mfi);
}
@@ -907,17 +905,18 @@ store_online_cb (CamelStore *store, void *data)
}
void
-mail_note_store(CamelStore *store, CamelOperation *op, EvolutionStorage *storage, GNOME_Evolution_Storage corba_storage,
+mail_note_store(CamelStore *store, CamelOperation *op, EStorage *storage,
void (*done)(CamelStore *store, CamelFolderInfo *info, void *data), void *data)
{
struct _store_info *si;
struct _update_data *ud;
const char *buf;
guint timeout;
+
+ g_return_if_fail (storage == NULL || E_IS_STORAGE (storage));
g_assert(CAMEL_IS_STORE(store));
g_assert(pthread_self() == mail_gui_thread);
- g_assert(storage == NULL || corba_storage == CORBA_OBJECT_NIL);
LOCK(info_lock);
@@ -942,8 +941,7 @@ mail_note_store(CamelStore *store, CamelOperation *op, EvolutionStorage *storage
CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name);
si->storage = storage;
if (storage != NULL)
- bonobo_object_ref((BonoboObject *)storage);
- si->corba_storage = corba_storage;
+ g_object_ref (storage);
si->store = store;
camel_object_ref((CamelObject *)store);
g_hash_table_insert(stores, store, si);
diff --git a/mail/mail-folder-cache.h b/mail/mail-folder-cache.h
index 3d612d7d6b..b9f8f44dfb 100644
--- a/mail/mail-folder-cache.h
+++ b/mail/mail-folder-cache.h
@@ -25,14 +25,15 @@
#ifndef _MAIL_FOLDER_CACHE_H
#define _MAIL_FOLDER_CACHE_H
-#include <shell/evolution-storage.h>
+#include "e-storage.h"
/* Add a store whose folders should appear in the shell
The folders are scanned from the store, and/or added at
runtime via the folder_created event */
void
-mail_note_store(CamelStore *store, CamelOperation *op, EvolutionStorage *storage, GNOME_Evolution_Storage corba_storage,
- void (*done)(CamelStore *store, CamelFolderInfo *info, void *data), void *data);
+mail_note_store(CamelStore *store, CamelOperation *op, EStorage *storage,
+ void (*done) (CamelStore *store, CamelFolderInfo *info, void *data),
+ void *data);
/* de-note a store */
void mail_note_store_remove(CamelStore *store);
@@ -41,7 +42,7 @@ void mail_note_store_remove(CamelStore *store);
The folder must have already been created on the store (which has already been noted)
before the folder can be opened
*/
-void mail_note_folder(struct _CamelFolder *folder);
+void mail_note_folder(CamelFolder *folder);
/* Returns true if a folder is available (yet), and also sets *folderp (if supplied)
to a (referenced) copy of the folder if it has already been opened */
diff --git a/mail/mail-font-prefs.c b/mail/mail-font-prefs.c
new file mode 100644
index 0000000000..0d26f1ccbd
--- /dev/null
+++ b/mail/mail-font-prefs.c
@@ -0,0 +1,130 @@
+/* -*- 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
new file mode 100644
index 0000000000..14a1d20b40
--- /dev/null
+++ b/mail/mail-font-prefs.h
@@ -0,0 +1,66 @@
+/* -*- 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
new file mode 100644
index 0000000000..92428af592
--- /dev/null
+++ b/mail/mail-format.c
@@ -0,0 +1,2131 @@
+/* -*- 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
new file mode 100644
index 0000000000..e39148ca83
--- /dev/null
+++ b/mail/mail-format.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 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
new file mode 100644
index 0000000000..2e3f517145
--- /dev/null
+++ b/mail/mail-identify.c
@@ -0,0 +1,130 @@
+/* -*- 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-importer.c b/mail/mail-importer.c
index 193726aa1e..a915c04668 100644
--- a/mail/mail-importer.c
+++ b/mail/mail-importer.c
@@ -36,12 +36,12 @@
#include <camel/camel-exception.h>
#include <e-util/e-path.h>
+#include "mail-component.h"
#include "mail-importer.h"
#include "mail-local.h"
#include "mail.h"
static GList *importer_modules = NULL;
-extern char *evolution_dir;
static GNOME_Evolution_Storage local_storage = NULL;
void mail_importer_uninit (void);
@@ -95,7 +95,8 @@ mail_importer_make_local_folder(const char *folderpath)
} else {
struct _create_data data = { GNOME_Evolution_Storage_GENERIC_ERROR, FALSE };
- tmp = g_strdup_printf("file://%s/local", evolution_dir);
+ tmp = g_strdup_printf("file://%s/local",
+ mail_component_peek_base_directory (mail_component_peek ()));
uri = e_path_to_physical(tmp, folderpath);
g_free(tmp);
tmp = strrchr(uri, '/');
diff --git a/mail/mail-local.c b/mail/mail-local.c
index 0ec4f5a8e9..565e028432 100644
--- a/mail/mail-local.c
+++ b/mail/mail-local.c
@@ -533,10 +533,11 @@ mlf_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args)
/* CamelObject args */
case CAMEL_OBJECT_ARG_DESCRIPTION:
if (mlf->description == NULL) {
+ const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
int pathlen;
/* string to describe a local folder as the location of a message */
- pathlen = strlen(evolution_dir) + strlen("local") + 1;
+ pathlen = strlen(base_directory) + strlen("local") + 1;
if (strlen(folder->full_name) > pathlen)
mlf->description = g_strdup_printf(_("Local folders/%s"), folder->full_name+pathlen);
else
diff --git a/mail/mail-mt.c b/mail/mail-mt.c
index 603e55dd50..cb75fe93bf 100644
--- a/mail/mail-mt.c
+++ b/mail/mail-mt.c
@@ -28,8 +28,6 @@
#include "mail-session.h"
#include "mail-mt.h"
-#include "component-factory.h"
-
/*#define MALLOC_CHECK*/
#define LOG_OPS
#define LOG_LOCKS
@@ -911,10 +909,11 @@ static void do_op_status(struct _mail_msg *mm)
what = msg->ops->describe_msg (msg, FALSE);
else
what = _("Working");
-
+
+ /* EPFIXME: redo activity client stuff. */
if (global_shell_client) {
activity = evolution_activity_client_new (global_shell_client,
- COMPONENT_ID,
+ "evolution-mail",
progress_icon, what, TRUE,
&display);
} else {
diff --git a/mail/mail-offline-handler.c b/mail/mail-offline-handler.c
index ccaff703f5..ce0bcf729f 100644
--- a/mail/mail-offline-handler.c
+++ b/mail/mail-offline-handler.c
@@ -27,9 +27,10 @@
#endif
#include "mail-offline-handler.h"
-#include "mail.h"
+#include "mail-component.h"
#include "mail-ops.h"
#include "mail-folder-cache.h"
+#include "mail.h"
#include <gtk/gtkmain.h>
@@ -77,10 +78,10 @@ create_connection_list (void)
list = GNOME_Evolution_ConnectionList__alloc ();
list->_length = 0;
- list->_maximum = mail_storages_count ();
+ list->_maximum = mail_component_get_storage_count (mail_component_peek ());
list->_buffer = CORBA_sequence_GNOME_Evolution_Connection_allocbuf (list->_maximum);
- mail_storages_foreach (add_connection, list);
+ mail_component_storages_foreach (mail_component_peek (), add_connection, list);
return list;
}
@@ -260,7 +261,7 @@ impl_goOffline (PortableServer_Servant servant,
/* FIXME: If send/receive active, wait for it to finish */
- mail_storages_foreach (storage_go_offline, progress_listener);
+ mail_component_storages_foreach (mail_component_peek (), storage_go_offline, progress_listener);
}
static void
@@ -270,8 +271,7 @@ storage_go_online (gpointer key, gpointer value, gpointer data)
if (service_is_relevant (CAMEL_SERVICE (store), FALSE)) {
mail_store_set_offline (store, FALSE, NULL, NULL);
- mail_note_store (store, NULL, NULL, CORBA_OBJECT_NIL,
- NULL, NULL);
+ mail_note_store (store, NULL, NULL, NULL, NULL);
}
}
@@ -288,7 +288,7 @@ impl_goOnline (PortableServer_Servant servant,
/* Enable auto-mail-checking */
camel_session_set_online (session, TRUE);
- mail_storages_foreach (storage_go_online, NULL);
+ mail_component_storages_foreach (mail_component_peek (), storage_go_online, NULL);
}
/* GObject methods. */
diff --git a/mail/mail-ops.c b/mail/mail-ops.c
index 65cc8676c1..8d3711ff4e 100644
--- a/mail/mail-ops.c
+++ b/mail/mail-ops.c
@@ -38,6 +38,7 @@
#include <camel/camel-vtrash-folder.h>
#include <camel/camel-vee-store.h>
#include "mail.h"
+#include "mail-component.h"
#include "mail-tools.h"
#include "mail-ops.h"
#include "mail-vfolder.h"
@@ -229,7 +230,8 @@ uid_cachename_hack (CamelStore *store)
url->host);
e_filename_make_safe (encoded_url);
- filename = g_strdup_printf ("%s/mail/pop3/cache-%s", evolution_dir, encoded_url);
+ filename = g_strdup_printf ("%s/mail/pop3/cache-%s",
+ mail_component_peek_base_directory (mail_component_peek ()), encoded_url);
/* lame hack, but we can't expect user's to actually migrate
their cache files - brain power requirements are too
@@ -238,7 +240,9 @@ uid_cachename_hack (CamelStore *store)
/* This is either the first time the user has checked
mail with this POP provider or else their cache
file is in the old location... */
- old_location = g_strdup_printf ("%s/config/cache-%s", evolution_dir, encoded_url);
+ old_location = g_strdup_printf ("%s/config/cache-%s",
+ mail_component_peek_base_directory (mail_component_peek ()),
+ encoded_url);
if (stat (old_location, &st) == -1) {
/* old location doesn't exist either so use the new location */
g_free (old_location);
diff --git a/mail/mail-search.c b/mail/mail-search.c
new file mode 100644
index 0000000000..424ccee991
--- /dev/null
+++ b/mail/mail-search.c
@@ -0,0 +1,403 @@
+/* -*- 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.h b/mail/mail-search.h
new file mode 100644
index 0000000000..cbcf563636
--- /dev/null
+++ b/mail/mail-search.h
@@ -0,0 +1,75 @@
+/* -*- 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 0ba9ae3179..6d70fe5047 100644
--- a/mail/mail-send-recv.c
+++ b/mail/mail-send-recv.c
@@ -46,6 +46,7 @@
#include "mail.h"
#include "mail-mt.h"
+#include "mail-component.h"
#include "mail-config.h"
#include "mail-session.h"
#include "mail-tools.h"
@@ -167,7 +168,7 @@ static void free_send_info(void *key, struct _send_info *info, void *data)
g_free(info->uri);
camel_operation_unref(info->cancel);
if (info->timeout_id != 0)
- gtk_timeout_remove(info->timeout_id);
+ g_source_remove(info->timeout_id);
g_free(info->what);
g_free(info);
}
@@ -207,7 +208,7 @@ static void hide_send_info(void *key, struct _send_info *info, void *data)
info->status = NULL;
if (info->timeout_id != 0) {
- gtk_timeout_remove (info->timeout_id);
+ g_source_remove (info->timeout_id);
info->timeout_id = 0;
}
}
@@ -366,7 +367,7 @@ build_dialogue (EAccountList *accounts, CamelFolder *outbox, const char *destina
info->keep = source->keep_on_server;
info->cancel = camel_operation_new (operation_status, info);
info->state = SEND_ACTIVE;
- info->timeout_id = gtk_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
+ info->timeout_id = g_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
g_hash_table_insert (data->active, info->uri, info);
list = g_list_prepend (list, info);
@@ -375,7 +376,7 @@ build_dialogue (EAccountList *accounts, CamelFolder *outbox, const char *destina
e_iterator_next (iter);
continue;
} else if (info->timeout_id == 0)
- info->timeout_id = gtk_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
+ info->timeout_id = g_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
recv_icon = gtk_image_new_from_file (EVOLUTION_BUTTONSDIR "/receive-24.png");
@@ -423,12 +424,12 @@ build_dialogue (EAccountList *accounts, CamelFolder *outbox, const char *destina
info->keep = FALSE;
info->cancel = camel_operation_new (operation_status, info);
info->state = SEND_ACTIVE;
- info->timeout_id = gtk_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
+ info->timeout_id = g_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
g_hash_table_insert (data->active, SEND_URI_KEY, info);
list = g_list_prepend (list, info);
} else if (info->timeout_id == 0)
- info->timeout_id = gtk_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
+ info->timeout_id = g_timeout_add (STATUS_TIMEOUT, operation_status_timeout, info);
send_icon = gtk_image_new_from_file (EVOLUTION_BUTTONSDIR "/send-24.png");
@@ -663,10 +664,10 @@ receive_update_got_store (char *uri, CamelStore *store, void *data)
struct _send_info *info = data;
if (store) {
- EvolutionStorage *storage = mail_lookup_storage (store);
+ EStorage *storage = mail_component_lookup_storage (mail_component_peek (), store);
if (storage) {
- mail_note_store(store, info->cancel, storage, CORBA_OBJECT_NIL, receive_update_done, info);
+ mail_note_store(store, info->cancel, storage, receive_update_done, info);
/*bonobo_object_unref (BONOBO_OBJECT (storage));*/
} else {
/* If we get here, store must be an external
@@ -770,7 +771,7 @@ static void auto_clean_set(void *key, struct _auto_data *info, GHashTable *set)
{
d(printf("removing auto-check for %s %p\n", info->uri, info));
g_hash_table_remove(set, info->uri);
- gtk_timeout_remove(info->timeout_id);
+ g_source_remove(info->timeout_id);
g_free(info->uri);
g_free(info);
}
@@ -811,15 +812,15 @@ mail_autoreceive_setup (void)
info->keep = source->keep_on_server;
if (info->period != source->auto_check_time*60) {
info->period = source->auto_check_time*60;
- gtk_timeout_remove(info->timeout_id);
- info->timeout_id = gtk_timeout_add(info->period*1000, auto_timeout, info);
+ g_source_remove(info->timeout_id);
+ info->timeout_id = g_timeout_add(info->period*1000, auto_timeout, info);
}
} else {
info = g_malloc0(sizeof(*info));
info->uri = g_strdup(source->url);
info->keep = source->keep_on_server;
info->period = source->auto_check_time*60;
- info->timeout_id = gtk_timeout_add(info->period*1000, auto_timeout, info);
+ info->timeout_id = g_timeout_add(info->period*1000, auto_timeout, info);
g_hash_table_insert(auto_active, info->uri, info);
/* If we do this at startup, it can cause the logon dialogue to be hidden,
so lets not */
diff --git a/mail/mail-session.c b/mail/mail-session.c
index 65b537246b..1c41007c53 100644
--- a/mail/mail-session.c
+++ b/mail/mail-session.c
@@ -40,6 +40,7 @@
#include "filter/filter-context.h"
#include "filter/filter-filter.h"
#include "mail.h"
+#include "mail-component.h"
#include "mail-config.h"
#include "mail-session.h"
#include "mail-tools.h"
@@ -593,7 +594,7 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException
gconf = mail_config_get_gconf_client ();
- user = g_strdup_printf ("%s/filters.xml", evolution_dir);
+ user = g_strdup_printf ("%s/filters.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/filtertypes.xml";
fc = (RuleContext *) filter_context_new ();
rule_context_load (fc, system, user);
@@ -743,16 +744,16 @@ mail_session_forget_password (const char *key)
}
void
-mail_session_init (void)
+mail_session_init (const char *base_directory)
{
char *camel_dir;
-
- if (camel_init (evolution_dir, TRUE) != 0)
+
+ if (camel_init (base_directory, TRUE) != 0)
exit (0);
session = CAMEL_SESSION (camel_object_new (MAIL_SESSION_TYPE));
- camel_dir = g_strdup_printf ("%s/mail", evolution_dir);
+ camel_dir = g_strdup_printf ("%s/mail", base_directory);
camel_session_construct (session, camel_dir);
/* The shell will tell us to go online. */
diff --git a/mail/mail-session.h b/mail/mail-session.h
index 72a2757fc4..2a7559964c 100644
--- a/mail/mail-session.h
+++ b/mail/mail-session.h
@@ -32,7 +32,7 @@ extern "C" {
#pragma }
#endif /* __cplusplus */
-void mail_session_init (void);
+void mail_session_init (const char *base_directory);
gboolean mail_session_get_interactive (void);
void mail_session_set_interactive (gboolean interactive);
char *mail_session_request_dialog (const char *prompt, gboolean secret,
diff --git a/mail/mail-signature-editor.c b/mail/mail-signature-editor.c
index 0c1907b3bb..570a0157c4 100644
--- a/mail/mail-signature-editor.c
+++ b/mail/mail-signature-editor.c
@@ -395,11 +395,11 @@ mail_signature_editor (MailConfigSignature *sig, GtkWindow *parent, gboolean is_
EVOLUTION_UIDIR "/evolution-signature-editor.xml",
"evolution-signature-editor", NULL);
- editor->control = bonobo_widget_new_control ("OAFIID:GNOME_GtkHTML_Editor:3.0",
+ editor->control = bonobo_widget_new_control ("OAFIID:GNOME_GtkHTML_Editor:3.1",
bonobo_ui_component_get_container (component));
if (editor->control == NULL) {
- g_warning ("Cannot get 'OAFIID:GNOME_GtkHTML_Editor:3.0'.");
+ g_warning ("Cannot get 'OAFIID:GNOME_GtkHTML_Editor:3.1'.");
destroy_editor (editor);
return;
diff --git a/mail/mail-summary.c b/mail/mail-summary.c
index 978347bb43..903c03ea4e 100644
--- a/mail/mail-summary.c
+++ b/mail/mail-summary.c
@@ -72,7 +72,6 @@ typedef struct {
static int queue_len = 0;
-extern char *evolution_dir;
extern EvolutionStorage *vfolder_storage;
#define MAIN_READER main_compipe[0]
@@ -295,7 +294,7 @@ generate_folder_summaries (MailSummary *summary)
CamelException *ex;
int i;
- user = g_strdup_printf ("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf ("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
system = EVOLUTION_PRIVDATADIR "/vfoldertypes.xml";
context = vfolder_context_new ();
diff --git a/mail/mail-tools.c b/mail/mail-tools.c
index 99fd749d78..e5fbeed05f 100644
--- a/mail/mail-tools.c
+++ b/mail/mail-tools.c
@@ -46,6 +46,7 @@
#include "e-util/e-meta.h"
#include "mail.h" /*session*/
+#include "mail-component.h"
#include "mail-config.h"
#include "mail-vfolder.h"
#include "mail-tools.h"
@@ -62,7 +63,8 @@ mail_tool_get_local_inbox (CamelException *ex)
CamelFolder *folder;
char *url;
- url = g_strdup_printf("file://%s/local/Inbox", evolution_dir);
+ url = g_strdup_printf("file://%s/local/Inbox",
+ mail_component_peek_base_directory (mail_component_peek ()));
folder = mail_tool_uri_to_folder (url, 0, ex);
g_free (url);
@@ -120,7 +122,9 @@ mail_tool_get_local_movemail_path (const unsigned char *uri)
if (strchr ("/:;=|%&#!*^()\\, ", *c) || !isprint ((int) *c))
*c = '_';
- path = g_strdup_printf ("%s/local/Inbox/movemail.%s", evolution_dir, safe_uri);
+ path = g_strdup_printf ("%s/local/Inbox/movemail.%s",
+ mail_component_peek_base_directory (mail_component_peek ()),
+ safe_uri);
g_free (safe_uri);
return path;
@@ -318,7 +322,7 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex)
store = camel_session_get_store (session, uri + offset, ex);
if (store) {
const char *name;
-
+
/* if we have a fragment, then the path is actually used by the store,
so the fragment is the path to the folder instead */
if (url->fragment) {
@@ -412,6 +416,7 @@ mail_tools_folder_to_url (CamelFolder *folder)
static char *meta_data_key(const char *uri, char **pathp)
{
+ const char *base_directory = mail_component_peek_base_directory (mail_component_peek ());
CamelURL *url;
GString *path;
const char *key;
@@ -421,12 +426,12 @@ static char *meta_data_key(const char *uri, char **pathp)
if (url == NULL) {
g_warning("Trying to retrieve meta-data for unparsable uri: %s", uri);
- *pathp = g_build_path(evolution_dir, "meta/unknown", NULL);
+ *pathp = g_build_path(base_directory, "meta/unknown", NULL);
return g_strdup("folder");
}
- path = g_string_new(evolution_dir);
+ path = g_string_new(base_directory);
g_string_append_printf(path, "/meta/%s/", url->protocol);
if (url->host && url->host[0]) {
diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c
index 001a0ae57e..b27aea30f7 100644
--- a/mail/mail-vfolder.c
+++ b/mail/mail-vfolder.c
@@ -32,6 +32,8 @@
#include "evolution-storage.h"
#include "evolution-shell-component.h"
+#include "folder-browser.h"
+#include "mail-component.h"
#include "mail-vfolder.h"
#include "mail-tools.h"
#include "mail-autofilter.h"
@@ -64,7 +66,6 @@ static GHashTable *vfolder_hash;
extern EvolutionShellClient *global_shell_client;
/* more globals ... */
-extern char *evolution_dir;
extern CamelSession *session;
static void rule_changed(FilterRule *rule, CamelFolder *folder);
@@ -471,7 +472,8 @@ mail_vfolder_delete_uri(CamelStore *store, const char *uri)
g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
gtk_widget_show (dialog);
- user = g_strdup_printf ("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf ("%s/vfolders.xml",
+ mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save ((RuleContext *) context, user);
g_free (user);
}
@@ -526,7 +528,7 @@ mail_vfolder_rename_uri(CamelStore *store, const char *from, const char *to)
char *user;
d(printf("Vfolders updated from renamed folder\n"));
- user = g_strdup_printf("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save((RuleContext *)context, user);
g_free(user);
}
@@ -646,7 +648,10 @@ static void context_rule_removed(RuleContext *ctx, FilterRule *rule)
/* TODO: remove from folder info cache? */
path = g_strdup_printf("/%s", rule->name);
- evolution_storage_removed_folder(mail_lookup_storage(vfolder_store), path);
+
+ /* EPFIXME This leaks, the original code was broken too. */
+ e_storage_removed_folder (mail_component_lookup_storage (mail_component_peek (), vfolder_store), path);
+
g_free(path);
LOCK();
@@ -697,7 +702,7 @@ store_folder_deleted(CamelObject *o, void *event_data, void *data)
g_object_unref(rule);
g_signal_connect(context, "rule_removed", G_CALLBACK(context_rule_removed), context);
- user = g_strdup_printf("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save((RuleContext *)context, user);
g_free(user);
} else {
@@ -738,7 +743,7 @@ store_folder_renamed(CamelObject *o, void *event_data, void *data)
filter_rule_set_name(rule, info->new->full_name);
g_signal_connect(rule, "changed", G_CALLBACK(rule_changed), folder);
- user = g_strdup_printf("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save((RuleContext *)context, user);
g_free(user);
@@ -750,7 +755,7 @@ store_folder_renamed(CamelObject *o, void *event_data, void *data)
}
void
-vfolder_load_storage(GNOME_Evolution_Shell shell)
+vfolder_load_storage(void)
{
char *user, *storeuri;
FilterRule *rule;
@@ -758,7 +763,7 @@ vfolder_load_storage(GNOME_Evolution_Shell shell)
vfolder_hash = g_hash_table_new(g_str_hash, g_str_equal);
/* first, create the vfolder store, and set it up */
- storeuri = g_strdup_printf("vfolder:%s/vfolder", evolution_dir);
+ storeuri = g_strdup_printf("vfolder:%s/vfolder", mail_component_peek_base_directory (mail_component_peek ()));
vfolder_store = camel_session_get_store(session, storeuri, NULL);
if (vfolder_store == NULL) {
g_warning("Cannot open vfolder store - no vfolders available");
@@ -773,10 +778,10 @@ vfolder_load_storage(GNOME_Evolution_Shell shell)
(CamelObjectEventHookFunc)store_folder_renamed, NULL);
d(printf("got store '%s' = %p\n", storeuri, vfolder_store));
- mail_load_storage_by_uri(shell, storeuri, _("VFolders"));
+ mail_component_load_storage_by_uri(mail_component_peek (), storeuri, _("VFolders"));
/* load our rules */
- user = g_strdup_printf ("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf ("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
context = vfolder_context_new ();
if (rule_context_load ((RuleContext *)context,
EVOLUTION_PRIVDATADIR "/vfoldertypes.xml", user) != 0) {
@@ -806,8 +811,7 @@ vfolder_editor_response (GtkWidget *dialog, int button, void *data)
{
char *user;
- user = alloca(strlen(evolution_dir)+16);
- sprintf(user, "%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf ("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
switch(button) {
case GTK_RESPONSE_ACCEPT:
@@ -820,6 +824,8 @@ vfolder_editor_response (GtkWidget *dialog, int button, void *data)
vfolder_editor = NULL;
gtk_widget_destroy(dialog);
+
+ g_free (user);
}
void
@@ -846,7 +852,7 @@ edit_rule_response(GtkWidget *w, int button, void *data)
FilterRule *orig = g_object_get_data (G_OBJECT (w), "orig");
filter_rule_copy(orig, rule);
- user = g_strdup_printf("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save((RuleContext *)context, user);
g_free(user);
}
@@ -877,7 +883,7 @@ vfolder_edit_rule(const char *uri)
GTK_STOCK_OK,
GTK_RESPONSE_OK,
NULL);
- gtk_container_set_border_width ((GtkContainer *) gd, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (gd), 6);
gtk_box_set_spacing ((GtkBox *) gd->vbox, 6);
gtk_dialog_set_default_response(gd, GTK_RESPONSE_OK);
g_object_set(gd, "allow_shrink", FALSE, "allow_grow", TRUE, NULL);
@@ -925,7 +931,7 @@ new_rule_clicked(GtkWidget *w, int button, void *data)
g_object_ref(rule);
rule_context_add_rule((RuleContext *)context, rule);
- user = g_strdup_printf("%s/vfolders.xml", evolution_dir);
+ user = g_strdup_printf("%s/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ()));
rule_context_save((RuleContext *)context, user);
g_free(user);
}
@@ -971,7 +977,7 @@ vfolder_gui_add_rule(VfolderRule *rule)
GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response(gd, GTK_RESPONSE_OK);
- gtk_container_set_border_width ((GtkContainer *) gd, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (gd), 6);
gtk_box_set_spacing ((GtkBox *) gd->vbox, 6);
g_object_set(gd, "allow_shrink", FALSE, "allow_grow", TRUE, NULL);
gtk_window_set_default_size (GTK_WINDOW (gd), 500, 500);
diff --git a/mail/mail-vfolder.h b/mail/mail-vfolder.h
index 955b358c30..50c0dc5d93 100644
--- a/mail/mail-vfolder.h
+++ b/mail/mail-vfolder.h
@@ -11,7 +11,7 @@
#include "filter/vfolder-rule.h"
#include "filter/filter-part.h"
-void vfolder_load_storage(GNOME_Evolution_Shell shell);
+void vfolder_load_storage(void);
void vfolder_edit (void);
void vfolder_edit_rule(const char *name);
diff --git a/mail/mail.h b/mail/mail.h
index 81832cd2a5..86ca868f5f 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -31,23 +31,7 @@
#include "mail-session.h"
#include "mail-types.h"
-extern char *evolution_dir;
-
/* mail-identify */
char *mail_identify_mime_part (CamelMimePart *part, MailDisplay *md);
-
-/* component factory for lack of a better place */
-void mail_add_storage (CamelStore *store, const char *name, const char *uri);
-void mail_load_storage_by_uri (GNOME_Evolution_Shell shell, const char *uri, const char *name);
-/*takes a GSList of MailConfigServices */
-void mail_load_storages (GNOME_Evolution_Shell shell, EAccountList *sources);
-
-void mail_hash_storage (CamelService *store, EvolutionStorage *storage);
-EvolutionStorage *mail_lookup_storage (CamelStore *store);
-void mail_remove_storage_by_uri (const char *uri);
-void mail_remove_storage (CamelStore *store);
-void mail_storages_foreach (GHFunc func, gpointer data);
-int mail_storages_count (void);
-
gboolean evolution_folder_info_factory_init (void);
diff --git a/mail/main.c b/mail/main.c
deleted file mode 100644
index b5192a9f5c..0000000000
--- a/mail/main.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * main.c: The core of the mail component
- *
- * Author:
- * Miguel de Icaza (miguel@ximian.com)
- *
- * (C) 2000 Ximian, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <signal.h>
-
-#include <libgnome/gnome-sound.h>
-#include <bonobo/bonobo-main.h>
-#include <bonobo-activation/bonobo-activation-init.h>
-#include <glade/glade.h>
-#include <libgnomevfs/gnome-vfs.h>
-
-#include <gconf/gconf.h>
-
-#include <gal/widgets/e-gui-utils.h>
-#include <gal/widgets/e-cursors.h>
-
-#include "e-util/e-passwords.h"
-#include "e-util/e-proxy.h"
-
-#include "component-factory.h"
-#include "composer/evolution-composer.h"
-#include "mail.h"
-#include "mail-mt.h"
-
-/*#define DO_MCHECK*/
-
-#ifdef DO_MCHECK
-static int blowup(int status)
-{
- switch(status) {
- case 1:
- printf("Double free failure\n");
- break;
- case 2:
- printf("Memory clobbered before block\n");
- break;
- case 3:
- printf("Memory clobbered after block\n");
- break;
- }
- abort();
- return status;
-}
-#endif
-
-/* The GNOME SEGV handler will lose if it's not run from the main Gtk
- * thread. So if we crash in another thread, redirect the signal.
- */
-static void (*gnome_segv_handler) (int);
-
-static GStaticMutex segv_mutex = G_STATIC_MUTEX_INIT;
-
-static void
-segv_redirect (int sig)
-{
- if (pthread_self () == mail_gui_thread)
- gnome_segv_handler (sig);
- else {
- pthread_kill (mail_gui_thread, sig);
- /* We can't return from the signal handler or the
- * thread may SEGV again. But we can't pthread_exit,
- * because then the thread may get cleaned up before
- * bug-buddy can get a stack trace. So we block by
- * trying to lock a mutex we know is already locked.
- */
- g_static_mutex_lock (&segv_mutex);
- }
-}
-
-int
-main (int argc, char *argv [])
-{
- CORBA_ORB orb;
- struct sigaction sa, osa;
-
- /* used to make elfence work */
- free(malloc (10));
-#ifdef DO_MCHECK
- /*mtrace();*/
- mcheck(blowup);
-#endif
- bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
- textdomain (GETTEXT_PACKAGE);
-
- g_thread_init (NULL);
-
- gnome_init_with_popt_table ("evolution-mail-component", VERSION,
- argc, argv, bonobo_activation_popt_options, 0, NULL);
-
- sigaction (SIGSEGV, NULL, &osa);
- if (osa.sa_handler != SIG_DFL) {
- sa.sa_flags = 0;
- sigemptyset (&sa.sa_mask);
- sa.sa_handler = segv_redirect;
- sigaction (SIGSEGV, &sa, NULL);
- sigaction (SIGBUS, &sa, NULL);
- sigaction (SIGFPE, &sa, NULL);
-
- sa.sa_handler = SIG_IGN;
- sigaction (SIGXFSZ, &sa, NULL);
- gnome_segv_handler = osa.sa_handler;
- g_static_mutex_lock (&segv_mutex);
- }
-
- if (!bonobo_init (&argc, argv)) {
- g_error ("Mail component could not initialize Bonobo.\n"
- "If there was a warning message about the "
- "RootPOA, it probably means\nyou compiled "
- "Bonobo against GOAD instead of Bonobo Activation.");
- }
-
- gconf_init (argc, argv, NULL);
-
- glade_init ();
-
- gnome_vfs_init ();
-
- e_cursors_init ();
-
- e_proxy_init ();
-
- mail_config_init ();
- mail_msg_init ();
-
- gnome_sound_init ("localhost");
-
- component_factory_init ();
- evolution_composer_factory_init (composer_send_cb, composer_save_draft_cb);
-
- if (gdk_threads_mutex) {
- g_mutex_free (gdk_threads_mutex);
- gdk_threads_mutex = NULL;
- }
-
- g_print ("Evolution Mail ready and running.\n");
-
- GDK_THREADS_ENTER ();
- bonobo_main ();
-
- mail_msg_cleanup();
-
- GDK_THREADS_LEAVE ();
-
- mail_config_write_on_exit ();
-
- e_passwords_shutdown ();
-
- gnome_sound_shutdown ();
-
- return 0;
-}
diff --git a/mail/message-browser.c b/mail/message-browser.c
new file mode 100644
index 0000000000..e8e6ee0ef2
--- /dev/null
+++ b/mail/message-browser.c
@@ -0,0 +1,402 @@
+/* -*- 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 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 (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
new file mode 100644
index 0000000000..a02b13ef06
--- /dev/null
+++ b/mail/message-browser.h
@@ -0,0 +1,63 @@
+/* -*- 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 char *uri,
+ const char *uid);
+
+#endif /* _MESSAGE_BROWSER_H_ */
+
diff --git a/mail/subscribe-dialog.c b/mail/subscribe-dialog.c
new file mode 100644
index 0000000000..a1c18ff870
--- /dev/null
+++ b/mail/subscribe-dialog.c
@@ -0,0 +1,1669 @@
+/* -*- 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-component.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;
+ EStorage *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 (EStorage *storage, const char *path, const char *name, const char *url)
+{
+ EFolder *folder;
+ char *parent, *pname, *p;
+
+ p = strrchr (path, '/');
+ if (p && p != path) {
+ parent = g_strndup (path, p - path);
+ if (! e_storage_get_folder (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);
+ }
+
+ folder = e_folder_new (name, "mail", NULL);
+ e_folder_set_physical_uri (folder, url);
+ e_folder_set_can_sync_offline (folder, TRUE);
+
+ e_storage_new_folder (storage, path, folder);
+}
+
+/* ** 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);
+ g_object_unref (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_component_lookup_storage (mail_component_peek (), 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.etspec b/mail/subscribe-dialog.etspec
new file mode 100644
index 0000000000..1f5decbb36
--- /dev/null
+++ b/mail/subscribe-dialog.etspec
@@ -0,0 +1,9 @@
+<ETableSpecification cursor-mode="line" no-headers="true">
+ <ETableColumn model_col="0" pixbuf="subscribed-image" expansion="0.0" minimum_width="16" resizable="false" cell="cell_toggle" compare="integer"/>
+ <ETableColumn model_col="1" _title="Folder" expansion="1.0" minimum_width="20" resizable="true" cell="cell_tree" compare="string"/>
+ <ETableState>
+ <column source="0"/>
+ <column source="1"/>
+ <grouping></grouping>
+ </ETableState>
+</ETableSpecification>
diff --git a/mail/subscribe-dialog.h b/mail/subscribe-dialog.h
new file mode 100644
index 0000000000..c2e6b3d46f
--- /dev/null
+++ b/mail/subscribe-dialog.h
@@ -0,0 +1,62 @@
+/* -*- 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_ */
diff --git a/mail/upgrade-mailer.c b/mail/upgrade-mailer.c
new file mode 100644
index 0000000000..130194ca6a
--- /dev/null
+++ b/mail/upgrade-mailer.c
@@ -0,0 +1,1169 @@
+/* -*- 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.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <bonobo.h>
+#include <bonobo-conf/bonobo-config-database.h>
+#include <gal/util/e-xml-utils.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <camel/camel-file-utils.h>
+
+struct _storeinfo {
+ char *base_url;
+ char *namespace;
+ char *encoded_namespace;
+ char dir_sep;
+ GPtrArray *folders;
+};
+
+
+
+static char
+find_dir_sep (const char *lsub_response)
+{
+ register const unsigned char *inptr;
+ const unsigned char *inend;
+
+ inptr = (const unsigned char *) lsub_response;
+ inend = inptr + strlen (inptr);
+
+ if (strncmp (inptr, "* LSUB (", 8))
+ return '\0';
+
+ inptr += 8;
+ while (inptr < inend && *inptr != ')')
+ inptr++;
+
+ if (inptr >= inend)
+ return '\0';
+
+ inptr++;
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ return '\0';
+
+ if (*inptr == '\"')
+ inptr++;
+
+ return inptr < inend ? *inptr : '\0';
+}
+
+static void
+si_free (struct _storeinfo *si)
+{
+ int i;
+
+ g_free (si->base_url);
+ g_free (si->namespace);
+ g_free (si->encoded_namespace);
+ if (si->folders) {
+ for (i = 0; i < si->folders->len; i++)
+ g_free (si->folders->pdata[i]);
+ g_ptr_array_free (si->folders, TRUE);
+ }
+ g_free (si);
+}
+
+static unsigned char tohex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static char *
+hex_encode (const char *in, size_t len)
+{
+ const unsigned char *inend = in + len;
+ unsigned char *inptr, *outptr;
+ char *outbuf;
+
+ outptr = outbuf = g_malloc ((len * 3) + 1);
+
+ inptr = (unsigned char *) in;
+ while (inptr < inend) {
+ if (*inptr > 127 || isspace ((int) *inptr)) {
+ *outptr++ = '%';
+ *outptr++ = tohex[(*inptr >> 4) & 0xf];
+ *outptr++ = tohex[*inptr & 0xf];
+ inptr++;
+ } else
+ *outptr++ = *inptr++;
+ }
+
+ *outptr = '\0';
+
+ return outbuf;
+}
+
+#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
+
+static char *
+hex_decode (const char *in, size_t len)
+{
+ const unsigned char *inend = in + len;
+ unsigned char *inptr, *outptr;
+ char *outbuf;
+
+ outptr = outbuf = g_malloc (len + 1);
+
+ inptr = (unsigned char *) in;
+ while (inptr < inend) {
+ if (*inptr == '%') {
+ if (isxdigit ((int) inptr[1]) && isxdigit ((int) inptr[2])) {
+ *outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
+ inptr += 3;
+ } else
+ *outptr++ = *inptr++;
+ } else
+ *outptr++ = *inptr++;
+ }
+
+ *outptr = '\0';
+
+ return outbuf;
+}
+
+static char *
+parse_lsub (const char *lsub, char *dir_sep)
+{
+ const unsigned char *inptr = (const unsigned char *) lsub;
+ const unsigned char *inend;
+ int inlen, quoted = 0;
+
+ inend = inptr + strlen (inptr);
+ if (strncmp (inptr, "* LSUB (", 8))
+ return NULL;
+
+ inptr += 8;
+ while (inptr < inend && *inptr != ')')
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ inptr++;
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ /* skip over the dir sep */
+ if (*inptr == '\"')
+ inptr++;
+
+ *dir_sep = (char) *inptr++;
+ if (*inptr == '\"')
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ return NULL;
+
+ if (*inptr == '\"') {
+ inptr++;
+ quoted = 1;
+ } else
+ quoted = 0;
+
+ inlen = strlen (inptr) - quoted;
+
+ return g_strndup (inptr, inlen);
+}
+
+static void
+cache_upgrade (struct _storeinfo *si, const char *folder_name)
+{
+ const char *old_folder_name = folder_name;
+ char *oldpath, *newpath, *p;
+ struct dirent *dent;
+ DIR *dir = NULL;
+
+ if (si->namespace && strcmp ("INBOX", folder_name)) {
+ if (!strncmp (old_folder_name, si->namespace, strlen (si->namespace))) {
+ old_folder_name += strlen (si->namespace);
+ if (*old_folder_name == si->dir_sep)
+ old_folder_name++;
+ }
+ }
+
+ oldpath = g_strdup_printf ("%s/evolution/mail/imap/%s/%s", getenv ("HOME"),
+ si->base_url + 7, old_folder_name);
+
+ newpath = g_strdup_printf ("%s/evolution/mail/imap/%s/folders/%s",
+ getenv ("HOME"), si->base_url + 7, folder_name);
+
+ if (!strcmp (folder_name, "folders"))
+ goto special_case_folders;
+
+ if (si->dir_sep != '/') {
+ p = newpath + strlen (newpath) - strlen (folder_name) - 1;
+ while (*p) {
+ if (*p == si->dir_sep)
+ *p = '/';
+ p++;
+ }
+ }
+
+ /* make sure all parent directories exist */
+ if ((p = strrchr (newpath, '/'))) {
+ *p = '\0';
+ camel_mkdir (newpath, 0755);
+ *p = '/';
+ }
+
+ if (rename (oldpath, newpath) == -1) {
+ fprintf (stderr, "Failed to upgrade cache for imap folder %s/%s: %s\n",
+ si->base_url, folder_name, g_strerror (errno));
+ }
+
+ g_free (oldpath);
+ g_free (newpath);
+
+ return;
+
+ special_case_folders:
+
+ /* the user had a toplevel folder named "folders" */
+ if (camel_mkdir (newpath, 0755) == -1) {
+ /* we don't bother to check EEXIST because well, if
+ folders/folders exists then we're pretty much
+ fucked */
+ goto exception;
+ }
+
+ if (!(dir = opendir (oldpath)))
+ goto exception;
+
+ while ((dent = readdir (dir))) {
+ char *old_path, *new_path;
+
+ if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
+ continue;
+
+ old_path = g_strdup_printf ("%s/%s", oldpath, dent->d_name);
+ new_path = g_strdup_printf ("%s/%s", newpath, dent->d_name);
+
+ /* make sure all parent directories exist */
+ if ((p = strrchr (new_path, '/'))) {
+ *p = '\0';
+ camel_mkdir (new_path, 0755);
+ *p = '/';
+ }
+
+ if (rename (old_path, new_path) == -1) {
+ g_free (old_path);
+ g_free (new_path);
+ goto exception;
+ }
+
+ g_free (old_path);
+ g_free (new_path);
+ }
+
+ closedir (dir);
+
+ g_free (oldpath);
+ g_free (newpath);
+
+ return;
+
+ exception:
+
+ fprintf (stderr, "Failed to upgrade cache for imap folder %s/%s: %s\n",
+ si->base_url, folder_name, g_strerror (errno));
+
+ if (dir)
+ closedir (dir);
+
+ g_free (oldpath);
+ g_free (newpath);
+}
+
+static int
+foldercmp (const void *f1, const void *f2)
+{
+ const char **folder1 = (const char **) f1;
+ const char **folder2 = (const char **) f2;
+
+ return strcmp (*folder1, *folder2);
+}
+
+static void
+cache_upgrade_and_free (gpointer key, gpointer val, gpointer user_data)
+{
+ struct _storeinfo *si = val;
+ GPtrArray *folders;
+ char *path = NULL;
+ char dir_sep;
+ int i;
+
+ if (si->folders) {
+ path = g_strdup_printf ("%s/evolution/mail/imap/%s/folders",
+ getenv ("HOME"), si->base_url + 7);
+
+ if (mkdir (path, 0755) == -1 && errno != EEXIST) {
+ fprintf (stderr, "Failed to create directory %s: %s", path, g_strerror (errno));
+ goto exception;
+ }
+
+ g_free (path);
+ folders = g_ptr_array_new ();
+ for (i = 0; i < si->folders->len; i++) {
+ if ((path = parse_lsub (si->folders->pdata[i], &dir_sep))) {
+ g_ptr_array_add (folders, path);
+ }
+ }
+
+ /* sort the folders so that parents get created before
+ their children */
+ qsort (folders->pdata, folders->len, sizeof (void *), foldercmp);
+
+ for (i = 0; i < folders->len; i++) {
+ cache_upgrade (si, folders->pdata[i]);
+ g_free (folders->pdata[i]);
+ }
+ }
+
+ si_free (si);
+
+ return;
+
+ exception:
+
+ fprintf (stderr, "Could not upgrade imap cache for %s: %s\n",
+ si->base_url + 7, g_strerror (errno));
+
+ g_free (path);
+
+ si_free (si);
+}
+
+static char *
+get_base_url (const char *protocol, const char *uri)
+{
+ unsigned char *base_url, *p;
+
+ p = (unsigned char *) uri + strlen (protocol) + 1;
+ if (!strncmp (p, "//", 2))
+ p += 2;
+
+ base_url = p;
+ p = strchr (p, '/');
+ base_url = g_strdup_printf ("%s://%.*s", protocol, p ? (int) (p - base_url) : (int) strlen (base_url), base_url);
+
+ return base_url;
+}
+
+static char *
+imap_namespace (const char *uri)
+{
+ unsigned char *name, *p;
+
+ if ((name = strstr (uri, ";namespace=\"")) == NULL)
+ return NULL;
+
+ name += strlen (";namespace=\"");
+ p = name;
+ while (*p && *p != '\"')
+ p++;
+
+ return g_strndup (name, p - name);
+}
+
+static char *
+find_folder (GPtrArray *folders, const char *folder, char *dir_sep)
+{
+ const unsigned char *inptr, *inend;
+ int inlen, len, diff, i;
+ int quoted;
+
+ len = strlen (folder);
+
+ for (i = 0; i < folders->len; i++) {
+ inptr = folders->pdata[i];
+ inend = inptr + strlen (inptr);
+ if (strncmp (inptr, "* LSUB (", 8))
+ continue;
+
+ inptr += 8;
+ while (inptr < inend && *inptr != ')')
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ inptr++;
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ /* skip over the dir sep */
+ if (*inptr == '\"')
+ inptr++;
+
+ *dir_sep = *inptr++;
+ if (*inptr == '\"')
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ while (inptr < inend && isspace ((int) *inptr))
+ inptr++;
+
+ if (inptr >= inend)
+ continue;
+
+ if (*inptr == '\"') {
+ inptr++;
+ quoted = 1;
+ } else
+ quoted = 0;
+
+ inlen = strlen (inptr) - quoted;
+ if (len > inlen)
+ continue;
+
+ diff = inlen - len;
+ if (!strncmp (inptr + diff, folder, len))
+ return hex_encode (inptr, inlen);
+ }
+
+ *dir_sep = '\0';
+
+ return NULL;
+}
+
+static char *
+imap_url_upgrade (GHashTable *imap_sources, const char *uri)
+{
+ struct _storeinfo *si;
+ unsigned char *base_url, *folder, *p, *new = NULL;
+ char dir_sep;
+
+ base_url = get_base_url ("imap", uri);
+
+ fprintf (stderr, "checking for %s... ", base_url);
+ if (!(si = g_hash_table_lookup (imap_sources, base_url))) {
+ fprintf (stderr, "not found.\n");
+ g_warning ("Unknown imap account: %s", base_url);
+ g_free (base_url);
+ return NULL;
+ }
+
+ fprintf (stderr, "found.\n");
+ p = (unsigned char *) uri + strlen (base_url) + 1;
+ if (!strcmp (p, "INBOX")) {
+ new = g_strdup_printf ("%s/INBOX", base_url);
+ g_free (base_url);
+ return new;
+ }
+
+ p = hex_decode (p, strlen (p));
+
+ fprintf (stderr, "checking for folder %s on %s... ", p, base_url);
+ folder = si->folders ? find_folder (si->folders, p, &dir_sep) : NULL;
+ if (folder == NULL) {
+ fprintf (stderr, "not found.\n");
+ folder = p;
+ if (si->namespace) {
+ if (!si->dir_sep) {
+ fprintf (stderr, "checking for directory separator in namespace param... ");
+ if (*si->namespace == '/') {
+ dir_sep = '/';
+ } else {
+ p = si->namespace;
+ while (*p && !ispunct ((int) *p))
+ p++;
+
+ dir_sep = (char) *p;
+ }
+ } else {
+ dir_sep = si->dir_sep;
+ }
+
+ if (dir_sep) {
+ fprintf (stderr, "found: '%c'\n", dir_sep);
+ p = folder;
+ folder = hex_encode (folder, strlen (folder));
+ new = g_strdup_printf ("%s/%s%c%s", base_url, si->encoded_namespace, dir_sep, folder);
+ g_free (folder);
+ folder = p;
+
+ p = new + strlen (base_url) + 1;
+ while (*p) {
+ if (*p == dir_sep)
+ *p = '/';
+ p++;
+ }
+ } else {
+ fprintf (stderr, "not found.");
+ g_warning ("Cannot update settings for imap folder %s: unknown directory separator", uri);
+ }
+ } else {
+ g_warning ("Cannot update settings for imap folder %s: unknown namespace", uri);
+ }
+
+ g_free (base_url);
+ g_free (folder);
+
+ return new;
+ } else
+ g_free (p);
+
+ fprintf (stderr, "found.\n");
+ new = g_strdup_printf ("%s/%s", base_url, folder);
+ g_free (folder);
+
+ if (!si->dir_sep)
+ si->dir_sep = dir_sep;
+
+ if (dir_sep) {
+ p = new + strlen (base_url) + 1;
+ while (*p) {
+ if (*p == dir_sep)
+ *p = '/';
+ p++;
+ }
+ }
+
+ g_free (base_url);
+
+ return new;
+}
+
+static char *
+exchange_url_upgrade (const char *uri)
+{
+ unsigned char *base_url, *folder;
+ char *url;
+
+ base_url = get_base_url ("exchange", uri);
+ folder = (unsigned char *) uri + strlen (base_url) + 1;
+
+ if (strncmp (folder, "exchange/", 9))
+ return g_strdup (uri);
+
+ folder += 9;
+ while (*folder && *folder != '/')
+ folder++;
+ if (*folder == '/')
+ folder++;
+
+ folder = hex_decode (folder, strlen (folder));
+ url = g_strdup_printf ("%s/personal/%s", base_url, folder);
+ g_free (base_url);
+ g_free (folder);
+
+ return url;
+}
+
+static int
+mailer_upgrade_account_info (Bonobo_ConfigDatabase db, const char *key, int num, GHashTable *imap_sources)
+{
+ char *path, *uri, *new;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ path = g_strdup_printf ("/Mail/Accounts/account_%s_folder_uri_%d", key, i);
+ uri = bonobo_config_get_string (db, path, NULL);
+ if (uri) {
+ if (!strncmp (uri, "imap:", 5)) {
+ new = imap_url_upgrade (imap_sources, uri);
+ if (new) {
+ bonobo_config_set_string (db, path, new, NULL);
+ g_free (new);
+ }
+ } else if (!strncmp (uri, "exchange:", 9)) {
+ new = exchange_url_upgrade (uri);
+ bonobo_config_set_string (db, path, new, NULL);
+ g_free (new);
+ }
+ }
+
+ g_free (uri);
+ g_free (path);
+ }
+
+ return 0;
+}
+
+static int
+mailer_upgrade_xml_file (GHashTable *imap_sources, const char *filename)
+{
+ unsigned char *buffer, *inptr, *start, *uri, *new;
+ ssize_t nread = 0, nwritten, n;
+ gboolean url_need_upgrade;
+ struct stat st;
+ size_t len;
+ char *bak;
+ int fd;
+
+ bak = g_strdup_printf ("%s.bak-1.0", filename);
+ if (stat (bak, &st) != -1) {
+ /* seems we have already converted this file? */
+ fprintf (stderr, "\n%s already exists, assuming %s has already been upgraded\n", bak, filename);
+ g_free (bak);
+ return 0;
+ }
+
+ if (stat (filename, &st) == -1 || (fd = open (filename, O_RDONLY)) == -1) {
+ /* file doesn't exist? I guess nothing to upgrade here */
+ fprintf (stderr, "\nCould not open %s: %s\n", filename, strerror (errno));
+ g_free (bak);
+ return 0;
+ }
+
+ start = buffer = g_malloc (st.st_size + 1);
+ do {
+ do {
+ n = read (fd, buffer + nread, st.st_size - nread);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nread += n;
+ } while (n != -1 && nread < st.st_size);
+ buffer[nread] = '\0';
+
+ if (nread < st.st_size) {
+ /* failed to load the entire file? */
+ fprintf (stderr, "\nFailed to load %s: %s\n", filename, strerror (errno));
+ g_free (buffer);
+ g_free (bak);
+ close (fd);
+ return -1;
+ }
+
+ close (fd);
+
+ inptr = buffer;
+ url_need_upgrade = FALSE;
+ do {
+ inptr = strstr (inptr, "uri=\"");
+ if (inptr) {
+ inptr += 5;
+ url_need_upgrade = !strncmp (inptr, "imap:", 5) || !strncmp (inptr, "exchange:", 9);
+ }
+ } while (inptr && !url_need_upgrade);
+
+ if (inptr == NULL) {
+ /* no imap urls in this xml file, so no need to "upgrade" it */
+ fprintf (stdout, "\nNo updates required for %s\n", filename);
+ g_free (buffer);
+ g_free (bak);
+ return 0;
+ }
+
+ if (rename (filename, bak) == -1) {
+ /* failed to backup xml file */
+ fprintf (stderr, "\nFailed to create backup file %s: %s\n", bak, strerror (errno));
+ g_free (buffer);
+ g_free (bak);
+ return -1;
+ }
+
+ if ((fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) {
+ /* failed to create new xml file */
+ fprintf (stderr, "\nFailed to create new %s: %s\n", filename, strerror (errno));
+ rename (bak, filename);
+ g_free (buffer);
+ g_free (bak);
+ return -1;
+ }
+
+ while (inptr != NULL) {
+ len = inptr - start;
+ nwritten = 0;
+ do {
+ do {
+ n = write (fd, start + nwritten, len - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (n != -1 && nwritten < len);
+
+ if (nwritten < len)
+ goto exception;
+
+ start = inptr;
+ while (*start && *start != '"')
+ start++;
+
+ uri = g_strndup (inptr, start - inptr);
+ if (!strncmp (uri, "imap:", 5)) {
+ if ((new = imap_url_upgrade (imap_sources, uri)) == NULL) {
+ new = uri;
+ uri = NULL;
+ }
+ } else if (!strncmp (uri, "exchange:", 9)) {
+ new = exchange_url_upgrade (uri);
+ } else {
+ new = uri;
+ uri = NULL;
+ }
+ g_free (uri);
+
+ nwritten = 0;
+ len = strlen (new);
+ do {
+ do {
+ n = write (fd, new + nwritten, len - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (n != -1 && nwritten < len);
+
+ g_free (new);
+
+ if (nwritten < len)
+ goto exception;
+
+ inptr = start;
+ url_need_upgrade = FALSE;
+ do {
+ inptr = strstr (inptr, "uri=\"");
+ if (inptr) {
+ inptr += 5;
+ url_need_upgrade = !strncmp (inptr, "imap:", 5) || !strncmp (inptr, "exchange:", 9);
+ }
+ } while (inptr && !url_need_upgrade);
+ }
+
+ nwritten = 0;
+ len = strlen (start);
+ do {
+ do {
+ n = write (fd, start + nwritten, len - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } while (n != -1 && nwritten < len);
+
+ if (nwritten < len)
+ goto exception;
+
+ if (fsync (fd) == -1)
+ goto exception;
+
+ close (fd);
+ g_free (buffer);
+
+ fprintf (stdout, "\nSuccessfully upgraded %s\nPrevious settings saved in %s\n\n", filename, bak);
+
+ g_free (bak);
+
+ return 0;
+
+ exception:
+
+ fprintf (stderr, "\nFailed to save updated settings to %s: %s\n\n", filename, strerror (errno));
+
+ close (fd);
+ g_free (buffer);
+ unlink (filename);
+ rename (bak, filename);
+ g_free (bak);
+
+ return -1;
+}
+
+static char *
+shortcuts_upgrade_uri (GHashTable *accounts, GHashTable *imap_sources, const char *account, const char *folder)
+{
+ char *url, *name, *decoded, *new = NULL;
+ struct _storeinfo *si;
+ int type;
+
+ type = GPOINTER_TO_INT ((si = g_hash_table_lookup (accounts, account)));
+ if (type == 1) {
+ /* exchange */
+ decoded = hex_decode (folder, strlen (folder));
+ name = g_strdup_printf ("personal/%s", decoded);
+ g_free (decoded);
+
+ return name;
+ } else {
+ /* imap */
+ url = g_strdup_printf ("%s/%s", si->base_url, folder);
+ new = imap_url_upgrade (imap_sources, url);
+ g_free (url);
+
+ if (new) {
+ name = new + strlen (si->base_url) + 1;
+ name = hex_decode (name, strlen (name));
+ g_free (new);
+
+ return name;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+shortcuts_upgrade_xml_file (GHashTable *accounts, GHashTable *imap_sources, const char *filename)
+{
+ char *bak, *uri, *account, *folder, *new, *new_uri, *type;
+ struct stat st;
+ xmlDoc *doc;
+ xmlNode *group, *item;
+ int account_len;
+ gboolean changed = FALSE;
+
+ bak = g_strdup_printf ("%s.bak-1.0", filename);
+ if (stat (bak, &st) != -1) {
+ /* seems we have already converted this file? */
+ fprintf (stderr, "\n%s already exists, assuming %s has already been upgraded\n", bak, filename);
+ g_free (bak);
+ return 0;
+ }
+
+ if (stat (filename, &st) == -1) {
+ /* file doesn't exist? I guess nothing to upgrade here */
+ fprintf (stderr, "\nCould not open %s: %s\n", filename, strerror (errno));
+ g_free (bak);
+ return 0;
+ }
+
+ doc = xmlParseFile (filename);
+ if (!doc || !doc->xmlRootNode) {
+ /* failed to load/parse the file? */
+ fprintf (stderr, "\nFailed to load %s\n", filename);
+ g_free (bak);
+ return -1;
+ }
+
+ for (group = doc->xmlRootNode->xmlChildrenNode; group; group = group->next) {
+ for (item = group->xmlChildrenNode; item; item = item->next) {
+ /* Fix IMAP/Exchange URIs */
+ uri = xmlNodeGetContent (item);
+ if (!strncmp (uri, "evolution:/", 11)) {
+ if (!strcmp (uri, "evolution:/local/Inbox")) {
+ xmlNodeSetContent (item, "default:mail");
+ changed = TRUE;
+ } else if (!strcmp (uri, "evolution:/local/Calendar")) {
+ xmlNodeSetContent (item, "default:calendar");
+ changed = TRUE;
+ } else if (!strcmp (uri, "evolution:/local/Contacts")) {
+ xmlNodeSetContent (item, "default:contacts");
+ changed = TRUE;
+ } else if (!strcmp (uri, "evolution:/local/Tasks")) {
+ xmlNodeSetContent (item, "default:tasks");
+ changed = TRUE;
+ } else {
+ account_len = strcspn (uri + 11, "/");
+ account = g_strndup (uri + 11, account_len);
+ if (g_hash_table_lookup (accounts, account)) {
+ folder = uri + 11 + account_len;
+ if (*folder)
+ folder++;
+ new = shortcuts_upgrade_uri (accounts, imap_sources, account, folder);
+ new_uri = g_strdup_printf ("evolution:/%s/%s", account, new);
+ xmlNodeSetContent (item, new_uri);
+ changed = TRUE;
+ g_free (new_uri);
+ }
+ g_free (account);
+ }
+ }
+ xmlFree (uri);
+
+ /* Fix LDAP shortcuts */
+ type = xmlGetProp (item, "type");
+ if (type) {
+ if (!strcmp (type, "ldap-contacts")) {
+ xmlSetProp (item, "type", "contacts/ldap");
+ changed = TRUE;
+ }
+ xmlFree (type);
+ }
+ }
+ }
+
+ if (!changed) {
+ fprintf (stdout, "\nNo updates required for %s\n", filename);
+ xmlFreeDoc (doc);
+ g_free (bak);
+ return 0;
+ }
+
+ if (rename (filename, bak) == -1) {
+ /* failed to backup xml file */
+ fprintf (stderr, "\nFailed to create backup file %s: %s\n", bak, strerror (errno));
+ xmlFreeDoc (doc);
+ g_free (bak);
+ return -1;
+ }
+
+ if (e_xml_save_file (filename, doc) == -1) {
+ fprintf (stderr, "\nFailed to save updated settings to %s: %s\n\n", filename, strerror (errno));
+ xmlFreeDoc (doc);
+ unlink (filename);
+ rename (bak, filename);
+ g_free (bak);
+ return -1;
+ }
+
+ fprintf (stdout, "\nSuccessfully upgraded %s\nPrevious settings saved in %s\n\n", filename, bak);
+
+ xmlFreeDoc (doc);
+ g_free (bak);
+
+ return 0;
+}
+
+
+static int
+mailer_upgrade (Bonobo_ConfigDatabase db)
+{
+ GHashTable *imap_sources, *accounts;
+ char *path, *uri;
+ char *account, *transport;
+ int num, i;
+
+ if ((num = bonobo_config_get_long_with_default (db, "/Mail/Accounts/num", 0, NULL)) == 0) {
+ /* nothing to upgrade */
+ return 0;
+ }
+
+ accounts = g_hash_table_new (g_str_hash, g_str_equal);
+ imap_sources = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; i < num; i++) {
+ struct _storeinfo *si;
+ struct stat st;
+ char *string;
+ guint32 tmp;
+ FILE *fp;
+ int j;
+
+ path = g_strdup_printf ("/Mail/Accounts/source_url_%d", i);
+ uri = bonobo_config_get_string (db, path, NULL);
+ g_free (path);
+ if (uri && !strncmp (uri, "imap:", 5)) {
+ path = g_strdup_printf ("/Mail/Accounts/account_name_%d", i);
+ account = bonobo_config_get_string (db, path, NULL);
+ g_free (path);
+
+ si = g_new (struct _storeinfo, 1);
+ si->base_url = get_base_url ("imap", uri);
+ si->namespace = imap_namespace (uri);
+ si->encoded_namespace = NULL;
+ si->dir_sep = '\0';
+ si->folders = NULL;
+
+ path = si->base_url + 7;
+
+ path = g_strdup_printf ("%s/evolution/mail/imap/%s/storeinfo", getenv ("HOME"), path);
+ if (stat (path, &st) != -1 && (fp = fopen (path, "r")) != NULL) {
+ camel_file_util_decode_uint32 (fp, &tmp);
+ camel_file_util_decode_uint32 (fp, &tmp);
+
+ j = 0;
+ si->folders = g_ptr_array_new ();
+ while (camel_file_util_decode_string (fp, &string) != -1) {
+ if (j++ > 0) {
+ g_ptr_array_add (si->folders, string);
+ } else {
+ if (!si->namespace)
+ si->namespace = string;
+ else
+ g_free (string);
+
+ camel_file_util_decode_uint32 (fp, &tmp);
+ si->dir_sep = (char) tmp & 0xff;
+ }
+ }
+
+ fclose (fp);
+ }
+ g_free (path);
+
+ if (si->folders && si->folders->len > 0)
+ si->dir_sep = find_dir_sep (si->folders->pdata[0]);
+
+ if (si->namespace) {
+ /* strip trailing dir_sep from namespace if it's there */
+ j = strlen (si->namespace) - 1;
+ if (si->namespace[j] == si->dir_sep)
+ si->namespace[j] = '\0';
+
+ /* set the encoded version of the namespace */
+ si->encoded_namespace = g_strdup (si->namespace);
+ for (j = 0; j < strlen (si->encoded_namespace); j++) {
+ if (si->encoded_namespace[j] == '/')
+ si->encoded_namespace[j] = '.';
+ }
+ }
+
+ g_hash_table_insert (imap_sources, si->base_url, si);
+
+ if (account)
+ g_hash_table_insert (accounts, account, si);
+ } else if (uri && !strncmp (uri, "exchange:", 9)) {
+ /* Upgrade transport uri */
+ path = g_strdup_printf ("/Mail/Accounts/transport_url_%d", i);
+ transport = bonobo_config_get_string (db, path, NULL);
+ if (transport && !strncmp (transport, "exchanget:", 10))
+ bonobo_config_set_string (db, path, uri, NULL);
+ g_free (transport);
+ g_free (path);
+
+ path = g_strdup_printf ("/Mail/Accounts/account_name_%d", i);
+ account = bonobo_config_get_string (db, path, NULL);
+ g_free (path);
+
+ if (account)
+ g_hash_table_insert (accounts, account, GINT_TO_POINTER (1));
+ }
+
+ g_free (uri);
+ }
+
+ if (g_hash_table_size (accounts) == 0) {
+ /* user doesn't have any imap/exchange accounts - nothing to upgrade */
+ g_hash_table_destroy (imap_sources);
+ return 0;
+ }
+
+ /* upgrade user's account info (bug #29135) */
+ mailer_upgrade_account_info (db, "drafts", num, imap_sources);
+ mailer_upgrade_account_info (db, "sent", num, imap_sources);
+
+ /* upgrade user's filters/vfolders (bug #24451) */
+ path = g_strdup_printf ("%s/evolution/filters.xml", getenv ("HOME"));
+ mailer_upgrade_xml_file (imap_sources, path);
+ g_free (path);
+
+ path = g_strdup_printf ("%s/evolution/vfolders.xml", getenv ("HOME"));
+ mailer_upgrade_xml_file (imap_sources, path);
+ g_free (path);
+
+ /* upgrade user's shortcuts (there's no bug # for this one) */
+ path = g_strdup_printf ("%s/evolution/shortcuts.xml", getenv ("HOME"));
+ shortcuts_upgrade_xml_file (accounts, imap_sources, path);
+ g_free (path);
+
+ g_hash_table_foreach (imap_sources, cache_upgrade_and_free, NULL);
+ g_hash_table_destroy (imap_sources);
+#if 0
+ path = g_strdup_printf ("%s/evolution/mail/imap", getenv ("HOME"));
+ bak = g_strdup_printf ("%s.bak-1.0", path);
+
+ if (rename (path, bak) == -1)
+ fprintf (stderr, "\nFailed to backup Evolution 1.0's IMAP cache: %s\n", strerror (errno));
+
+ g_free (path);
+ g_free (bak);
+#endif
+
+ return 0;
+}
+
+static Bonobo_ConfigDatabase
+get_config_db (void)
+{
+ Bonobo_ConfigDatabase db;
+ CORBA_Environment ev;
+
+ CORBA_exception_init (&ev);
+
+ db = bonobo_get_object ("wombat:", "Bonobo/ConfigDatabase", &ev);
+ if (BONOBO_EX (&ev) || db == CORBA_OBJECT_NIL) {
+ fprintf (stderr, "get_config_db(): Could not get the config database object '%s'",
+ bonobo_exception_get_text (&ev));
+ db = CORBA_OBJECT_NIL;
+ }
+
+ CORBA_exception_free (&ev);
+
+ return db;
+}
+
+static int
+upgrade (void)
+{
+ Bonobo_ConfigDatabase db;
+ CORBA_Environment ev;
+
+ if ((db = get_config_db ()) == CORBA_OBJECT_NIL)
+ g_error ("Could not get config db");
+
+ mailer_upgrade (db);
+
+ CORBA_exception_init (&ev);
+ Bonobo_ConfigDatabase_sync (db, &ev);
+
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+int main (int argc, char **argv)
+{
+ CORBA_ORB orb;
+
+ gnome_init ("evolution-upgrade", "1.0", argc, argv);
+
+ if ((orb = oaf_init (argc, argv)) == NULL)
+ g_error ("Cannot init oaf");
+
+ if (bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL) == FALSE)
+ g_error ("Cannot init bonobo");
+
+ gtk_idle_add ((GtkFunction) upgrade, NULL);
+
+ bonobo_main ();
+
+ return 0;
+}