/***************************************************************************
                          search.c  -  description
                             -------------------
    begin                : Tue Feb 25 2003
    copyright            : (C) 2003 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.                                   *
 *                                                                         *
 ***************************************************************************/

/* GtkOptionMenu is deprecated in Gtk+-2.4. If we
 *  use GTK_DISABLE_DEPRECATED, the type cast macros
 *  will not be defined, and we'll get linker errors. */
#include <gtk/gtkversion.h>
#if ((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION >= 3))
# undef GTK_DISABLE_DEPRECATED
#endif

#include "blacklist.h"
#include "core-conn.h"
#include "colors.h"
#include "downloads-list.h"
#include "filter.h"
#include "options.h"
#include "icons.h"
#include "mainwindow.h"
#include "misc.h"
#include "misc_gtk.h"
#include "misc_strings.h"
#include "notebook.h"
#include "search.h"
#include "search_popup.h"

#include "status_page.h"
#include "statusbar.h"

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

#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkimage.h>
#include <gtk/gtklabel.h>
#include <gtk/gtknotebook.h>
#include <gtk/gtkoptionmenu.h>
#include <gtk/gtkradiomenuitem.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkspinbutton.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtktreemodelsort.h>
#include <gtk/gtkvbox.h>

typedef enum
{
  SEARCH_TYPE_ANY = 0,
  SEARCH_TYPE_AUDIO,
  SEARCH_TYPE_VIDEO,
  SEARCH_TYPE_PROGRAM,
  SEARCH_TYPE_DOCUMENT,
  SEARCH_TYPE_IMAGE,
  SEARCH_TYPE_USER,
  SEARCH_TYPE_N
} SearchType;

const gchar *search_types[SEARCH_TYPE_N]      =
                { N_("Any"), N_("Audio"), N_("Video"), N_("Programs"), N_("Documents"), N_("Images"), N_("Users") };

const gchar *search_type_short[SEARCH_TYPE_N] =
                { NULL,  "Audio", "Video", "Pro",      "Doc",       "Image",  NULL    };

/* variables */


/* These are only exported to search_popup.c */
GtkWidget           *button_extend = NULL;
GtkWidget           *button_more   = NULL;

static GtkWidget    *sbminsize, *sbmaxsize, *sbminavail, *sbminbitrate;		/* GtkSpinButton widgets */
static GtkWidget    *search_hbox_extra_options;
static gboolean      search_has_changed;       /* FALSE */
static GtkWidget    *alignlabel;               /* NULL  */
static GtkWidget    *om_search;                /* NULL  */

static guint64      *shared_files_hashes; /* NULL */  /* only the first 64 bits of the 128-bit hashes */


/* variables exported only to search_popup.c */
GtkWidget    *s_notebook   = NULL;
GList        *searches     = NULL;   /* contains pointers to Search structures */

/* local variables */

static guint         scol_fname;   /* 0 */
static guint         scol_artist;  /* 0 */
static guint         scol_album;   /* 0 */
static guint         scol_title;   /* 0 */
static guint         scol_length;  /* 0 */
static guint         scol_codec;   /* 0 */
static guint         scol_bitrate; /* 0 */
static guint         scol_avail;   /* 0 */

/* functions */

static void          search_view_update_visible_columns (Search *search);

static void          search_remove_iter (GtkListStore *store, GtkTreeIter *iter);

static gboolean      search_get_iter_from_hash( const Search *search,
                                                const guint8 *filehash,
                                                GtkTreeIter *iter );

static guint         search_get_tree_view_column_number ( GtkWidget *view,
                                                          GtkTreeViewColumn *col);

static gboolean      search_onButtonPress (GtkWidget *widget, GdkEventButton *event, Search *search);

static gboolean      search_onPopUpMenu (GtkWidget *widget, Search *search);

static gboolean      search_onRowActivated (GtkWidget *view, GtkTreePath *path, GtkTreeViewColumn *col, Search *search);

static void          search_onTabCancel (GtkWidget *view, Search *search);

static GtkWidget    *search_make_new_search_label_hbox (const gchar *text, Search *search);

static GtkWidget    *search_create_notebook (void);

static GtkWidget    *search_create_buttons_hbox (void);

static GtkWidget    *append_searchlist_om_item (GtkWidget *menu, GSList **group_p, SearchType type);

#if 0
static gboolean      search_get_extra_search_options ( guint *minsize,
                                                       guint *maxsize,
                                                       guint *minavail,
                                                       guint *minbitrate,
                                                       SearchType *type );

static void          search_onResetSearchOptions (GtkWidget *widget, gpointer data);
#endif

static void          search_make_new_search (const gchar *searchphrase_utf8);



/******************************************************************************
 *
 *  atExit
 *
 ******************************************************************************/

static void
atExit (void)
{
	if (shared_files_hashes)
		g_free(shared_files_hashes);
}


/******************************************************************************
 *
 *  onSearchExtended
 *
 ******************************************************************************/

static void
onSearchExtended (GuiCoreConn *conn, const gchar *servername, gpointer data)
{
	statusbar_msg (_(" Extending search to server %s..."), servername);
}


/******************************************************************************
 *
 *  extend_5x
 *
 ******************************************************************************/

static void
extend_5x (void)
{
	gui_core_conn_send_extended_search(core);
	gui_core_conn_send_extended_search(core);
	gui_core_conn_send_extended_search(core);
	gui_core_conn_send_extended_search(core);
	gui_core_conn_send_extended_search(core);
}


/******************************************************************************
 *
 *  onSearchResults
 *
 ******************************************************************************/

static void
onSearchResults (GuiCoreConn    *conn,
                 gboolean        from_main_server,
                 gboolean        more,
	               guint           num,
	               const guint8  **hash_arr,
	               const guint8  **servicehash_arr,
	               const guint    *size_arr,
	               const guint    *avail_arr,
	               const gchar   **name_arr,
	               const gchar   **type_arr,
	               const gchar   **format_arr,
	               const gchar   **artist_arr,
	               const gchar   **album_arr,
	               const gchar   **title_arr,
	               const gchar   **length_arr,
	               const guint    *bitrate_arr,
	               const gchar   **codec_arr,
	               gpointer        data)
{
	guint i;

	if (from_main_server  &&  num == 0)
	{
		statusbar_msg(_(" No results. Automatically extending search to five more servers..."));
		extend_5x();
		gtk_widget_set_sensitive (button_extend, TRUE);
		return;
	}

	for (i = 0;  i < num;  ++i)
	{
		search_add_or_update_record ( search_get_current_search(),
		                              hash_arr[i], servicehash_arr[i], size_arr[i],
		                              avail_arr[i], name_arr[i], type_arr[i],
		                              format_arr[i], artist_arr[i], album_arr[i],
		                              title_arr[i], length_arr[i], bitrate_arr[i],
		                              codec_arr[i]);
	}

	if (from_main_server)
	{
		static gint	 req_more_results; /* 0 */ /* counter, so we don't go on forever asking for more results */
		GString     *actstr;
		Search      *search;
		guint        results;

		search = search_get_current_search();

		g_return_if_fail ( search        != NULL );
		g_return_if_fail ( search->store != NULL );

		results = (search->store)->length;

		gtk_widget_set_sensitive (button_more, more);

		actstr = g_string_sized_new (256);

		g_string_printf (actstr, _("Search results received. (%u results filtered) "), search->filtered);

		if (results < 10 && (!more))
		{
			actstr = g_string_append (actstr, _("Not very many though - extending search..."));
			extend_5x();
		}

		/* make sure we don't request forever */
		if ((more) && req_more_results < 20)
		{
			actstr = g_string_append (actstr, _("More results available..."));

			/* only ask automatically for more results if
			 * we didn't get a single one that we don't
			 * have yet - otherwise let user do it manually */

			if (results == 0)
			{
				gchar numstr[16];

				g_snprintf (numstr, sizeof(numstr), "(%d)", req_more_results);
				actstr = g_string_append (actstr, numstr);

				gui_core_conn_send_more_search(core);

				req_more_results++;
			}
		}
		else
		{
			actstr = g_string_append (actstr, _("Search completed."));
			req_more_results = 0;
		}

		statusbar_msg (" %s", actstr->str);

		gtk_widget_set_sensitive (button_extend, TRUE);

		g_string_free(actstr,TRUE);
	}
}

/******************************************************************************
 *
 *  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)
{
	guint n;

	if (shared_files_hashes)
		g_free(shared_files_hashes);

	if (num == 0)
		return;

	shared_files_hashes = g_new(guint64, num+1);

	for (n = 0; n < num; ++n)
		shared_files_hashes[n] = *((guint64*)(hash_arr[n]));

	shared_files_hashes[num] = 0;
}



/******************************************************************************
 *
 *  is_shared_file
 *
 ******************************************************************************/

