//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// objparam.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 "include/objparam.h"
#include "include/viewmanager.h"
#include "include/preferences.h"
#include "include/objectlist.h"
#include "include/matlist.h"
#include "include/tvio.h"
#include "include/undo.h"

const float sensitivity = 2.1;
const float control_sensitivity = 15;

//*********************************************
// Base
//*********************************************
void ObjParam::flush( app_objs *app_ref )
{
chgd = true;

if ( refresh )
	{
	VMAN_DEF
	vmanager->refresh();
	}
}

void ObjParam::grab_pointer( GdkCursorType cursor, app_objs *app_ref )
{
OBJLIST_DEF
if ( objlist->get_current_param() == this ) return;
pmode_change = true;
VMAN_DEF
vmanager->set_custom_pointer_mode( cursor );
pmode_change = false;
objlist->set_current_param( this );
}

//*********************************************
// Option combo
//*********************************************
void ObjParam_option_combo::get_widget( GtkWidget *box, bool tt )
{
TvWidget_option_combo::get_widget( box, tt );
gtk_signal_connect( GTK_OBJECT(combo), "changed", GTK_SIGNAL_FUNC(sign_option_combo_flush), this );
}

void ObjParam_option_combo::get_widget( GtkWidget *tab, bool tt, int row )
{
TvWidget_option_combo::get_widget( tab, tt, row );
gtk_signal_connect( GTK_OBJECT(combo), "changed", GTK_SIGNAL_FUNC(sign_option_combo_flush), this );
}

void ObjParam_option_combo::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_option_combo *copy = new ObjParam_option_combo( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_option_combo::flush();
ObjParam::flush( app_ref );
}

void ObjParam_option_combo::swap_data( ObjParam *param )
{
ObjParam_option_combo *src = (ObjParam_option_combo*)param;
int swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}

//*********************************************
// Int
//*********************************************
void ObjParam_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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_int_flush), this );

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

void ObjParam_int::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, 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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_int_flush), this );
set_tooltip( label );
}

void ObjParam_int::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_int *copy = new ObjParam_int( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_int::flush();
ObjParam::flush( app_ref );
}

void ObjParam_int::swap_data( ObjParam *param )
{
ObjParam_int *src = (ObjParam_int*)param;
int swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}

//*********************************************
// Float
//*********************************************
void ObjParam_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 );

GtkObject *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_widget_set_usize( spin, 20, -1 );
gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_float_flush), this );
TvWidget::pack_widget( box, tt, spin );
}

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

GtkObject *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_widget_set_usize( spin, 20, -1 );
gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_float_flush), this );
set_tooltip( label );
}

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

GtkObject *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_widget_set_usize( spin, 20, -1 );
gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(func), dat );
TvWidget::pack_widget( box, tt, spin );
}

void ObjParam_float::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_float *copy = new ObjParam_float( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_float::flush();
ObjParam::flush( app_ref );
}


void ObjParam_float::swap_data( ObjParam *param )
{
ObjParam_float *src = (ObjParam_float*)param;
double swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}


//*********************************************
// Float angle
//*********************************************
void ObjParam_float_angle::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 0 );

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new(_( name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_float_angle_clicked), this );
gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );

GtkObject *adj = gtk_adjustment_new( data, down, up, rate, rate*10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 6 );
gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spin), TRUE );
gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_float_angle_flush), this );
gtk_box_pack_start( GTK_BOX(widget), spin, TRUE, TRUE, 0 );

pack_widget( box, tt, button );
undo_data = data;
}


void ObjParam_float_angle::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_hbox_new( FALSE, 0 );

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new(_( name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_float_angle_clicked), this );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
gtk_table_attach_defaults( GTK_TABLE(tab), button, 0, 1,  row-1, row );

GtkObject *adj = gtk_adjustment_new( data, down, up, rate, rate*10, 0 );
spin = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 6 );
gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spin), TRUE );
gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_float_angle_flush), this );
gtk_box_pack_start( GTK_BOX(widget), spin, TRUE, TRUE, 0 );
set_tooltip( button );
undo_data = data;
}


