/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2006 Hiroyuki Yamamoto
 *
 * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "defs.h"

#include <glib.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtktreestore.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkstatusbar.h>
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkitemfactory.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkversion.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "main.h"
#include "mainwindow.h"
#include "folderview.h"
#include "summaryview.h"
#include "query_search.h"
#include "inputdialog.h"
#include "subscribedialog.h"
#include "foldersel.h"
#include "manage_window.h"
#include "alertpanel.h"
#include "menu.h"
#include "stock_pixmap.h"
#include "statusbar.h"
#include "procmsg.h"
#include "utils.h"
#include "gtkutils.h"
#include "trayicon.h"
#include "prefs_common.h"
#include "prefs_account.h"
#include "prefs_folder_item.h"
#include "prefs_search_folder.h"
#include "filter.h"
#include "account.h"
#include "account_dialog.h"
#include "folder.h"
#include "inc.h"
#include "virtual.h"

enum
{
	COL_FOLDER_NAME,
	COL_NEW,
	COL_UNREAD,
	COL_TOTAL,
	COL_FOLDER_ITEM,
	COL_PIXBUF,
	COL_PIXBUF_OPEN,
	COL_FOREGROUND,
	COL_BOLD,
	N_COLS
};

#define COL_FOLDER_WIDTH	150
#define COL_NUM_WIDTH		32

#define STATUSBAR_PUSH(mainwin, str) \
{ \
	gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
			   mainwin->folderview_cid, str); \
	gtkut_widget_draw_now(mainwin->statusbar); \
}

#define STATUSBAR_POP(mainwin) \
{ \
	gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
			  mainwin->folderview_cid); \
}

static GList *folderview_list = NULL;

static GdkPixbuf *inbox_pixbuf;
static GdkPixbuf *outbox_pixbuf;
static GdkPixbuf *folder_pixbuf;
static GdkPixbuf *folderopen_pixbuf;
static GdkPixbuf *foldernoselect_pixbuf;
static GdkPixbuf *draft_pixbuf;
static GdkPixbuf *trash_pixbuf;
static GdkPixbuf *virtual_pixbuf;

static void folderview_set_columns	(FolderView	*folderview);

static void folderview_select_row	(FolderView	*folderview,
					 GtkTreeIter	*iter);
static void folderview_select_row_ref	(FolderView	*folderview,
					 GtkTreeRowReference *row);

static void folderview_set_folders	(FolderView	*folderview);
static void folderview_append_folder	(FolderView	*folderview,
					 Folder		*folder);

static void folderview_update_row	(FolderView	*folderview,
					 GtkTreeIter	*iter);
static void folderview_update_row_all	(FolderView	*folderview);

static gint folderview_folder_name_compare	(GtkTreeModel	*model,
						 GtkTreeIter	*a,
						 GtkTreeIter	*b,
						 gpointer	 data);

/* callback functions */
static gboolean folderview_button_pressed	(GtkWidget	*treeview,
						 GdkEventButton	*event,
						 FolderView	*folderview);
static gboolean folderview_button_released	(GtkWidget	*treeview,
						 GdkEventButton	*event,
						 FolderView	*folderview);

static gboolean folderview_key_pressed	(GtkWidget	*widget,
					 GdkEventKey	*event,
					 FolderView	*folderview);

static void folderview_selection_changed(GtkTreeSelection	*selection,
					 FolderView		*folderview);

static void folderview_row_expanded	(GtkTreeView		*treeview,
					 GtkTreeIter		*iter,
					 GtkTreePath		*path,
					 FolderView		*folderview);
static void folderview_row_collapsed	(GtkTreeView		*treeview,
					 GtkTreeIter		*iter,
					 GtkTreePath		*path,
					 FolderView		*folderview);

static void folderview_popup_close	(GtkMenuShell	*menu_shell,
					 FolderView	*folderview);

static void folderview_col_resized	(GtkWidget	*widget,
					 GtkAllocation	*allocation,
					 FolderView	*folderview);

static void folderview_download_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_update_tree_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_update_summary_cb(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_mark_all_read_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_new_folder_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_rename_folder_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_move_folder_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_delete_folder_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_empty_trash_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_remove_mailbox_cb(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_rm_imap_server_cb (FolderView	*folderview,
					  guint		 action,
					  GtkWidget	*widget);

static void folderview_new_news_group_cb(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_rm_news_group_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);
static void folderview_rm_news_server_cb(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_search_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static void folderview_property_cb	(FolderView	*folderview,
					 guint		 action,
					 GtkWidget	*widget);

static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
					  GdkDragContext *context,
					  gint            x,
					  gint            y,
					  guint           time,
					  FolderView     *folderview);
static void folderview_drag_leave_cb     (GtkWidget        *widget,
					  GdkDragContext   *context,
					  guint             time,
					  FolderView       *folderview);
static void folderview_drag_received_cb  (GtkWidget        *widget,
					  GdkDragContext   *context,
					  gint              x,
					  gint              y,
					  GtkSelectionData *data,
					  guint             info,
					  guint             time,
					  FolderView       *folderview);

static GtkTargetEntry folderview_drag_types[] =
{
	{"text/plain", GTK_TARGET_SAME_APP, 0}
};

static GtkItemFactoryEntry folderview_mail_popup_entries[] =
{
	{N_("/Create _new folder..."),	NULL, folderview_new_folder_cb, 0, NULL},
	{N_("/_Rename folder..."),	NULL, folderview_rename_folder_cb, 0, NULL},
	{N_("/_Move folder..."),	NULL, folderview_move_folder_cb, 0, NULL},
	{N_("/_Delete folder"),		NULL, folderview_delete_folder_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Empty _trash"),		NULL, folderview_empty_trash_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/_Check for new messages"),
					NULL, folderview_update_tree_cb, 0, NULL},
	{N_("/R_ebuild folder tree"),	NULL, folderview_update_tree_cb, 1, NULL},
	{N_("/_Update summary"),	NULL, folderview_update_summary_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Mar_k all read"),		NULL, folderview_mark_all_read_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/_Search messages..."),	NULL, folderview_search_cb, 0, NULL},
	{N_("/Ed_it search condition..."),
					NULL, folderview_search_cb, 0, NULL},
	{N_("/_Properties..."),		NULL, folderview_property_cb, 0, NULL}
};

static GtkItemFactoryEntry folderview_imap_popup_entries[] =
{
	{N_("/Create _new folder..."),	NULL, folderview_new_folder_cb,    0, NULL},
	{N_("/_Rename folder..."),	NULL, folderview_rename_folder_cb, 0, NULL},
	{N_("/_Move folder..."),	NULL, folderview_move_folder_cb, 0, NULL},
	{N_("/_Delete folder"),		NULL, folderview_delete_folder_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Empty _trash"),		NULL, folderview_empty_trash_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Down_load"),		NULL, folderview_download_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/_Check for new messages"),
					NULL, folderview_update_tree_cb, 0, NULL},
	{N_("/R_ebuild folder tree"),	NULL, folderview_update_tree_cb, 1, NULL},
	{N_("/_Update summary"),	NULL, folderview_update_summary_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Mar_k all read"),		NULL, folderview_mark_all_read_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/_Search messages..."),	NULL, folderview_search_cb, 0, NULL},
	{N_("/Ed_it search condition..."),
					NULL, folderview_search_cb, 0, NULL},
	{N_("/_Properties..."),		NULL, folderview_property_cb, 0, NULL}
};

static GtkItemFactoryEntry folderview_news_popup_entries[] =
{
	{N_("/Su_bscribe to newsgroup..."),
					NULL, folderview_new_news_group_cb, 0, NULL},
	{N_("/_Remove newsgroup"),	NULL, folderview_rm_news_group_cb, 0, NULL},
	{N_("/_Rename folder..."),	NULL, folderview_rename_folder_cb, 0, NULL},
	{N_("/_Delete folder"),		NULL, folderview_delete_folder_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Down_load"),		NULL, folderview_download_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/_Check for new messages"),
					NULL, folderview_update_tree_cb, 0, NULL},
	{N_("/_Update summary"),	NULL, folderview_update_summary_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/Mar_k all read"),		NULL, folderview_mark_all_read_cb, 0, NULL},
	{N_("/---"),			NULL, NULL, 0, "<Separator>"},
	{N_("/_Search messages..."),	NULL, folderview_search_cb, 0, NULL},
	{N_("/Ed_it search condition..."),
					NULL, folderview_search_cb, 0, NULL},
	{N_("/_Properties..."),		NULL, folderview_property_cb, 0, NULL}
};


FolderView *folderview_create(void)
{
	FolderView *folderview;
	GtkWidget *scrolledwin;
	GtkWidget *treeview;
	GtkTreeStore *store;
	GtkTreeSelection *selection;
	GtkTreeViewColumn *column;
	GtkCellRenderer *renderer;
	GtkWidget *mail_popup;
	GtkWidget *news_popup;
	GtkWidget *imap_popup;
	GtkItemFactory *mail_factory;
	GtkItemFactory *news_factory;
	GtkItemFactory *imap_factory;
	gint n_entries;

	debug_print(_("Creating folder view...\n"));
	folderview = g_new0(FolderView, 1);

	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy
		(GTK_SCROLLED_WINDOW(scrolledwin),
		 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
					    GTK_SHADOW_IN);
	gtk_widget_set_size_request(scrolledwin,
				    prefs_common.folderview_width,
				    prefs_common.folderview_height);

	store = gtk_tree_store_new(N_COLS, G_TYPE_STRING, G_TYPE_STRING,
				   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER,
				   GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF,
				   GDK_TYPE_COLOR, G_TYPE_INT);
	gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store),
					COL_FOLDER_NAME,
					folderview_folder_name_compare,
					NULL, NULL);

	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
	g_object_unref(G_OBJECT(store));
	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), FALSE);
	gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview),
					COL_FOLDER_NAME);
	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE);
	/* g_object_set(treeview, "fixed-height-mode", TRUE, NULL); */

	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
	gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);

	gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);

	/* create folder icon + name column */
	column = gtk_tree_view_column_new();
	gtk_tree_view_column_set_spacing(column, 2);
	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width
		(column, prefs_common.folder_col_folder);
	gtk_tree_view_column_set_resizable(column, TRUE);

	renderer = gtk_cell_renderer_pixbuf_new();
	g_object_set(renderer, "ypad", 0, NULL);
	gtk_tree_view_column_pack_start(column, renderer, FALSE);
	gtk_tree_view_column_set_title(column, _("Folder"));
	gtk_tree_view_column_set_attributes
		(column, renderer,
		 "pixbuf", COL_PIXBUF,
		 "pixbuf-expander-open", COL_PIXBUF_OPEN,
		 "pixbuf-expander-closed", COL_PIXBUF,
		 NULL);

	renderer = gtk_cell_renderer_text_new();
#if GTK_CHECK_VERSION(2, 6, 0)
	g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, "ypad", 0,
		     NULL);
#else
	g_object_set(renderer, "ypad", 0, NULL);