static gboolean
is_shared_file (const guint8 *hash)
{
	if ((hash) && (shared_files_hashes))
	{
		guint n;
		for (n = 0;  shared_files_hashes[n] > 0;  ++n)
		{
			if (shared_files_hashes[n] == *((guint64*)hash))
				return TRUE;
		}
	}

	return FALSE;
}

/******************************************************************************
 *
 *  search_from_toolbar
 *
 ***/

void
search_from_toolbar (const gchar *searchphrase_utf8)
{
	gchar       *searchphrase_locale;
/*	guint        minsize, maxsize, minavail, minbitrate;
	gboolean     extra;
	SearchType   type;
*/
	g_return_if_fail (searchphrase_utf8 != NULL);

	if (s_notebook == NULL)
	{
		statusbar_msg("%s\n", _("You have disabled the search page."));
		return;
	}
	
/*	extra = search_get_extra_search_options ( &minsize, &maxsize, &minavail, &minbitrate, &type ); */

	/* only create a new notebook page if the
	 * phrase or type has been changed        */

	if ( search_has_changed )
		search_make_new_search (searchphrase_utf8);

	search_has_changed = FALSE;

	/* 'extend' and 'more' search are not implemented in
	 *  the 1.0 core at the moment, so there's no point
	 *  in showing the buttons */
	 
/*	if (!gui_core_conn_is_overnet(core) || gui_core_conn_is_hybrid(core))
	{
		gtk_widget_show (button_extend);
		gtk_widget_show (button_more);
		gtk_widget_set_sensitive (button_extend, FALSE);
		gtk_widget_set_sensitive (button_more, FALSE);
	}
	else
	{
		gtk_widget_hide (button_extend);
		gtk_widget_hide (button_more);
	}
*/
	gtk_widget_hide (button_extend);
	gtk_widget_hide (button_more);


	searchphrase_locale = TO_LOCALE(searchphrase_utf8);

	if (!searchphrase_locale)
		searchphrase_locale = g_strdup(searchphrase_utf8);
#if 0
	/* user search? (NOT IMPLEMENTED IN CORE) */
	g_return_if_fail ( type != SEARCH_TYPE_USER );

	/* simple search? */
	if ( type == SEARCH_TYPE_ANY )
	{
		if ( !extra )
		{
			gui_core_conn_send_simple_search(core, searchphrase_locale);
		}
		else
		{
			gui_core_conn_send_search_with_extra_options(core, searchphrase_locale, minsize, maxsize, minavail);
		}
	}
	else
	{
		if (!extra)
		{
			gui_core_conn_send_type_search(core, searchphrase_locale, search_type_short[type], minbitrate);
		}
		else
		{
			gui_core_conn_send_type_search_with_extra_options(core, searchphrase_locale,
			                                                        search_type_short[type],
			                                                        minsize,
			                                                        maxsize,
			                                                        minavail,
			                                                        minbitrate);
		}
	}
#endif

	gui_core_conn_send_simple_search(core, searchphrase_locale);

	statusbar_msg (_(" Searching..."));

	g_free (searchphrase_locale);
}



/******************************************************************************
 *
 *   search_get_current_search
 *
 ******************************************************************************/

/* TODO: IMPLEMENT SERVICEHASH STUFF FOR OVERNET */

Search *
search_get_current_search (void)
{
	GList *node;

	/* search page disabled? */
	if (s_notebook == NULL)
		return NULL;
	
	g_return_val_if_fail (searches != NULL, NULL);
	
	node = g_list_last(searches);

	return (Search*)node->data;
}



/******************************************************************************
 *
 *   search_view_update_visible_columns
 *
 ***/

static void
search_view_update_visible_columns (Search *search)
{
	GtkTreeViewColumn *col;
	gint               w, h;

	g_return_if_fail ( search       != NULL );
	g_return_if_fail ( search->view != NULL );

	if (window)
	{
		gdk_drawable_get_size (GDK_DRAWABLE(window->window), &w, &h); /* window = main window */
		col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_fname );
		gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
		gtk_tree_view_column_set_fixed_width(col, w/2);
	}

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_artist );
	if ( gtk_tree_view_column_get_visible(col) != search->hasArtist )
		gtk_tree_view_column_set_visible(col, search->hasArtist );

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_album );
	if ( gtk_tree_view_column_get_visible(col) != search->hasAlbum )
		gtk_tree_view_column_set_visible(col, search->hasAlbum );

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_title );
	if ( gtk_tree_view_column_get_visible(col) != search->hasTitle )
		gtk_tree_view_column_set_visible(col, search->hasTitle );

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_length );
	if ( gtk_tree_view_column_get_visible(col) != search->hasLength )
		gtk_tree_view_column_set_visible(col, search->hasLength );

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_codec );
	if ( gtk_tree_view_column_get_visible(col) != search->hasCodec )
		gtk_tree_view_column_set_visible(col, search->hasCodec );

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_bitrate );
	if ( gtk_tree_view_column_get_visible(col) != search->hasBitrate )
		gtk_tree_view_column_set_visible(col, search->hasBitrate );

	col = gtk_tree_view_get_column ( GTK_TREE_VIEW(search->view), scol_avail );
	if ( gtk_tree_view_column_get_visible(col) != search->hasAvailability )
		gtk_tree_view_column_set_visible(col, search->hasAvailability );
}



/******************************************************************************
 *
 *  search_remove_iter
 *
 ***/

static void
search_remove_iter (GtkListStore *store, GtkTreeIter *iter)
{
	GValue  v_hash = {0,}, v_alt_names = {0,};
	GSList  *alt_names, *alt_names_node;
	guint8  *hash;

	g_return_if_fail ( iter  != NULL );
	g_return_if_fail ( store != NULL );

	gtk_tree_model_get_value (GTK_TREE_MODEL(store), iter, SEARCH_HASH_COL, &v_hash);
	gtk_tree_model_get_value (GTK_TREE_MODEL(store), iter, SEARCH_ALTNAMES_COL, &v_alt_names);

	alt_names = g_value_get_pointer(&v_alt_names);

	hash = g_value_get_pointer(&v_hash);
	G_FREE(hash);

	for ( alt_names_node = alt_names; alt_names_node != NULL; alt_names_node = alt_names_node->next)
		G_FREE(alt_names_node->data);

	g_value_unset(&v_alt_names);
	g_value_unset(&v_hash);

	gtk_list_store_remove (store, iter);
}



/******************************************************************************
 *
 *  search_remove_all
 *
 *  remove all items, or, if flag > 0, all items where flag is set
 *
 ***/

void
search_remove_all (Search *search, guint flag)
{
	GtkTreeRowReference  *ref;
	GtkTreePath          *path;
	GtkTreeIter           tmpiter;
	gboolean              val;
	GSList               *node, *rlist = NULL;

	g_return_if_fail (s_notebook != NULL);
	
	val = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(search->store), &tmpiter);

	while (val)
	{
		guint     flags;
		gboolean  remove;

		gtk_tree_model_get ( GTK_TREE_MODEL(search->store), &tmpiter,
		                     SEARCH_FLAGS_COL, &flags,
		                     -1);

		remove = ( flag == 0 );

		if (!remove)
			remove = ((flags&flag)!=0);

		if (remove)
		{
			path = gtk_tree_model_get_path (GTK_TREE_MODEL(search->store), &tmpiter);
			ref =  gtk_tree_row_reference_new (GTK_TREE_MODEL(search->store), path);

			rlist = g_slist_prepend (rlist, ref);

			gtk_tree_path_free(path);
		}

		val = gtk_tree_model_iter_next (GTK_TREE_MODEL(search->store), &tmpiter);
	}

	for ( node = rlist; node != NULL; node = node->next )
	{
		ref = (GtkTreeRowReference*) node->data;
		path = gtk_tree_row_reference_get_path (ref);
		gtk_tree_model_get_iter (GTK_TREE_MODEL(search->store), &tmpiter, path);

		search_remove_iter (search->store, &tmpiter);

		gtk_tree_path_free(path);
		gtk_tree_row_reference_free(ref);
	}
}



/******************************************************************************
 *
 *   search_get_iter_from_hash
 *
 ***/

