//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// tvwidgets.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"
#include "include/scene.h"

const float pi = 3.141592654;
app_objs *TvWidget::app_ref = NULL;

//*********************************************************
// Classe de base
//*********************************************************
TvWidget::TvWidget( const char *nom, const char *snom, const char *aide, app_objs *appref )
{
app_ref = appref;
name = nom;
sname = snom;
tooltip = aide;
widget = NULL;
ttwidget = NULL;
in_update = false;
}

TvWidget::TvWidget( TvWidget & ref )
{
app_ref = ref.app_ref;
name = ref.name;
sname = ref.sname;
tooltip = ref.tooltip;
widget = NULL;
ttwidget = NULL;
in_update = false;
}

void TvWidget::pack_widget( GtkWidget *box, bool tt, GtkWidget *target )
{
if ( tt && tooltip != NULL ) 
	{
	if ( target == NULL ) target = widget;
	ttwidget = gtk_tooltips_new();
	gtk_tooltips_set_tip( ttwidget, target, tooltip, NULL );
	}
gtk_box_pack_start( GTK_BOX(box), widget, FALSE, TRUE, 1 ); 
}

void TvWidget::set_tooltip( GtkWidget *target )
{
if ( !GTK_IS_WIDGET(widget) || tooltip == NULL ) return;
PREF_DEF
if ( pref->tooltips->value() )
	{
	if ( !GTK_IS_TOOLTIPS(ttwidget) ) 
		{
		ttwidget = gtk_tooltips_new();
		gtk_tooltips_set_tip( ttwidget, target, _(tooltip), NULL );
		}
	else gtk_tooltips_enable( ttwidget );
	}
else if ( GTK_IS_TOOLTIPS(ttwidget) ) gtk_tooltips_disable( ttwidget );
}

void TvWidget::flush()
{
SCENE_DEF
scene->set_modified();
}

//*********************************************************
// Rolling_box
//*********************************************************
app_objs *Rolling_box::app_ref = NULL;

Rolling_box::Rolling_box( const char *titre, app_objs *appref, TvWidget *obj )
{
title = titre;
app_ref = appref;
child_widget = obj;
rolled_up = true;
rolling_box = NULL;
mother_box = NULL;
}

Rolling_box::Rolling_box( Rolling_box & ref )
{
title = ref.title;
child_widget = ref.child_widget;
rolled_up = ref.rolled_up;
rolling_box = NULL;
mother_box = NULL;
}


void Rolling_box::copy( Rolling_box *ref )
{
title = ref->title;
rolled_up = ref->rolled_up;
rolling_box = NULL;
mother_box = NULL;
}


void Rolling_box::set_bar_pix()
{
char *pix_fname;
if ( ! rolled_up ) pix_fname = tv_get_pixmap( "view_rollup.png" );
else pix_fname = tv_get_pixmap( "view_rolldown.png" );
if ( bar_pix == NULL ) bar_pix = gtk_image_new_from_file( pix_fname );
else gtk_image_set_from_file( GTK_IMAGE(bar_pix), pix_fname );
delete pix_fname;
}

void Rolling_box::get_widget_rb( GtkWidget *box, bool tt )
{
tooltips = tt;

GtkWidget *bar_frame = gtk_frame_new( NULL );
gtk_box_pack_start( GTK_BOX(box), bar_frame, FALSE, FALSE, 0 );
gtk_container_set_border_width( GTK_CONTAINER(bar_frame), 0 );
gtk_frame_set_shadow_type( GTK_FRAME(bar_frame), GTK_SHADOW_OUT );	
gtk_widget_set_size_request( bar_frame, -1, 20 );

GtkWidget *bar_box = gtk_hbox_new( FALSE, 0 );
gtk_container_add( GTK_CONTAINER(bar_frame), bar_box );
GtkWidget *bar_label = gtk_label_new( title );
gtk_box_pack_start( GTK_BOX(bar_box), bar_label, TRUE, TRUE, 0 );

GtkWidget *roll_button = gtk_button_new();
gtk_button_set_relief( GTK_BUTTON(roll_button), GTK_RELIEF_NONE );
GTK_WIDGET_UNSET_FLAGS( roll_button, GTK_CAN_FOCUS );	
gtk_container_set_border_width( GTK_CONTAINER(roll_button), 0 );
gtk_box_pack_start( GTK_BOX(bar_box), roll_button, FALSE, TRUE, 0 );	

bar_pix = NULL;
set_bar_pix();
gtk_container_add( GTK_CONTAINER(roll_button), bar_pix );

gtk_signal_connect( GTK_OBJECT(roll_button), "clicked", GTK_SIGNAL_FUNC(sign_rolling_box_clicked), this );

mother_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(box), mother_box, FALSE, FALSE, 0 );

