/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* subscribe-dialog.c: Subscribe dialog */ /* * Authors: Chris Toshok * * Copyright 2000 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "e-util/e-html-utils.h" #include "evolution-shell-component-utils.h" #include "mail.h" #include "mail-tools.h" #include "mail-mt.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 DEFAULT_STORE_TABLE_WIDTH 200 #define DEFAULT_WIDTH 500 #define DEFAULT_HEIGHT 300 #define PARENT_TYPE (gtk_object_get_type ()) #ifdef JUST_FOR_TRANSLATORS static char *list [] = { N_("Folder"), N_("Store"), }; #endif #define FOLDER_ETREE_SPEC " \ \ \ \ \ \ \ \ " #define STORE_ETABLE_SPEC " \ \ \ \ \ \ " enum { FOLDER_COL_SUBSCRIBED, FOLDER_COL_NAME, FOLDER_COL_LAST }; enum { STORE_COL_NAME, STORE_COL_LAST }; static GtkObjectClass *subscribe_dialog_parent_class; static void build_tree (SubscribeDialog *sc, CamelStore *store); static EPixmap pixmaps [] = { E_PIXMAP ("/Toolbar/SubscribeFolder", "buttons/fetch-mail.png"), /* XXX */ E_PIXMAP ("/Toolbar/UnsubscribeFolder", "buttons/compose-message.png"), /* XXX */ E_PIXMAP ("/Toolbar/RefreshList", "buttons/forward.png"), /* XXX */ E_PIXMAP_END }; static GtkWidget* make_folder_search_widget (GtkSignalFunc start_search_func, gpointer user_data_for_search) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data_for_search); GtkWidget *search_hbox = gtk_hbox_new (FALSE, 0); sc->search_entry = gtk_entry_new (); if (start_search_func) { gtk_signal_connect (GTK_OBJECT (sc->search_entry), "activate", start_search_func, user_data_for_search); } /* add the search entry to the our search_vbox */ gtk_box_pack_start (GTK_BOX (search_hbox), gtk_label_new(_("Display folders starting with:")), FALSE, TRUE, 0); gtk_box_pack_start (GTK_BOX (search_hbox), sc->search_entry, FALSE, TRUE, 3); return search_hbox; } /* Our async operations */ typedef void (*SubscribeGetStoreCallback)(SubscribeDialog *sc, CamelStore *store, gpointer cb_data); typedef void (*SubscribeFolderCallback)(SubscribeDialog *sc, gboolean success, gpointer cb_data); /* ** GET STORE ******************************************************* */ struct _get_store_msg { struct _mail_msg msg; SubscribeDialog *sc; char *url; SubscribeGetStoreCallback cb; gpointer cb_data; CamelStore *store; }; static char *get_store_desc(struct _mail_msg *mm, int done) { struct _get_store_msg *m = (struct _get_store_msg *)mm; return g_strdup_printf(_("Getting store for \"%s\""), m->url); } static void get_store_get(struct _mail_msg *mm) { struct _get_store_msg *m = (struct _get_store_msg *)mm; m->store = camel_session_get_store (session, m->url, &mm->ex); } static void get_store_got(struct _mail_msg *mm) { struct _get_store_msg *m = (struct _get_store_msg *)mm; m->cb(m->sc, m->store, m->cb_data); } static void get_store_free(struct _mail_msg *mm) { struct _get_store_msg *m = (struct _get_store_msg *)mm; if (m->store) camel_object_unref((CamelObject *)m->store); g_free(m->url); } static struct _mail_msg_op get_store_op = { get_store_desc, get_store_get, get_store_got, get_store_free, }; static void subscribe_do_get_store (SubscribeDialog *sc, const char *url, SubscribeGetStoreCallback cb, gpointer cb_data) { struct _get_store_msg *m; int id; g_return_if_fail (url != NULL); m = mail_msg_new(&get_store_op, NULL, sizeof(*m)); m->sc = sc; m->url = g_strdup(url); m->cb = cb; m->cb_data = cb_data; id = m->msg.seq; e_thread_put(mail_thread_queued, (EMsg *)m); mail_msg_wait(id); } /* ** SUBSCRIBE FOLDER ******************************************************* */ /* Given a CamelFolderInfo, construct the corresponding * EvolutionStorage path to it. */ static char * storage_tree_path (CamelFolderInfo *info) { int len; CamelFolderInfo *i; char *path, *p; for (len = 0, i = info; i; i = i->parent) len += strlen (i->name) + 1; /* We do this backwards because that's the way the pointers point. */ path = g_malloc (len + 1); p = path + len; *p = '\0'; for (i = info; i; i = i->parent) { len = strlen (i->name); p -= len; memcpy (p, i->name, len); *--p = '/'; } return path; } /* ********************************************************************** */ /* Subscribe folder */ struct _subscribe_msg { struct _mail_msg msg; SubscribeDialog *sc; CamelStore *store; gboolean subscribe; SubscribeFolderCallback cb; gpointer cb_data; char *path; char *name; char *full_name; char *url; }; 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 recursive_add_folder (EvolutionStorage *storage, const char *path, const char *name, const char *url) { char *parent, *pname, *p; p = strrchr (path, '/'); if (p && p != path) { parent = g_strndup (path, p - path); if (!evolution_storage_folder_exists (storage, parent)) { p = strrchr (parent, '/'); if (p) pname = g_strdup (p + 1); else pname = g_strdup (""); recursive_add_folder (storage, parent, pname, ""); g_free (pname); } g_free (parent); } evolution_storage_new_folder (storage, path, name, "mail", url, name, FALSE); } static void subscribe_folder_subscribed(struct _mail_msg *mm) { struct _subscribe_msg *m = (struct _subscribe_msg *)mm; if (!camel_exception_is_set (&mm->ex)) { if (m->subscribe) recursive_add_folder(m->sc->storage, m->path, m->name, m->url); else evolution_storage_removed_folder(m->sc->storage, m->path); } if (m->cb) m->cb(m->sc, !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->path); g_free(m->name); g_free(m->full_name); g_free(m->url); camel_object_unref((CamelObject *)m->store); /* in wrong thread to do this? gtk_object_unref (GTK_OBJECT (input->sc));*/ } static struct _mail_msg_op subscribe_folder_op = { subscribe_folder_desc, subscribe_folder_subscribe, subscribe_folder_subscribed, subscribe_folder_free, }; static void subscribe_do_subscribe_folder (SubscribeDialog *sc, CamelStore *store, CamelFolderInfo *info, gboolean subscribe, SubscribeFolderCallback cb, gpointer cb_data) { struct _subscribe_msg *m; g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (info); m = mail_msg_new(&subscribe_folder_op, NULL, sizeof(*m)); m->sc = sc; m->store = store; camel_object_ref((CamelObject *)store); m->subscribe = subscribe; m->cb = cb; m->cb_data = cb_data; m->path = storage_tree_path (info); m->name = g_strdup (info->name); m->full_name = g_strdup (info->full_name); m->url = g_strdup (info->url); /* gtk_object_ref (GTK_OBJECT (sc));*/ e_thread_put(mail_thread_new, (EMsg *)m); } static gboolean folder_info_subscribed (SubscribeDialog *sc, CamelFolderInfo *info) { return camel_store_folder_subscribed (sc->store, info->full_name); } static void node_changed_cb (SubscribeDialog *sc, gboolean changed, gpointer data) { ETreePath node = data; if (changed) e_tree_model_node_data_changed (sc->folder_model, node); } static void subscribe_folder_info (SubscribeDialog *sc, CamelFolderInfo *info, ETreePath node) { /* folders without urls cannot be subscribed to */ if (info->url == NULL) return; subscribe_do_subscribe_folder (sc, sc->store, info, TRUE, node_changed_cb, node); } static void unsubscribe_folder_info (SubscribeDialog *sc, CamelFolderInfo *info, ETreePath node) { /* folders without urls cannot be subscribed to */ if (info->url == NULL) return; subscribe_do_subscribe_folder (sc, sc->store, info, FALSE, node_changed_cb, node); } static void subscribe_close (BonoboUIComponent *uic, void *user_data, const char *path) { SubscribeDialog *sc = (SubscribeDialog*)user_data; gtk_widget_destroy (sc->app); } static void subscribe_select_all (BonoboUIComponent *uic, void *user_data, const char *path) { SubscribeDialog *sc = (SubscribeDialog*)user_data; ETreeScrolled *scrolled = E_TREE_SCROLLED (sc->folder_etree); ETree *tree = e_tree_scrolled_get_tree (scrolled); ESelectionModel *esm = e_tree_get_selection_model (E_TREE (tree)); e_selection_model_select_all (E_SELECTION_MODEL (esm)); } static void subscribe_invert_selection (BonoboUIComponent *uic, void *user_data, const char *path) { SubscribeDialog *sc = (SubscribeDialog*)user_data; ETreeScrolled *scrolled = E_TREE_SCROLLED (sc->folder_etree); ETree *tree = e_tree_scrolled_get_tree (scrolled); ESelectionModel *esm = e_tree_get_selection_model (E_TREE (tree)); e_selection_model_invert_selection (E_SELECTION_MODEL (esm)); } static void subscribe_folder_foreach (int model_row, gpointer closure) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (closure); ETreePath node = e_tree_node_at_row (e_tree_scrolled_get_tree(E_TREE_SCROLLED(sc->folder_etree)), model_row); CamelFolderInfo *info = e_tree_memory_node_get_data (E_TREE_MEMORY(sc->folder_model), node); if (!folder_info_subscribed (sc, info)) subscribe_folder_info (sc, info, node); } static void subscribe_folders (BonoboUIComponent *componet, gpointer user_data, const char *cname) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); e_tree_selected_row_foreach (e_tree_scrolled_get_tree(E_TREE_SCROLLED(sc->folder_etree)), subscribe_folder_foreach, sc); } static void unsubscribe_folder_foreach (int model_row, gpointer closure) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (closure); ETreePath node = e_tree_node_at_row (e_tree_scrolled_get_tree(E_TREE_SCROLLED(sc->folder_etree)), model_row); CamelFolderInfo *info = e_tree_memory_node_get_data (E_TREE_MEMORY(sc->folder_model), node); if (folder_info_subscribed(sc, info)) unsubscribe_folder_info (sc, info, node); } static void unsubscribe_folders (BonoboUIComponent *component, gpointer user_data, const char *cname) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); e_tree_selected_row_foreach (e_tree_scrolled_get_tree(E_TREE_SCROLLED(sc->folder_etree)), unsubscribe_folder_foreach, sc); } static void subscribe_refresh_list (BonoboUIComponent *component, gpointer user_data, const char *cname) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); e_utf8_gtk_entry_set_text (GTK_ENTRY (sc->search_entry), ""); if (sc->search_top) { g_free (sc->search_top); sc->search_top = NULL; } if (sc->store) build_tree (sc, sc->store); } static void subscribe_search (GtkWidget *widget, gpointer user_data) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (user_data); char* search_pattern = e_utf8_gtk_entry_get_text(GTK_ENTRY(widget)); if (sc->search_top) { g_free (sc->search_top); sc->search_top = NULL; } if (search_pattern && *search_pattern) sc->search_top = search_pattern; if (sc->store) build_tree (sc, sc->store); } #if 0 /* HTML Helpers */ static void html_size_req (GtkWidget *widget, GtkRequisition *requisition) { if (GTK_LAYOUT (widget)->height > 90) requisition->height = 90; else requisition->height = GTK_LAYOUT (widget)->height; } /* Returns a GtkHTML which is already inside a GtkScrolledWindow. If * @white is TRUE, the GtkScrolledWindow will be inside a GtkFrame. */ static GtkWidget * html_new (gboolean white) { GtkWidget *html, *scrolled, *frame; GtkStyle *style; html = gtk_html_new (); GTK_LAYOUT (html)->height = 0; gtk_signal_connect (GTK_OBJECT (html), "size_request", GTK_SIGNAL_FUNC (html_size_req), NULL); gtk_html_set_editable (GTK_HTML (html), FALSE); style = gtk_rc_get_style (html); if (style) { gtk_html_set_default_background_color (GTK_HTML (html), white ? &style->white : &style->bg[0]); } gtk_widget_set_sensitive (html, FALSE); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_NEVER); gtk_container_add (GTK_CONTAINER (scrolled), html); if (white) { frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_add (GTK_CONTAINER (frame), scrolled); gtk_widget_show_all (frame); } else gtk_widget_show_all (scrolled); return html; } static void put_html (GtkHTML *html, char *text) { GtkHTMLStream *handle; text = e_text_to_html (text, (E_TEXT_TO_HTML_CONVERT_NL | E_TEXT_TO_HTML_CONVERT_SPACES | E_TEXT_TO_HTML_CONVERT_URLS)); handle = gtk_html_begin (html); gtk_html_write (html, handle, "", 12); gtk_html_write (html, handle, text, strlen (text)); gtk_html_write (html, handle, "", 14); g_free (text); gtk_html_end (html, handle, GTK_HTML_STREAM_OK); } #endif /* etree stuff for the subscribe ui */ static int folder_etree_column_count (ETreeModel *etm, void *data) { return FOLDER_COL_LAST; } static void* folder_etree_duplicate_value (ETreeModel *etm, int col, const void *val, void *data) { return g_strdup (val); } static void folder_etree_free_value (ETreeModel *etm, int col, void *val, void *data) { g_free (val); } static void* folder_etree_init_value (ETreeModel *etm, int col, void *data) { return g_strdup (""); } static gboolean folder_etree_value_is_empty (ETreeModel *etm, int col, const void *val, void *data) { return !(val && *(char *)val); } static char* folder_etree_value_to_string (ETreeModel *etm, int col, const void *val, void *data) { return g_strdup(val); } static GdkPixbuf* folder_etree_icon_at (ETreeModel *etree, ETreePath path, void *model_data) { return NULL; /* XXX no icons for now */ } static void* folder_etree_value_at (ETreeModel *etree, ETreePath path, int col, void *model_data) { SubscribeDialog *dialog = SUBSCRIBE_DIALOG (model_data); CamelFolderInfo *info = e_tree_memory_node_get_data (E_TREE_MEMORY(etree), path); if (col == FOLDER_COL_NAME) { return info->name; } else /* FOLDER_COL_SUBSCRIBED */ { /* folders without urls cannot be subscribed to */ if (info->url == NULL) return GINT_TO_POINTER(0); /* empty */ else if (!folder_info_subscribed(dialog, info)) return GINT_TO_POINTER(0); /* XXX unchecked */ else return GUINT_TO_POINTER (1); /* checked */ } } static void folder_etree_set_value_at (ETreeModel *etree, ETreePath path, int col, const void *val, void *model_data) { /* nothing */ } static gboolean folder_etree_is_editable (ETreeModel *etree, ETreePath path, int col, void *model_data) { return FALSE; } static int store_etable_col_count (ETableModel *etm, void *data) { return STORE_COL_LAST; } static int store_etable_row_count (ETableModel *etm, void *data) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (data); return g_list_length (sc->store_list); } static void* store_etable_value_at (ETableModel *etm, int col, int row, void *data) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (data); CamelStore *store = (CamelStore*)g_list_nth_data (sc->store_list, row); return camel_service_get_name (CAMEL_SERVICE (store), TRUE); } static void store_etable_set_value_at (ETableModel *etm, int col, int row, const void *val, void *data) { /* nada */ } static gboolean store_etable_is_editable (ETableModel *etm, int col, int row, void *data) { return FALSE; } static void* store_etable_duplicate_value (ETableModel *etm, int col, const void *val, void *data) { return g_strdup (val); } static void store_etable_free_value (ETableModel *etm, int col, void *val, void *data) { g_free (val); } static void* store_etable_initialize_value (ETableModel *etm, int col, void *data) { return g_strdup (""); } static gboolean store_etable_value_is_empty (ETableModel *etm, int col, const void *val, void *data) { return !(val && *(char *)val); } static char* store_etable_value_to_string (ETableModel *etm, int col, const void *val, void *data) { return g_strdup(val); } static void build_etree_from_folder_info (SubscribeDialog *sc, ETreePath parent, CamelFolderInfo *info) { CamelFolderInfo *i; if (info == NULL) return; for (i = info; i; i = i->sibling) { ETreePath node = e_tree_memory_node_insert (E_TREE_MEMORY(sc->folder_model), parent, -1, i); build_etree_from_folder_info (sc, node, i->child); } } static void build_tree (SubscribeDialog *sc, CamelStore *store) { CamelException *ex = camel_exception_new(); /* free up the existing CamelFolderInfo* if there is any */ if (sc->folder_info) camel_store_free_folder_info (sc->store, sc->folder_info); if (sc->storage) gtk_object_unref (GTK_OBJECT (sc->storage)); sc->store = store; sc->storage = mail_lookup_storage (sc->store); sc->folder_info = camel_store_get_folder_info (sc->store, sc->search_top, CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_FAST, ex); if (camel_exception_is_set (ex)) { printf ("camel_store_get_folder_info failed\n"); camel_exception_free (ex); return; } e_tree_memory_freeze(E_TREE_MEMORY(sc->folder_model)); e_tree_memory_node_remove (E_TREE_MEMORY(sc->folder_model), sc->folder_root); sc->folder_root = e_tree_memory_node_insert (E_TREE_MEMORY(sc->folder_model), NULL, 0, NULL); build_etree_from_folder_info (sc, sc->folder_root, sc->folder_info); e_tree_memory_thaw(E_TREE_MEMORY(sc->folder_model)); camel_exception_free (ex); } static void storage_selected_cb (ETree *table, int row, gpointer data) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (data); CamelStore *store = (CamelStore*)g_list_nth_data (sc->store_list, row); build_tree (sc, store); } static void folder_toggle_cb (ETree *tree, int row, ETreePath path, int col, GdkEvent *event, gpointer data) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (data); CamelFolderInfo *info = e_tree_memory_node_get_data (E_TREE_MEMORY(sc->folder_model), path); if (folder_info_subscribed(sc, info)) unsubscribe_folder_info (sc, info, path); else subscribe_folder_info (sc, info, path); e_tree_model_node_data_changed (sc->folder_model, path); } #define EXAMPLE_DESCR "And the beast shall come forth surrounded by a roiling cloud of vengeance.\n" \ " The house of the unbelievers shall be razed and they shall be scorched to the\n" \ " earth. Their tags shall blink until the end of days. \n" \ " from The Book of Mozilla, 12:10" static BonoboUIVerb verbs [] = { /* File Menu */ BONOBO_UI_VERB ("FileCloseWin", subscribe_close), /* Edit Menu */ BONOBO_UI_VERB ("EditSelectAll", subscribe_select_all), BONOBO_UI_VERB ("EditInvertSelection", subscribe_invert_selection), /* Folder Menu / Toolbar */ BONOBO_UI_VERB ("SubscribeFolder", subscribe_folders), BONOBO_UI_VERB ("UnsubscribeFolder", unsubscribe_folders), /* Toolbar Specific */ BONOBO_UI_VERB ("RefreshList", subscribe_refresh_list), BONOBO_UI_VERB_END }; static void store_cb (SubscribeDialog *sc, CamelStore *store, gpointer data) { if (!store) return; if (camel_store_supports_subscriptions (store)) { camel_object_ref((CamelObject *)store); sc->store_list = g_list_prepend (sc->store_list, store); e_table_model_row_inserted (sc->store_model, 0); } } static void populate_store_foreach (MailConfigService *service, SubscribeDialog *sc) { g_return_if_fail (service->url != NULL); subscribe_do_get_store (sc, service->url, store_cb, NULL); } static void populate_store_list (SubscribeDialog *sc) { const GSList *news; GSList *sources; sources = mail_config_get_sources (); g_slist_foreach (sources, (GFunc)populate_store_foreach, sc); g_slist_free (sources); news = mail_config_get_news (); g_slist_foreach ((GSList *)news, (GFunc)populate_store_foreach, sc); e_table_model_changed (sc->store_model); } static void subscribe_dialog_gui_init (SubscribeDialog *sc) { ETableExtras *extras; ECell *cell; GdkPixbuf *toggles[2]; BonoboUIComponent *component; BonoboUIContainer *container; GtkWidget *folder_search_widget; BonoboControl *search_control; CORBA_Environment ev; CORBA_exception_init (&ev); /* Construct the app */ sc->app = bonobo_window_new ("subscribe-dialog", _("Manage Subscriptions")); /* Build the menu and toolbar */ container = bonobo_ui_container_new (); bonobo_ui_container_set_win (container, BONOBO_WINDOW (sc->app)); /* set up the bonobo stuff */ component = bonobo_ui_component_new_default (); bonobo_ui_component_set_container ( component, bonobo_object_corba_objref (BONOBO_OBJECT (container))); bonobo_ui_component_add_verb_list_with_data ( component, verbs, sc); bonobo_ui_component_freeze (component, NULL); bonobo_ui_util_set_ui (component, EVOLUTION_DATADIR, "evolution-subscribe.xml", "evolution-subscribe"); e_pixmaps_update (component, pixmaps); bonobo_ui_component_thaw (component, NULL); sc->table = gtk_table_new (1, 2, FALSE); sc->hpaned = e_hpaned_new (); folder_search_widget = make_folder_search_widget (subscribe_search, sc); gtk_widget_show_all (folder_search_widget); search_control = bonobo_control_new (folder_search_widget); bonobo_ui_component_object_set (component, "/Searchbar/FolderSearch", bonobo_object_corba_objref (BONOBO_OBJECT (search_control)), NULL); /* set our our contents */ #if 0 sc->description = html_new (TRUE); put_html (GTK_HTML (sc->description), EXAMPLE_DESCR); gtk_table_attach (GTK_TABLE (sc->table), sc->description->parent->parent, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); #endif /* set up the store etable */ sc->store_model = e_table_simple_new (store_etable_col_count, store_etable_row_count, store_etable_value_at, store_etable_set_value_at, store_etable_is_editable, store_etable_duplicate_value, store_etable_free_value, store_etable_initialize_value, store_etable_value_is_empty, store_etable_value_to_string, sc); extras = e_table_extras_new (); sc->store_etable = e_table_scrolled_new (E_TABLE_MODEL(sc->store_model), extras, STORE_ETABLE_SPEC, NULL); gtk_object_sink (GTK_OBJECT (extras)); gtk_signal_connect (GTK_OBJECT (e_table_scrolled_get_table(E_TABLE_SCROLLED (sc->store_etable))), "cursor_change", GTK_SIGNAL_FUNC (storage_selected_cb), sc); /* set up the folder etable */ sc->folder_model = e_tree_memory_callbacks_new (folder_etree_icon_at, folder_etree_column_count, NULL, NULL, NULL, NULL, folder_etree_value_at, folder_etree_set_value_at, folder_etree_is_editable, folder_etree_duplicate_value, folder_etree_free_value, folder_etree_init_value, folder_etree_value_is_empty, folder_etree_value_to_string, sc); e_tree_memory_set_expanded_default (E_TREE_MEMORY(sc->folder_model), TRUE); sc->folder_root = e_tree_memory_node_insert (E_TREE_MEMORY(sc->folder_model), NULL, 0, NULL); toggles[0] = gdk_pixbuf_new_from_xpm_data ((const char **)empty_xpm); toggles[1] = gdk_pixbuf_new_from_xpm_data ((const char **)mark_xpm); extras = e_table_extras_new (); cell = e_cell_text_new(NULL, GTK_JUSTIFY_LEFT); e_table_extras_add_cell (extras, "cell_text", cell); e_table_extras_add_cell (extras, "cell_toggle", e_cell_toggle_new (0, 2, toggles)); e_table_extras_add_cell (extras, "cell_tree", e_cell_tree_new(NULL, NULL, TRUE, cell)); gtk_object_set (GTK_OBJECT (cell), "bold_column", FOLDER_COL_SUBSCRIBED, NULL); e_table_extras_add_pixbuf (extras, "subscribed-image", toggles[1]); sc->folder_etree = e_tree_scrolled_new (E_TREE_MODEL(sc->folder_model), extras, FOLDER_ETREE_SPEC, NULL); e_tree_root_node_set_visible (e_tree_scrolled_get_tree(E_TREE_SCROLLED(sc->folder_etree)), FALSE); gtk_object_sink (GTK_OBJECT (extras)); gdk_pixbuf_unref(toggles[0]); gdk_pixbuf_unref(toggles[1]); gtk_signal_connect (GTK_OBJECT (e_tree_scrolled_get_tree(E_TREE_SCROLLED (sc->folder_etree))), "double_click", GTK_SIGNAL_FUNC (folder_toggle_cb), sc); gtk_table_attach ( GTK_TABLE (sc->table), sc->folder_etree, 0, 1, 1, 3, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0); e_paned_add1 (E_PANED (sc->hpaned), sc->store_etable); e_paned_add2 (E_PANED (sc->hpaned), sc->table); e_paned_set_position (E_PANED (sc->hpaned), DEFAULT_STORE_TABLE_WIDTH); bonobo_window_set_contents (BONOBO_WINDOW (sc->app), sc->hpaned); #if 0 gtk_widget_show (sc->description); #endif gtk_widget_show (sc->folder_etree); gtk_widget_show (sc->table); gtk_widget_show (sc->store_etable); gtk_widget_show (sc->hpaned); /* FIXME: Session management and stuff? */ gtk_window_set_default_size (GTK_WINDOW (sc->app), DEFAULT_WIDTH, DEFAULT_HEIGHT); populate_store_list (sc); } static void subscribe_dialog_destroy (GtkObject *object) { SubscribeDialog *sc; sc = SUBSCRIBE_DIALOG (object); /* free our folder information */ e_tree_memory_node_remove (E_TREE_MEMORY(sc->folder_model), sc->folder_root); gtk_object_unref (GTK_OBJECT (sc->folder_model)); if (sc->folder_info) camel_store_free_folder_info (sc->store, sc->folder_info); /* free our store information */ gtk_object_unref (GTK_OBJECT (sc->store_model)); g_list_foreach (sc->store_list, (GFunc)gtk_object_unref, NULL); /* free our storage */ if (sc->storage) gtk_object_unref (GTK_OBJECT (sc->storage)); /* free our search */ if (sc->search_top) g_free (sc->search_top); subscribe_dialog_parent_class->destroy (object); } static void subscribe_dialog_class_init (GtkObjectClass *object_class) { object_class->destroy = subscribe_dialog_destroy; subscribe_dialog_parent_class = gtk_type_class (PARENT_TYPE); } static void subscribe_dialog_init (GtkObject *object) { } static void subscribe_dialog_construct (GtkObject *object, GNOME_Evolution_Shell shell) { SubscribeDialog *sc = SUBSCRIBE_DIALOG (object); /* * Our instance data */ sc->shell = shell; sc->store = NULL; sc->storage = NULL; sc->folder_info = NULL; sc->store_list = NULL; sc->search_top = NULL; subscribe_dialog_gui_init (sc); } GtkWidget * subscribe_dialog_new (GNOME_Evolution_Shell shell) { SubscribeDialog *subscribe_dialog; subscribe_dialog = gtk_type_new (subscribe_dialog_get_type ()); subscribe_dialog_construct (GTK_OBJECT (subscribe_dialog), shell); return GTK_WIDGET (subscribe_dialog->app); } E_MAKE_TYPE (subscribe_dialog, "SubscribeDialog", SubscribeDialog, subscribe_dialog_class_init, subscribe_dialog_init, PARENT_TYPE);