#endif
	gtk_tree_view_column_pack_start(column, renderer, TRUE);
	gtk_tree_view_column_set_attributes(column, renderer,
					    "text", COL_FOLDER_NAME,
					    "foreground-gdk", COL_FOREGROUND,
					    "weight", COL_BOLD,
					    NULL);

	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
	gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
	g_signal_connect(G_OBJECT(column->button), "size-allocate",
			 G_CALLBACK(folderview_col_resized), folderview);

	renderer = gtk_cell_renderer_text_new();
	g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL);
	column = gtk_tree_view_column_new_with_attributes
		(_("New"), renderer, "text", COL_NEW, NULL);
	gtk_tree_view_column_set_alignment(column, 1.0);
	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width
		(column, prefs_common.folder_col_new);
	gtk_tree_view_column_set_min_width(column, 8);
	gtk_tree_view_column_set_resizable(column, TRUE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
	g_signal_connect(G_OBJECT(column->button), "size-allocate",
			 G_CALLBACK(folderview_col_resized), folderview);

	renderer = gtk_cell_renderer_text_new();
	g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL);
	column = gtk_tree_view_column_new_with_attributes
		(_("Unread"), renderer, "text", COL_UNREAD, NULL);
	gtk_tree_view_column_set_alignment(column, 1.0);
	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width
		(column, prefs_common.folder_col_unread);
	gtk_tree_view_column_set_min_width(column, 8);
	gtk_tree_view_column_set_resizable(column, TRUE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
	g_signal_connect(G_OBJECT(column->button), "size-allocate",
			 G_CALLBACK(folderview_col_resized), folderview);

	renderer = gtk_cell_renderer_text_new();
	g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL);
	column = gtk_tree_view_column_new_with_attributes
		(_("#"), renderer, "text", COL_TOTAL, NULL);
	gtk_tree_view_column_set_alignment(column, 1.0);
	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width
		(column, prefs_common.folder_col_total);
	gtk_tree_view_column_set_min_width(column, 8);
	gtk_tree_view_column_set_resizable(column, TRUE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
	g_signal_connect(G_OBJECT(column->button), "size-allocate",
			 G_CALLBACK(folderview_col_resized), folderview);

	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store),
					     COL_FOLDER_NAME,
					     GTK_SORT_ASCENDING);

	/* popup menu */
	n_entries = sizeof(folderview_mail_popup_entries) /
		sizeof(folderview_mail_popup_entries[0]);
	mail_popup = menu_create_items(folderview_mail_popup_entries,
				       n_entries,
				       "<MailFolder>", &mail_factory,
				       folderview);
	n_entries = sizeof(folderview_imap_popup_entries) /
		sizeof(folderview_imap_popup_entries[0]);
	imap_popup = menu_create_items(folderview_imap_popup_entries,
				       n_entries,
				       "<IMAPFolder>", &imap_factory,
				       folderview);
	n_entries = sizeof(folderview_news_popup_entries) /
		sizeof(folderview_news_popup_entries[0]);
	news_popup = menu_create_items(folderview_news_popup_entries,
				       n_entries,
				       "<NewsFolder>", &news_factory,
				       folderview);

	g_signal_connect(G_OBJECT(treeview), "button_press_event",
			 G_CALLBACK(folderview_button_pressed), folderview);
	g_signal_connect(G_OBJECT(treeview), "button_release_event",
			 G_CALLBACK(folderview_button_released), folderview);
	g_signal_connect(G_OBJECT(treeview), "key_press_event",
			 G_CALLBACK(folderview_key_pressed), folderview);

	g_signal_connect(G_OBJECT(selection), "changed",
			 G_CALLBACK(folderview_selection_changed), folderview);

	g_signal_connect_after(G_OBJECT(treeview), "row-expanded",
			       G_CALLBACK(folderview_row_expanded),
			       folderview);
	g_signal_connect_after(G_OBJECT(treeview), "row-collapsed",
			       G_CALLBACK(folderview_row_collapsed),
			       folderview);

	g_signal_connect(G_OBJECT(mail_popup), "selection_done",
			 G_CALLBACK(folderview_popup_close), folderview);
	g_signal_connect(G_OBJECT(imap_popup), "selection_done",
			 G_CALLBACK(folderview_popup_close), folderview);
	g_signal_connect(G_OBJECT(news_popup), "selection_done",
			 G_CALLBACK(folderview_popup_close), folderview);

        /* drop callback */
	gtk_drag_dest_set(treeview, GTK_DEST_DEFAULT_ALL,
			  folderview_drag_types, 1,
			  GDK_ACTION_MOVE | GDK_ACTION_COPY);
	g_signal_connect(G_OBJECT(treeview), "drag-motion",
			 G_CALLBACK(folderview_drag_motion_cb),
			 folderview);
	g_signal_connect(G_OBJECT(treeview), "drag-leave",
			 G_CALLBACK(folderview_drag_leave_cb),
			 folderview);
	g_signal_connect(G_OBJECT(treeview), "drag-data-received",
			 G_CALLBACK(folderview_drag_received_cb),
			 folderview);

	folderview->scrolledwin  = scrolledwin;
	folderview->treeview     = treeview;
	folderview->store        = store;
	folderview->selection    = selection;
	folderview->mail_popup   = mail_popup;
	folderview->mail_factory = mail_factory;
	folderview->imap_popup   = imap_popup;
	folderview->imap_factory = imap_factory;
	folderview->news_popup   = news_popup;
	folderview->news_factory = news_factory;

	folderview->display_folder_unread = prefs_common.display_folder_unread;

	folderview_set_columns(folderview);

	gtk_widget_show_all(scrolledwin);

	folderview_list = g_list_append(folderview_list, folderview);

	return folderview;
}

void folderview_init(FolderView *folderview)
{
	GtkWidget *treeview = folderview->treeview;

	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_INBOX, &inbox_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_OUTBOX, &outbox_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_DIR_CLOSE, &folder_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_DIR_OPEN, &folderopen_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_DIR_NOSELECT,
			 &foldernoselect_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_DRAFT, &draft_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_TRASH, &trash_pixbuf);
	stock_pixbuf_gdk(treeview, STOCK_PIXMAP_GROUP, &virtual_pixbuf);
}

void folderview_reflect_prefs(FolderView *folderview)
{
	folderview_set_columns(folderview);
	if (folderview->display_folder_unread !=
	    prefs_common.display_folder_unread) {
		folderview->display_folder_unread =
			prefs_common.display_folder_unread;
		folderview_update_row_all(folderview);
	}
}

FolderView *folderview_get(void)
{
	return (FolderView *)folderview_list->data;
}

void folderview_set(FolderView *folderview)
{
	MainWindow *mainwin = folderview->mainwin;
	GtkTreeIter iter;

	debug_print(_("Setting folder info...\n"));
	STATUSBAR_PUSH(mainwin, _("Setting folder info..."));

	main_window_cursor_wait(mainwin);

	folderview_unselect(folderview);

	gtk_tree_store_clear(folderview->store);

	folderview_set_folders(folderview);

	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(folderview->store),
					  &iter))
		folderview_select_row(folderview, &iter);

	main_window_cursor_normal(mainwin);
	STATUSBAR_POP(mainwin);
}

void folderview_set_all(void)
{
	GList *list;

	for (list = folderview_list; list != NULL; list = list->next)
		folderview_set((FolderView *)list->data);
}

static void folderview_set_columns(FolderView *folderview)
{
	GtkTreeView *treeview = GTK_TREE_VIEW(folderview->treeview);
	GtkTreeViewColumn *column;

	column = gtk_tree_view_get_column(treeview, COL_NEW);
	gtk_tree_view_column_set_visible
		(column, prefs_common.display_folder_num_columns);
	column = gtk_tree_view_get_column(treeview, COL_UNREAD);
	gtk_tree_view_column_set_visible
		(column, prefs_common.display_folder_num_columns);
	column = gtk_tree_view_get_column(treeview, COL_TOTAL);
	gtk_tree_view_column_set_visible
		(column, prefs_common.display_folder_num_columns);
}

void folderview_select(FolderView *folderview, FolderItem *item)
{
	GtkTreeIter iter;

	if (!item) return;

	if (gtkut_tree_model_find_by_column_data
		(GTK_TREE_MODEL(folderview->store), &iter, NULL,
		 COL_FOLDER_ITEM, item))
		folderview_select_row(folderview, &iter);
}

static void folderview_select_row(FolderView *folderview, GtkTreeIter *iter)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreePath *path;

	g_return_if_fail(iter != NULL);

	path = gtk_tree_model_get_path(model, iter);

	gtkut_tree_view_expand_parent_all(GTK_TREE_VIEW(folderview->treeview),
					  iter);

	folderview->open_folder = TRUE;
	gtk_tree_view_set_cursor(GTK_TREE_VIEW(folderview->treeview), path,
				 NULL, FALSE);
	if (folderview->summaryview->folder_item &&
	    folderview->summaryview->folder_item->total > 0)
		gtk_widget_grab_focus(folderview->summaryview->treeview);
	else
		gtk_widget_grab_focus(folderview->treeview);

	gtk_tree_path_free(path);
}

static void folderview_select_row_ref(FolderView *folderview,
				      GtkTreeRowReference *row)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	if (!row) return;

	path = gtk_tree_row_reference_get_path(row);
	if (!path)
		return;
	gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, path);
	gtk_tree_path_free(path);

	folderview_select_row(folderview, &iter);
}

void folderview_unselect(FolderView *folderview)
{
	if (folderview->selected) {
		gtk_tree_row_reference_free(folderview->selected);
		folderview->selected = NULL;
	}
	if (folderview->opened) {
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}
}

static gboolean folderview_find_next_unread(GtkTreeModel *model,
					    GtkTreeIter *next,
					    GtkTreeIter *iter)
{
	FolderItem *item;
	GtkTreeIter iter_;
	gboolean valid;

	if (iter) {
		iter_ = *iter;
		valid = gtkut_tree_model_next(model, &iter_);
	} else
		valid = gtk_tree_model_get_iter_first(model, &iter_);

	while (valid) {
		item = NULL;
		gtk_tree_model_get(model, &iter_, COL_FOLDER_ITEM, &item, -1);
		if (item && item->unread > 0 && item->stype != F_TRASH) {
			if (next)
				*next = iter_;
			return TRUE;
		}

		valid = gtkut_tree_model_next(model, &iter_);
	}

	return FALSE;
}

void folderview_select_next_unread(FolderView *folderview)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter, next;

	if (folderview->opened) {
		GtkTreePath *path;

		path = gtk_tree_row_reference_get_path(folderview->opened);
		if (!path)
			return;
		gtk_tree_model_get_iter(model, &iter, path);
		gtk_tree_path_free(path);
	} else {
		if (!gtk_tree_model_get_iter_first(model, &iter))
			return;
	}
	if (folderview_find_next_unread(model, &next, &iter)) {
		folderview_select_row(folderview, &next);
		return;
	}

	if (!folderview->opened)
		return;

	/* search again from the first row */
	if (folderview_find_next_unread(model, &next, NULL))
		folderview_select_row(folderview, &next);
}

