//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// mapedit.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Copyright (C) 2000-2001 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/mapedit.h"
#include "include/dlgutils.h"
#include "include/tvio.h"

const float diff = 0.0000001;

//********************************************
// MAP ITEM
//********************************************
MapItem::MapItem( char *nom, float val = -1 )
{
name = nom;
value = val;
button = NULL;
}


MapItem::MapItem( MapItem & res )
{
name = res.name;
value = res.value;
button = NULL;
}


void MapItem::get_widget( GtkWidget *tbox, bool tt, TvWidget_map_editor *mother )
{
refresh_name();
button = gtk_toggle_button_new_with_label( name );
gtk_button_set_relief( GTK_BUTTON(button), GTK_RELIEF_HALF );
gtk_box_pack_start( GTK_BOX(tbox), button, TRUE, TRUE, 0 ); 
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_mapedit_selection), mother );
}

void MapItem::save( ofstream & file )
{ 
file << "\nMAPITEM{ PARAMS{ VALUE=" << value;
file << " R=" << (int)color[0] << " G=" << (int)color[1] << " B=" << (int)color[2] << " A=" << (int)color[3] << " } ";
}


bool MapItem::load( ifstream & file, char * tag )
{
if ( strcmp( "PARAMS", tag ) ) return false;
char * val = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return true;
	if ( ! strcmp( val, "VALUE" ) ) { value = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "R" ) ) { color[0] = tvio_get_value_as_int( file ); continue; }
	if ( ! strcmp( val, "G" ) ) { color[1] = tvio_get_value_as_int( file ); continue; }
	if ( ! strcmp( val, "B" ) ) { color[2] = tvio_get_value_as_int( file ); continue; }
	if ( ! strcmp( val, "A" ) ) { color[3] = tvio_get_value_as_int( file ); continue; }
	}
while( val != NULL );
return true;
}


//********************************************
// MAP EDITOR
//********************************************
// Constantes
const int grad_x = 15;
const int grad_y = 3;
const int grad_w = 50;
const int linebreak2 = grad_x + grad_w + 1;
const guchar fond_col1[3] = { 180, 180, 180 };
const guchar fond_col2[3] = { 120, 120, 120 };

// Constructeur
TvWidget_map_editor::TvWidget_map_editor(  const char *name, const char *sname, const char *tooltip, app_objs *appref, MapItem* (*func)(gpointer), gpointer par )
			: TvWidget( name, sname, tooltip, appref )
{
widget = NULL;
cursor = NULL;
pointer = NULL;
grad_image = NULL;
drawing_pix = NULL;
items_box = NULL;
in_change = false;
selected = 0;
item_feeder = func;
item_feeder_param = par;
tree_store = NULL;
editor_box = NULL;
in_drag = false;
}


// Copy Constructor
TvWidget_map_editor::TvWidget_map_editor(  TvWidget_map_editor & res ) : TvWidget( res )
{
widget = NULL;
cursor = NULL;
pointer = NULL;
grad_image = NULL;
drawing_pix = NULL;
items_box = NULL;
in_change = false;
selected = 0;
item_feeder = res.item_feeder;
item_feeder_param = res.item_feeder_param;
tree_store = NULL;
editor_box = NULL;
in_drag = false;
for ( unsigned int i = 0 ; i < res.item_list.size() ; i++ )
	{
	MapItem *nitem = res.item_list[i]->duplicate_yourself();
	item_list.push_back( nitem );
	}
}


// Destructeur
TvWidget_map_editor::~TvWidget_map_editor()
{
clear_widget();
for ( unsigned int i = 0 ; i < item_list.size() ; i++ )
	delete item_list[i];
}

// Nettoyage
void TvWidget_map_editor::clear_widget()
{
if ( drawing_pix != NULL ) gdk_pixmap_unref( drawing_pix );
if ( cursor != NULL ) { gdk_pixmap_unref( cursor ); gdk_pixmap_unref( cursor_mask ); }
if ( pointer != NULL ) gdk_cursor_destroy( pointer );
if ( grad_image != NULL ) delete grad_image;
widget = NULL;
drawing_pix = NULL;
cursor = NULL;
pointer = NULL;
grad_image = NULL;
items_box = NULL;
editor_box = NULL;
in_drag = false;
//for ( unsigned int i = 0 ; i < item_list.size() ; i++ )
//	item_list[i]->destroy_editor();
}