static gboolean
search_get_iter_from_hash(const Search *search, const guint8 *filehash, GtkTreeIter *iter)
{
	GtkTreeIter  tmpiter;
	gboolean     val;

	g_return_val_if_fail ( search        != NULL, FALSE );
	g_return_val_if_fail ( filehash      != NULL, FALSE );
	g_return_val_if_fail ( iter          != NULL, FALSE );
	g_return_val_if_fail ( search->store != NULL, FALSE );

	val = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(search->store), &tmpiter);

	while (val)
	{
		guint8 *hash = NULL;

		gtk_tree_model_get ( GTK_TREE_MODEL(search->store), &tmpiter, SEARCH_HASH_COL, &hash, -1);

		g_return_val_if_fail ( hash != NULL, FALSE );

		if ( memcmp(hash,filehash,16) == 0 )
		{
			*iter = tmpiter;
			return TRUE;
		}

		val = gtk_tree_model_iter_next (GTK_TREE_MODEL(search->store), &tmpiter);
	}

	return FALSE;
}

/******************************************************************************
 *
 *   search_update_filename_weight_set
 *
 *   makes filenames printed in bold or not
 *
 *   walk the store and re-make the mark-up string displayed according to
 *   the options set (e.g. bold or not etc.)
 *
 *   should be called after changing OPT_GUI_SEARCH_FILENAMES_BOLD
 *
 ***/

void
search_update_filename_weight_set (void)
{
	gboolean     bold_flag;
	GList       *node;

	/* search page disabled? */
	if (s_notebook == NULL)
		return;
	
	bold_flag = opt_get_bool(OPT_GUI_SEARCH_FILENAMES_BOLD);

	/* go through each line of each search list and toggle bold print (=weight) there */

	for (node = searches; node != NULL; node = node->next)
	{
		GtkTreeIter  iter;
		gboolean     val;
		Search      *s = (Search*) node->data;

		g_return_if_fail ( s != NULL );

		val = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(s->store), &iter);

		while (val)
		{
			gtk_list_store_set (s->store, &iter,
			                    SEARCH_WEIGHT, (bold_flag) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
			                    SEARCH_WEIGHT_SET, bold_flag, -1);

			val = gtk_tree_model_iter_next (GTK_TREE_MODEL(s->store), &iter);
		}
	}
}

/******************************************************************************
 *
 *   search_add_or_update_record
 *
 ***/

void
search_add_or_update_record ( Search *search,
                              const guint8 *hash,
                              const guint8 *servicehash,
                              guint32 size,
                              guint32 availability,
                              const gchar *name,
                              const gchar *type,
                              const gchar *format,
                              const gchar *mp3_artist,
                              const gchar *mp3_album,
                              const gchar *mp3_title,
                              const gchar *length,
                              guint32 bitrate,
                              const gchar *codec)
{
	GdkColor    *fg     = NULL;
	gint         weight = 0; 
	gboolean     we_have_this, blacklisted, bold_flag;
	GtkTreeIter  iter;
	const gchar *frametxt;

	guint        existing_bitrate, existing_availability, flags;
	gchar       *existing_artist, *existing_title, *existing_album;
	gchar       *existing_length, *existing_codec;
	GSList      *alternative_names     = NULL;

	g_return_if_fail (search        != NULL);
	g_return_if_fail (hash          != NULL);
	g_return_if_fail (name          != NULL);
	g_return_if_fail (search->store != NULL);
	g_return_if_fail (search->view  != NULL);

	existing_bitrate = existing_availability = flags = 0;
	existing_artist = existing_title = existing_album = NULL;
	existing_codec = existing_length = NULL;

	/* search page disabled? */
	g_return_if_fail (s_notebook != NULL);
	
	if (filter_this_filename(name))
	{
		search->filtered++;
		return;
	}

	blacklisted = is_item_in_blacklist(hash,size);

	if (  blacklisted == TRUE  &&  !opt_get_bool(OPT_GUI_SEARCH_SHOW_BLACKLISTED_ITEMS) )
	{
		search->filtered++;
		return;
	}

	we_have_this = ( is_shared_file(hash) || (gui_download_list_get_download_from_hash(download_list, hash)));

	if ( we_have_this == TRUE  &&  opt_get_bool(OPT_GUI_SEARCH_SHOW_OWN_FILES) )
		return;

	/* make sure we have sane values */
	if ( bitrate > 9999 )
		bitrate = 0;

	if ( availability > 9999 )
		availability = 0;

	if ( search_get_iter_from_hash(search, hash, &iter) == TRUE )
	{
		const gchar *name_utf8;
		gchar       *existing_name_utf8;

		gtk_tree_model_get (GTK_TREE_MODEL(search->store), &iter,
		                    SEARCH_FLAGS_COL,   &flags,
		                    SEARCH_BITRATE_COL, &existing_bitrate,
		                    SEARCH_AVAILABILITY_COL, &existing_availability,
		                    SEARCH_TITLE_COL,  &existing_title,
		                    SEARCH_ALBUM_COL,  &existing_album,
		                    SEARCH_ARTIST_COL, &existing_artist,
		                    SEARCH_LENGTH_COL, &existing_length,
		                    SEARCH_CODEC_COL,  &existing_codec,
		                    SEARCH_NAME_COL,  &existing_name_utf8,
		                    SEARCH_ALTNAMES_COL,  &alternative_names,
		                    -1 );

		g_return_if_fail ( existing_name_utf8 != NULL );

		name_utf8 = UTF8_SHORT_PRINTF ( "%s", name );

		/* If it is a different name than the one we got, check whether 
		 *  we already have this alternative name in our list. If not, add it. */

		if ( g_utf8_collate(name_utf8, existing_name_utf8) != 0 )
		{
			GSList   *node;
			gboolean  in_list = FALSE;

			/* is this alternative name already in our list? */
			for (node = alternative_names; node != NULL; node = node->next)
			{
				if ((node->data) && g_utf8_collate((gchar*)node->data,name_utf8)==0)
				{
					in_list = TRUE;
					break;
				}
			}

			if (!in_list)
				alternative_names = g_slist_append (alternative_names, g_strdup(name_utf8));
		}

		G_FREE(existing_name_utf8);
	}
	else
	{
		guint      num;
		GdkPixbuf *pixbuf = NULL;

		num = (search->store)->length;

		gtk_list_store_append (search->store, &iter);

		if ( format == NULL )
			format = misc_get_extension_from_filename(name);

		if ( type == NULL  &&  format != NULL )
			type = misc_get_filetype_from_extension (format);

		if (format)
			gtk_list_store_set (search->store, &iter, SEARCH_FORMAT_COL, UTF8_SHORT_PRINTF("%s",format), -1);

		if (type)
			gtk_list_store_set (search->store, &iter, SEARCH_TYPE_COL, UTF8_SHORT_PRINTF("%s",type), -1);

		if (opt_get_bool(OPT_GUI_SHOW_ICONS_IN_SEARCH_LIST))
			pixbuf = (!blacklisted) ? get_icon_for_file(name) : get_icon (ICON_MENU_BLACKLIST);

		gtk_list_store_set (search->store, &iter,
		                    SEARCH_NUM_COL , num+1,
		                    SEARCH_NAME_COL, UTF8_SHORT_PRINTF("%s",name),
		                    SEARCH_HASH_COL, g_memdup(hash,16),
		                    SEARCH_HASH_STRING_COL, hash_to_hash_str(hash),
		                    SEARCH_ICON_COL, pixbuf,
		                    SEARCH_SIZE_COL, size,
		                    SEARCH_SIZE_STRING_COL, misc_get_human_size_utf8 (NULL, 0, size),
		                    -1);
	}

	if ( existing_bitrate == 0  &&  bitrate > 0 )
	{
		gtk_list_store_set (search->store, &iter, SEARCH_BITRATE_COL, bitrate, -1);
		search->hasBitrate = TRUE;
	}

	if ( existing_title == NULL  &&  mp3_title != NULL )
	{
		gtk_list_store_set (search->store, &iter, SEARCH_TITLE_COL, UTF8_SHORT_PRINTF("%s",mp3_title), -1 );
		search->hasTitle = TRUE;
	}

	if ( existing_album == NULL  &&  mp3_album != NULL )
	{
		gtk_list_store_set (search->store, &iter, SEARCH_ALBUM_COL, UTF8_SHORT_PRINTF("%s",mp3_album), -1 );
		search->hasAlbum = TRUE;
	}

	if ( existing_artist == NULL  &&  mp3_artist != NULL )
	{
		gtk_list_store_set (search->store, &iter, SEARCH_ARTIST_COL, UTF8_SHORT_PRINTF("%s",mp3_artist), -1 );
		search->hasArtist = TRUE;
	}

	if ( existing_codec == NULL  &&  codec != NULL )
	{
		gtk_list_store_set (search->store, &iter, SEARCH_CODEC_COL, UTF8_SHORT_PRINTF("%s",codec), -1 );
		search->hasCodec = TRUE;
	}

	/* bug in overnet core v44 + v45 (search result packets one byte short) */
	if ( (length)  &&  length[strlen(length)-1] == (gchar)0xe3 )
		length = NULL;

	if ( existing_length == NULL  &&  length != NULL && *length != 0x00)
	{
		guint hours=0, mins=0, secs=0;

		if (sscanf (length,"%u:%u", &hours, &mins)   != 2 
		 && sscanf (length,"%uh %um %us", &hours, &mins, &secs) != 3
		 && sscanf (length,"%um %us", &mins, &secs)  != 2
		 && sscanf (length,"%uh %um", &hours, &mins) != 2
		 && sscanf (length,"%us", &secs) != 1)
			g_printerr ("REPORT ME, I MIGHT BE A BUG: couldn't parse length '%s' properly (%s).\n", length, __FUNCTION__);

		if (hours > 0 || mins > 0)
		{
			gtk_list_store_set (search->store, &iter,
			                    SEARCH_LENGTH_COL, UTF8_SHORT_PRINTF("%s",length),
			                    SEARCH_LENGTH_MINUTES_COL, ((hours*60) + mins),
			                    -1 );

			search->hasLength = TRUE;
		}
	}

	if ( we_have_this == TRUE )
		flags = flags | SEARCH_FLAG_ALREADY_HAVE_THIS;

	if ( blacklisted == TRUE )
		flags = flags | SEARCH_FLAG_BLACKLISTED;

	if ( (availability + existing_availability) >= SEARCH_HIGH_AVAILABILITY )
		flags = flags | SEARCH_FLAG_HIGH_AVAILABILITY;

	if ( blacklisted || we_have_this )
	{
		fg = &dark_grey;
	}
	else if ( (flags & SEARCH_FLAG_HIGH_AVAILABILITY) != 0 )
	{
		fg = &dark_green;
		weight = PANGO_WEIGHT_ULTRABOLD;
	}

	bold_flag = opt_get_bool(OPT_GUI_SEARCH_FILENAMES_BOLD);

	weight = (bold_flag) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL;

	gtk_list_store_set (search->store, &iter,
	                    SEARCH_AVAILABILITY_COL, (availability + existing_availability),
	                    SEARCH_FLAGS_COL, flags,
	                    SEARCH_WEIGHT, weight,
	                    SEARCH_WEIGHT_SET, ((weight > 0) && opt_get_bool(OPT_GUI_SEARCH_FILENAMES_BOLD)),
	                    SEARCH_FOREGROUND_GDK, fg,
	                    SEARCH_FOREGROUND_SET, (fg != NULL),
	                    -1 );

	if ( (availability + existing_availability) > 0 )
		search->hasAvailability = TRUE;

	search_view_update_visible_columns(search);

	frametxt = UTF8_SHORT_PRINTF (_(" %u search results "), (search->store)->length);

	if ((frametxt) && (search->frame))
		gtk_frame_set_label (GTK_FRAME(search->frame), frametxt);
}