void ObjParam_float_angle::mouse_drag( struct drag_info *drag  )
{
if ( drag->first_click )
	{
	if ( send_undo )
		{
		UNDO_DEF
		ObjParam_float_angle *copy = new ObjParam_float_angle( *this );
		undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
		}
	drag->first_click = false;
	reversible_change = true;
	}
else reversible_change = false;
	
if ( drag->previous_x == drag->origin_x || drag->current_x == drag->origin_x ) return;
double temp1 = drag->previous_x - drag->origin_x;
double temp2 = drag->current_x - drag->origin_x;
double up = (drag->origin_y - drag->previous_y)/temp1 -  (drag->origin_y - drag->current_y) / temp2;
up *= temp1 * - temp2;
double down = drag->origin_y * ( drag->current_y + drag->previous_y - drag->origin_y );
down += drag->origin_x * ( drag->current_x + drag->previous_x - drag->origin_x );
down -= drag->previous_y * drag->current_y + drag->previous_x * drag->current_x;
if ( down == 0 ) return;
double phi = atan( up/ down );
if ( drag->control ) phi /= control_sensitivity;

changing = true;
undo_data = data;
data -= phi*180/3.14159;
if ( data > 360 ) data -= 360;
if ( data < 0 ) data += 360;
gtk_spin_button_set_value( GTK_SPIN_BUTTON(spin), data );
changing = false;
	
ObjParam::flush( app_ref );
}


//*********************************************
// Bool
//*********************************************
void ObjParam_bool::get_widget( GtkWidget *box, bool tt )
{
TvWidget_bool::get_widget( box, tt );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_bool_flush), this );
}

void ObjParam_bool::get_widget( GtkWidget *tab, bool tt, int row )
{
TvWidget_bool::get_widget( tab, tt, row );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_bool_flush), this );
}

void ObjParam_bool::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_bool *copy = new ObjParam_bool( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_bool::flush();
ObjParam::flush( app_ref );
}


void ObjParam_bool::swap_data( ObjParam *param )
{
ObjParam_bool *src = (ObjParam_bool*)param;
bool swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}


//*********************************************
// Bool activator
//*********************************************
void ObjParam_bool_activator::get_widget( GtkWidget *box, bool tt )
{
TvWidget_bool::get_widget( box, tt );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_bool_activator_flush), this );
toggle();
}

void ObjParam_bool_activator::get_widget( GtkWidget *tab, bool tt, int row )
{
TvWidget_bool::get_widget( tab, tt, row );
gtk_signal_connect( GTK_OBJECT(widget), "clicked", GTK_SIGNAL_FUNC(sign_bool_activator_flush), this );
toggle();
}

void ObjParam_bool_activator::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_bool_activator *copy = new ObjParam_bool_activator( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_bool::flush();
TvWidget_bool_activator::toggle();
ObjParam::flush( app_ref );
}


void ObjParam_bool_activator::swap_data( ObjParam *param )
{
ObjParam_bool_activator *src = (ObjParam_bool_activator*)param;
bool swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}

//*********************************************
// Color
//*********************************************
void ObjParam_color::get_widget( GtkWidget *box, bool tt )
{
TvWidget_color::get_widget( box, tt );
memcpy( undo_data, data, sizeof(double)*4 );
gtk_signal_connect( GTK_OBJECT(picker), "color_set", GTK_SIGNAL_FUNC(sign_color_flush), this );
}


void ObjParam_color::get_widget( GtkWidget *tab, bool tt, int row )
{
TvWidget_color::get_widget( tab, tt, row );
memcpy( undo_data, data, sizeof(double)*4 );
gtk_signal_connect( GTK_OBJECT(picker), "color_set", GTK_SIGNAL_FUNC(sign_color_flush), this );	
}

void ObjParam_color::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_color *copy = new ObjParam_color( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_color::flush();
ObjParam::flush( app_ref );
}


void ObjParam_color::swap_data( ObjParam *param )
{
ObjParam_color *src = (ObjParam_color*)param;
double swap;
for ( int i = 0 ; i < 4 ; i++ )
	{
	swap = data[i];
	data[i] = src->data[i];
	src->data[i] = swap;
	}
update_widget();
ObjParam::flush( app_ref );
}



//*********************************************
// Point
//*********************************************
void ObjParam_point::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 0 );
obj = NULL;
	
button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_point_clicked), this );
gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );

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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_point_flush), this );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	gtk_widget_set_usize( spins[i], 10, -1 );
	}
pack_widget( box, tt, button );
memcpy( undo_data, data, sizeof(float)*3 );
}

void ObjParam_point::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_hbox_new( FALSE, 0 );
obj = NULL;
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
	
button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_point_clicked), this );
gtk_table_attach( GTK_TABLE(tab), button, 0, 1,  row-1, row, GTK_FILL, (GtkAttachOptions)0, 0, 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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_point_flush), this );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	gtk_widget_set_usize( spins[i], 10, -1 );
	}