FolderItem *folderview_get_selected_item(FolderView *folderview)
{
	GtkTreePath *path;
	GtkTreeIter iter;
	FolderItem *item = NULL;

	if (!folderview->selected)
		return NULL;

	path = gtk_tree_row_reference_get_path(folderview->selected);
	if (!path)
		return NULL;
	gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, path);
	gtk_tree_path_free(path);
	gtk_tree_model_get(GTK_TREE_MODEL(folderview->store), &iter,
			   COL_FOLDER_ITEM, &item, -1);

	return item;
}

void folderview_set_opened_item(FolderView *folderview, FolderItem *item)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter;
	GtkTreePath *path;

	gtk_tree_row_reference_free(folderview->opened);
	folderview->opened = NULL;

	if (!item)
		return;

	if (gtkut_tree_model_find_by_column_data
		 (model, &iter, NULL, COL_FOLDER_ITEM, item)) {
		path = gtk_tree_model_get_path(model, &iter);
		folderview->opened = gtk_tree_row_reference_new(model, path);
		gtk_tree_path_free(path);
	}
}

void folderview_update_opened_msg_num(FolderView *folderview)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	if (!folderview->opened)
		return;

	path = gtk_tree_row_reference_get_path(folderview->opened);
	if (!path)
		return;
	gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter, path);
	gtk_tree_path_free(path);

	folderview_update_row(folderview, &iter);
}

gboolean folderview_append_item(FolderView *folderview, GtkTreeIter *iter,
				FolderItem *item, gboolean expand_parent)
{
	FolderItem *parent_item;
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter_, child;
	GtkTreeIter *iter_p = &iter_;

	g_return_val_if_fail(item != NULL, FALSE);
	g_return_val_if_fail(item->folder != NULL, FALSE);

	parent_item = item->parent;

	if (!parent_item)
		iter_p = NULL;
	else if (!gtkut_tree_model_find_by_column_data
		(model, iter_p, NULL, COL_FOLDER_ITEM, parent_item))
		return FALSE;

	if (!gtkut_tree_model_find_by_column_data
		(model, &child, iter_p, COL_FOLDER_ITEM, item)) {
		gtk_tree_store_append(folderview->store, &child, iter_p);
		gtk_tree_store_set(folderview->store, &child,
				   COL_FOLDER_NAME, item->name,
				   COL_FOLDER_ITEM, item,
				   -1);
		folderview_update_row(folderview, &child);
		if (iter)
			*iter = child;
		if (expand_parent && iter_p) {
			GtkTreePath *path;

			path = gtk_tree_model_get_path(model, iter_p);
			gtk_tree_view_expand_row
				(GTK_TREE_VIEW(folderview->treeview),
				 path, FALSE);
			gtk_tree_path_free(path);
		}
		return TRUE;
	}

	return FALSE;
}

static void folderview_set_folders(FolderView *folderview)
{
	GList *list;

	list = folder_get_list();

	for (; list != NULL; list = list->next)
		folderview_append_folder(folderview, FOLDER(list->data));
}

static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
				      gpointer data)
{
	GList *list;
	gchar *rootpath;

	if (FOLDER_IS_LOCAL(folder))
		rootpath = LOCAL_FOLDER(folder)->rootpath;
	else if (FOLDER_TYPE(folder) == F_IMAP && folder->account &&
		 folder->account->recv_server)
		rootpath = folder->account->recv_server;
	else if (FOLDER_TYPE(folder) == F_NEWS && folder->account &&
		 folder->account->nntp_server)
		rootpath = folder->account->nntp_server;
	else
		return;

	for (list = folderview_list; list != NULL; list = list->next) {
		FolderView *folderview = (FolderView *)list->data;
		MainWindow *mainwin = folderview->mainwin;
		gchar *str;

		if (item->path)
			str = g_strdup_printf(_("Scanning folder %s%c%s ..."),
					      rootpath, G_DIR_SEPARATOR,
					      item->path);
		else
			str = g_strdup_printf(_("Scanning folder %s ..."),
					      rootpath);

		STATUSBAR_PUSH(mainwin, str);
		STATUSBAR_POP(mainwin);
		g_free(str);
	}
}

static GtkWidget *label_window_create(const gchar *str)
{
	GtkWidget *window;
	GtkWidget *label;

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_size_request(window, 380, 60);
	gtk_container_set_border_width(GTK_CONTAINER(window), 8);
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_window_set_title(GTK_WINDOW(window), str);
	gtk_window_set_modal(GTK_WINDOW(window), TRUE);
	gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
	manage_window_set_transient(GTK_WINDOW(window));

	label = gtk_label_new(str);
	gtk_container_add(GTK_CONTAINER(window), label);
	gtk_widget_show(label);

	gtk_widget_show_now(window);

	return window;
}

static void folderview_rescan_tree(FolderView *folderview, Folder *folder)
{
	GtkWidget *window;
	AlertValue avalue;

	g_return_if_fail(folder != NULL);

	if (!folder->klass->scan_tree) return;

	avalue = alertpanel
		(_("Rebuild folder tree"),
		 _("The folder tree will be rebuilt. Continue?"),
		 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	if (avalue != G_ALERTDEFAULT) return;

	if (!FOLDER_IS_LOCAL(folder) &&
	    !main_window_toggle_online_if_offline(folderview->mainwin))
		return;

	inc_lock();
	window = label_window_create(_("Rebuilding folder tree..."));

	summary_show(folderview->summaryview, NULL, FALSE);
	GTK_EVENTS_FLUSH();

	folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
	if (folder->klass->scan_tree(folder) < 0)
		alertpanel_error(_("Rebuilding of the folder tree failed."));
	folder_set_ui_func(folder, NULL, NULL);

	folder_write_list();
	folderview_set_all();
	statusbar_pop_all();

	gtk_widget_destroy(window);
	inc_unlock();
}

gint folderview_check_new(Folder *folder)
{
	FolderItem *item;
	FolderView *folderview;
	GtkTreeModel *model;
	GtkTreeIter iter;
	gboolean valid;
	gint prev_new, prev_unread, n_updated = 0;

	folderview = (FolderView *)folderview_list->data;
	model = GTK_TREE_MODEL(folderview->store);

	if (folder && !FOLDER_IS_LOCAL(folder)) {
		if (!main_window_toggle_online_if_offline(folderview->mainwin))
			return 0;
	}

	inc_lock();
	main_window_lock(folderview->mainwin);
	gtk_widget_set_sensitive(folderview->treeview, FALSE);
	GTK_EVENTS_FLUSH();

	for (valid = gtk_tree_model_get_iter_first(model, &iter);
	     valid; valid = gtkut_tree_model_next(model, &iter)) {
		item = NULL;
		gtk_tree_model_get(model, &iter,
				   COL_FOLDER_ITEM, &item, -1);
		if (!item || !item->path || !item->folder) continue;
		if (item->stype == F_VIRTUAL) continue;
		if (item->no_select) continue;
		if (folder && folder != item->folder) continue;
		if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;

		prev_new = item->new;
		prev_unread = item->unread;
		folderview_scan_tree_func(item->folder, item, NULL);
		if (folder_item_scan(item) < 0) {
			if (folder && !FOLDER_IS_LOCAL(folder))
				break;
		}
		folderview_update_row(folderview, &iter);
		if (prev_unread < item->unread)
			n_updated += item->unread - prev_unread;
		else if (prev_new < item->new)
			n_updated += item->new - prev_new;
	}

	gtk_widget_set_sensitive(folderview->treeview, TRUE);
	main_window_unlock(folderview->mainwin);
	inc_unlock();
	statusbar_pop_all();

	folder_write_list();

	return n_updated;
}

gint folderview_check_new_item(FolderItem *item)
{
	Folder *folder;
	FolderView *folderview;
	GtkTreeModel *model;
	GtkTreeIter iter;
	gint prev_new, prev_unread, n_updated = 0;

	g_return_val_if_fail(item != NULL, 0);
	g_return_val_if_fail(item->folder != NULL, 0);

	if (!item->path || item->no_select)
		return 0;

	folderview = (FolderView *)folderview_list->data;
	model = GTK_TREE_MODEL(folderview->store);

	folder = item->folder;

	if (!FOLDER_IS_LOCAL(folder)) {
		if (!main_window_toggle_online_if_offline(folderview->mainwin))
			return 0;
	}

	if (!gtkut_tree_model_find_by_column_data
		(model, &iter, NULL, COL_FOLDER_ITEM, item))
		return 0;

	inc_lock();
	main_window_lock(folderview->mainwin);
	gtk_widget_set_sensitive(folderview->treeview, FALSE);
	GTK_EVENTS_FLUSH();

	prev_new = item->new;
	prev_unread = item->unread;
	folderview_scan_tree_func(folder, item, NULL);
	folder_item_scan(item);
	folderview_update_row(folderview, &iter);
	if (prev_unread < item->unread)
		n_updated = item->unread - prev_unread;
	else if (prev_new < item->new)
		n_updated = item->new - prev_new;

	gtk_widget_set_sensitive(folderview->treeview, TRUE);
	main_window_unlock(folderview->mainwin);
	inc_unlock();
	statusbar_pop_all();

	folder_write_list();

	return n_updated;
}

gint folderview_check_new_all(void)
{
	GList *list;
	GtkWidget *window;
	FolderView *folderview;
	gint n_updated = 0;

	folderview = (FolderView *)folderview_list->data;

	inc_lock();
	main_window_lock(folderview->mainwin);
	window = label_window_create
		(_("Checking for new messages in all folders..."));

	list = folder_get_list();
	for (; list != NULL; list = list->next) {
		Folder *folder = list->data;

		n_updated += folderview_check_new(folder);
	}

	gtk_widget_destroy(window);
	main_window_unlock(folderview->mainwin);
	inc_unlock();

	return n_updated;
}

static gboolean folderview_search_new_recursive(GtkTreeModel *model,
						GtkTreeIter *iter)
{
	FolderItem *item = NULL;
	GtkTreeIter iter_;
	gboolean valid;

	if (iter) {
		gtk_tree_model_get(model, iter, COL_FOLDER_ITEM, &item, -1);
		if (item) {
			if (item->new > 0 ||
			    (item->stype == F_QUEUE && item->total > 0))
				return TRUE;
		}
		valid = gtk_tree_model_iter_children(model, &iter_, iter);
	} else
		valid = gtk_tree_model_get_iter_first(model, &iter_);

	while (valid) {
		if (folderview_search_new_recursive(model, &iter_) == TRUE)
			return TRUE;
		valid = gtk_tree_model_iter_next(model, &iter_);
	}

	return FALSE;
}

static gboolean folderview_have_new_children(FolderView *folderview,
					     GtkTreeIter *iter)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter_;
	gboolean valid;

	if (iter)
		valid = gtk_tree_model_iter_children(model, &iter_, iter);
	else
		valid = gtk_tree_model_get_iter_first(model, &iter_);

	while (valid) {
		if (folderview_search_new_recursive(model, &iter_) == TRUE)
			return TRUE;
		valid = gtk_tree_model_iter_next(model, &iter_);
	}

	return FALSE;
}