//*************************************
// GET WIDGET
//*************************************
void TvWidget_map_editor::get_widget( GtkWidget *box, bool tt )
{
// Frame
widget = dlg_simple_box_frame( name, box );
gtk_container_set_border_width( GTK_CONTAINER(widget), 3 );

// Bouttons
GtkWidget *hbox = gtk_hbox_new( TRUE, 3 );
gtk_box_pack_start( GTK_BOX(widget), hbox, TRUE, TRUE, 1 );
	GtkWidget *button = gtk_button_new_with_label( _("Append") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_mapedit_append_item), this );
	button = gtk_button_new_with_label( _("Prepend") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_mapedit_prepend_item), this );
	button = gtk_button_new_with_label( _("Duplicate") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_mapedit_duplicate_item), this );
	button = gtk_button_new_with_label( _("Delete") );
	gtk_box_pack_start( GTK_BOX(hbox), button, TRUE, TRUE, 1 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_mapedit_delete_item), this );
	
// Status
//status = gtk_label_new( "" );
//gtk_box_pack_start( GTK_BOX(widget), status, TRUE, FALSE, 0 );

// Conteneur horizontal
horbox = gtk_hbox_new( TRUE, 0 );
gtk_box_pack_start( GTK_BOX(widget), horbox, TRUE, TRUE, 2 );

// Gradient
drawing_area = gtk_drawing_area_new();
gtk_widget_set_events( drawing_area, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
gtk_signal_connect( GTK_OBJECT(drawing_area), "realize", GTK_SIGNAL_FUNC(sign_mapedit_realize), this );
gtk_signal_connect( GTK_OBJECT(drawing_area), "expose_event", GTK_SIGNAL_FUNC(sign_mapedit_expose), this );
gtk_signal_connect( GTK_OBJECT(drawing_area), "configure_event", GTK_SIGNAL_FUNC(sign_mapedit_configure), this );
gtk_signal_connect( GTK_OBJECT(drawing_area), "button_press_event", GTK_SIGNAL_FUNC(sign_mapedit_mouse_click), this );
gtk_signal_connect( GTK_OBJECT(drawing_area), "button_release_event", GTK_SIGNAL_FUNC(sign_mapedit_mouse_release), this );
gtk_signal_connect( GTK_OBJECT(drawing_area), "motion_notify_event", GTK_SIGNAL_FUNC(sign_mapedit_mouse_moved), this );
gtk_widget_set_usize( drawing_area, -1, 200 );
gtk_box_pack_start( GTK_BOX(horbox), drawing_area, TRUE, TRUE, 0 );

// Edition de la valeur dans le gradient
GtkWidget *valbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(widget), valbox, TRUE, TRUE, 0 );
GtkWidget *vallabel = gtk_label_new( _("Value") );
gtk_box_pack_start( GTK_BOX(valbox), vallabel, TRUE, TRUE, 0 );
GtkObject *adj = gtk_adjustment_new( 0, 0, 1, 0.01, 0.1, 0 );
value_entry = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
gtk_box_pack_start( GTK_BOX(valbox), value_entry, TRUE, TRUE, 0 );
gtk_signal_connect( GTK_OBJECT(value_entry), "changed", GTK_SIGNAL_FUNC(sign_mapedit_valentry_changed), this );

rebuild_item_list();
set_item_editor();

//char text[30];
valentry_inchange = true;
//sprintf( text, "%f", item_list[selected]->value );
//gtk_entry_set_text( GTK_ENTRY(value_entry), text );
gtk_spin_button_set_value( GTK_SPIN_BUTTON(value_entry) , item_list[selected]->value );
valentry_inchange = false;
} 

// Set Editor
void TvWidget_map_editor::set_item_editor()
{
valentry_inchange = true;
//char str[25];
//snprintf( str, 24, "%f", (item_list[selected]->value) );
gtk_spin_button_set_value( GTK_SPIN_BUTTON(value_entry) , item_list[selected]->value );
valentry_inchange = false;

if ( editor_box != NULL ) gtk_widget_destroy( editor_box );
editor_box = gtk_hbox_new( TRUE, 0 );
gtk_box_pack_start( GTK_BOX(widget), editor_box, TRUE, TRUE, 0 );
item_list[selected]->get_editor( editor_box, true );
item_list[selected]->connect_signal(  &sign_mapedit_item_changed, (gpointer)this );
gtk_widget_show_all( editor_box );
}


