/* sunone-folder-tree.c
 *
 * Copyright (C) 2002-2004 Sun Microsystems, Inc
 *
 * AUTHORS
 *     Harry Lu <harry.lu@sun.com>
 *     Alfred Peng <alfred.peng@sun.com>
 *     Jedy Wang <jedy.wang@sun.com>
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include <e-util/e-dialog-utils.h>
#include <e-util/e-icon-factory.h>
#include <e-util/e-popup.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glib/gi18n.h>

#include "sunone-config-listener.h"
#include "sunone-permissions-dialog.h"
#include "sunone-subscription-dialog.h"
#include "sunone-invitation-list.h"
#include "sunone-offline-listener.h"

#include "sunone-folder-tree.h"

#define d(x)

static GObjectClass *parent_class = NULL;
static GHashTable *full_hash = NULL;

struct _SunOneFolderTreePrivate {
	GtkTreeView *treeview;
	GtkTreeStore *treeview_model;
	GtkWidget *scrolled;
	GtkNotebook *body;

	GList *invitation_models;
	GHashTable *uri_to_page;
};

static GType col_types[] = {
	G_TYPE_STRING,
	G_TYPE_POINTER,
	G_TYPE_STRING,
	G_TYPE_STRING,
	G_TYPE_STRING,
	G_TYPE_UINT,
	G_TYPE_BOOLEAN,
	G_TYPE_BOOLEAN
};

static gboolean sft_tree_button_press (GtkTreeView *treeview, GdkEventButton *event, SunOneFolderTree *tree);
static gboolean sft_tree_popup_menu (GtkWidget *widget, SunOneFolderTree *tree);

G_DEFINE_TYPE (SunOneFolderTree, sunone_folder_tree, G_TYPE_OBJECT)

static void
destroy_page (gpointer key, gpointer value, gpointer user_data)
{
	g_free (key);
	g_object_unref (GTK_WIDGET (value));
}

static void
dispose (GObject *object)
{
	SunOneFolderTree *tree = SUNONE_FOLDER_TREE (object);
	SunOneFolderTreePrivate *priv = tree->priv;
	GList *l;

	if (priv) {
		for (l = priv->invitation_models; l != NULL; l = l->next)
			g_object_unref (G_OBJECT (l->data));
		g_list_free (priv->invitation_models);
		priv->invitation_models = NULL;

		if (priv->uri_to_page) {
			g_hash_table_foreach (priv->uri_to_page, destroy_page, NULL);
			g_hash_table_destroy (priv->uri_to_page);
			priv->uri_to_page = NULL;
		}

		g_free (priv);
		tree->priv = NULL;
	}

	G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void
finalize (GObject *object)
{
	G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
sunone_folder_tree_class_init (SunOneFolderTreeClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = dispose;
	object_class->finalize = finalize;
}

static void
sunone_folder_tree_init (SunOneFolderTree *backend)
{
	SunOneFolderTreePrivate *priv;

	backend->priv = g_new0 (SunOneFolderTreePrivate, 1);
	priv = backend->priv;
	priv->invitation_models = NULL;
	priv->uri_to_page = g_hash_table_new (g_str_hash, g_str_equal);
}

static GtkWidget *
sunone_folder_tree_view_new (SunOneFolderTree *tree, const char *physical_uri)
{
	SunOneInvitationListModel *model;
	GtkWidget *widget = NULL;
	
	model = sunone_folder_tree_get_invitation_list_model (tree, physical_uri);
	g_return_val_if_fail (model, NULL);

	widget = sunone_invitation_list_new (model);
	gtk_widget_show (widget);
	g_object_ref (widget);

	return widget;
}

static GtkWidget *
sunone_folder_tree_no_view ()
{
	static GtkWidget *label = NULL;

	if (!label) {
		label = gtk_label_new (_("To setup a new account, please goto Edit->Preferences->Mail accounts\nand select Sun Calendar WCAP as server type.\n\nThis view is used to create or delete calendar/tasks folders,\nmanage folder permissions and subscriptions, show your invitations.\nPlease switch to Calendars/Tasks view to see the calendars/tasks."));
		gtk_label_set_justify ((GtkLabel *)label, GTK_JUSTIFY_CENTER);
	}
	g_object_ref (label);

	return label;
}

static gboolean
update_invitation_model (gpointer data) {
	sunone_invitation_list_model_poll_cb (data);
	return FALSE;
}

static char *
sunone_folder_tree_get_selected_uri (SunOneFolderTree *tree)
{
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
	char *physical_uri = NULL;

	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	selection = gtk_tree_view_get_selection (tree->priv->treeview);
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
		gtk_tree_model_get (model, &iter, COL_STRING_URI, &physical_uri, -1);

	return physical_uri;
}

static void
account_selection_changed (GtkTreeSelection *selection, gpointer user_data)
{
	SunOneFolderTree *view = user_data;
	SunOneFolderTreePrivate *priv = view->priv;
	GtkWidget *page;
	char *physical_uri, *type;
	int page_num;

	physical_uri = sunone_folder_tree_get_selected_uri (view);
	if (!physical_uri) {
		return;
	}

	type = sunone_util_get_parameter_from_uri (physical_uri, "type");

	page = g_hash_table_lookup (priv->uri_to_page, physical_uri);
	if (page) {
		/* we need to do a refresh on invitations view */
		page_num = gtk_notebook_page_num (priv->body, page);
		if (!strcmp (type, "invitations")) {
			SunOneInvitationListModel *model = sunone_folder_tree_get_invitation_list_model (view, physical_uri);
			if (model)
				g_idle_add (update_invitation_model, model);
		}
	}
	else {
		if (!strcmp (type, "invitations"))
			page = sunone_folder_tree_view_new (view, physical_uri);
		else {
			page = sunone_folder_tree_no_view ();
			page_num = gtk_notebook_page_num (priv->body, page);
			if (page_num != -1)
				gtk_notebook_remove_page (priv->body, page_num);
		}
		gtk_widget_show (page);
		gtk_notebook_append_page (priv->body, page, NULL);
		page_num = gtk_notebook_page_num (priv->body, page);

		g_hash_table_insert (priv->uri_to_page, g_strdup (physical_uri), page);
	}

	g_free (type);
	g_free (physical_uri);

	gtk_notebook_set_current_page (priv->body, page_num);
}

