/***************************************************************************
              status_page.c  -  GTK2 version of the status page
              -------------------------------------------------
    begin                : Sat Feb 1 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.                                   *
 *                                                                         *
 ***************************************************************************/

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

#define _GNU_SOURCE
#include <stdio.h>		/* for vasprintf() declaration */

#include "colors.h"
#include "core-conn.h"
#include "global.h"
#include "logfile.h"
#include "mainwindow.h"
#include "misc.h"
#include "misc_strings.h"
#include "options.h"
#include "statusbar.h"
#include "status_page.h"
#include <string.h>
#include <errno.h>

#include <gtk/gtkframe.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtktextview.h>
#include <gtk/gtktextbuffer.h>
#include <gtk/gtkvbox.h>


/* local variables */

static GtkWidget    	*view = NULL;		/* status text view widget */
static GtkTextBuffer	*viewbuf = NULL;	/* status text buffer      */

/* local functions */

static void              status_message_coloured (GdkColor *colour, const gchar *msg);

static GtkTextTag       *status_page_lookup_colour_tag (GdkColor *color);

static void              status_select_and_copy_ed2klinks_in_status_text (void);



/******************************************************************************
 *
 * gui_print_error_function
 *
 * This is our new error print handler (for g_printerr())
 *
 * If the window and the status text widget exist, it will output the error
 *	to stderr and the status log (and thus also the status log file)
 * If the window or the status text widget are not initialised yet, output will
 *	go to stderr only
 *
 */

void
gui_print_error_function (const gchar *errstring)
{
	gchar *newline = "";
	g_return_if_fail (errstring!=NULL);
	g_return_if_fail (*errstring!=0x00);

	if (errstring[strlen(errstring)-1] != '\n')
		newline = "\n";

	/* This will also output the line to the logfile */
	status_warning (_("GUI: %s%s"), errstring, newline);

	/* Might contain markup escapes */
	if ( strchr(errstring,'&') != NULL )
	{
		gchar *unesc;
		unesc = misc_strings_unescape_text(errstring);
		if (unesc)
		{
			fprintf(stderr, _("GUI: %s%s"), unesc, newline);
			g_free(unesc);
		}
	}
	else
		fprintf (stderr, _("GUI: %s%s"), errstring, newline);
}


/******************************************************************************
 *
 *   status_select_and_copy_ed2klinks_in_status_text
 *
 *   selects all (consecutive) ed2k-links at the end of the textbuf
 *   and copies them to the clipboard
 *
 ***/

static void
status_select_and_copy_ed2klinks_in_status_text (void)
{
	GtkClipboard  *clipboard;
	GtkTextIter    end, firstlink, emptyiter, previous;
	gint           l, lines;

	g_return_if_fail ( viewbuf != NULL );

	memset(&firstlink, 0x00, sizeof(GtkTextIter));
	memset(&emptyiter, 0x00, sizeof(GtkTextIter));

	lines = gtk_text_buffer_get_line_count(GTK_TEXT_BUFFER(viewbuf));

	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER(viewbuf), &end);

	memcpy(&previous, &end, sizeof(GtkTextIter));

	for ( l = lines-2; l >= 0; l--)
	{
		GtkTextIter   linestart;
		gchar        *txt;

		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER(viewbuf), &linestart, l);

		txt = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(viewbuf), &linestart, &previous, FALSE);

		memcpy(&previous, &linestart, sizeof(GtkTextIter));

		if (!txt)
			break;

		/* these are all ascii chars, so utf-8 must match ascii */
		if ( g_ascii_strncasecmp(txt,"ed2k:",5) != 0 )
		{
			g_free(txt);
			break;
		}

		memcpy(&firstlink, &linestart, sizeof(GtkTextIter));

		g_free(txt);
	}

	/* note: we've just printed a link, so there must be at least
	 * one line with an ed2k-link in the buffer                    */

	g_return_if_fail ( memcmp(&firstlink, &emptyiter, sizeof(GtkTextIter)) !=0 );

	gtk_text_buffer_move_mark_by_name (GTK_TEXT_BUFFER(viewbuf), "insert", &firstlink);
	gtk_text_buffer_move_mark_by_name (GTK_TEXT_BUFFER(viewbuf), "selection_bound", &end);

	clipboard = gtk_clipboard_get (gdk_atom_intern("CLIPBOARD",TRUE));
	gtk_text_buffer_copy_clipboard (GTK_TEXT_BUFFER(viewbuf), clipboard);

}



