/******************************************************************************
*		       							      *
* gnome/app.c (part of rcalc)					       	      *
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.		      *
*								       	      *
* 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.				       	      *
*								       	      *
* This program is distributed in the hope that it will be useful,      	      *
* but WITHOUT ANY WARRANTY; without even the implied warranty of       	      *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	       	      *
* GNU General Public License for more details.			       	      *
*								       	      *
* You should have received a copy of the GNU General Public License    	      *
* along with this program; if not, write to the Free Software	       	      *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	       	      *
*								       	      *
******************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "app.h"		/* This file's header	     		*/
#include "conf.h"		/* User interface preferences		*/
#include "prefs.h"		/* The preferences dialog box.		*/
#include <engine/engine.h>	/* The calculation engine		*/
#include <engine/debug.h>	/* Debugging functions			*/

#include <gnome.h>
#include <libzvt/libzvt.h>

/*****************************************************************************/

void
terminal_widget_set_size (GtkWidget            *widget,
                          int                   width_chars,
                          int                   height_chars)
{
  zvt_term_set_size (ZVT_TERM (widget), width_chars, height_chars);
}

void
terminal_widget_get_size (GtkWidget            *widget,
                          int                  *width_chars,
                          int                  *height_chars)
{
  if (width_chars)
    *width_chars = ZVT_TERM (widget)->grid_width;
  if (height_chars)
    *height_chars = ZVT_TERM (widget)->grid_height;
}

void
terminal_widget_get_cell_size (GtkWidget            *widget,
                               int                  *cell_width_pixels,
                               int                  *cell_height_pixels)
{
  if (cell_width_pixels)
    *cell_width_pixels = ZVT_TERM (widget)->charwidth;
  if (cell_height_pixels)
    *cell_height_pixels = ZVT_TERM (widget)->charheight;
}

#define PADDING 0 /* from zvtterm.c */
void
terminal_widget_get_padding (GtkWidget *widget,
                             int       *xpad,
                             int       *ypad)
{
  if (xpad)
    *xpad = widget->style->xthickness * 2 + PADDING;
  
  if (ypad)
    *ypad = widget->style->ythickness * 2;
}

/* Handle the angle unit submenu.
*/
static void angle_unit_cmd( GtkWidget *caller, GtkWidget *app )
{
	GtkWidget **radios;
	RcEngine *engine;
	RcAngleUnit unit;
	
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( caller );
	g_assert( app );

	/* Ignore deselects */
	if( !GTK_CHECK_MENU_ITEM(caller)->active ) return;
	
	radios = gtk_object_get_data( GTK_OBJECT(app), "angle_radios" );
	engine = gtk_object_get_data( GTK_OBJECT(app), "engine" );
	g_assert( radios );
	g_assert( engine );

	/* Determine which one was selected then set the unit.
	*/
	for( unit=0; ; unit++ )
	{
		g_assert( unit<_RC_NUMBER_OF_ANGLE_UNITS );
		if( radios[unit] == caller ) break;
	}

	rc_engine_set_angleunit( engine, unit );
}

/* Apply the calculator's state to the settings menu.
*/
static void apply_engine_state( GtkWidget *caller, GtkWidget *app )
{
	GtkWidget **radios;
	RcEngine *engine;
	RcAngleUnit unit;
	
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );

	radios = gtk_object_get_data( GTK_OBJECT(app), "angle_radios" );
	engine = gtk_object_get_data( GTK_OBJECT(app), "engine" );
	g_assert( radios );
	g_assert( engine );

	/* Set the angle unit submenu.
	*/
	unit = rc_engine_get_angleunit( engine );
	gtk_check_menu_item_set_active(
		GTK_CHECK_MENU_ITEM(radios[unit]), TRUE );	
}