static void
full_hash_insert (SunOneFolderTree *tree, GtkTreeIter *iter, const char *full_name)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	GtkTreeRowReference *uri_row;
	GtkTreePath *path;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	if (!full_hash)
		full_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)gtk_tree_row_reference_free);

	path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->treeview_model), iter);
	uri_row = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->treeview_model), path);
	gtk_tree_path_free (path);

	g_hash_table_insert (full_hash, g_strdup (full_name), uri_row);
}

static void
add_folder_tree_item (SunOneFolderTree *tree, SunOneAccount *account, GtkTreeIter *iter,
		const char *calid, const char *display_name, gboolean personal)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	GtkTreeIter iter1, iter2;
	SunOneInvitationListModel *model;
	char *full_name;
	char *physical_uri;
	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
	
	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));
	g_return_if_fail (account != NULL);

	/* create the invitation list model now */
	physical_uri = sunone_account_get_physical_uri (account, calid, "invitations");
	g_static_mutex_lock (&mutex);
	model = SUNONE_INVITATION_LIST_MODEL (sunone_invitation_list_model_new (physical_uri));
	g_static_mutex_unlock (&mutex);
	g_free (physical_uri);
	g_return_if_fail (model != NULL);
	priv->invitation_models = g_list_prepend (priv->invitation_models, model);

	full_name = g_strdup_printf ("/%s/%s/", sunone_account_get_name (account), calid);
	physical_uri = sunone_account_get_physical_uri (account, calid, "folder");
	gtk_tree_store_append (priv->treeview_model, &iter1, iter);
	gtk_tree_store_set (priv->treeview_model, &iter1,
				COL_STRING_DISPLAY_NAME, display_name,
				COL_POINTER_ACCOUNT, account,
				COL_STRING_FOLDER_TYPE, "folder",
				COL_STRING_URI, physical_uri,
				COL_STRING_FULLNAME, full_name,
				COL_BOOL_IS_STORE, TRUE, 
				COL_BOOL_IS_ACCOUNT, FALSE,
				COL_BOOL_IS_PERSONAL, personal,
				-1);
	g_free (physical_uri);
	full_hash_insert (tree, &iter1, full_name);
	g_free (full_name);

	full_name = g_strdup_printf ("/%s/%s/invitations", sunone_account_get_name (account), calid);
	physical_uri = sunone_account_get_physical_uri (account, calid, "invitations");
	gtk_tree_store_append (priv->treeview_model, &iter2, &iter1);
	gtk_tree_store_set (priv->treeview_model, &iter2,
				COL_STRING_DISPLAY_NAME, "Invitations",
				COL_POINTER_ACCOUNT, account,
				COL_STRING_FOLDER_TYPE, "invitations",
				COL_STRING_URI, physical_uri,
				COL_STRING_FULLNAME, full_name,
				COL_BOOL_IS_STORE, FALSE, 
				COL_BOOL_IS_ACCOUNT, FALSE,
				COL_BOOL_IS_PERSONAL, FALSE,
				-1); 
	g_free (physical_uri);
	full_hash_insert (tree, &iter2, full_name);
	g_free (full_name);
}

static void
add_folder_tree (SunOneFolderTree *tree, SunOneAccount *account, GtkTreeIter *iter,
		SunOneConnectionPreferences *prefs)
{
	GList *l;
	SunOneCalendarSubscription *sub;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));
	g_return_if_fail (account != NULL);

	for (l = prefs->subscriptions; l != NULL; l = l->next) {
		sub = (SunOneCalendarSubscription *) l->data;
		if (!sub)
			continue;

		if (!strcasecmp (sub->calid, prefs->default_calendar)) {
			add_folder_tree_item (tree, account, iter, sub->calid,
					sub->display_name && *(sub->display_name) ? sub->display_name : sub->calid, TRUE);
		} else {
			add_folder_tree_item (tree, account, iter, sub->calid,
					sub->display_name && *(sub->display_name) ? sub->display_name : sub->calid, FALSE);
		}
	}
}

static void
sunone_folder_tree_connect (SunOneFolderTree *tree, SunOneAccount *account,
		GtkTreeIter *iter)
{
	SunOneConnectionPreferences *prefs;
	SunOneConnection *cnc;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	sunone_account_set_connecting (account, TRUE);
	cnc = sunone_account_get_connection (account);
	if (!cnc) {
		sunone_account_set_connecting (account, FALSE);
		return;
	}

	prefs = sunone_connection_get_preferences (cnc);
	if (!prefs) {
		sunone_account_set_connecting (account, FALSE);
		return;
	}

	/* add tree items to sunone folder tree */
	add_folder_tree (tree, account, iter, prefs);
	/* add sources to Calendar and Task */
	add_sources (account, prefs);
	
	sunone_connection_free_preferences (prefs);
	sunone_account_set_connecting (account, FALSE);
}