/******************************************************************************
 *
 * status_msg_print_ed2klink
 *
 * Just prints an ed2k://|file| link to the status log
 * Makes sure that the filename doesn't contain illegal characters.
 *
 ***/

void
status_msg_print_ed2klink (const gchar *filename, guint size, const guint8* hash)
{
	gchar *fn, *name_locale, *name;

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

	fn = misc_strings_unescape_text(filename);	/* just in case */

	g_return_if_fail ( fn != NULL );

	g_strdelimit(fn, "|\\/$~\"&^", ' ');	/* do NOT delimit '%' here */

	name = TO_LOCALE(fn);

	g_return_if_fail ( name != NULL );

	name_locale = misc_strings_escape_ascii_url (name);

	g_return_if_fail ( name_locale != NULL );

	status_msg ("ed2k://|file|%s|%u|%s|/\n", name_locale, size, hash_to_hash_str(hash));

	if (opt_get_bool(OPT_GUI_NO_LINKS_TO_LOGFILE))
	{
		statusbar_msg (_(" ed2k-link of this file written to status window and copied to the clipboard."));
	}
	else
	{
		statusbar_msg (_(" ed2k-link of this file written to logfile 'gui_statuslog' and to status log."));
	}

	status_select_and_copy_ed2klinks_in_status_text();

	g_free(fn);
	g_free(name);
	g_free(name_locale);
}


/******************************************************************************
 *
 * status_system_error_msg
 *
 * writes a status line in red to the statuslog, including the last error (errno must be set)
 * 'GUI:' is automatically prepended
 *
 */

void
status_system_error_msg (const gchar *msg_text)
{
	status_warning (_("GUI: %s (%s)\n"), (msg_text!=NULL) ? msg_text: "", g_strerror(errno));
}


/******************************************************************************
 *
 * status_parse_error_msg
 *
 * prints a 'GUI: parse error in file X line Y: <line beginning> ...' message
 *
 */

void
status_parse_error_msg (const gchar *infile, gint lineno, const gchar *linestring)
{
	g_return_if_fail (infile!=NULL);
	g_return_if_fail (linestring!=NULL);
	status_msg (_("GUI: parse error in %s file line %d: %20s...\n"), infile, lineno, linestring);
}



/******************************************************************************
 *
 *  status_page_lookup_colour_tag
 *
 */

static GtkTextTag *
status_page_lookup_colour_tag(GdkColor *color)
{
	if (color == &red)
	{
		return gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table(viewbuf), "RED_TAG");
	}
	else if (color == &blue)
	{
		return gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(viewbuf), "BLUE_TAG");
	}
	else if ( color == &purple )
		return gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(viewbuf), "PURPLE_TAG");

	return gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(viewbuf), "BLACK_TAG");
}


/******************************************************************************
 *
 *   status_page_scroll_to_end
 *
 ***/

static gboolean
status_page_scroll_to_end (gpointer data)
{
	GtkTextIter iter;

	if ((viewbuf)&&(view))
	{
		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (viewbuf), &iter);
		gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW(view), &iter, 0, TRUE, 0.0, 1.0);
	}

	return FALSE;
}

/******************************************************************************
 *
 *   status_message_coloured
 *
 *   Prints a message to the status window in a certain colour.
 *
 *   o will take care to suppress and/or parse certain messages
 *   o will write the message to the status log file if not suppressed.
 *
 */