memcpy( undo_data, data, sizeof(float)*3 );
set_tooltip( button );
}

void ObjParam_point::get_widget( GtkWidget *box, bool tt, void(*func)(GtkWidget*, gpointer ), gpointer dat  )
{
widget = gtk_hbox_new( FALSE, 0 );
callback = func;
obj = dat;

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_point_clicked), this );
gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );

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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(func), dat );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	gtk_widget_set_usize( spins[i], 10, -1 );
	}
pack_widget( box, tt, button );
memcpy( undo_data, data, sizeof(float)*3 );
}

void ObjParam_point::get_widget( GtkWidget *tab, bool tt, int row, void(*func)(GtkWidget*, gpointer ), gpointer dat  )
{
widget = gtk_hbox_new( FALSE, 0 );
callback = func;
obj = dat;
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_point_clicked), this );
//gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );
gtk_table_attach_defaults( GTK_TABLE(tab), button, 0, 1,  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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(func), dat );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	gtk_widget_set_usize( spins[i], 10, -1 );
	}
memcpy( undo_data, data, sizeof(float)*3 );
}


void ObjParam_point::get_widget_no_button( 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, TRUE, 1 );

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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_point_flush), this );
	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, button );
memcpy( undo_data, data, sizeof(float)*3 );
}


void ObjParam_point::get_widget_no_button( GtkWidget *tab, bool tt, int row )
{
widget = gtk_hbox_new( FALSE, 0 );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_alignment( GTK_MISC(label), 0, 0.5 );
gtk_table_attach( GTK_TABLE(tab), label, 0, 1,  row-1, row, (GtkAttachOptions)(GTK_EXPAND|GTK_FILL), (GtkAttachOptions)0, 0, 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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_point_flush), this );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
memcpy( undo_data, data, sizeof(float)*3 );
}

void ObjParam_point::mouse_drag( struct drag_info *drag )
{
if ( drag->first_click )
	{
	UNDO_DEF
	ObjParam_point *copy = new ObjParam_point( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	drag->first_click = false;
	reversible_change = true;
	}
else reversible_change = false;
	
float point[3];
double view_scale = ( drag->gdkview->height + drag->gdkview->width ) / 2 / sensitivity;
if ( drag->control ) { view_scale *= control_sensitivity; }
double xoffset = (double)( drag->current_x - drag->previous_x ) / view_scale;
double yoffset = (double)( drag->current_y - drag->previous_y ) / -view_scale;
drag->rotation->rotate_vector( point, xoffset, yoffset, 0.0 );

changing = true;
memcpy( undo_data, data, sizeof(float) * 3 );
TvWidget_point::add( point );
TvWidget_point::update_widget();
changing = false;
ObjParam::flush( app_ref );	
if ( obj != NULL ) callback( widget, obj );
}

void ObjParam_point::flush()
{
if ( in_update ) return;
if ( changing ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_point *copy = new ObjParam_point( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
for ( register int i = 0 ; i < 3 ; i ++ )
	if ( spins[i] != NULL ) gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
TvWidget_point::flush();
ObjParam::flush( app_ref );
}


void ObjParam_point::swap_data( ObjParam *param )
{
ObjParam_point *src = (ObjParam_point*)param;
double swap = 0;
for ( register int i = 0 ; i < 3 ; i++ )
	{
	swap = data[i];
	data[i] = src->data[i];
	src->data[i] = swap;
	}
update_widget();
ObjParam::flush( app_ref );
}

//*********************************************
// Point virtual
//*********************************************
void ObjParam_point_virtual::display( glview *view )
{
//cout << "\nDisplay point !"; cout.flush();
glColor3f( 1.0, 0, 0 );
glPointSize( 4 );
glBegin( GL_POINTS );
	glVertex3f( (GLfloat)data[0], (GLfloat)data[1], (GLfloat)data[2] );
glEnd();
}


//*********************************************
// Vector4
//*********************************************
void ObjParam_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, TRUE, 1 );

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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_vector4_flush), this );
	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, button );
memcpy( undo_data, data, sizeof(float)*3 );
}

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

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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_vector4_flush), this );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	}
memcpy( undo_data, data, sizeof(float)*3 );
}


