/***************************************************************************
                               shared_files.c
                               --------------
    begin                : Wed Jul 10 2002
    copyright            : (C) 2001-2002 by Tim-Philipp Müller
    email                : t.i.m at 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 "core-conn.h"
#include "downloads-list.h"
#include "global.h"
#include "icons.h"
#include "misc.h"
#include "misc_gtk.h"
#include "misc_strings.h"
#include "options.h"

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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

#define NUM_VIEW_COLS		5


static gboolean       core_supports_upload_priority; /* FALSE */

static GHashTable    *shared_files_ht;      /* NULL */ /* 'hashes' as keys, and GtkTreeRowReferences as values   */
static GHashTable    *shared_files_name_ht; /* NULL */ /* utf8_names as keys, and GtkTreeRowReferences as values */

static GtkListStore  *shared_files_store;      /* NULL */
static GtkWidget     *shared_files_view;      /* NULL */

static guint          shared_files_count;      /* 0 */
static guint          shared_dirs_count;       /* 0 */

static GQueue        *shared_files_add_queue;  /* NULL */


enum
{
	// view widget data

	SHARED_FILES_NAME_COLUMN = 0,
//	SHARED_FILES_NAME_KEY_COLUMN,		/* key produced by g_utf8_collate_key(name_utf8) */
	SHARED_FILES_FORMAT_COLUMN,
	SHARED_FILES_TYPE_COLUMN,

	SHARED_FILES_SIZE_COLUMN,
	SHARED_FILES_PRIORITY_COLUMN,

	SHARED_FILES_ICON_COLUMN,

	SHARED_FILES_DATA_HASH_COLUMN,
	SHARED_FILES_PRIORITY_STRING_COLUMN,
	SHARED_FILES_SIZE_STRING_COLUMN,
	SHARED_FILES_MARKED_FOR_REMOVAL_FLAG,
	SHARED_FILES_N_COLUMNS
};

#define SHARED_FILES_TYPE_PLACEHOLDER \
	G_TYPE_STRING, /* SHARED_FILES_NAME_COLUMN */\
/*	G_TYPE_STRING, --SHARED_FILES_NAME_KEY_COLUMN */\
	G_TYPE_STRING, /* SHARED_FILES_FORMAT_COLUMN */\
	G_TYPE_STRING, /* SHARED_FILES_TYPE_COLUMN */\
\
	G_TYPE_UINT, /* SHARED_FILES_SIZE_COLUMN */\
	G_TYPE_UINT, /* SHARED_FILES_PRIORITY_COLUMN */\
\
	GDK_TYPE_PIXBUF, /* SHARED_FILES_ICON_COLUMN */\
\
	G_TYPE_POINTER,/* SHARED_FILES_DATA_HASH_COLUMN */\
	G_TYPE_STRING, /* SHARED_FILES_PRIORITY_STRING_COLUMN */\
	G_TYPE_STRING, /* SHARED_FILES_SIZE_STRING_COLUMN */\
\
	G_TYPE_BOOLEAN /* SHARED_FILES_MARKED_FOR_REMOVAL_FLAG */

enum
{
 	SF_MENUITEM_LINK_TO_LOG = 0,
 	SF_MENUITEM_CLEAR_SELECTION,
 	SF_MENUITEM_SELECT_ALL,
 	SF_MENUITEM_REFRESH_LIST
};

/*
struct _GuiSharedFile
{
	guint8      hash[16];

	gchar      *name;
	gchar      *name_utf8;
	gchar       format[32];
	gchar       type[64];

	guint       size;
	gchar       sizestr[32];

	guint8      prio;
	gchar       priostr[32];

	gboolean    marked_for_removal;

	GdkPixbuf  *icon;
};

typedef struct _GuiSharedFile GuiSharedFile;
*/


static gboolean       shared_files_sorting_enabled = TRUE;

static guint          column_widths[NUM_VIEW_COLS];

/* local function declarations */

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

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

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

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

/* event handlers */

static gint   onRowActivated (GtkWidget *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer baz);

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

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

static void   shared_files_popup_menu (guint button, guint32 activate_time);


static gboolean   shared_files_get_iter_from_hash (const guint8 *hash, GtkTreeIter *iter);

static gboolean   shared_files_add_timeout (gpointer data);

static void       shared_files_enable_sorting(void);

static void       shared_files_disable_sorting(void);

static void       shared_files_add_or_update_record ( const guint8 *hash,
                                                      const gchar *name,
                                                      guint32 size,
                                                      const gchar *format,
                                                      const gchar *type,
                                                      guint prio );

static void       shared_files_mark_all_for_removal (void);

static void       shared_files_remove_old (void);


/******************************************************************************
 *
 *  onSharedDirs
 *
 *  This is just here to avoid another exported function from
 *   shared_dirs.c, so that Axel doesn't start screaming
 *   about modularity etc. etc. ;)
 *
 ******************************************************************************/

static void
onSharedDirs (GtkListStore *store, guint num, const gchar **dirname_arr, gpointer data)
{
	shared_dirs_count = num;
}

/******************************************************************************
 *
 *  onSharedFiles
 *
 ******************************************************************************/

