/***************************************************************************
                             shared_dirs.c
                             -------------
    begin                : Sat Jul 27 2002
    copyright            : (C) 2002 by Tim-Philipp Müller
    email                : t.i.m@orange.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "global.h"
#include "core-conn.h"
#include "options.h"
#include "misc_gtk.h"
#include "misc_strings.h"

#include "statusbar.h"
#include "shared_dirs.h"
#include "shared_page.h"
#include "status_page.h"

#include <string.h>
#include <time.h>

#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtkfilesel.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkmenu.h>


enum
{
	SHARED_DIRS_RECORD_COLUMN = 0,
	SHARED_DIRS_ICON_COLUMN,
	SHARED_DIRS_NAME_COLUMN,
	SHARED_DIRS_N_COLUMNS
};

#define SHARED_DIRS_TYPES_PLACEHOLDER \
	G_TYPE_POINTER,\
	GDK_TYPE_PIXBUF,\
	G_TYPE_STRING


struct _GuiSharedDir
{
	gchar        *name_utf8;
	gchar        *name_locale;
	guint         name_locale_hash;
	guint         idx;
	gboolean      unshare_flag;
	gboolean      remove_flag;
	GtkTreeIter   iter;          /* GtkListStore has persistent iters */
};

typedef struct _GuiSharedDir  GuiSharedDir;


static GtkWidget     *shared_dirs_view = NULL;

static GtkListStore  *shared_dirs_store = NULL;

static gchar         *shared_dirs_fileselector_lastdir = NULL;

static GList         *dirs; /* NULL */ /* for the number of dirs we have, a linked list should be efficient enough */

/* functions */

static gint     helper_send_remove_records_sort_func (GuiSharedDir *dir1, GuiSharedDir *dir2);

static void     shared_dirs_send_request (guint millisecs);

static gint     shared_dirs_send_request_timeout (gpointer data);

static void     shared_dirs_add_dir (const gchar *path, int add_whole_structure);

static gboolean check_if_path_is_allowed (const gchar *addpath);

static void     shared_dirs_menu_onRefresh (GtkWidget *widget, gpointer data);

static void     shared_dirs_menu_onUnshareSelectedDirectoryTrees (GtkWidget *widget, gpointer data);

static void     shared_dirs_menu_onUnshareSelectedDirectories (GtkWidget *widget, gpointer data);

static void     shared_dirs_menu_onAddDirectoryTreeToShare (GtkWidget *widget, gpointer data);

static void     shared_dirs_popup_menu (guint button, guint32 activate_time);

static gint     shared_dirs_onButtonPress (GtkWidget *widget, GdkEventButton *event, gpointer data);

static gint     shared_dirs_onPopupMenu (GtkWidget *widget, gpointer data);



/******************************************************************************
 *
 *    gui_shared_dir_free
 *
 ******************************************************************************/

static void
gui_shared_dir_free (GuiSharedDir *dir)
{
	g_return_if_fail ( dir != NULL );

	G_FREE(dir->name_utf8);
	G_FREE(dir->name_locale);

	memset(dir, 0x00, sizeof(GuiSharedDir));
	g_free(dir);
}


/******************************************************************************
 *
 *    shared_dirs_onSharedDirs
 *
 ******************************************************************************/

