/*************************************************************************
 *
 *	GTK Euler
 *
 *************************************************************************/
 
#ifdef HAVE_CONFIG_H
	#include <config.h>
#endif

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/times.h>
#include <time.h>
#include <dirent.h>
#include <fnmatch.h>

#include <locale.h>

#include "../pixmaps/icon.xpm"
#include "../pixmaps/logo.xpm"

#ifdef RS6000
	#include <sys/time.h>
	void gettimer (int, struct timestruc_t *);
#endif

#include "sysdep.h"
#include "mainloop.h"
#include "help.h"
#include "rc.h"
#include "term.h"
#include "metagtk.h"
#include "metaps.h"
#include "output.h"
#include "earray.h"	

/*-----------------------------------------------------------------------
 *	Global variables
 *----------------------------------------------------------------------*/
static int is_demo = 0;
static int in_text = 1;

static EulerPrefs prefs;
static GtkWidget *term_window = NULL;
static GtkWidget *term;
static GtkWidget *meta_window = NULL;
static GtkWidget *meta;

/*-----------------------------------------------------------------------
 *	Euler callbacks
 *----------------------------------------------------------------------*/

/*  text_mode
 *  Switch to text. Graphics should not be deleted.
 *  On a window system make text visible.
 */
void text_mode (void)
{
	if (!in_text)
	{
		in_text=1;
		gdk_window_raise(term_window->window);
	}
}

/*  graphic_mode
 *	Switch to graphics. Text must not be deleted.
 *	On a window system make graphics visible.
 */
void graphic_mode (void)
{
	if (in_text)
	{
		in_text=0;
		gdk_window_raise(meta_window->window);
	}
}

/************************************************************************
 *
 *	Graphic window
 *
 *		The graphic screen has coordinates from 0.0 to 1024.0 (double).
 *		There should be a function, which computes the correct screen
 *		coordinates from these internal ones.
 *
 ************************************************************************/
int usecolors=1;

/*	getpixelsize
 *	Compute the size of pixel in screen coordinates.
 */
void getpixelsize (double *x, double *y)
{
	*x=1024.0/meta->allocation.width;
	*y=1024.0/meta->allocation.height;
}

/*	gflush
 *	Flush out remaining graphic commands (for multitasking).
 *	This serves to synchronize the graphics on multitasking systems.
 */
void gflush (void)
{
}

/*
 *			S O U N D   W A V E S
 */
#ifdef WAVES
void sys_playwav (char *file)
{
}
#endif

/*
 *			G R A P H I C   I N P U T
 */

/*  mouse
 *	wait, until the user marked a screen point with the mouse.
 *	Return screen coordinates.
 */
void mouse (double *x, double *y)
{
	while (1)
	{
		if (gdk_events_pending())
		{
			GtkMeta *m = GTK_META(meta);
			
			gtk_main_iteration();
			
			if (m->x>0.0 && m->y>0.0)
			{	
				*x=m->x;
				*y=m->y;
				m->x = m->y = -1.0;
				return;
			}
			else
			{
				GtkTerm *t = GTK_TERM(term);
				if (t->scan==escape)
				{
					*x=-1.0; *y=-1.0; return;
				}
				t->scan = t->code = 0;
			}
		}
		else usleep(1);
	}
}

static int notekey=0;

/*  test_key
 *	see, if user pressed the keyboard.
 *	return the scancode, if he did.
 */
int test_key ()
{
	while (gtk_events_pending())
	{
		GtkTerm *t = GTK_TERM(term);

		gtk_main_iteration();

		if (t->scan==escape) {
			t->scan = t->code = 0;
			return escape;
		} else {
			if (t->scan) notekey=t->scan;
			else notekey=t->code;
			t->scan = t->code = 0;
			return 0;
		}
	}
	return 0;
}

int test_code (void)
{
	int key=0;

	if (notekey)
	{	key=notekey;
		notekey=0;
		return key;
	}
	while (gtk_events_pending())
	{
		GtkTerm *t=GTK_TERM(term);

		gtk_main_iteration();
		if (t->scan) {
			key = t->scan;
			t->scan = t->code = 0;
			return key;
		} else if (t->code) {
			key = t->code;
			t->scan = t->code = 0;
			return key;
		}
	}
	return 0;
}

/************************************************************************
 *
 *	Text Window
 *
 *		The following text screen commands should be emulated on a graphic
 *		work station. This can be done by a standard emulator (e.g. VT100)
 *		or within a window displaying the text. Additional features may be
 *		added, such as viewing old text. But the input line should be
 *		visible as soon as a key is pressed by the user.
 *
 ************************************************************************/

/* Clear the text screen */
void clear_screen (void)
{
	gtk_term_clear(term);
}

/*	Print a line onto the text screen, parse tabs and '\n'.
 *	Printing should be done at the cursor position. There is no need
 *	to clear the line at a '\n'.
 *	The cursor should move forward with the print.
 *	Think of the function as a simple emulator.
 *	If you have a line buffered input with echo then do not print,
 *	when the command line is on.
 */
void gprint (char *s) /* print an output text (no newline) */
{
	gtk_term_print(term,s);
}


/*	wait for a keystroke. return the scancode and the ascii code.
 *	scancode should be a code from scantyp. Do at least generate
 *	'enter'.
 */
gboolean forcebreak = FALSE;

int wait_key (int *scan)
{
	int key;
	GtkTerm *t = GTK_TERM(term);

	t->scan = t->code = 0;
	*scan=0;
	while (!forcebreak) {
		if (gtk_events_pending())
		{
			gtk_main_iteration();
			if (t->scan || t->code) break;
			t->scan = t->code = 0;
		}
		else usleep(1);
	}
	
	if (forcebreak){
		
		t->scan = enter;
		e_insert(t->history,e_get_length(t->history)-1,e_get_text(t->a,t->cur)+t->promptlen,0);
		if (e_get_length(t->history) > t->max_history)
			e_remove(t->history,0);
		t->hist=e_get_length(t->history)-1;
		set_editline(e_get_text(t->a,t->cur)+t->promptlen);
		
		key   = 0;
		forcebreak = FALSE;
	}		
	else 
		key   = t->code;
	
	*scan = t->scan;
	t->scan = t->code = 0;
	return key;
}


/* the command line is active (edit.c) */
void edit_on (void)
{
	gtk_term_edit_on(term);
}

/* the command line is no longer in use (graphics or computing) (edit.c) */
void edit_off (void)
{
	gtk_term_edit_off(term);
}

/************************************************************************
 *
 *	System
 *
 ************************************************************************/

static char path[256];

/* sets the path if dir!=0 and returns the path */
char *cd (char *dir)
{
	chdir(dir);
	if (getcwd(path,256)) return path;
	return dir;
}

static void shellsort(char **buf, int n)
{
	int h, i, j;
	int seq[28];
	int p1=1, p2=1, p3=1, s=-1;

	/*
	 * establish increment sequence (see Knuth, Vol 3)
	 */
	do {
		if (++s % 2) {
			seq[s] = 8*p1 - 6*p2 + 1;
		} else {
			seq[s] = 9*p1 - 9*p3 + 1;
			p2 *= 2;
			p3 *= 2;
		}
		p1 *= 2;
	} while(3*seq[s] < n);

	s = s > 0 ? s-1 : 0;
		
	/*
	 *	sort
	 */
	while (s >= 0) {
		/* sort-by-insertion in increments of h */
		h = seq[s--];
		for (i = h; i<n; i++) {
			char *t = buf[i];
			for (j = i-h; j >= 0 && strcmp(buf[j], t)>0; j -= h)
				buf[j+h] = buf[j];
			buf[j+h] = t;
		}
	}
}

static int match (char *pat, char *s)
{	if (*pat==0) return *s==0;
	if (*pat=='*')
	{	pat++;
		if (!*pat) return 1;
		while (*s)
		{	if (match(pat,s)) return 1;
			s++;
		}
		return 0;
	}
	if (*s==0) return 0;
	if (*pat=='?') return match(pat+1,s+1);
	if (*pat!=*s) return 0;
	return match(pat+1,s+1);
}

/*
 *	scan a directory and get :
 *		files : an array of entries matching the pattern
 *		files_count : number of files entries
 *	the function returns the max length of a file entry
 */
int scan_dir(char *dir_name, char *pat, char ** files[], int *files_count)
{
	DIR *dir;
	struct dirent *entry;
	int entry_count=0, len=0;
	char **buf = NULL;

	dir = opendir(dir_name);

	if (dir) {
		while((entry = readdir(dir)) != NULL) {
			if (match(pat,entry->d_name)) {
				int l=strlen(entry->d_name);
				len = MAX(len,l);
				entry_count ++;
				buf = (char**)realloc(buf,entry_count*sizeof(char *));
				buf[entry_count-1]=(char*)malloc(l+1);
				strcpy(buf[entry_count-1],entry->d_name);
			}
		}

		closedir(dir);

		shellsort(buf, entry_count);
	}

	*files = buf;
	*files_count = entry_count;

	return len;
}


/* execute
 * Call an external program, return 0, if there was no error.
 * No need to support this on multitasking systems.
 */
int execute (char *dir, char *progname)
{
	return 0;
}

/* shrink
 * allows shrinking of memory for single task systems.
 * simply return 1 if you do not support this or set NOSHRINK in funcs.c
 */
int shrink (size_t size)
{
	return 1;
}

/* myclock
 * define a timer in seconds.
 */
double myclock (void)
{
#ifdef RS6000
	struct timestruc_t t;
	gettimer(TIMEOFDAY,&t);
	return (t.tv_sec+t.tv_nsec/1000000000.0);
#else
	return ((double)(times(NULL)))/CLK_TCK;
#endif
}

/*	sys_wait
 *	Wait for time seconds or until a key press.
 *	Return the scan code or 0 (time exceeded).
 */
void sys_wait (double delay, int *scan)
{
	double now;
	GtkTerm *t = GTK_TERM(term);
	now=myclock();
	*scan=0;
	t->scan = t->code = 0;
	while (myclock()<now+delay)
	{
		while (gtk_events_pending())
		{
			gtk_main_iteration();
			if (t->scan) {
				*scan = notekey = t->scan;
				t->scan = t->code = 0;
			} else if (t->code) {
				*scan = notekey = t->code;
				t->scan = t->code = 0;
			}
		}
		if (*scan==switch_screen)
		{	if (in_text)
			{	graphic_mode();
				wait_key(scan);
				text_mode();
			}
			else
			{	text_mode();
				wait_key(scan);
				graphic_mode();
			}
			*scan=0;
		}
		if (*scan) return;
		usleep(1);
	}
	*scan=0;
}


/*  stack_init
 *	get memory for stack.
 */
static int stack_init (long size)
{
	ramstart = (char *)malloc(size*1024l);
	if (!ramstart) return 0; /* fail */
	ramend = ramstart+size*1024l;
	return 1; /* success */
}

static void save_rc()
{
	if (prefs.saveatexit) eulerrc_save(&prefs);
}

static void euler_exit()
{
	save_rc();
	exit(0);
}