static void
onSharedFiles (GuiCoreConn   *conn,
               guint          num,
               const guint8 **hash_arr,
               const gchar  **name_arr,
               guint         *size_arr,
               const gchar  **format_arr,
               const gchar  **type_arr,
               guint         *prio_arr,
               gpointer       data)
{
	gboolean  manyfiles;
	guint     n;

	core_supports_upload_priority = FALSE;

	if (!g_queue_is_empty(shared_files_add_queue))
		return;

	shared_files_mark_all_for_removal();

	manyfiles = ( num > 3000 );

	if ( manyfiles )
	{
		static gboolean shwn = FALSE;

		shared_files_disable_sorting();
		opt_set_bool(OPT_GUI_SHOW_ICONS_IN_SHARED_FILES_LIST, FALSE);

		if (!shwn)
		{
			status_warning (_("GUI: Disabled sorting in the shared files list. Too many files ( > 3000 ).\n"));
			shwn = TRUE;
		}
	}
	else
		shared_files_enable_sorting();

	for ( n = 0;  n < num;  ++n)
	{
		shared_files_add_or_update_record (hash_arr[n], name_arr[n], size_arr[n], format_arr[n], type_arr[n], prio_arr[n]);

		if (prio_arr[n] != G_MAXUINT)
			core_supports_upload_priority = TRUE;
	}

	shared_files_remove_old ();
}


/******************************************************************************
 *
 *   shared_files_mark_all_for_removal
 *
 ***/

static gboolean
shared_files_mark_all_for_removal_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
	gtk_list_store_set ( GTK_LIST_STORE(model), iter, SHARED_FILES_MARKED_FOR_REMOVAL_FLAG, TRUE, -1 );
	return FALSE;
}

static void
shared_files_mark_all_for_removal (void)
{
	if (!shared_files_store)
		return;

	if (!g_queue_is_empty(shared_files_add_queue))
		return;

	gtk_tree_model_foreach (GTK_TREE_MODEL(shared_files_store), shared_files_mark_all_for_removal_foreach, NULL);
}

/******************************************************************************
 *
 *   onColumnResizedSaveWidths
 *
 ******************************************************************************/

static gboolean
onColumnResizedSaveWidths (gint *p_handler)
{
	gchar buf[512], num[32], i;

	if (p_handler)
		*p_handler = 0;

	buf[0] = 0x00;

	for (i = 0;  i < NUM_VIEW_COLS;  ++i)
	{
		g_snprintf(num, sizeof(num), "%u;", column_widths[(guint)i]);
		g_strlcat(buf, num, sizeof(buf));
	}

	if (buf[0] != 0x00)
	{
		buf[strlen(buf)-1] = 0x00; /* remove semicolon at the end */
	}

	opt_set_str(OPT_GUI_COLUMN_WIDTHS_SHARED_FILES, buf);

	return FALSE; /* Don't call again */
}

/******************************************************************************
 *
 *   onColumnResized
 *
 ******************************************************************************/

static void
onColumnResized (GObject *obj, GParamSpec *pspec, gpointer data)
{
	GtkTreeViewColumn *col;
	static gint        handler; /* 0 */
	GList             *cols;
	gint               num;

	col = GTK_TREE_VIEW_COLUMN(obj);

	cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(shared_files_view));

	num = g_list_index(cols, col);

	g_list_free(cols);

	if (num < 0)
		return;

	g_return_if_fail (num < NUM_VIEW_COLS);

	column_widths[num] = gtk_tree_view_column_get_width(col);

	if (handler == 0)
		handler = g_timeout_add(1000, (GSourceFunc) onColumnResizedSaveWidths, &handler);
}

/******************************************************************************
 *
 *  onSortColumnChanged
 *
 ******************************************************************************/

static void
onSortColumnChanged (GtkTreeSortable *sortable, gpointer data)
{
	GtkSortType order;
	gint        sortid;

	if (gtk_tree_sortable_get_sort_column_id(sortable, &sortid, &order))
	{
/*
		opt_set_int (OPT_GUI_SORT_COL_SHARED_FILES, sortid);
		opt_set_int (OPT_GUI_SORT_TYPE_SHARED_FILES, order);
*/
	}
}


/******************************************************************************
 *
 *   shared_files_print_shared_stats_message
 *
 ***/

static void
sf_get_total_size_foreach_helper(const guint8 *hash, GtkTreeRowReference *ref, gfloat *total)
{
	GuiDownload *dl;

	/* If the file is a current download, only add the
	 *  current 'transfered' size to the amount shared */
	dl = gui_download_list_get_download_from_hash (download_list, hash);
	if (dl)
	{
		*total += ((gfloat)(dl->transfered))/(1024.0*1024.0);  /* it's MegaBytes we want */
	}
	else
	{
		if (ref)
		{
			GtkTreeIter iter;
			if (misc_gtk_tree_model_get_iter_from_row_reference (GTK_TREE_MODEL(shared_files_store), ref, &iter))
			{
				guint size;

				gtk_tree_model_get(GTK_TREE_MODEL(shared_files_store), &iter, SHARED_FILES_SIZE_COLUMN, &size, -1);

				*total += ((gfloat)size)/(1024.0*1024.0);  /* it's MegaBytes we want */
			}
		}
	}
}


void
shared_files_print_shared_stats_message(void)
{
	gfloat shared_files_size_total = 0.0;

	/* shared page disabled? */
	if (shared_files_ht == NULL)
		return;

	g_hash_table_foreach(shared_files_ht, (GHFunc)sf_get_total_size_foreach_helper, &shared_files_size_total);

	if (shared_files_size_total < 1024.0)
	{
		statusbar_msg ( _(" You are sharing %d files in %d directories, which adds up to %.2fM"),
		                            shared_files_count, shared_dirs_count, shared_files_size_total );
	}
	else
	{
		statusbar_msg ( _(" You are sharing %d files in %d directories, which adds up to %.2fG"),
		                            shared_files_count, shared_dirs_count, shared_files_size_total/1024.0 );
	}
}