static void
status_message_coloured (GdkColor *colour, const gchar *msg)
{
	const gchar *coltag = "";
	gchar       *new_msg = NULL;
	gchar       *msg_utf8 = NULL;
	GtkTextIter  end;

	g_return_if_fail ( colour != NULL);
	g_return_if_fail ( msg    != NULL);

	if (colour == &red)
		coltag = _("RED: ");

	if (colour == &blue)
		coltag = _("BLUE: ");

	logfile_write ("%s%s", coltag, msg);

	if ((!window) || (!view) || (!viewbuf))
		return;

	/* output time stamp */

	if ( opt_get_bool(OPT_GUI_LOG_HAVE_TIMESTAMP)
	      && *msg != '\n'
	      && g_ascii_strncasecmp(msg,"ed2k:",5) !=0 )
	{
		const gchar  *timestamp_utf8;
		GtkTextIter   enditer;

		timestamp_utf8 = UTF8_SHORT_PRINTF(logfile_get_timestamp());
		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER(viewbuf), &enditer);
		gtk_text_buffer_insert_with_tags (GTK_TEXT_BUFFER(viewbuf), &enditer, timestamp_utf8, -1, status_page_lookup_colour_tag (&purple), NULL);
	}

	/* (strip off all potential newlines etc.) */
	new_msg = g_strdup(msg);
	g_return_if_fail (new_msg != NULL);
	(void) g_strdelimit(new_msg, "\n\t", ' ');
	(void) g_strstrip(new_msg);

	/* output to status window */
	msg_utf8 = TO_UTF8(msg);

	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (viewbuf), &end);
	gtk_text_buffer_insert_with_tags (GTK_TEXT_BUFFER(viewbuf), &end, msg_utf8, -1, status_page_lookup_colour_tag (colour), NULL);

	g_idle_add ( (GSourceFunc) status_page_scroll_to_end, NULL );
	
	/* output to statusbar */
	statusbar_msg (" %s", new_msg);

	G_FREE(new_msg);
	FREE_UTF8(msg_utf8);
}

/******************************************************************************
 *
 *   status_msg
 *   status_warning
 *   status_message_blue
 *
 *   print messages to the status page (and to the status bar)
 *
 */

void
status_msg (const gchar *format, ...)
{
	gchar   *output;
	va_list  args;

	g_return_if_fail ( format != NULL );

	va_start(args, format);
	output = g_strdup_vprintf(format,args);
	va_end(args);

	status_message_coloured (&black, output);

	g_free(output);
}

void
status_warning (const gchar *format, ...)
{
	gchar   *output;
	va_list  args;

	g_return_if_fail ( format != NULL );

	va_start(args, format);
	output = g_strdup_vprintf(format,args);
	va_end(args);

	status_message_coloured (&red, output);

	g_free(output);
}

void
status_message_blue (const gchar *format, ...)
{
	gchar   *output;
	va_list  args;

	g_return_if_fail ( format != NULL );

	va_start(args, format);
	output = g_strdup_vprintf(format,args);
	va_end(args);

	status_message_coloured (&blue, output);

	g_free(output);
}



/******************************************************************************
 *
 *   status_page_clear
 *
 *   clears the status window
 *
 */

void
status_page_clear (void)
{
	static gboolean  license_shown = FALSE;
	GtkTextIter      startiter, enditer;
	gint             save_option_logtimestamp = opt_get_bool(OPT_GUI_LOG_HAVE_TIMESTAMP);

	if ((!window)||(!view)||(!viewbuf))
		return;

	opt_set_bool (OPT_GUI_LOG_HAVE_TIMESTAMP, FALSE);

	gtk_text_buffer_get_bounds(viewbuf, &startiter, &enditer);
	gtk_text_buffer_delete(viewbuf, &startiter, &enditer);

	status_warning (" \n                  ");
	gtk_text_buffer_get_end_iter(viewbuf, &enditer);
	gtk_text_buffer_insert_pixbuf(viewbuf, &enditer, get_icon(ICON_TOOLBAR_ED2K_LOGO));

	status_warning ("        ");
	gtk_text_buffer_get_end_iter(viewbuf, &enditer);
	gtk_text_buffer_insert_pixbuf(viewbuf, &enditer, get_icon(ICON_TOOLBAR_OVERNET_LOGO));

	status_warning (" \n \n");

	status_warning (_("Welcome to this %s GUI for eDonkey2000 and Overnet.\n"), ""); /* to keep translation string fixed */
	status_warning ("ed2k-gtk-gui version %s, Copyright (c) 2001-2003 Tim-Philipp Müller\n", VERSION);

	if (!license_shown)
	{
		license_shown = TRUE;
		status_warning (" \n");		/* need the space so timestamps don't look funny */
		status_warning (_("This program is free software; you can redistribute it and/or modify\n"));
		status_warning (_("it under the terms of the GNU General Public License as published by\n"));
		status_warning (_("the Free Software Foundation; either version 2 of the License, or\n"));
		status_warning (_("(at your option) any later version.\n"));
		status_warning (" \n");		/* need the space so timestamps don't look funny */
		status_warning (_("This program is distributed in the hope that it will be useful, but\n"));
		status_warning (_("without any warranty; without even the implied warranty of merchantability\n"));
		status_warning (_("or fitness for a particular purpose. See the GNU General Public License\n"));
		status_warning (_("for more details: http://www.gnu.org\n"));
	}

	status_warning (" \n");		/* need the space so timestamps don't look funny */
	status_msg     (_("It is written in plain C using the Gimp Toolkit (GTK+) and the\n"));
	status_msg     (_("gnet library. Project homepage: http://ed2k-gtk-gui.sourceforge.net\n"));
	status_msg     (" \n");		/* so the statusbar is empty */
	status_message_blue ( _("Click on the eDonkey/Overnet logo in the top left corner to access extra functions.\n"));
	status_message_blue ( _("Type 'help' in the top toolbar to see what you can do with it and how to use it.\n"));
	status_msg          (" \n");		/* so the statusbar is empty */

	opt_set_bool (OPT_GUI_LOG_HAVE_TIMESTAMP, save_option_logtimestamp);
}