/******************************************************************************
 *
 *   search_get_tree_view_column_number
 *
 *   This gets the column number of a column from a treeview.
 *   We need this later in order to show/hide the 'album' 'artist' etc.
 *    columns of individual searches. This way we only need to get the
 *    column NUMBER of those columns once and don't need to save pointers
 *    to the widgets anywhere.
 *
 ***/

static guint
search_get_tree_view_column_number (GtkWidget *view, GtkTreeViewColumn *col)
{
	GList  *columns, *node;
	guint   num = 0;

	g_return_val_if_fail ( col  != NULL, -1 );
	g_return_val_if_fail ( view != NULL, -1 );

	columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(view));

	for ( node = columns; node != NULL; node = node->next )
	{
		if ( node->data == col )
			break;

		num++;
	}

	g_return_val_if_fail ( node != NULL, -1 ); /* not found */

	g_list_free(columns);

	return num;
}



/******************************************************************************
 *
 *   search_delete
 *
 ***/

void
search_delete (Search *search)
{
	gint page_num = -1;

	g_return_if_fail (search     != NULL);
	g_return_if_fail (s_notebook != NULL);
	
	page_num = gtk_notebook_page_num ( GTK_NOTEBOOK(s_notebook), search->frame );

	g_return_if_fail ( page_num >= 0 );

	/* free all items incl. allocated contents in list store */
	search_remove_all (search, 0);

	/* this should free all the page's children as well, and thus take
	 * care of the frame and the tree view and the tree store */
	gtk_notebook_remove_page ( GTK_NOTEBOOK(s_notebook), page_num );

	G_FREE( search->phrase_utf8 );

	searches = g_list_remove ( searches, search );

	memset (search, 0x00, sizeof(Search));
	g_free ( search );


	if (!searches) /* FIXME: was && !about_to_quit */
		search_make_new_search (UTF8_SHORT_PRINTF("%s", _("empty")));
}




/******************************************************************************
 *
 *   search_onButtonPress
 *
 *   a single click on a row has happened
 *
 ***/

static gboolean
search_onButtonPress (GtkWidget *widget, GdkEventButton *event, Search *search)
{
	GtkTreeSelection *selection;

	g_return_val_if_fail ( search != NULL, FALSE );

	gtk_widget_grab_focus(widget);

	/* right-click -> popup the menu */
	if ( event->button != 3 )
		return FALSE;

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(search->view));

	if ( misc_gtk_tree_selection_count_selected_rows (selection) <= 1 )
	{
		GtkTreePath *path = NULL;

		gtk_tree_selection_unselect_all(selection);

		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW(search->view), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
			gtk_tree_selection_select_path (selection, path);

		if (path)
			gtk_tree_path_free(path);
	}

	search_popup_menu (widget, event, search);

	return TRUE; /* event has been dealt with */
}





/******************************************************************************
 *
 *   search_onPopUpMenu
 *
 ***/

static gboolean
search_onPopUpMenu (GtkWidget *widget, Search *search)
{
	search_popup_menu (widget, NULL, search);
	return TRUE;
}



/******************************************************************************
 *
 *   search_onRowActivated
 *
 *   a row has been double-clicked.
 *
 ***/

static gboolean
search_onRowActivated (GtkWidget *view, GtkTreePath *path, GtkTreeViewColumn *col, Search *search)
{
	GtkTreeIter   iter;
	guint8       *hash = NULL;
	gchar        *name_utf8 = NULL, *name_locale;
	guint         size;

	g_return_val_if_fail ( search != NULL, FALSE );

	if (opt_get_bool(OPT_GUI_IGNORE_DOUBLE_CLICKS))
		return TRUE;

	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL(search->store), &iter, path) == TRUE)
		return TRUE;

	gtk_tree_model_get ( GTK_TREE_MODEL(search->store), &iter,
	                     SEARCH_SIZE_COL, &size,
	                     SEARCH_HASH_COL, &hash,
	                     SEARCH_NAME_COL, &name_utf8,
	                     -1 );

	g_return_val_if_fail ( hash      != NULL, FALSE );
	g_return_val_if_fail ( name_utf8 != NULL, FALSE );

	/* name needs to be converted to locale */

	name_locale = TO_LOCALE(name_utf8);

	g_return_val_if_fail ( name_locale != NULL, FALSE );

	gui_core_conn_send_new_download(core, hash, size, name_locale);

	G_FREE(name_utf8);
	G_FREE(name_locale);

	return TRUE;

}


/******************************************************************************
 *
 *   search_onTabCancel
 *
 ***/

static void
search_onTabCancel (GtkWidget *view, Search *search)
{
	g_return_if_fail ( search != NULL );

	search_delete(search);
}


/******************************************************************************
 *
 *   search_make_new_search_label_hbox
 *
 ***/

