//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// tvwidgets2.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Copyright (C) 2000-2005 Vincent LE PRINCE
// This file is part of the TRUEVISION Package

//   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.  */ 
//*******************************************************************************************
#include "math.h"
#include "include/tvwidgets.h"
#include "include/preferences.h"
#include "include/dlgutils.h"
#include "include/tvio.h"

//*********************************************************
// Classe rotation
//*********************************************************
TvWidget_rotation::TvWidget_rotation( TvWidget_rotation & ref ) : TvWidget( ref )
{
memcpy( data, ref.data, sizeof(float)*3 );
for ( int i = 0 ; i < 3 ; i++ ) spins[i] = NULL;
}

void TvWidget_rotation::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 0 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_box_pack_start( GTK_BOX(widget), label, FALSE, FALSE, 0 );

GtkObject *adj;
for ( int i = 0 ; i < 3 ; i++ )
	{
	adj = gtk_adjustment_new( data[i], -360, 360, 1, 10, 2 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spins[i]), TRUE );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );	
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
pack_widget( box, tt, spins[0] );
}

void TvWidget_rotation::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_hbox_new( FALSE, 0 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_alignment( GTK_MISC(label), 0, 0.5 );

GtkObject *adj;
for ( int i = 0 ; i < 3 ; i++ )
	{
	adj = gtk_adjustment_new( data[i], -360, 360, 1, 10, 2 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spins[i]), TRUE );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );	
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
set_tooltip( widget );
}


void TvWidget_rotation::reduce_size()
{
for ( int i = 0 ; i < 3 ; i++ )
	if ( GTK_IS_WIDGET(spins[i] ) ) gtk_widget_set_size_request( spins[i], 10, -1 );
}

void TvWidget_rotation::flush() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
for ( int i = 0 ; i < 3 ; i++ )
	{
	data[i] = gtk_spin_button_get_value_as_float( GTK_SPIN_BUTTON(spins[i]) );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
}

void TvWidget_rotation::gl_rotate() 
{
glRotatef( data[0], 1, 0, 0 );
glRotatef( data[1], 0, 1, 0 );
glRotatef( data[2], 0, 0, 1 );
}

void TvWidget_rotation::save( ofstream & file )
{
TvWidget::save( file );
file << "X=" << data[0] << " Y=" << data[1] << " Z=" << data[2];
file << "} ";
}

bool TvWidget_rotation::load( ifstream & file, char * tag )
{
if ( strcmp( sname, tag ) ) return false;
char * val = (char*)sname;
while ( val != NULL )
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;

	if ( ! strcmp( val, "X" ) ) { data[0] = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "Y" ) ) { data[1] = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "Z" ) ) { data[2] = tvio_get_value_as_float( file ); continue; }
	}
return true;
}


//*********************************************************
// Classe transformation
//*********************************************************
TvWidget_transformation::TvWidget_transformation(  const char *name,  const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
transfo = new TvWidget_bool_activator( N_("Use transformation"), "USED", NULL, app_ref, false );
scale = new TvWidget_point( N_("Scale"), "SCALE", NULL, app_ref );
scale->set( 1, 1, 1 );
translate = new TvWidget_point( N_("Translate"), "TRANSL", NULL, app_ref );
translate->set( 0, 0, 0 );
rotate = new TvWidget_rotation( N_("Rotate"), "ROT", NULL, app_ref );
rotate->set( 0, 0, 0 );
}

TvWidget_transformation::TvWidget_transformation( TvWidget_transformation & ref )
	: TvWidget( ref )
{
transfo = new TvWidget_bool_activator( *ref.transfo );
scale = new TvWidget_point( *ref.scale );
translate = new TvWidget_point( *ref.translate );
rotate = new TvWidget_rotation( *ref.rotate );
}

TvWidget_transformation::~TvWidget_transformation()
{
delete transfo;
delete scale;
delete translate;
delete rotate;
}

void TvWidget_transformation::get_widget_wnframe( GtkWidget *box, bool tt, bool frame )
{
if ( frame ) widget = dlg_simple_box_frame( _(name), box );
else 
	{
	widget = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(box), widget, FALSE, TRUE, 2 );
	}
transfo->get_widget_no_toggle( widget, tt );

GtkWidget *tbox = new_table_no_frame( widget, 3 );
transfo->set_target( tbox );
	translate->get_widget( tbox, tt, 1 );
	translate->reduce_size();
	scale->get_widget( tbox, tt, 2 );
	scale->reduce_size();
	rotate->get_widget( tbox, tt, 3 );
	rotate->reduce_size();
transfo->update_widget();
}

void TvWidget_transformation::clear_widget()
{
TvWidget::clear_widget();
transfo->clear_widget();
scale->clear_widget();
translate->clear_widget();
rotate->clear_widget();
}

void TvWidget_transformation::flush()
{
if ( widget == NULL ) return;
TvWidget::flush();
transfo->flush();
scale->flush();
translate->flush();
rotate->flush();
}

void TvWidget_transformation::output_to_povray( ofstream & file )
{
if ( !transfo->value() ) return;
file << " translate ";
translate->output_to_povray( file );
file << " scale ";
scale->output_to_povray( file );
file << " rotate ";
rotate->output_to_povray( file );
}

void TvWidget_transformation::save( ofstream & file )
{
if ( !transfo->value() ) return;
TvWidget::save( file );
rotate->save( file ); 
scale->save( file );
translate->save( file );
file << "} ";
}

bool TvWidget_transformation::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;
transfo->set( true );

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( rotate->load( file , tag ) ) continue;
	if ( scale->load( file , tag ) ) continue;
	if ( translate->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}

//*********************************************************
// Classe axis
//*********************************************************
const int axis_num = 3;
const char *axis_list[axis_num] = { "x", "y", "z" };

TvWidget_axis::TvWidget_axis(  const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
axis = new TvWidget_option_combo( name, "DIR", NULL, app_ref );
axis->set_list( axis_list, axis_num, 0 );
offset = new TvWidget_float( N_("Value"), "VAL", NULL, app_ref, 2.0 );
}

TvWidget_axis::TvWidget_axis( TvWidget_axis & ref )
	: TvWidget( ref )
{
axis = new TvWidget_option_combo( *ref.axis );
offset = new TvWidget_float( *ref.offset );
}

TvWidget_axis::~TvWidget_axis()
{
delete axis;
delete offset;
}

void TvWidget_axis::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_vbox_new( FALSE, 0 );
axis->get_widget( widget, tt );
offset->get_widget( widget, tt );
pack_widget( box, tt );
}

void TvWidget_axis::get_widget( GtkWidget *tab, bool tt, int row )
{
axis->get_widget( tab, tt, row );
offset->get_widget( tab, tt, row+1 );
}

void TvWidget_axis::clear_widget()
{
TvWidget::clear_widget();
axis->clear_widget();
offset->clear_widget();
}

void TvWidget_axis::flush()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
axis->flush();
offset->flush();
}

void TvWidget_axis::output_to_povray( ofstream & file )
{
file << axis_list[axis->value()] << "*";
offset->output_to_povray( file );
}

void TvWidget_axis::save( ofstream & file )
{
TvWidget::save( file );
axis->save( file );
offset->save( file );
file << "} ";
}

bool TvWidget_axis::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( axis->load( file , tag ) ) continue;
	if ( offset->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}

//*********************************************************
// Classe blockpattern
//*********************************************************
const int blockpattern_type_num = 3;
const char *blockpattern_type_list[blockpattern_type_num] = { N_("Brick"), N_("Checker"), N_("Hexagon") };
const char *blockpattern_output[blockpattern_type_num] = { "brick", "checker", "hexagon" };

TvWidget_blockpattern::TvWidget_blockpattern(  const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
type = new TvWidget_option_combo( N_("Type"), "TYPE", NULL, app_ref );
type->set_list( blockpattern_type_list, blockpattern_type_num, 0 );
bricksize = new TvWidget_point( N_("Brick size"), "BSIZE", NULL, app_ref );
bricksize->set( 8, 3, 4.5 );
mortar = new TvWidget_float( N_("Mortar"), "MORTAR", NULL, app_ref, 0.5 );
mortar->set_range( 200, 0, 0.1, 5 );
func = NULL;
}

TvWidget_blockpattern::TvWidget_blockpattern( TvWidget_blockpattern & ref )
	: TvWidget( ref )
{
type = new TvWidget_option_combo( *ref.type );
bricksize = new TvWidget_point( *ref.bricksize );
mortar = new TvWidget_float( *ref.mortar );
func = NULL;
data = NULL;
}

TvWidget_blockpattern::~TvWidget_blockpattern()
{
delete type;
delete bricksize;
delete mortar;
}

void TvWidget_blockpattern::get_widget( GtkWidget *box, bool tt )
{
func = NULL;
widget = dlg_simple_box_frame( _(name), box );

type->get_widget( widget, tt );
bricksize->get_widget( widget, tt );
mortar->get_widget( widget, tt );

type_changed();
type->connect_signal( GTK_SIGNAL_FUNC(sign_blockp_type_changed), this );
}

void TvWidget_blockpattern::type_changed()
{
if ( widget == NULL ) return;
type->flush();
if ( type->value() == 0 ) 
	{
	bricksize->set_widget_active();
	mortar->set_widget_active();
	}
else
	{
	bricksize->set_widget_inactive();
	mortar->set_widget_inactive();
	}
if ( func != NULL ) (*func)( data );
}

void TvWidget_blockpattern::clear_widget()
{
widget = NULL;
func = NULL;
type->clear_widget();
bricksize->clear_widget();
mortar->clear_widget();
}