/******************************************************************************
 *
 *  status_page_print_clientID_info
 *
 *  prints the clientID and firewalled/not firewalled info to the statusbar
 *
 *  if idstring is NULL, it prints the last available ID info, otherwise
 *   it saves idstring as new ID info and does not print it out.
 *
 ***/

static gchar        *idinfo = NULL;

void
status_page_print_clientID_info (const gchar *idstring)
{
	if (idstring)
	{
		G_FREE(idinfo);
		idinfo = g_strdup(idstring);
		return;
	}

	if ( idinfo  &&  *idinfo != 0x00 )
		statusbar_msg(UTF8_SHORT_PRINTF("%s", idinfo));
	else
		statusbar_msg(_("No clientID information available yet."));

}

/***************************************************************************
 *
 *   onStatusMessage
 *
 ***************************************************************************/

static void
onStatusMessage (GuiCoreConn *conn, const gchar *msg, gpointer data)
{
	status_msg ("%s\n", msg);
}

/***************************************************************************
 *
 *   onErrorMessage
 *
 ***************************************************************************/

static void
onErrorMessage (GuiCoreConn *conn, const gchar *msg, gpointer data)
{
	status_warning ("%s", msg);

	if (g_ascii_strncasecmp(msg, "Not all written", 14) == 0)
	{
		status_msg ("GUI: %s", _("The above error _might_ be due to a full harddisk.\n"));
	}
	else if (g_ascii_strncasecmp(msg, "Import failed", 12) == 0)
	{
		status_msg ("%s.\n", _("GUI: This might be because you only have read, but no write permission on that file"));
	}
}

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

static void
onOvernetStatus (GuiCoreConn *conn, const gchar *idstr, const gchar *fwstatus, gboolean *overnet_mode)
{
	G_FREE(idinfo);

	idinfo = g_strconcat (idstr, fwstatus, "\n", NULL);

	/* Only print this the first time after connecting to the core */
	if ((overnet_mode) && *overnet_mode == FALSE)
	{
		status_msg(idinfo);

		status_message_blue(_("GUI: This seems to be an OVERNET core.\n"));

		*overnet_mode = TRUE;
	}
}

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

static void
onHybridStatus (GuiCoreConn *conn, const gchar *idstr, const gchar *fwstatus, gboolean *overnet_mode)
{
	G_FREE(idinfo);

	idinfo = g_strconcat (idstr, fwstatus, "\n", NULL);

	if ((overnet_mode) && *overnet_mode == FALSE)
	{
		status_msg(idinfo);

		status_message_blue(_("GUI: This seems to be an HYBRID core.\n"));

		*overnet_mode = TRUE;
	}
}

/******************************************************************************
 *
 *   onEDonkeyStatus
 *
 ******************************************************************************/

static void
onEDonkeyStatus (GuiCoreConn *conn, guint clientid, const gchar *fwstatus, gpointer data)
{
	G_FREE(idinfo);

	idinfo = g_strdup_printf ("ClientID: %u  (%s)\n", clientid, fwstatus);
}

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