if ( rolled_up ) return;
rolling_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(mother_box), rolling_box, FALSE, FALSE, 0 );
child_widget->get_widget_noframe( rolling_box, tooltips );
}

void Rolling_box::button_clicked()
{
if ( !rolled_up ) 
	{
	child_widget->flush();
	child_widget->clear_widget();
	gtk_widget_destroy( rolling_box );
	}
else
	{
	rolling_box = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(mother_box), rolling_box, FALSE, FALSE, 0 );
	child_widget->get_widget_noframe( rolling_box, tooltips );
	gtk_widget_show_all( mother_box );
	}
rolled_up = ! rolled_up;
set_bar_pix();
}


//*********************************************************
// Classe bool
//*********************************************************
void TvWidget_bool::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_check_button_new_with_label( _(name) );
gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(widget), data );
TvWidget::pack_widget( box, tt );
}

void TvWidget_bool::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_check_button_new_with_label( _(name) );
gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(widget), data );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 0, 2,  row-1, row );
set_tooltip( widget );
}

void TvWidget_bool::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return; 
if ( widget == NULL ) return;
TvWidget::flush();
data = GTK_TOGGLE_BUTTON(widget)->active;
}

void TvWidget_bool::save( ofstream & file )
{
TvWidget::save( file );
file << "VALUE=" << ( data ? 'Y' : 'N' );
file << "} ";
}

bool TvWidget_bool::load( ifstream & file, char * tag )
{
if ( strcmp( sname, tag ) ) return false;
char * val = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) )
		{
		data = tvio_get_value_as_bool( file );
		continue;
		}
	}
while( val != NULL );
return true;

}

//*********************************************************
// Classe bool activator
//*********************************************************
void TvWidget_bool_activator::get_widget_no_toggle( GtkWidget *box, bool tt )
{
TvWidget_bool::get_widget( box, tt );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_bool_activator_clicked), this );
}

//*********************************************************
// Classe int
//*********************************************************
TvWidget_int::TvWidget_int( TvWidget_int & ref ) : TvWidget( ref )
{
data = ref.data;
up = ref.data;
down = ref.data;
rate = ref.rate;
spin = NULL;
}

void TvWidget_int::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, 4 );

adj = gtk_adjustment_new( data, down, up, rate, rate*10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 0 );
gtk_box_pack_start( GTK_BOX(widget), spin, TRUE, TRUE, 0 );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );

TvWidget::pack_widget( box, tt, spin );
}

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

adj = gtk_adjustment_new( data, down, up, rate, rate*10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 0 );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
	
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), spin, 1, 5,  row-1, row );
set_tooltip( widget );
widget = spin;
}

void TvWidget_int::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return; 
if ( widget == NULL ) return;
TvWidget::flush();
data = gtk_spin_button_get_value_as_int( GTK_SPIN_BUTTON(spin) );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
}

void  TvWidget_int::update_widget()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
in_update = true;
gtk_spin_button_set_value( GTK_SPIN_BUTTON(spin), data );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
in_update = false;
}

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

bool TvWidget_int::load( ifstream & file, char * tag )
{
if ( strcmp( sname, tag ) ) return false;
char * val = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) )
		{
		data = tvio_get_value_as_int( file );
		continue;
		}
	}
while( val != NULL );
return true;
}