void TvWidget_blockpattern::flush()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
type->flush();
bricksize->flush();
mortar->flush();
}


void TvWidget_blockpattern::output_to_povray( ofstream & file )
{ file << blockpattern_output[type->value()]; }

void TvWidget_blockpattern::output_to_povray_options( ofstream & file )
{
if ( type->value() != 0 ) return;
file << "\n\t\t\tbrick_size ";
bricksize->output_to_povray( file );
file << "\n\t\t\tmortar ";
mortar->output_to_povray( file );
}

void TvWidget_blockpattern::save( ofstream & file )
{
TvWidget::save( file );
type->save( file );
bricksize->save( file );
mortar->save( file );
file << "} "; 
}

bool TvWidget_blockpattern::load( ifstream & file, char *ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( type->load( file , tag ) ) continue;
	if ( bricksize->load( file , tag ) ) continue;
	if ( mortar->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}


//*********************************************************
// Classe noise
//*********************************************************
TvWidget_noise::TvWidget_noise(  const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
use = new TvWidget_bool_activator( N_("Add noise"), "USED", NULL, app_ref, false );
turbulence = new TvWidget_point( N_("Turbulence"), "TURB",  NULL, app_ref );
turbulence->set( 0, 0, 0 );
octaves = new TvWidget_int( N_("Octaves"), "OCT", NULL, app_ref, 6 );
octaves->set_range( 10, 0, 1 );
omega = new TvWidget_float( N_("Omega"), "OMEG", NULL, app_ref, 0.5 );
omega->set_range( 20, 0, 0.1, 3 );
lambda = new TvWidget_float( N_("Lambda"), "LAMB", NULL, app_ref, 2.0 );
lambda->set_range( 20, 0, 0.1, 3 );
noisegen = new TvWidget_noise_generator( N_("Noise generator"), "NOISEGEN", NULL, app_ref );
}

TvWidget_noise::TvWidget_noise( TvWidget_noise & ref )
	: TvWidget( ref )
{
use = new TvWidget_bool_activator( *ref.use );
turbulence = new TvWidget_point( *ref.turbulence );
octaves = new TvWidget_int( *ref.octaves );
octaves->set_range( 10, 0, 1 );
omega = new TvWidget_float( *ref.omega );
lambda = new TvWidget_float( *ref.lambda );
noisegen = new TvWidget_noise_generator( *ref.noisegen );
}

TvWidget_noise::~TvWidget_noise()
{
delete use;
delete turbulence;
delete octaves;
delete omega;
delete lambda;
delete noisegen;
}

void TvWidget_noise::get_widget_wnframe( GtkWidget *box, bool tt, bool frame )
{
if ( frame ) widget = dlg_simple_box_frame( _(name), box );
else 
	{
	widget = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(box), widget, FALSE, TRUE, 2 );
	}
use->get_widget_no_toggle( widget, tt );

GtkWidget *tbox = new_table_no_frame( widget, 5 );
use->set_target( tbox );
	turbulence->get_widget( tbox, tt, 1 );
	turbulence->reduce_size();
	octaves->get_widget( tbox, tt, 2 );
	lambda->get_widget( tbox, tt, 3 );
	omega->get_widget( tbox, tt, 4 );
	noisegen->get_widget( tbox, tt, 5 );
use->update_widget();
}

void TvWidget_noise::clear_widget()
{
TvWidget::clear_widget();
use->clear_widget();
turbulence->clear_widget();	
octaves->clear_widget();
lambda->clear_widget();
omega->clear_widget();
noisegen->clear_widget();
}

void TvWidget_noise::flush()
{
if ( ! GTK_IS_WIDGET(widget) )  return;
TvWidget::flush();
use->flush();
turbulence->flush();
octaves->flush();
lambda->flush();
omega->flush();
noisegen->flush();
}

void TvWidget_noise::output_to_povray( ofstream & file )
{
if ( !use->value() ) return;
file << "\n\t\t turbulence ";
turbulence->output_to_povray( file );
file << "\n\t\t octaves ";
octaves->output_to_povray( file );
file << "\n\t\t lambda ";
lambda->output_to_povray( file );
file << "\n\t\t omega ";
omega->output_to_povray( file );
noisegen->output_to_povray( file );
}

void TvWidget_noise::save( ofstream & file )
{
if ( !use->value() ) return;
TvWidget::save( file );
turbulence->save( file );
octaves->save( file );
lambda->save( file );
omega->save( file );
noisegen->save( file );
file << "} ";
}

bool TvWidget_noise::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;
use->set( true );

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( turbulence->load( file , tag ) ) continue;
	if ( octaves->load( file , tag ) ) continue;
	if ( lambda->load( file , tag ) ) continue;
	if ( omega->load( file , tag ) ) continue;
	if ( noisegen->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}

//*********************************************************
// Classe warp
//*********************************************************
const int warp_num = 7;
const char *warp_list[warp_num] = { N_("Black hole warp"), N_("Repeat warp"), N_("Turbulence warp"),
															 N_("Cylindrical"), N_("Spherical"), N_("Toroidal"), N_("Planar") };

TvWidget_warp::TvWidget_warp(  const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
changing_box = NULL;
use = new TvWidget_bool_activator( N_("Use warp"), "USED", NULL, app_ref, false );
type = new TvWidget_option_combo( N_("Type"), "TYPE", NULL, app_ref );
type->set_list( warp_list, warp_num, 0 );

center = new TvWidget_point( N_("Center"), "CENTER", NULL, app_ref );
center->set( 0, 0, 0 );
repeat = new TvWidget_point( N_("Repeat"), "REPEAT", NULL, app_ref );
repeat->set( 0, 0, 0 );
radius = new TvWidget_float( N_("Radius"), "RADIUS", NULL, app_ref, 0.5 );
strength = new TvWidget_float( N_("Strength"), "STRENGTH", NULL, app_ref, 1.0 );
falloff = new TvWidget_float( N_("Falloff"), "FALLOFF", NULL, app_ref, 1.0 );
inverse = new TvWidget_bool( N_("Inverse"), "INVERSE", NULL, app_ref, false );

repeat_axis = new TvWidget_axis( N_("Direction"), "DIR", NULL, app_ref );
offset = new TvWidget_point( N_("Offset"), "OFFSET", NULL, app_ref );
offset->set( 0, 0, 0 );
flip = new TvWidget_point( N_("Flip"), "FLIP", NULL, app_ref );
flip->set( 0, 0, 0 );

turbulence = new TvWidget_point( N_("Turbulence"), "TURB", NULL, app_ref );
turbulence->set( 0, 0, 0 );
octaves = new TvWidget_int( N_("Octaves"), "OCT", NULL, app_ref, 6 );
omega = new TvWidget_float( N_("Omega"), "OMEG", NULL, app_ref, .05 );
lambda = new TvWidget_float( N_("Lambda"), "LAMB", NULL, app_ref, 2.0 );

dist_exp = new TvWidget_int( N_("Distance exponant"), "DISTEXP", NULL, app_ref, 0 );
dist_exp->set_range( 10, 0, 1 );
major_radius = new TvWidget_float( N_("Major radius"), "MRAD", NULL, app_ref, 1 );
major_radius->set_range( 10, 0, 0.1, 2 );
direction = new TvWidget_point( N_("Orientation"), "ORIENT", NULL, app_ref );
direction->set( 0, 0, 1 );
normal = new TvWidget_point( N_("Normal"), "NORMAL", NULL, app_ref );
normal->set( 0, 0, 1 );
distance = new TvWidget_float( N_("Distance"), "DISTANCE", NULL, app_ref, 0 );
distance->set_range( 30, -30, 0.1, 2 );
}

TvWidget_warp::TvWidget_warp( TvWidget_warp & ref )
	: TvWidget( ref )
{
changing_box = NULL;
use = new TvWidget_bool_activator( *ref.use );
type = new TvWidget_option_combo( *ref.type );

center = new TvWidget_point( *ref.center );
repeat = new TvWidget_point( *ref.repeat );
radius = new TvWidget_float( *ref.radius );
strength = new TvWidget_float( *ref.strength );
falloff = new TvWidget_float( *ref.falloff );
inverse = new TvWidget_bool( *ref.inverse );
repeat_axis = new TvWidget_axis( *ref.repeat_axis );
offset = new TvWidget_point( *ref.offset );
flip = new TvWidget_point( *ref.flip );
turbulence = new TvWidget_point( *ref.turbulence );
octaves = new TvWidget_int( *ref.octaves );
omega = new TvWidget_float( *ref.omega );
lambda = new TvWidget_float( *ref.lambda );
direction = new TvWidget_point( *ref.direction );
dist_exp = new TvWidget_int( *ref.dist_exp );
major_radius = new TvWidget_float( *ref.major_radius );
normal = new TvWidget_point( *ref.normal );
distance = new TvWidget_float( *ref.distance );
}

void TvWidget_warp::copy( TvWidget *wo )
{
TvWidget_warp *wr = (TvWidget_warp*)wo;
use->copy( wr->use );
type->copy( wr->type );
center->copy( wr->center );
repeat->copy( wr->repeat );
radius->copy( wr->radius );
strength->copy( wr->strength );
falloff->copy( wr->falloff );
inverse->copy( wr->inverse );
repeat_axis->copy( wr->repeat_axis );
offset->copy( wr->offset );
flip->copy( wr->flip );
turbulence->copy( wr->turbulence );
octaves->copy( wr->octaves );
omega->copy( wr->omega );
lambda->copy( wr->lambda );
direction->copy( wr->direction );
dist_exp->copy( wr->dist_exp );
major_radius->copy( wr->major_radius ) ;
normal->copy( wr->normal );
distance->copy( wr->distance );
}