static void
onServerConnected (GuiCoreConn *conn, const gchar *name, gpointer data)
{
	static gint lastnamehash; /* 0 */
	
	if (lastnamehash == 0 || lastnamehash != g_str_hash(name))
	{
		status_msg (_(" --- connected to server '%s' ---\n"), name);
		lastnamehash = g_str_hash(name);
	}
}



/***************************************************************************
 *
 *   onCoreConnStatusError
 *
 ***************************************************************************/

static void
onCoreConnStatusError (GuiCoreConn *conn, guint laststatus)
{
	switch (laststatus)
	{
		case CONN_STATUS_CONNECTING:
		{
			status_msg ( _("GUI: Could not establish GUI-core connection. (Core running? Core listening on 'aport' 4663? Core firewalled?)\n"));
		}
		break;

		case CONN_STATUS_AUTHENTICATING:
		{
			status_msg (_("GUI: Have you set up a user and password with the core? (see FAQ)\n"));
			status_msg (_("GUI: Established GUI-core connection, but authentification failed.\n"));
		}
		break;

		case CONN_STATUS_CLOSED_TIMEOUT:
		{
			/* Houston, we've got a problem - haven't gotten reply from core for 90secs */
			g_printerr ( _("Is core dead? Haven't received any data from core for %u seconds now...\n"), 90 /*CONN_TIMEOUT/1000*/);
#if 0
			if (gui_core_conn_is_local(core))
			{
				g_printerr ( _("(currently I won't do anything. option to restart dead core will be implemented soon)\n"));
				g_printerr ( _("dead core PID = %u\n"), local_donkey_pid);
			}
			else
			{
				g_printerr ( _("core is on another computer?!\n"));
				// TODO: have command in options to execute when dead remote core is detected,
				// so one could set it up to start a remote script via ssh or something
			}
#endif
		}
		break;

		case CONN_STATUS_CLOSED_TIMEOUT_NO_DATA:
		{
			const gchar	*msg[] = {
					"---\n",
					_("I haven't gotten a response from the core for 15 seconds now,\n"),
					_("although we are obviously connected to somewhere.\n"),
					_("Probably you are connecting to the core on the wrong port\n"),
					_("(ie the port for clients, 4662).\n\n"),
					_("You have to connect to the 'admin port', which is\n"),
					_("usually port 4663 (unless you have changed it with the\n"),
					_("'aport' command within the command line client).\n\n"),
					_("If you can't connect on that port, make sure you have started a core - \n"),
					_("either by clicking on the 'spawn local donkey' button (the dialog should\n"),
					_("display 'core already running' after that) or manually from the command\n"),
					_("line by using the '-' option (ie. './donkey - ! &amp;').\n\n"),
					_("In short: Try port 4663, otherwise please consult the FAQ\n"),
					"---\n", NULL };
			const gchar		**line = msg;
			const gchar    *hname;
			guint           port;

			while (*line)
			{
				g_printerr ("%s", *line);
				++line;
			}

			hname = gui_core_conn_get_host(core, &port);
			status_msg (_("GUI-core connection to %s:%u timed out and we have never received any data.\n"), hname, port);
			/* not trying to reconnect here - what's the point? */
		}
		break;

		default:
		{
/*
			if (gui_core_conn_is_local(core))
			{
				guint checkpid = donkey_process_running_check();

				g_printerr (_("I think the core crashed or was killed.\n"));

				if (checkpid > 0 && checkpid != G_MAXUINT)
				{
					g_printerr (_("Hmm, but it seems like there is still a local donkey running?! (PID=%u)\n"), checkpid);
				}
				g_printerr (_("(core restart on crash option is disabled for now until it works properly.)\n"));
			}
*/
			status_msg (_("GUI: GUI-core connection broke unexpectedly! (core crashed/killed?)\n"));
		}
		break;
	}
}

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