/******************************************************************************
 *
 *   shared_files_clear_list
 *
 ***/

static void
shared_files_clear_list (void)
{
	GtkTreeIter tmpiter;
	gboolean    val;

	shared_files_count = 0;

	if (!shared_files_store)
		return;

	val = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(shared_files_store), &tmpiter);

	while (val)
	{
		GValue   hval = { 0, };
		guint8	*sthash = NULL;

		gtk_tree_model_get_value ( GTK_TREE_MODEL(shared_files_store), &tmpiter, SHARED_FILES_DATA_HASH_COLUMN, &hval );

		sthash = g_value_get_pointer(&hval);
		G_FREE(sthash);

		g_value_unset(&hval);

		val = gtk_tree_model_iter_next (GTK_TREE_MODEL(shared_files_store), &tmpiter);
	}

	gtk_list_store_clear (shared_files_store);

	if (shared_files_ht)
		g_hash_table_destroy(shared_files_ht);

	if (shared_files_name_ht)
		g_hash_table_destroy(shared_files_name_ht);

	shared_files_ht = g_hash_table_new_full ( misc_hash_hash, misc_hash_equal ,g_free, (GDestroyNotify) gtk_tree_row_reference_free );
	shared_files_name_ht = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) gtk_tree_row_reference_free );

	while (!g_queue_is_empty(shared_files_add_queue))
		g_free(g_queue_pop_head(shared_files_add_queue));

}


/******************************************************************************
 *
 *   shared_files_remove_old
 *   shared_files_remove_callback
 *
 ***/

static gboolean
shared_files_remove_callback ( GtkTreeIter *iter)
{
	if (iter)
	{
		GValue       hval = { 0, };
		GValue       val = { 0, };
		guint8      *hash;
		const gchar *name;

		gtk_tree_model_get_value (GTK_TREE_MODEL(shared_files_store), iter, SHARED_FILES_DATA_HASH_COLUMN, &hval);
		gtk_tree_model_get_value (GTK_TREE_MODEL(shared_files_store), iter, SHARED_FILES_NAME_COLUMN, &val);

		hash = g_value_get_pointer(&hval);
		if (hash)
		{
			(void) g_hash_table_remove(shared_files_ht, hash);
			g_free(hash);
		}

		name = g_value_get_string(&val);
		if (name)
		  (void) g_hash_table_remove(shared_files_name_ht, name);

		gtk_list_store_remove( shared_files_store, iter);
		gtk_tree_iter_free(iter);

		g_value_unset (&val);
		g_value_unset (&hval);
	}

	return FALSE;
}

static gboolean
shared_files_remove_old_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
	gboolean remove = FALSE;

	gtk_tree_model_get ( GTK_TREE_MODEL(model), iter, SHARED_FILES_MARKED_FOR_REMOVAL_FLAG, &remove, -1);

	if (remove)	/* GtkListStore iters are persistent */
		g_idle_add ( (GSourceFunc) shared_files_remove_callback, gtk_tree_iter_copy(iter));

	return FALSE;
}

static void
shared_files_remove_old (void)
{
	if (!shared_files_store)
		return;

	if (!g_queue_is_empty(shared_files_add_queue))
		return;

	gtk_tree_model_foreach (GTK_TREE_MODEL(shared_files_store), shared_files_remove_old_foreach, NULL);
}



/******************************************************************************
 *
 *   shared_files_get_iter_from_hash
 *
 ***/

static gboolean
shared_files_get_iter_from_hash (const guint8 *hash, GtkTreeIter *iter)
{
	GtkTreeRowReference     *ref;

	g_return_val_if_fail (hash != NULL, FALSE);

	ref = g_hash_table_lookup ( shared_files_ht, hash );

	if (!ref)
		return FALSE;

	return misc_gtk_tree_model_get_iter_from_row_reference ( GTK_TREE_MODEL(shared_files_store), ref, iter );
}


static void
shared_files_disable_sorting(void)
{
	shared_files_sorting_enabled = FALSE;

	gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(shared_files_store),
	                                  SHARED_FILES_NAME_COLUMN,
	                                  shared_files_sort_function_nosort,
	                                  NULL, NULL );

	gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(shared_files_store),
	                                  SHARED_FILES_FORMAT_COLUMN,
	                                  shared_files_sort_function_nosort,
	                                  NULL, NULL );

	gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(shared_files_store),
	                                  SHARED_FILES_TYPE_COLUMN,
	                                  shared_files_sort_function_nosort,
	                                  NULL, NULL );

//	if (gtk_tree_sortable_has_default_sort_func(GTK_TREE_SORTABLE(shared_files_store)))
//	{
//		gtk_tree_sortable_set_sort_column_id ( GTK_TREE_SORTABLE(shared_files_store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);

//		gtk_tree_sortable_sort_column_changed ( GTK_TREE_SORTABLE(shared_files_store) );

//		gtk_tree_sortable_set_default_sort_func ( GTK_TREE_SORTABLE(shared_files_store), NULL, NULL, NULL);
//	}


}