void ObjParam_vector4::flush()
{
if ( in_update ) return;
if ( changing ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_vector4*copy = new ObjParam_vector4( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
for ( register int i = 0 ; i < 4 ; i ++ )
	if ( spins[i] != NULL ) gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
TvWidget_vector4::flush();
ObjParam::flush( app_ref );
}


void ObjParam_vector4::swap_data( ObjParam *param )
{
ObjParam_vector4 *src = (ObjParam_vector4*)param;
double swap = 0;
for ( register int i = 0 ; i < 4 ; i++ )
	{
	swap = data[i];
	data[i] = src->data[i];
	src->data[i] = swap;
	}
update_widget();
ObjParam::flush( app_ref );
}


//*****************************************************************
// Point  2D
//****************************************************************
void ObjParam_point_2d::get_widget( GtkWidget *box, bool tt, void(*func)(GtkWidget*, gpointer ), gpointer dat  )
{
callback = func;
obj = dat;
widget = gtk_hbox_new( FALSE, 0 );

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_point_clicked), this );
gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );

GtkObject *adj;
for ( int i = 0 ; i < 2 ; 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_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(func), dat );
	gtk_box_pack_start( GTK_BOX(widget), spins[i], TRUE, TRUE, 0 );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );
	gtk_widget_set_usize( spins[i], 10, -1 );
	}
pack_widget( box, tt, button );
memcpy( undo_data, data, sizeof(float)*3 );
}

void ObjParam_point_2d::update_widget() 
{
if ( !GTK_IS_WIDGET(widget) ) return;
in_update = true;
for ( int i = 0 ; i < 2 ; 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 ObjParam_point_2d::flush() 
{
//cout << "\n\tflushing"; cout.flush();
if ( !GTK_IS_WIDGET(widget) ) return;
if ( changing ) return;
TvWidget::flush();
reversible_change = true;
if ( send_undo == true ) 
	{
	UNDO_DEF
	ObjParam_point *copy = new ObjParam_point( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
for ( int i = 0 ; i < 2 ; 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 );
	}
reversible_change = false;
}

void ObjParam_point_2d::mouse_drag( struct drag_info *drag )
{
in_drag = true;
if ( drag->first_click )
	{
	if ( send_undo == true ) 
		{
		UNDO_DEF
		ObjParam_point *copy = new ObjParam_point( *this );
		undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
		}
	drag->first_click = false;
	reversible_change = true;
	}
else reversible_change = false;

float point[3];
double view_scale = ( drag->gdkview->height + drag->gdkview->width ) / 2 / sensitivity;
if ( drag->control ) { view_scale *= control_sensitivity; }
double xoffset = (double)( drag->current_x - drag->previous_x ) / view_scale;
double yoffset = (double)( drag->current_y - drag->previous_y ) / view_scale;

if ( for_lathe ) drag->rotation->rotate_vector( point, xoffset, -yoffset, 0.0 );
else drag->rotation->rotate_vector( point, xoffset, 0, yoffset );

changing = true;
memcpy( undo_data, data, sizeof(float) * 3 );
TvWidget_point::add( point );
update_widget();
changing = false;
callback( widget, obj );
in_drag = false;
}

//*********************************************
// Scale
//*********************************************
void ObjParam_scale::mouse_drag( struct drag_info *drag )
{
if ( drag->first_click )
	{
	if ( send_undo )
		{
		UNDO_DEF
		ObjParam_scale *copy = new ObjParam_scale( *this );
		undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
		}
	drag->first_click = false;
	reversible_change = true;
	}
reversible_change = false;

float vector[3];
double view_scale = ( drag->gdkview->height + drag->gdkview->width ) / 2 / sensitivity;
if ( drag->control ) view_scale *= control_sensitivity;

if ( drag->shift )
	{
	double xoffset = (double)( drag->previous_x - drag->center_x ) / view_scale;
	double yoffset = (double)( drag->previous_y - drag->center_y ) / view_scale;
	double dp = sqrt( xoffset*xoffset + yoffset*yoffset );
	xoffset = (double)( drag->current_x - drag->center_x ) / view_scale;
	yoffset = (double)( drag->current_y - drag->center_y ) / view_scale;
	double dc = sqrt( xoffset*xoffset + yoffset*yoffset );

	vector[0] = vector[1] = vector[2] = dc - dp;
	}
else
	{
	double xoffset = (double)( drag->current_x - drag->previous_x ) / view_scale;
	double yoffset = (double)( drag->current_y - drag->previous_y ) / view_scale;

	float point[3];
	if ( drag->origin_x < drag->center_x ) xoffset = -xoffset;
	if ( drag->origin_y < drag->center_y ) yoffset = -yoffset;
	drag->rotation->rotate_vector( point, xoffset, yoffset, 0.0 );
	point[2] = -point[2];

	OBJLIST_DEF
	Rotation *rot = objlist->get_current()->get_rotation();
	if ( rot == NULL ) return;
	rot->rotate_vector_inverse( vector, (double)point[0], (double)point[1], (double)point[2] );
	}
changing = true;
memcpy( undo_data, data, sizeof(float) * 3 );
TvWidget_point::add( vector );
TvWidget_point::update_widget();
changing = false;
ObjParam::flush( app_ref );	

}


//*********************************************
// Rotation
//*********************************************
ObjParam_rotation::ObjParam_rotation( ObjParam_rotation & ref ) : ObjParam( ref ), TvWidget( ref )
{
changing = false;
for ( int i = 0 ; i < 3 ; i++ ) data[i] = ref.data[i];
rotate.set_from_euler( data );
}

void ObjParam_rotation::get_widget( GtkWidget *box, bool tt )
{
widget = gtk_hbox_new( FALSE, 0 );

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_rotation_clicked), this );
gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );

GtkObject *adj;
for ( int i = 0 ; i < 3 ; i++ )
	{
	adj = gtk_adjustment_new( data[i], 0, 359, 1, 10, 0 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spins[i]), TRUE );
	gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_rotation_flush), this );
	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, button );