/*-----------------------------------------------------------------------
 *	GTK things
 *----------------------------------------------------------------------*/
static void destroy_yesno_dialog(GtkWidget *widget)
{
	if (widget) {
		GtkWidget *dialog = (GtkWidget*) g_object_get_data(G_OBJECT(widget), "dialog");
		gtk_widget_destroy(dialog);
	}
}

static char* get_filename(gpointer data, char *prefix)
{
	char *filename; 
	char *newname;
	GtkWidget *filechooser = (GtkWidget*)data;
		
	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
	
	if (!g_str_has_suffix(filename, prefix))
		newname = g_strdup_printf("%s%s", filename, prefix);
	else
		newname = g_strdup(filename);
	
	return newname;
}

static void infobox(GtkWidget *parent, char *text)
{
	GtkWidget *	dialog;
	GtkWidget *	hbox;
	GtkWidget * icon;
	GtkWidget * label;
	GtkWidget *	button;

	dialog = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(dialog),"Information");
	gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent));
	gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
	gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE);
	g_signal_connect(G_OBJECT(dialog),"delete_event",G_CALLBACK(gtk_widget_destroy),NULL);
	g_signal_connect(G_OBJECT(dialog),"destroy",G_CALLBACK(gtk_widget_destroy),NULL);
	gtk_widget_realize(dialog);

	hbox = gtk_hbox_new(FALSE,10);
	gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),hbox,TRUE,TRUE,0);

	icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
	gtk_box_pack_start(GTK_BOX(hbox),icon,FALSE,FALSE,0);

	label = gtk_label_new(text);
	gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_LEFT);
	gtk_box_pack_start(GTK_BOX(hbox),label,TRUE,TRUE,0);

	/*
	 *	setup the buttons (Yes, No and Cancel)
	 */ 
	button = gtk_button_new_from_stock(GTK_STOCK_OK);
	GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT);
	g_signal_connect_swapped(G_OBJECT(button),"clicked",G_CALLBACK(gtk_widget_destroy),GTK_OBJECT(dialog));
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),button,TRUE,TRUE,0);
	gtk_widget_grab_default(button);

	gtk_widget_show_all(dialog);
}

static void yesnoquestion(char *text, GCallback yes_cb, GCallback no_cb, GtkWidget *window)
{
	GtkWidget *	dialog;
	GtkWidget *	hbox;
	GtkWidget * icon;
	GtkWidget * label;
	GtkWidget *	button;

	dialog = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(dialog),"Question");
	gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(window));
	gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
	gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER_ON_PARENT);
	g_signal_connect(G_OBJECT(dialog),"delete_event",
					 G_CALLBACK(gtk_widget_destroy),NULL);
	g_signal_connect(G_OBJECT(dialog),"destroy",
					 G_CALLBACK(gtk_widget_destroy),NULL);
	gtk_widget_realize(dialog);

	hbox = gtk_hbox_new(FALSE,10);
	gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),hbox,TRUE,TRUE,0);

	icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
	gtk_box_pack_start(GTK_BOX(hbox),icon,FALSE,FALSE,0);

	label = gtk_label_new(text);
	gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_LEFT);
	gtk_box_pack_start(GTK_BOX(hbox),label,TRUE,TRUE,0);

	/*
	 *	setup the buttons (Yes, No and Cancel)
	 */
	button = gtk_button_new_from_stock(GTK_STOCK_YES);
	GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT);
	g_object_set_data(G_OBJECT(button), "dialog", (gpointer) dialog);
	if (GTK_IS_FILE_CHOOSER(window)) 
		g_signal_connect(G_OBJECT(button),"clicked",
					 G_CALLBACK(yes_cb),window);
	else
		g_signal_connect(G_OBJECT(button),"clicked",
					 G_CALLBACK(yes_cb),NULL);
	
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),button,TRUE,TRUE,0);
	gtk_widget_grab_default(button);

	button = gtk_button_new_from_stock(GTK_STOCK_NO);
	GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT);
	g_object_set_data(G_OBJECT(button), "dialog", (gpointer) dialog);
	if (no_cb)
		g_signal_connect(G_OBJECT(button),"clicked",
						 G_CALLBACK(no_cb),NULL);
	else
		g_signal_connect_swapped(G_OBJECT(button),"clicked",
							 G_CALLBACK(gtk_widget_destroy),dialog);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),button,TRUE,TRUE,0);

	button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
	GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT);
	if (GTK_IS_FILE_CHOOSER(window)) 
		g_signal_connect_swapped(G_OBJECT(button),"clicked",
							  	 G_CALLBACK(gtk_widget_destroy),window);
	g_signal_connect_swapped(G_OBJECT(button),"clicked",
							  G_CALLBACK(gtk_widget_destroy),GTK_OBJECT(dialog));
	
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),button,TRUE,TRUE,0);

	gtk_widget_show_all(dialog);
}

/*-----------------------------------------------------------------------
 *	About box
 *----------------------------------------------------------------------*/
void activate_url(GtkAboutDialog *about, const gchar *url, gpointer data)
{
	char *browser;
	browser = g_strconcat(prefs.browser," ",url," &", NULL);
	system(browser);
	g_free(browser);
}

static void about()
{
	GtkWidget *aboutdialog;
	GdkPixbuf *buff;
	const gchar *authors[] = {"Rene Grothmann <grothm@ku-eichstaett.de>",
							  "Eric Boucharé <bouchare.eric@wanadoo.fr>",
							  "Puji Prasetiyo <aripujiprasetiyo@yahoo.com>",
							  NULL
							  };
							  
	aboutdialog = gtk_about_dialog_new ();
	buff = gdk_pixbuf_new_from_xpm_data((const char**) logo_xpm);
	gtk_about_dialog_set_logo (GTK_ABOUT_DIALOG (aboutdialog), buff);  	
	gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (aboutdialog), VERSION);
	gtk_about_dialog_set_name (GTK_ABOUT_DIALOG (aboutdialog), "Euler");

	gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG (aboutdialog), 
	"Copyright © 2001,2005 Rene Grothmann, Eric Boucharé, Puji Prasetiyo");

	gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (aboutdialog), 
	"EULER is a program for quickly and interactively\n"
	"computing with real and complex numbers and\n"
	"matrices, or with intervals. It can draw your\n"
	"functions in two and three dimensions");

	gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG (aboutdialog), authors);
    gtk_about_dialog_set_url_hook (activate_url, NULL, NULL);
	gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (aboutdialog), "http://euler.sourceforge.net");    
	gtk_about_dialog_set_license(GTK_ABOUT_DIALOG (aboutdialog),
    "This program is free software; you can redistribute it and/or\n"
    "modify it under the terms of the GNU Library General Public License as\n"
    "published by the Free Software Foundation; either version 2 of the\n"
    "License, or (at your option) any later version.\n"
    "\n"
    "This library is distributed in the hope that it will be useful,\n"
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
    "Library General Public License for more details.\n"
    "\n"
    "You should have received a copy of the GNU Library General Public\n"
    "License along with the Gnome Library; see the file COPYING.LIB.  If not,\n"
    "write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n"
    "Boston, MA 02111-1307, USA.\n");
   	
	gtk_dialog_run (GTK_DIALOG(aboutdialog));
	gtk_widget_destroy (aboutdialog);
}

/*---------------------------------------------------------------------------
 *	preference box
 *---------------------------------------------------------------------------*/
static GtkWidget * estack;
static GtkWidget * gstack;
static GtkWidget * glines;
static GtkWidget * browser;
static GtkWidget * color_button[16];
static GtkWidget * atexitbut;
static GtkWidget * fontbutton;
static GtkWidget * editordialog;
static GtkWidget * etextview;

static short def_colors[3][MAX_COLORS] = {
	{255,0,100,0  ,0  ,0  ,100,150,100,50,220 ,80  ,80  ,80  ,140,190},
	{255,0,0  ,100,0  ,100,100,150,100,50,80  ,220 ,80  ,140 ,140,190},
	{255,0,0  ,0  ,100,100,  0,150,100,50,80  ,80  ,220 ,140 , 80,190}
};

static void prefs_apply_cb(GtkWidget *widget, gpointer data)
{
	int val, i, j, changed = 0;
	const gchar *pfont = NULL;
	
	val = atol(gtk_entry_get_text(GTK_ENTRY(estack)));
	if (val) prefs.estack = val;
	
	val = atol(gtk_entry_get_text(GTK_ENTRY(gstack)));
	if (val) prefs.gstack = val;

	val = atol(gtk_entry_get_text(GTK_ENTRY(glines)));

	pfont = gtk_font_button_get_font_name(GTK_FONT_BUTTON(fontbutton));
	if (pfont) {
		strcpy(prefs.pfont,pfont);
		if (editordialog)
			gtk_widget_modify_font(etextview, pango_font_description_from_string(pfont));
	}
	
	if (val) {
		prefs.glines = val;
		setmetalines(prefs.glines);
		gtk_meta_update_lines(meta);
	}

	for (i=0 ; i<16 ; i++) {
		GdkColor gcolor;
		short color[3];
		gtk_color_button_get_color(GTK_COLOR_BUTTON(color_button[i]),&gcolor); 
		color[0] = gcolor.red>>8;
		color[1] = gcolor.green>>8;
		color[2] = gcolor.blue>>8;
				
		for (j=0 ; j<3 ; j++)
			if (prefs.colors[j][i]!=color[j]) {
				prefs.colors[j][i]=color[j];
				changed =1;
			}
	}

	if (changed) {
		setmetacolors(prefs.colors);
		gtk_meta_update_colors(meta);
	}
	
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(atexitbut)))
		prefs.saveatexit = 1;
	else
		prefs.saveatexit = 0;
	
	if (strlen(gtk_entry_get_text(GTK_ENTRY(browser))))
		strcpy(prefs.browser,gtk_entry_get_text(GTK_ENTRY(browser)));
}

static void prefs_ok_cb(GtkWidget *widget, gpointer data)
{
	prefs_apply_cb(widget,data);
}

static void prefs_reset_cb(GtkWidget *widget, gpointer data)
{
	GdkColor color;
	char *s;
	int i;

	gtk_entry_set_text(GTK_ENTRY(browser),E_BROWSER_DEFAULT);
	
	s = g_strdup_printf("%ld",E_ESTACK_DEFAULT);
	gtk_entry_set_text(GTK_ENTRY(estack),s);
	g_free(s);
	s = g_strdup_printf("%ld",E_GSTACK_DEFAULT);
	gtk_entry_set_text(GTK_ENTRY(gstack),s);
	g_free(s);
	s = g_strdup_printf("%d",E_GLINES_DEFAULT);
	gtk_entry_set_text(GTK_ENTRY(glines),s);
	g_free(s);
	s = g_strdup_printf("%s",E_PFONT_DEFAULT);
	gtk_font_button_set_font_name(GTK_FONT_BUTTON(fontbutton),s);
	g_free(s);
	
	for (i=0 ; i<16 ; i++) {
		color.red   = ((gushort)def_colors[0][i])<<8;
		color.green = ((gushort)def_colors[1][i])<<8;
		color.blue  = ((gushort)def_colors[2][i])<<8;
		gtk_color_button_set_color(GTK_COLOR_BUTTON(color_button[i]), &color);
	}
	
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(atexitbut),TRUE);
}