static void
shared_files_enable_sorting(void)
{
	shared_files_sorting_enabled = TRUE;

	gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(shared_files_store),
	                                  SHARED_FILES_NAME_COLUMN,
	                                  shared_files_sort_name_function,
	                                  NULL, NULL );

	gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(shared_files_store),
	                                  SHARED_FILES_FORMAT_COLUMN,
	                                  shared_files_sort_format_function,
	                                  NULL, NULL );

	gtk_tree_sortable_set_sort_func ( GTK_TREE_SORTABLE(shared_files_store),
	                                  SHARED_FILES_TYPE_COLUMN,
	                                  shared_files_sort_type_function,
	                                  NULL, NULL );
}


/******************************************************************************
 *
 *   shared_files_add_record
 *
 *   Adds a record to the shared files list.
 *
 ***/

static void
shared_files_add_record ( const guint8 *hash,
                          const gchar  *name,
                          guint32       size,
                          const gchar  *format,
                          const gchar  *type,
                          guint         prio )
{
	GtkTreeRowReference *ref, *ref2;
	GtkTreeIter          iter;
	GdkPixbuf           *pixbuf = NULL;
	gchar               *name_utf8;
//	gchar               *name_nocase_utf8;

	g_return_if_fail ( hash   != NULL );
	g_return_if_fail ( name   != NULL );

	name_utf8 = TO_UTF8(name);
//	name_nocase_utf8 = g_utf8_casefold(name_utf8,-1);

	gtk_list_store_append (shared_files_store, &iter);

	if (opt_get_bool(OPT_GUI_SHOW_ICONS_IN_SHARED_FILES_LIST))
		pixbuf = get_icon_for_file (name);

	gtk_list_store_set ( shared_files_store, &iter,
	                     SHARED_FILES_ICON_COLUMN, pixbuf,
	                     SHARED_FILES_DATA_HASH_COLUMN, g_memdup(hash, 16),
	                     SHARED_FILES_SIZE_COLUMN, size,
	                     SHARED_FILES_SIZE_STRING_COLUMN, misc_get_human_size_utf8(NULL,0,size),
	                     SHARED_FILES_NAME_COLUMN, name_utf8,
//	                     SHARED_FILES_NAME_KEY_COLUMN, g_utf8_collate_key(name_nocase_utf8,-1),
	                     SHARED_FILES_FORMAT_COLUMN, format ? format : "",
	                     SHARED_FILES_TYPE_COLUMN, type ? type : "",
	                     SHARED_FILES_PRIORITY_COLUMN, prio,
	                     SHARED_FILES_PRIORITY_STRING_COLUMN, misc_get_prio_string(prio),
	                     SHARED_FILES_MARKED_FOR_REMOVAL_FLAG, FALSE,
	                     -1);

	ref = misc_gtk_tree_model_get_row_reference_from_iter (GTK_TREE_MODEL(shared_files_store), &iter);

	g_return_if_fail ( ref != NULL );

	g_hash_table_insert (shared_files_ht, g_memdup(hash,16), ref);

	/* gtk_tree_row_reference_copy() doesn't exist in gtk+-2.0.x *sigh* */
	/* XXX - use shallow copies? */
	ref2 = misc_gtk_tree_model_get_row_reference_from_iter (GTK_TREE_MODEL(shared_files_store), &iter);

	g_hash_table_insert (shared_files_name_ht, g_strdup(name_utf8), ref2);

	shared_files_count++;

	FREE_UTF8(name_utf8);
//	G_FREE(name_nocase_utf8);
}


/******************************************************************************
 *
 *   shared_files_add_timeout
 *
 *   Rationale: the GUI simply deals very badly with many shared files.
 *              This is to a large extent due to all the UTF8 string
 *              conversions, and also due to sorting etc. In any case,
 *              we can't add many items to the list w/o freezing the GUI.
 *              Doing the occasional gtk_main_iteration_do() doesn't help
 *              much either, but seems to deadlock at some point for some
 *              reason I don't understand very well.
 *
 *              What we do now is to add all shared files to a queue, and
 *              have a callback pop one entry from the qeueu and add it to
 *              the list every 200ms or so. This way the GUI will stay
 *              usable more or less even when the number of files shared
 *              approaches 10000.
 *
 *              If anyone has any better ideas/solutions: let me know :)
 ***/

static gboolean
shared_files_add_timeout (gpointer data)
{
	SharedFilesDelayedAdd *da;
	gboolean               empty;
	static gint            counter = 0;

	da = g_queue_pop_head (shared_files_add_queue);

	g_return_val_if_fail ( da != NULL, TRUE );

	shared_files_add_record (da->hash, da->name, da->size, da->format, da->type, da->prio );

	G_FREE(da->hash);
	G_FREE(da->name);
	G_FREE(da->format);
	G_FREE(da->type);

	g_free(da);

//	g_print (".");

	empty = g_queue_is_empty(shared_files_add_queue);

	if (empty)
	{
		gtk_tree_view_set_model (GTK_TREE_VIEW(shared_files_view), GTK_TREE_MODEL(shared_files_store));
		statusbar_msg ("added all %u files to the shared files list.", shared_files_count);
	}
	else
	{
		if ( (++counter%100) == 0 )
			statusbar_msg ("added %u of %u files to the shared files list.", shared_files_count, shared_files_count + shared_files_add_queue->length);
	}

	return (!empty);
}


/******************************************************************************
 *
 *   shared_files_add_or_update_record
 *
 *   Adds a record to the shared files list.
 *   If it already exists, the fields are updated.
 *
 ***/