static GtkWidget *
search_make_new_search_label_hbox (const gchar *text, Search *search)
{
	GtkTooltips  *tabtooltips;
	GtkWidget    *hbox, *img, *label, *cancel;
	gchar        *phrase, *stop, *markup;

	g_return_val_if_fail ( text != NULL, NULL);

	phrase = g_markup_escape_text(text,-1);

	if ( g_utf8_strlen(text, -1) > SEARCH_MAX_TAB_TEXT_LEN )
	{
		stop = g_utf8_offset_to_pointer(phrase, SEARCH_MAX_TAB_TEXT_LEN-1);

		if (stop)
			*stop = '*';

		stop = g_utf8_offset_to_pointer(phrase, SEARCH_MAX_TAB_TEXT_LEN);

		if (stop)
			*stop = 0x00;
	}

	hbox = gtk_hbox_new ( FALSE, 2 );
	gtk_widget_show(hbox);

	img = gtk_image_new_from_pixbuf ( get_icon(ICON_TAB_SEARCH) );
	gtk_widget_show(img);

	gtk_box_pack_start (GTK_BOX(hbox), img, FALSE, FALSE, 0);

	label = gtk_label_new(NULL);
	gtk_widget_show(label);

	/* phrase is already utf8 */
	markup = g_strconcat ("<small>", phrase, "</small>", NULL);

	gtk_label_set_markup (GTK_LABEL(label), markup);

	gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);

	tabtooltips = gtk_tooltips_new();

	cancel = gtk_button_new();
	g_signal_connect (cancel, "clicked", G_CALLBACK(search_onTabCancel), search);

	gtk_button_set_relief (GTK_BUTTON(cancel), GTK_RELIEF_NONE);
	gtk_tooltips_set_tip (tabtooltips, cancel, UTF8_SHORT_PRINTF("%s",_("forget this search")), NULL);

	gtk_container_add(GTK_CONTAINER(cancel), make_icon_and_label_hbox("", ICON_TAB_FORGET));

	gtk_widget_show(cancel);

	gtk_box_pack_start (GTK_BOX(hbox), cancel, FALSE, FALSE, 0);

	G_FREE(phrase);
	G_FREE(markup);

	return hbox;
}


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

static void
onOptionChanged (OptNum num)
{
	GList *node;

	for ( node = searches;  node != NULL;  node = node->next )
	{
		Search *s = (Search*)node->data;

		if (s)
		{
			gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(s->view), opt_get_bool(OPT_GUI_SET_RULES_HINT_ON_LISTS));
		}
	}
}


/******************************************************************************
 *
 *   search_make_new_search
 *
 ***/

static void
search_make_new_search (const gchar *searchphrase_utf8)
{
	Search             *new_search;
	GtkWidget          *hbox, *scrollwin;
	GtkCellRenderer    *renderer;
	GtkTreeViewColumn  *column;
	GtkTreeModel       *search_store_sort;
	GtkTreeSelection   *selection;

	g_return_if_fail ( searchphrase_utf8 != NULL );
	g_return_if_fail ( s_notebook        != NULL );

	if (!window)
		return;

	if (!g_utf8_validate(searchphrase_utf8,-1,NULL))
		g_return_if_reached();

	new_search = g_new0 (Search, 1);

	g_return_if_fail ( new_search != NULL );

	new_search->phrase_utf8 = g_strdup(searchphrase_utf8);

	hbox = search_make_new_search_label_hbox (new_search->phrase_utf8, new_search);

	new_search->frame = gtk_frame_new (UTF8_SHORT_PRINTF("%s",_(" search results ")));

	if ( !opt_get_bool(OPT_GUI_NO_GREEN) )
	{
		gtk_widget_set_style  (new_search->frame, style_notebook_page);
		gtk_widget_set_style  (s_notebook, style_notebook_page);
		set_style_recursively (hbox, style_notebook_label);
	}

	new_search->store = gtk_list_store_new ( SEARCH_N_COLUMNS, SEARCH_COLUMN_TYPES );

	search_store_sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(new_search->store));

	new_search->view = gtk_tree_view_new_with_model ( GTK_TREE_MODEL(new_search->store) );

	g_object_unref(new_search->store);

	/* let's add some columns */

	/* num */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("#"), renderer,
	                                                  "text", SEARCH_NUM_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_NUM_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);
	gtk_tree_view_column_set_min_width (column, 5);

	/* icon and filename */

	column = gtk_tree_view_column_new();
	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", SEARCH_ICON_COL, NULL);
	gtk_tree_view_column_set_title(column, UTF8_SHORT_PRINTF("%s",_("Filename")));
	gtk_tree_view_column_set_alignment(column, 0.02);

	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", SEARCH_NAME_COL,
	                                     "weight", SEARCH_WEIGHT,
	                                     "weight-set", SEARCH_WEIGHT_SET,
	                                     "foreground-gdk", SEARCH_FOREGROUND_GDK,
	                                     "foreground-set", SEARCH_FOREGROUND_SET,
	                                     NULL);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_NAME_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);

	scol_fname = search_get_tree_view_column_number (new_search->view, column);

	/* size in human size */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Size")), renderer,
	                                                  "text", SEARCH_SIZE_STRING_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_SIZE_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);
	gtk_tree_view_column_set_min_width (column, 5);


	/* size in bytes */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Size")), renderer,
	                                                  "text", SEARCH_SIZE_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_SIZE_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);
	gtk_tree_view_column_set_min_width (column, 5);


	/* availability */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Availability")), renderer,
	                                                  "text", SEARCH_AVAILABILITY_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_AVAILABILITY_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);
	gtk_tree_view_column_set_min_width (column, 5);
	scol_avail   = search_get_tree_view_column_number (new_search->view, column);


	/* format */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Format")), renderer,
	                                                  "text", SEARCH_FORMAT_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_FORMAT_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.5);
	g_object_set (G_OBJECT(renderer), "xalign", 0.5, NULL);

	/* length */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Length")), renderer,
	                                                  "text", SEARCH_LENGTH_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_LENGTH_MINUTES_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);

	scol_length  = search_get_tree_view_column_number (new_search->view, column);


	/* bitrate */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Bitrate")), renderer,
	                                                  "text", SEARCH_BITRATE_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_BITRATE_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.9);
	g_object_set (G_OBJECT(renderer), "xpad", 5, NULL);
	g_object_set (G_OBJECT(renderer), "xalign", 1.0, NULL);
	gtk_tree_view_column_set_min_width (column, 5);

	scol_bitrate = search_get_tree_view_column_number (new_search->view, column);


	/* artist */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Artist")),
	                                                  renderer, "text", SEARCH_ARTIST_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_ARTIST_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->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);

	scol_artist  = search_get_tree_view_column_number (new_search->view, column);


	/* title */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Type")),
	                                                  renderer, "text", SEARCH_TITLE_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_TITLE_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->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);

	scol_title   = search_get_tree_view_column_number (new_search->view, column);


	/* album */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Album")),
	                                                  renderer, "text", SEARCH_ALBUM_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_ALBUM_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->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);

	scol_album   = search_get_tree_view_column_number (new_search->view, column);


	/* codec */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Codec")),
	                                                  renderer, "text", SEARCH_CODEC_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_CODEC_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->view), column);
	gtk_tree_view_column_set_alignment(column, 0.5);
	g_object_set (G_OBJECT(renderer), "xalign", 0.5, NULL);

	scol_codec   = search_get_tree_view_column_number (new_search->view, column);


	/* type */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Type")),
	                                                  renderer, "text", SEARCH_TYPE_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_TYPE_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->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);

	/* hash */

	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes(UTF8_SHORT_PRINTF("%s",_("Hash")), renderer,
	                                                  "text", SEARCH_HASH_STRING_COL, NULL);
	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
	gtk_tree_view_column_set_sort_column_id(column, SEARCH_HASH_STRING_COL);
	gtk_tree_view_append_column(GTK_TREE_VIEW(new_search->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);

	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(new_search->view), opt_get_bool(OPT_GUI_SET_RULES_HINT_ON_LISTS));

	/* put search view in scrolled window */
	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), new_search->view);

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(new_search->view));

	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);


	g_signal_connect (G_OBJECT(new_search->view), "row-activated",
	                  G_CALLBACK(search_onRowActivated), new_search);

	g_signal_connect (G_OBJECT(new_search->view), "popup_menu",
	                  G_CALLBACK(search_onPopUpMenu), new_search);

	g_signal_connect (G_OBJECT(new_search->view), "button_press_event",
	                  G_CALLBACK(search_onButtonPress), new_search);

	g_signal_connect (G_OBJECT(new_search->view), "key_press_event",
	                  G_CALLBACK(search_onKeyPress), new_search);


	/* put scrollwin in frame and append new page to search notebook */

	gtk_container_add (GTK_CONTAINER(new_search->frame), scrollwin);

	gtk_notebook_append_page (GTK_NOTEBOOK (s_notebook), new_search->frame, hbox);

	search_switch_to_search_page(new_search);

	gtk_widget_show(scrollwin);
	gtk_widget_show(new_search->view);
	gtk_widget_show_all(new_search->frame);
	gtk_widget_show_all(new_search->view);

	searches = g_list_append ( searches, new_search );

	if ( g_list_length (searches) == 2 )	/* means: before there was only one */
	{
		Search *s = (Search*) searches->data;

		g_return_if_fail ( s              != NULL );
		g_return_if_fail ( s->phrase_utf8 != NULL );

		if ( g_utf8_collate(s->phrase_utf8, UTF8_SHORT_PRINTF("%s",_("empty"))) == 0 )
		{
			search_delete(s);
		} 
	}

	/* hide 'more' and 'extend' buttons? */

	if (gui_core_conn_is_overnet(core) || !gui_core_conn_is_hybrid(core))
	{
		gtk_widget_hide(button_more);
		gtk_widget_hide(button_extend);
	}

}