static void prefs_box()
{
	GtkWidget *dialog;
	GtkWidget *notebook;
	GtkWidget *frame;
	GtkWidget *label;
	GtkWidget *alignment;
	GtkWidget *table;
	GtkWidget *hbox;
	GtkWidget *pref_vbox;
	GtkWidget *vbox;
	GtkWidget *button;

	int i;
	char *s;

	dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	g_signal_connect (G_OBJECT(dialog),"delete_event",
					  G_CALLBACK(gtk_widget_destroy),NULL);
	g_signal_connect (G_OBJECT(dialog),"destroy",
					  G_CALLBACK(gtk_widget_destroy),NULL);
	gtk_window_set_title (GTK_WINDOW (dialog), "Preferences ...");
	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
	gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
	gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);

	pref_vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (dialog), pref_vbox);
	gtk_container_set_border_width (GTK_CONTAINER (pref_vbox), 5);

	notebook = gtk_notebook_new ();
	gtk_box_pack_start (GTK_BOX (pref_vbox), notebook, TRUE, TRUE, 0);

	frame = gtk_frame_new (NULL);
	gtk_container_add (GTK_CONTAINER (notebook), frame);
	gtk_container_set_border_width (GTK_CONTAINER (frame), 5);

	alignment = gtk_alignment_new (0, 0, 0, 0);
	gtk_container_add (GTK_CONTAINER (frame), alignment);

	vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (alignment), vbox);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);

	label = gtk_label_new ("You can setup the memory size of the stack used for calculation. This will be effective on Euler restart.");
	gtk_label_set_line_wrap(GTK_LABEL(label),TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

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

	label = gtk_label_new ("Stack memory (KB) :");
	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

	label = gtk_label_new (NULL);
	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);

	estack = gtk_entry_new ();
	gtk_box_pack_start (GTK_BOX (hbox), estack, FALSE, TRUE, 0);
	s = g_strdup_printf("%d",prefs.estack);
	gtk_entry_set_text(GTK_ENTRY(estack),s);
	g_free(s);

	label = gtk_label_new ("You can setup the memory size of the buffer used for graphics. This will be effective on Euler][ restart.");
	gtk_label_set_line_wrap(GTK_LABEL(label),TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

	hbox = gtk_hbox_new (FALSE, 5);
	gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);

	label = gtk_label_new ("Graphics buffer (KB) :");
	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

	label = gtk_label_new (NULL);
	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);

	gstack = gtk_entry_new ();
	gtk_box_pack_start (GTK_BOX (hbox), gstack, FALSE, TRUE, 0);
	s = g_strdup_printf("%d",prefs.gstack);
	gtk_entry_set_text(GTK_ENTRY(gstack),s);
	g_free(s);

	label = gtk_label_new ("Memory");
	gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 0), label);

	frame = gtk_frame_new (NULL);
	gtk_container_add (GTK_CONTAINER (notebook), frame);
	gtk_container_set_border_width (GTK_CONTAINER (frame), 5);

	alignment = gtk_alignment_new (0, 0, 1, 1);
	gtk_container_add (GTK_CONTAINER (frame), alignment);

	vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (alignment), vbox);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);

	label = gtk_label_new ("You can setup the height of the graphics window in lines of text. This will not change the effective height of the graphics window, but the font size.");
	gtk_label_set_line_wrap(GTK_LABEL(label),TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

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

	label = gtk_label_new ("Number of lines :");
	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

	label = gtk_label_new (NULL);
	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);

	glines = gtk_entry_new ();
	gtk_box_pack_start (GTK_BOX (hbox), glines, FALSE, FALSE, 0);
	s = g_strdup_printf("%d",prefs.glines);
	gtk_entry_set_text(GTK_ENTRY(glines),s);
	g_free(s);
	
	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
	
	label = gtk_label_new("Editor font:  ");
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
	
	fontbutton = gtk_font_button_new ();
  	gtk_font_button_set_use_font (GTK_FONT_BUTTON (fontbutton), TRUE);
	gtk_font_button_set_font_name(GTK_FONT_BUTTON (fontbutton), prefs.pfont);
	gtk_box_pack_start (GTK_BOX (hbox), fontbutton, TRUE, TRUE, 0);
  	
  
	label = gtk_label_new ("Font");
	gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1), label);

	frame = gtk_frame_new (NULL);
	gtk_container_add (GTK_CONTAINER (notebook), frame);
	gtk_container_set_border_width (GTK_CONTAINER (frame), 5);

	alignment = gtk_alignment_new (0, 0, 1, 1);
	gtk_container_add (GTK_CONTAINER (frame), alignment);
	gtk_container_set_border_width (GTK_CONTAINER (alignment), 5);

	vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (alignment), vbox);

	label = gtk_label_new ("You can setup the colors used for the graphics. The change will be set when you click on Apply or OK.");
	gtk_label_set_line_wrap(GTK_LABEL(label),TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);

	table = gtk_table_new (8, 4, TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);

	for (i=0 ; i<8 ; i++) {
		GdkColor color;
		s = g_strdup_printf("%d",i);
		label = gtk_label_new(s);
		gtk_widget_show (label);
		gtk_table_attach(GTK_TABLE(table),label,0,1,i,i+1,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,2);
		color_button[i] = gtk_color_button_new();
		color.red   = ((gushort)prefs.colors[0][i])<<8; 
		color.green = ((gushort)prefs.colors[1][i])<<8;
		color.blue  = ((gushort)prefs.colors[2][i])<<8;	  
		gtk_color_button_set_color(GTK_COLOR_BUTTON(color_button[i]), &color);  
		gtk_widget_show (color_button[i]);
		gtk_table_attach(GTK_TABLE(table),color_button[i],1,2,i,i+1,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,2);
		g_free(s);

		s = g_strdup_printf("%d",i+8);
		color_button[i+8] = gtk_color_button_new();
		color.red   = ((gushort)prefs.colors[0][i+8])<<8; 
		color.green = ((gushort)prefs.colors[1][i+8])<<8;
		color.blue  = ((gushort)prefs.colors[2][i+8])<<8;	  
		gtk_color_button_set_color(GTK_COLOR_BUTTON(color_button[i+8]), &color);  
		gtk_widget_show (color_button[i+8]);
		gtk_table_attach(GTK_TABLE(table),color_button[i+8],2,3,i,i+1,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,2);
		label = gtk_label_new(s);
		gtk_widget_show (label);
		gtk_table_attach(GTK_TABLE(table),label,3,4,i,i+1,GTK_FILL|GTK_EXPAND,GTK_FILL|GTK_EXPAND,2,2);
		g_free(s);
	}
	
	label = gtk_label_new ("Colors");
	gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 2), label);

	frame = gtk_frame_new (NULL);
	gtk_container_add (GTK_CONTAINER (notebook), frame);
	gtk_container_set_border_width (GTK_CONTAINER (frame), 5);

	alignment = gtk_alignment_new (0, 0, 1, 1);
	gtk_container_add (GTK_CONTAINER (frame), alignment);
	gtk_container_set_border_width (GTK_CONTAINER (alignment), 5);

	vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (alignment), vbox);

	atexitbut = gtk_check_button_new_with_mnemonic ("Save preferences at Euler exit");
	gtk_box_pack_start (GTK_BOX (vbox), atexitbut, FALSE, FALSE, 0);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (atexitbut), TRUE);

	label = gtk_label_new ("Browser used to view documentation");
	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);

	browser = gtk_entry_new ();
	gtk_box_pack_start (GTK_BOX (vbox), browser, FALSE, FALSE, 0);
	s = g_strdup_printf("%s",prefs.browser);
	gtk_entry_set_text(GTK_ENTRY(browser),s);
	g_free(s);

	label = gtk_label_new ("Misc");
	gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 3), label);

	hbox = gtk_hbox_new (FALSE, 3);
	gtk_box_pack_start (GTK_BOX (pref_vbox), hbox, FALSE, TRUE, 0);

	button = gtk_button_new_with_mnemonic ("Default");
	g_signal_connect (G_OBJECT(button), "clicked",
					  G_CALLBACK (prefs_reset_cb),NULL);
	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);

	label = gtk_label_new (NULL);
	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);

	button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
	g_signal_connect_swapped (G_OBJECT(button), "clicked",
							  G_CALLBACK (gtk_widget_destroy),G_OBJECT(dialog));
	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);

	button = gtk_button_new_with_mnemonic ("Apply");
	g_signal_connect (G_OBJECT(button), "clicked",
					  G_CALLBACK (prefs_apply_cb),NULL);
	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

	button = gtk_button_new_from_stock (GTK_STOCK_OK);
	g_signal_connect (G_OBJECT(button), "clicked",
					  G_CALLBACK (prefs_ok_cb),NULL);
	g_signal_connect_swapped (G_OBJECT(button), "clicked",
					  G_CALLBACK (gtk_widget_destroy),G_OBJECT(dialog));
	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);

	gtk_widget_show_all(dialog);
}


static void edit_comment_cb(GtkWidget *widget, gpointer data)
{
	char *text;
	GtkTextIter start, end;
	GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(data));
	
	if (buffer) {
		gtk_text_buffer_get_bounds(buffer, &start, &end);
		text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
		if (g_utf8_validate(text, -1, NULL))
			text = g_locale_from_utf8(text, -1, NULL, NULL, NULL);
		gtk_term_set_comment(term,text);
	}
}

/*---------------------------------------------------------------------------
 *	comment editor
 *---------------------------------------------------------------------------*/
static void editcomment(char *text)
{
	GtkWidget *dialog;
	GtkWidget *vbox;
	GtkWidget *scrolledwindow;
	GtkWidget *textview;
	GtkWidget *hbuttonbox;
	GtkWidget *button;
	GtkTextBuffer *buffer;
	
	dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	g_signal_connect(G_OBJECT(dialog),"delete_event",
					 G_CALLBACK(gtk_widget_destroy),NULL);
	g_signal_connect(G_OBJECT(dialog),"destroy",
					 G_CALLBACK(gtk_widget_destroy),NULL);
	gtk_window_set_title (GTK_WINDOW (dialog), "Edit comment");
	gtk_window_set_default_size (GTK_WINDOW (dialog), 350, 250);
	gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
	gtk_window_set_modal(GTK_WINDOW(dialog),TRUE);
	
	vbox = gtk_vbox_new (FALSE, 5);
	gtk_container_add (GTK_CONTAINER (dialog), vbox);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);

	scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
	gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);

	buffer = gtk_text_buffer_new(NULL);
	if (text != NULL) gtk_text_buffer_set_text(buffer, text, -1);
	textview = gtk_text_view_new_with_buffer (buffer);
	gtk_container_add (GTK_CONTAINER (scrolledwindow), textview);

	hbuttonbox = gtk_hbutton_box_new ();
	gtk_widget_show (hbuttonbox);
	gtk_box_pack_start (GTK_BOX (vbox), hbuttonbox, FALSE, FALSE, 0);

	button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
	g_signal_connect_swapped(G_OBJECT(button),"clicked",
							 G_CALLBACK(gtk_widget_destroy),GTK_OBJECT(dialog));
	gtk_container_add (GTK_CONTAINER (hbuttonbox), button);

	button = gtk_button_new_from_stock (GTK_STOCK_OK);
	g_signal_connect(G_OBJECT(button), "clicked",
					 G_CALLBACK(edit_comment_cb),GTK_OBJECT(textview));
	g_signal_connect_swapped(G_OBJECT(button),"clicked",
							 G_CALLBACK(gtk_widget_destroy),GTK_OBJECT(dialog));
	gtk_container_add (GTK_CONTAINER (hbuttonbox), button);

	gtk_widget_show_all(dialog);
}