TvWidget_warp::~TvWidget_warp()
{
delete use;
delete type;
delete center;
delete repeat;
delete radius;
delete strength;
delete falloff;
delete inverse;
delete repeat_axis;
delete offset;
delete flip;
delete turbulence;
delete octaves;
delete omega;
delete lambda;
delete direction;
delete dist_exp;
delete major_radius;
delete normal;
delete distance;
}

void TvWidget_warp::get_widget_wnframe( GtkWidget *box, bool tt, bool frame )
{
changing_box = NULL;
if ( frame ) widget = dlg_simple_box_frame( _(name), box );
else 
	{
	widget = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(box), widget, FALSE, TRUE, 2 );
	}
use->get_widget_no_toggle( widget, tt );

activated_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(widget), activated_box, FALSE, FALSE, 0 );
use->set_target( activated_box );
	type->get_widget( activated_box, tt );
	type->connect_signal( GTK_SIGNAL_FUNC(sign_warptype_changed), this );
	type_changed();
use->update_widget();
}


void TvWidget_warp::type_changed()
{
bool tt = true;
type->flush();
if ( changing_box != NULL )
	{
	gtk_widget_destroy( changing_box );
	center->clear_widget();
	repeat->clear_widget();
	radius ->clear_widget();
	strength->clear_widget();
	falloff->clear_widget();
	inverse->clear_widget();
	offset->clear_widget();
	flip->clear_widget();
	turbulence->clear_widget();
	octaves->clear_widget();
	lambda->clear_widget();
	omega->clear_widget();
	direction->clear_widget();
	dist_exp->clear_widget();
	major_radius->clear_widget();
	normal->clear_widget();
	distance->clear_widget();
	}
changing_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(activated_box), changing_box, FALSE, FALSE, 0 );
	
GtkWidget *table;
switch( type->value() )
	{
	case 0:
		table = new_table_no_frame( changing_box, 7 );
		center->get_widget( table, tt, 1 );
		center->reduce_size();
		radius->get_widget( table, tt, 2 );
		falloff->get_widget( table, tt, 3 );
		strength->get_widget( table, tt, 4 );
		repeat->get_widget( table, tt, 5 );
		repeat->reduce_size();
		turbulence->get_widget( table, tt, 6 );
		turbulence->reduce_size();
		inverse->get_widget( table, tt, 7 );
		break;

	case 1:
		table = new_table_no_frame( changing_box, 4 );
		repeat_axis->get_widget( table, tt, 1 );
		offset->get_widget( table, tt, 3 );
		offset->reduce_size();
		flip->get_widget( table, tt, 4 );
		flip->reduce_size();
		break;
	
	case 2:
		table = new_table_no_frame( changing_box, 4 );
		turbulence->get_widget( table, tt, 1 );
		turbulence->reduce_size();
		octaves->get_widget( table, tt, 2 );
		lambda->get_widget( table, tt, 3 );
		omega->get_widget( table, tt, 4 );
		break;

	case 3:
	case 4:
		table = new_table_no_frame( changing_box, 2 );
		direction->get_widget( table, tt, 1 );
		direction->reduce_size();
		dist_exp->get_widget( table, tt, 2 );
		break;

	case 5:
		table = new_table_no_frame( changing_box, 3 );
		direction->get_widget( table, tt, 1 );
		direction->reduce_size();
		dist_exp->get_widget( table, tt, 2 );
		major_radius->get_widget( table, tt, 3 );
		break;

	case 6:
		table = new_table_no_frame( changing_box, 2 );
		normal->get_widget( table, tt, 1 );
		normal->reduce_size();
		distance->get_widget( table, tt, 2 );
		break;

	default:
		break;
	}
gtk_widget_show_all( changing_box );
}


void TvWidget_warp::clear_widget()
{
widget = NULL;
use->clear_widget();
type->clear_widget();
center->clear_widget();
repeat->clear_widget();
radius ->clear_widget();
strength->clear_widget();
falloff->clear_widget();
inverse->clear_widget();
repeat_axis->clear_widget();
offset->clear_widget();
flip->clear_widget();
turbulence->clear_widget();
octaves->clear_widget();
lambda->clear_widget();
omega->clear_widget();
direction->clear_widget();
dist_exp->clear_widget();
major_radius->clear_widget();
normal->clear_widget();
distance->clear_widget();
}

void TvWidget_warp::flush()
{
if ( widget == NULL ) return;
TvWidget::flush();
use->flush();
center->flush();
repeat->flush();
radius ->flush();
strength->flush();
falloff->flush();
type->flush();
inverse->flush();
repeat_axis->flush();
offset->flush();
flip->flush();
turbulence->flush();
octaves->flush();
lambda->flush();
omega->flush();
direction->flush();
dist_exp->flush();
major_radius->flush();
normal->flush();
distance->flush();
}

void TvWidget_warp::output_to_povray( ofstream & file )
{
if ( !use->value() ) return;
file << "\n\t\t warp {";
switch( type->value() )
	{
	case 0:
		file << "\n\t\t\tblack_hole ";
		center->output_to_povray( file );
		file << " , ";
		radius->output_to_povray( file );
		file << "\n\t\t\tfalloff ";
		falloff->output_to_povray( file );
		file << "\n\t\t\tstrength ";
		strength->output_to_povray( file );
		file << "\n\t\t\trepeat ";
		repeat->output_to_povray( file );
		file << "\n\t\t\tturbulence ";
		turbulence->output_to_povray( file );
		if ( inverse->value() ) file << "\n\t\t\tinverse";
		break;

	case 1:
		file << "\n\t\t\trepeat ";
		repeat_axis->output_to_povray( file );
		file << "\n\t\t\toffset ";
		offset->output_to_povray( file );
		file << "\n\t\t\tflip ";
		flip->output_to_povray( file );
		break;

	case 2:
		file << "\n\t\t\tturbulence ";
		turbulence->output_to_povray( file );
		file << "\n\t\t\toctaves ";
		octaves->output_to_povray( file );
		file << "\n\t\t\tlambda ";
		lambda->output_to_povray( file );
		file << "\n\t\t\tomega ";
		omega->output_to_povray( file );
		break;

	case 3:
		file << "\n\t\tcylindrical ";
		file << "\n\t\t\tdist_exp ";
		dist_exp->output_to_povray( file );
		break;

	case 4:
		file << "\n\t\tspherical ";
		file << "\n\t\t\tdist_exp ";
		dist_exp->output_to_povray( file );
		break;

	case 5:
		file << "\n\t\ttoroidal ";
		file << "\n\t\t\tdist_exp ";
		dist_exp->output_to_povray( file );
		break;

	case 6:
		file << "\n\t\tplanar ";
		normal->output_to_povray( file );
		file << ",";
		distance->output_to_povray( file );
	break;

	}
file << "\n\t\t}";	
}


void TvWidget_warp::save( ofstream & file )
{
if ( !use->value() ) return;
TvWidget::save( file );
type->save( file );
center->save( file );
radius->save( file );
falloff->save( file );
strength->save( file );
repeat->save( file );
inverse->save( file ) ;
repeat_axis->save( file );
offset->save( file );
flip->save( file );
turbulence->save( file );
octaves->save( file );
lambda->save( file );
omega->save( file );
direction->save( file );
dist_exp->save( file );
major_radius->save( file );
normal->save( file );
distance->save( file );
file << "} ";	
}

bool TvWidget_warp::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( type->load( file , tag ) ) continue;
	if ( center->load( file , tag ) ) continue;
	if ( radius->load( file , tag ) ) continue;
	if ( falloff->load( file , tag ) ) continue;
	if ( strength->load( file , tag ) ) continue;
	if ( repeat->load( file , tag ) ) continue;
	if ( inverse->load( file , tag ) ) continue;
	if ( repeat_axis->load( file , tag ) ) continue;
	if ( offset->load( file , tag ) ) continue;
	if ( flip->load( file , tag ) ) continue;
	if ( turbulence->load( file , tag ) ) continue;
	if ( octaves->load( file , tag ) ) continue;
	if ( lambda->load( file , tag ) ) continue;
	if ( omega->load( file , tag ) ) continue;
	if ( direction->load( file , tag ) ) continue;
	if ( dist_exp->load( file , tag ) ) continue;
	if ( major_radius->load( file , tag ) ) continue;
	if ( normal->load( file , tag ) ) continue;
	if ( distance->load( file , tag ) ) continue;
	
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}


//*********************************************************
// Classe blendmap
//*********************************************************
const int blendmap_num = 25;
const char *blendmap_list[blendmap_num] = {
	N_("Agate"),
	N_("Boxed"),
	N_("Bozo / Spotted"),
	N_("Bumps"),
	N_("Crackle"),
	N_("Cylindrical"),
	N_("Dents"),
	N_("Gradient"),
	N_("Granite"),
	N_("Leopard"),
	N_("Mandel"),
	N_("Marble"),
	N_("Onion"),
	N_("Planar"),
	N_("Quilted"),
	N_("Radial"),
	N_("Ripples"),
	N_("Spherical"),
	N_("Spiral 1"),
	N_("Spiral 2"),
	N_("Waves"),
	N_("Wood"),
	N_("Wrinckles"),
	N_("Density file"),
	N_("Average"),
	};