static void
shared_files_add_or_update_record ( const guint8 *hash,
                                    const gchar *name,
                                    guint32 size,
                                    const gchar *format,
                                    const gchar *type,
                                    guint prio )
{
	GtkTreeIter   iter;
	const gchar  *store_name;
	gchar        *name_utf8;
	GValue        val = {0,};
	GValue        uval = {0,};

	g_return_if_fail (hash!=NULL);
	g_return_if_fail (name!=NULL);

	if (!shared_files_get_iter_from_hash (hash, &iter))
	{
		if ( shared_files_count > 300 )
		{
			SharedFilesDelayedAdd *da = g_new0 ( SharedFilesDelayedAdd, 1 );

			if (g_queue_is_empty(shared_files_add_queue))
			{
				g_idle_add_full ( 2*G_PRIORITY_LOW, shared_files_add_timeout, NULL, NULL);
				gtk_tree_view_set_model (GTK_TREE_VIEW(shared_files_view), NULL);
			}

			da->hash = g_memdup(hash,16);
			da->name = g_strdup(name);
			da->format = g_strdup(format);
			da->type = g_strdup(type);
			da->prio = prio;
			da->size = size;

			g_queue_push_tail (shared_files_add_queue, da);
		}
		else
		{
			shared_files_add_record (hash, name, size, format, type, prio );
		}

		return;
	}

	g_return_if_fail ( g_queue_is_empty(shared_files_add_queue) );

	gtk_tree_model_get_value (GTK_TREE_MODEL(shared_files_store), &iter, SHARED_FILES_NAME_COLUMN, &val);
	gtk_tree_model_get_value (GTK_TREE_MODEL(shared_files_store), &iter, SHARED_FILES_MARKED_FOR_REMOVAL_FLAG, &uval);

	store_name = g_value_get_string(&val);

	name_utf8 = TO_UTF8(name);

	g_return_if_fail ( name_utf8  != NULL );
	g_return_if_fail ( store_name != NULL );

	if ( !g_value_get_boolean(&uval) && g_queue_is_empty(shared_files_add_queue) )
	{
		g_printerr (_("GUI: You seem to have two identical files shared in different directories\n"));
		g_printerr (_("GUI: or under different names:\n"));
		g_printerr (_("GUI:   (a) %s\n"), store_name);
		g_printerr (_("GUI:   (b) %s\n"), name_utf8);
	}

	gtk_list_store_set ( shared_files_store, &iter,
	                     SHARED_FILES_MARKED_FOR_REMOVAL_FLAG, FALSE,
	                     -1);

	/* strcmp should be fine here, as we can always assume that identical
	 * input strings wil produce identical output strings */
	if ( strcmp (store_name, name_utf8) != 0 )
	{
		gtk_list_store_set ( shared_files_store, &iter,
		                     SHARED_FILES_NAME_COLUMN, name_utf8,
		                     -1);
	}

	g_value_unset(&val);
	g_value_unset(&uval);
	g_free(name_utf8);
}


/******************************************************************************
 *
 *   shared_files_selection_print_link
 *
 ***/

static void
shared_files_selection_print_link (GtkTreeModel *model,
                                   GtkTreePath *path,
                                   GtkTreeIter *iter,
                                   gpointer data)
{
	GValue       v_hash = {0,}, v_name = {0,}, v_size = {0,};

	gtk_tree_model_get_value ( model, iter, SHARED_FILES_NAME_COLUMN,      &v_name);
	gtk_tree_model_get_value ( model, iter, SHARED_FILES_SIZE_COLUMN,      &v_size);
	gtk_tree_model_get_value ( model, iter, SHARED_FILES_DATA_HASH_COLUMN, &v_hash);

	status_msg_print_ed2klink ( g_value_get_string(&v_name),
	                            g_value_get_uint(&v_size),
	                            (guint8*) g_value_get_pointer(&v_hash) );

	g_value_unset (&v_hash);
	g_value_unset (&v_size);
	g_value_unset (&v_name);
}


/******************************************************************************
 *
 *   shared_files_selection_set_upload_priority
 *
 ***/

static void
shared_files_selection_set_upload_priority (GtkTreeModel *model,
                                            GtkTreePath *path,
                                            GtkTreeIter *iter,
                                            gpointer data)
{
	guint    newprio = GPOINTER_TO_UINT(data);
	guint8  *hash = NULL;

	g_return_if_fail ( newprio>=0 && newprio <=2 );

	gtk_tree_model_get ( GTK_TREE_MODEL (model), iter, SHARED_FILES_DATA_HASH_COLUMN, &hash, -1 );

	g_return_if_fail ( hash != NULL );

	gtk_list_store_set ( shared_files_store, iter,
	                     SHARED_FILES_PRIORITY_COLUMN, newprio,
	                     SHARED_FILES_PRIORITY_STRING_COLUMN, misc_get_prio_string(newprio),
	                     -1 );

	gui_core_conn_send_set_upload_priority(core, hash, newprio);

}

/******************************************************************************
 *
 *   shared_files_popupmenu_item_selected
 *
 *   An item from the shared files pop-up menu has been selected. Take some action...
 *
 *   data contains the menuitem selected (lowest 8 bits)
 *   Note: widget might be NULL
 *
 ***/