// Item List
void TvWidget_map_editor::rebuild_item_list()
{
if ( items_box != NULL ) gtk_widget_destroy( items_box );
items_box = gtk_vbox_new( TRUE, 1 );
gtk_box_pack_start( GTK_BOX(horbox), items_box, TRUE, TRUE, 0 );
for ( unsigned int i = 0 ; i < item_list.size() ; i++ )
	(item_list[i])->get_widget( items_box, true, this );
in_change = true;
item_list[selected]->toggle();
set_item_editor();
in_change = false;
gtk_widget_show_all( items_box );
}


void TvWidget_map_editor::flush()
{
//for ( unsigned int i = 0 ; i < item_list.size() ; i++ )
//	(item_list[i])->flush();
(item_list[selected])->flush();
}

//*************************************
// DRAW
//*************************************
void TvWidget_map_editor::expose( GdkEventExpose *ev )
{
// Nettoyage
//gdk_draw_rectangle( drawing_pix, drawing_area->style->bg_gc[drawing_area->state], TRUE, ev->area.x, ev->area.y, ev->area.width, ev->area.height );
gdk_draw_rectangle( drawing_pix, drawing_area->style->bg_gc[drawing_area->state], TRUE, 0, 0, width, height );

// Cadre du gradient 
gdk_draw_rectangle( drawing_pix, drawing_area->style->fg_gc[drawing_area->state], FALSE, grad_x, grad_y, grad_w, grad_h );

// Labels
gdk_draw_text( drawing_pix, gtk_style_get_font(drawing_area->style), drawing_area->style->fg_gc[drawing_area->state], grad_x-11, grad_y+11, "0", 1 );
gdk_draw_text( drawing_pix, gtk_style_get_font(drawing_area->style), drawing_area->style->fg_gc[drawing_area->state], grad_x-11, grad_h, "1", 1 );
update_gradient();

// Items
unsigned int size = item_list.size();
int item_widget_height = height / size;
for ( register unsigned int i = 0 ; i < size ; i++ )
	{
	int point = (int)( (float)(grad_h-2) * item_list[i]->value ) + grad_y + 1;
	int base = item_widget_height * i + item_widget_height / 2;
	item_list[i]->cursor_pos = point-5;
	if ( (int)i == selected )
		{
		gdk_draw_line( drawing_pix, drawing_area->style->fg_gc[drawing_area->state], linebreak1, base, height-1, base );
		gdk_draw_line( drawing_pix, drawing_area->style->fg_gc[drawing_area->state], linebreak2, point, linebreak1, point );
		gdk_draw_line( drawing_pix, drawing_area->style->fg_gc[drawing_area->state], linebreak1, point, linebreak1, base );
		}
	gdk_draw_pixmap( drawing_pix, drawing_area->style->fg_gc[drawing_area->state], cursor, 0, 0, linebreak2+1, point-5, 6, 11 );
	}

//GdkRectangle rect; rect.width = width - 1; rect.height = height-1;
//cout << "\nredraw x->" << ev->area.x << " - y->" << ev->area.y ; cout.flush();
//gdk_gc_set_clip_origin( 	drawing_area->style->fg_gc[drawing_area->state], 0, 0 );
//gdk_gc_set_clip_rectangle( drawing_area->style->fg_gc[drawing_area->state], &rect );
//gdk_draw_pixmap( drawing_area->window, drawing_area->style->fg_gc[drawing_area->state], drawing_pix, 0, 0, 0, 0, width, height );
//gdk_gc_set_clip_rectangle( drawing_area->style->fg_gc[drawing_area->state], NULL );
	
gdk_gc_set_clip_rectangle( drawing_area->style->fg_gc[drawing_area->state], &ev->area );
gdk_draw_pixmap( drawing_area->window, drawing_area->style->fg_gc[drawing_area->state], drawing_pix, ev->area.x, ev->area.y, ev->area.x, ev->area.y, ev->area.width, ev->area.height );
gdk_gc_set_clip_rectangle( drawing_area->style->fg_gc[drawing_area->state], NULL );
}