memcpy( undo_data, data, sizeof(double)*3 );
}

void ObjParam_rotation::get_widget( GtkWidget *tab, bool tt, int row )
{
widget = gtk_hbox_new( FALSE, 0 );
gtk_table_attach_defaults( GTK_TABLE(tab), widget, 1, 5,  row-1, row );

button = gtk_toggle_button_new();
gtk_container_set_border_width( GTK_CONTAINER(button), 1 );
GtkWidget *label = gtk_label_new( _(name) );
gtk_misc_set_padding( GTK_MISC(label), 5, 0 );
gtk_container_add( GTK_CONTAINER(button), label );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_rotation_clicked), this );
//gtk_box_pack_start( GTK_BOX(widget), button, FALSE, TRUE, 1 );
gtk_table_attach_defaults( GTK_TABLE(tab), button, 0, 1,  row-1, row );

GtkObject *adj;
for ( int i = 0 ; i < 3 ; i++ )
	{
	adj = gtk_adjustment_new( data[i], 0, 359, 1, 10, 0 );
	spins[i] = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(spins[i]), TRUE );
	gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_rotation_flush), this );
	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, button );
memcpy( undo_data, data, sizeof(double)*3 );
}

void ObjParam_rotation::mouse_drag( struct drag_info *drag )
{
if ( drag->first_click )
	{
	if ( send_undo )
		{
		UNDO_DEF
		ObjParam_rotation *copy = new ObjParam_rotation( *this );
		undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
		}
	drag->first_click = false;
	reversible_change = true;
	}
reversible_change = false;

if ( drag->previous_x == drag->center_x || drag->current_x == drag->center_x ) return;
double temp1 = drag->previous_x - drag->center_x;
double temp2 = drag->current_x - drag->center_x;
double up = (drag->center_y - drag->previous_y)/temp1 -  (drag->center_y - drag->current_y) / temp2;
up *= temp1 * - temp2;

double down = drag->center_y * ( drag->current_y + drag->previous_y - drag->center_y );
down += drag->center_x * ( drag->current_x + drag->previous_x - drag->center_x );
down -= drag->previous_y * drag->current_y + drag->previous_x * drag->current_x;
if ( down == 0 ) return;

double phi = atan( up/ down );
if ( drag->control ) phi /= control_sensitivity;
double axis[3];
drag->rotation->rotate_z( axis );

changing = true;
rotate.add( axis, phi );
memcpy( undo_data, data, sizeof(double)*3 );
rotate.get_as_euler( data );

for ( int i = 0 ; i < 3 ; i++ )
	{
	gtk_spin_button_set_value( GTK_SPIN_BUTTON(spins[i]), data[i] );
	gtk_editable_set_position( GTK_EDITABLE(spins[i] ), 0 );	
   }
changing = false;
ObjParam::flush( app_ref );	
}

void ObjParam_rotation::flush()
{
if ( changing ) return;
if ( in_update ) return;
if ( !GTK_IS_WIDGET(widget) ) return; 
UNDO_DEF
ObjParam_rotation *copy = new ObjParam_rotation( *this );
undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
//memcpy( undo_data, data, sizeof(double) * 3 );

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 );
	}
rotate.set_from_euler( data );
TvWidget::flush();
ObjParam::flush( app_ref );
}