void TvWidget_int::reset_range( int min, int max )
{
in_update = true;
up = max; down = min;
if ( GTK_IS_WIDGET(widget) ) gtk_spin_button_set_range( GTK_SPIN_BUTTON(spin), min, max );
in_update = false;
}

//*********************************************************
// Classe float
//*********************************************************
TvWidget_float::TvWidget_float( TvWidget_float & ref ) : TvWidget( ref )
{
data = ref.data;
up = ref.up;
down = ref.down;
rate = ref.rate;
precision = ref.precision;
spin = NULL;
}

void TvWidget_float::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, 4 );

adj = gtk_adjustment_new( data, down, up, rate, rate*10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, precision );
gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spin), TRUE );
gtk_box_pack_start( GTK_BOX(widget), spin, TRUE, TRUE, 0 );

gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
TvWidget::pack_widget( box, tt, spin );
}

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

adj = gtk_adjustment_new( data, down, up, rate, rate*10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, precision );
gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spin), TRUE );

gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), spin, 1, 5,  row-1, row );
widget = spin;
set_tooltip( widget );
}

void TvWidget_float::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return; 
if ( widget == NULL ) return;
TvWidget::flush();
data = gtk_spin_button_get_value_as_float( GTK_SPIN_BUTTON(spin) );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
}

void TvWidget_float::update_widget()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
in_update = true;
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), data );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
in_update = false;
}

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

bool TvWidget_float::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, "VALUE" ) )
		{
		data = tvio_get_value_as_float( file );
		continue;
		}
	}
return true;
}


//********************************************************
// TvWidget Percent
//********************************************************
void TvWidget_percent::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, 4 );

adj = gtk_adjustment_new( data*100.0, 0, 100, 1, 10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 2 );
//gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spin), TRUE );
gtk_box_pack_start( GTK_BOX(widget), spin, TRUE, TRUE, 0 );
GtkWidget *slider = gtk_hscale_new( GTK_ADJUSTMENT(adj) );
gtk_box_pack_start( GTK_BOX(widget), slider, TRUE, TRUE, 0 );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
TvWidget::pack_widget( box, tt, spin );
}

void TvWidget_percent::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 );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );

adj = gtk_adjustment_new( data*100.0, 0, 100, 1, 10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 2 );
gtk_box_pack_start( GTK_BOX(widget), spin, FALSE, TRUE, 0 );
GtkWidget *slider = gtk_hscale_new( GTK_ADJUSTMENT(adj) );
gtk_box_pack_start( GTK_BOX(widget), slider, TRUE, TRUE, 0 );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
set_tooltip( label );
}


void TvWidget_percent::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
if ( widget == NULL ) return;
TvWidget::flush();
data = gtk_spin_button_get_value_as_float( GTK_SPIN_BUTTON(spin) ) / 100.0;
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
}

void TvWidget_percent::update_widget()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
in_update = true;
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), data*100.0 );
gtk_editable_set_position( GTK_EDITABLE(spin), 0 );
in_update = false;
}



//*********************************************************
// Classe entry
//*********************************************************
TvWidget_entry::~TvWidget_entry()
{
if ( undo_data != data && undo_data != NULL ) delete undo_data;
if ( data != NULL ) delete data;
}

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

void TvWidget_entry::store()
{
if ( undo_data != data &&  undo_data != NULL ) delete undo_data;
undo_data = data;
}

void TvWidget_entry::restore()
{
if ( data == undo_data ) { undo_data = NULL; return; }
delete data;
data = undo_data;
undo_data = NULL;
}

void TvWidget_entry::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, 4 );

entry = gtk_entry_new();
if ( data != NULL ) gtk_entry_set_text( GTK_ENTRY(entry), data );
gtk_box_pack_start( GTK_BOX(widget), entry, TRUE, TRUE, 0 );

TvWidget::pack_widget( box, tt, entry );
}

void TvWidget_entry::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 );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );

entry = gtk_entry_new();
if ( data != NULL ) gtk_entry_set_text( GTK_ENTRY(entry), data );
gtk_box_pack_start( GTK_BOX(widget), entry, TRUE, TRUE, 0 );
set_tooltip( label );
}