/******************************************************************************
 *
 *   search_switch_to_search_page
 *
 *   we need to use an idle timeout here because we can't directly
 *   switch to the page after it's just been created (but not realised yet).
 *
 ***/

static void
onSearchPageRealized (GtkWidget *frame, Search *search)
{
	gint pagenum;

	g_return_if_fail (search != NULL);

	pagenum = gtk_notebook_page_num (GTK_NOTEBOOK(s_notebook), search->frame);

	if ( pagenum >= 0 )
		gtk_notebook_set_current_page (GTK_NOTEBOOK(s_notebook), pagenum);
}

void
search_switch_to_search_page (Search *search)
{
	g_return_if_fail (search     != NULL);
	g_return_if_fail (s_notebook != NULL);
	
	if (GTK_WIDGET_REALIZED(s_notebook) && GTK_WIDGET_REALIZED(search->frame))
	{
		gint pagenum;

		pagenum = gtk_notebook_page_num (GTK_NOTEBOOK(s_notebook), search->frame);

		if ( pagenum >= 0 )
			gtk_notebook_set_current_page (GTK_NOTEBOOK(s_notebook), pagenum);
	}
	else
	{
		g_signal_connect(search->frame, "realize", (GCallback) onSearchPageRealized, search);
//		g_idle_add ((GSourceFunc)search_switch_to_search_page_timeout, search);
	}
}


/******************************************************************************
 *
 *   search_get_ed2k_link_search
 *
 ***/

Search *
search_get_ed2k_link_search (void)
{
	GList *node;

	/* search page disabled? */
	if (s_notebook == NULL)
		return NULL;
	
	for (node = searches;  node != NULL;  node = node->next)
	{
		Search *s = (Search*) node->data;

		g_return_val_if_fail ( s              != NULL, NULL );
		g_return_val_if_fail ( s->phrase_utf8 != NULL, NULL );

		if ( g_utf8_collate(s->phrase_utf8, UTF8_SHORT_PRINTF("%s",LINK_SEARCHLIST_LABEL)) == 0 )
		{
			return s;
		}
	}

	search_make_new_search (UTF8_SHORT_PRINTF("%s",LINK_SEARCHLIST_LABEL));

	node = g_list_last(searches);

	g_return_val_if_fail ( node != NULL, NULL );

	return (Search*)node->data;
}


/******************************************************************************
 *
 *   create_search_notebook
 *
 ***/

static GtkWidget *
search_create_notebook (void)
{
	s_notebook = gtk_notebook_new();

	gtk_widget_show(s_notebook);

	gtk_notebook_set_scrollable (GTK_NOTEBOOK(s_notebook),TRUE);

	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (s_notebook), opt_get_int(OPT_GUI_SEARCH_NOTEBOOK_TAB_POS));

	gtk_notebook_set_scrollable (GTK_NOTEBOOK (s_notebook), TRUE);

	search_make_new_search (UTF8_SHORT_PRINTF("%s", _("empty")));

	return s_notebook;
}



/******************************************************************************
 *
 *   search_entry_widget_changed
 *
 ***/

void
search_onEntryWidgetChanged (GtkWidget *widget, gpointer data)
{
	/* search page disabled? */
	if (s_notebook == NULL)
		return;
		
	g_return_if_fail (button_extend != NULL);

	gtk_widget_set_sensitive (button_extend, FALSE);

	search_has_changed = TRUE;
}


/******************************************************************************
 *
 *   search_onOptionMenuItemSelected
 *
 *   N.B. each click on an item results in two calls to this function: first 
 *        from the previously selected menu item widget, then from the newly 
 *        selected menu item widget. In our case that doesn't matter.
 *
 ***/

static void
search_onOptionMenuItemSelected (GtkWidget *widget, gpointer data)
{
	static SearchType  lasttype = SEARCH_TYPE_N;	/* something invalid */
	SearchType         type;

	g_return_if_fail ( widget != NULL );

	type = (SearchType) GPOINTER_TO_UINT(data);

	search_has_changed = TRUE;

	if ( type == lasttype )
		return;

	gtk_widget_set_sensitive (button_extend, FALSE);

	gtk_widget_set_sensitive (sbminbitrate, ( type == SEARCH_TYPE_AUDIO ) );

	lasttype = type;
}


/******************************************************************************
 *
 *   create_searchlist_om_item
 *
 ***/

static GtkWidget *
append_searchlist_om_item (GtkWidget *menu, GSList **group_p, SearchType type)
{
	const gchar *item_text_locale, *utf8;
	GtkWidget   *menu_item;

	g_return_val_if_fail ( group_p != NULL, NULL );
	g_return_val_if_fail ( type < SEARCH_TYPE_N, NULL );
	
	item_text_locale = _(search_types[type]);
	utf8 = UTF8_SHORT_PRINTF(item_text_locale);

	menu_item = gtk_radio_menu_item_new_with_label (*group_p, utf8);

	*group_p  = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM(menu_item));

	gtk_widget_show (menu_item);

	g_signal_connect (menu_item, "activate", G_CALLBACK (search_onOptionMenuItemSelected), GUINT_TO_POINTER(type));

	g_object_set_data (G_OBJECT(menu_item), "searchtype", GUINT_TO_POINTER(type));

	gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);

	return (menu_item);
}

/******************************************************************************
 *
 *   search_set_overnet_mode
 *
 ***/

void
search_set_overnet_mode (gboolean overnet)
{
	/* search page disabled? */
	if (s_notebook == NULL)
		return;
		
	/* gui-core-protocol does not support anything
	 * else thansimple searches in overnet yet     */

	if (overnet)
	{
		gtk_widget_set_sensitive (sbminsize,    FALSE );
		gtk_widget_set_sensitive (sbmaxsize,    FALSE );
		gtk_widget_set_sensitive (sbminavail,   FALSE );
		gtk_widget_set_sensitive (sbminbitrate, FALSE );
		gtk_option_menu_set_history (GTK_OPTION_MENU(om_search), 0);
		gtk_widget_set_sensitive (om_search,    FALSE );
		gtk_label_set_markup (GTK_LABEL(alignlabel),
		          UTF8_SHORT_PRINTF("<span size=\"x-small\">%s</span>",
		          _("overnet does not\n(yet) support this")));
	}
	else
	{
		gtk_widget_set_sensitive (sbminsize,    TRUE );
		gtk_widget_set_sensitive (sbmaxsize,    TRUE );
		gtk_widget_set_sensitive (sbminavail,   TRUE );
		gtk_widget_set_sensitive (sbminbitrate, TRUE );
		gtk_widget_set_sensitive (om_search,    TRUE );
		gtk_label_set_markup (GTK_LABEL(alignlabel),"");
	}
}

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

static void
onCoreConnStatus (GuiCoreConn *conn, guint status, gpointer data)
{
	/* Set back to donkey mode until we know that it's an overnet core */
	if (status == CONN_STATUS_AUTHENTICATING)
	{
		gtk_widget_set_sensitive (sbminsize,    TRUE );
		gtk_widget_set_sensitive (sbmaxsize,    TRUE );
		gtk_widget_set_sensitive (sbminavail,   TRUE );
		gtk_widget_set_sensitive (sbminbitrate, TRUE );
		gtk_widget_set_sensitive (om_search,    TRUE );
		gtk_label_set_markup (GTK_LABEL(alignlabel),"");
	}
	else if (status != CONN_STATUS_COMMUNICATING)
	{
		/* removes all search windows apart
		 *  from the 'ed2k-link' window     */

		GList *searches_copy, *node;

		/* we can't modify the GList by calling
		 *  search_delete() while we are looping
		 *  through it => make a copy of the list */

		searches_copy = g_list_copy(searches);

		for ( node = searches_copy; node != NULL; node = node->next)
		{
			Search *search = (Search*) node->data;

			if ((search) && g_utf8_collate (search->phrase_utf8, UTF8_SHORT_PRINTF("%s", LINK_SEARCHLIST_LABEL)) != 0)
				search_delete(search);
		}

		g_list_free(searches_copy);
	}
}


/******************************************************************************
 *
 *   search_onShowSearchOptionsToggled
 *
 ***/