static void
shared_dirs_onSharedDirs (GtkListStore *store, guint num, const gchar **dirname_arr, gpointer data)
{
	GList  *node;
	guint   n, dirname_hash;

	/* Flag all dirs in the shared dirs list for removal. */
	for (node = dirs;  node != NULL && node->data != NULL;  node = node->next)
		((GuiSharedDir*)node->data)->remove_flag = TRUE;

	for (n = 0;  n < num;  ++n)
	{
		GuiSharedDir  *dir = NULL;

		g_return_if_fail ( dirname_arr[n] != NULL );

		dirname_hash = g_str_hash(dirname_arr[n]);

		/* Check whether we already have this dir. If yes, just
		 *  update the values (index number and flags */
		for (node = dirs;  node != NULL && dir == NULL;  node = node->next)
		{
			GuiSharedDir *checkdir = (GuiSharedDir*) node->data;

			if ((checkdir) && checkdir->name_locale_hash == dirname_hash)
			{
				checkdir->idx          = n+1;
				checkdir->remove_flag  = FALSE;
				checkdir->unshare_flag = FALSE;
				dir = checkdir;
			}
		}

		if (dir)
			continue;

		/* if we don't have it yet, add it */
		dir = g_new0(GuiSharedDir, 1);

		gtk_list_store_append (store, &dir->iter);

		dir->idx              = n+1;
		dir->name_locale      = g_strdup(dirname_arr[n]);
		dir->name_utf8        = TO_UTF8(dirname_arr[n]);
		dir->name_locale_hash = g_str_hash(dirname_arr[n]);

		gtk_list_store_set (store, &dir->iter,
		                    SHARED_DIRS_RECORD_COLUMN, dir,
		                    SHARED_DIRS_ICON_COLUMN, get_icon (ICON_FILETYPE_FOLDER),
		                    SHARED_DIRS_NAME_COLUMN, dir->name_utf8,
		                    -1);

		dirs = g_list_append(dirs, dir);
	}

	/* Remove those that we did not receive this time (ie. old ones) */
	for (node = dirs;  node != NULL && node->data != NULL;  node = node->next)
	{
		GuiSharedDir *dir = (GuiSharedDir*) node->data;
		if (dir->remove_flag == TRUE)
		{
			gtk_list_store_remove (shared_dirs_store, &dir->iter);
			gui_shared_dir_free(dir);
			dirs = g_list_remove_all(dirs, dir);
		}
	}
}


/******************************************************************************
 *
 *   onCoreConnStatus
 *
 ******************************************************************************/

static void
onCoreConnStatus (GuiCoreConn *conn, guint status, GtkListStore *store)
{
	if (status != CONN_STATUS_COMMUNICATING && status != CONN_STATUS_AUTHENTICATING)
	{
		GList *node;

		gtk_list_store_clear (store);

		for (node = dirs;  node != NULL && node->data != NULL;  node = node->next)
		{
			GuiSharedDir *dir = (GuiSharedDir*) node->data;
			gui_shared_dir_free(dir);
		}

		g_list_free(dirs);
		dirs = NULL;
	}
}


/******************************************************************************
 *
 *  helper_send_remove_records_sort_func
 *
 *   callback for g_list_sort() to sort a linked list of GuiSharedDir records
 *   in reverse order of their index numbers (highest numbers first)
 *
 ******************************************************************************/

static gint
helper_send_remove_records_sort_func (GuiSharedDir *dir1, GuiSharedDir *dir2)
{
	g_return_val_if_fail ( dir1 && dir2, 0 );

	if (dir1->idx == dir2->idx)
		return 0;

	return (dir1->idx > dir2->idx) ? -1 : 1;
}


/******************************************************************************
 *
 *   shared_dirs_unshare_all_flagged
 *
 ******************************************************************************/

static void
shared_dirs_unshare_all_flagged (void)
{
	GuiSharedDir *dir;
	GList        *node, *reclist = NULL;

	/* get indices of all directories that are flagged for unsharing */
	for ( node = dirs;  node != NULL && node->data != NULL;  node = node->next )
	{
		dir = (GuiSharedDir*) node->data;

		if (dir->unshare_flag)
		{
			g_return_if_fail ( dir->idx > 0 );
			reclist = g_list_insert_sorted (reclist, dir, (GCompareFunc)helper_send_remove_records_sort_func);
		}
	}

	/* now that we have a sorted list with directory indices (highest first), unshare */
	for ( node = reclist;  node != NULL && node->data != NULL;  node = node->next )
	{
		guint num = ((GuiSharedDir*)node->data)->idx;

		status_message_blue (_("GUI: unsharing directory #%u\n"), num);
		gui_core_conn_send_unshare_dir_no(core, num);
	}

	g_list_free(reclist);

	shared_dirs_send_request(2000);
}


/******************************************************************************
 *
 * shareddirs_send_request
 *
 * updates the list of shared dirs in X millisecs
 *
 ******************************************************************************/

static gint
shared_dirs_send_request_timeout (gpointer data)
{
	gui_core_conn_send_get_shared_dirs(core);
	return 0; /* don't call us again */
}

static void
shared_dirs_send_request (guint millisecs)
{
	(void) g_timeout_add (millisecs, shared_dirs_send_request_timeout, NULL);
}