static gboolean folderview_search_unread_recursive(GtkTreeModel *model,
						   GtkTreeIter *iter)
{
	FolderItem *item = NULL;
	GtkTreeIter iter_;
	gboolean valid;

	if (iter) {
		gtk_tree_model_get(model, iter, COL_FOLDER_ITEM, &item, -1);
		if (item) {
			if (item->stype == F_TRASH)
				return FALSE;
			if (item->unread > 0 ||
			    (item->stype == F_QUEUE && item->total > 0))
				return TRUE;
		}
		valid = gtk_tree_model_iter_children(model, &iter_, iter);
	} else
		valid = gtk_tree_model_get_iter_first(model, &iter_);

	while (valid) {
		if (folderview_search_unread_recursive(model, &iter_) == TRUE)
			return TRUE;
		valid = gtk_tree_model_iter_next(model, &iter_);
	}

	return FALSE;
}

static gboolean folderview_have_unread_children(FolderView *folderview,
						GtkTreeIter *iter)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter_;
	gboolean valid;

	if (iter)
		valid = gtk_tree_model_iter_children(model, &iter_, iter);
	else
		valid = gtk_tree_model_get_iter_first(model, &iter_);

	while (valid) {
		if (folderview_search_unread_recursive(model, &iter_) == TRUE)
			return TRUE;
		valid = gtk_tree_model_iter_next(model, &iter_);
	}

	return FALSE;
}

static void folderview_update_row(FolderView *folderview, GtkTreeIter *iter)
{
	GtkTreeStore *store = folderview->store;
	GtkTreeModel *model = GTK_TREE_MODEL(store);
	GtkTreePath *path;
	GtkTreeIter parent;
	FolderItem *item = NULL;
	GdkPixbuf *pixbuf, *open_pixbuf;
	gchar *name, *str;
	gchar new_s[11], unread_s[11], total_s[11];
	gboolean add_unread_mark;
	gboolean use_color;
	PangoWeight weight = PANGO_WEIGHT_NORMAL;
	GdkColor *foreground = NULL;

	gtk_tree_model_get(model, iter, COL_FOLDER_ITEM, &item, -1);
	g_return_if_fail(item != NULL);

	switch (item->stype) {
	case F_INBOX:
		pixbuf = open_pixbuf = inbox_pixbuf;
		name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
				!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
				item->name);
		break;
	case F_OUTBOX:
		pixbuf = open_pixbuf = outbox_pixbuf;
		name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
				!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
				item->name);
		break;
	case F_QUEUE:
		pixbuf = open_pixbuf = outbox_pixbuf;
		name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
				!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
				item->name);
		break;
	case F_TRASH:
		pixbuf = open_pixbuf = trash_pixbuf;
		name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
				!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
				item->name);
		break;
	case F_DRAFT:
		pixbuf = open_pixbuf = draft_pixbuf;
		name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
				!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
				item->name);
		break;
#if 0
	case F_JUNK:
		pixbuf = folder_pixbuf;
		open_pixbuf = folderopen_pixbuf;
		name = g_strdup(FOLDER_IS_LOCAL(item->folder) &&
				!strcmp2(item->name, JUNK_DIR) ? _("Junk") :
				item->name);
		break;
#endif
	case F_VIRTUAL:
		pixbuf = open_pixbuf = virtual_pixbuf;
		name = g_strdup(item->name);
		break;
	default:
		if (item->no_select) {
			pixbuf = open_pixbuf = foldernoselect_pixbuf;
		} else {
			pixbuf = folder_pixbuf;
			open_pixbuf = folderopen_pixbuf;
		}

		if (!item->parent) {
			switch (FOLDER_TYPE(item->folder)) {
			case F_MH:
				name = " (MH)"; break;
			case F_IMAP:
				name = " (IMAP4)"; break;
			case F_NEWS:
				name = " (News)"; break;
			default:
				name = "";
			}
			name = g_strconcat(item->name, name, NULL);
		} else {
			if (FOLDER_TYPE(item->folder) == F_NEWS &&
			    item->path &&
			    !strcmp2(item->name, item->path))
				name = get_abbrev_newsgroup_name
					(item->path,
					 prefs_common.ng_abbrev_len);
			else
				name = g_strdup(item->name);
		}
	}

	path = gtk_tree_model_get_path(model, iter);
	if (!gtk_tree_view_row_expanded
		(GTK_TREE_VIEW(folderview->treeview), path) &&
	    folderview_have_unread_children(folderview, iter))
		add_unread_mark = TRUE;
	else
		add_unread_mark = FALSE;
	gtk_tree_path_free(path);

	if (item->stype == F_QUEUE && item->total > 0 &&
	    folderview->display_folder_unread) {
		str = g_strdup_printf("%s (%d%s)", name, item->total,
				      add_unread_mark ? "+" : "");
		g_free(name);
		name = str;
	} else if ((item->unread > 0 || add_unread_mark) &&
		   folderview->display_folder_unread) {
		if (item->unread > 0)
			str = g_strdup_printf("%s (%d%s)", name, item->unread,
					      add_unread_mark ? "+" : "");
		else
			str = g_strdup_printf("%s (+)", name);
		g_free(name);
		name = str;
	}

	if (!item->parent) {
		strcpy(new_s, "-");
		strcpy(unread_s, "-");
		strcpy(total_s, "-");
	} else {
		itos_buf(new_s, item->new);
		itos_buf(unread_s, item->unread);
		itos_buf(total_s, item->total);
	}

	if (item->stype == F_OUTBOX || item->stype == F_DRAFT ||
	    item->stype == F_TRASH) {
		use_color = FALSE;
	} else if (item->stype == F_QUEUE) {
		/* highlight queue folder if there are any messages */
		use_color = (item->total > 0);
		if (item->total > 0)
			weight = PANGO_WEIGHT_BOLD;
	} else {
		/* if unread messages exist, print with bold font */
		if ((item->unread > 0) || add_unread_mark)
			weight = PANGO_WEIGHT_BOLD;
		/* if new messages exist, print with colored letter */
		use_color =
			(item->new > 0) ||
			(add_unread_mark &&
			 folderview_have_new_children(folderview, iter));
	}

	if (item->no_select)
		foreground = &folderview->color_noselect;
	else if (use_color)
		foreground = &folderview->color_new;

	gtk_tree_store_set(store, iter,
			   COL_FOLDER_NAME, name,
			   COL_NEW, new_s,
			   COL_UNREAD, unread_s,
			   COL_TOTAL, total_s,
			   COL_FOLDER_ITEM, item,
			   COL_PIXBUF, pixbuf,
			   COL_PIXBUF_OPEN, open_pixbuf,
			   COL_FOREGROUND, foreground,
			   COL_BOLD, weight,
			   -1);
	g_free(name);

	item->updated = FALSE;

	if (gtkut_tree_view_find_collapsed_parent
		(GTK_TREE_VIEW(folderview->treeview), &parent, iter))
		folderview_update_row(folderview, &parent);
}

static void folderview_update_row_all(FolderView *folderview)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter;
	gboolean valid;

	valid = gtk_tree_model_get_iter_first(model, &iter);

	while (valid) {
		folderview_update_row(folderview, &iter);
		valid = gtkut_tree_model_next(model, &iter);
	}
}

void folderview_update_item(FolderItem *item, gboolean update_summary)
{
	FolderView *folderview;
	GtkTreeIter iter;

	g_return_if_fail(item != NULL);

	folderview = folderview_get();

	if (gtkut_tree_model_find_by_column_data
		(GTK_TREE_MODEL(folderview->store), &iter, NULL,
		 COL_FOLDER_ITEM, item)) {
		folderview_update_row(folderview, &iter);
		if (update_summary &&
		    folderview->summaryview->folder_item == item)
			summary_show(folderview->summaryview, item, FALSE);
	}
}

static void folderview_update_item_foreach_func(gpointer key, gpointer val,
						gpointer data)
{
	folderview_update_item((FolderItem *)key, GPOINTER_TO_INT(data));
}

void folderview_update_item_foreach(GHashTable *table, gboolean update_summary)
{
	g_hash_table_foreach(table, folderview_update_item_foreach_func,
			     GINT_TO_POINTER(update_summary));
}

static gboolean folderview_update_all_updated_func(GNode *node, gpointer data)
{
	FolderItem *item;

	item = FOLDER_ITEM(node->data);
	if (item->updated) {
		debug_print("folderview_update_all_updated(): '%s' is updated\n", item->path);
		folderview_update_item(item, GPOINTER_TO_INT(data));
	}

	return FALSE;
}

void folderview_update_all_updated(gboolean update_summary)
{
	GList *list;
	Folder *folder;

	for (list = folder_get_list(); list != NULL; list = list->next) {
		folder = (Folder *)list->data;
		g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
				folderview_update_all_updated_func,
				GINT_TO_POINTER(update_summary));
	}
}

static gboolean folderview_insert_item_recursive(FolderView *folderview,
						 FolderItem *item,
						 GtkTreeIter *iter)
{
	GNode *node;
	GtkTreeIter iter_;
	gboolean valid;

	g_return_val_if_fail(item != NULL, FALSE);

	valid = folderview_append_item(folderview, &iter_, item, FALSE);
	g_return_val_if_fail(valid == TRUE, FALSE);

	for (node = item->node->children; node != NULL; node = node->next) {
		FolderItem *child_item = FOLDER_ITEM(node->data);
		folderview_insert_item_recursive(folderview, child_item, NULL);
	}

	if (item->node->children && !item->collapsed) {
		GtkTreePath *path;

		path = gtk_tree_model_get_path
			(GTK_TREE_MODEL(folderview->store), &iter_);
		gtk_tree_view_expand_row(GTK_TREE_VIEW(folderview->treeview),
					 path, FALSE);
		gtk_tree_path_free(path);
	}

	if (iter)
		*iter = iter_;
	return TRUE;
}

static void folderview_append_folder(FolderView *folderview, Folder *folder)
{
	g_return_if_fail(folder != NULL);

	folderview_insert_item_recursive
		(folderview, FOLDER_ITEM(folder->node->data), NULL);
}

void folderview_new_folder(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (item->folder->klass->create_folder)
		folderview_new_folder_cb(folderview, 0, NULL);
	else if (FOLDER_TYPE(item->folder) == F_NEWS)
		folderview_new_news_group_cb(folderview, 0, NULL);
}

void folderview_rename_folder(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (!item->path) return;
	if (item->stype != F_NORMAL) return;

	if (item->folder->klass->rename_folder)
		folderview_rename_folder_cb(folderview, 0, NULL);
}

void folderview_move_folder(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (!item->path) return;
	if (item->stype != F_NORMAL) return;

	if (item->folder->klass->move_folder)
		folderview_move_folder_cb(folderview, 0, NULL);
}