static void
remove_calendar (SunOneFolderTree *tree, const char *full_name)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	GtkTreeStore *model;
	GtkTreeRowReference *uri_row;
	GtkTreePath *path;
	GtkTreeIter iter;
	GtkTreeIter child;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	if (!full_hash)
		return;
	uri_row = g_hash_table_lookup (full_hash, full_name);
	if (!uri_row)
		return;

	model = priv->treeview_model;
	path = gtk_tree_row_reference_get_path (uri_row);
	gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
	/*remove all children*/
	gtk_tree_model_iter_nth_child(GTK_TREE_MODEL (model), &child, &iter, 0);
	while (gtk_tree_store_iter_is_valid (model, &child)) {
		char *child_full_name;

		gtk_tree_model_get (GTK_TREE_MODEL (model), &child, COL_STRING_FULLNAME, &child_full_name, -1);
		gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &child);
		remove_calendar (tree, child_full_name);
		g_hash_table_remove (full_hash, child_full_name);
		g_free (child_full_name);
	}
	gtk_tree_store_remove (model, &iter);
	gtk_tree_path_free (path);
	g_hash_table_remove (full_hash, full_name);
}

typedef struct 
{
	SunOneFolderTree *tree;
	SunOneAccount *account;
	GtkTreeIter iter;
} TreeCreateData;

static void
create_folder_tree_with_thread (TreeCreateData *data)
{
	SunOneFolderTreePrivate *priv = data->tree->priv;
	SunOneAccount *account = data->account;
	GtkTreeIter iter = data->iter;
	GtkTreeIter sub_iter;
	GtkTreePath *path;

	gtk_tree_store_append (priv->treeview_model, &sub_iter, &iter);
	gtk_tree_store_set (priv->treeview_model, &sub_iter,
				COL_STRING_DISPLAY_NAME, _("Connecting..."),
				COL_POINTER_ACCOUNT, NULL,
				COL_STRING_FOLDER_TYPE, "",
				COL_STRING_URI, NULL,
				COL_STRING_FULLNAME, NULL,
				COL_BOOL_IS_STORE, TRUE, 
				COL_BOOL_IS_ACCOUNT, FALSE,
				COL_BOOL_IS_PERSONAL, FALSE,
				-1);
	path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->treeview_model), &iter);
	gtk_tree_view_expand_row (priv->treeview, path, FALSE);
	gtk_tree_path_free (path);

	sunone_folder_tree_connect (data->tree, account, &iter);

	gtk_tree_store_remove (priv->treeview_model, &sub_iter);
	g_object_unref (data->account);
	g_free (data);

	sunone_folder_tree_sync ();
}

void
sunone_folder_tree_created (SunOneFolderTree *tree, SunOneAccount *account)
{
	SunOneFolderTreePrivate *priv;
	TreeCreateData *data;
	GtkTreeIter iter;
	GThread *thread = NULL;
	GError *error = NULL;
	char *physical_uri;
	char *full_name;
	int mode;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));
	g_return_if_fail (account != NULL);

	priv = tree->priv;
	/*remove old tree, if exists*/
	full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account));
	remove_calendar (tree, full_name);

	physical_uri = sunone_account_get_physical_uri (account, sunone_account_get_user (account), "folder");
	gtk_tree_store_append (priv->treeview_model, &iter, NULL);
	gtk_tree_store_set (priv->treeview_model, &iter,
				COL_STRING_DISPLAY_NAME, sunone_account_get_name (account),
				COL_POINTER_ACCOUNT, account,
				COL_STRING_FOLDER_TYPE, "folder",
				COL_STRING_URI, physical_uri,
				COL_STRING_FULLNAME, full_name,
				COL_BOOL_IS_STORE, TRUE, 
				COL_BOOL_IS_ACCOUNT, TRUE,
				COL_BOOL_IS_PERSONAL, FALSE,
				-1);
	g_free (physical_uri);

	full_hash_insert (tree, &iter, full_name);
	g_free (full_name);

	/*only connect if we are online */
	sunone_account_is_offline (account, &mode);
	if (mode == ONLINE_MODE) {
		data = g_malloc (sizeof (TreeCreateData));
		data->tree = tree;
		data->account = g_object_ref (account);
		data->iter = iter;
		thread = g_thread_create ((GThreadFunc)create_folder_tree_with_thread, data, FALSE, &error);
		if (!thread) {
			g_warning (G_STRLOC ": %s", error->message);
			g_error_free (error);
			g_object_unref (data->account);
			g_free (data);
		}
	}
}

void
sunone_folder_tree_removed (SunOneFolderTree *tree, SunOneAccount *account)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	char *full_name;
	GtkWidget *page;
	int page_num;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account));
	remove_calendar (tree, full_name);
	g_free (full_name);

	remove_source (account, sunone_account_get_user (account), TRUE);

	page = sunone_folder_tree_no_view ();
	page_num = gtk_notebook_page_num (priv->body, page);
	if (page_num != -1)
		gtk_notebook_remove_page (priv->body, page_num);
	gtk_widget_show (page);
	gtk_notebook_append_page (priv->body, page, NULL);
	page_num = gtk_notebook_page_num (priv->body, page);
	gtk_notebook_set_current_page (priv->body, page_num);
}

void
sunone_folder_tree_remove_calendar (SunOneFolderTree *tree, SunOneAccount *account, const char *calid)
{
	char *full_name;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));
	g_return_if_fail (account != NULL);

	full_name = g_strdup_printf ("/%s/%s/", sunone_account_get_name (account), calid);
	remove_calendar (tree, full_name);
	g_free (full_name);

	remove_source (account, calid, FALSE);
}