/* Apply the contents of the configuration to the application.
*/
static void apply_ui_config( GtkWidget *caller, GtkWidget *app )
{
	RcGnomeUiCfg *config;
	GtkWidget    *terminal;
	GtkWidget    *scrollbar;
	
	PangoFontDescription *desc;
	
	RcScrollbarPos scrollbar_pos;
	GdkFont *font=NULL;
	GdkGeometry hints;
	const gushort *foreground;
	const gushort *background;
	const gushort *prompt;
	const gushort *error;
	gushort palette[3][18];
	int i;
	
	int xpad, ypad;
	int char_width;
   int char_height;
  
	
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );

	config    = gtk_object_get_data( GTK_OBJECT(app), "config" );
	terminal  = gtk_object_get_data( GTK_OBJECT(app), "terminal" );
	scrollbar = gtk_object_get_data( GTK_OBJECT(app), "scrollbar" );

	g_assert( config );
	g_assert( terminal );
	g_assert( scrollbar );
	
	
	/* Set the scrollbar position
	*/
	scrollbar_pos = rcg_conf_get_scrollbar_pos(config);
	if( scrollbar_pos == RC_SCROLLBAR_HIDDEN )
	{
		gtk_widget_hide( scrollbar );
	}
	else
	{
		GtkWidget *box = scrollbar->parent;
		gtk_box_set_child_packing(
			GTK_BOX(box), scrollbar,
			FALSE, TRUE, 0,
			( scrollbar_pos == RC_SCROLLBAR_LEFT ?
			  GTK_PACK_START : GTK_PACK_END ) );
		gtk_widget_show( scrollbar );
	}

	/* Set the terminal behaviour.
	*/
	
	zvt_term_set_auto_window_hint (ZVT_TERM (terminal), FALSE);
	
	zvt_term_set_scrollback(
		ZVT_TERM(terminal), rcg_conf_get_scrollback(config) );
	zvt_term_set_scroll_on_keystroke(
		ZVT_TERM(terminal), rcg_conf_get_scroll_on_key(config) );
	zvt_term_set_scroll_on_output(
		ZVT_TERM(terminal), rcg_conf_get_scroll_on_out(config) );
	zvt_term_set_blink(
		ZVT_TERM(terminal), rcg_conf_get_blink(config) );
	zvt_term_set_bell(
		ZVT_TERM(terminal), rcg_conf_get_bell(config) );

	/* Set the font (and the resize hints)
	*/
	
	desc = pango_font_description_from_string(rcg_conf_get_font_name(config));
	
	/*g_print("Trying to load font <%s>\n",
	   pango_font_description_to_string (desc));*/
	
	g_assert(desc);
	zvt_term_set_pango_font (ZVT_TERM(terminal), desc);

#if 0	
	if (desc) {	   
		font = gdk_font_from_description(desc);
   }

#define DEFAULT_FONT_OLD		"-*-courier-medium-r-*-*-*-120-*-*-*-*-*-*"	
	if (!(font)) {
		font = gdk_font_load(DEFAULT_FONT_OLD);
		printf(_("Warning: Falling back in old font system\n"));
		printf(_(" Font selection should not work in config. dialog.\n"));
	}
	
	g_assert(font);
		
	/* ?!? this stopped working one day, but it used to work !	
	  there's really something to do here.
	  In addition, libzvt does not support pango font yet */	
	zvt_term_set_fonts( ZVT_TERM(terminal), font, font ); 
#endif		
	/*terminal_widget_set_size (terminal, 90,100);*/
	terminal_widget_get_padding (terminal, &xpad, &ypad);
	terminal_widget_get_cell_size (terminal, &char_width, &char_height);

/* these values are a hack to get a proper aspect... 
that need to be fixed */
#define MIN_WIDTH_CHARS 20
#define MIN_HEIGHT_CHARS 20
	
	hints.base_width  = xpad;
	hints.base_height = ypad;

	hints.width_inc  = char_width;
	hints.height_inc = char_height;
	
	hints.min_width = hints.base_width + hints.width_inc * MIN_WIDTH_CHARS;
   hints.min_height = hints.base_height + hints.height_inc * MIN_HEIGHT_CHARS;

 /*  g_print ("hints: base %dx%d min %dx%d inc %d %d\n",
               hints.base_width,
               hints.base_height,
               hints.min_width,
               hints.min_height,
               hints.width_inc,
               hints.height_inc);
*/	
	gtk_window_set_geometry_hints( GTK_WINDOW(app),
				       GTK_WIDGET(terminal),
				       &hints,
				       GDK_HINT_RESIZE_INC |
				       GDK_HINT_MIN_SIZE);

	/* Set the colours.
	*/
	foreground = rcg_conf_get_colour( config, RC_COLOUR_FOREGROUND );
	background = rcg_conf_get_colour( config, RC_COLOUR_BACKGROUND );
	prompt	   = rcg_conf_get_colour( config, RC_COLOUR_PROMPT );
	error	   = rcg_conf_get_colour( config, RC_COLOUR_ERROR );

	memset( palette, 0, sizeof(palette) );
	for( i=0; i<3; i++ )
	{
		palette[i][16] = palette[i][7] = foreground[i];
		palette[i][17] = palette[i][0] = background[i];
		palette[i][4]  = prompt[i];
		palette[i][1]  = error[i];
	}

	zvt_term_set_color_scheme(
		ZVT_TERM(terminal), palette[0], palette[1], palette[2] );
}