/*---------------------------------------------------------------------------
 *	menu
 *---------------------------------------------------------------------------*/
void file_select (GtkFileChooserAction action, 
						 gchar *title, 
						 gchar *pattern,
						 gchar *filename,
						 GCallback func, 
						 GtkWidget *window)
{
    GtkWidget     *dialog;
  	GtkWidget     *dialog_vbox;
  	GtkWidget     *dialog_action_area;
  	GtkWidget     *button;
  	GtkFileFilter *filter; 
	
  	dialog = gtk_file_chooser_dialog_new (title, GTK_WINDOW(window), action, NULL);
  	g_signal_connect(G_OBJECT(dialog), "delete_event", 
					 G_CALLBACK(gtk_widget_destroy), NULL);
  	g_signal_connect(G_OBJECT(dialog), "destroy", 
					 G_CALLBACK(gtk_widget_destroy), NULL);
	gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);

	filter = gtk_file_filter_new ();
	gtk_file_filter_add_pattern(filter, pattern);
	gtk_file_filter_set_name(filter, pattern);
	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
	
	if (filename && action == GTK_FILE_CHOOSER_ACTION_SAVE)
		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), g_path_get_basename(filename));
	
	dialog_vbox = GTK_DIALOG (dialog)->vbox;

  	dialog_action_area = GTK_DIALOG (dialog)->action_area;
  	gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area), GTK_BUTTONBOX_END);

  	button = gtk_button_new_from_stock ("gtk-cancel");
  	g_signal_connect_swapped(G_OBJECT(button), "clicked",
					 		 G_CALLBACK(gtk_widget_destroy), dialog);
	gtk_box_pack_start(GTK_BOX(dialog_action_area), button, FALSE, FALSE, 0);
  	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

  	if (action == GTK_FILE_CHOOSER_ACTION_OPEN) 
		button = gtk_button_new_from_stock ("gtk-open");
	else if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
		button = gtk_button_new_from_stock ("gtk-save");
	else
		return;
		
	g_signal_connect(G_OBJECT(button),"clicked",
					 G_CALLBACK(func),dialog);
	
	gtk_box_pack_start(GTK_BOX(dialog_action_area), button, FALSE, FALSE, 0);
  	GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

  	gtk_widget_grab_default (button);
  	gtk_widget_show_all(dialog);
  	gtk_dialog_run (GTK_DIALOG (dialog));
}

static void open_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");
		
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
		
	if (!gtk_term_load(term,filename)) {
		char *txt = g_strdup_printf("\nI could not open the file\n%s !\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
	} else {
		gtk_widget_destroy((GtkWidget*)data);		
		is_demo = 0;
	}
	chdir(g_dirname(filename));
}

static void postscript_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".eps");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
	if (!dump_postscript(filename)) {
		char *txt = g_strdup_printf("\nI could not save the postscript graphics to the file\n%s\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
	}
	else
		gtk_widget_destroy((GtkWidget*)data);
}

static void overwrite_yes_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");

	destroy_yesno_dialog(widget);
	
	if (!gtk_term_save(term,filename)) {
		char *txt = g_strdup_printf("\nI could not save the notebook to the file\n%s\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
	}
	else
		gtk_widget_destroy((GtkWidget*)data);
	
	chdir(g_dirname(filename));
}

static void save_as_cb(GtkWidget *widget, gpointer data)
{
    struct stat statbuf;
  	
	char *filename = get_filename(data, ".en");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
    if (stat(filename, &statbuf) == 0)   /* file exists */
        yesnoquestion("Destination file exists, overwrite it?", 
					  G_CALLBACK(overwrite_yes_cb), NULL, (GtkWidget*)data);
	else
		overwrite_yes_cb(NULL, data);
}

static void overwrite_yes_open_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");
	
	destroy_yesno_dialog(widget);
		
	if (gtk_term_save(term,filename)) {
		gtk_widget_destroy((GtkWidget*)data);
		chdir(g_dirname(filename));
		file_select(GTK_FILE_CHOOSER_ACTION_OPEN,
					"Open A Notebook ...", "*.en", NULL,
					G_CALLBACK(open_cb), term_window);
	} else {
		char *txt = g_strdup_printf("\nI could not save the notebook to the file\n%s\nThe notebook open command is aborted ...\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
		chdir(g_dirname(filename));
	}
}

static void save_as_open_cb(GtkWidget *widget, gpointer data)
{
	struct stat statbuf;
		
	char *filename = get_filename(data, ".en");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
	if (stat(filename, &statbuf) == 0)   /* file exists */
        yesnoquestion("Destination file exists, overwrite it?", 
					  G_CALLBACK(overwrite_yes_open_cb), NULL, (GtkWidget*)data);
	else
		overwrite_yes_open_cb(NULL, data);
}

static void save_as_new_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
	if (gtk_term_save(term,filename)) {
		gtk_widget_destroy((GtkWidget*)data);
		gtk_term_clear_new(term);
		is_demo = 0;
	} else {
		char *txt = g_strdup_printf("\nI could not save the notebook to the file\n%s\nThe new notebook command is aborted ...\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
	}

	chdir(g_dirname(filename));
}

static void overwrite_yes_quit_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");
	
	destroy_yesno_dialog(widget);
		
	if (gtk_term_save(term,filename)){
		gtk_widget_destroy((GtkWidget*)data);
		euler_exit();
	}
	else {
		char *txt = g_strdup_printf("\nI could not save the notebook to the file\n%s\nThe quit notebook command is aborted ...\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
	}
	chdir(g_dirname(filename));
}

static void save_as_quit_cb(GtkWidget *widget, gpointer data)
{
	struct stat statbuf;
		
	char *filename = get_filename(data, ".en");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
	if (stat(filename, &statbuf) == 0)   /* file exists */
        yesnoquestion("Destination file exists, overwrite it?", 
					  G_CALLBACK(overwrite_yes_quit_cb), NULL, (GtkWidget*)data);
	else
		overwrite_yes_quit_cb(NULL, data);
}

static void yes_quit_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	
	if (!gtk_term_is_named(term))
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
					"Save Notebook As ...", "*.en", "untitled",
					G_CALLBACK(save_as_quit_cb), term_window);
	else {
		gtk_term_save(term,NULL);
		euler_exit();
	}
}

static void no_quit_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	euler_exit();
}

static void quit_cb(GObject *widget, gpointer data)
{
	if (!is_demo && gtk_term_is_changed(term))
		yesnoquestion("\nThe current notebook has been modified ...\nDo you want to save it?\n",
					  G_CALLBACK(yes_quit_cb),G_CALLBACK(no_quit_cb),term_window);
	else
		euler_exit();
}

/*---------------------------------------------------------------------------
 *	toolbar callbacks
 *---------------------------------------------------------------------------*/
static void yes_new_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	
	if (!gtk_term_is_named(term))
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
					"Save Notebook As ...", "*.en", "untitled",
					G_CALLBACK(save_as_new_cb), term_window);
	else {
		gtk_term_save(term,NULL);
		gtk_term_clear_new(term);
		is_demo = 0;
	}
}

static void no_new_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	gtk_term_clear_new(term);
	is_demo = 0;
}

static void yes_open_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	
	if (!gtk_term_is_named(term))
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
					"Save Notebook As ...", "*.en", "untitled",
					G_CALLBACK(save_as_open_cb), term_window);
	else {
		gtk_term_save(term,NULL);
		file_select(GTK_FILE_CHOOSER_ACTION_OPEN,
					"Open A Notebook ...", "*.en", NULL,
					G_CALLBACK(open_cb), term_window);
	}
}

static void no_open_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	
	file_select(GTK_FILE_CHOOSER_ACTION_OPEN,
				"Open A Notebook ...", "*.en", NULL,
				G_CALLBACK(open_cb), term_window);
}

/*-------------------------------------------------------------
 *	internal editor
 *------------------------------------------------------------*/
static GtkWidget * estatusbar;
static GtkWidget * entry;
static GtkWidget * loadbutton;
static GtkWidget * savebutton;
static GtkTextBuffer * etextbuffer;
static gint row, col;
static gchar *efname = "";
static gboolean echanged = FALSE;

static void buffer_changed(void)
{
	char *title = g_strdup_printf("Editor [*%s]",efname);
	gtk_window_set_title(GTK_WINDOW(editordialog), title);
	g_free(title);
	echanged = TRUE;
	gtk_widget_set_sensitive(loadbutton, FALSE);
	gtk_widget_set_sensitive(savebutton, TRUE);
}

static void buffer_saved(void)
{
	char *title = g_strdup_printf("Editor [%s]",efname);
	gtk_window_set_title(GTK_WINDOW(editordialog), title);
	g_free(title);
	echanged = FALSE;
	gtk_widget_set_sensitive(loadbutton, strlen(efname));
	gtk_widget_set_sensitive(savebutton, FALSE);
}

static void clear_editor(void)
{
	efname = "";
	gtk_text_buffer_set_text(etextbuffer, "", -1);
	gtk_widget_grab_focus (etextview);
	buffer_saved();
}

static void get_row_col(GtkTextIter iter, int *row, int *col)
{
	GtkTextIter start;
	guint tab_size = 8;
	
	*row = gtk_text_iter_get_line(&iter);
	
	start = iter;
	gtk_text_iter_set_line_offset(&start, 0);
	*col = 0;

	while (!gtk_text_iter_equal(&start, &iter))
	{
		if (gtk_text_iter_get_char (&start) == '\t')
					
			(*col) += (tab_size - (*col  % tab_size));
		else
			++(*col);

		gtk_text_iter_forward_char(&start);
	}
}

static void find(GtkTextView *text_view, const gchar *text, GtkTextIter *iter)
{
	GtkTextIter mstart, mend;
  	GtkTextMark *last_pos;

  	if(gtk_text_iter_forward_search(iter, text, 0, &mstart, &mend, NULL))
    {
      	gtk_text_buffer_select_range(etextbuffer, &mstart, &mend);
      	last_pos = gtk_text_buffer_create_mark(etextbuffer, "last_pos", 
                                               &mend, FALSE);
      	gtk_text_view_scroll_to_mark(text_view, last_pos, 0, TRUE, 0.5, 0.5);
    }
}