void
sunone_folder_tree_add_calendar (SunOneFolderTree *tree, SunOneAccount *account,
			const char *calid, const char *display_name)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	GtkTreeRowReference *uri_row;
	GtkTreePath *path;
	GtkTreeIter iter;
	char *full_name;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account));
	uri_row = g_hash_table_lookup (full_hash, full_name);
	g_free (full_name);
	path = gtk_tree_row_reference_get_path (uri_row);
	gtk_tree_model_get_iter ((GtkTreeModel *)priv->treeview_model, &iter, path);
	gtk_tree_path_free (path);
	
	add_folder_tree_item (tree, account, &iter, calid, display_name, FALSE);
	add_source (account, calid, display_name, FALSE);
}

void
sunone_folder_tree_rename_calendar (SunOneFolderTree *tree, SunOneAccount *account,
			const char *calid, const char *display_name)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	GtkTreeRowReference *uri_row;
	GtkTreePath *path;
	GtkTreeIter iter;
	char *full_name;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));
	g_return_if_fail (account != NULL);

	/* remove old folder */
	full_name = g_strdup_printf ("/%s/%s/", sunone_account_get_name (account), calid);
	remove_calendar (tree, full_name);
	g_free (full_name);

	/* add new folder */
	full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account));
	uri_row = g_hash_table_lookup (full_hash, full_name);
	g_free (full_name);
	path = gtk_tree_row_reference_get_path (uri_row);
	gtk_tree_model_get_iter ((GtkTreeModel *)priv->treeview_model, &iter, path);
	
	add_folder_tree_item (tree, account, &iter, calid, display_name, FALSE);

	/* rename source */
	rename_source (account, calid, display_name, FALSE);
}

enum {
	FOLDER_ICON_NORMAL,
	FOLDER_ICON_INVITATION,
	FOLDER_ICON_LAST
};

static GdkPixbuf *folder_icons[FOLDER_ICON_LAST];

static void render_pixbuf (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
		GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
	static gboolean initialised = FALSE;
	GdkPixbuf *pixbuf = NULL;
	gboolean is_store;
	char *folder_type;

	if (!initialised) {
		folder_icons[FOLDER_ICON_NORMAL] = e_icon_factory_get_icon ("stock_normal", E_ICON_SIZE_MENU);
		folder_icons[FOLDER_ICON_INVITATION] = e_icon_factory_get_icon ("stock_new-meeting", E_ICON_SIZE_MENU);
		initialised = TRUE;
	}

	gtk_tree_model_get (model, iter, COL_STRING_FOLDER_TYPE, &folder_type,
				COL_BOOL_IS_STORE, &is_store, -1);

	if (!is_store && folder_type != NULL) {
		if (!strcmp (folder_type, "invitations"))
			pixbuf = folder_icons[FOLDER_ICON_INVITATION];
		else
			pixbuf = folder_icons[FOLDER_ICON_NORMAL];
	}
	
	g_object_set (renderer, "pixbuf", pixbuf, "visible", !is_store, NULL);
	g_free (folder_type);
}

static void
render_display_name (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
		     GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
	gboolean is_account, is_store, bold = FALSE;
	char *name, *physical_uri, *folder_type, *display;
	SunOneFolderTree *tree = user_data;
	SunOneAccount *account;
	
	gtk_tree_model_get (model, iter, COL_STRING_DISPLAY_NAME, &name,
				COL_BOOL_IS_STORE, &is_store,
				COL_BOOL_IS_ACCOUNT, &is_account, 
				COL_STRING_URI, &physical_uri, 
				COL_STRING_FOLDER_TYPE, &folder_type, 
				COL_POINTER_ACCOUNT, &account,
				-1);

	display = name;

	if (folder_type && !strcmp ("invitations", folder_type)) {
		/* check unread count for invitation list */
		SunOneInvitationListModel *model;

		model = sunone_folder_tree_get_invitation_list_model (tree, physical_uri);
		if (model) {
			gint count =  gtk_tree_model_iter_n_children ((GtkTreeModel *)model, NULL);
			if (count > 0) {
				display = g_strdup_printf ("%s (%u)", name, count);
				g_free (name);
				bold = TRUE;
			}
		}
	}

	if (!bold)
		bold = is_store;

	if (is_account) {
		int state;

		sunone_account_is_offline (account, &state);
		if (state == OFFLINE_MODE) {
			display = g_strdup_printf ("%s (%s)", name, _("Offline"));
			g_free (name);
		}
	}
	
	g_object_set (renderer, "text", display,
		      "weight", bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
		      NULL);
	
	g_free (display);
	g_free (folder_type);
	g_free (physical_uri);
}