//*****************************************
// Gradient
//*****************************************
// Draw line
void TvWidget_map_editor::draw_gradient_line( int pos, guint8 *col )
{
int cols = ( grad_w - 3 ) * 3;
guchar colpond[3];
guchar coltemp[3];
guchar prev = 0;
float alphapond = 255 - col[3];
for ( register int i = 0 ; i < 3 ; i++ ) colpond[i] = (guchar)(( (float)col[i] * col[3] ) / 255.0);
int line = pos * cols;
for ( register int w = 0 ; w < cols ; w += 3 )
	{
	int pt = line + w;
	if ( prev !=  grad_image[pt] )
		{
		prev = grad_image[pt];
		guchar pond = (guchar)( ( (float)grad_image[pt] * alphapond ) / 255.0 );
		for ( int i = 0 ; i < 3 ; i++ )
			coltemp[i] = grad_image[pt++] = colpond[i] + pond;
		}
	else
		{
		grad_image[pt++] = coltemp[0];
		grad_image[pt++] = coltemp[1];
		grad_image[pt] = coltemp[2];
		}
	}
}

// Update
void TvWidget_map_editor::update_gradient()
{
register int pos = 0;
int lines = grad_h-3;

// Fond
int cols = ( grad_w - 3 ) * 3 ;
int hcols = (cols / 2);
guchar line1[cols], line2[cols];
for ( register int w = 0 ; w < hcols ; w += 3 )
	{
	line1[w] = fond_col1[0];
	line2[w] = fond_col2[0];
	line1[w+1] = fond_col1[1];
	line2[w+1] = fond_col2[1];
	line1[w+2] = fond_col1[2];
	line2[w+2] = fond_col2[2];	
	}
memcpy( line1 + hcols + 1, line2, sizeof(guchar)*hcols );
memcpy( line2 + hcols + 1, line1, sizeof(guchar)*hcols );
while ( pos < lines-5 )
	{
	for ( int i = 0 ; i < 32 ; i++ )
		{
		if ( pos == lines ) break;
		memcpy( grad_image + pos*cols, line1, sizeof(guchar)*cols );
		pos++;
		}
	for ( int i = 0 ; i < 32 ; i++ )
		{
		if ( pos == lines ) break;
		memcpy( grad_image + pos*cols, line2, sizeof(guchar)*cols );
		pos++;
		}		
	}


// Premiere couleur
int next_pos = (int)( item_list[0]->value * (float)(lines) ) - 1;
guint8 *col = item_list[0]->get_color();
for ( pos = 0 ; pos < next_pos ; pos++ )
	draw_gradient_line( pos, col );

// Gradients	
int size = item_list.size();
for ( int i = 0 ; i < size-1 ; i++ )
	{
	guint8 col1[4], col2[4];
	memcpy( col1, item_list[i]->get_color(), sizeof(guint8)*4 );
	memcpy( col2, item_list[i+1]->get_color(), sizeof(guint8)*4 );

	next_pos = (int)( item_list[i+1]->value * (float)(lines) ) - 1;
	float progre[4];
	float offset = next_pos - pos;
	for ( int y = 0 ; y < 4 ; y++ ) progre[y] = (float)( col2[y] - col1[y] ) / offset ;

	float diff;
	for ( diff = 0 ; pos < next_pos ; pos++ )
		{
		col2[0] = (guint)(col1[0] + diff * progre[0]);
		col2[1] = (guint)(col1[1] + diff * progre[1]);
		col2[2] = (guint)(col1[2] + diff * progre[2]);
		col2[3] = (guint)(col1[3] + diff++ * progre[3]);		
		draw_gradient_line( pos, col2 );
		}
	}

// Derniere couleur
col = item_list[size-1]->get_color();
for ( pos = pos ; pos < lines ; pos++ )
	draw_gradient_line( pos, col );

// Copie dans le buffer
gdk_draw_rgb_image( drawing_pix, drawing_area->style->fg_gc[drawing_area->state], 
	grad_x+2, grad_y+2, grad_w-3, grad_h-3, GDK_RGB_DITHER_NORMAL, grad_image, (grad_w-3)*3 );	
}