void
search_onShowSearchOptionsToggled (GtkWidget *widget, gpointer data)
{
	/* search page disabled? */
	if (s_notebook == NULL)
		return;
		
	/* disabled while core doesn't support it */
	gtk_widget_hide (search_hbox_extra_options);
/*
	if (GTK_WIDGET_VISIBLE(search_hbox_extra_options))
	{
		gtk_widget_hide (search_hbox_extra_options);
	}
	else
	{
		gtk_widget_show (search_hbox_extra_options);

		search_onResetSearchOptions (NULL,NULL);
	}

	opt_set_bool(OPT_GUI_TOOLBAR_SHOW_SEARCH_OPTIONS, GTK_WIDGET_VISIBLE(search_hbox_extra_options));
*/
}

#if 0
/******************************************************************************
 *
 *   search_onResetSearchOptions
 *
 ***/

static void
search_onResetSearchOptions (GtkWidget *widget, gpointer data)
{
	if ( GTK_WIDGET_VISIBLE(search_hbox_extra_options) )
	{
		gtk_spin_button_set_value (GTK_SPIN_BUTTON(sbminsize), 0.0);
		gtk_spin_button_set_value (GTK_SPIN_BUTTON(sbmaxsize), 4096.0);
		gtk_spin_button_set_value (GTK_SPIN_BUTTON(sbminavail), 1.0);
		gtk_spin_button_set_value (GTK_SPIN_BUTTON(sbminbitrate), 128.0);
	}
}
#endif

/******************************************************************************
 *
 *   onOvernetStatus
 *
 ******************************************************************************/

static void
onOvernetStatus (GuiCoreConn *conn, const gchar *idstr, const gchar *fwstatus, gpointer data)
{
	/* gui-core-protocol does not support anything
	 * but simple searches in overnet at the moment */

	gtk_widget_set_sensitive (sbminsize,    FALSE );
	gtk_widget_set_sensitive (sbmaxsize,    FALSE );
	gtk_widget_set_sensitive (sbminavail,   FALSE );
	gtk_widget_set_sensitive (sbminbitrate, FALSE );
	gtk_option_menu_set_history (GTK_OPTION_MENU(om_search), 0);
	gtk_widget_set_sensitive (om_search,    FALSE );

	gtk_label_set_markup (GTK_LABEL(alignlabel),
		          UTF8_SHORT_PRINTF("<span size=\"x-small\">%s</span>",
		          _("overnet does not\n(yet) support this")));
}

/******************************************************************************
 *
 *   onHybridStatus
 *
 ******************************************************************************/

static void
onHybridStatus (GuiCoreConn *conn, const gchar *idstr, const gchar *fwstatus, gpointer data)
{
	gtk_widget_set_sensitive (sbminsize,    TRUE );
	gtk_widget_set_sensitive (sbmaxsize,    TRUE );
	gtk_widget_set_sensitive (sbminavail,   TRUE );
	gtk_widget_set_sensitive (sbminbitrate, TRUE );
	gtk_widget_set_sensitive (om_search,    TRUE );
//	gtk_label_set_markup (GTK_LABEL(alignlabel),"");

}

/***************************************************************************
 *
 *   onStatusMessageUnfiltered
 *
 ***************************************************************************/

static void
onStatusMessageUnfiltered (GuiCoreConn *conn, const gchar *msg, gpointer data)
{
	if (!msg)
		return;

	if (g_ascii_strncasecmp(msg,"Not connected to a server", 24) == 0)
	{
		gtk_widget_set_sensitive(button_extend, FALSE);
		gtk_widget_set_sensitive(button_more, FALSE);
	}
	else if (g_ascii_strncasecmp(msg,"No more servers to extend to", 26) == 0)
	{
		statusbar_msg (" %s", msg);
	}
}

/***************************************************************************
 *
 *   onServerConnected
 *
 ***************************************************************************/

static void
onServerConnected (GuiCoreConn *conn, const gchar *name, gpointer data)
{
	gtk_widget_set_sensitive(button_extend, FALSE);
	gtk_widget_set_sensitive(button_more, FALSE);

	gui_core_conn_send_get_stats(conn);
}

/******************************************************************************
 *
 *   onServerDisconnected
 *
 ******************************************************************************/

static void
onServerDisconnected (void)
{
	gtk_widget_set_sensitive(button_extend, FALSE);
	gtk_widget_set_sensitive(button_more, FALSE);
}

#if 0
/******************************************************************************
 *
 *   search_get_extra_search_options
 *
 *   sets variables to extra search controls' values.
 *
 *   returns FALSE if it is a simple search or typed search, and
 *           TRUE if it is a search with extra options like min size etc.
 *
 ***/

static gboolean
search_get_extra_search_options ( guint *minsize,
                                  guint *maxsize,
                                  guint *minavail,
                                  guint *minbitrate,
                                  SearchType *type )
{
	gfloat       f_minsize, f_maxsize;        /* size in MB */
	gboolean     minbratdefault;
	GtkWidget   *active_om_item;

	g_return_val_if_fail ( minsize    != NULL, FALSE );
	g_return_val_if_fail ( maxsize    != NULL, FALSE );
	g_return_val_if_fail ( minavail   != NULL, FALSE );
	g_return_val_if_fail ( minbitrate != NULL, FALSE );
	g_return_val_if_fail ( type       != NULL, FALSE );

	*minsize    = 0;
	*maxsize    = 0xFFFFFFFF;
	*minavail   = 0;
	*minbitrate = 0;
	*type       = SEARCH_TYPE_ANY;

	if ( !GTK_WIDGET_VISIBLE(search_hbox_extra_options) )
		return FALSE;

	g_return_val_if_fail ( om_search != NULL, FALSE );

	active_om_item = gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(GTK_OPTION_MENU(om_search))));

	*type = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(active_om_item), "searchtype"));

	f_minsize = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sbminsize));
	*minsize = (guint) MIN(f_minsize*1024.0*1024.0, 4294967295.0);

	f_maxsize = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sbmaxsize));
	*maxsize = (guint) MIN(f_maxsize*1024.0*1024.0, 4294967295.0);

	*minavail = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sbminavail));

	*minbitrate = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sbminbitrate));

	minbratdefault = ( !GTK_WIDGET_SENSITIVE(sbminbitrate)  ||  *minbitrate == 128 );

	if ( *minsize == 0  &&  *maxsize == 0xffffffff  &&  *minavail == 1  &&  minbratdefault )
		return FALSE;

	return TRUE;
}
#endif

/******************************************************************************
 *
 *   search_create_options_hbox
 *
 ***/