SunOneFolderTree *
sunone_folder_tree_new_with_model (GtkTreeModel *model)
{
	SunOneFolderTree *tree;
	SunOneFolderTreePrivate *priv;
	GtkTreeViewColumn *column;
	GtkTreeSelection *selection;
	GtkWidget *widget, *scrolled;
	GtkCellRenderer *renderer;

	tree = g_object_new (SUNONE_TYPE_FOLDER_TREE, NULL);
	priv = tree->priv;

	widget = gtk_notebook_new ();
	gtk_widget_show (widget);
	priv->body = GTK_NOTEBOOK (widget);
	gtk_notebook_set_show_tabs (priv->body, FALSE);
	gtk_notebook_set_show_border (priv->body, FALSE);
	gtk_notebook_append_page (priv->body, gtk_drawing_area_new (), NULL);

	priv->treeview_model = GTK_TREE_STORE (model);

	widget = gtk_tree_view_new_with_model (model);
	GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);

	column = gtk_tree_view_column_new ();
	gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);

	renderer = gtk_cell_renderer_pixbuf_new ();
	gtk_tree_view_column_pack_start (column, renderer, FALSE);
	gtk_tree_view_column_set_cell_data_func (column, renderer, render_pixbuf, NULL, NULL);

	renderer = gtk_cell_renderer_text_new ();
	gtk_tree_view_column_pack_start (column, renderer, TRUE);
	gtk_tree_view_column_set_cell_data_func (column, renderer, render_display_name, tree, NULL);

	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);

	gtk_tree_view_set_search_column (GTK_TREE_VIEW (widget), COL_STRING_DISPLAY_NAME);

	gtk_widget_show (widget);
	priv->treeview = GTK_TREE_VIEW (widget);

	g_signal_connect (priv->treeview, "button-press-event", G_CALLBACK (sft_tree_button_press), tree);
	g_signal_connect (priv->treeview, "popup_menu", G_CALLBACK (sft_tree_popup_menu), tree);

	scrolled = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
	gtk_container_add (GTK_CONTAINER (scrolled), widget);
	gtk_widget_show (scrolled);
	priv->scrolled = scrolled;

	selection = gtk_tree_view_get_selection (priv->treeview);
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
	g_signal_connect (selection, "changed",
			  G_CALLBACK (account_selection_changed), tree);

	return tree;
}

static void
error_handler (guint errcode)
{
	switch (errcode) {
	case SUNONE_ERROR_ILLEGAL_CALID_NAME:
		e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Cannot create a folder with that name"));
		break;
	case SUNONE_ERROR_CANNOT_MODIFY_LINKED_OBJECTS:
		e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Operation not supported"));
		break;
	case SUNONE_ERROR_ACCESS_DENIED:
	case SUNONE_ERROR_MUSTBEOWNER_OPERATION:
	case SUNONE_ERROR_NOT_ALLOWED_TO_REQUEST_PUBLISH:
		e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Permission denied"));
		break;
	default:
		e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Generic error"));
		break;
	}
}

SunOneFolderTree *
sunone_folder_tree_new (void)
{
	SunOneFolderTree *tree;
	GtkTreeModel *model;

	model = GTK_TREE_MODEL (gtk_tree_store_newv (NUM_COLUMNS, col_types));
	tree = sunone_folder_tree_new_with_model (model);

	return tree;
}


static void
name_entry_changed (GtkEntry *entry, GtkWidget *dialog)
{
	const char *text = NULL;
	gboolean active;

	if (entry->text_length > 0)
		text = gtk_entry_get_text (entry);

	active = text && !strchr (text, '/');
	gtk_dialog_set_response_sensitive ((GtkDialog *) dialog, GTK_RESPONSE_OK, active);
}

static gboolean
check_offline (SunOneAccount *account, GtkWidget *parent)
{
	int state;

	sunone_account_is_offline (account, &state);
	if (state == OFFLINE_MODE) {
		e_notice (parent, GTK_MESSAGE_ERROR, 
			_("This operation cannot be performed in offline mode"));
		return FALSE;
	}

	return TRUE;

}
static void
sft_popup_new_folder_response (GtkWidget *dialog, int response, SunOneFolderTree *tree)
{
	GtkEntry *entry;
	SunOneAccount *account;
	guint errcode;
	const char *name;
	char *real_calid = NULL;
	char *p;
	SunOneConnection *cnc;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	if (response != GTK_RESPONSE_OK) {
		gtk_widget_destroy (dialog);
		return;
	}

	account = sunone_folder_tree_get_account (tree);
	if (!check_offline (account, dialog))
		return;
	
	cnc = sunone_account_get_connection (account);
	if (!cnc) {
		gtk_widget_destroy (dialog);
		return;
	}

	entry = g_object_get_data (G_OBJECT (dialog), "entry");
	name = gtk_entry_get_text (entry);
	for (p = (char *)name; *p; p++) {
		/* don't allow illegal charactors in the calendar name */
		if (!((*p >= 'a' && *p <= 'z')
				|| (*p >= 'A' && *p <= 'Z')
				|| (*p >= '0' && *p <= '9')
				|| *p == '.' || *p == '_'
				|| *p == '-' || *p == '&'
				|| *p == '!' || *p == '@'))
			*p = '_';
	}

	errcode = sunone_connection_createcalendar (cnc, name, &real_calid);
	if (SUNONE_ERROR_IS_SUCCESSFUL (errcode)) {
		sunone_folder_tree_add_calendar (tree, account, real_calid, name);
		sunone_folder_tree_sync ();
	} else {
		error_handler (errcode);
	}
	g_free (real_calid);

	gtk_widget_destroy (dialog);
}

static void
sft_popup_new_calendar (EPopup *ep, EPopupItem *item, void *data) 
{
	SunOneFolderTree *tree = data;
	GtkWidget *dialog;
	GtkWidget *prompt_label;
	GtkWidget *vbox;
	GtkWidget *entry;
	const char *title = _("Create calendar");

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	if (!sunone_folder_tree_get_selected (tree))
		return;

	dialog = gtk_dialog_new_with_buttons (title, NULL,
					GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					GTK_STOCK_OK, GTK_RESPONSE_OK,
					NULL);
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
	gtk_window_set_default_size (GTK_WINDOW (dialog), 275, -1);

	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); 

	vbox = GTK_DIALOG (dialog)->vbox;
	
	prompt_label = gtk_label_new (_("Please input the name of the new calendar:"));
	gtk_box_pack_start (GTK_BOX (vbox), prompt_label, TRUE, TRUE, 6);
	gtk_box_set_spacing (GTK_BOX (vbox), 6); 

	entry = gtk_entry_new ();
	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
	gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
	g_signal_connect (GTK_ENTRY (entry), "changed", G_CALLBACK (name_entry_changed), dialog);
	gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 3);

	g_object_set_data (G_OBJECT (dialog), "entry", entry);
	
	g_signal_connect (dialog, "response", G_CALLBACK (sft_popup_new_folder_response), tree);
	gtk_widget_grab_focus (entry);
	
	gtk_widget_show (prompt_label);
	gtk_widget_show (entry);
	gtk_widget_show (dialog);
}