static void
shared_files_popupmenu_onItemSelected (GtkWidget *widget, gpointer data)
{
	guint		menuitem =  GPOINTER_TO_UINT (data) & 0xff;

	switch (menuitem)
	{
		case SF_MENUITEM_LINK_TO_LOG:
		{
			GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_files_view));

			if (selection)

				gtk_tree_selection_selected_foreach (selection, shared_files_selection_print_link, NULL);


		}
		break;

		case SF_MENUITEM_CLEAR_SELECTION:
		{
		
			 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_files_view));

			 gtk_tree_selection_unselect_all(selection);

		}
		break;

		case SF_MENUITEM_SELECT_ALL:
		{

			GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_files_view));

			gtk_tree_selection_select_all(selection);
		}
		break;

		case SF_MENUITEM_REFRESH_LIST:
		{

			gui_core_conn_send_get_shared_files(core);

		}
		break;

		default:
			g_printerr (_("GUI: Fix me - called with unknown value %u (%s)\n"), menuitem, __FUNCTION__);
		break;
	}
}


/******************************************************************************
 *
 *   shared_files_popupmenu_onSetPriority
 *
 ***/

static void
shared_files_popupmenu_onSetPriority (GtkWidget *widget, gpointer data)
{
	GtkTreeSelection *selection;

	g_return_if_fail ( shared_files_view != NULL );

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_files_view));

	if (selection)
		gtk_tree_selection_selected_foreach (selection, shared_files_selection_set_upload_priority, data);
}


/******************************************************************************
 *
 *   onToggleIcons
 *
 ***/

static void
onToggleIcons (GtkWidget *widget, gpointer data)
{
	GtkTreeIter tmpiter;
	gboolean    val, showicons;

	if (!shared_files_store)
		return;

	opt_toggle_bool(OPT_GUI_SHOW_ICONS_IN_SHARED_FILES_LIST);

	showicons = opt_get_bool(OPT_GUI_SHOW_ICONS_IN_SHARED_FILES_LIST);

	val = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(shared_files_store), &tmpiter);

	while (val)
	{
		GdkPixbuf  *pixbuf = NULL;

		if (showicons)
		{
			GValue  val = { 0, };

			gtk_tree_model_get_value (GTK_TREE_MODEL(shared_files_store), &tmpiter, SHARED_FILES_NAME_COLUMN, &val);

			pixbuf = get_icon_for_file (g_value_get_string(&val));

			g_value_unset (&val);
		}

		gtk_list_store_set ( shared_files_store, &tmpiter,
		                     SHARED_FILES_ICON_COLUMN, pixbuf, -1 );

		val = gtk_tree_model_iter_next (GTK_TREE_MODEL(shared_files_store), &tmpiter);
	}
}


/******************************************************************************
 *
 *   shared_files_popup_menu
 *
 ******************************************************************************/