// Force redraw
void TvWidget_map_editor::invalidate_drawing_area()
{
GdkRectangle rect;
rect.x = grad_x; rect.y = grad_y-5; rect.width = width-grad_x; rect.height = grad_h+10;
gtk_widget_draw( drawing_area, &rect );
}

//****************************************
// Configure & REALIZE
//***************************************
void TvWidget_map_editor::realize()
{
// Creation du cursor
if ( cursor != NULL ) { gdk_pixmap_unref( cursor ); gdk_pixmap_unref( cursor_mask ); } 
char *pixmap = tv_get_pixmap( "mapedit_cursor.xpm"  );

cursor = gdk_pixmap_create_from_xpm( drawing_area->window, &cursor_mask, drawing_area->style->bg, pixmap );
delete pixmap;

// Pointeur
if ( pointer != NULL ) gdk_cursor_destroy( pointer );
pointer = gdk_cursor_new( GDK_HAND2 );
}

void TvWidget_map_editor::configure()
{
//cout << "\nConfigure !"; cout.flush();
width = drawing_area->allocation.width;
height = drawing_area->allocation.height;
grad_h = height - 8;
if ( grad_h < 0 ) return;
linebreak1 = width - (width - grad_x - grad_w) / 2;

// Gradient
if ( grad_image != NULL ) delete grad_image;
grad_image = new guchar[ (grad_w-3) * (grad_h-3) * 3 ];

// PB
// Recoit deux signaux 'configure' dont un avec des valeurs ( taille de fenetre )
// négatives qui font crasher l'attribution de mémoire. Le test n'est pas
// définitif mais semble marcher pour le moment.
//cout << "\nAttribution de : " << grad_w-3 << " x " << grad_h-3 << " x 3 octets";
//cout.flush();

// Drawing pixmap
if ( drawing_pix != NULL ) gdk_pixmap_unref( drawing_pix );
drawing_pix = gdk_pixmap_new( drawing_area->window, width, height, -1 );	
//invalidate_drawing_area();
}


//****************************************
// MOUSE
//***************************************
// get mouse position ( over a cursor ? )
int TvWidget_map_editor::test_mouse_pos( int xpos, int ypos, int sens, bool ignore_current, int current  )
{
bool is_inside = false;
int size = item_list.size();
int min_x = grad_x + grad_w;
int max_x = min_x + 11;
register int i;

// Without control-key try to keep already selected item
// Usefull for overlapped cursors
if ( ! ignore_current )
	{
	if ( xpos > min_x && xpos < max_x && ypos > item_list[current]->cursor_pos && ypos < item_list[current]->cursor_pos + 11 )
		return current;
	}

// Otherwise do classic selection
// From top to bottom with left click
// and from bottom to top with right click
/*int start = ( sens == 1 ) ? 0 : size;
int stop = ( sens == 1 )  ? size : 0;
int inc = ( sens == 1 ) ? 1 : -1;*/
if ( sens == 1 )
	{
	for ( i = 0 ; i < size ; i ++ )
		{
		if ( xpos < min_x ) continue;
		if ( xpos > max_x ) continue;
		if ( ypos < item_list[i]->cursor_pos ) continue;
		if ( ypos > item_list[i]->cursor_pos + 11 ) continue;
		is_inside = true; break;
		}
	}
else
	{
	for ( i = size-1 ; i > -1  ; i-- )
		{
		if ( xpos < min_x ) continue;
		if ( xpos > max_x ) continue;
		if ( ypos < item_list[i]->cursor_pos ) continue;
		if ( ypos > item_list[i]->cursor_pos + 11 ) continue;
		is_inside = true; break;
		}
	}
if ( !is_inside ) return -1;
if ( i == 0 && item_list[0]->value == 0 && (item_list[1]->cursor_pos - item_list[0]->cursor_pos) < 12 ) return 1;
else return i;
}


// Mouse click
void TvWidget_map_editor::mouse_click( GdkEventButton *ev )
{
GdkModifierType state;
int drag_x;
gdk_window_get_pointer( ev->window, &drag_x, &drag_y, &state );

int sens = ( ev->button == 1 ) ? 1 : 0 ;
int drag_item = test_mouse_pos( drag_x, drag_y,  sens, ( state & GDK_CONTROL_MASK ), selected );
if ( drag_item == -1 ) return;

if ( selected != drag_item )
	{
	in_change = true;
	item_list[selected]->untoggle();
	selected = drag_item;
	item_list[selected]->toggle();
	set_item_editor();
	in_change = false;
	}

if ( selected== 0 ) drag_min = 0;
else drag_min = item_list[selected-1]->value + diff;
if ( selected== (int)item_list.size() - 1 ) drag_max = 1;
else drag_max = item_list[selected+1]->value - diff;
in_drag = true;
}