void ObjParam_rotation::swap_data( ObjParam *param )
{
ObjParam_rotation *src = (ObjParam_rotation*)param;
double swap = 0;
for ( register int i = 0 ; i < 3 ; i++ )
	{
	swap = data[i];
	data[i] = src->data[i];
	src->data[i] = swap;
	}
rotate.set_from_euler( data );

in_update = true;
for ( int i = 0 ; i < 3 ; i++ )
	gtk_spin_button_set_value( GTK_SPIN_BUTTON(spins[i]), data[i] );
in_update = false;
ObjParam::flush( app_ref );
}


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


bool ObjParam_rotation::load( ifstream & file, char *tag )
{
if ( strcmp( sname, tag ) ) return false;

char * val = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) break;

	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; }
	} while ( val != NULL );
rotate.set_from_euler( data );
return true;
}


//*********************************************
// Texture reference
//*********************************************
ObjParam_texref::ObjParam_texref( const char *name, const char *sname, const char *tooltip, app_objs *appref, bool refr ) 
	:  ObjParam( refr ), TvWidget( name, sname, tooltip, appref)
{
tex_link = new TvWidget_option_combo( N_("Material"), "MAT", NULL, app_ref );
current = NULL;
in_update = false;
}

ObjParam_texref::ObjParam_texref( ObjParam_texref & ref )  : ObjParam( ref ), TvWidget( ref )
{
tex_link = new TvWidget_option_combo( *ref.tex_link );
current = ref.current;
in_update = false;
}

void ObjParam_texref::get_widget( GtkWidget *box, bool tt ) 
{
MATLIST_DEF
const char **ref_list = (const char**)matlist->get_mat_name_list();
int ref_num = matlist->get_mat_name_list_size();
int current_index = matlist->get_index_by_pointer( current ) + 1;
tex_link->set_list( ref_list, ref_num, current_index );
matlist->set_current_ref( this );

tex_link->get_widget( box, tt );
tex_link->connect_signal( GTK_SIGNAL_FUNC(sign_ref_changed), this );
	
GtkWidget *button = gtk_button_new();
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_texref_edit), this );
	GtkWidget *pix = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON );
	gtk_container_add( GTK_CONTAINER( button), pix );
	gtk_button_set_relief( GTK_BUTTON(button), GTK_RELIEF_NONE );
	GTK_WIDGET_UNSET_FLAGS( button, GTK_CAN_FOCUS );	
	gtk_box_pack_start(	GTK_BOX(tex_link->get_gtk_widget()), button, FALSE, FALSE, 4 );
}


void ObjParam_texref::get_widget( GtkWidget *tab, bool tt, int row ) 
{
MATLIST_DEF
const char **ref_list = (const char**)matlist->get_mat_name_list();
int ref_num = matlist->get_mat_name_list_size();
int current_index = matlist->get_index_by_pointer( current ) + 1;
tex_link->set_list( ref_list, ref_num, current_index );
matlist->set_current_ref( this );

tex_link->get_widget( tab, tt, row );
tex_link->connect_signal( GTK_SIGNAL_FUNC(sign_ref_changed), this );
	
GtkWidget *button = gtk_button_new();
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_texref_edit), this );
	GtkWidget *pix = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON );
	gtk_container_add( GTK_CONTAINER( button), pix );
	gtk_button_set_relief( GTK_BUTTON(button), GTK_RELIEF_NONE );
	GTK_WIDGET_UNSET_FLAGS( button, GTK_CAN_FOCUS );	
	gtk_box_pack_start(	GTK_BOX(tex_link->get_gtk_widget()), button, FALSE, FALSE, 4 );
}

void ObjParam_texref::edit_material() 
{
if ( current == NULL ) return;
MATLIST_DEF
matlist->raise_texeditor( current );
}


void ObjParam_texref::update_ref_list()
{
MATLIST_DEF
const char **ref_list = (const char**)matlist->get_mat_name_list();
int ref_num = matlist->get_mat_name_list_size();
int current_index = matlist->get_index_by_pointer( current ) + 1;
if ( current_index == 0 )  current = NULL;

tex_link->set_list( ref_list, ref_num, current_index );
in_update = true;
tex_link->update_widget();
in_update = false;
}

void ObjParam_texref::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_texref *copy = new ObjParam_texref( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
MATLIST_DEF
tex_link->flush();
if ( tex_link->value() > 0 )
	current = matlist->get_pointer_by_index( tex_link->value()-1 );
else current = NULL;
ObjParam::flush( app_ref );
}