const char *blendmap_keyword[blendmap_num] = {
	"agate", "boxed", "bozo", "bumps", "crackle", "cylindrical", "dents", "gradient",
	"granite", "leopard", "mandel", "marble", "onion", "planar", "quilted", "radial", "ripples",
	"spherical", "spiral1", "spiral2", "waves", "wood", "wrinkles", "density_file", "average",
	};

const int bfiletype_num = 1;
const char *bfiletype_ext[bfiletype_num] = { "df3" };
const char *bfiletype_def[bfiletype_num] = { " df3 " };

TvWidget_blendmap::TvWidget_blendmap(  const char *name, const char *sname, const char *tooltip, app_objs *appref, bool is_colmap  )
 : TvWidget( name, sname, tooltip, appref )
{
option_box = NULL;
colmap_mode = is_colmap;

type = new TvWidget_option_combo( N_("Type"), "TYPE", NULL, app_ref );
type->set_list( blendmap_list, colmap_mode ? blendmap_num-1 : blendmap_num , 0 );
agate_turb = new TvWidget_float( N_("Agate turbulence"), "ATURB", NULL, app_ref, 0.5 );
agate_turb->set_range( 5, 0, 0.1, 2 );
gradient_dir = new TvWidget_point( N_("Orientation"), "ORIENT", NULL, app_ref );
gradient_dir->set( 1, 1, 1 );
mandel_iterations = new TvWidget_int( N_("Mandel max iterations"), "MANDMAX", NULL, app_ref, 10 );
mandel_iterations->set_range( 256, 10, 1 );
quilted_control0 = new TvWidget_float( N_("Control 0"), "CTRL0", NULL, app_ref, 1 );
quilted_control0->set_range( 1, 0, 0.01, 3 );
quilted_control1 = new TvWidget_float( N_("Control 1"), "CTRL1", NULL, app_ref, 1 );
quilted_control1->set_range( 1, 0, 0.01, 3 );
spiral_arms = new TvWidget_int( N_("Spiral arms"), "SPIRARMS", NULL, app_ref, 4 );
spiral_arms->set_range( 20, 1, 1 );
density_file = new TvWidget_file( N_("File"), "FILE", NULL, app_ref );
density_file->set_list( bfiletype_ext, bfiletype_def, bfiletype_num );
interpolate = new TvWidget_bool( N_("Interpolate"), "INTERP", NULL, app_ref, false );
frequency = new TvWidget_float( N_("Frequency"), "FREQ", NULL, app_ref, 1.0 );
frequency->set_range( 1000, -1000, 0.1, 3 );
phase = new TvWidget_float( N_("Phase"), "PHASE", NULL, app_ref, 0 );
phase->set_range( 1000, -1000, 0.1, 3 );}

TvWidget_blendmap::TvWidget_blendmap( TvWidget_blendmap & ref ) : TvWidget( ref )
{
option_box = NULL;
type = new TvWidget_option_combo( *ref.type );
agate_turb= new TvWidget_float( *ref.agate_turb );
gradient_dir = new TvWidget_point( *ref.gradient_dir );
mandel_iterations = new TvWidget_int( *ref.mandel_iterations );
quilted_control0 = new TvWidget_float( *ref.quilted_control0 );
quilted_control1 = new TvWidget_float( *ref.quilted_control1 );
spiral_arms = new TvWidget_int( *ref.spiral_arms );
density_file = new TvWidget_file( *ref.density_file );
interpolate = new TvWidget_bool( *ref.interpolate );
frequency = new TvWidget_float( *ref.frequency );
phase = new TvWidget_float( *ref.phase );
}

void TvWidget_blendmap::copy( TvWidget *to )
{
TvWidget_blendmap *tm = (TvWidget_blendmap*)to;
type->copy( tm->type );
agate_turb->copy( tm->agate_turb );
gradient_dir->copy( tm->gradient_dir );
mandel_iterations->copy( tm->mandel_iterations );
quilted_control0->copy( tm->quilted_control0 );
quilted_control1->copy( tm->quilted_control1 );
spiral_arms->copy( tm->spiral_arms );
density_file->copy( tm->density_file );
interpolate->copy( tm->interpolate );
frequency->copy( tm->frequency );
phase->copy( tm->phase );
}


TvWidget_blendmap::~TvWidget_blendmap()
{
delete type;
delete agate_turb;
delete gradient_dir;
delete mandel_iterations;
delete quilted_control0;
delete quilted_control1;
delete spiral_arms;
delete density_file;
delete interpolate;
delete frequency;
delete phase;
}

void TvWidget_blendmap::get_widget( GtkWidget *box, bool tt )
{
option_box = NULL;
widget = dlg_simple_box_frame( _(name), box );
type->get_widget( widget, tt );
type->connect_signal( GTK_SIGNAL_FUNC(sign_blendtype_changed), this );
type_changed();
}

void TvWidget_blendmap::type_changed()
{
if ( option_box != NULL )
	{
	agate_turb->clear_widget();
	gradient_dir->clear_widget();
	mandel_iterations->clear_widget();
	quilted_control0->clear_widget();
	quilted_control1->clear_widget();
	spiral_arms->clear_widget();
	density_file->clear_widget();
	interpolate->clear_widget();
	frequency->clear_widget();
	phase->clear_widget();
	gtk_widget_destroy( option_box );
	}
option_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(widget), option_box, TRUE, TRUE, 0 );

type->flush();
bool tt = true;
switch ( type->value() )
	{
	case 0:
		agate_turb->get_widget( option_box, tt );
		break;

	case 7:
		gradient_dir->get_widget( option_box, tt );
		break;

	case 10:
		mandel_iterations->get_widget( option_box, tt );
		break;

	case 14:
		quilted_control0->get_widget( option_box, tt );
		quilted_control1->get_widget( option_box, tt );
		break;

	case 18:
	case 19:
		spiral_arms->get_widget( option_box, tt );
		break;

	case 23:
		density_file->get_widget( option_box, tt );
		interpolate->get_widget( option_box, tt );
		break;
		
	case 16:
	case 20:
		frequency->get_widget( option_box, tt );
		phase->get_widget( option_box, tt );
		break;
	}
gtk_widget_show_all( option_box );
}

void TvWidget_blendmap::clear_widget()
{
TvWidget::clear_widget();
type->clear_widget();
agate_turb->clear_widget();
gradient_dir->clear_widget();
mandel_iterations->clear_widget();
quilted_control0->clear_widget();
quilted_control1->clear_widget();
spiral_arms->clear_widget();
density_file->clear_widget();
interpolate->clear_widget();
frequency->clear_widget();
phase->clear_widget();
}

void TvWidget_blendmap::flush()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
type->flush();
agate_turb->flush();
gradient_dir->flush();
mandel_iterations->flush();
quilted_control0->flush();
quilted_control1->flush();
spiral_arms->flush();
density_file->flush();
interpolate->flush();
frequency->flush();
phase->flush();
}

void TvWidget_blendmap::output_to_povray( ofstream & file )
{
file << blendmap_keyword[type->value()];
switch ( type->value() )
	{
	case 0:
		file << " agate_turb ";
		agate_turb->output_to_povray( file );
		break;

	case 7:
		file << " ";
		gradient_dir->output_to_povray( file );
		break;

	case 10:
		file << " ";
		mandel_iterations->output_to_povray( file );
		break;

	case 14:
		file << " control0 ";
		quilted_control0->output_to_povray( file );
		file << " control1 ";
		quilted_control1->output_to_povray( file );
		break;

	case 18:
	case 19:
		file << " ";
		spiral_arms->output_to_povray( file );
		break;

	case 23:
		//file << " df3 ";
		density_file->output_to_povray( file );
		if ( interpolate->value() == true ) file << "\n\t\t\tinterpolate 1";
		break;
		
	case 16:
	case 20:
		file << " frequency " << frequency->value();
		file << " phase " << phase->value();
		break;
	}
}


void TvWidget_blendmap::save( ofstream & file )
{
TvWidget::save( file );
type->save( file );
agate_turb->save( file );
gradient_dir->save( file );
mandel_iterations->save( file );
quilted_control0->save( file );
quilted_control1->save( file );
spiral_arms->save( file );
density_file->save( file );
interpolate->save( file );
frequency->save( file );
phase->save( file );
file << "} ";
}

bool TvWidget_blendmap::load( ifstream & file, char *ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( type->load( file , tag ) ) continue;
	if ( agate_turb->load( file , tag ) ) continue;
	if ( gradient_dir->load( file , tag ) ) continue;
	if ( mandel_iterations->load( file , tag ) ) continue;
	if ( quilted_control0->load( file , tag ) ) continue;
	if ( quilted_control1->load( file , tag ) ) continue;
	if ( spiral_arms->load( file , tag ) ) continue;
	if ( density_file->load( file , tag ) ) continue;
	if ( interpolate->load( file , tag ) ) continue;
	if ( frequency->load( file , tag ) ) continue;
	if ( phase->load( file , tag ) ) continue;
	
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}


//*********************************************************
// Classe blendmap modifiers
//*********************************************************
const int blendmap_mod_num = 6;
const char *blendmap_mod_list[blendmap_mod_num] = {
	N_("Cubic"),
	N_("Poly"),
	N_("Ramp"),
	N_("Scallop"),
	N_("Sine"),
	N_("Triangle")
	};

const char *blendmap_mod_keyword[blendmap_mod_num] = {
	"cubic_wave", "poly_wave", "ramp_wave", "scallop_wave", "sine_wave", "triangle_wave"
	};