void TvWidget_entry::save( ofstream & file )
{
if ( data == NULL ) return;
TvWidget::save( file );
file << "VALUE=\"" << data << "\"} ";
}

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

do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) )
		{
		set( tvio_get_value_as_string( file ) );
		continue;
		}
	}  while ( val != NULL );
return true;
}


//*********************************************************
// Classe couleur
//*********************************************************
TvWidget_color::TvWidget_color( TvWidget_color & ref ) : TvWidget( ref )
{
for ( int i = 0 ; i < 4 ; i++ ) data[i] = ref.data[i];
picker = NULL;
}

void TvWidget_color::get_widget( GtkWidget *box, bool tt )
{	
widget = gtk_hbox_new( FALSE, 0 );	
picker = gtk_color_button_new();
if ( use_alpha ) gtk_color_button_set_use_alpha( GTK_COLOR_BUTTON(picker), TRUE );
gtk_color_button_set_title( GTK_COLOR_BUTTON(picker), _(name) );
gtk_box_pack_start( GTK_BOX(widget), picker, FALSE, FALSE, 3 );
GdkColor col;
gdk_col( col );
gtk_color_button_set_color( GTK_COLOR_BUTTON(picker), &col );
gtk_color_button_set_alpha( GTK_COLOR_BUTTON(picker), (gushort)(data[3]*65535) );
GtkWidget *label = gtk_label_new( _(name) );
gtk_box_pack_start( GTK_BOX(widget), label, FALSE, FALSE, 10 );
pack_widget( box, tt, picker );
}

void TvWidget_color::get_widget( GtkWidget *tab, bool tt, int row )
{
picker = gtk_color_button_new();
	if ( use_alpha ) gtk_color_button_set_use_alpha( GTK_COLOR_BUTTON(picker), TRUE );
	gtk_color_button_set_title( GTK_COLOR_BUTTON(picker), _(name) );
	GdkColor col;
	gdk_col( col );
	gtk_color_button_set_color( GTK_COLOR_BUTTON(picker), &col );
	gtk_color_button_set_alpha( GTK_COLOR_BUTTON(picker), (gushort)(data[3]*65535) );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_alignment( GTK_MISC(label), 0, 0.5 );

gtk_table_attach_defaults( GTK_TABLE(tab), picker, 1, 5,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );
widget = picker;
set_tooltip( label );
}


void TvWidget_color::update_widget()
{
if ( ! GTK_IS_WIDGET(widget) ) return;
in_update = true;
	GdkColor col;
	gdk_col( col );
	gtk_color_button_set_color( GTK_COLOR_BUTTON(picker), &col );
	gtk_color_button_set_alpha( GTK_COLOR_BUTTON(picker), (gushort)(data[3]*65535) );
in_update = false;
}

void TvWidget_color::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
GdkColor col;
gtk_color_button_get_color( GTK_COLOR_BUTTON(picker), &col );
set( col );
}

void TvWidget_color::gl_set_rgb()
{ 
glColor3f( data[0], data[1], data[2] );
GLfloat color[] = { data[0], data[1], data[2], 0 };
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color );
}

void TvWidget_color::gl_set_rgb_for_wireframe()
{ 
glColor3f( data[0], data[1], data[2] );

GLfloat specular[4] = { 1, 1, 1, 1 };
GLfloat shininess[1] = { 0.9 };
GLfloat color[4] = { data[0], data[1], data[2], 1.0 };
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular );
glMaterialfv( GL_FRONT_AND_BACK, GL_SHININESS, shininess );	
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color );
}

void TvWidget_color::save( ofstream & file )
{
TvWidget::save( file );
file << "R=" << data[0] << " G=" << data [1] << " B=" << data[2];
file << "} ";
}

bool TvWidget_color::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, "R" ) ) { data[0] = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "G" ) ) { data[1] = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "B" ) ) { data[2] = tvio_get_value_as_float( file ); continue; }
	}