static void save_file(GtkWidget *parent, char *filename)
{
	FILE *file;
	char *text;
	GtkTextIter start, end;

	file = fopen(filename,"w");
	
	if (!file) {
		char *txt = g_strdup_printf("\nCould not save the file\n%s !\n",filename);
		infobox(parent, txt);
		g_free(txt);
		return;
	}
	else {
		gtk_text_buffer_get_bounds(etextbuffer, &start, &end);	
		text = gtk_text_buffer_get_text(etextbuffer, &start, &end, TRUE);
	
		if (g_utf8_validate(text, -1, NULL))
			text = g_locale_from_utf8(text, -1, NULL, NULL, NULL);
				
		fprintf(file,text); 
		fclose(file);
		efname = filename;
		buffer_saved();
		if (GTK_IS_FILE_CHOOSER(parent)) gtk_widget_destroy(parent);
	}
	chdir(g_dirname(filename));
}

static void cursor_moved(void)
{
	char *msg;
	GtkTextIter iter;
	int r, c;
	
	gtk_text_buffer_get_iter_at_mark (etextbuffer, &iter,
					  		 		  gtk_text_buffer_get_insert(etextbuffer));
	
	get_row_col(iter, &r, &c);
	row = r; col = c;
	msg = g_strdup_printf("Ln:%d Col:%d", row + 1, col + 1);
	
	gtk_statusbar_push(GTK_STATUSBAR (estatusbar), 0, msg);

   	g_free (msg);
}

static void overwrite_yes_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	save_file((GtkWidget*)data, get_filename(data, ".e"));
}

static void save_as_e_cb(GtkWidget *widget, gpointer data)
{
    struct stat statbuf;
	
	char *filename = get_filename(data, ".e");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
    if (stat(filename, &statbuf) == 0)   /* file exists */
        yesnoquestion("Destination file exists, overwrite it?",
					  G_CALLBACK(overwrite_yes_e_cb), 
					  NULL, (GtkWidget*)data);
	else 
		save_file((GtkWidget*)data, filename);
}

static void open_file(GtkWidget *parent, char *filename)
{
	FILE *file;
	char line[1024];
	char *text;
	int length;
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;

	if (!(file = fopen(filename, "r+")))
	if (!(file = fopen(filename, "r"))) {
		char *txt = g_strdup_printf("\nCould not open the file\n%s !\n",filename);
		infobox(parent, txt);
		g_free(txt);
		return;
	}
	
	if (GTK_IS_FILE_CHOOSER(parent)) gtk_widget_destroy(parent);
	clear_editor();
	efname = filename;
	
	while(!feof(file) && !ferror(file)) {
		length = fread(line, sizeof(char), 1023, file);
		line[length] = '\0';
		if (!g_utf8_validate(line, strlen(line), NULL)) {
			text = g_locale_to_utf8(line, strlen(line), NULL, NULL, NULL);
			gtk_text_buffer_insert_at_cursor(etextbuffer, text, strlen(text));
		}
		else
			gtk_text_buffer_insert_at_cursor(etextbuffer, line, strlen(line));
	}		
	
	fclose(file);
	buffer_saved();
	chdir(g_dirname(filename));
}

static void open_e_cb(GtkWidget *widget, gpointer data)
{
	open_file((GtkWidget*)data, get_filename(data, ".e"));
}

static void save_or_save_as(void)
{
	if (strlen(efname))
		save_file(editordialog, efname);
	else
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
					"Save Module As ...", "*.e", "untitled",
					G_CALLBACK(save_as_e_cb), editordialog);
}

static void yes_new_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	save_or_save_as();
	clear_editor();
}

static void no_new_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	clear_editor();
}

static void yes_open_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	save_or_save_as();	
	file_select(GTK_FILE_CHOOSER_ACTION_OPEN,
				"Open A Module ...", "*.e", NULL,
				G_CALLBACK(open_e_cb), editordialog);
}

static void no_open_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	file_select(GTK_FILE_CHOOSER_ACTION_OPEN,
				"Open module ...", "*.e", NULL,
				G_CALLBACK(open_e_cb), editordialog);
}

static void yes_quit_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	save_or_save_as();
	gtk_widget_destroy(editordialog);
	editordialog = NULL;
}

static void no_quit_e_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	gtk_widget_destroy(editordialog);
	editordialog = NULL;
}

static void yes_quit_e_quit_cb(GtkWidget *widget, gpointer data)
{
	yes_quit_e_cb(widget, NULL);
	quit_cb(NULL, NULL);
}

static void no_quit_e_quit_cb(GtkWidget *widget, gpointer data)
{
	no_quit_e_cb(widget, NULL);
	quit_cb(NULL, NULL);
}

static void editor_delete_cb(GtkWidget *widget, gpointer data)
{
	if (echanged)
		if (widget)
			yesnoquestion("\nThe current module has been modified ...\nDo you want to save it?\n",
					  	  G_CALLBACK(yes_quit_e_quit_cb),G_CALLBACK(no_quit_e_quit_cb),editordialog);	
		else
			yesnoquestion("\nThe current module has been modified ...\nDo you want to save it?\n",
					  	  G_CALLBACK(yes_quit_e_cb),G_CALLBACK(no_quit_e_cb),editordialog);	
	else {
		gtk_widget_destroy(editordialog);
		editordialog = NULL;
		if (widget) quit_cb(NULL, NULL);
	}
}

static void on_editor_new(GtkWidget *widget, gpointer data)
{
	if (echanged)
		yesnoquestion("\nThe current module has been modified ...\nDo you want to save it?\n",
					  G_CALLBACK(yes_new_e_cb),G_CALLBACK(no_new_e_cb),editordialog);	
	else
		clear_editor();
}

static void on_editor_open(GtkWidget *widget, gpointer data)
{
	if(echanged) 
		yesnoquestion("\nThe current module has been modified ...\nDo you want to save it?\n",
					  G_CALLBACK(yes_open_e_cb),G_CALLBACK(no_open_e_cb),editordialog);
	else
		file_select(GTK_FILE_CHOOSER_ACTION_OPEN,
					"Open A Module ...", "*.e", NULL,
					G_CALLBACK(open_e_cb), editordialog); 
}

static void on_editor_save(GtkWidget *widget, gpointer data)
{
	save_or_save_as();
}

static void on_editor_save_as(GtkWidget *widget, gpointer data)
{
	file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
				"Save Module As ...", "*.e", strlen(efname) ? efname : "untitled",
				G_CALLBACK(save_as_e_cb), editordialog);
}

static void on_editor_find(GtkWidget *widget, gpointer data)
{
	const gchar *text;
  	GtkTextMark *last_pos;
  	GtkTextIter iter;
	int r, c;
	
  	text = gtk_entry_get_text(GTK_ENTRY(entry));
	if (!strlen(text)) return;
		
  	last_pos = gtk_text_buffer_get_mark (etextbuffer, "last_pos");
  	  	
	if (last_pos == NULL)
    	gtk_text_buffer_get_start_iter (etextbuffer, &iter);
	else{
		gtk_text_buffer_get_iter_at_mark (etextbuffer, &iter, last_pos);
		get_row_col(iter, &r, &c);
		if (row != r || col != (c-strlen(text)))
			gtk_text_buffer_get_iter_at_line_offset(etextbuffer, &iter, row, col);
	}
	
	find (GTK_TEXT_VIEW(etextview), text, &iter);
}

static void on_editor_load(GtkWidget *widget, gpointer data)
{
	GtkTerm *t = GTK_TERM(term);
	if (t->editing) {
		char *text = g_strdup_printf("load \"%s\"", efname);
		if (!strlen(efname)) return;
		
		if (t->pos<=e_get_text_length(t->a,t->cur))
			e_remove_text(t->a,t->cur,1,e_get_text_length(t->a,t->cur)-1);
		gtk_term_delete_current_output(term);
		
		output(text);
		gtk_term_redraw(term);
		strcpy(input_line, text);
		forcebreak = TRUE;
		g_free(text);
	}	
}