void TvWidget_map_editor::mouse_moved( GdkEventMotion *ev )
{
int xpos, ypos;
GdkModifierType state;

if ( ev->is_hint ) gdk_window_get_pointer( ev->window, &xpos, &ypos, &state );
else {
	xpos = (int)ev->x;
	ypos = (int)ev->y;
	state = (GdkModifierType)ev->state;
	}
if ( (state & GDK_BUTTON1_MASK) || (state & GDK_BUTTON3_MASK) )
	{
	if ( in_drag == false ) return;
	if ( selected == -1 ) return;
	float offset = ( 1.0 / (float)grad_h ) * (float)( drag_y - ypos );
	item_list[selected]->value -= offset;
	if ( item_list[selected]->value < drag_min ) item_list[selected]->value = drag_min;
	if ( item_list[selected]->value > drag_max ) item_list[selected]->value = drag_max;
	drag_y = ypos;
	invalidate_drawing_area();
	//char text[30];
	valentry_inchange = true;
	//sprintf( text, "%f", item_list[selected]->value );
	//gtk_entry_set_text( GTK_ENTRY(value_entry), text );
	gtk_spin_button_set_value( GTK_SPIN_BUTTON(value_entry) , item_list[selected]->value );
	valentry_inchange = false;
	return;
	}

if ( test_mouse_pos( xpos, ypos, 1, true, selected ) != -1 ) gdk_window_set_cursor( drawing_area->window, pointer );
else  gdk_window_set_cursor( drawing_area->window, NULL );
}

//*********************************************
// SELECT
//*********************************************
int TvWidget_map_editor::get_sender_item( GtkWidget *wid )
{
int newsel = -1;
for ( unsigned int i = 0; i < item_list.size() ; i++ )
	if ( item_list[i]->button == wid ) 
		{
		newsel = i;
		break;
		}
return newsel;
}

void TvWidget_map_editor::select( GtkWidget *wid )
{
if ( in_change ) return;
int newsel = get_sender_item( wid );
if ( newsel == -1 ) return;

in_change = true;
item_list[selected]->untoggle();
selected = newsel;
item_list[selected]->toggle();
set_item_editor();
in_change = false;
invalidate_drawing_area();
}

void TvWidget_map_editor::delete_item()
{
if ( item_list.size() == 2 ) return;
in_change = true;
item_list[selected]->destroy_widget();
if ( tree_store != NULL ) item_list[selected]->remove_from_tree();
delete item_list[selected];
vector<MapItem*>::iterator it = item_list.begin() + selected;
item_list.erase(it);
if ( selected == (int)item_list.size() ) selected--;
item_list[selected]->toggle();
set_item_editor();
in_change = false;
invalidate_drawing_area();
}

void TvWidget_map_editor::append_item()
{
int size = item_list.size();
if ( size > 254 ) return; 
if ( item_feeder == NULL ) return;
MapItem *newone = (*item_feeder)(item_feeder_param);
if ( selected == size - 1 )
	{
	newone->value = ( 1.0 + item_list[selected]->value ) / 2.0;
	memcpy( newone->get_color(), item_list[selected]->get_color(), sizeof(guint8)*4 );
	item_list.push_back( newone );
	}
else 
	{
	newone->value = ( item_list[selected]->value + item_list[selected+1]->value ) / 2.0;
	for ( register int i = 0 ; i < 4 ; i++ )
		newone->color[i] = ( item_list[selected]->color[i] + item_list[selected+1]->color[i] ) / 2;
	vector<MapItem*>::iterator it = item_list.begin() + selected + 1;
	item_list.insert( it, newone );
	}
if ( tree_store != NULL ) newone->add_to_tree( tree_view, tree_store, tree_selection, node_iter );
selected++;
item_list[selected]->update_editor();
invalidate_drawing_area();
rebuild_item_list();
}