#define CORE_IS_UPLOAD_PRIORITY_ENABLED  \
          (gui_core_conn_is_newer_than(core,  3, 3, 2003))

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

	g_return_if_fail ( shared_files_view != NULL );

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

	/* priority sub-menu */
	pmenu  = gtk_menu_new();
	misc_gtk_add_menu_item (pmenu, _(" high ")  , shared_files_popupmenu_onSetPriority, GUINT_TO_POINTER(2), ICON_MENU_PRIO_HIGH  , TRUE );
	misc_gtk_add_menu_item (pmenu, _(" normal "), shared_files_popupmenu_onSetPriority, GUINT_TO_POINTER(1), ICON_MENU_PRIO_MEDIUM, TRUE );
	misc_gtk_add_menu_item (pmenu, _(" low ")   , shared_files_popupmenu_onSetPriority, GUINT_TO_POINTER(0), ICON_MENU_PRIO_LOW   , TRUE );

	/* main pop-up menu */
	menu  = gtk_menu_new();

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

	misc_gtk_add_menu_item (menu, _(" select all "), shared_files_popupmenu_onItemSelected, GUINT_TO_POINTER(SF_MENUITEM_SELECT_ALL), ICON_NONE, TRUE);
	misc_gtk_add_menu_item (menu, _(" unselect all "), shared_files_popupmenu_onItemSelected, GUINT_TO_POINTER(SF_MENUITEM_CLEAR_SELECTION), ICON_NONE, TRUE);

	misc_gtk_add_menu_separator (menu);

	misc_gtk_add_submenu   (menu, _(" set upload priority ") , ICON_MENU_PRIO_MEDIUM, pmenu, (sel_count > 0) && CORE_IS_UPLOAD_PRIORITY_ENABLED );
	misc_gtk_add_menu_item (menu, _(" write ed2k-link to log/clipboard "), shared_files_popupmenu_onItemSelected, GUINT_TO_POINTER(SF_MENUITEM_LINK_TO_LOG), ICON_MENU_STATUS, (sel_count > 0) );

	misc_gtk_add_menu_separator (menu);

	misc_gtk_add_menu_item ( menu, _(" show icons "), onToggleIcons, NULL,
	                         (opt_get_bool(OPT_GUI_SHOW_ICONS_IN_SHARED_FILES_LIST)) ? ICON_OPTION_TRUE : ICON_OPTION_FALSE, TRUE );

	misc_gtk_add_menu_separator (menu);

	misc_gtk_add_menu_item (menu, _(" refresh list "), shared_files_popupmenu_onItemSelected, GUINT_TO_POINTER(SF_MENUITEM_REFRESH_LIST), ICON_REFRESH, 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_widget_show(menu);

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



/******************************************************************************
 *
 *   onButtonPress
 *
 ***/

static gboolean
onButtonPress (GtkWidget *widget, GdkEventButton *event, gpointer data)
{

	/* right-click -> popup the menu */
	if (event->button == 3)
	{
		shared_files_popup_menu (event->button, event->time);
		return TRUE; /* event has been dealt with */
	}

	return FALSE;
}


/******************************************************************************
 *
 *   onPopupMenu
 *
 ***/

static gboolean
onPopupMenu (GtkWidget *widget, gpointer data)
{
	shared_files_popup_menu (0, gdk_event_get_time(NULL));
	return TRUE;
}


/******************************************************************************
 *
 *   onRowActivated
 *
 *   double click on a row => print ed2k-link
 *
 ***/

static gint
onRowActivated (GtkWidget *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer baz)
{
	GtkTreeIter	iter;

	if (opt_get_bool(OPT_GUI_IGNORE_DOUBLE_CLICKS))
		return TRUE;

	if (gtk_tree_model_get_iter (GTK_TREE_MODEL(shared_files_store), &iter, path) == TRUE)
	{
		shared_files_selection_print_link (GTK_TREE_MODEL(shared_files_store), path, &iter, baz);
	}

	return TRUE;
}


/******************************************************************************
 *
 *   shared_files_sort_function_nosort
 *
 ***/

static gint
shared_files_sort_function_nosort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
{
	return 0; /* everything is equal to everything => sorting disabled */
}




/******************************************************************************
 *
 *   shared_files_sort_format_function
 *
 ***/

static gint
shared_files_sort_format_function (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
{
	GValue   vala = {0,};
	GValue   valb = {0,};
	gint     ret = 0;

	g_return_val_if_fail ( a     != NULL, 0 );
	g_return_val_if_fail ( b     != NULL, 0 );
	g_return_val_if_fail ( model != NULL, 0 );

	gtk_tree_model_get_value (model, a, SHARED_FILES_FORMAT_COLUMN, &vala);
	gtk_tree_model_get_value (model, b, SHARED_FILES_FORMAT_COLUMN, &valb);

	ret = g_utf8_collate (g_value_get_string(&vala), g_value_get_string(&valb));

	g_value_unset (&vala);
	g_value_unset (&valb);

	return ret;
}

/******************************************************************************
 *
 *   shared_files_sort_type_function
 *
 ***/

static gint
shared_files_sort_type_function (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
{
	GValue   vala = {0,};
	GValue   valb = {0,};
	gint     ret = 0;

	g_return_val_if_fail ( a     != NULL, 0 );
	g_return_val_if_fail ( b     != NULL, 0 );
	g_return_val_if_fail ( model != NULL, 0 );

	gtk_tree_model_get_value (model, a, SHARED_FILES_TYPE_COLUMN, &vala);
	gtk_tree_model_get_value (model, b, SHARED_FILES_TYPE_COLUMN, &valb);

	ret = g_utf8_collate (g_value_get_string(&vala), g_value_get_string(&valb));

	g_value_unset (&vala);
	g_value_unset (&valb);

	return ret;
}



/******************************************************************************
 *
 *   shared_files_sort_name_function
 *
 ***/

static gint
shared_files_sort_name_function (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
{
	GValue   vala = {0,};
	GValue   valb = {0,};
	gint     ret = 0;

	if (!shared_files_sorting_enabled)
		return 0;

	g_return_val_if_fail ( a     != NULL, 0 );
	g_return_val_if_fail ( b     != NULL, 0 );
	g_return_val_if_fail ( model != NULL, 0 );

	gtk_tree_model_get_value (model, a, SHARED_FILES_NAME_COLUMN, &vala);
	gtk_tree_model_get_value (model, b, SHARED_FILES_NAME_COLUMN, &valb);

	/* XXX - this is SLOW */
	ret = g_utf8_collate (g_value_get_string(&vala), g_value_get_string(&valb));

	g_value_unset (&vala);
	g_value_unset (&valb);

	return ret;
}



/******************************************************************************
 *
 *   shared_files_update_rules_hint
 *
 ***/

void
shared_files_update_rules_hint (void)
{
	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(shared_files_view), opt_get_bool(OPT_GUI_SET_RULES_HINT_ON_LISTS));
}


/******************************************************************************
 *
 *   restore_tree_view_column_widths
 *
 ******************************************************************************/

static void
restore_tree_view_column_widths (GtkWidget *tview)
{
	GtkTreeViewColumn *col;
	const gchar       *pos;
	GList             *cols;
	guint              n = 0;

	cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(tview));

	g_assert (g_list_length(cols) == NUM_VIEW_COLS);

 	pos = opt_get_str(OPT_GUI_COLUMN_WIDTHS_SHARED_FILES);
	while ((pos) && *pos != 0x00 &&  n < NUM_VIEW_COLS)
	{
		column_widths[n] = (guint) atoi(pos);

		col = GTK_TREE_VIEW_COLUMN(g_list_nth_data(cols, n));

		g_signal_handlers_block_by_func(col, onColumnResized, NULL);

		gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
		gtk_tree_view_column_set_resizable(col, TRUE);
		gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(col), column_widths[n]);

		g_signal_handlers_unblock_by_func(col, onColumnResized, NULL);

		pos = strchr(pos, ';');

		if (pos)
			++pos; /* skip ';' */

		++n;
	}

	g_list_free(cols);
}

/******************************************************************************
 *
 *   onOptionChanged
 *
 ******************************************************************************/

static void
onOptionChanged (OptNum num)
{
	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(shared_files_view), opt_get_bool(OPT_GUI_SET_RULES_HINT_ON_LISTS));
}


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