void ObjParam_texref::swap_data( ObjParam *param )
{
ObjParam_texref *src = (ObjParam_texref*)param;
int swap = tex_link->value();
tex_link->set( src->tex_link->value() );
src->tex_link ->set( swap );
MATLIST_DEF
current = matlist->get_pointer_by_index( tex_link->value()-1 );
in_update = true;
tex_link->update_widget();
in_update = false;
ObjParam::flush( app_ref );
}

void ObjParam_texref::clear_widget()
{
widget = NULL;
tex_link->clear_widget();
MATLIST_DEF
matlist->set_current_ref( NULL );
}

void ObjParam_texref::output_to_povray( ofstream & file )
{
MATLIST_DEF
int current_index = matlist->get_index_by_pointer( current ) + 1;
if ( current_index == 0 )  current = NULL;

if ( current == NULL ) { return; }
if ( !current->is_used() ) { return; }
file << "\tmaterial { ";
current->get_underscore_name( file );
file << " }";
}

void ObjParam_texref::save( ofstream & file )
{
MATLIST_DEF
int current_index = matlist->get_index_by_pointer( current ) + 1;
if ( current_index == 0 )  current = NULL;
TvWidget::save( file );
file << "VALUE=\"" << (( current == NULL || !current->is_used() ) ? "NONE" : current->get_name()) << '\"';
file << "} ";
}

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

do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) break;
	if ( ! strcmp( val, "VALUE" ) )
		{
		res= tvio_get_value_as_string( file );
		MATLIST_DEF
		if ( res == NULL || ! strcmp( res, "NONE" ) )
			current = NULL;
		else
			current = matlist->get_pointer_by_name( res );
		continue;
		}
	}  while ( val != NULL );
return true;
}

void ObjParam_texref::get_material( vector<Material*> & mlist )
{
MATLIST_DEF
int current_index = matlist->get_index_by_pointer( current ) + 1;
if ( current_index == 0 )  current = NULL;
if ( current == NULL ) return;
for ( unsigned int i = 0 ; i < mlist.size() ; i ++ )
	if ( mlist[i] == current ) return;
mlist.push_back( current );
}



//*********************************************
// Object reference
//*********************************************
ObjParam_objref::ObjParam_objref( const char *name, const char *sname, const char *tooltip, app_objs *appref, bool refr ) 
	:  ObjParam( refr ), TvWidget( name, sname, tooltip, appref)
{
obj_link = new TvWidget_option_combo( name, "OBJR", NULL, app_ref );
current = NULL;
in_update = false;
}

ObjParam_objref::ObjParam_objref( ObjParam_objref & ref )  : ObjParam( ref ), TvWidget( ref )
{
obj_link = new TvWidget_option_combo( *ref.obj_link );
current = ref.current;
in_update = false;
}

void ObjParam_objref::get_widget( GtkWidget *box, bool tt ) 
{
OBJLIST_DEF
const char **ref_list = (const char**)objlist->get_obj_name_list();
int ref_num = objlist->get_obj_name_list_size();
int current_index = objlist->get_index_by_pointer( current )+1;
obj_link->set_list( ref_list, ref_num, current_index );
objlist->set_current_ref( this );

obj_link->get_widget( box, tt );
obj_link->connect_signal( GTK_SIGNAL_FUNC(sign_ref_changed), this );
}

void ObjParam_objref::get_widget( GtkWidget *tab, bool tt, int row ) 
{
OBJLIST_DEF
const char **ref_list = (const char**)objlist->get_obj_name_list();
int ref_num = objlist->get_obj_name_list_size();
int current_index = objlist->get_index_by_pointer( current )+1;
obj_link->set_list( ref_list, ref_num, current_index );
objlist->set_current_ref( this );

obj_link->get_widget( tab, tt, row );
obj_link->connect_signal( GTK_SIGNAL_FUNC(sign_ref_changed), this );
}

void ObjParam_objref::update_ref_list()
{
OBJLIST_DEF
const char **ref_list = (const char**)objlist->get_obj_name_list();
int ref_num = objlist->get_obj_name_list_size();
int current_index = objlist->get_index_by_pointer( current ) + 1;
if ( current_index == 0 )  current = NULL;

obj_link->set_list( ref_list, ref_num, current_index );
in_update = true;
obj_link->update_widget();
in_update = false;
}

void ObjParam_objref::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_objref *copy = new ObjParam_objref( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
OBJLIST_DEF
obj_link->flush();
if ( obj_link->value() > 0 )
	current = objlist->get_pointer_by_index( obj_link->value()-1 );
else current = NULL;
ObjParam::flush( app_ref );
}