void folderview_delete_folder(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (!item->path) return;
	if (item->stype != F_NORMAL) return;

	if (item->folder->klass->remove_folder)
		folderview_delete_folder_cb(folderview, 0, NULL);
	else if (FOLDER_TYPE(item->folder) == F_NEWS)
		folderview_rm_news_group_cb(folderview, 0, NULL);
}

void folderview_check_new_selected(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);
	if (item->parent != NULL) return;

	folderview_check_new(item->folder);
}

void folderview_remove_mailbox(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);
	if (item->parent != NULL) return;

	switch (FOLDER_TYPE(item->folder)) {
	case F_MH:
	case F_MBOX:
	case F_MAILDIR:
		folderview_remove_mailbox_cb(folderview, 0, NULL);
		break;
	case F_IMAP:
		folderview_rm_imap_server_cb(folderview, 0, NULL);
		break;
	case F_NEWS:
		folderview_rm_news_server_cb(folderview, 0, NULL);
		break;
	default:
		break;
	}
}

void folderview_rebuild_tree(FolderView *folderview)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);
	if (item->parent != NULL) return;

	folderview_rescan_tree(folderview, item->folder);
}

static gboolean folderview_menu_popup(FolderView *folderview,
				      GdkEventButton *event)
{
	FolderItem *item = NULL;
	Folder *folder;
	GtkWidget *popup;
	GtkItemFactory *ifactory;
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreeIter iter;
	gboolean new_folder      = FALSE;
	gboolean rename_folder   = FALSE;
	gboolean move_folder     = FALSE;
	gboolean delete_folder   = FALSE;
	gboolean empty_trash     = FALSE;
	gboolean download_msg    = FALSE;
	gboolean update_tree     = FALSE;
	gboolean update_summary  = FALSE;
	gboolean mark_all_read   = FALSE;
	gboolean rescan_tree     = FALSE;
	gboolean remove_tree     = FALSE;
	gboolean search_folder   = FALSE;
	gboolean folder_property = FALSE;

	if (!event) return FALSE;

	if (event->button != 3)
		return FALSE;

	if (!gtk_tree_selection_get_selected
		(folderview->selection, NULL, &iter))
		return FALSE;

	gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1);
	g_return_val_if_fail(item != NULL, FALSE);
	g_return_val_if_fail(item->folder != NULL, FALSE);
	folder = item->folder;

	if (folderview->mainwin->lock_count == 0) {
		new_folder = TRUE;
		folder_property = TRUE;
		search_folder = TRUE;
		if (item->parent == NULL) {
			update_tree = remove_tree = TRUE;
		} else {
			if (FOLDER_TYPE(folder) != F_IMAP)
				mark_all_read = TRUE;
			if (gtkut_tree_row_reference_equal
				(folderview->selected, folderview->opened)) {
				update_summary = TRUE;
				mark_all_read = TRUE;
			}
		}
		if (FOLDER_IS_LOCAL(folder) || FOLDER_TYPE(folder) == F_IMAP) {
			if (item->parent == NULL)
				update_tree = rescan_tree = TRUE;
			else if (item->stype == F_NORMAL) {
				rename_folder = delete_folder = TRUE;
				if (folder->klass->move_folder)
					move_folder = TRUE;
			} else if (item->stype == F_TRASH) {
				if (item->total > 0)
					empty_trash = TRUE;
			}
		} else if (FOLDER_TYPE(folder) == F_NEWS) {
			if (item->parent != NULL)
				delete_folder = TRUE;
		}
		if (item->stype == F_VIRTUAL) {
			new_folder = FALSE;
			rename_folder = delete_folder = TRUE;
		}
		if (FOLDER_TYPE(folder) == F_IMAP ||
		    FOLDER_TYPE(folder) == F_NEWS) {
			if (item->parent != NULL && item->no_select == FALSE &&
			    item->stype != F_VIRTUAL)
				download_msg = TRUE;
		}
	} else {
		search_folder = TRUE;
		if (item->parent) {
			if (FOLDER_TYPE(folder) != F_IMAP)
				mark_all_read = TRUE;
			if (gtkut_tree_row_reference_equal
				(folderview->selected, folderview->opened)) {
				update_summary = TRUE;
				mark_all_read = TRUE;
			}
			if (item->stype == F_TRASH) {
				if (item->total > 0)
					empty_trash = TRUE;
			}
		}
	}

#define SET_SENS(factory, name, sens)				\
{								\
	GtkWidget *widget;					\
	widget = gtk_item_factory_get_item(factory, name);	\
	if (widget)						\
		gtk_widget_set_sensitive(widget, sens);		\
}

#define SET_VISIBILITY(factory, name, visible)			\
{								\
	GtkWidget *widget;					\
	widget = gtk_item_factory_get_item(factory, name);	\
	if (widget) {						\
		if (visible)					\
			gtk_widget_show(widget);		\
		else						\
			gtk_widget_hide(widget);		\
	}							\
}

	if (FOLDER_IS_LOCAL(folder)) {
		popup = folderview->mail_popup;
		ifactory = folderview->mail_factory;
	} else if (FOLDER_TYPE(folder) == F_IMAP) {
		popup = folderview->imap_popup;
		ifactory = folderview->imap_factory;
	} else if (FOLDER_TYPE(folder) == F_NEWS) {
		popup = folderview->news_popup;
		ifactory = folderview->news_factory;
	} else
		return FALSE;

	menu_set_insensitive_all(GTK_MENU_SHELL(popup));

	SET_SENS(ifactory, "/Create new folder...", new_folder);
	SET_SENS(ifactory, "/Rename folder...", rename_folder);
	SET_SENS(ifactory, "/Move folder...", move_folder);
	SET_SENS(ifactory, "/Delete folder", delete_folder);
	SET_SENS(ifactory, "/Empty trash", empty_trash);
	SET_SENS(ifactory, "/Download", download_msg);
	SET_SENS(ifactory, "/Check for new messages", update_tree);
	SET_SENS(ifactory, "/Rebuild folder tree", rescan_tree);
	SET_SENS(ifactory, "/Update summary", update_summary);
	SET_SENS(ifactory, "/Mark all read", mark_all_read);
	SET_SENS(ifactory, "/Search messages...", search_folder);
	SET_SENS(ifactory, "/Edit search condition...", search_folder);
	SET_SENS(ifactory, "/Properties...", folder_property);

	if (FOLDER_TYPE(folder) == F_NEWS) {
		SET_SENS(ifactory, "/Subscribe to newsgroup...", new_folder);
		SET_SENS(ifactory, "/Remove newsgroup", delete_folder);
		SET_VISIBILITY(ifactory, "/Remove newsgroup",
			       item->stype != F_VIRTUAL);
		SET_VISIBILITY(ifactory, "/Rename folder...",
			       item->stype == F_VIRTUAL);
		SET_VISIBILITY(ifactory, "/Delete folder",
			       item->stype == F_VIRTUAL);
	}

	SET_VISIBILITY(ifactory, "/Search messages...",
		       item->stype != F_VIRTUAL);
	SET_VISIBILITY(ifactory, "/Edit search condition...",
		       item->stype == F_VIRTUAL);


#undef SET_SENS
#undef SET_VISIBILITY

	gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
		       event->button, event->time);

	return FALSE;
}


/* callback functions */

static gboolean folderview_button_pressed(GtkWidget *widget,
					  GdkEventButton *event,
					  FolderView *folderview)
{
	GtkTreeView *treeview = GTK_TREE_VIEW(widget);
	GtkTreePath *path;

	if (!event)
		return FALSE;

	if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
					   &path, NULL, NULL, NULL))
		return TRUE;

	if (folderview->selection_locked ||
	    summary_is_locked(folderview->summaryview))
		return TRUE;

	if (event->button == 1 || event->button == 2) {
		if (event->type == GDK_2BUTTON_PRESS) {
			if (gtk_tree_view_row_expanded(treeview, path))
				gtk_tree_view_collapse_row(treeview, path);
			else
				gtk_tree_view_expand_row(treeview, path, FALSE);
		}
		folderview->open_folder = TRUE;
	} else if (event->button == 3) {
		if (folderview->selected) {
			folderview->prev_selected =
				gtk_tree_row_reference_copy
					(folderview->selected);
		}
		gtk_tree_selection_select_path(folderview->selection, path);
		folderview_menu_popup(folderview, event);
		gtk_tree_path_free(path);
		return TRUE;
	}

	gtk_tree_path_free(path);
	return FALSE;
}

static gboolean folderview_button_released(GtkWidget *treeview,
					   GdkEventButton *event,
					   FolderView *folderview)
{
	folderview->open_folder = FALSE;
	return FALSE;
}

static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
				       FolderView *folderview)
{
	GtkTreePath *opened = NULL, *selected = NULL;

	if (!event) return FALSE;

	if (folderview->selection_locked ||
	    summary_is_locked(folderview->summaryview))
		return TRUE;

	switch (event->keyval) {
	case GDK_Return:
	case GDK_KP_Enter:
		if (folderview->selected) {
			folderview_select_row_ref(folderview,
						  folderview->selected);
		}
		return TRUE;
		break;
	case GDK_space:
	case GDK_KP_Space:
		if (folderview->selected) {
			if (folderview->opened)
				opened = gtk_tree_row_reference_get_path
					(folderview->opened);
			selected = gtk_tree_row_reference_get_path
				(folderview->selected);
			if (opened && selected &&
			    gtk_tree_path_compare(opened, selected) == 0 &&
			    (!folderview->summaryview->folder_item ||
			     folderview->summaryview->folder_item->total == 0))
				folderview_select_next_unread(folderview);
			else
				folderview_select_row_ref(folderview,
							  folderview->selected);
			gtk_tree_path_free(selected);
			gtk_tree_path_free(opened);
			return TRUE;
		}
		break;
	default:
		break;
	}

	return FALSE;
}

static gboolean folderview_focus_idle_func(gpointer data)
{
	FolderView *folderview = (FolderView *)data;

	GTK_WIDGET_SET_FLAGS(folderview->treeview, GTK_CAN_FOCUS);

	return FALSE;
}

