diff options
Diffstat (limited to 'mail/mail-component.c')
-rw-r--r-- | mail/mail-component.c | 1625 |
1 files changed, 1625 insertions, 0 deletions
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; +} |