/******************************************************************************
 *
 *   check_if_path_is_allowed
 *
 *   displays warning message if path is not allowed.
 *
 *   returns 0 if path is not allowed, otherwise 1
 *
 ******************************************************************************/

static gboolean
check_if_path_is_allowed (const gchar *addpath)
{
	const gchar *msg = NULL;

	g_return_val_if_fail ( addpath  != NULL, FALSE );

	/* path entered was '/' (because tailing '/' has been chopped off) */
	if ( !(*addpath) )
	{
		msg = _("I don't think this is a sensible path to share. Sorry.");
	}
	else if (     strncmp(addpath, "/dev",     4) == 0
	           || strncmp(addpath, "/etc",     4) == 0
	           || strncmp(addpath, "/sbin",    5) == 0
	           || strncmp(addpath, "/bin",     4) == 0
	           || strncmp(addpath, "/boot",    5) == 0
	           || strncmp(addpath, "/proc",    5) == 0
	           || strncmp(addpath, "/usr/bin", 8) == 0
	           || strncmp(addpath, "/usr/sbin",9) == 0
	           || strcmp (addpath, "/home")       == 0 )
	{
		msg = _("I don't think this is a sensible path to share. Sorry.");
	}

	/* absolute path? */
	else if (gui_core_conn_is_local(core) &&  !g_path_is_absolute(addpath) )
	{
		msg = _("Please specify an absolute path (e.g. starting with '/')");
	}

	/* home path shared? -> caution! */
	else if ( strcmp (addpath, g_get_home_dir()) == 0 )
	{
		msg = _("You are trying to share your home "
		        "directory or your _whole_ home directory structure.\n"
		        "This is a security risk, as the donkey will automatically "
		        "include all hidden files and hidden subdirectories,\nwhich "
		        "might contain secret information like usernames and "
		        "passwords (think about the configuration file of\n your "
		        "mail client, or your .ssh subdirectory, or your .gnupg "
		        "subdirectory if you are using pgp public keys...).\n"
		        "However, you can share specific folders from your home "
		        "directory if you enter something like "
		        "\"/home/joe/sharethis\"");
	}

	else if (gui_core_conn_is_local(core) &&  !g_file_test(addpath, G_FILE_TEST_EXISTS) )
	{
		msg = _("The path you entered does not exist.");
	}

	else if (gui_core_conn_is_local(core) &&  !g_file_test(addpath, G_FILE_TEST_IS_DIR) )
	{
		msg = _("The path you entered is not a directory.");
	}

	if ( !msg )
		return TRUE;

	misc_gtk_ok_dialog (GTK_MESSAGE_WARNING, msg);

	return FALSE ;
}



/******************************************************************************
 *
 *   shared_dirs_add_dir
 *
 *   path has previously been returned from fileselector
 *     and is in on-disk encoding
 *
 ******************************************************************************/

static void
shared_dirs_add_dir (const gchar *path, int add_whole_structure) // auxiliary to file select dialg
{
	gchar   *addpath;

	if (!path)
		return;

	addpath = g_strdup(path);

	/* chop of tailing '/' if there */
	if ( addpath[strlen(addpath)-1] == G_DIR_SEPARATOR )
		addpath[strlen(addpath)-1] = 0x00;

	if ( !check_if_path_is_allowed(addpath) )
	{
		g_free(addpath);
		return;
	}

	if (add_whole_structure)
	{
		status_message_blue (_("Adding directory tree to shared files. If this does not work, it's the core's fault (really)."));
		gui_core_conn_send_add_shared_dir_structure(core, addpath);
	}
	else
	{
		gui_core_conn_send_add_shared_dir(core, addpath);
	}

	shared_dirs_send_request (3000);

	statusbar_msg (_(" The core is hashing the newly shared files..."));

	g_free(addpath);

	/* note: filelist will update automatically, because we'll get a
	 * Done loading shared file message" when the new files have been hashed */
}



/******************************************************************************
 *
 *   helper_flag_all_subdirs_for_unsharing
 *
 *   dirname should be in locale encoding
 *
 ******************************************************************************/