/* Destroy event for the preferences window.
*/
static void prefs_destroy( GtkWidget *caller, GtkWidget *app )
{
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );

	gtk_object_set_data( GTK_OBJECT(app), "prefs", NULL );
}
	
/* Run the preferences dialog.
*/
static void prefs_cmd( GtkWidget *caller, GtkWidget *app )
{
	GtkWidget *prefs;
	
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );

	prefs = gtk_object_get_data( GTK_OBJECT(app), "prefs" );
	if( prefs )
	{
		gtk_widget_show( prefs );
	}
	else
	{
		prefs = rcg_prefs(
			gtk_object_get_data( GTK_OBJECT(app), "config" ),
			(RcApplyConfigFunc)apply_ui_config,
			app );

		gtk_signal_connect(
			GTK_OBJECT(prefs), "destroy",
			GTK_SIGNAL_FUNC(prefs_destroy), app );

		gnome_dialog_set_parent(
			GNOME_DIALOG(prefs), GTK_WINDOW(app) );
		
		gtk_widget_show_all( prefs );

		gtk_object_set_data( GTK_OBJECT(app), "prefs", prefs );
	}
}

/* Exit the program.
*/
static void close_app( GtkWidget *app )
{
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	gtk_main_quit();
}
static void close_app_cmd( GtkWidget *caller, GtkWidget *app )
{
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	close_app( app );
}

static void copy_cmd( GtkWidget *caller, GtkWidget *app )
{
	GtkWidget *terminal;
		
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );

	terminal = gtk_object_get_data( GTK_OBJECT(app), "terminal" );
	g_assert( ZVT_TERM(terminal) );
	zvt_term_copy_clipboard (ZVT_TERM (terminal));
}

/* Paste stuff into the terminal.
*/
static void paste_cmd( GtkWidget *caller, GtkWidget *app )
{
	GdkAtom string_atom;
	GdkEvent *event;
	gint32 time;
	GtkWidget *terminal;
		
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );

	terminal = gtk_object_get_data( GTK_OBJECT(app), "terminal" );
	g_assert( ZVT_TERM(terminal) );
	
	zvt_term_paste_clipboard (ZVT_TERM (terminal));
	
	/* All this code is straight from gnome-terminal.
	** I have no idea what it does =).
	*/
	
	/*string_atom = gdk_atom_intern( "STRING", FALSE );
	if( string_atom == GDK_NONE ) return;

	event = gtk_get_current_event();
	switch( event->type )
	{
	case GDK_BUTTON_PRESS:
	case GDK_2BUTTON_PRESS:
	case GDK_BUTTON_RELEASE:
		time = event->button.time;
		break;

	case GDK_KEY_PRESS:
	case GDK_KEY_RELEASE:
		time = event->key.time;
		break;

	default:
		time = GDK_CURRENT_TIME;
	}
	
	gtk_selection_convert(
		terminal, GDK_SELECTION_PRIMARY, string_atom, time );
*/
}

/* Show the about box
*/
static void about_cmd( GtkWidget *caller, GtkWidget *app )
{
	GtkWidget *dialog;
	const gchar *authors[] =
	{
		/* About | Authors */
		_( "Gary Benson (versions <= 0.3.7)\n"
"Clement Bourdarias (versions > 0.3.7)"),
		NULL
		
	};
	
	const gchar *documenters[] =
	{
		NULL
	};
	
	const gchar *translator_credits =
		_( "Johann Glaser, German translations.\n"
"Mathieu Roy, French translations.\n"
"Luis Recuerda Santiago, Spanish translations.\n"
"Stefano Barazza, Italian translations.\n"
"Richard Soderblom, Afrikaans translations.\n" );

	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );
	
	dialog = gnome_about_new(
		PACKAGE, VERSION,
		_("(C) Copyright 1999, 2000, 2001, 2002, 2003, Gary Benson and Clement Bourdarias" ),
		_( "A fast, simple, symbolic calculator." ),
		 authors, documenters, translator_credits, NULL);

/*	gnome_dialog_set_parent( GNOME_DIALOG(dialog), GTK_WINDOW(app) );
	gnome_dialog_run_and_close( GNOME_DIALOG(dialog) );
	*/
	gtk_widget_show (dialog);
}