static void editor(void)
{
	GtkWidget *vbox;
  	GtkWidget *toolbar;
  	GtkWidget *button;
  	GtkWidget *toolitem;
  	GtkWidget *vseparator;
  	GtkWidget *scrolledwindow;
	GtkWidget *image;
	GtkTooltips *tips;
  	PangoFontDescription *pfd;
	GtkIconSize tmp_toolbar_icon_size;
	
  	if (editordialog) {
		gdk_window_raise(editordialog->window);
		return;
	}
	
  	editordialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
//  gtk_window_set_transient_for(GTK_WINDOW(editordialog),GTK_WINDOW(term_window));
//	gtk_window_set_modal(GTK_WINDOW(editordialog),TRUE);
	gtk_window_set_position(GTK_WINDOW(editordialog),GTK_WIN_POS_MOUSE);
	gtk_window_set_title (GTK_WINDOW (editordialog), "Editor []");
  	gtk_window_set_default_size (GTK_WINDOW (editordialog), 500, 400);
	g_signal_connect_swapped(G_OBJECT(editordialog), "delete_event", 
					 G_CALLBACK(editor_delete_cb), NULL);
					 
	vbox = gtk_vbox_new (FALSE, 0);
  	gtk_container_add (GTK_CONTAINER (editordialog), vbox);

  	toolbar = gtk_toolbar_new ();
  	gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
	gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar), TRUE);
    gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE);
	  
  	tips = gtk_tooltips_new();
	tmp_toolbar_icon_size = gtk_toolbar_get_icon_size (GTK_TOOLBAR (toolbar));

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_widget_show (toolitem);
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	button = gtk_button_new ();
  	gtk_tooltips_set_tip(tips, button, "Create new module", NULL);
  	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
	gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_editor_new), NULL);
  	gtk_container_add (GTK_CONTAINER (toolitem), button);

  	image = gtk_image_new_from_stock ("gtk-new", GTK_ICON_SIZE_BUTTON);
  	gtk_container_add (GTK_CONTAINER (button), image);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	button = gtk_button_new ();
  	gtk_tooltips_set_tip(tips, button, "Open module", NULL);
  	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
	gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
  	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_editor_open), NULL);
	gtk_container_add (GTK_CONTAINER (toolitem), button);

  	image = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_BUTTON);
  	gtk_container_add (GTK_CONTAINER (button), image);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	savebutton = gtk_button_new ();
  	gtk_tooltips_set_tip(tips, savebutton, "Save module", NULL);
	gtk_button_set_relief (GTK_BUTTON (savebutton), GTK_RELIEF_NONE);
	gtk_button_set_focus_on_click (GTK_BUTTON (savebutton), FALSE);
  	g_signal_connect(G_OBJECT(savebutton), "clicked", G_CALLBACK(on_editor_save), NULL);
	gtk_container_add (GTK_CONTAINER (toolitem), savebutton);

  	image = gtk_image_new_from_stock ("gtk-save", GTK_ICON_SIZE_BUTTON);
  	gtk_container_add (GTK_CONTAINER (savebutton), image);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	button = gtk_button_new ();
  	gtk_tooltips_set_tip(tips, button, "Save module as ...", NULL);
  	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
	gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_editor_save_as), NULL);
  	gtk_container_add (GTK_CONTAINER (toolitem), button);

  	image = gtk_image_new_from_stock ("gtk-save-as", GTK_ICON_SIZE_BUTTON);
  	gtk_container_add (GTK_CONTAINER (button), image);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);
  	gtk_container_set_border_width (GTK_CONTAINER (toolitem), 5);

  	vseparator = gtk_vseparator_new ();
  	gtk_container_add (GTK_CONTAINER (toolitem), vseparator);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	entry = gtk_entry_new ();
  	gtk_container_add (GTK_CONTAINER (toolitem), entry);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	button = gtk_button_new ();
  	gtk_tooltips_set_tip(tips, button, "Find text", NULL);
  	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_editor_find), NULL);
  	gtk_container_add (GTK_CONTAINER (toolitem), button);

  	image = gtk_image_new_from_stock ("gtk-find", GTK_ICON_SIZE_BUTTON);
  	gtk_container_add (GTK_CONTAINER (button), image);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);
  	gtk_container_set_border_width (GTK_CONTAINER (toolitem), 5);

  	vseparator = gtk_vseparator_new ();
  	gtk_widget_show (vseparator);
  	gtk_container_add (GTK_CONTAINER (toolitem), vseparator);

  	toolitem = (GtkWidget*) gtk_tool_item_new ();
  	gtk_container_add (GTK_CONTAINER (toolbar), toolitem);

  	loadbutton = gtk_button_new ();
  	gtk_tooltips_set_tip(tips, loadbutton, "Load module into euler", NULL);
  	gtk_button_set_relief (GTK_BUTTON (loadbutton), GTK_RELIEF_NONE);
	gtk_button_set_focus_on_click (GTK_BUTTON (loadbutton), FALSE);
	g_signal_connect(G_OBJECT(loadbutton), "clicked", G_CALLBACK(on_editor_load), NULL);
  	gtk_container_add (GTK_CONTAINER (toolitem), loadbutton);

  	image = gtk_image_new_from_stock ("gtk-go-down", GTK_ICON_SIZE_BUTTON);
  	gtk_container_add (GTK_CONTAINER (loadbutton), image);
  
  	scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
  	gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
  	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);

  	etextview = gtk_text_view_new ();
  	pfd = pango_font_description_from_string (prefs.pfont);
	gtk_widget_modify_font(etextview,pfd);
  	gtk_container_add (GTK_CONTAINER (scrolledwindow), etextview);
	
	etextbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(etextview));
	g_signal_connect (etextbuffer, "mark_set", G_CALLBACK (cursor_moved), NULL);
	g_signal_connect (etextbuffer, "changed", G_CALLBACK (cursor_moved), NULL);
	g_signal_connect (etextbuffer, "changed", G_CALLBACK (buffer_changed), NULL);
	
	estatusbar = gtk_statusbar_new();
	gtk_box_pack_start(GTK_BOX(vbox), estatusbar, FALSE, FALSE, 0);
	
	if (strlen(efname)) open_file(editordialog, efname);
	
  	gtk_widget_show_all(editordialog);
	gtk_widget_grab_focus (etextview);
	cursor_moved();
	buffer_saved();
}

/*---------------------------------------------------------------------------
 *	term callbacks and drag & drop
 *---------------------------------------------------------------------------*/

static guint id;
static gboolean savesuccess = FALSE;

static GdkAtom text_uri_list, application_octet_stream, text_plain;

enum {TARGET_RAW_DATA, TARGET_URI_LIST};

/* Targets which other apps can offer to us */
static GtkTargetEntry targets_to_accept[] =
{
	{"application/octet-stream", 0, TARGET_RAW_DATA},
	{"text/uri-list", 0, TARGET_URI_LIST}
};

#define MAX_HOST_NAME_LEN 256	/* XXX: Limit */
char    our_host_name[MAX_HOST_NAME_LEN];

static gchar *dnd_path=NULL;


static void term_changed_cb(GtkWidget *widget, gpointer data)
{
	char *title = g_strdup_printf("Euler [*%s]",gtk_term_get_name(term));
	gtk_window_set_title(GTK_WINDOW(term_window), title);
	g_free(title);
}

static void term_saved_cb(GtkWidget *widget, gpointer data)
{
	char *title = g_strdup_printf("Euler [%s]",gtk_term_get_name(term));
	gtk_window_set_title(GTK_WINDOW(term_window), title);
	g_free(title);
}

static void term_editing_cb(GtkWidget *widget, gpointer data)
{
	if (gtk_term_is_initialized(term))
		gtk_statusbar_pop(GTK_STATUSBAR(widget),id);
	else
		gtk_statusbar_push(GTK_STATUSBAR(widget),id," Editing ...");
}

static void term_interpreting_cb(GtkWidget *widget, gpointer data)
{
	gtk_statusbar_push(GTK_STATUSBAR(widget),id," Running ...");
}

static gint term_cfg_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
{
	GtkTerm *term = GTK_TERM(widget);
	prefs.twidth = term->twidth;
	prefs.theight = term->theight;
	return FALSE;
}

static void overwrite_yes_dndopen_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");

	destroy_yesno_dialog(widget);
		
	if (gtk_term_save(term,filename)) {
		gtk_widget_destroy((GtkWidget*)data);
		chdir(g_dirname(dnd_path));
		savesuccess = gtk_term_load(term, dnd_path);
	} else {
		char *txt = g_strdup_printf("\nI could not save the notebook to the file\n%s\nThe notebook open command is aborted ...\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
		chdir(g_dirname(filename));
		savesuccess = FALSE;
	}
}

static void save_as_dndopen_cb(GtkWidget *widget, gpointer data)
{
	struct stat statbuf;
		
	char *filename = get_filename(data, ".en");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
	if (stat(filename, &statbuf) == 0)   /* file exists */
        yesnoquestion("Destination file exists, overwrite it?",
					  G_CALLBACK(overwrite_yes_dndopen_cb), 
					  NULL, (GtkWidget*)data);
	else {
		overwrite_yes_dndopen_cb(NULL, data);
		gtk_widget_destroy((GtkWidget*)data);
	}
	
}

static void yes_dndopen_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	
	if (!gtk_term_is_named(term))
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
					"Save Notebook As ...", "*.en", "untitled",
					G_CALLBACK(save_as_dndopen_cb), term_window);
	else {
		gtk_term_save(term,NULL);
		gtk_term_load(term, dnd_path);
		chdir(g_dirname(dnd_path));
	}
}

static void no_dndopen_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	gtk_term_load(term, dnd_path);
	chdir(g_dirname(dnd_path));
}

static GdkAtom best_raw(GdkDragContext *context);
static gboolean provides(GdkDragContext *context, GdkAtom target);

/* Convert a list of URIs into a list of strings.
 * Lines beginning with # are skipped.
 * The text block passed in is zero terminated (after the final CRLF)
 */
GSList *uri_list_to_gslist(char *uri_list)
{
	GSList   *list = NULL;

	while (*uri_list)
	{
		char	*linebreak;
		char	*uri;
		int	length;

		linebreak = strchr(uri_list, 13);

		if (!linebreak || linebreak[1] != 10)
		{
			infobox(term_window, "Incorrect or missing line break "
				"in text/uri-list data\n");
			return list;
		}

		length = linebreak - uri_list;

		if (length && uri_list[0] != '#')
		{
			uri = g_malloc(sizeof(char) * (length + 1));
			strncpy(uri, uri_list, length);
			uri[length] = 0;
			list = g_slist_append(list, uri);
		}

		uri_list = linebreak + 2;
	}

	return list;
}

/* Convert a URI to a local pathname (or NULL if it isn't local).
 * The returned pointer points inside the input string.
 * Possible formats:
 *	/path
 *	///path
 *	//host/path
 *	file://host/path
 */
char *get_local_path(char *uri)
{
	if (*uri == '/')
	{
		char    *path;

		if (uri[1] != '/')
			return uri;	/* Just a local path - no host part */

		path = strchr(uri + 2, '/');
		if (!path)
			return NULL;	    /* //something */

		if (path - uri == 2)
			return path;	/* ///path */
		if (strlen(our_host_name) == path - uri - 2 &&
			strncmp(uri + 2, our_host_name, path - uri - 2) == 0)
			return path;	/* //myhost/path */

		return NULL;	    /* From a different host */
	}
	else
	{
		if (strncasecmp(uri, "file:", 5))
			return NULL;	    /* Don't know this format */

		uri += 5;

		if (*uri == '/')
			return get_local_path(uri);

		return NULL;
	}
}

/*
 * This is called when the remote application wants to send us some
 * data. We get to decide what kind of data we'd like.
 */
static int drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time)
{
	/* FIXME 
		make term_window can't accept dnd
		if it has a shown transient window */
		
	guchar	*leafname = NULL;
	GdkAtom	target;

	if (gtk_drag_get_source_widget(context))
		return TRUE;		/* Drag to ourselves - ignore */

	if (provides(context, text_uri_list))
		target = text_uri_list;
	else
	{
		leafname = g_strdup("Untitled");
		target = best_raw(context);
	}

	/* Associate the leafname with the context */
	if (leafname)
		g_dataset_set_data_full(context, "uri", leafname, g_free);

	if (target)
		gtk_drag_get_data(widget, context, target, time);
	else
		gtk_drag_finish(context, FALSE, FALSE, time);

	return TRUE;
}

/* Look for the best target type for transferring data */
static GdkAtom best_raw(GdkDragContext *context)
{
	if (provides(context, text_plain))
		return text_plain;
	if (provides(context, application_octet_stream))
		return application_octet_stream;

	infobox(term_window, "I can't get the data from the other application\n");
	return GDK_NONE;
}

/* Is the sended willing to supply this target type? */
static gboolean provides(GdkDragContext *context, GdkAtom target)
{
	GList *targets = context->targets;

	while (targets && ((GdkAtom) targets->data != target))
	targets = targets->next;

	return targets != NULL;
}

/* Called when some data arrives from the remote app (which we asked for
 * in drag_drop.
 */
static void drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint32 time)
{
	if (!selection_data->data)
	{
		/* Timeout? */ 
		gtk_drag_finish(context, FALSE, FALSE, time);
		return;
	}

	if (selection_data->target == text_uri_list)
	{
		GSList	*files;
		char	*localpath;

		files = uri_list_to_gslist(selection_data->data);

		if (files == NULL || files->next)
		{
			/* Only one file at a time for now */ 
			infobox(term_window, "Sorry, can only load one file at a time");
			gtk_drag_finish(context, FALSE, FALSE, time);
			return;
		}

		localpath = get_local_path((char *) files->data);

		/* Remember the URI for the data */ 
		g_dataset_set_data_full(context,"uri",files->data,g_free);

		if (localpath)
		{
			gboolean 	success = FALSE;
			
			if (gtk_term_is_editing(term)) {
				if (!is_demo && gtk_term_is_changed(term)) {
					dnd_path=localpath;
					yesnoquestion("\nThe current notebook has been modified ...\nDo you want to save it?\n",
								  G_CALLBACK(yes_dndopen_cb),G_CALLBACK(no_dndopen_cb),term_window);
					success = savesuccess;
				} else {
					success = gtk_term_load(term, localpath);
					chdir(g_dirname(localpath));
				}
			} else {
				infobox(term_window, "You cannot load another notebook\nwhile the interpreter is running ...");
			}
			gtk_drag_finish(context,success,FALSE,time);
		}
		else
		{
			gtk_drag_finish(context, FALSE, FALSE, time);
		}
	}
	else
	{
		gtk_drag_finish(context, FALSE, FALSE, time);
	}
}