static void
helper_flag_all_subdirs_for_unsharing (const gchar *dirname)
{
	GuiSharedDir *dir;
	GList        *node;
	gint          parentdir_len, checkdir_len;

	g_return_if_fail (dirname != NULL);

	parentdir_len = strlen(dirname);

	for (node = dirs;  node != NULL && node->data != NULL;  node = node->next)
	{
		dir = (GuiSharedDir*)node->data;

		if (dir->unshare_flag == TRUE || !dir->name_locale)
			continue;

		checkdir_len = strlen(dir->name_locale);

		/* subdir path must have at least 2 chars more than parent dir (slash + char) */
		if (checkdir_len < (parentdir_len+2))
			continue;

		/* subdir must have slash where parent dir has terminating zero */
		if (dir->name_locale[parentdir_len] != G_DIR_SEPARATOR)
			continue;

		/* subdir and parent dir must match at the beginning */
		if (strncmp(dir->name_locale,dirname,parentdir_len)==0)
			dir->unshare_flag = TRUE;                      /* it's a subdir => flag it for unsharing */

		/* note: recursion here is not necessary, because
		 * /mnt/foo/blub/one/two/three is still a subdir of
		 * /mnt/foo/blub the way we check for it, so we do
		 * not need to find subdirs of /mnt/foo/blub/one here.
		 */
	}
}





/*----------------------------------------------------------------·
 |								  |
 |								  |
 |                            POPUP MENU			  |
 |								  |
 |								  |
 ·----------------------------------------------------------------·
*/

/******************************************************************************
 *
 *    shared_dirs_fileselector_tree_onOk
 *
 ******************************************************************************/

static void
shared_dirs_fileselector_tree_onOk (GtkWidget *w, GtkFileSelection *fs)
{

	if (shared_dirs_fileselector_lastdir)

		g_free (shared_dirs_fileselector_lastdir);

	shared_dirs_fileselector_lastdir =  g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs)));

	shared_dirs_add_dir (shared_dirs_fileselector_lastdir, 1);

	gtk_widget_destroy (GTK_WIDGET(fs));


}


/******************************************************************************
 *
 *    shared_dirs_fileselector_dir_onOk
 *
 ******************************************************************************/

static void
shared_dirs_fileselector_dir_onOk (GtkWidget *w, GtkFileSelection *fs)
{
	if (shared_dirs_fileselector_lastdir)
		g_free (shared_dirs_fileselector_lastdir);

	shared_dirs_fileselector_lastdir =  g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs)));

	shared_dirs_add_dir (shared_dirs_fileselector_lastdir, 0);

	gtk_widget_destroy (GTK_WIDGET(fs));
}


/******************************************************************************
 *
 *    shared_dirs_fileselector_onCancel
 *
 ******************************************************************************/

static void
shared_dirs_fileselector_onCancel (GtkWidget *widget, GtkWidget *dialog)
{
	G_FREE(shared_dirs_fileselector_lastdir);

	shared_dirs_fileselector_lastdir = g_strdup(gtk_file_selection_get_filename (GTK_FILE_SELECTION(dialog)));

	gtk_widget_destroy (dialog);
}


/******************************************************************************
 *
 *    shared_dirs_fileselector_dialog
 *
 ******************************************************************************/

static void
shared_dirs_fileselector_dialog (int add_structure_flag)
{
	GtkWidget  *filew;

	if ( add_structure_flag == 0 )
		filew = gtk_file_selection_new (UTF8_SHORT_PRINTF("%s", _(" Select a directory to share ")));
	else
		filew = gtk_file_selection_new (UTF8_SHORT_PRINTF("%s", _(" Select a directory tree to share ")));


	if (!shared_dirs_fileselector_lastdir)
	{
		shared_dirs_fileselector_lastdir = g_strdup_printf("%s%c", g_get_home_dir(), G_DIR_SEPARATOR);
	}

	gtk_file_selection_set_filename (GTK_FILE_SELECTION(filew), shared_dirs_fileselector_lastdir);

	/* cancel = destroy dialogue widget */

	g_signal_connect ( G_OBJECT(GTK_FILE_SELECTION(filew)->cancel_button),
	                   "clicked",
	                   G_CALLBACK(shared_dirs_fileselector_onCancel),
	                   filew);

	/* ok button */

	g_signal_connect ( G_OBJECT(GTK_FILE_SELECTION(filew)->ok_button),
	                   "clicked",
	                   (add_structure_flag==0) ? G_CALLBACK(shared_dirs_fileselector_dir_onOk)
	                                            : G_CALLBACK(shared_dirs_fileselector_tree_onOk),
	                   filew);


	/* from the gtk-app-devel-list, 25-03-99, by Kemal 'Disq' Hadimili - thank you. */
	gtk_widget_hide(GTK_FILE_SELECTION(filew)->file_list);
	gtk_widget_hide(GTK_FILE_SELECTION(filew)->file_list->parent);

	gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION(filew));
	gtk_widget_show(filew);
}