static void folderview_selection_changed(GtkTreeSelection *selection,
					 FolderView *folderview)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	FolderItem *item = NULL;
	GtkTreeIter iter;
	GtkTreePath *path;
	gboolean opened;

	if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
		if (folderview->selected) {
			gtk_tree_row_reference_free(folderview->selected);
			folderview->selected = NULL;
		}
		return;
	}

	path = gtk_tree_model_get_path(model, &iter);

	gtk_tree_row_reference_free(folderview->selected);
	folderview->selected = gtk_tree_row_reference_new(model, path);

	main_window_set_menu_sensitive(folderview->mainwin);

	if (!folderview->open_folder) {
		gtk_tree_path_free(path);
		return;
	}
	folderview->open_folder = FALSE;

	gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1);
	if (!item) {
		gtk_tree_path_free(path);
		return;
	}

	if (item->path)
		debug_print(_("Folder %s is selected\n"), item->path);

	if (summary_is_locked(folderview->summaryview)) {
		gtk_tree_path_free(path);
		return;
	}

	if (folderview->opened) {
		GtkTreePath *open_path = NULL;

		open_path = gtk_tree_row_reference_get_path(folderview->opened);
		if (open_path && gtk_tree_path_compare(open_path, path) == 0) {
			gtk_tree_path_free(open_path);
			gtk_tree_path_free(path);
			return;
		}
		gtk_tree_path_free(open_path);
	}

	folderview->selection_locked = TRUE;

	GTK_EVENTS_FLUSH();
	opened = summary_show(folderview->summaryview, item, FALSE);

	if (opened) {
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = gtk_tree_row_reference_new(model, path);
		gtk_tree_view_scroll_to_cell
			(GTK_TREE_VIEW(folderview->treeview), path, NULL, FALSE,
			 0.0, 0.0);
		if (item->total > 0) {
			/* don't let GtkTreeView::gtk_tree_view_button_press()
			 * grab focus */
			GTK_WIDGET_UNSET_FLAGS(folderview->treeview,
					       GTK_CAN_FOCUS);
			g_idle_add(folderview_focus_idle_func, folderview);
		}
	} else
		folderview_select_row_ref(folderview, folderview->opened);

	gtk_tree_path_free(path);

	folderview->selection_locked = FALSE;
}

static void folderview_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter,
				    GtkTreePath *path, FolderView *folderview)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	FolderItem *item = NULL;
	GtkTreeIter iter_;
	gboolean valid;

	folderview->open_folder = FALSE;

	gtk_tree_model_get(GTK_TREE_MODEL(folderview->store), iter,
			   COL_FOLDER_ITEM, &item, -1);
	g_return_if_fail(item != NULL);
	item->collapsed = FALSE;
	folderview_update_row(folderview, iter);

	valid = gtk_tree_model_iter_children(model, &iter_, iter);

	while (valid) {
		FolderItem *child_item = NULL;

		gtk_tree_model_get(model, &iter_, COL_FOLDER_ITEM, &child_item,
				   -1);
		if (child_item && child_item->node->children &&
		    !child_item->collapsed) {
			GtkTreePath *path;

			path = gtk_tree_model_get_path(model, &iter_);
			gtk_tree_view_expand_row
				(GTK_TREE_VIEW(folderview->treeview),
				 path, FALSE);
			gtk_tree_path_free(path);
		}
		valid = gtk_tree_model_iter_next(model, &iter_);
	}
}

static void folderview_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter,
				     GtkTreePath *path, FolderView *folderview)
{
	FolderItem *item = NULL;

	folderview->open_folder = FALSE;

	gtk_tree_model_get(GTK_TREE_MODEL(folderview->store), iter,
			   COL_FOLDER_ITEM, &item, -1);
	g_return_if_fail(item != NULL);
	item->collapsed = TRUE;
	folderview_update_row(folderview, iter);
}

static void folderview_popup_close(GtkMenuShell *menu_shell,
				   FolderView *folderview)
{
	GtkTreePath *path;

	if (!folderview->prev_selected) return;

	path = gtk_tree_row_reference_get_path(folderview->prev_selected);
	gtk_tree_row_reference_free(folderview->prev_selected);
	folderview->prev_selected = NULL;
	if (!path)
		return;
	gtk_tree_selection_select_path(folderview->selection, path);
	gtk_tree_path_free(path);
}

static void folderview_col_resized(GtkWidget *widget, GtkAllocation *allocation,
				   FolderView *folderview)
{
	GtkTreeViewColumn *column;
	gint type;
	gint width = allocation->width;

	for (type = 0; type <= COL_TOTAL; type++) {
		column = gtk_tree_view_get_column
			(GTK_TREE_VIEW(folderview->treeview), type);
		if (column && column->button == widget) {
			switch (type) {
			case COL_FOLDER_NAME:
				prefs_common.folder_col_folder = width;
				break;
			case COL_NEW:
				prefs_common.folder_col_new = width;
				break;
			case COL_UNREAD:
				prefs_common.folder_col_unread = width;
				break;
			case COL_TOTAL:
				prefs_common.folder_col_total = width;
				break;
			default:
				break;
			}
			break;
		}
	}
}

static void folderview_download_func(Folder *folder, FolderItem *item,
				     gpointer data)
{
	GList *list;

	for (list = folderview_list; list != NULL; list = list->next) {
		FolderView *folderview = (FolderView *)list->data;
		MainWindow *mainwin = folderview->mainwin;
		gchar *str;

		str = g_strdup_printf
			(_("Downloading messages in %s ..."), item->path);
		main_window_progress_set(mainwin,
					 GPOINTER_TO_INT(data), item->total);
		STATUSBAR_PUSH(mainwin, str);
		STATUSBAR_POP(mainwin);
		g_free(str);
	}
}

static void folderview_download_cb(FolderView *folderview, guint action,
				   GtkWidget *widget)
{
	MainWindow *mainwin = folderview->mainwin;
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;
	if (item->stype == F_VIRTUAL)
		return;

	g_return_if_fail(item->folder != NULL);

	if (!main_window_toggle_online_if_offline(folderview->mainwin))
		return;

	main_window_cursor_wait(mainwin);
	inc_lock();
	main_window_lock(mainwin);
	gtk_widget_set_sensitive(folderview->treeview, FALSE);
	main_window_progress_on(mainwin);
	GTK_EVENTS_FLUSH();
	folder_set_ui_func(item->folder, folderview_download_func, NULL);
	if (folder_item_fetch_all_msg(item) < 0) {
		gchar *name;

		name = trim_string(item->name, 32);
		alertpanel_error(_("Error occurred while downloading messages in `%s'."), name);
		g_free(name);
	}
	folder_set_ui_func(item->folder, NULL, NULL);
	main_window_progress_off(mainwin);
	gtk_widget_set_sensitive(folderview->treeview, TRUE);
	main_window_unlock(mainwin);
	inc_unlock();
	main_window_cursor_normal(mainwin);
	statusbar_pop_all();
}

static void folderview_update_tree_cb(FolderView *folderview, guint action,
				      GtkWidget *widget)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (action == 0)
		folderview_check_new(item->folder);
	else
		folderview_rescan_tree(folderview, item->folder);
}

static void folderview_update_summary_cb(FolderView *folderview, guint action,
					 GtkWidget *widget)
{
	if (!folderview->summaryview->folder_item)
		return;

	GTK_EVENTS_FLUSH();
	summary_show(folderview->summaryview,
		     folderview->summaryview->folder_item, TRUE);
}

static void folderview_mark_all_read_cb(FolderView *folderview, guint action,
					GtkWidget *widget)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	if (item == folderview->summaryview->folder_item)
		summary_mark_all_read(folderview->summaryview);
	else {
		procmsg_mark_all_read(item);
		folderview_update_item(item, FALSE);
		trayicon_set_tooltip(NULL);
		trayicon_set_notify(FALSE);
	}
}

static void folderview_new_folder_cb(FolderView *folderview, guint action,
				     GtkWidget *widget)
{
	FolderItem *item;
	FolderItem *new_item;
	gchar *new_folder;
	gchar *name;
	gchar *p;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (FOLDER_TYPE(item->folder) == F_IMAP)
		g_return_if_fail(item->folder->account != NULL);

	if (FOLDER_TYPE(item->folder) == F_IMAP) {
		new_folder = input_dialog
			(_("New folder"),
			 _("Input the name of new folder:\n"
			   "(if you want to create a folder to store subfolders,\n"
			   " append `/' at the end of the name)"),
			 _("NewFolder"));
	} else {
		new_folder = input_dialog(_("New folder"),
					  _("Input the name of new folder:"),
					  _("NewFolder"));
	}
	if (!new_folder) return;
	AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});

	p = strchr(new_folder, G_DIR_SEPARATOR);
	if ((p && FOLDER_TYPE(item->folder) != F_IMAP) ||
	    (p && FOLDER_TYPE(item->folder) == F_IMAP && *(p + 1) != '\0')) {
		alertpanel_error(_("`%c' can't be included in folder name."),
				 G_DIR_SEPARATOR);
		return;
	}

	name = trim_string(new_folder, 32);
	AUTORELEASE_STR(name, {g_free(name); return;});

	/* find whether the directory already exists */
	if (folder_find_child_item_by_name(item, new_folder)) {
		alertpanel_error(_("The folder `%s' already exists."), name);
		return;
	}

	new_item = item->folder->klass->create_folder(item->folder, item,
						      new_folder);
	if (!new_item) {
		alertpanel_error(_("Can't create the folder `%s'."), name);
		return;
	}

	folderview_append_item(folderview, NULL, new_item, TRUE);
	folder_write_list();
}

static void folderview_rename_folder_cb(FolderView *folderview, guint action,
					GtkWidget *widget)
{
	FolderItem *item;
	gchar *new_folder;
	gchar *name;
	gchar *message;
	gchar *old_path;
	gchar *old_id;
	gchar *new_id;
	GtkTreePath *sel_path;
	GtkTreePath *open_path = NULL;
	GtkTreeIter iter;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);

	name = trim_string(item->name, 32);
	message = g_strdup_printf(_("Input new name for `%s':"), name);
	new_folder = input_dialog(_("Rename folder"), message,
				  g_basename(item->path));
	g_free(message);
	g_free(name);
	if (!new_folder) {
		gtk_tree_path_free(sel_path);
		return;
	}
	AUTORELEASE_STR(new_folder, {g_free(new_folder); gtk_tree_path_free(sel_path); return;});

	if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
		alertpanel_error(_("`%c' can't be included in folder name."),
				 G_DIR_SEPARATOR);
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folder_find_child_item_by_name(item->parent, new_folder)) {
		name = trim_string(new_folder, 32);
		alertpanel_error(_("The folder `%s' already exists."), name);
		g_free(name);
		gtk_tree_path_free(sel_path);
		return;
	}

	Xstrdup_a(old_path, item->path, {g_free(new_folder); return;});
	old_id = folder_item_get_identifier(item);

	if (item->stype == F_VIRTUAL) {
		if (virtual_get_class()->rename_folder(item->folder, item,
						       new_folder) < 0) {
			alertpanel_error(_("Can't rename the folder '%s'."),
					 item->name);
			g_free(old_id);
			gtk_tree_path_free(sel_path);
			return;
		}
	} else if (item->folder->klass->rename_folder(item->folder, item,
						      new_folder) < 0) {
		alertpanel_error(_("Can't rename the folder '%s'."),
				 item->name);
		g_free(old_id);
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folder_get_default_folder() == item->folder)
		filter_list_rename_path(old_path, item->path);
	new_id = folder_item_get_identifier(item);
	filter_list_rename_path(old_id, new_id);
	g_free(old_id);
	g_free(new_id);

	if (folderview->opened)
		open_path = gtk_tree_row_reference_get_path(folderview->opened);
	if (sel_path) {
		gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store),
					&iter, sel_path);
		folderview_update_row(folderview, &iter);
	}
	if (sel_path && open_path &&
	    (gtk_tree_path_compare(open_path, sel_path) == 0 ||
	     gtk_tree_path_is_ancestor(sel_path, open_path))) {
		GtkTreeRowReference *row;

		row = gtk_tree_row_reference_copy(folderview->opened);
		folderview_unselect(folderview);
		folderview_select_row_ref(folderview, row);
		gtk_tree_row_reference_free(row);
	}
	gtk_tree_path_free(open_path);
	gtk_tree_path_free(sel_path);

	folder_write_list();
}