return true;
}

//*********************************************************
// Classe pigment
//*********************************************************
TvWidget_pigment::TvWidget_pigment( const char *name, const char *sname, const char *tooltip, app_objs *appref )
 : TvWidget( name, sname, tooltip, appref )
{
color = new TvWidget_color( N_("RGB Color"), "RGB", NULL, app_ref );
color->set( 0, 0, 0 );
filter = new TvWidget_percent( N_("Filter(%)"), "FILT", NULL, app_ref, 0 );
filter->set_range( 1, 0, 0.01, 4 );
transmit = new TvWidget_percent( N_("Transmit(%)"), "TRANS", NULL, app_ref, 0 );
transmit->set_range( 1, 0, 0.01, 4 );
}

TvWidget_pigment::TvWidget_pigment( TvWidget_pigment & ref ) : TvWidget( ref )
{
color = new TvWidget_color( *ref.color );
filter = new TvWidget_percent( *ref.filter );
transmit = new TvWidget_percent( *ref.transmit );

}

void TvWidget_pigment::get_widget_wnframe( GtkWidget *box, bool tt, bool frame )
{
if ( frame ) widget = new_table( box, _(name), 3 );
else widget = new_table_no_frame( box, 3 );
	color->get_widget( widget, tt, 1 );
	filter->get_widget( widget, tt, 2 );
	transmit->get_widget( widget, tt, 3 );
}

void TvWidget_pigment::clear_widget()
{ 
widget = NULL;
color->clear_widget();
filter->clear_widget();
transmit->clear_widget();
}

void TvWidget_pigment::flush()
{ 
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
color->flush(); 
filter->flush(); 
transmit->flush();
}

void TvWidget_pigment::connect_signal( void (*fn)(gpointer), gpointer data )
{
func = fn;
func_data = data;
color->connect_signal( GTK_SIGNAL_FUNC(sign_pigcolor_changed), this );
filter->connect_signal( GTK_SIGNAL_FUNC(sign_pigalpha_changed), this );
transmit->connect_signal( GTK_SIGNAL_FUNC(sign_pigalpha_changed), this );
}

void TvWidget_pigment::output_to_povray( ofstream & file )
{
file << "rgbft<" << color->get(0) << ',' << color->get(1) << ',';
file << color->get(2) << ',' << filter->value() << ',' << transmit->value() << '>';
}

void TvWidget_pigment::save( ofstream & file )
{
TvWidget::save( file );
color->save( file );
filter->save( file );
transmit->save( file );
file << "} ";
}

bool TvWidget_pigment::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 ( color->load( file , tag ) ) continue;
	if ( filter->load( file , tag ) ) continue;
	if ( transmit->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}

//*********************************************************
// Classe option combo
//*********************************************************
TvWidget_option_combo::TvWidget_option_combo( TvWidget_option_combo & ref ) : TvWidget( ref )
{
data =  ref.data;
items = ref.items;
num_items = ref.num_items;
combo = NULL;
}

void TvWidget_option_combo::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, 4 );

combo = gtk_combo_box_new_text();
gtk_box_pack_start( GTK_BOX(widget), combo, TRUE, TRUE, 0 );
for ( int i = 0 ; i < num_items ; i++ )
	gtk_combo_box_append_text( GTK_COMBO_BOX(combo), items[i] );
gtk_combo_box_set_active( GTK_COMBO_BOX(combo), data );
pack_widget( box, tt, combo );
}


void TvWidget_option_combo::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 );
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 );

combo = gtk_combo_box_new_text();
gtk_box_pack_start( GTK_BOX(widget), combo, TRUE, TRUE, 0 );
for ( int i = 0 ; i < num_items ; i++ )
	gtk_combo_box_append_text( GTK_COMBO_BOX(combo), items[i] );	
gtk_combo_box_set_active( GTK_COMBO_BOX(combo), data );
set_tooltip( label );
}

void TvWidget_option_combo::flush() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
in_update = true;
TvWidget::flush();
data = gtk_combo_box_get_active( GTK_COMBO_BOX(combo) );
in_update = false;
}