TvWidget_blendmap_mod::TvWidget_blendmap_mod(  const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
use = new TvWidget_bool_activator( N_("Use modifiers"), "USED", NULL, app_ref, false );
waveform = new TvWidget_option_combo( N_("Wave"), "WAVE", NULL, app_ref );
waveform->set_list( blendmap_mod_list, blendmap_mod_num, 2 );
frequency = new TvWidget_float( N_("Frequency"), "FREQ", NULL, app_ref, 1.0 );
frequency->set_range( 1000, -1000, 0.1, 3 );
phase = new TvWidget_float( N_("Phase"), "PHASE", NULL, app_ref, 0 );
phase->set_range( 1000, -1000, 0.1, 3 );
polywave_count =  new TvWidget_float( N_("Exponent"), "EXP", NULL, app_ref, 1.0 );
polywave_count->set_range( 100, 0, 0.1, 2 );
}

TvWidget_blendmap_mod::TvWidget_blendmap_mod( TvWidget_blendmap_mod & ref ) : TvWidget( ref )
{
use = new TvWidget_bool_activator( *ref.use );
waveform = new TvWidget_option_combo( *ref.waveform );
frequency = new TvWidget_float( *ref.frequency );
phase = new TvWidget_float( *ref.phase );
polywave_count =  new TvWidget_float( *ref.polywave_count );
}


void TvWidget_blendmap_mod::copy( TvWidget *bo )
{
TvWidget_blendmap_mod *bm = (TvWidget_blendmap_mod*)bo;
use->copy( bm->use );
waveform->copy( bm->waveform );
frequency->copy( bm->frequency );
phase->copy( bm->phase );
polywave_count->copy( bm->polywave_count );
}


TvWidget_blendmap_mod::~TvWidget_blendmap_mod()
{
delete use;
delete waveform;
delete frequency;
delete phase;
delete polywave_count;
}

void TvWidget_blendmap_mod::get_widget( GtkWidget *box, bool tt )
{
widget = dlg_simple_box_frame( _(name), box );
use->get_widget_no_toggle( widget, tt );

GtkWidget *activated_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(widget), activated_box, FALSE, FALSE, 0 );
use->set_target( activated_box );
	frequency->get_widget( activated_box, tt );
	phase->get_widget( activated_box, tt );
	waveform->get_widget( activated_box, tt );
	polywave_count->get_widget( activated_box, tt );
	waveform->connect_signal( GTK_SIGNAL_FUNC(sign_wavetype_changed), this );
	wavetype_changed();
use->update_widget();
}

void TvWidget_blendmap_mod::wavetype_changed()
{
waveform->flush();
if ( waveform->value() != 1 ) polywave_count->set_widget_inactive();
else polywave_count->set_widget_active();
}

void TvWidget_blendmap_mod::clear_widget()
{
TvWidget::clear_widget();
use->clear_widget();
waveform->clear_widget();
polywave_count->clear_widget();
frequency->clear_widget();
phase->clear_widget();
}

void TvWidget_blendmap_mod::flush()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
use->flush();
waveform->flush();
polywave_count->flush();
frequency->flush();
phase->flush();
}

void TvWidget_blendmap_mod::output_to_povray( ofstream & file )
{
if ( !use->value() ) return;
file << "frequency ";
frequency->output_to_povray( file );
file << " phase ";
phase->output_to_povray( file );
file << ' ';
file << blendmap_mod_keyword[waveform->value()];
if ( waveform->value() == 1 ) { file << ' '; polywave_count->output_to_povray( file ); }
}

void TvWidget_blendmap_mod::save( ofstream & file )
{
if ( !use->value() ) return;
TvWidget::save( file );
frequency->save( file );
phase->save( file );
waveform->save( file );
polywave_count->save( file );
file << "} ";
}

bool TvWidget_blendmap_mod::load( ifstream & file, char *ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( frequency->load( file , tag ) ) continue;
	if ( phase->load( file , tag ) ) continue;
	if ( waveform->load( file , tag ) ) continue;
	if ( polywave_count->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
use->set( true );
return true;
}

//*********************************************************
// Classe file
//*********************************************************
TvWidget_file::TvWidget_file(  const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
path = new TvWidget_path( name, "PATH", NULL, appref );
store = new TvWidget_bool( N_("Store"), "STORE", NULL, app_ref, false );
type = -1;
loaded = false;
}

void TvWidget_file::set_list( const char **ext, const char **def, const int num )
{
ext_list = (char**)ext;
def_list = (char**)def;
list_num = (int)num;
}

TvWidget_file::TvWidget_file( TvWidget_file & ref ) : TvWidget( ref )
{
path = new TvWidget_path( *ref.path );
store = new TvWidget_bool( *ref.store );
loaded = false;
type = ref.type;
ext_list = (char**)ref.ext_list;
def_list = (char**)ref.def_list;
list_num = (int)ref.list_num;
}

TvWidget_file::~TvWidget_file()
{
if ( loaded == true ) unlink( path->value() );
delete path;
delete store;
}

void TvWidget_file::get_widget( GtkWidget *box, bool tt )
{
path->get_widget( box, tt );
path->connect_signal( GTK_SIGNAL_FUNC(sign_file_changed), this );
store->get_widget( box, tt );
}

void TvWidget_file::get_widget( GtkWidget *box, bool tt, int row )
{
path->get_widget( box, tt, row );
path->connect_signal( GTK_SIGNAL_FUNC(sign_file_changed), this );
store->get_widget( box, tt, row+1 );
}

void TvWidget_file::file_changed()
{
if ( in_update == true ) return;
in_update = true;
char *oldptr = path->value();
char *old = NULL;
if ( oldptr != NULL )	
	{
	old = new char[ strlen(oldptr) +1 ];
	strcpy( old, oldptr );
	}
path->flush();
char *res = path->value();
if ( old != NULL && res != NULL ) if ( ! strcmp( old, res ) ) {  delete old; in_update = false; return; }
	
if ( loaded ) unlink( old );
if ( old != NULL ) delete old;
loaded = false;
type = -1;
if ( res == NULL ) { in_update = false; return; }
int res_len = strlen( res );
if ( res_len == 0 )  { in_update = false; return; }
for ( register int i = 0 ; i < list_num ; i++ )
	{
	int ext_len = strlen( ext_list[i] );
	if ( !strcmp( ext_list[i], res + res_len - ext_len ) )
		type = i;
	}
in_update = false;
}

void TvWidget_file::clear_widget()
{
TvWidget::clear_widget();
path->clear_widget();
store->clear_widget();
}

void TvWidget_file::flush()
{
TvWidget::flush();
path->flush();
store->flush();
}

void TvWidget_file::output_to_povray( ofstream & file )
{
//file_changed();
char *res = path->value();
file << def_list[ ( type == -1 ) ? 5 : type ];
file << " \"" << res << '\"';
}

void TvWidget_file::save( ofstream & file )
{
TvWidget::save( file );
int file_size = 0;
char *fname = path->value();
if ( fname == NULL || strlen( fname ) == 0 )  { file << "SIZE=0 } "; return; }

ifstream ifile( fname );
if ( ! ifile ) { file << "SIZE=0 } "; app_warning( "Cannot open", path->value() ); return; }

ifile.seekg( 0, ios::end );
file_size = ifile.tellg();
ifile.seekg( 0, ios::beg );
file << "SIZE=" << file_size << " TYPE=" << type << " DATA=";
for ( register int i = 0 ; i < file_size ; i++ )
	file << (char)ifile.get();
file << " } ";
}

bool TvWidget_file::load( ifstream & file, char *ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * val = NULL;
int file_size = 0;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "SIZE" ) )
		{
		file_size = tvio_get_value_as_int( file );
		continue;
		}

	if ( ! strcmp( val, "TYPE" ) )
		{
		type = tvio_get_value_as_int( file );
		continue;
		}

	if ( ! strcmp( val, "DATA" ) )
		{
		char *tmpfile = get_temp_filename();
		path->set( tmpfile );
		ofstream ifile( tmpfile );
		ifile.seekp( 0, ios::beg );
		for ( register int i = 0 ; i < file_size ; i++ )
			ifile << (char)file.get();
		ifile.close();
		loaded = true;
		delete tmpfile;
		}
	}
while( val != NULL );
return true;
}

void TvWidget_file::connect_signal( GtkSignalFunc funcd , gpointer datad )
{
path->connect_signal( GTK_SIGNAL_FUNC(funcd) , datad );
store->connect_signal( GTK_SIGNAL_FUNC(funcd) , datad );
}

void TvWidget_file::swap_data( TvWidget_file *param )
{
bool swap = store->value();
store->set( param->store->value() );
param->store->set( swap );
char *swap2 = path->value();
path->set( param->path->value() );
param->path->set( swap2 );
}

//*********************************************************
// Classe bitmap
//*********************************************************
const int maptype_num = 4;
const char *maptype_list[maptype_num] = { N_("Planar"), N_("Spherical"), N_("Cylindrical"), N_("Donut") };
const int maptype_def[maptype_num] = { 0, 1, 2, 5 };
const int interpolate_num = 3;
const char *interpolate_list[interpolate_num] = { N_("None"), N_("Bilinear"), N_("Normalized") };
const int filetype_num = 8;
const char *filetype_ext[filetype_num] = { "gif", "tga", "iff", "ppm", "pgm", "png", "jpg", "tif" };
const char *filetype_def[filetype_num] = { "gif", "tga", "iff", "ppm", "pgm", "png", "jpeg", "tiff" };