/* Stop CTRL-C from being passed to the child (and interrupting it).
*/
static gint gobble_bad_keystrokes( GtkWidget *caller, GdkEvent *event )
{
	if( event->type == GDK_KEY_PRESS )
	{
		if( event->key.keyval == GDK_c &&
		    event->key.state & GDK_CONTROL_MASK )
		{
			rc_debug( RC_DBG_GNOMEUI, "%s(): handled", __FUNCTION__ );
			return TRUE;	/* Event handled... */
		}
	}
	return FALSE;
}

/* Read a line of input from the reader process and execute it.
*/
static void reader_data_available(
	GtkWidget *app, gint source_fd, GdkInputCondition condition )
{
	FILE	 *source;
	FILE	 *dest;
	RcEngine *engine;
	size_t	  size;
	gchar	 *user_input;
	gboolean  quit_flag;
	
	rc_debug( RC_DBG_GNOMEUI, "%s()", __FUNCTION__ );
	g_assert( app );
	
	source = gtk_object_get_data( GTK_OBJECT(app), "pipe_from_reader" );
	dest   = gtk_object_get_data( GTK_OBJECT(app), "pipe_to_reader" );
	engine = gtk_object_get_data( GTK_OBJECT(app), "engine" );

	g_assert( source );
	g_assert( engine );
	
	/* Read the user's input into a buffer.
	*/
	fread( &size, sizeof(size_t), 1, source );
	g_assert( size>0 );
	
	user_input = g_malloc( size+1 );
	fread( user_input, 1, size, source );
	user_input[size] = '\0';

	/* Execute the line.
	*/
	quit_flag = rc_engine_execute( engine, user_input );
	g_free( user_input );
	if( quit_flag )
	{
		close_app( app );
		return;	
	}
	fputc( 0, dest );
	fflush( dest );
	apply_engine_state( app, app );
}
	
/*****************************************************************************/

/* The menubar.
*/
static GnomeUIInfo file_menu[] =
{
	GNOMEUIINFO_MENU_EXIT_ITEM( close_app_cmd, NULL ),
	GNOMEUIINFO_END
};
static GnomeUIInfo edit_menu[] =
{
	GNOMEUIINFO_MENU_COPY_ITEM( copy_cmd, NULL ),
	GNOMEUIINFO_MENU_PASTE_ITEM( paste_cmd, NULL ),
	GNOMEUIINFO_END
};
static GnomeUIInfo angle_unit_radios[] =
{
	/* Settings menu | Angle unit | Degrees */
	GNOMEUIINFO_RADIOITEM( N_("Degrees"), NULL, angle_unit_cmd, NULL ),
	/* Settings menu | Angle unit | Radians */
	GNOMEUIINFO_RADIOITEM( N_("Radians"), NULL, angle_unit_cmd, NULL ),
	GNOMEUIINFO_END
};
static GnomeUIInfo angle_unit_menu[] =
{
	GNOMEUIINFO_RADIOLIST( angle_unit_radios ),
	GNOMEUIINFO_END
};
static GnomeUIInfo settings_menu[] =
{
  	GNOMEUIINFO_MENU_PREFERENCES_ITEM( prefs_cmd, NULL ),
	/* Settings menu | Angle unit submenu */
	GNOMEUIINFO_SUBTREE( N_("Angle unit"), angle_unit_menu ),
	GNOMEUIINFO_END
};
static GnomeUIInfo help_menu[] =
{
	GNOMEUIINFO_HELP( "index" ),
	GNOMEUIINFO_MENU_ABOUT_ITEM( about_cmd, NULL ),
	GNOMEUIINFO_END	
};
static GnomeUIInfo menu_bar[] =
{
	GNOMEUIINFO_MENU_FILE_TREE( file_menu ),
	GNOMEUIINFO_MENU_EDIT_TREE( edit_menu ),
	GNOMEUIINFO_MENU_SETTINGS_TREE( settings_menu ),
  	GNOMEUIINFO_MENU_HELP_TREE( help_menu ),
	GNOMEUIINFO_END
};

/*****************************************************************************/