static void folderview_move_folder_cb(FolderView *folderview, guint action,
				      GtkWidget *widget)
{
	FolderItem *item;
	FolderItem *new_parent;
	GtkTreePath *sel_path;
	GtkTreePath *open_path = NULL;
	GtkTreeIter iter;
	gchar *old_path, *old_id, *new_id;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);
	g_return_if_fail(sel_path != NULL);

	new_parent = foldersel_folder_sel(item->folder, FOLDER_SEL_MOVE_FOLDER,
					  NULL);
	if (!new_parent || new_parent->folder != item->folder ||
	    new_parent == item->parent || new_parent->stype == F_VIRTUAL) {
		gtk_tree_path_free(sel_path);
		return;
	}

	old_path = g_strdup(item->path);
	old_id = folder_item_get_identifier(item);

	if (item->folder->klass->move_folder
		(item->folder, item, new_parent) < 0) {
		alertpanel_error(_("Can't move the folder `%s'."), item->name);
		g_free(old_id);
		g_free(old_path);
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folder_get_default_folder() == item->folder)
		filter_list_rename_path(old_path, item->path);
	new_id = folder_item_get_identifier(item);
	filter_list_rename_path(old_id, new_id);
	g_free(new_id);
	g_free(old_id);
	g_free(old_path);

	if (folderview->opened)
		open_path = gtk_tree_row_reference_get_path(folderview->opened);
	gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter,
				sel_path);
	if (sel_path && open_path &&
	    (gtk_tree_path_compare(open_path, sel_path) == 0 ||
	     gtk_tree_path_is_ancestor(sel_path, open_path))) {
		summary_clear_all(folderview->summaryview);
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}
	gtk_tree_path_free(open_path);
	gtk_tree_path_free(sel_path);

	gtk_tree_store_remove(folderview->store, &iter);
	if (folderview_insert_item_recursive(folderview, item, &iter)) {
		gtkut_tree_view_expand_parent_all
			(GTK_TREE_VIEW(folderview->treeview), &iter);
	}

	folder_write_list();
}

static void folderview_delete_folder_cb(FolderView *folderview, guint action,
					GtkWidget *widget)
{
	Folder *folder;
	FolderItem *item;
	gchar *message, *name;
	AlertValue avalue;
	gchar *old_path;
	gchar *old_id;
	GtkTreePath *sel_path, *open_path = NULL;
	GtkTreeIter iter;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);
	g_return_if_fail(sel_path != NULL);

	folder = item->folder;

	name = trim_string(item->name, 32);
	AUTORELEASE_STR(name, {g_free(name); gtk_tree_path_free(sel_path); return;});
	if (item->stype == F_VIRTUAL) {
		message = g_strdup_printf
			(_("Delete the search folder '%s' ?\n"
			   "The real messages are not deleted."), name);
		avalue = alertpanel_full(_("Delete search folder"), message,
					 ALERT_WARNING, G_ALERTALTERNATE, FALSE,
					 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	} else {
		message = g_strdup_printf
			(_("All folders and messages under '%s' will be permanently deleted.\n"
			   "Recovery will not be possible.\n\n"
			   "Do you really want to delete?"), name);
		avalue = alertpanel_full(_("Delete folder"), message,
					 ALERT_WARNING, G_ALERTALTERNATE, FALSE,
					 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	}
	g_free(message);
	if (avalue != G_ALERTDEFAULT) {
		gtk_tree_path_free(sel_path);
		return;
	}

	Xstrdup_a(old_path, item->path, return);
	old_id = folder_item_get_identifier(item);

	if (folderview->opened)
		open_path = gtk_tree_row_reference_get_path(folderview->opened);
	gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter,
				sel_path);
	if (sel_path && open_path &&
	    (gtk_tree_path_compare(open_path, sel_path) == 0 ||
	     gtk_tree_path_is_ancestor(sel_path, open_path))) {
		summary_clear_all(folderview->summaryview);
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}
	gtk_tree_path_free(open_path);
	gtk_tree_path_free(sel_path);

	if (item->stype == F_VIRTUAL) {
		if (virtual_get_class()->remove_folder(folder, item) < 0) {
			alertpanel_error(_("Can't remove the folder '%s'."),
					 name);
			g_free(old_id);
			return;
		}
	} else if (folder->klass->remove_folder(folder, item) < 0) {
		alertpanel_error(_("Can't remove the folder '%s'."), name);
		g_free(old_id);
		return;
	}

	if (folder_get_default_folder() == folder)
		filter_list_delete_path(old_path);
	filter_list_delete_path(old_id);
	g_free(old_id);

	gtk_tree_store_remove(folderview->store, &iter);

	folder_write_list();
}

static void folderview_empty_trash_cb(FolderView *folderview, guint action,
				      GtkWidget *widget)
{
	FolderItem *item;
	Folder *folder;
	GtkTreePath *sel_path, *open_path;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	folder = item->folder;

	if (folder->trash != item) return;
	if (item->stype != F_TRASH) return;

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);

	if (alertpanel(_("Empty trash"),
		       _("Delete all messages in the trash folder?"),
		       GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT) {
		gtk_tree_path_free(sel_path);
		return;
	}

	procmsg_empty_trash(folder->trash);
	statusbar_pop_all();
	folderview_update_item(folder->trash, TRUE);
	trayicon_set_tooltip(NULL);
	trayicon_set_notify(FALSE);

	open_path = gtk_tree_row_reference_get_path(folderview->opened);
	if (open_path && sel_path &&
	    gtk_tree_path_compare(open_path, sel_path) == 0)
		gtk_widget_grab_focus(folderview->treeview);
	gtk_tree_path_free(open_path);
	gtk_tree_path_free(sel_path);
}

static void folderview_remove_mailbox_cb(FolderView *folderview, guint action,
					 GtkWidget *widget)
{
	FolderItem *item;
	gchar *name;
	gchar *message;
	AlertValue avalue;
	GtkTreePath *sel_path;
	GtkTreeIter iter;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);
	if (item->parent) return;

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);

	name = trim_string(item->folder->name, 32);
	message = g_strdup_printf
		(_("Really remove the mailbox `%s' ?\n"
		   "(The messages are NOT deleted from the disk)"), name);
	avalue = alertpanel_full(_("Remove mailbox"), message,
				 ALERT_WARNING, G_ALERTALTERNATE, FALSE,
				 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	g_free(message);
	g_free(name);
	if (avalue != G_ALERTDEFAULT) {
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folderview->summaryview->folder_item &&
	    folderview->summaryview->folder_item->folder == item->folder) {
		summary_clear_all(folderview->summaryview);
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}
	folder_destroy(item->folder);

	if (sel_path) {
		gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store),
					&iter, sel_path);
		gtk_tree_path_free(sel_path);
		gtk_tree_store_remove(folderview->store, &iter);
	}

	folder_write_list();
}

static void folderview_rm_imap_server_cb(FolderView *folderview, guint action,
					 GtkWidget *widget)
{
	FolderItem *item;
	PrefsAccount *account;
	gchar *name;
	gchar *message;
	AlertValue avalue;
	GtkTreePath *sel_path;
	GtkTreeIter iter;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);
	g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
	g_return_if_fail(item->folder->account != NULL);

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);

	name = trim_string(item->folder->name, 32);
	message = g_strdup_printf(_("Really delete IMAP4 account `%s'?"), name);
	avalue = alertpanel_full(_("Delete IMAP4 account"), message,
				 ALERT_WARNING, G_ALERTALTERNATE, FALSE,
				 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	g_free(message);
	g_free(name);

	if (avalue != G_ALERTDEFAULT) {
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folderview->summaryview->folder_item &&
	    folderview->summaryview->folder_item->folder == item->folder) {
		summary_clear_all(folderview->summaryview);
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}

	account = item->folder->account;
	folder_destroy(item->folder);
	account_destroy(account);
	account_write_config_all();

	if (sel_path) {
		gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store),
					&iter, sel_path);
		gtk_tree_path_free(sel_path);
		gtk_tree_store_remove(folderview->store, &iter);
	}

	account_set_menu();
	main_window_reflect_prefs_all();
	folder_write_list();
}

static void folderview_new_news_group_cb(FolderView *folderview, guint action,
					 GtkWidget *widget)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	Folder *folder;
	FolderItem *item;
	FolderItem *rootitem = NULL;
	FolderItem *newitem;
	GSList *new_subscr;
	GSList *cur;
	GNode *gnode;
	GtkTreePath *server_path;
	GtkTreeIter iter, root;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	folder = item->folder;
	g_return_if_fail(folder != NULL);
	g_return_if_fail(FOLDER_TYPE(folder) == F_NEWS);
	g_return_if_fail(folder->account != NULL);

	server_path = gtk_tree_row_reference_get_path(folderview->selected);
	g_return_if_fail(server_path != NULL);
	gtk_tree_model_get_iter(model, &iter, server_path);
	gtk_tree_path_free(server_path);

	if (!gtk_tree_model_iter_parent(model, &root, &iter))
		root = iter;

	gtk_tree_model_get(model, &root, COL_FOLDER_ITEM, &rootitem, -1);

	new_subscr = subscribe_dialog(folder);

	/* remove unsubscribed newsgroups */
	for (gnode = folder->node->children; gnode != NULL; ) {
		GNode *next = gnode->next;
		GtkTreeIter found;

		item = FOLDER_ITEM(gnode->data);
		if (g_slist_find_custom(new_subscr, item->path,
					(GCompareFunc)g_ascii_strcasecmp)
		    != NULL) {
			gnode = next;
			continue;
		}

		if (!gtkut_tree_model_find_by_column_data
			(model, &found, &root, COL_FOLDER_ITEM, item)) {
			gnode = next;
			continue;
		}

		if (folderview->summaryview->folder_item == item) {
			summary_clear_all(folderview->summaryview);
			gtk_tree_row_reference_free(folderview->opened);
			folderview->opened = NULL;
		}

		folder_item_remove(item);
		gtk_tree_store_remove(folderview->store, &found);

		gnode = next;
	}

	/* add subscribed newsgroups */
	for (cur = new_subscr; cur != NULL; cur = cur->next) {
		gchar *name = (gchar *)cur->data;

		if (folder_find_child_item_by_name(rootitem, name) != NULL)
			continue;

		newitem = folder_item_new(name, name);
		folder_item_append(rootitem, newitem);
		folderview_append_item(folderview, NULL, newitem, TRUE);
	}

	if (new_subscr) {
		server_path = gtk_tree_model_get_path(model, &root);
		gtk_tree_view_expand_row(GTK_TREE_VIEW(folderview->treeview),
					 server_path, FALSE);
		gtk_tree_path_free(server_path);
	}

	slist_free_strings(new_subscr);
	g_slist_free(new_subscr);

	folder_write_list();
}