static void
sft_popup_rename_folder_response (GtkWidget *dialog, int response, SunOneFolderTree *tree)
{
	SunOneAccount *account;
	SunOneCalendarProperties *calprops;
	SunOneConnectionPreferences *prefs;
	SunOneConnection *cnc;
	SunOneCalendarSubscription *sub;
	GtkWidget *entry;
	GList *l;
	guint errcode = SUNONE_ERROR_CANNOT_MODIFY_LINKED_OBJECTS;
	char *physical_uri, *calid, *type, *p;
	const char *display_name = NULL;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	if (response != GTK_RESPONSE_OK) {
		gtk_widget_destroy (dialog);
		return;
	}

	account = sunone_folder_tree_get_account (tree);
	if (!check_offline (account, dialog))
		return;
	
	cnc = sunone_account_get_connection (account);
	if (!cnc) {
		gtk_widget_destroy (dialog);
		return;
	}

	physical_uri = sunone_folder_tree_get_selected_uri (tree);
	calid = sunone_util_get_calid_from_uri (physical_uri);
	type = sunone_util_get_parameter_from_uri (physical_uri, "type");

	if (strcmp (type, "folder") || !strcmp (calid, sunone_account_get_user (account)))
		goto out;

	entry = g_object_get_data (G_OBJECT (dialog), "entry");
	display_name = gtk_entry_get_text (GTK_ENTRY (entry));
	for (p = (char *)display_name; *p; p++) {
		/* don't allow illegal charactors in the calendar name */
		if (!((*p >= 'a' && *p <= 'z')
				|| (*p >= 'A' && *p <= 'Z')
				|| (*p >= '0' && *p <= '9')
				|| *p == '.' || *p == '_'
				|| *p == '-' || *p == '&'
				|| *p == '!' || *p == '@'))
			*p = '_';
	}

	calprops = sunone_connection_get_calprops (cnc, calid, FALSE);
	if (calprops) {
		if (calprops->display_name)
			g_free (calprops->display_name);
		calprops->display_name = g_strdup (display_name);

		errcode = sunone_connection_set_calprops (cnc, calid, calprops);
		if (errcode == SUNONE_ERROR_COMMAND_SUCCESSFUL) {
			/* now, update the subscription list */
			prefs = sunone_connection_get_preferences (cnc);
			if (prefs) {
				for (l = prefs->subscriptions; l != NULL; l = l->next) {
					sub = l->data;
					if (!strcmp (calid, sub->calid)) {
						if (sub->display_name)
							g_free (sub->display_name);
						sub->display_name = g_strdup (display_name);
						break;
					}
				}

				if (!SUNONE_ERROR_IS_SUCCESSFUL (
					    sunone_connection_set_preferences (cnc, prefs)))
					errcode = SUNONE_ERROR_COMMAND_FAILED;

				sunone_connection_free_preferences (prefs);
			} else
				errcode = SUNONE_ERROR_COMMAND_FAILED;
		}

		sunone_connection_free_calprops (calprops);
	} else
		errcode = SUNONE_ERROR_COMMAND_FAILED;

out:
	if (SUNONE_ERROR_IS_SUCCESSFUL (errcode)) {
		sunone_folder_tree_rename_calendar (tree, account, calid, display_name);
		sunone_folder_tree_sync ();
	} else
		error_handler (errcode);

	g_free (physical_uri);
	g_free (type);
	g_free (calid);

	gtk_widget_destroy (dialog);
}

static void
sft_popup_rename_folder (EPopup *ep, EPopupItem *item, void *data) 
{
	SunOneFolderTree *tree = data;
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkWidget *dialog;
	GtkWidget *prompt_label;
	GtkWidget *vbox;
	GtkWidget *entry;
	const char *title = _("Rename folder");
	char *display_name, *prompt;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	selection = gtk_tree_view_get_selection (tree->priv->treeview);
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
		gtk_tree_model_get (model, &iter, COL_STRING_DISPLAY_NAME, &display_name, -1);

	prompt = g_strdup_printf (_("Rename the \"%s\" folder to:"), display_name);

	dialog = gtk_dialog_new_with_buttons (title, NULL,
					GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					GTK_STOCK_OK, GTK_RESPONSE_OK,
					NULL);
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
	gtk_window_set_default_size (GTK_WINDOW (dialog), 275, -1);

	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); 

	vbox = GTK_DIALOG (dialog)->vbox;
	
	prompt_label = gtk_label_new (prompt);
	gtk_box_pack_start (GTK_BOX (vbox), prompt_label, TRUE, TRUE, 6);
	gtk_box_set_spacing (GTK_BOX (vbox), 6); 

	entry = gtk_entry_new ();
	gtk_entry_set_text (GTK_ENTRY (entry), display_name);
	gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
	gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
	g_signal_connect (GTK_ENTRY (entry), "changed", G_CALLBACK (name_entry_changed), dialog);
	gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 3);

	g_object_set_data (G_OBJECT (dialog), "entry", entry);
	
	g_signal_connect (dialog, "response", G_CALLBACK (sft_popup_rename_folder_response), tree);
	gtk_widget_grab_focus (entry);
	
	g_free (display_name);
	g_free (prompt);
	
	gtk_widget_show (prompt_label);
	gtk_widget_show (entry);
	gtk_widget_show (dialog);
}