static void
onCoreConnStatus (GuiCoreConn *conn, guint status, gpointer data)
{
	if (status != CONN_STATUS_COMMUNICATING && status != CONN_STATUS_AUTHENTICATING)
		shared_files_clear_list();
}


/******************************************************************************
 *
 *   shared_files_create_view
 *
 *   Creates the shared files view  embedded in a scrollwin.
 *   Returns the scrollwin widget.
 *
 ***/

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

	opt_notify (OPT_GUI_SET_RULES_HINT_ON_LISTS, onOptionChanged);

	shared_files_store = gtk_list_store_new ( SHARED_FILES_N_COLUMNS,
	                                          SHARED_FILES_TYPE_PLACEHOLDER );

	g_signal_connect(core, "shared-dirs",      (GCallback) onSharedDirs, NULL);
	g_signal_connect(core, "shared-files",     (GCallback) onSharedFiles, NULL);
	g_signal_connect(core, "core-conn-status", (GCallback) onCoreConnStatus, NULL);

	g_signal_connect(shared_files_store, "sort-column-changed", (GCallback) onSortColumnChanged,  NULL);

/*
	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(shared_files_store),
	                                     opt_get_int(OPT_GUI_SORT_COL_SHARED_FILES),
	                                     opt_get_int(OPT_GUI_SORT_TYPE_SHARED_FILES));
*/

	shared_files_view = gtk_tree_view_new_with_model ( GTK_TREE_MODEL(shared_files_store));

	column = gtk_tree_view_column_new();
	g_signal_connect(column, "notify::width", (GCallback) onColumnResized, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);

	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_FILES_ICON_COLUMN, NULL);
	gtk_tree_view_column_set_title(column, UTF8_SHORT_PRINTF("%s",_("Filename")));

	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_FILES_NAME_COLUMN, NULL);
	gtk_tree_view_column_set_sort_column_id(column, SHARED_FILES_NAME_COLUMN);
	gtk_tree_view_append_column(GTK_TREE_VIEW(shared_files_view), column);
	gtk_tree_view_column_set_alignment(column, 0.02);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Size")), renderer,
	                                                  "text", SHARED_FILES_SIZE_STRING_COLUMN, NULL);
	g_signal_connect(column, "notify::width", (GCallback) onColumnResized, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SHARED_FILES_SIZE_COLUMN);
	gtk_tree_view_append_column(GTK_TREE_VIEW(shared_files_view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 10, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Format")), renderer,
	                                                  "text", SHARED_FILES_FORMAT_COLUMN, NULL);
	g_signal_connect(column, "notify::width", (GCallback) onColumnResized, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SHARED_FILES_FORMAT_COLUMN);
	gtk_tree_view_append_column(GTK_TREE_VIEW(shared_files_view), column);
	gtk_tree_view_column_set_alignment(column, 0.5);
	g_object_set (G_OBJECT(renderer), "xalign", 0.5, NULL);

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Type")),
	                                                  renderer, "text", SHARED_FILES_TYPE_COLUMN, NULL);
	g_signal_connect(column, "notify::width", (GCallback) onColumnResized, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SHARED_FILES_TYPE_COLUMN);
	gtk_tree_view_append_column(GTK_TREE_VIEW(shared_files_view), column);
	gtk_tree_view_column_set_alignment(column, 0.1);
	g_object_set (G_OBJECT(renderer), "xalign", 0.0, NULL);
	g_object_set (G_OBJECT(renderer), "xpad", 10, NULL);


	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Upload Priority")), renderer,
	                                                  "text", SHARED_FILES_PRIORITY_STRING_COLUMN, NULL);
	g_signal_connect(column, "notify::width", (GCallback) onColumnResized, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SHARED_FILES_PRIORITY_COLUMN);
	gtk_tree_view_append_column(GTK_TREE_VIEW(shared_files_view), column);
	gtk_tree_view_column_set_alignment(column, 0.1);
	g_object_set (G_OBJECT(renderer), "xalign", 0.0, NULL);
	g_object_set (G_OBJECT(renderer), "xpad", 10, NULL);


	scrollwin = gtk_scrolled_window_new (NULL,NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_container_add (GTK_CONTAINER(scrollwin), shared_files_view);

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(shared_files_view));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);

	shared_files_disable_sorting();

	g_signal_connect (G_OBJECT(shared_files_view), "row-activated", G_CALLBACK(onRowActivated), NULL);
	g_signal_connect (G_OBJECT(shared_files_view), "button_press_event", G_CALLBACK(onButtonPress), NULL);
	g_signal_connect (G_OBJECT(shared_files_view), "popup_menu", G_CALLBACK(onPopupMenu), NULL);

	restore_tree_view_column_widths(shared_files_view);

	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(shared_files_view), opt_get_bool(OPT_GUI_SET_RULES_HINT_ON_LISTS));

	gtk_widget_show_all (scrollwin);


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

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

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

	shared_files_ht = g_hash_table_new_full ( misc_hash_hash, misc_hash_equal, g_free, (GDestroyNotify) gtk_tree_row_reference_free );

	shared_files_name_ht = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) gtk_tree_row_reference_free );

	g_object_set_data_full (G_OBJECT(shared_files_view), "foo_sfhtd1",
	                        &shared_files_ht, (GDestroyNotify)misc_hashtable_destroy);

	g_object_set_data_full (G_OBJECT(shared_files_view), "foo_sfhtd2",
	                        &shared_files_name_ht, (GDestroyNotify)misc_hashtable_destroy);

	shared_files_add_queue = g_queue_new();

	return (scrollwin);
}