static void
onCoreConnStatus (GuiCoreConn *conn, guint status, gpointer data)
{
	static guint laststatus = G_MAXUINT;


	switch (status)
	{
		case CONN_STATUS_AUTHENTICATING:
		{
			G_FREE(idinfo);
			idinfo = g_strdup(_(" Not connected to a server. "));

			status_msg (_("GUI: we are CONNECTED to the core. Logging in...\n"));
		}
		break;

		case CONN_STATUS_COMMUNICATING:
		{
			if (laststatus != status)
				status_msg (_("GUI: we are LOGGED IN to the core. Enjoy.\n"));
		}
		break;

		case CONN_STATUS_CLOSED_DELIBERATELY:
		{
			status_msg (_("GUI: GUI-core connection closed deliberately.\n"));
		}
		break;

		case CONN_STATUS_CLOSED_TIMEOUT:
		case CONN_STATUS_CLOSED_UNEXPECTEDLY:
		case CONN_STATUS_CLOSED_TIMEOUT_NO_DATA:
		{
			G_FREE(idinfo);
			idinfo = g_strdup("");

			onCoreConnStatusError (conn, laststatus);
		}
		break;

		default:
			break;
	}

	laststatus = status;
}


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

static void
onServerDisconnected (void)
{
	statusbar_msg(_(" Not connected to a server. "));
	status_msg (_(" --- disconnected from server ---\n"));
}

/******************************************************************************
 *
 *  everyMinute
 *
 ******************************************************************************/

static gboolean
everyMinute (gpointer data)
{
	static gint minutes; /* 0 */

	/* clear status log if user has set the option accordingly */
	if ( opt_get_int(OPT_GUI_STATUS_WINDOW_CLEAR_INTERVAL) > 0
	     && minutes >= opt_get_int(OPT_GUI_STATUS_WINDOW_CLEAR_INTERVAL)
	     && minutes >  0
	     && (minutes % opt_get_int(OPT_GUI_STATUS_WINDOW_CLEAR_INTERVAL)) == 0
	     && gui_core_conn_is_alive(core))
	{
		status_page_clear();
		status_msg (_("GUI: cleared status window. You will find all previous output in the gui_statuslog file.\n"));
	}

	++minutes;


	return TRUE; /* call again */
}

/******************************************************************************
 *
 *  status_page_create
 *
 *  create the status window notebook page and returns it.
 *
 ***/

GtkWidget *
status_page_create (void)
{
	GtkWidget   *frame;
	GtkWidget   *scrollwin;
	GtkTextTag  *tag;

	frame = gtk_frame_new (NULL);
	gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);

	/* create non-editable text widget with word-wrap and add it to packing table */
	view = gtk_text_view_new();
	gtk_text_view_set_editable (GTK_TEXT_VIEW(view), FALSE);
	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW(view), FALSE);
	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(view), GTK_WRAP_WORD);

	viewbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
	gtk_text_buffer_set_text(viewbuf, " ", -1);
	g_object_set (G_OBJECT(view), "left-margin", 15, NULL);
	g_object_set (G_OBJECT(view), "right-margin", 15, NULL);

	/* create the tag table with some handy tags */

	tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (viewbuf), "RED_TAG",  "foreground-gdk", &red, NULL);
	tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (viewbuf), "BLUE_TAG", "foreground-gdk", &blue, NULL);
	tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (viewbuf), "BLACK_TAG", "foreground-gdk", &black, NULL);
	tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (viewbuf), "PURPLE_TAG", "foreground-gdk", &purple, "size-points", 7.0, NULL);

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

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


	status_page_clear();

	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), view);
	gtk_container_add (GTK_CONTAINER(frame), scrollwin);

	gtk_widget_show_all (frame);

	if (1)
	{
		static gboolean  overnet_mode; /* FALSE */
		static gboolean  hybrid_mode;  /* FALSE */

		g_signal_connect(core, "core-conn-status",    (GCallback) onCoreConnStatus, NULL);
		g_signal_connect(core, "server-connected",    (GCallback) onServerConnected, NULL);
		g_signal_connect(core, "server-disconnected", (GCallback) onServerDisconnected, NULL);
		g_signal_connect(core, "edonkey-status",      (GCallback) onEDonkeyStatus, NULL);
		g_signal_connect(core, "overnet-status",      (GCallback) onOvernetStatus, &overnet_mode);
		g_signal_connect(core, "hybrid-status",      (GCallback) onHybridStatus, &hybrid_mode);
		g_signal_connect(core, "status-message",      (GCallback) onStatusMessage, NULL);
		g_signal_connect(core, "error-message",       (GCallback) onErrorMessage, NULL);

		g_timeout_add(60*1000, (GSourceFunc) everyMinute, NULL);
	}

	return frame;
}