void TvWidget_option_combo::update_widget() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
in_update = true;
//while ( gtk_combo_box_get_active( GTK_COMBO_BOX(combo) ) != -1 ) 
//	gtk_combo_box_remove_text( GTK_COMBO_BOX(combo), 0 );
GtkListStore *list_store = GTK_LIST_STORE( gtk_combo_box_get_model( GTK_COMBO_BOX(combo) ) );
gtk_list_store_clear( list_store );
for ( int i = 0 ; i < num_items ; i++ )
	gtk_combo_box_append_text( GTK_COMBO_BOX(combo), items[i] );	
gtk_combo_box_set_active( GTK_COMBO_BOX(combo), data );
in_update = false;
}

void TvWidget_option_combo::reset_list ( const char **it, const int num )
{
items = (char**)it; 
num_items = (int)num; 
}

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


bool TvWidget_option_combo::load( ifstream & file, char * tag )
{
if ( strcmp( sname, tag ) ) return false;
char * val = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) )
		{
		set( tvio_get_value_as_int( file ) );
		continue;
		}
	}
while( val != NULL );
return true;
}


//*********************************************************
// Classe path
//*********************************************************
TvWidget_path::TvWidget_path( TvWidget_path & ref ) : TvWidget( ref )
{
data = undo_data = NULL;
set( ref.data );
fentry = NULL;
action = ref.action;
}

TvWidget_path::~TvWidget_path()
{
if ( undo_data != data && undo_data != NULL ) delete undo_data;
if ( data != NULL ) delete data;
}

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

void TvWidget_path::store()
{
if ( undo_data != data &&  undo_data != NULL ) delete undo_data;
undo_data = data;
}

void TvWidget_path::restore()
{
if ( data == undo_data ) { undo_data = NULL; return; }
delete data;
data = undo_data;
undo_data = NULL;
}

void TvWidget_path::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 2 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_box_pack_start( GTK_BOX(widget), label, FALSE, TRUE, 4 );
fentry = gtk_file_chooser_button_new( name, action );
gtk_box_pack_start( GTK_BOX(widget), fentry, TRUE, TRUE, 2 );
if ( data != NULL ) gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(fentry), data );

pack_widget( box, tt, label );
}

void TvWidget_path::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_hbox_new( FALSE, 2 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_alignment( GTK_MISC(label), 0, 0.5 );
fentry = gtk_file_chooser_button_new( name, action );
if ( data != NULL ) gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(fentry), data );
gtk_box_pack_start( GTK_BOX(widget), fentry, TRUE, TRUE, 2 );
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( label );
}

void TvWidget_path::set_default_path( char *str )
{
if ( ! GTK_IS_WIDGET(fentry) ) return;
if ( data != NULL ) gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(fentry), data );
}

void TvWidget_path::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
char *res = (char*)gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(fentry) );
if ( res == NULL ) { set( NULL ); return; }
if ( strlen(res) > 0 ) set( res );
else set( NULL );
if ( res != NULL ) g_free( res );
}

void TvWidget_path::save( ofstream & file )
{
TvWidget::save( file );
file << "VALUE=\"" << data << "\"} ";
}


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

do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) )
		{
		set( tvio_get_value_as_string( file ) );
		continue;
		}
	}  while ( val != NULL );
return true;
}

//*********************************************************
// Classe path alt - see header
//*********************************************************
TvWidget_path_alt::TvWidget_path_alt( TvWidget_path_alt & ref ) : TvWidget( ref )
{
data = undo_data = NULL;
set( ref.data );
entry = fentry = NULL;
}

TvWidget_path_alt::~TvWidget_path_alt()
{
if ( undo_data != data && undo_data != NULL ) delete undo_data;
if ( data != NULL ) delete data;
}

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

void TvWidget_path_alt::store()
{
if ( undo_data != data &&  undo_data != NULL ) delete undo_data;
undo_data = data;
}

void TvWidget_path_alt::restore()
{
if ( data == undo_data ) { undo_data = NULL; return; }
delete data;
data = undo_data;
undo_data = NULL;
}

