
2001-07-02 Sam Creasey <sammy@oh.verio.com> * mail-account-editor-news.c: Added an MailAccountEditorNews, for NNTP configuratuion. Based on MailAccountEditor, but stripped. * Makefile.am: added mail-account-editor-news.c and mail-account-editor-news.h to SOURCES if ENABLE_NNTP is defined. * mail-accounts.c (load_news): Moved this function, and fixed some slight brokenness. (news_edit): Added functional code using MailAccountEditorNews (news_add): Added functional code using news_edit after allocation. * mail-config.glade: news_editor_window widget added. Used by MailAccountEditorNews. * mail-display.c (save_data_cb): Store the pathname used when saving messages so that the next save box will default to the previous path. * message-browser.c (message_browser_new): add signal handler for size_allocate on the message browser. Thus new windows are size as they were last allocated. (message_browser_size_allocate_cb): handler to store allocations. * message-list.c (message_list_setup_etree): connect to the info_changed signals for the state of the message_list->tree. Save the folder state to disk, so that when additional message_lists are created, they are consistant. e.g. the next buttons do the same thing in the browser, and in the message viewer after changing sorting options. * subscribe-dialog.c (build_tree): freeze sc->folder model while building the tree. Not doing so takes a very long time over 40000 newsgroups. svn path=/trunk/; revision=10719
1109 lines
29 KiB
C
1109 lines
29 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||
/* subscribe-dialog.c: Subscribe dialog */
|
||
/*
|
||
* Authors: Chris Toshok <toshok@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 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 <bonobo/bonobo-main.h>
|
||
#include <bonobo/bonobo-object.h>
|
||
#include <bonobo/bonobo-generic-factory.h>
|
||
#include <bonobo/bonobo-control.h>
|
||
#include <bonobo/bonobo-ui-component.h>
|
||
#include <bonobo/bonobo-ui-util.h>
|
||
#include <bonobo/bonobo-widget.h>
|
||
|
||
#include <gtkhtml/gtkhtml.h>
|
||
#include <gal/util/e-util.h>
|
||
#include <gal/widgets/e-unicode.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-table-scrolled.h>
|
||
#include <gal/e-table/e-table-simple.h>
|
||
#include <gal/e-table/e-table.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 <gal/e-paned/e-hpaned.h>
|
||
|
||
#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 "<ETableSpecification cursor-mode=\"line\"> \
|
||
<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>"
|
||
|
||
#define STORE_ETABLE_SPEC "<ETableSpecification cursor-mode=\"line\"> \
|
||
<ETableColumn model_col=\"0\" _title=\"Store\" expansion=\"1.0\" minimum_width=\"20\" resizable=\"true\" cell=\"string\" compare=\"string\"/> \
|
||
<ETableState> \
|
||
<column source=\"0\"/> \
|
||
<grouping></grouping> \
|
||
</ETableState> \
|
||
</ETableSpecification>"
|
||
|
||
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, "<HTML><BODY>", 12);
|
||
gtk_html_write (html, handle, text, strlen (text));
|
||
gtk_html_write (html, handle, "</BODY></HTML>", 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);
|
||
|