static void
sft_popup_delete_folder_response (GtkWidget *dialog, int response, SunOneFolderTree *tree)
{
	SunOneAccount *account;
	SunOneConnection *cnc;
	guint errcode;
	char *physical_uri, *calid;
	
	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	if (response != GTK_RESPONSE_OK) {
		gtk_widget_destroy (dialog);
		return;
	}

	account = sunone_folder_tree_get_account (tree);
	if (!check_offline (account, dialog))
		return;
	
	cnc = sunone_account_get_connection (account);

	if (!cnc) {
		gtk_widget_destroy (dialog);
		return;
	}

	physical_uri = sunone_folder_tree_get_selected_uri (tree);
	calid = sunone_util_get_calid_from_uri (physical_uri);

	errcode = sunone_connection_deletecalendar (cnc, calid);
	if (SUNONE_ERROR_IS_SUCCESSFUL (errcode)) {
		sunone_folder_tree_remove_calendar (tree, account, calid);
		sunone_folder_tree_sync ();
	} else {
		error_handler (errcode);
	}

	g_free (physical_uri);
	g_free (calid);

	gtk_widget_destroy (dialog);
}

static void
sft_popup_delete_folder (EPopup *ep, EPopupItem *item, void *data) 
{
	SunOneFolderTree *tree = data;
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkWidget *dialog;
	char *display_name;
	char *title;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	selection = gtk_tree_view_get_selection (tree->priv->treeview);
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
		gtk_tree_model_get (model, &iter, COL_STRING_DISPLAY_NAME, &display_name, -1);


	dialog = gtk_message_dialog_new (NULL,
				GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
				GTK_MESSAGE_QUESTION,
				GTK_BUTTONS_NONE,
				(_("Really delete folder \"%s\"?")), display_name);

	gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
	gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_DELETE, GTK_RESPONSE_OK);

	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);

	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);

	title = g_strdup_printf (("Delete \"%s\""), display_name);
	gtk_window_set_title (GTK_WINDOW (dialog), title);
	g_free (title);
	g_free (display_name);

	g_signal_connect (dialog, "response", G_CALLBACK (sft_popup_delete_folder_response), tree);

	gtk_widget_show (dialog);
}

static void
sft_popup_permission (EPopup *ep, EPopupItem *item, void *data) 
{
	SunOneFolderTree *tree = data;
	SunOneFolderTreePrivate *priv = tree->priv;
	SunOneAccount *account;
	SunOneConnection *cnc;
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
	GtkWidget *dialog = NULL;
	char *type, *physical_uri;
	char *calid = NULL;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	selection = gtk_tree_view_get_selection (priv->treeview);
	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
		return;
	gtk_tree_model_get (model, &iter, COL_POINTER_ACCOUNT, &account,
				COL_STRING_FOLDER_TYPE, &type,
				COL_STRING_URI, &physical_uri,
				-1);

	g_return_if_fail (physical_uri != NULL);
	g_return_if_fail (account != NULL);

	if (!check_offline (account, NULL))
		return;
	
	if (!strcmp (type, "invitations")) {
		e_notice(NULL, GTK_MESSAGE_ERROR, ("There are no permissions to set for this folder"));
	} else {
		calid = sunone_util_get_calid_from_uri (physical_uri);

		cnc = sunone_account_get_connection (account);
		if (!cnc)
			return;

		if (calid) {
			dialog = sunone_permissions_dialog_new (cnc, calid);
			g_free (calid);
		}
	}

	g_free (physical_uri);
	g_free (type);

	if (dialog)
		gtk_widget_show (dialog);
}

static void
sft_popup_subscription (EPopup *ep, EPopupItem *item, void *data) 
{
	SunOneFolderTree *tree = data;
	SunOneAccount *account;
	SunOneConnection *cnc;
	GtkWidget *dialog = NULL;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	account = sunone_folder_tree_get_account (tree);
	if (!account)
		return;

	if (!check_offline (account, NULL))
		return;
	
	cnc = sunone_account_get_connection (account);	
	if (!cnc)
		return;

	dialog = sunone_subscription_dialog_new (tree, cnc);
	
	if (dialog)
		gtk_widget_show (dialog);
}

static void
sft_popup_login (EPopup *ep, EPopupItem *item, void *data) 
{
	SunOneFolderTree *tree = data;
	SunOneAccount *account;

	g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree));

	account = sunone_folder_tree_get_account (tree);
	if (!account)
		return;

	/* recreate the folder list */
	sunone_account_set_dont_ask_password (account, FALSE);
	sunone_account_forget_password (account);
	sunone_folder_tree_created (tree, account);
}

static EPopupItem sft_popup_menu[] = {
	{ E_POPUP_ITEM, "10.ej.00", N_("_New Calendar..."), sft_popup_new_calendar, NULL, "stock_folder",EJ_POPUP_FOLDER_NEW_CALENDAR ,0 },
	{ E_POPUP_ITEM, "10.ej.01", N_("_Rename"), sft_popup_rename_folder, NULL, NULL,EJ_POPUP_FOLDER_OP ,0 },
	{ E_POPUP_ITEM, "10.ej.02", N_("_Delete"), sft_popup_delete_folder, NULL, "stock_delete",EJ_POPUP_FOLDER_OP ,0 },

	{ E_POPUP_BAR, "20.ej" },

	{ E_POPUP_ITEM, "20.ej.00", N_("_Permissions..."), sft_popup_permission, NULL, "stock_folder-properties",EJ_POPUP_FOLDER_PERM ,0 },
	{ E_POPUP_ITEM, "20.ej.01", N_("Manage _Subscriptions..."), sft_popup_subscription, NULL, "stock_subscript",EJ_POPUP_FOLDER_SUB ,0 },
	{ E_POPUP_ITEM, "20.ej.02", N_("_Login"), sft_popup_login, NULL, NULL,EJ_POPUP_FOLDER_LOGIN ,0 },
};