/*---------------------------------------------------------------------------
 *	metagtk callbacks
 *---------------------------------------------------------------------------*/

static gint meta_cfg_cb(GtkWidget *widget, GdkEventConfigure *event)
{
	prefs.gwidth = event->width;
	prefs.gheight = event->height;
	return FALSE;
}

/*---------------------------------------------------------------------------
 *	demo callbacks
 *---------------------------------------------------------------------------*/
static const char *demo_name = NULL;

static void save_as_demo_cb(GtkWidget *widget, gpointer data)
{
	char *filename = get_filename(data, ".en");
	
	if ((!filename) || (strcmp(filename,"") == 0))
		return;
	
	if (gtk_term_save(term,filename)) {
		gchar *s;
		chdir(g_dirname(filename));
		s = g_strconcat(INSTALL_DIR,"/share/euler/progs/",demo_name,NULL);
		gtk_term_load(term,s);
		g_free(s);
		is_demo = 1;
		gtk_widget_destroy((GtkWidget*)data);
	} else {
		char *txt = g_strdup_printf("\nI could not save the notebook to the file\n%s\nThe notebook open command is aborted ...\n",filename);
		infobox((GtkWidget*)data, txt);
		g_free(txt);
		chdir(g_dirname(filename));
	}
}

static void yes_demo_cb(GtkWidget *widget, gpointer data)
{
	destroy_yesno_dialog(widget);
	
	if (!gtk_term_is_named(term))
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE,
					"Save Notebook As ...", "*.en", "untitled",
					G_CALLBACK(save_as_demo_cb), term_window);
	else {
		gchar *s;
		gtk_term_save(term,NULL);
		s = g_strconcat(INSTALL_DIR,"/share/euler/progs/",demo_name,NULL);
		gtk_term_load(term,s);
		g_free(s);
		is_demo = 1;
	}
}

static void no_demo_cb(GtkWidget *widget, gpointer data)
{
	gchar *s;
	
	destroy_yesno_dialog(widget);
	s = g_strconcat(INSTALL_DIR,"/share/euler/progs/",demo_name,NULL);
	gtk_term_load(term,s);
	g_free(s);
	is_demo = 1;
}

static void demo_cb(GtkAction *action)
{
	g_return_if_fail(action != NULL);
	g_return_if_fail(GTK_IS_ACTION(action));
	demo_name = gtk_action_get_name(action); 
	
	if (!is_demo) {
		if (gtk_term_is_changed(term))
			yesnoquestion("\nThe current notebook has been modified ...\nDo you want to save it?\n",
						   G_CALLBACK(yes_demo_cb),G_CALLBACK(no_demo_cb),term_window);
		else {
			gchar *s;
			s = g_strconcat(INSTALL_DIR,"/share/euler/progs/",demo_name,NULL);
			gtk_term_load(term,s);
			g_free(s);
			is_demo = 1;
		}
	} else {
		gchar *s;
		s = g_strconcat(INSTALL_DIR,"/share/euler/progs/",demo_name,NULL);
		gtk_term_load(term,s);
		g_free(s);
		is_demo = 1;
	}
}

/*---------------------------------------------------------------------------
 *	main
 *---------------------------------------------------------------------------*/
static void on_file_new(GtkAction *action)
{
	if (!is_demo && gtk_term_is_changed(term))
		yesnoquestion("\nThe current notebook has been modified ...\nDo you want to save it?\n",
					  G_CALLBACK(yes_new_cb),G_CALLBACK(no_new_cb),term_window);
	else {
		gtk_term_clear_new(term);
		is_demo = 0;
	}
}

static void on_file_open(GtkAction *action)
{
	if (!is_demo && gtk_term_is_changed(term))
		yesnoquestion("\nThe current notebook has been modified ...\nDo you want to save it?\n",
					  G_CALLBACK(yes_open_cb),G_CALLBACK(no_open_cb),term_window);
	else
		file_select(GTK_FILE_CHOOSER_ACTION_OPEN, 
					"Open A Notebook ...", "*.en", NULL,
					G_CALLBACK(open_cb), term_window);	
}

static void on_file_save(GtkAction *action)
{
	if (!gtk_term_is_named(term))
		file_select(GTK_FILE_CHOOSER_ACTION_SAVE, 
					"Save Notebook As ...", "*.en", "untitled",
					G_CALLBACK(save_as_cb), term_window);
	else if (!is_demo)
		gtk_term_save(term,NULL);
}

static void on_file_save_as(GtkAction *action)
{
	file_select(GTK_FILE_CHOOSER_ACTION_SAVE, 
				"Save Notebook As ...", "*.en", gtk_term_is_named(term) ? gtk_term_get_name(term) : "untitled",
				G_CALLBACK(save_as_cb), term_window);
}

static void on_export_to_postscript(GtkAction *action)
{
	file_select(GTK_FILE_CHOOSER_ACTION_SAVE, 
				"Export Graphics As ...", "*.eps", "untitled",
				G_CALLBACK(postscript_cb), term_window);
}

static void on_file_quit(GObject *object)
{
	if (editordialog) {
		gdk_window_raise(editordialog->window);
		editor_delete_cb(term_window, NULL);
	}
	else quit_cb(object, NULL);		
}

static void on_edit_cut (GtkAction *action)
{
	gtk_term_cut(term);
}

static void on_edit_copy (GtkAction *action)
{
	gtk_term_copy(term);
}

static void on_edit_paste (GtkAction *action)
{
	gtk_term_paste(term);
}

static void on_edit_insert_command (GtkAction *action)
{
	gtk_term_insert_command(term,NULL);
}

static void on_edit_delete_command (GtkAction *action)
{
	gtk_term_delete_command(term);
}

static void on_edit_clear_outputs (GtkAction *action)
{
	gtk_term_delete_outputs(term);
}

static void on_edit_delete_output (GtkAction *action)
{
	gtk_term_delete_current_output(term);
}

static void on_edit_edit_comment (GtkAction *action)
{
	gchar *buffer = gtk_term_get_comment(term);
	if (buffer)
		if (!g_utf8_validate(buffer, -1, NULL))
			buffer = g_locale_to_utf8(buffer, -1, NULL, NULL, NULL);
	editcomment(buffer);
}

static void on_misc_editor (GtkAction *action)
{
	editor();
}

static void on_misc_preferences (GtkAction *action)
{
	prefs_box();
}

static void on_help_doc (GtkAction *action)
{
	char *browser;
	browser = g_strconcat(prefs.browser," ",INSTALL_DIR,"/share/doc/euler/index.html &",NULL);
	system(browser);
	g_free(browser);
}

static void on_help_about (GtkAction *action)
{
	about();	
}

static GtkActionEntry actionentries[] = {
/* file menu */  
  { "FileMenu"     , NULL                 , "_File" },
  { "New"          , GTK_STOCK_NEW        , "_New"           , "<control>N", "Create a new file"       , G_CALLBACK (on_file_new) },
  { "Open"         , GTK_STOCK_OPEN       , "_Open"          , "<control>O", "Open a file"             , G_CALLBACK (on_file_open) },
  { "Save"         , GTK_STOCK_SAVE       , "_Save"          , "<control>S", "Save current file"       , G_CALLBACK (on_file_save) },
  { "SaveAs"       , GTK_STOCK_SAVE_AS    , "Save _As..."    , NULL        , "Save to a file"          , G_CALLBACK (on_file_save_as) },
  { "ExportMenu"   , NULL                 , "Export Graphics To" },
  { "Postscript"   , NULL                 , "Postscript"     , "<control>P", NULL                      , G_CALLBACK (on_export_to_postscript) },
  { "Quit"         , GTK_STOCK_QUIT       , "_Quit"          , "<control>Q", "Quit"                    , G_CALLBACK (on_file_quit) },
/* edit menu */  
  { "EditMenu"     , NULL                 , "_Edit" },
  { "Cut"          , GTK_STOCK_CUT        , "Cu_t"           , "<control>X", "Cut selection"           , G_CALLBACK (on_edit_cut) },
  { "Copy"         , GTK_STOCK_COPY       , "_Copy"          , "<control>C", "Copy selection"          , G_CALLBACK (on_edit_copy) },
  { "Paste"        , GTK_STOCK_PASTE      , "_Paste"         , "<control>V", "Paste clipboard content" , G_CALLBACK (on_edit_paste) },
  { "InsertCommand", GTK_STOCK_ADD        , "Insert Command" , "<control>I", "Insert Command"          , G_CALLBACK (on_edit_insert_command) },
  { "DeleteCommand", GTK_STOCK_REMOVE     , "Delete Command" , "<control>D", "Delete Current Command"  , G_CALLBACK (on_edit_delete_command) },
  { "ClearOutputs" , GTK_STOCK_CLEAR      , "Clear All Outputs" , NULL     , "Clear All Outputs"       , G_CALLBACK (on_edit_clear_outputs) },
  { "DeleteOutput" , GTK_STOCK_DELETE     , "Delete Current Output" , NULL , "Delete Current Output"   , G_CALLBACK (on_edit_delete_output) },
  { "EditComment"  , GTK_STOCK_JUSTIFY_FILL , "Edit Comment", "<control>E", "Edit Command..."          , G_CALLBACK (on_edit_edit_comment) },
/* misc menu */  
  { "MiscMenu"     , NULL                 , "_Misc" },
  { "DemoMenu"     , NULL                 , "Demo" },
  { "Editor"       , GTK_STOCK_EDIT       , "Editor"         , NULL        , "Internal Editor..."      , G_CALLBACK (on_misc_editor) },
  { "Preferences"  , GTK_STOCK_PREFERENCES, "Preferences"    , NULL        , NULL                      , G_CALLBACK (on_misc_preferences) },
/* help menu */  
  { "HelpMenu"     , NULL                 , "_Help" },
  { "Documentation", GTK_STOCK_HELP       , "Documentation"  , "F1"        , NULL                      , G_CALLBACK (on_help_doc) },
  { "About"        , GTK_STOCK_ABOUT      , "_About"         , "<control>A", "About"                   , G_CALLBACK (on_help_about) },
};

static guint n_actionentries = G_N_ELEMENTS (actionentries);