/* The main process for the GNOME user interface.
*/
void rcg_main_process(
	FILE *to_reader, FILE *from_reader, ZvtTerm *terminal )
{
	RcGnomeUiCfg	*config;	/* The user interface configuration  */
	RcEngine	*engine;	/* The calculation engine itself     */
	GtkWidget	*app;		/* The main window		     */
	GtkWidget	*statusbar;	/* The statusbar		     */
	GtkWidget	*scrollbar;	/* The scrollbar		     */
	GtkWidget	*angle_radios[_RC_NUMBER_OF_ANGLE_UNITS];
					/* The angle unit menu items	     */
	GtkWidget	*hbox;
	int i;

	g_assert( to_reader );
	g_assert( from_reader );
	g_assert( terminal );

	
	/* Create and initialise the engine.
	*/
	engine = rc_engine_new();
	rc_engine_set_streams( engine, to_reader, to_reader );
	rc_engine_set_help_func( engine, (RcDisplayHelpFunc)gnome_help_display );
	
	rc_engine_load_state( engine );

	/* Create and load the user interface configuration.
	*/
	config = rcg_conf_new();
	rcg_conf_load_state( config );

	
	/* Create the main window.
	*/
	app = gnome_app_new( PACKAGE, PACKAGE );
	gtk_container_border_width( GTK_CONTAINER(app), 0 );
	gtk_signal_connect(
		GTK_OBJECT(app), "delete_event",
		GTK_SIGNAL_FUNC(close_app), app );

	gtk_object_set_data( GTK_OBJECT(app), "config", config );
	gtk_object_set_data( GTK_OBJECT(app), "engine", engine );
	gtk_object_set_data( GTK_OBJECT(app), "terminal", terminal );
	gtk_object_set_data( GTK_OBJECT(app), "pipe_to_reader", to_reader );
	gtk_object_set_data( GTK_OBJECT(app), "pipe_from_reader", from_reader );

	gtk_input_add_full(
		fileno(from_reader), GDK_INPUT_READ,
		(GdkInputFunction)reader_data_available,
		NULL, app, NULL );

	/* Create and attach the statusbar. */
	/* We don't want it but GnomeUI complains if it ain't there */
	statusbar = gnome_appbar_new( FALSE, TRUE, GNOME_PREFERENCES_NEVER );
	gnome_app_set_statusbar( GNOME_APP(app), GTK_WIDGET(statusbar) );

	/* Create and attach the menus. */
	gnome_app_create_menus_with_data( GNOME_APP(app), menu_bar, app );
	gnome_app_install_menu_hints( GNOME_APP(app), menu_bar );

	for( i=0; i<_RC_NUMBER_OF_ANGLE_UNITS; i++ )
	{
		angle_radios[i] = angle_unit_radios[i].widget;
	}
	gtk_object_set_data( GTK_OBJECT(app), "angle_radios", angle_radios );
	
	/* Create a horizontal box to hold the terminal and the scrollbar. */
	hbox = gtk_hbox_new( FALSE, 0 );
	gtk_box_set_spacing( GTK_BOX(hbox), 1 );
	gtk_container_border_width( GTK_CONTAINER(hbox), 0 );
	gtk_widget_show( hbox );
	gnome_app_set_contents( GNOME_APP(app), hbox );

	/* Create the scrollbar and set it and the terminal up */
	scrollbar = gtk_vscrollbar_new( terminal->adjustment );
	gtk_object_set_data( GTK_OBJECT(app), "scrollbar", scrollbar );

	gtk_box_pack_start( GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0 );
	gtk_box_pack_start( GTK_BOX(hbox), GTK_WIDGET(terminal), TRUE, TRUE, 0);
	gtk_signal_connect(
		GTK_OBJECT(terminal), "event",
		GTK_SIGNAL_FUNC(gobble_bad_keystrokes), app );
	gtk_signal_connect_after(
		GTK_OBJECT(terminal), "realize",
		GTK_SIGNAL_FUNC(apply_ui_config), app );
	gtk_signal_connect_after(
		GTK_OBJECT(app), "realize",
		GTK_SIGNAL_FUNC(apply_engine_state), app );

	gtk_widget_show( GTK_WIDGET(terminal) );
	gtk_widget_show( app );
	gtk_widget_hide( statusbar );
	gtk_widget_grab_focus( GTK_WIDGET(terminal) );


	/* Run the application.
	*/
	rc_debug( RC_DBG_GNOMEUI, "entering gtk_main()" );
	gtk_main();
	rc_debug( RC_DBG_GNOMEUI, "left gtk_main()" );

	
	/* Save the states and free the engine.
	*/
	rcg_conf_save_state( config );
	rcg_conf_delete( config );
	rc_engine_save_state( engine );
	rc_engine_delete( engine );
}

/*** end of gnome/app.c ******************************************************/