GtkWidget *
search_create_options_hbox ( GtkSizeGroup *sizegroup_go_about,
                             GtkSizeGroup *sizegroup_logos,
                             GSList **list_entrywidgets,
                             GSList **list_labelwidgets)
{
	GtkWidget    *hbox;
	GtkWidget    *hbox_size;
	GtkWidget    *hbox_bitrate;
	GtkWidget    *hbox_avail;
	GtkWidget    *label, *menu_search;
	GtkSizeGroup *sizegroup;
	const gchar  *template = "<span size=\"small\">%s</span>";
	GSList       *group;	/* for option menu */

	g_return_val_if_fail ( list_entrywidgets != NULL, NULL );

	sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
	
	hbox = gtk_hbox_new (FALSE, 10);
	search_hbox_extra_options = hbox;

/* sizegroup_logo is disabled for now */

	/* dummy label at very beginning */
/*	label = misc_gtk_label_new_with_markup ( NULL, NULL);
	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
	gtk_size_group_add_widget (sizegroup_logos, label);
*/
	/* size */

	hbox_size = gtk_hbox_new(FALSE,0);
	gtk_widget_show (hbox_size);
	gtk_box_pack_start (GTK_BOX(hbox), hbox_size, TRUE, TRUE, 20);

	label = misc_gtk_label_new_with_markup ( template, _(" size between ") );
	gtk_label_set_justify (GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
	gtk_box_pack_start (GTK_BOX(hbox_size), label, FALSE, FALSE, 0);

	if (!opt_get_bool(OPT_GUI_NO_GREEN))
		gtk_widget_set_style (label, style_notebook_label);

	sbminsize = gtk_spin_button_new (GTK_ADJUSTMENT(gtk_adjustment_new(0.0,0.0,4096.0,1.0,50.0,50.0)), 1.0, 0);
	gtk_widget_show (sbminsize);
	gtk_box_pack_start (GTK_BOX(hbox_size), sbminsize, TRUE, TRUE, 0);  
	gtk_size_group_add_widget (sizegroup, sbminsize);
	*list_entrywidgets = g_slist_append (*list_entrywidgets, sbminsize);

	label = misc_gtk_label_new_with_markup ( template, _(" and ") );

	gtk_box_pack_start (GTK_BOX(hbox_size), label, FALSE, FALSE, 0);

	gtk_label_set_justify (GTK_LABEL(label), GTK_JUSTIFY_CENTER);

	if (!opt_get_bool(OPT_GUI_NO_GREEN))
		gtk_widget_set_style (label, style_notebook_label);

	sbmaxsize = gtk_spin_button_new (GTK_ADJUSTMENT(gtk_adjustment_new(4096.0,0.0,4096.0,1.0,50.0,50.0)), 1.0, 0);
	gtk_widget_show (sbmaxsize);
	gtk_box_pack_start (GTK_BOX(hbox_size), sbmaxsize, TRUE, TRUE, 0);
	gtk_size_group_add_widget (sizegroup, sbmaxsize);
	*list_entrywidgets = g_slist_append (*list_entrywidgets, sbmaxsize);

	label = misc_gtk_label_new_with_markup ( template, _(" MB") );
	gtk_label_set_justify (GTK_LABEL(label), GTK_JUSTIFY_LEFT);
	gtk_box_pack_start (GTK_BOX(hbox_size), label, FALSE, FALSE, 0);

	if (!opt_get_bool(OPT_GUI_NO_GREEN))
		gtk_widget_set_style (label, style_notebook_label);


	/* bitrate */

	hbox_bitrate = gtk_hbox_new(FALSE,0);
	gtk_widget_show (hbox_bitrate);
	gtk_box_pack_start (GTK_BOX(hbox), hbox_bitrate, TRUE, TRUE, 0);

	label = misc_gtk_label_new_with_markup ( template, _("bitrate >=") );
	gtk_box_pack_start (GTK_BOX(hbox_bitrate), label, FALSE, FALSE, 0);
	gtk_label_set_justify (GTK_LABEL(label), GTK_JUSTIFY_RIGHT);

	if (!opt_get_bool(OPT_GUI_NO_GREEN))
		gtk_widget_set_style (label, style_notebook_label);

	sbminbitrate = gtk_spin_button_new (GTK_ADJUSTMENT(gtk_adjustment_new(128.0, 32.0,512.0, 16.0,32.0,32.0)), 16.0, 0);
	gtk_widget_show(sbminbitrate);
	gtk_widget_set_sensitive (sbminbitrate, FALSE);
	gtk_box_pack_start (GTK_BOX(hbox_bitrate), sbminbitrate, TRUE, TRUE, 0);
	gtk_size_group_add_widget (sizegroup, sbminbitrate);
	*list_entrywidgets = g_slist_append (*list_entrywidgets, sbminbitrate);


	/* availability */

	hbox_avail = gtk_hbox_new(FALSE,0);
	gtk_widget_show (hbox_avail);
	gtk_box_pack_start (GTK_BOX(hbox), hbox_avail, TRUE, TRUE, 0);

	label = misc_gtk_label_new_with_markup ( template, _(" min. avail ") );
	gtk_label_set_justify (GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
	gtk_box_pack_start (GTK_BOX(hbox_avail), label, FALSE, FALSE, 0);

	if ( !opt_get_bool(OPT_GUI_NO_GREEN) )
		gtk_widget_set_style (label, style_notebook_label);

	sbminavail = gtk_spin_button_new (GTK_ADJUSTMENT(gtk_adjustment_new(1.0,1.0,9999.0,1.0,10.0,10.0)), 1.0, 0);
	gtk_widget_show (sbminavail);
	gtk_box_pack_start (GTK_BOX(hbox_avail), sbminavail, TRUE, TRUE, 0);
	gtk_size_group_add_widget (sizegroup, sbminavail);
	*list_entrywidgets = g_slist_append (*list_entrywidgets, sbminavail);


	/* dummy label at very end */
	alignlabel = misc_gtk_label_new_with_markup ( NULL, NULL);
	gtk_box_pack_end (GTK_BOX (hbox), alignlabel, FALSE, FALSE, 0);
	gtk_size_group_add_widget(sizegroup_go_about, alignlabel);


	/* create search types options menu */

	om_search   = gtk_option_menu_new();        /* create the option menu */

	menu_search = gtk_menu_new();               /* create a menu          */
	group       = NULL;

	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_ANY);
	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_AUDIO);
	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_VIDEO);
//	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_USER);
	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_IMAGE);
	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_PROGRAM);
	(void) append_searchlist_om_item (menu_search, &group, SEARCH_TYPE_DOCUMENT);
	gtk_widget_show (menu_search);

	*list_labelwidgets = g_slist_concat (*list_labelwidgets, g_slist_copy(group));

	gtk_option_menu_set_menu (GTK_OPTION_MENU(om_search), menu_search);

	gtk_widget_show (om_search);

	gtk_box_pack_end (GTK_BOX (hbox), om_search, FALSE, TRUE, 0);

	g_object_unref(G_OBJECT(sizegroup));

	/* hide for now, while the filtering doesn't work in edonkey/overnet (1.0) anyway */
	gtk_widget_hide (hbox_size);
	gtk_widget_hide (hbox_bitrate);
	gtk_widget_hide (hbox_avail);
	gtk_widget_hide (om_search);

	return hbox;
}


/******************************************************************************
 *
 *   search_create_buttons_hbox
 *
 ***/

static GtkWidget *
search_create_buttons_hbox (void)
{
	GtkWidget	*hbox_search;
	GtkTooltips *tooltips;

	tooltips = gtk_tooltips_new();

	/* hbox_search is the whole lower row */
	hbox_search = gtk_hbox_new (FALSE,5);
	gtk_widget_show (hbox_search);

	/* more search button */

	new_icon_button_pack_and_signal_connect ( &button_more, hbox_search , _(" more "),
	                                          search_onMoreResults, NULL,
	                                          0, ICON_MENU_200);

 	gtk_widget_set_sensitive (button_more, FALSE);

	gtk_tooltips_set_tip ( tooltips, button_more,
	                       UTF8_SHORT_PRINTF("%s", _("get the next 200 matches from the main server (if there are more matches)")), NULL);

	/* extend search button */

	new_icon_button_pack_and_signal_connect ( &button_extend,
	                                          hbox_search , _(" extend 5x "),
	                                          search_onExtendSearch, NULL,
	                                          0, ICON_MENU_EXTEND);

 	gtk_widget_set_sensitive (button_extend, FALSE);

	gtk_tooltips_set_tip ( tooltips, button_extend,
	                       UTF8_SHORT_PRINTF("%s", _("search five other servers (note: you'll need to search your main server first!)")), NULL);

	return (hbox_search);
}




/******************************************************************************
 *
 *   search_create_page
 *
 ***/

GtkWidget *
search_create_page (void)
{
	GtkWidget *vbox, *button_hbox;

	opt_notify (OPT_GUI_SET_RULES_HINT_ON_LISTS, onOptionChanged);

	vbox = gtk_vbox_new (FALSE, 5);

	/* Need to create this before we create the view */
	button_hbox = search_create_buttons_hbox();

	gtk_box_pack_start (GTK_BOX(vbox), search_create_notebook(), TRUE, TRUE, 5);

	gtk_box_pack_end (GTK_BOX(vbox), button_hbox, FALSE, FALSE, 5);

	gtk_widget_show (vbox);

	g_signal_connect(core, "server-connected",          (GCallback) onServerConnected,         NULL);
	g_signal_connect(core, "server-disconnected",       (GCallback) onServerDisconnected,      NULL);
	g_signal_connect(core, "overnet-status",            (GCallback) onOvernetStatus,           NULL);
	g_signal_connect(core, "hybrid-status",            (GCallback) onHybridStatus,           NULL);
	g_signal_connect(core, "shared-files",              (GCallback) onSharedFiles,             NULL);
	g_signal_connect(core, "status-message-unfiltered", (GCallback) onStatusMessageUnfiltered, NULL);
	g_signal_connect(core, "search-extended",           (GCallback) onSearchExtended,          NULL);
	g_signal_connect(core, "search-results",            (GCallback) onSearchResults,           NULL);
	g_signal_connect(core, "core-conn-status",          (GCallback) onCoreConnStatus,          NULL);

	g_atexit(&atExit);

	/* disabled while core doesn't support it */
/*
	if (opt_get_bool(OPT_GUI_TOOLBAR_SHOW_SEARCH_OPTIONS))
		gtk_widget_show (search_hbox_extra_options);
	else
		gtk_widget_hide (search_hbox_extra_options);
*/
	gtk_widget_hide (search_hbox_extra_options);

	return vbox;
}