void ObjParam_objref::swap_data( ObjParam *param )
{
ObjParam_objref *src = (ObjParam_objref*)param;
int swap = obj_link->value();
obj_link->set( src->obj_link->value() );
src->obj_link ->set( swap );
OBJLIST_DEF
current = objlist->get_pointer_by_index( obj_link->value()-1 );
in_update = true;
obj_link->update_widget();
in_update = false;
ObjParam::flush( app_ref );
}

void ObjParam_objref::clear_widget()
{
widget = NULL;
obj_link->clear_widget();
OBJLIST_DEF
objlist->set_current_ref( NULL );
}

void ObjParam_objref::save( ofstream & file )
{
TvWidget::save( file );
file << "VALUE=\"" << (( current == NULL  ) ? "NONE" : current->get_name()) << '\"';
file << "} ";
}

bool ObjParam_objref::load( ifstream & file, char *tag )
{
if ( strcmp( sname, tag ) ) return false;
char * val = NULL;
char *res = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) break;
	if ( ! strcmp( val, "VALUE" ) )
		{
		res= tvio_get_value_as_string( file );
		OBJLIST_DEF
		if ( res == NULL || ! strcmp( res, "NONE" ) )
			current = NULL;
		else
			current = objlist->get_pointer_by_name( res );
		continue;
		}
	}  while ( val != NULL );
return true;
}




//*********************************************
// entry
//*********************************************
void ObjParam_entry::get_widget( GtkWidget *box, bool tt )
{
TvWidget_entry::get_widget( box, tt );
gtk_signal_connect( GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(sign_objparam_entry_flush), this );
}

void ObjParam_entry::get_widget( GtkWidget *tab, bool tt, int row )
{
TvWidget_entry::get_widget( tab, tt, row );
gtk_signal_connect( GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(sign_objparam_entry_flush), this );
}

void ObjParam_entry::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_entry *copy = new ObjParam_entry( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_entry::flush();
ObjParam::flush( app_ref );
}

void ObjParam_entry::swap_data( ObjParam *param )
{
ObjParam_entry *src = (ObjParam_entry*)param;
char *swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}

//*********************************************
// path
//*********************************************
void ObjParam_path::get_widget( GtkWidget *box, bool tt )
{
TvWidget_path::get_widget( box, tt );
connect_signal(  GTK_SIGNAL_FUNC(sign_objparam_path_flush), this );
//gtk_signal_connect( GTK_OBJECT(fentry), "selection-changed",GTK_SIGNAL_FUNC(sign_objparam_path_flush), this );
}

void ObjParam_path::get_widget( GtkWidget *tab, bool tt, int row )
{
TvWidget_path::get_widget( tab, tt, row );
connect_signal(  GTK_SIGNAL_FUNC(sign_objparam_path_flush), this );
//gtk_signal_connect( GTK_OBJECT(fentry), "selection-changed",GTK_SIGNAL_FUNC(sign_objparam_path_flush), this );
}

void ObjParam_path::flush()
{
if ( in_update ) return;
if ( send_undo )
	{
	UNDO_DEF
	ObjParam_path *copy = new ObjParam_path( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
TvWidget_path::flush();
ObjParam::flush( app_ref );
}

void ObjParam_path::swap_data( ObjParam *param )
{
ObjParam_path *src = (ObjParam_path*)param;
char *swap = data;
data = src->data;
src->data = swap;
update_widget();
ObjParam::flush( app_ref );
}

//*********************************************
// file
//*********************************************
void ObjParam_file::get_widget( GtkWidget *box, bool tt )
{
TvWidget_file::get_widget( box, tt );
connect_signal(  GTK_SIGNAL_FUNC(sign_objparam_file_flush), this );
}

void ObjParam_file::get_widget( GtkWidget *box, bool tt, int row )
{
TvWidget_file::get_widget( box, tt, row );
connect_signal(  GTK_SIGNAL_FUNC(sign_objparam_file_flush), this );
}

void ObjParam_file::flush()
{
if ( in_update ) return;
if ( send_undo )
	{	
	UNDO_DEF
	ObjParam_file *copy = new ObjParam_file( *this );
	undoman->push( TV_UNDO_PARAM_CHANGED, this, copy );
	}
//TvWidget_file::flush();
ObjParam::flush( app_ref );
}

void ObjParam_file::swap_data( ObjParam *param )
{
//ObjParam_file *src = (ObjParam_file*)param;
TvWidget_file::swap_data( (TvWidget_file*)param );
update_widget();
ObjParam::flush( app_ref );
}