void TvWidget_path_alt::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 2 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_box_pack_start( GTK_BOX(widget), label, FALSE, TRUE, 4 );

fentry = gnome_file_entry_new( NULL, name );
entry = gnome_file_entry_gtk_entry( GNOME_FILE_ENTRY(fentry) );
if ( data != NULL ) gtk_entry_set_text( GTK_ENTRY(entry), data );
gtk_box_pack_start( GTK_BOX(widget), fentry, FALSE, TRUE, 2 );

pack_widget( box, tt, entry );
}

void TvWidget_path_alt::set_default_path( char *str )
{
if ( ! GTK_IS_WIDGET(fentry) ) return;
gnome_file_entry_set_default_path( GNOME_FILE_ENTRY(fentry), str );
}

void TvWidget_path_alt::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
char *res = (char*)gtk_entry_get_text( GTK_ENTRY(entry) );
if ( strlen(res) > 0 ) set( res );
else set( NULL );
}

void TvWidget_path_alt::save( ofstream & file )
{
TvWidget::save( file );
file << "VALUE=\"" << data << "\"} ";
}


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

do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) )
		{
		set( tvio_get_value_as_string( file ) );
		continue;
		}
	}  while ( val != NULL );
return true;
}


//*********************************************************
// Classe point
//*********************************************************
TvWidget_point::TvWidget_point( TvWidget_point & ref ) : TvWidget( ref )
{
memcpy( data, ref.data, sizeof(float)*3 );
for ( int i = 0 ; i < 3 ; i++ ) spins[i] = NULL;
up = ref.up;
down = ref.down;
rate = ref.rate;
precision = ref.precision;
}

void TvWidget_point::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], down, up, rate, rate*10, 0 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, precision );
	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_point::get_widget( GtkWidget *tab, bool tt, int row )
{
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_alignment( GTK_MISC(label), 0, 0.5 );
gtk_table_attach_defaults( GTK_TABLE(tab), label, 0, 1,  row-1, row );

widget = gtk_hbox_new( FALSE, 0 );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
GtkObject *adj;
for ( int i = 0 ; i < 3 ; i++ )
	{
	adj = gtk_adjustment_new( data[i], down, up, rate, rate*10, 0 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, precision );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
set_tooltip( label );
}


void TvWidget_point::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_point::flush() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
for ( int i = 0 ; i < 3 ; i++ )
	{
	if ( spins[i] == NULL ) break;
	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_point::update_widget() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
in_update = true;
for ( int i = 0 ; i < 3 ; i++ )
	{
	if ( spins[i] == NULL ) break;
	gtk_spin_button_set_value( GTK_SPIN_BUTTON(spins[i]), data[i] );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
in_update = false;
}

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


bool TvWidget_point::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 vector4d
//*********************************************************
TvWidget_vector4::TvWidget_vector4( TvWidget_vector4 & ref ) : TvWidget( ref )
{
memcpy( data, ref.data, sizeof(float)*4 );
for ( int i = 0 ; i < 3 ; i++ ) spins[i] = NULL;
up = ref.up;
down = ref.down;
rate = ref.rate;
precision = ref.precision;
}

void TvWidget_vector4::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 < 4 ; i++ )
	{
	adj = gtk_adjustment_new( data[i], down, up, rate, rate*10, 0 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, precision );
	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_vector4::reduce_size()
{
for ( int i = 0 ; i < 4 ; i++ )
	if ( GTK_IS_WIDGET(spins[i] ) ) gtk_widget_set_size_request( spins[i], 15, -1 );
}

void TvWidget_vector4::flush() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
for ( int i = 0 ; i < 4 ; i++ )
	{
	if ( spins[i] == NULL ) break;
	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_vector4::update_widget() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
in_update = true;
for ( int i = 0 ; i < 4 ; i++ )
	{
	if ( spins[i] == NULL ) break;
	gtk_spin_button_set_value( GTK_SPIN_BUTTON(spins[i]), data[i] );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
in_update = false;
}

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


bool TvWidget_vector4::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; }
	if ( ! strcmp( val, "W" ) ) { data[3] = tvio_get_value_as_float( file ); continue; }
	}
return true;
}