TvWidget_bitmap::TvWidget_bitmap(  const char *name,  const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
file = new TvWidget_file( N_("File"), "FILE", NULL, appref );
file->set_list( filetype_ext, filetype_def, filetype_num );
filter_all = new TvWidget_percent( N_("Filter all (%)"), "FILT", NULL, app_ref, 0 );
filter_all->set_range( 1, 0, 0.01, 4 );
transmit_all = new TvWidget_percent( N_("Transmit all(%)"), "TRANS", NULL, app_ref, 0 );
transmit_all->set_range( 1, 0, 0.01, 4 );

maptype = new TvWidget_option_combo( N_("Map type"), "TYPE", NULL, app_ref );
maptype->set_list( maptype_list, maptype_num, 0 );
interpolate = new TvWidget_option_combo( N_("Interpolate"), "INTERP", NULL, app_ref );
interpolate->set_list( interpolate_list, interpolate_num, 0 );
once = new TvWidget_bool( N_("Once"), "ONCE", NULL, app_ref, false );
}

TvWidget_bitmap::TvWidget_bitmap( TvWidget_bitmap & ref ) : TvWidget( ref )
{
file = new TvWidget_file( *ref.file );
filter_all = new TvWidget_percent( *ref.filter_all );
transmit_all = new TvWidget_percent( *ref.transmit_all );
maptype = new TvWidget_option_combo( *ref.maptype );
interpolate = new TvWidget_option_combo( *ref.interpolate );
once = new TvWidget_bool( *ref.once );
}

TvWidget_bitmap::~TvWidget_bitmap()
{
delete file;
delete filter_all;
delete transmit_all;
delete maptype;
delete interpolate;
delete once;
}

void TvWidget_bitmap::get_widget( GtkWidget *box, bool tt )
{
widget = dlg_simple_box_frame( _(name), box );
file->get_widget( widget, tt );
filter_all->get_widget( widget, tt );
transmit_all->get_widget( widget, tt );
maptype->get_widget( widget, tt );
interpolate->get_widget( widget, tt );
once->get_widget( widget, tt );
}

void TvWidget_bitmap::clear_widget()
{
TvWidget::clear_widget();
file->clear_widget();
filter_all->clear_widget();
transmit_all->clear_widget();
maptype->clear_widget();
interpolate->clear_widget();
once->clear_widget();
}

void TvWidget_bitmap::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
file->flush();
filter_all->flush();
transmit_all->flush();
maptype->flush();
interpolate->flush();
once->flush();
}

void TvWidget_bitmap::output_to_povray_nodecl( ofstream & ofile )
{
file->output_to_povray( ofile );
if ( once->value() ) ofile << "\n\t\t\t once";
if ( interpolate->value() != 0 ) ofile << "\n\t\t\t interpolate " << interpolate->value()*2;
ofile << "\n\t\t\t map_type " << maptype_def[maptype->value()];
ofile << "\n\t\t\tfilter all " << filter_all->value();
ofile <<  "\n\t\t\ttransmit all " << transmit_all->value();
}

void TvWidget_bitmap::output_to_povray( ofstream & ofile )
{
ofile << "image_map{ ";
output_to_povray_nodecl(ofile);
ofile << "\n\t\t\t}";
}

void TvWidget_bitmap::save( ofstream & ofile )
{
TvWidget::save( ofile );
file->save( ofile );
filter_all->save( ofile );
transmit_all->save( ofile );
once->save( ofile );
interpolate->save( ofile );
maptype->save( ofile );
ofile << "} ";
}

bool TvWidget_bitmap::load( ifstream & ofile, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( ofile );
	if ( tag == NULL ) break;
	if( file->load( ofile, tag ) ) continue;
	if( filter_all->load( ofile, tag ) ) continue;
	if( transmit_all->load( ofile, tag ) ) continue;
	if( once->load( ofile, tag ) ) continue;
	if( interpolate->load( ofile, tag ) ) continue;
	if( maptype->load( ofile, tag ) ) continue;
	tvio_skip_section( ofile );
	}
while( tag != NULL );
return true;
}



//***************************************
void TvWidget_bumpmap::output_to_povray( ofstream & ofile )
{
ofile << "bump_map{ ";
file->output_to_povray( ofile );
if ( once->value() ) ofile << "\n\t\t\t once";
if ( once->value() ) ofile << "\n\t\t\t use_color";
if ( interpolate->value() != 0 ) ofile << "\n\t\t\t interpolate " << interpolate->value()*2;
ofile << "\n\t\t\t map_type " << maptype_def[maptype->value()];
ofile << "\n\t\t\t}";
}

void TvWidget_bumpmap::get_widget( GtkWidget *box, bool tt )
{
widget = dlg_simple_box_frame( _(name), box );
file->get_widget( widget, tt );
maptype->get_widget( widget, tt );
interpolate->get_widget( widget, tt );
once->get_widget( widget, tt );
use_color->get_widget( widget, tt );
}

//*********************************************************
// Classe text
//*********************************************************
void TvWidget_text::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_vbox_new( FALSE, 0 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_label_set_justify( GTK_LABEL(label), GTK_JUSTIFY_LEFT );
gtk_box_pack_start( GTK_BOX(widget), label, FALSE, FALSE, 4 );

text = gtk_text_view_new();
gtk_widget_set_usize( text, -1, 40 );
gtk_text_view_set_editable( GTK_TEXT_VIEW(text), TRUE );
gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(text), GTK_WRAP_WORD  );
gtk_box_pack_start( GTK_BOX(widget), text, FALSE, TRUE, 4 );
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
if ( data != NULL ) gtk_text_buffer_set_text( buffer, data, -1 );
	
TvWidget::pack_widget( box, tt, text );
}

void TvWidget_text::flush()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
//size = gtk_text_buffer_get_length( GTK_TEXT_BUFFER(text) );
//gchar * txt = gtk_editable_get_chars( GTK_EDITABLE(text), 0, -1 );
if ( data != NULL ) { delete data; data = NULL; }
//data = new char[ size + 1 ];
//data[0] = '\0';
//strncpy( data, txt, size+1 );
GtkTextIter start, end;
gtk_text_buffer_get_bounds( buffer, &start, &end );
gchar *tmp = gtk_text_buffer_get_text( buffer, &start, &end, FALSE );
size = strlen( tmp );
data = new char[ size + 1 ];
data[0] = '\0';
strncpy( data, tmp, size+1 );
g_free( tmp );
}

void TvWidget_text::save( ofstream & file )
{
TvWidget::save( file );
file << "SIZE=" << size;
file << " VALUE=";
for ( register int i = 0 ; i < size ; i++ )
	file << data[i];
file << "} ";
}

bool TvWidget_text::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * val = NULL;
int text_size = 0;

do
	{
	val = tvio_get_next_val( file );
	
	if ( val == NULL ) break;
	if ( ! strcmp( val, "SIZE" ) )
		{
		text_size = tvio_get_value_as_int( file );
		if ( data != NULL ) delete data;
		data = NULL;
		if ( text_size != 0 ) data = new char[ text_size + 1 ];
		continue;
		}

	if ( ! strcmp( val, "VALUE" ) )
		{
		if ( text_size == 0 ) continue;
		for ( register int i = 0 ; i < text_size ; i++ )
			data[i] = (char)file.get();
		data[text_size] = '\0';
		continue;
		}
		
	}
while( val != NULL );

return true;
}


void TvWidget_text::update_widget()
{
if ( !GTK_IS_WIDGET(widget) ) return;
in_update = true;
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
GtkTextIter start, end;
gtk_text_buffer_get_bounds( buffer, &start, &end );
gtk_text_buffer_delete( buffer, &start, &end );
if ( data != NULL ) gtk_text_buffer_set_text( buffer, data, -1 );
//gtk_text_backward_delete( GTK_TEXT(text), gtk_text_get_length( GTK_TEXT(text) ) );
//if ( data != NULL ) gtk_text_insert( GTK_TEXT(text), NULL, NULL, NULL, data, -1 );
in_update = false;
}


//*********************************************************
// Classe noise generator
//*********************************************************
const int noise_mode_num = 3;
const gchar * noise_mode_list[noise_mode_num] = { N_("Povray 3.1"), N_("Range corrected"), N_("Perlin noise") };

TvWidget_noise_generator::TvWidget_noise_generator( const char *name, const char *sname, const char *tooltip, app_objs *appref ) : TvWidget( name, sname, tooltip, appref )
{ 
mode = new TvWidget_option_combo( name, sname, tooltip, appref );
mode->set_list( noise_mode_list, noise_mode_num, 1 );
}


//*********************************************************
// Classe String List
//*********************************************************

TvWidget_StringList::TvWidget_StringList( const char *name, const char *sname, const char *tooltip, app_objs *appref, int asize ) : TvWidget( name, sname, tooltip, appref )
{
size_max = asize;
}

TvWidget_StringList::~TvWidget_StringList()
{
Strings.clear();
}

void TvWidget_StringList::push_string( char *str )
{
if ( str == NULL ) return;
if ( Strings.size() == size_max ) 
	{ 
	delete Strings.back(); 
	Strings.pop_back(); 
	}
char *text = new char[ strlen(str) + 1 ];
strcpy( text, str );
Strings.insert( Strings.begin(), text );
}

bool TvWidget_StringList::pop_string( char *str )
{
if ( str == NULL ) return false;
for ( unsigned int i = 0 ; i < Strings.size() ; i++ )
	if ( !strcmp( str, Strings[i] ) )
		{
		delete Strings[i];
		Strings.erase( Strings.begin() + i );
		return true;
		}
return false;
}


void TvWidget_StringList::push_string_unique( char *str )
{
for ( unsigned int i = 0 ; i < Strings.size() ; i++ )
	{
	if ( Strings[i] == NULL ) break;
	if ( !strcmp( str, Strings[i] ) ) return;
	}
push_string( str );
}