/******************************************************************************
 *
 *    shared_dirs_menu_onAddDirectoryToShare
 *
 ******************************************************************************/

static void
shared_dirs_menu_onAddDirectoryToShare (GtkWidget *widget, gpointer data)
{
	if (gui_core_conn_is_local(core))
	{
		shared_dirs_fileselector_dialog (0);
	}
	else
	{
		misc_gtk_ok_dialog (GTK_MESSAGE_INFO,
			_(" The core seems to be running on a \n"
			  " different computer than this one. \n"
			  " This means I am not able to show \n"
			  " you a directory selector dialog. \n"
			  " \n"
			  " Please type this into the toolbar: \n"
			  "   !a <full directory path>\n\n"));
	}
}


/******************************************************************************
 *
 *    shared_dirs_menu_onAddDirectoryTreeToShare
 *
 ******************************************************************************/

static void
shared_dirs_menu_onAddDirectoryTreeToShare (GtkWidget *widget, gpointer data)
{
	if (gui_core_conn_is_local(core))
	{
		shared_dirs_fileselector_dialog (1);
	}
	else
	{
		misc_gtk_ok_dialog (GTK_MESSAGE_INFO,
			_(" The core seems to be running on a \n"
			  " different computer than this one. \n"
			  " This means I am not able to show \n"
			  " you a directory selector dialog. \n"
			  " \n"
			  " Please type this into the toolbar: \n"
			  "   !a+ <full directory path>\n\n"));
	}
}



/******************************************************************************
 *
 *   for_each_helper_flag_selected_dirs_for_unsharing
 *
 ******************************************************************************/

static void
for_each_helper_flag_selected_dirs_for_unsharing (GtkTreeModel *model,
                                                  GtkTreePath  *path,
                                                  GtkTreeIter  *iter,
                                                  gpointer      data)
{
	GuiSharedDir *dir = NULL;

	gtk_tree_model_get(model, iter, SHARED_DIRS_RECORD_COLUMN, &dir, -1);

	if (dir)
		dir->unshare_flag = TRUE;
}


/******************************************************************************
 *
 *   for_each_helper_flag_selected_dir_trees_for_unsharing
 *
 ******************************************************************************/

static void
for_each_helper_flag_selected_dir_trees_for_unsharing (GtkTreeModel *model,
                                                       GtkTreePath *path,
                                                       GtkTreeIter *iter,
                                                       gpointer data)
{
	GuiSharedDir *dir = NULL;

	gtk_tree_model_get (GTK_TREE_MODEL(model), iter, SHARED_DIRS_RECORD_COLUMN, &dir, -1);
	g_return_if_fail ( dir != NULL );

	helper_flag_all_subdirs_for_unsharing (dir->name_locale);
	/* don't only flag subdirs, but selected dir as well */
	dir->unshare_flag = TRUE;
}


/******************************************************************************
 *
 *   shared_dirs_menu_onUnshareSelectedDirectories
 *
 ******************************************************************************/

static void
shared_dirs_menu_onUnshareSelectedDirectories (GtkWidget *widget, gpointer data)
{
	GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_dirs_view));

	g_return_if_fail (misc_gtk_tree_selection_count_selected_rows (selection) > 0);

	gtk_tree_selection_selected_foreach ( selection,
	                                      for_each_helper_flag_selected_dirs_for_unsharing,
	                                      NULL);

	shared_dirs_unshare_all_flagged();

	statusbar_msg (_(" Unshared selected directories."));
}


/******************************************************************************
 *
 *    shared_dirs_menu_onUnshareSelectedDirectoryTrees
 *
 ******************************************************************************/