void TvWidget_map_editor::prepend_item()
{
int size = item_list.size();
if ( size > 254 ) return; 
if ( item_feeder == NULL ) return;
MapItem *newone = (*item_feeder)(item_feeder_param);
if ( selected == 0 )
	{
	newone->value = item_list[selected]->value / 2.0;
	memcpy( newone->get_color(), item_list[selected]->get_color(), sizeof(guint8)*4 );
	vector<MapItem*>::iterator it = item_list.begin();
	item_list.insert( it, newone );
	}
else 
	{
	newone->value = ( item_list[selected]->value + item_list[selected-1]->value ) / 2.0;
	for ( register int i = 0 ; i < 4 ; i++ )
	newone->color[i] = ( item_list[selected]->color[i] + item_list[selected-1]->color[i] ) / 2;
	vector<MapItem*>::iterator it = item_list.begin() + selected;
	item_list.insert( it, newone );
	}
if ( tree_store != NULL ) newone->add_to_tree( tree_view, tree_store, tree_selection, node_iter );
item_list[selected]->update_editor();
invalidate_drawing_area();
rebuild_item_list();
}


// Duplicate Item
void TvWidget_map_editor::duplicate_item()
{
int size = item_list.size();
if ( size > 254 ) return;
if ( item_feeder == NULL ) return;
MapItem *newone = item_list[selected]->duplicate_yourself();
if ( newone->value < 0.999 ) newone->value += diff;
if ( selected == size - 1 )
	{
	vector<MapItem*>::iterator it = item_list.begin() + selected;
	item_list.insert( it, newone );
	}
else
	{
	vector<MapItem*>::iterator it = item_list.begin() + selected + 1;
	item_list.insert( it, newone );
	}
if ( tree_store != NULL ) newone->add_to_tree( tree_view, tree_store, tree_selection, node_iter );
selected++;
//item_list[selected]->update_editor();
invalidate_drawing_area();
rebuild_item_list();
}

//********************************
// VALUE ENTRY CHANGED
//********************************
void TvWidget_map_editor::value_entry_changed()
{
if ( valentry_inchange == true ) return;
float value = gtk_spin_button_get_value_as_float( GTK_SPIN_BUTTON(value_entry) );;
//sscanf( gtk_entry_get_text( GTK_ENTRY(value_entry) ), "%f", &value );

if ( value < 0 || value > 1 ) return;
float maxval, minval;
if ( selected == 0 ) minval = 0;
else minval = item_list[selected-1]->value + diff;
if ( selected == (int)item_list.size() - 1 ) maxval = 1;
else maxval = item_list[selected+1]->value - diff;
if ( value > maxval ) value = maxval;
if ( value < minval ) value = minval;

item_list[selected]->value = value;
invalidate_drawing_area();
}



//********************************
// TREE
//********************************
void TvWidget_map_editor::add_to_tree( GtkTreeView *view, GtkTreeStore *store, GtkTreeSelection *selection, GtkTreeIter *parent, GtkTreeIter *sibling  )
{
tree_store = store;
tree_view = view;
tree_selection = selection;
node_iter = parent;
int size = item_list.size();
for ( register int i = 0 ; i < size ; i ++ )
	item_list[i]->add_to_tree( view, store, selection, parent );
}

void TvWidget_map_editor::remove_from_tree()
{
tree_store = NULL;
int size = item_list.size();
for ( register int i = 0 ; i < size ; i ++ )
	item_list[i]->remove_from_tree();
}


//********************************
// Output
//********************************
void TvWidget_map_editor::output_to_povray( ofstream & file )
{
int size = item_list.size();
for ( int i = 0 ; i < size ; i++ )
	{
	file << "\n\t\t\t\t[ " << item_list[i]->value << " ";
	item_list[i]->output_to_povray( file );
	file << " ]";
	}
}




//********************************
// Sauvegarde
//********************************
void TvWidget_map_editor::save( ofstream & file )
{
int size = item_list.size();
TvWidget::save( file );
for ( int i = 0 ; i < size ; i++ )
	item_list[i]->save( file );
	file << " } ";
}


bool TvWidget_map_editor::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 ( ! strcmp( "MAPITEM", tag ) )
		{
		MapItem *newone = (*item_feeder)(item_feeder_param);
		newone->load( file, tag );
		item_list.push_back( newone );	
		continue;
		}
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}