static void
sunone_popup_free(EPopup *ep, GSList *items, void *data)
{
	g_slist_free(items);
}

static gboolean
sft_tree_popup (SunOneFolderTree *tree, GdkEvent *event)
{
	SunOneAccount *account;
	GtkMenu *menu;
	GSList *menus = NULL;
	int i;
	EPopup *sp;
	EPopupTarget *target;
	GtkTreeSelection * selection;
	GtkTreeModel *model;
	GtkTreeIter iter;
	gboolean is_store, is_account, is_personal;
	guint32 flags = ~0ul;

	selection = gtk_tree_view_get_selection (tree->priv->treeview);
	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
		return FALSE; 
	gtk_tree_model_get (model, &iter, COL_POINTER_ACCOUNT, &account,
				COL_BOOL_IS_STORE, &is_store,
				COL_BOOL_IS_ACCOUNT, &is_account,
				COL_BOOL_IS_PERSONAL, &is_personal,
				-1);

	if (is_account) {
		flags &= ~(EJ_POPUP_FOLDER_LOGIN | EJ_POPUP_FOLDER_SUB | EJ_POPUP_FOLDER_NEW_CALENDAR);
	} else if (is_store) {
		if (is_personal)
			flags = ~EJ_POPUP_FOLDER_PERM;
		else
			flags = ~(EJ_POPUP_FOLDER_OP | EJ_POPUP_FOLDER_PERM);
	}

	sp = e_popup_new ("org.gnome.evolution-jescs.foldertree.popup");
	
	target = e_popup_target_new(sp, 0, sizeof(*target));

	for (i = 0; i < sizeof (sft_popup_menu) /sizeof (sft_popup_menu[0]); i++) {
		menus = g_slist_prepend (menus, &sft_popup_menu[i]);
	}

	e_popup_add_items (sp, menus, NULL, sunone_popup_free, tree);
	menu = e_popup_create_menu_once (sp, target, flags);

	/* FIXME free g_list menus*/

	if (event == NULL || event->type == GDK_KEY_PRESS) {
		gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
	} else {
		gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button.button, event->button.time);
	}
	
	return TRUE;

}

static gboolean
sft_tree_button_press (GtkTreeView *treeview, GdkEventButton *event, SunOneFolderTree *tree)
{
	GtkTreePath *tree_path;

	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), FALSE);

	if (event->button != 3 && !(event->button == 1 && event->type == GDK_2BUTTON_PRESS))
		return FALSE;
	
	if (!gtk_tree_view_get_path_at_pos (treeview, (int) event->x, (int) event->y, &tree_path, NULL, NULL, NULL))
		return FALSE;
	
	/* select/focus the row that was right-clicked or double-clicked */
	gtk_tree_view_set_cursor (treeview, tree_path, NULL, FALSE);

	gtk_tree_path_free (tree_path);
	
	if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
		return TRUE;
	}

	return sft_tree_popup (tree, (GdkEvent *) event);
	

}

static gboolean
sft_tree_popup_menu (GtkWidget *widget, SunOneFolderTree *tree)
{
	return sft_tree_popup (tree, NULL);
}

gboolean
sunone_folder_tree_get_selected (SunOneFolderTree *tree)
{
	GtkTreeSelection *selection;

	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), FALSE);

	selection = gtk_tree_view_get_selection (tree->priv->treeview);
	if (gtk_tree_selection_get_selected (selection, NULL, NULL))
		return TRUE;
	return FALSE;
}

GtkWidget *
sunone_folder_tree_get_scrolled (SunOneFolderTree *tree)
{
	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	return tree->priv->scrolled;
}

GtkNotebook *
sunone_folder_tree_get_body (SunOneFolderTree *tree)
{
	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	return tree->priv->body;
}

GtkTreeView *
sunone_folder_tree_get_tree_view (SunOneFolderTree *tree)
{
	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	return tree->priv->treeview;
}

GtkTreeModel *
sunone_folder_tree_get_model (SunOneFolderTree *tree)
{
	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	return (GtkTreeModel *)tree->priv->treeview_model;
}

SunOneAccount *
sunone_folder_tree_get_account (SunOneFolderTree *tree)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	SunOneAccount *account;
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;

	g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL);

	selection = gtk_tree_view_get_selection (priv->treeview);
	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
		return NULL;
	gtk_tree_model_get (model, &iter, COL_POINTER_ACCOUNT, &account, -1);

	return account;
}

SunOneInvitationListModel *
sunone_folder_tree_get_invitation_list_model (SunOneFolderTree *tree, const char *physical_uri)
{
	SunOneFolderTreePrivate *priv = tree->priv;
	GList *l;
	
	for (l = priv->invitation_models; l != NULL; l = l->next) {
		SunOneInvitationListModel *model;

		model = l->data;
		if (sunone_invitation_list_model_get_uri (model) &&
				!strcmp (sunone_invitation_list_model_get_uri (model), physical_uri))
			return model;
	}
	
	return NULL;
}