static void
shared_dirs_menu_onUnshareSelectedDirectoryTrees (GtkWidget *widget, gpointer data)
{
	GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_dirs_view));

	g_return_if_fail (misc_gtk_tree_selection_count_selected_rows (selection) > 0);

	gtk_tree_selection_selected_foreach ( selection,
	                                      for_each_helper_flag_selected_dir_trees_for_unsharing,
	                                      NULL);

	shared_dirs_unshare_all_flagged();

	statusbar_msg (_(" Unshared selected directories and sub-directories. (list update might take a while)"));
}


/******************************************************************************
 *
 *    shared_dirs_menu_onRefresh
 *
 ******************************************************************************/

static void
shared_dirs_menu_onRefresh (GtkWidget *widget, gpointer data)
{
	gui_core_conn_send_get_shared_dirs(core);
	gui_core_conn_send_get_shared_files(core);
}


/******************************************************************************
 *
 *    shared_dirs_timeout_refresh_list
 *
 ******************************************************************************/

static gboolean
shared_dirs_timeout_refresh_list (gpointer baz)
{
	gui_core_conn_send_get_shared_dirs(core);
	gui_core_conn_send_get_shared_files(core);
	return FALSE; /* don't call again */
}


/******************************************************************************
 *
 *    shared_dirs_menu_onRehashSharedFiles
 *
 ******************************************************************************/

void
shared_dirs_menu_onRehashSharedFiles (GtkWidget *widget, gpointer data)
{
	static gboolean  working; /* FALSE */

	if (working)
		return;

	working = TRUE;

	status_message_blue ("%s\n", _(" Forcing core to rehash shared files. If you see a nonsense error message, ignore it."));

	/* make sure message is shown immediately */
	while ((gtk_events_pending()))
		gtk_main_iteration_do(FALSE);

	working = FALSE;

	/* old core versions: recheck all dirs by adding bogus directory                              */
	/* new core versions: add directory that is already shared to
	 *                    make it update the shared files
	 * very new versions (v53-pre-X and later): use 'refresh' command
	 */
	if (gui_core_conn_is_newer_than(core,  1, 4, 2004))
	{
		gui_core_conn_send_command (core, "refresh");
		g_timeout_add(15 * 1000, (GSourceFunc) shared_dirs_timeout_refresh_list, NULL);
	}
	else if (gui_core_conn_is_newer_than(core,  1, 1, 2003))
	{
		g_return_if_fail (dirs != NULL && dirs->data != NULL);

		gui_core_conn_send_add_shared_dir(core, ((GuiSharedDir*)dirs->data)->name_locale);

		if (gui_core_conn_is_newer_than(core,  1, 11, 2003))
		{
			if (!opt_get_bool(OPT_GUI_SLOW_GUI_CORE_CONNECTION))
				g_timeout_add(2  * 1000, (GSourceFunc) shared_dirs_timeout_refresh_list, NULL);

			g_timeout_add(15 * 1000, (GSourceFunc) shared_dirs_timeout_refresh_list, NULL);
		}
	}
	else
	{
		gui_core_conn_send_add_shared_dir(core, _("/ignore_this_error_message_it_is_to_force_the_core_to_rehash"));
	}
}



/******************************************************************************
 *
 *   shared_dirs_popup_menu
 *
 ******************************************************************************/

static void
shared_dirs_popup_menu (guint button, guint32 activate_time)
{
	GtkTreeSelection    *selection;
	GtkWidget           *menu;
	guint                sel_count;

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_dirs_view));
	sel_count = misc_gtk_tree_selection_count_selected_rows (selection);

	menu  = gtk_menu_new();

	misc_gtk_add_menu_header(menu, _("Shared directories"), NULL);

	misc_gtk_add_menu_item ( menu, _(" add directory to share... "),
	                         shared_dirs_menu_onAddDirectoryToShare, NULL, ICON_NONE, TRUE);

	misc_gtk_add_menu_item ( menu, _(" add directory tree to share... "),
	                         shared_dirs_menu_onAddDirectoryTreeToShare, NULL, ICON_NONE, TRUE);

	misc_gtk_add_menu_separator (menu);

	misc_gtk_add_menu_item ( menu, _(" unshare selected directories "),
	                         shared_dirs_menu_onUnshareSelectedDirectories, NULL, ICON_NONE, (sel_count>0) );

	misc_gtk_add_menu_item ( menu, _(" unshare selected directory trees "),
	                         shared_dirs_menu_onUnshareSelectedDirectoryTrees, NULL, ICON_NONE, (sel_count>0) );

	misc_gtk_add_menu_separator (menu);

	misc_gtk_add_menu_item ( menu, _(" refresh list "),
	                         shared_dirs_menu_onRefresh, NULL, ICON_NONE, TRUE );

	misc_gtk_add_menu_separator (menu);

	misc_gtk_add_menu_item ( menu, _(" rehash shared files "),
	                         shared_dirs_menu_onRehashSharedFiles, NULL, ICON_NONE, TRUE );


	gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, button, activate_time);
}