void TvWidget_StringList::save( ofstream & file ) {
file << sname << "{SIZE=" << Strings.size();
for ( unsigned int i = 0 ; i < Strings.size() ; i++ )
	if ( Strings[i] != NULL ) file << " STR=\"" << Strings[i] << "\", ";
file << "}";		
}

bool TvWidget_StringList::load( ifstream & file, char *ltag ) 
{	
if ( strcmp( sname, ltag ) ) return false;
char * val = NULL;

do
	{
	val = tvio_get_next_val( file );
	
	if ( val == NULL ) break;
	if ( ! strcmp( val, "SIZE" ) )
		{ int asize = tvio_get_value_as_int( file ); asize=+1; } // Old version

	if ( ! strcmp( val, "STR" ) )
		{
		char *str = tvio_get_value_as_string( file );
		char *text = new char[ strlen( str ) + 1 ];
		strcpy( text, str );
		Strings.push_back( text );
		continue;
		}
		
	}
while( val != NULL );

return true;
};

void TvWidget_StringList::reput_on_top( int num )
{
char *str = Strings[num];
Strings.erase( Strings.begin() + num );
Strings.insert( Strings.begin(), str );
}

//*********************************************************
// Classe fonte
//*********************************************************
/*TvWidget_font::TvWidget_font( TvWidget_font & ref ) : TvWidget( ref )
{
set( ref.get() );
picker = NULL;
}

void TvWidget_font::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 0 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_box_pack_start( GTK_BOX(widget), label, FALSE, FALSE, 3 );
picker = gnome_font_picker_new();
gtk_box_pack_start( GTK_BOX(widget), picker, FALSE, FALSE, 10 );
pack_widget( box, tt, picker );
}

void TvWidget_font::update_widget()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
in_update = true;
gnome_font_picker_set_font_name( GNOME_FONT_PICKER(picker), data );
in_update = false;
}

void TvWidget_font::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
char *temp = (char*)gnome_font_picker_get_font_name( GNOME_FONT_PICKER(picker) );
set( temp );
}

void TvWidget_font::save( ofstream & file )
{
TvWidget::save( file );
file << "FNAME=" << data;
file << "} ";
}

bool TvWidget_font::load( ifstream & file, char * tag )
{
if ( strcmp( sname, tag ) ) return false;
char * val = (char*)sname;
while ( val != NULL )
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;

	if ( ! strcmp( val, "FNAME" ) ) { set( tvio_get_value_as_string( file )); continue; }
	}
return true;
}

void TvWidget_font ::set( char *val )
{
if ( data != NULL ) delete data;
data = new char[ strlen(val) + 1 ];
strcpy( data, val );
}
*/

//*********************************************************
// Classe function
//*********************************************************
const gchar *tvwid_funcs[ tvwid_funcs_num ] = {  "cos", "cosh", "acos", "acosh", "sin", 
	"sinh", "asin", "asinh", 	"tan", "tanh", "atan", "atanh",  "atan2",
	 "degrees",  "radians", "int", 
	"ceil", "abs", "floor",  "div", "mod",	"min", "max",
	"exp", "ln", 	"log", "sqrt", "pow",
	// Objects
	"f_algbr_cyl1", "f_algbr_cyl2", 
	"f_algbr_cyl3", "f_algbr_cyl4", "f_blob", "f_blob2", "f_cross_ellipsoids",
	"f_ellipsoid", "f_helical_torus", "f_helix1", "f_helix2", 
	"f_hyperbolic_torus", "f_mesh1", "f_paraboloid", "f_parabolic_torus",
	"f_quartic_paraboloid", "f_quartic_saddle", "f_quartic_cylinder",
	"f_rounded_box", "f_sphere", "f_spiral", "f_strophoid", "f_strophoid_2d",
	"f_superellipsoid", "f_torus", "f_torus2", 
	// Funny things
	"f_comma", "f_umbrella", "f_bicorn", "f_bifolia", "f_cubic_saddle",
	"f_heart", "f_isect_ellipsoids", "f_mitre", "f_pillow", "f_piriform",
	"f_piriform_2d", "f_polytubes","f_spikes", "f_spikes_2d", "f_torus_gumdrop",
	// Scientific
	"f_steiners_roman", "f_boy_surface", "f_dupin_cyclid", "f_enneper",
	"f_hex_x", "f_hex_y","f_hunt_surface", "f_kampyle_of_eudoxus", 
	"f_kampyle_of_eudoxus_2d", "f_klein_bottle", "f_kummer_surface_v1", 
	"f_kummer_surface_v2", "f_lemniscate_of_gerono", "f_lemniscate_of_gerono_2d",
	"f_ovals_of_cassini", "f_poly4", "f_steiners_roman", 
	// Weird
	"f_crossed_through", "f_cushion", "f_devils_curve", "f_devils_curve_2d", 
	"f_flange_cover", "f_folium_surface", "f_folium_surface_2d", "f_glob",
	"f_hetero_mf", "f_nodal_cubic","f_odd", "f_ph", "f_quantum", "f_r",  "f_th", 
	"f_witch_of_agnesi", "f_witch_of_agnesi_2d", 
	};
const int tvwid_funcs_cat_num = 8;
const gchar *tvwid_funcs_cat[ tvwid_funcs_cat_num ] = { N_("Trigonometry"), N_("Conversion"),
	N_("Arithmetic"), N_("Maths") , N_("Objects"), N_("Funny"), N_("Scientific"), N_("Weird")  };
const int tvwid_funcs_cat_size[ tvwid_funcs_cat_num ] = { 13, 3, 7, 5, 
	24, 16, 15, 17 };
	
const int tvwid_funcs_arg_num[ tvwid_funcs_num ] = {  
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 
	1, 1, 1, 
	1, 1, 1, 2, 2, 2, 2, 
	1, 1, 1, 1, 2,
	
	8, 8, 8, 8, 8, 7, 7, 6, 13, 10,10, 5, 7, 8, 4, 6, 4, 4, 6, 7, 4, 9, 7, 10, 5, 5, 6, 
	4, 4, 5, 5, 4, 4, 4, 4, 4, 4, 4,10, 9, 8, 7, 4, 
	4, 5, 9, 4, 4, 6, 9, 4, 4, 7, 4, 9, 7, 8, 4, 
	4, 4, 4, 9, 7, 6, 9, 4, 9, 4, 4, 3, 4, 3, 3, 5, 9,
	};

void TvWidget_function::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_button_new_with_label( name );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_tvwid_func_open_dialog), this );
TvWidget::pack_widget( box, tt, widget );
}

void TvWidget_function::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_button_new_with_label( name );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_tvwid_func_open_dialog), this );
//TvWidget::pack_widget( box, tt, widget );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 0, 5,  row-1, row );
//gtk_table_attach( GTK_TABLE(tab), widget, 0, 5,  row-1, row, (GtkAttachOptions)(GTK_FILL), (GtkAttachOptions)0, 0, 0 );
}

void TvWidget_function::save( ofstream & file )
{
TvWidget::save( file );
file << "SIZE=" << size;
file << " VALUE=";
for ( register int i = 0 ; i < size ; i++ )
	file << data[i];
file << "} ";
}

bool TvWidget_function::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * val = NULL;
int text_size = 0;

do
	{
	val = tvio_get_next_val( file );
	
	if ( val == NULL ) break;
	if ( ! strcmp( val, "SIZE" ) )
		{
		text_size = tvio_get_value_as_int( file );
		if ( data != NULL ) delete data;
		data = NULL;
		if ( text_size != 0 ) data = new char[ text_size + 1 ];
		continue;
		}

	if ( ! strcmp( val, "VALUE" ) )
		{
		if ( text_size == 0 ) continue;
		for ( register int i = 0 ; i < text_size ; i++ )
			data[i] = (char)file.get();
		data[text_size] = '\0';
		continue;
		}
		
	}
while( val != NULL );
if ( text_size != 0 ) func_parser.set_expression( data );
size = text_size;
return true;
}

void TvWidget_function::open_dialog()
{
if ( GTK_IS_DIALOG(dialog) )
	{
	gdk_window_raise( dialog->window );
	return;
	}
dialog =  gtk_dialog_new_with_buttons( _("Function editor"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK,  GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,NULL );
gtk_window_set_modal( GTK_WINDOW(dialog), TRUE );
g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(sign_tvwid_func_dlg_click), this );
g_signal_connect( G_OBJECT(dialog), "close", G_CALLBACK(sign_tvwid_func_dlg_destroy), this );
GtkBox *vbox = GTK_BOX( ((GtkDialog*)(dialog))->vbox );

// Menus
GtkWidget *menu_bar = gtk_menu_bar_new();
gtk_box_pack_start( vbox, menu_bar, FALSE, TRUE, 12 );
GtkWidget *insert_menu = gtk_menu_item_new_with_label( N_("Insert") );
gtk_menu_bar_append( menu_bar, insert_menu );
GtkWidget *sub_menu = gtk_menu_new();
gtk_menu_item_set_submenu( GTK_MENU_ITEM(insert_menu), sub_menu );
	
	// Predefined functions
	int offset = 0;
	for ( int cat = 0 ; cat < tvwid_funcs_cat_num ; cat ++ )
		{
		GtkWidget *menu_item = gtk_menu_item_new_with_label( tvwid_funcs_cat[cat] );
		gtk_menu_append( GTK_MENU(sub_menu), menu_item );
		GtkWidget *menu = gtk_menu_new();
		gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu_item), menu );
			for ( int i = 0 ; i < tvwid_funcs_cat_size[cat] ; i++ )
				{
				insert_menu_widgets[offset] = gtk_menu_item_new_with_label( tvwid_funcs[offset] );
				gtk_menu_append( GTK_MENU(menu), insert_menu_widgets[offset]  );	
				gtk_signal_connect( GTK_OBJECT(insert_menu_widgets[offset] ), "activate", GTK_SIGNAL_FUNC(sign_tvwid_func_insert), this );
				offset++;
				}
		}