//*********************************************************
// Classe gl material
//*********************************************************
TvWidget_glmat::TvWidget_glmat( const char *name, const char *sname, const char *tooltip, app_objs *appref ) : TvWidget( name, sname, tooltip, appref )
{ 
//ambient = new TvWidget_color( N_("Ambient"), "AMBIENT", NULL, appref, true );
//ambient->set( .2, .2, .2, 1.0 );
diffuse = new TvWidget_color( N_("Diffuse and ambient"), "DIFFUSE", NULL, appref, true );
diffuse->set( .8, .8, .8, 1.0 );
specular = new TvWidget_color( N_("Specular"), "SPECULAR", NULL, appref, true );
specular->set( 0.0, 0.0, 0.0, 1.0 );
emission = new TvWidget_color( N_("Emission"), "EMISSION", NULL, appref, true );
emission->set( 0.0, 0.0, 0.0, 1.0 );
shininess = new TvWidget_float( N_("Shininess"), "SHINY", NULL, appref, 0 );
shininess->set_range( 1, 0, 0.01, 3 );
}

TvWidget_glmat::TvWidget_glmat( TvWidget_glmat & ref ) : TvWidget( ref )
{
//ambient = new TvWidget_color( *ref.ambient );
diffuse = new TvWidget_color( *ref.diffuse );
specular = new TvWidget_color( *ref.specular );
emission = new TvWidget_color( *ref.emission );
shininess = new TvWidget_float( *ref.shininess );
}

TvWidget_glmat::~TvWidget_glmat()
{
//delete ambient;
delete diffuse;
delete specular;
delete emission;
delete shininess;
}

void TvWidget_glmat::get_widget( GtkWidget *box, bool tt )
{ 
widget = new_table( box, _(name),4 );
//ambient->get_widget( widget, tt );
diffuse->get_widget( widget, tt, 1 );
specular->get_widget( widget, tt, 2 );
shininess->get_widget( widget, tt, 3 );
emission->get_widget( widget, tt, 4 );
}

void TvWidget_glmat::clear_widget()
{
TvWidget::clear_widget();
//ambient->clear_widget();
diffuse->clear_widget();
specular->clear_widget();
emission->clear_widget();
shininess->clear_widget();
}

void TvWidget_glmat::flush()
{
if ( !GTK_IS_WIDGET(widget) ) return;
TvWidget::flush();
//ambient->flush();
diffuse->flush();
specular->flush();
emission->flush();
shininess->flush();
}

void TvWidget_glmat::gl_set_material()
{
flush();
//GLfloat ambient_col[4];
GLfloat diffuse_col[4]; 
GLfloat specular_col[4];
GLfloat emission_col[4];
GLfloat shininess_col[] = { (GLfloat)shininess->value() };
for ( register int i = 0 ; i < 4 ; i++ )
	{
	//ambient_col[i] = (GLfloat)ambient->get(i);
	diffuse_col[i] =  (GLfloat)diffuse->get(i);
	specular_col[i] =  (GLfloat)specular->get(i);
	emission_col[i] =  (GLfloat)emission->get(i);
	}
//glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, ambient_col );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuse_col );
glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, emission_col );
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, specular_col );
glMaterialfv( GL_FRONT_AND_BACK, GL_SHININESS, shininess_col );
glColor4fv( diffuse_col );
}

void TvWidget_glmat::save( ofstream & file )
{
TvWidget::save( file );
//ambient->save( file );
diffuse->save( file );
specular->save( file );
emission->save( file );
shininess->save( file );
file << "} ";
}

bool TvWidget_glmat::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 ( ambient->load( file , tag ) ) continue;
	if ( diffuse->load( file , tag ) ) continue;
	if ( specular->load( file , tag ) ) continue;
	if ( emission->load( file , tag ) ) continue;
	if ( shininess->load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}