static const gchar *ui_info = 
"<ui>"
"  <menubar name='MenuBar'>"
"    <menu name='File' action='FileMenu'>"
"      <menuitem action='New'/>"
"      <menuitem action='Open'/>"
"      <menuitem action='Save'/>"
"      <menuitem action='SaveAs'/>"
"      <separator/>"
"      <menu action='ExportMenu'>"
"        <menuitem action='Postscript'/>"
"      </menu>"
"      <separator/>"
"      <menuitem action='Quit'/>"
"    </menu>"
"    <menu name='Edit' action='EditMenu'>"
"      <menuitem action='Cut'/>"
"      <menuitem action='Copy'/>"
"      <menuitem action='Paste'/>"
"      <separator/>"
"      <menuitem action='InsertCommand'/>"
"      <menuitem action='DeleteCommand'/>"
"      <separator/>"
"      <menuitem action='ClearOutputs'/>"
"      <menuitem action='DeleteOutput'/>"
"      <separator/>"
"      <menuitem action='EditComment'/>"
"    </menu>"
"    <menu name='Misc' action='MiscMenu'>"
"      <menu name='Demo' action='DemoMenu'/>"
"      <menuitem action='Editor'/>"
"      <menuitem action='Preferences'/>"
"    </menu>"
"    <menu name='Help' action='HelpMenu'>"
"      <menuitem action='Documentation'/>"
"      <menuitem action='About'/>"
"    </menu>"
"  </menubar>"
"  <toolbar name='ToolBar'>"
"    <toolitem action='New'/>"
"    <toolitem action='Open'/>"
"    <toolitem action='Save'/>"
"    <separator/>"
"    <toolitem action='Cut'/>"
"    <toolitem action='Copy'/>"
"    <toolitem action='Paste'/>"
"    <separator/>"
"    <toolitem action='InsertCommand'/>"
"    <toolitem action='DeleteCommand'/>"
"    <separator/>"
"    <toolitem action='ClearOutputs'/>"
"    <toolitem action='DeleteOutput'/>"
"    <separator/>"
"    <toolitem action='EditComment'/>"
"    <toolitem action='Editor'/>"
"  </toolbar>"
"  <popup name='Popup'>"
"	   <menuitem action='Cut'/>"
"      <menuitem action='Copy'/>"
"      <menuitem action='Paste'/>"
"      <separator/>"
"      <menuitem action='InsertCommand'/>"
"      <menuitem action='DeleteCommand'/>"
"      <separator/>"
"      <menuitem action='ClearOutputs'/>"
"      <menuitem action='DeleteOutput'/>"
"      <separator/>"
"      <menuitem action='EditComment'/>"
"  </popup>"
"</ui>";

int main(int argc, char *argv[])
{
  	GtkWidget *vbox;
  			
	GtkUIManager   *ui;
    GtkActionGroup *actions,*demoactions;
    GError         *error = NULL;
  
	GtkWidget     *statusbar;
  	GtkWidget     *hbox;
	GtkWidget     *scrollbar;
	GdkBitmap 	  *icon_mask;
	GdkPixmap     *icon_pixmap;
	GdkColor       alpha;

	char **entries=NULL;
	int n_entries=0,i;
	char *s;
	
	/* this should be equivalent to setlocale (LC_ALL, "") */
	gtk_set_locale();

	/* This must be the same for all locales */
	setlocale(LC_NUMERIC, "POSIX");

	/* Disable gtk's ability to set the locale. */
	/* If gtk is allowed to set the locale, then it will override the     */
	/* setlocale for LC_NUMERIC (which is important for proper PS output. */
	/* This may look funny here, given we make a call to gtk_set_locale() */
	/* above.  I don't know yet, if this is really the right thing to do. */
	gtk_disable_setlocale();
	
	gtk_init (&argc, &argv);
	prefs = eulerrc_init();
		
	/* allocate stack space */
	if (!stack_init(prefs.estack)) {
		g_print("Stack allocation failed!\nExiting.....\n");
		exit(1);
	}
	
	/* allocate graphic stack space */
	if (!openmeta(prefs.gstack,NULL)) {
		g_print("Graphic stack allocation failed!\nExiting.....\n");
		exit(1);
	}
	setmetalines(prefs.glines);
	setmetacolors(prefs.colors);

	/*
	 *	setup atoms needed for drag & drop
	 */
	text_plain = gdk_atom_intern("text/plain", FALSE);
	text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
	application_octet_stream = gdk_atom_intern("application/octet-stream", FALSE);

	/*
	 * create the graphic window
	 */
	meta_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(meta_window), "Euler graphics window");
	g_signal_connect(G_OBJECT(meta_window),"delete_event",
					 G_CALLBACK(on_file_quit),NULL);
	g_signal_connect(G_OBJECT(meta_window),"destroy",
					 G_CALLBACK(on_file_quit),NULL);

	meta = gtk_meta_new(prefs.gwidth,prefs.gheight);
	gtk_container_add(GTK_CONTAINER(meta_window),meta);

	/*
	 *	add a hook to collect geometry changes of the graphic window
	 */
	g_signal_connect(G_OBJECT(meta_window),"configure_event",
					 G_CALLBACK(meta_cfg_cb),NULL);
	
	gtk_widget_show_all(meta_window);
	
	/*
	 * create the main window
	 */
  	term_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  	gtk_window_set_title (GTK_WINDOW (term_window), "Euler []");
  	gtk_widget_realize(term_window);
	icon_pixmap = gdk_pixmap_create_from_xpm_d(term_window->window,&icon_mask,&alpha,icon_xpm);
	gdk_window_set_icon(term_window->window,NULL,icon_pixmap,icon_mask);
	gdk_window_set_group(meta_window->window,term_window->window);
	
  	actions = gtk_action_group_new ("Actions");
    gtk_action_group_add_actions (actions, actionentries, n_actionentries, NULL);
      
    ui = gtk_ui_manager_new ();
    gtk_ui_manager_insert_action_group (ui, actions, 0);
    gtk_window_add_accel_group (GTK_WINDOW (term_window), 
    							gtk_ui_manager_get_accel_group (ui));
      
	if(!gtk_ui_manager_add_ui_from_string(ui, ui_info, -1, &error))
	{
	  g_message ("Building menus failed: %s", error->message);
	  g_error_free (error);
	}

	gtk_ui_manager_set_add_tearoffs(ui,TRUE);
	gtk_toolbar_set_style(GTK_TOOLBAR(gtk_ui_manager_get_widget(ui, "ui/ToolBar")), GTK_TOOLBAR_ICONS);
	
	vbox = gtk_vbox_new (FALSE, 0);
  	gtk_container_add (GTK_CONTAINER (term_window), vbox);

  	gtk_box_pack_start (GTK_BOX (vbox), gtk_ui_manager_get_widget (ui, "/MenuBar"),
			  			FALSE, FALSE, 0);
  	
	gtk_box_pack_start (GTK_BOX (vbox), gtk_ui_manager_get_widget (ui, "/ToolBar"),
			  			FALSE, FALSE, 0);
		
	/*
	 * parse the install directory for available demos
	 */
	s = g_strconcat(INSTALL_DIR,"/share/euler/progs",NULL);
			
	scan_dir(s,"*.en",&entries,&n_entries);
	
	if (entries) {
		GtkActionEntry demoentries[n_entries];
		for (i=0;i<n_entries;i++) {
			demoentries[i].name = entries[i];  
			demoentries[i].stock_id = NULL; 
			demoentries[i].label = entries[i];
			demoentries[i].accelerator = NULL;
			demoentries[i].tooltip = NULL;
			demoentries[i].callback = G_CALLBACK(demo_cb);
		}
		
		demoactions = gtk_action_group_new ("DemoActions");
    	gtk_action_group_add_actions (demoactions, demoentries, n_entries, NULL);
		gtk_ui_manager_insert_action_group (ui, demoactions, 0);
		
		for (i=0;i<n_entries;i++){
			gtk_ui_manager_add_ui(ui,gtk_ui_manager_new_merge_id(ui), "/ui/MenuBar/Misc/Demo", 
						  		  entries[i],entries[i], GTK_UI_MANAGER_MENUITEM, FALSE);
			g_free(entries[i]);
		}
		g_free(entries);
		entries = NULL;
	}
	g_free(s);
	
	hbox = gtk_hbox_new(FALSE,0);
	gtk_box_pack_start(GTK_BOX(vbox),hbox,TRUE,TRUE,0);
	
	term = gtk_term_new(prefs.twidth,prefs.theight,prefs.tfont);
	
	gtk_box_pack_start_defaults(GTK_BOX(hbox),term);
	
	g_signal_connect(G_OBJECT(term),"term_changed",
					 G_CALLBACK(term_changed_cb),NULL);
	g_signal_connect(G_OBJECT(term),"term_saved",
					 G_CALLBACK(term_saved_cb),NULL);
	g_signal_connect_after(G_OBJECT(term),"size_allocate",
					 G_CALLBACK(term_cfg_cb),NULL);
	
	/* Set the window up to receive drops */
	gtk_drag_dest_set(term,
					  GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT,
					  targets_to_accept,
					  sizeof(targets_to_accept) / sizeof(*targets_to_accept),
					  GDK_ACTION_COPY | GDK_ACTION_PRIVATE);
	g_signal_connect(G_OBJECT(term), "drag_drop",
					 G_CALLBACK(drag_drop), NULL);
	g_signal_connect(G_OBJECT(term), "drag_data_received",
					 G_CALLBACK(drag_data_received), NULL);
			
	scrollbar = gtk_vscrollbar_new(GTK_TERM(term)->v_adj);
	GTK_WIDGET_UNSET_FLAGS (scrollbar, GTK_CAN_FOCUS);
	gtk_box_pack_start(GTK_BOX(hbox),scrollbar,FALSE,FALSE,0);
	gtk_term_set_scrollbar(term, scrollbar);
	
	/*
	 * set the popup menu
	 */
	gtk_term_set_popup(term,GTK_MENU(gtk_ui_manager_get_widget(ui,"/Popup")));
	
	statusbar = gtk_statusbar_new ();
  	gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0);
	id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),"euler status messages");
	g_signal_connect_swapped(G_OBJECT(term),"term_editing",
							 G_CALLBACK(term_editing_cb),GTK_OBJECT(statusbar));
	g_signal_connect_swapped(G_OBJECT(term),"term_interpreting",
							 G_CALLBACK(term_interpreting_cb),GTK_OBJECT(statusbar));
	gtk_statusbar_push(GTK_STATUSBAR(statusbar),id," Initializing Euler ...");
	
	g_signal_connect (G_OBJECT(term_window), "delete_event",
					  G_CALLBACK (on_file_quit),NULL);
	g_signal_connect (G_OBJECT(term_window), "destroy",
					  G_CALLBACK (on_file_quit),NULL);

	/*
	 *	show everything and lets go
	 */
	gtk_widget_show_all(term_window);

	setmetadevice(gtk_meta_get_device(meta));
	
	while (gtk_events_pending()) gtk_main_iteration_do(FALSE);
	
	s=g_strconcat(INSTALL_DIR,"/share/euler/help.txt",NULL);
	loadhelp(s);
	g_free(s);
	
	main_loop(argc,argv);
	
	closemeta();
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1