/******************************************************************************
 *
 *   shared_dirs_onPopupMenu
 *
 ******************************************************************************/

static gint
shared_dirs_onPopupMenu (GtkWidget *widget, gpointer data)
{
	shared_dirs_popup_menu (0, gdk_event_get_time(NULL));
	return TRUE;
}

/******************************************************************************
 *
 *   shared_dirs_onButtonPress
 *
 ******************************************************************************/

static gint
shared_dirs_onButtonPress (GtkWidget *widget, GdkEventButton *event, gpointer data)
{
	if ( event->button != 3 )
		return FALSE;

	shared_dirs_popup_menu (event->button, event->time);
	return TRUE;
}


/******************************************************************************
 *
 *   shared_dirs_create_view
 *
 *   Creates the GtkTreeView inside a scrolled window.
 *
 ******************************************************************************/

GtkWidget *
shared_dirs_create_view (void)
{
	GtkWidget           *scrollwin;
	GtkTreeViewColumn   *column;
	GtkCellRenderer     *renderer;
	GtkTreeSelection    *selection;

	scrollwin = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	shared_dirs_store = gtk_list_store_new ( SHARED_DIRS_N_COLUMNS,
	                                         SHARED_DIRS_TYPES_PLACEHOLDER );

	g_signal_connect_swapped(core, "shared-dirs", (GCallback) shared_dirs_onSharedDirs, shared_dirs_store);

	shared_dirs_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(shared_dirs_store));

	column = gtk_tree_view_column_new();
	gtk_tree_view_column_set_sort_column_id(column, SHARED_DIRS_NAME_COLUMN);

	renderer = gtk_cell_renderer_pixbuf_new();
	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer,
	                                    "pixbuf", SHARED_DIRS_ICON_COLUMN, NULL);
	gtk_tree_view_column_set_title(column, UTF8_SHORT_PRINTF("%s",_(" directory")));

	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN(column), renderer, FALSE);
	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer,
	                                    "text", SHARED_DIRS_NAME_COLUMN, NULL);
	gtk_tree_view_column_set_sort_column_id(column, SHARED_DIRS_NAME_COLUMN);
	gtk_tree_view_append_column(GTK_TREE_VIEW(shared_dirs_view), column);
	
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_dirs_view));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);

	g_signal_connect (G_OBJECT(shared_dirs_view), "button_press_event", G_CALLBACK(shared_dirs_onButtonPress), NULL);
	g_signal_connect (G_OBJECT(shared_dirs_view), "popup_menu", G_CALLBACK(shared_dirs_onPopupMenu), NULL);

	gtk_container_add (GTK_CONTAINER(scrollwin), shared_dirs_view);

	gtk_widget_show_all (scrollwin);

	/* make sure variables are set to NULL when objects are destroyed */

	g_object_set_data_full (G_OBJECT(shared_dirs_store), "foo1",
	                        &shared_dirs_store, (GDestroyNotify)g_nullify_pointer);

	g_object_set_data_full (G_OBJECT(shared_dirs_view), "foo1",
	                        &shared_dirs_view, (GDestroyNotify)g_nullify_pointer);

	/* make sure this string is freed when view is destroyed (in case string is not empty) */

	g_object_set_data_full (G_OBJECT(shared_dirs_view), "foo2",
	                        shared_dirs_fileselector_lastdir, (GDestroyNotify)g_free);

	g_signal_connect (core, "core-conn-status", (GCallback) onCoreConnStatus, shared_dirs_store);

	return scrollwin;
}