// Utils
GtkWidget *utils_menu = gtk_menu_item_new_with_label( N_("Utils") );
gtk_menu_bar_append( menu_bar, utils_menu );
sub_menu = gtk_menu_new();
gtk_menu_item_set_submenu( GTK_MENU_ITEM(utils_menu), sub_menu );
	GtkWidget *menu_item = gtk_menu_item_new_with_label( N_("Clear") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_func_clear), this );

	menu_item = gtk_separator_menu_item_new( );
	gtk_menu_append( sub_menu, menu_item );
	
	menu_item = gtk_menu_item_new_with_label( N_("Cut") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_func_cut), this );

	menu_item = gtk_menu_item_new_with_label( N_("Copy") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_func_copy), this );
	
	menu_item = gtk_menu_item_new_with_label( N_("Paste") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_func_paste), this );

// Multiline text editor
GtkWidget *scroll = gtk_scrolled_window_new( NULL, NULL );
gtk_widget_set_usize( scroll, 450, 200 );
gtk_box_pack_start( vbox, scroll, TRUE, TRUE, 4 );
text = gtk_text_view_new();
gtk_text_view_set_editable( GTK_TEXT_VIEW(text), TRUE );
gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(text), GTK_WRAP_WORD  );
gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(scroll), text );
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
if ( data != NULL ) gtk_text_buffer_set_text( buffer, data, -1 );	

gtk_widget_show_all(dialog);	
}


void TvWidget_function::clicked_dlg( int button )
{
switch( button )
	{
	case GTK_RESPONSE_OK:
		{
		GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
		if ( data != NULL ) { delete data; data = NULL; }
		GtkTextIter start, end;
		gtk_text_buffer_get_bounds( buffer, &start, &end );
		gchar *tmp = gtk_text_buffer_get_text( buffer, &start, &end, FALSE );
		size = strlen( tmp );
		data = new char[ size + 1 ];
		data[0] = '\0';
		strncpy( data, tmp, size+1 );
		g_free( tmp );
		clicked_dlg( -1 );
		func_parser.set_expression( data );
		if ( func_parser.get_status() == false ) 
			app_warning( func_parser.get_error() );
		}
		flush();
		break;
		
	// Close
	default:
	case GTK_RESPONSE_CLOSE:
		gtk_widget_destroy( dialog );
		dialog = NULL;
		break;
	}
}


void TvWidget_function::insert_function( GtkWidget *sender )
{
gchar *str = NULL;
int arg_num = 0;
for ( int i = 0 ; i < tvwid_funcs_num ; i ++ )
	if ( insert_menu_widgets[i] == sender ) 
		{ 
		str = (gchar*)tvwid_funcs[i]; 
		arg_num = (int)tvwid_funcs_arg_num[i] - 1; 
		}
if ( str == NULL ) return;
	
int len = strlen( str ) + 4 + arg_num*2;
char *insert = new char[ len ];
strcpy( insert, str );
strcat( insert, "(" );
for ( int i = 0 ; i < arg_num ; i++ ) strcat( insert, " ," );
strcat( insert, " )" );
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
gtk_text_buffer_insert_at_cursor( buffer, insert, len-1 );
delete insert;
}

void TvWidget_function::clear_editor()
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
GtkTextIter start, end;
gtk_text_buffer_get_bounds( buffer, &start, &end );
gtk_text_buffer_delete( buffer, &start, &end );
}

void TvWidget_function::cut()
{
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
	GtkClipboard *clip = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );	
	gtk_text_buffer_cut_clipboard( buffer, clip, true );
}

void TvWidget_function::copy()
{
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
	GtkClipboard *clip = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );	
	gtk_text_buffer_copy_clipboard( buffer, clip );
}	

void TvWidget_function::paste()
{
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
	GtkClipboard *clip = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );	
	gtk_text_buffer_paste_clipboard( buffer, clip, NULL, true );
}	

//*********************************************************
// Classe  Text Button
//*********************************************************
void TvWidget_TextButton::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_button_new_with_label( name );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_tvwid_tb_open_dialog), this );
TvWidget::pack_widget( box, tt, widget );
}

void TvWidget_TextButton::save( ofstream & file )
{
TvWidget::save( file );
file << "SIZE=" << size;
file << " VALUE=";
for ( register int i = 0 ; i < size ; i++ )
	file << data[i];
file << "} ";
}

bool TvWidget_TextButton::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * val = NULL;
int text_size = 0;

do
	{
	val = tvio_get_next_val( file );
	
	if ( val == NULL ) break;
	if ( ! strcmp( val, "SIZE" ) )
		{
		text_size = tvio_get_value_as_int( file );
		if ( data != NULL ) delete data;
		data = NULL;
		if ( text_size != 0 ) data = new char[ text_size + 1 ];
		continue;
		}

	if ( ! strcmp( val, "VALUE" ) )
		{
		if ( text_size == 0 ) continue;
		for ( register int i = 0 ; i < text_size ; i++ )
			data[i] = (char)file.get();
		data[text_size] = '\0';
		continue;
		}
		
	}
while( val != NULL );
size = text_size;
return true;
}

void TvWidget_TextButton::open_dialog()
{
if ( GTK_IS_DIALOG(dialog) )
	{
	gdk_window_raise( dialog->window );
	return;
	}
dialog =  gtk_dialog_new_with_buttons( _("Script editor"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK,  GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,NULL );
gtk_window_set_modal( GTK_WINDOW(dialog), TRUE );
g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(sign_tvwid_tb_dlg_click), this );
g_signal_connect( G_OBJECT(dialog), "close", G_CALLBACK(sign_tvwid_tb_dlg_destroy), this );
GtkBox *vbox = GTK_BOX( ((GtkDialog*)(dialog))->vbox );

// Menus
GtkWidget *menu_bar = gtk_menu_bar_new();
gtk_box_pack_start( vbox, menu_bar, FALSE, TRUE, 12 );
// Utils
GtkWidget *utils_menu = gtk_menu_item_new_with_label( N_("Utils") );
gtk_menu_bar_append( menu_bar, utils_menu );
GtkWidget *sub_menu = gtk_menu_new();
gtk_menu_item_set_submenu( GTK_MENU_ITEM(utils_menu), sub_menu );
	GtkWidget *menu_item = gtk_menu_item_new_with_label( N_("Clear") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_tb_clear), this );

	menu_item = gtk_separator_menu_item_new( );
	gtk_menu_append( sub_menu, menu_item );
	
	menu_item = gtk_menu_item_new_with_label( N_("Cut") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_tb_cut), this );

	menu_item = gtk_menu_item_new_with_label( N_("Copy") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_tb_copy), this );
	
	menu_item = gtk_menu_item_new_with_label( N_("Paste") );
	gtk_menu_append( sub_menu, menu_item );
	gtk_signal_connect( GTK_OBJECT(menu_item), "activate", GTK_SIGNAL_FUNC(sign_tvwid_tb_paste), this );
	
// Multiline text editor
GtkWidget *scroll = gtk_scrolled_window_new( NULL, NULL );
gtk_widget_set_usize( scroll, 450, 200 );
gtk_box_pack_start( vbox, scroll, TRUE, TRUE, 4 );
text = gtk_text_view_new();
gtk_text_view_set_editable( GTK_TEXT_VIEW(text), TRUE );
gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(text), GTK_WRAP_WORD  );
gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(scroll), text );
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
if ( data != NULL ) gtk_text_buffer_set_text( buffer, data, -1 );	

gtk_widget_show_all(dialog);	
}


void TvWidget_TextButton::clicked_dlg( int button )
{
switch( button )
	{
	case GTK_RESPONSE_OK:
		{
		GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
		if ( data != NULL ) { delete data; data = NULL; }
		GtkTextIter start, end;
		gtk_text_buffer_get_bounds( buffer, &start, &end );
		gchar *tmp = gtk_text_buffer_get_text( buffer, &start, &end, FALSE );
		size = strlen( tmp );
		data = new char[ size + 1 ];
		data[0] = '\0';
		strncpy( data, tmp, size+1 );
		g_free( tmp );
		clicked_dlg( -1 );
		flush();
		}
		break;
		
	// Close
	default:
	case GTK_RESPONSE_CLOSE:
		gtk_widget_destroy( dialog );
		dialog = NULL;
		break;
	}
}


void TvWidget_TextButton::clear_editor()
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
GtkTextIter start, end;
gtk_text_buffer_get_bounds( buffer, &start, &end );
gtk_text_buffer_delete( buffer, &start, &end );
}

void TvWidget_TextButton::cut()
{
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
	GtkClipboard *clip = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );	
	gtk_text_buffer_cut_clipboard( buffer, clip, true );
}

void TvWidget_TextButton::copy()
{
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
	GtkClipboard *clip = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );	
	gtk_text_buffer_copy_clipboard( buffer, clip );
}	

void TvWidget_TextButton::paste()
{
	GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(text) );
	GtkClipboard *clip = gtk_clipboard_get( GDK_SELECTION_CLIPBOARD );	
	gtk_text_buffer_paste_clipboard( buffer, clip, NULL, true );
}