static void folderview_rm_news_group_cb(FolderView *folderview, guint action,
					GtkWidget *widget)
{
	FolderItem *item;
	gchar *name;
	gchar *message;
	AlertValue avalue;
	GtkTreePath *sel_path, *open_path = NULL;
	GtkTreeIter iter;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	if (item->stype == F_VIRTUAL) {
		folderview_delete_folder_cb(folderview, 0, widget);
		return;
	}

	g_return_if_fail(item->folder != NULL);
	g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
	g_return_if_fail(item->folder->account != NULL);

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);
	g_return_if_fail(sel_path != NULL);

	name = trim_string_before(item->path, 32);
	message = g_strdup_printf(_("Really delete newsgroup `%s'?"), name);
	avalue = alertpanel_full(_("Delete newsgroup"), message,
				 ALERT_WARNING, G_ALERTALTERNATE, FALSE,
				 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	g_free(message);
	g_free(name);
	if (avalue != G_ALERTDEFAULT) {
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folderview->opened)
		open_path = gtk_tree_row_reference_get_path(folderview->opened);
	gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store), &iter,
				sel_path);
	if (open_path && sel_path &&
	    gtk_tree_path_compare(open_path, sel_path) == 0) {
		summary_clear_all(folderview->summaryview);
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}
	gtk_tree_path_free(open_path);
	gtk_tree_path_free(sel_path);

	folder_item_remove(item);
	gtk_tree_store_remove(folderview->store, &iter);
	folder_write_list();
}

static void folderview_rm_news_server_cb(FolderView *folderview, guint action,
					 GtkWidget *widget)
{
	FolderItem *item;
	PrefsAccount *account;
	gchar *name;
	gchar *message;
	AlertValue avalue;
	GtkTreePath *sel_path;
	GtkTreeIter iter;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);
	g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
	g_return_if_fail(item->folder->account != NULL);

	sel_path = gtk_tree_row_reference_get_path(folderview->selected);

	name = trim_string(item->folder->name, 32);
	message = g_strdup_printf(_("Really delete news account `%s'?"), name);
	avalue = alertpanel_full(_("Delete news account"), message,
				 ALERT_WARNING, G_ALERTALTERNATE, FALSE,
				 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
	g_free(message);
	g_free(name);

	if (avalue != G_ALERTDEFAULT) {
		gtk_tree_path_free(sel_path);
		return;
	}

	if (folderview->summaryview->folder_item &&
	    folderview->summaryview->folder_item->folder == item->folder) {
		summary_clear_all(folderview->summaryview);
		gtk_tree_row_reference_free(folderview->opened);
		folderview->opened = NULL;
	}

	account = item->folder->account;
	folder_destroy(item->folder);
	account_destroy(account);
	account_write_config_all();

	if (sel_path) {
		gtk_tree_model_get_iter(GTK_TREE_MODEL(folderview->store),
					&iter, sel_path);
		gtk_tree_path_free(sel_path);
		gtk_tree_store_remove(folderview->store, &iter);
	}

	account_set_menu();
	main_window_reflect_prefs_all();
	folder_write_list();
}

static void folderview_search_cb(FolderView *folderview, guint action,
				 GtkWidget *widget)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	if (item->stype == F_VIRTUAL) {
		GtkTreePath *sel_path, *open_path;

		sel_path = gtk_tree_row_reference_get_path
			(folderview->selected);
		open_path = gtk_tree_row_reference_get_path(folderview->opened);

		if (prefs_search_folder_open(item)) {
			if (sel_path && open_path &&
			    gtk_tree_path_compare(open_path, sel_path) == 0) {
				GtkTreeRowReference *row;
				row = gtk_tree_row_reference_copy(folderview->opened);
				folderview_unselect(folderview);
				summary_clear_all(folderview->summaryview);
				folderview_select_row_ref(folderview, row);
				gtk_tree_row_reference_free(row);
			}
		}

		gtk_tree_path_free(open_path);
		gtk_tree_path_free(sel_path);
	} else
		query_search(item);
}

static void folderview_property_cb(FolderView *folderview, guint action,
				   GtkWidget *widget)
{
	FolderItem *item;

	item = folderview_get_selected_item(folderview);
	if (!item)
		return;

	g_return_if_fail(item->folder != NULL);

	if (item->parent == NULL && item->folder->account)
		account_open(item->folder->account);
	else
		prefs_folder_item_open(item);
}

static gint auto_expand_timeout(gpointer data)
{
	FolderView *folderview = data;
	GtkTreeView *treeview = GTK_TREE_VIEW(folderview->treeview);
	GtkTreePath *path = NULL;

	gtk_tree_view_get_drag_dest_row(treeview, &path, NULL);

	if (path) {
		gtk_tree_view_expand_row(treeview, path, FALSE);
		gtk_tree_path_free(path);
		folderview->expand_timeout = 0;

		return FALSE;
	} else
		return TRUE;
}

static void remove_auto_expand_timeout(FolderView *folderview)
{
	if (folderview->expand_timeout != 0) {
		g_source_remove(folderview->expand_timeout);
		folderview->expand_timeout = 0;
	}
}

static gint auto_scroll_timeout(gpointer data)
{
	FolderView *folderview = data;

	gtkut_tree_view_vertical_autoscroll
		(GTK_TREE_VIEW(folderview->treeview));

	return TRUE;
}

static void remove_auto_scroll_timeout(FolderView *folderview)
{
	if (folderview->scroll_timeout != 0) {
		g_source_remove(folderview->scroll_timeout);
		folderview->scroll_timeout = 0;
	}
}

static gboolean folderview_drag_motion_cb(GtkWidget      *widget,
					  GdkDragContext *context,
					  gint            x,
					  gint            y,
					  guint           time,
					  FolderView     *folderview)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreePath *path = NULL, *prev_path = NULL;
	GtkTreeIter iter;
	FolderItem *item = NULL, *src_item = NULL;
	gboolean acceptable = FALSE;

	if (gtk_tree_view_get_dest_row_at_pos
		(GTK_TREE_VIEW(widget), x, y, &path, NULL)) {
		gtk_tree_model_get_iter(model, &iter, path);
		gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1);
		src_item = folderview->summaryview->folder_item;
		if (src_item && src_item != item &&
		    src_item->stype != F_QUEUE && item->stype != F_QUEUE &&
		    item->stype != F_VIRTUAL)
			acceptable = FOLDER_ITEM_CAN_ADD(item);
	} else
		remove_auto_expand_timeout(folderview);

	gtk_tree_view_get_drag_dest_row(GTK_TREE_VIEW(widget),
					&prev_path, NULL);
	if (!path || (prev_path && gtk_tree_path_compare(path, prev_path) != 0))
		remove_auto_expand_timeout(folderview);
	if (prev_path)
		gtk_tree_path_free(prev_path);

	gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(widget), path,
					GTK_TREE_VIEW_DROP_INTO_OR_AFTER);

	if (path) {
		if (folderview->expand_timeout == 0) {
			folderview->expand_timeout =
				g_timeout_add(1000, auto_expand_timeout,
					      folderview);
		} else if (folderview->scroll_timeout == 0) {
			folderview->scroll_timeout =
				g_timeout_add(150, auto_scroll_timeout,
					      folderview);
		}
	}

#ifdef G_OS_WIN32
	/* Win32 hack: somehow context->actions is not properly set on Win32 */
	{
		GdkWindow *rootwin;
		GdkModifierType state;

		rootwin = gtk_widget_get_root_window(widget);
		gdk_window_get_pointer(rootwin, NULL, NULL, &state);
		if ((state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == 0)
			context->actions = GDK_ACTION_MOVE | GDK_ACTION_COPY;
	}
#endif

	if (acceptable) {
		if ((context->actions & GDK_ACTION_MOVE) != 0 &&
		    FOLDER_ITEM_CAN_ADD(src_item))
			gdk_drag_status(context, GDK_ACTION_MOVE, time);
		else if ((context->actions & GDK_ACTION_COPY) != 0)
			gdk_drag_status(context, GDK_ACTION_COPY, time);
		else if ((context->actions & GDK_ACTION_LINK) != 0)
			gdk_drag_status(context, GDK_ACTION_LINK, time);
		else
			gdk_drag_status(context, 0, time);
	} else
		gdk_drag_status(context, 0, time);

	if (path)
		gtk_tree_path_free(path);

	return TRUE;
}

static void folderview_drag_leave_cb(GtkWidget      *widget,
				     GdkDragContext *context,
				     guint           time,
				     FolderView     *folderview)
{
	remove_auto_expand_timeout(folderview);
	remove_auto_scroll_timeout(folderview);

	gtk_tree_view_set_drag_dest_row
		(GTK_TREE_VIEW(widget), NULL, GTK_TREE_VIEW_DROP_INTO_OR_AFTER);
}

static void folderview_drag_received_cb(GtkWidget        *widget,
					GdkDragContext   *context,
					gint              x,
					gint              y,
					GtkSelectionData *data,
					guint             info,
					guint             time,
					FolderView       *folderview)
{
	GtkTreeModel *model = GTK_TREE_MODEL(folderview->store);
	GtkTreePath *path = NULL;
	GtkTreeIter iter;
	FolderItem *item = NULL, *src_item;

	remove_auto_expand_timeout(folderview);
	remove_auto_scroll_timeout(folderview);

	if (!gtk_tree_view_get_dest_row_at_pos
		(GTK_TREE_VIEW(widget), x, y, &path, NULL))
		return;

	gtk_tree_model_get_iter(model, &iter, path);
	gtk_tree_model_get(model, &iter, COL_FOLDER_ITEM, &item, -1);
	src_item = folderview->summaryview->folder_item;

	if (FOLDER_ITEM_CAN_ADD(item) && src_item && src_item != item &&
	    src_item->stype != F_QUEUE && item->stype != F_QUEUE &&
	    item->stype != F_VIRTUAL) {
		if ((context->actions & GDK_ACTION_MOVE) != 0 &&
		    FOLDER_ITEM_CAN_ADD(src_item)) {
			summary_move_selected_to(folderview->summaryview, item);
			context->action = 0;
			gtk_drag_finish(context, TRUE, FALSE, time);
		} else if ((context->actions & GDK_ACTION_COPY) != 0) {
			summary_copy_selected_to(folderview->summaryview, item);
			gtk_drag_finish(context, TRUE, FALSE, time);
		} else
			gtk_drag_finish(context, FALSE, FALSE, time);
	} else
		gtk_drag_finish(context, FALSE, FALSE, time);

	gtk_tree_path_free(path);
}

static gint folderview_folder_name_compare(GtkTreeModel *model,
					   GtkTreeIter *a, GtkTreeIter *b,
					   gpointer data)
{
	FolderItem *item_a = NULL, *item_b = NULL;

	gtk_tree_model_get(model, a, COL_FOLDER_ITEM, &item_a, -1);
	gtk_tree_model_get(model, b, COL_FOLDER_ITEM, &item_b, -1);

	return folder_item_compare(item_a, item_b);
}
