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

#include "include/lights.h"
#include "include/box.h"
#include "include/sphere.h"
#include "include/cylinder.h"
#include "include/cone.h"
#include "include/atmosphere.h"
#include "include/obj3dcsg.h"
#include "include/plane.h"
#include "include/torus.h"
#include "include/disc.h"
#include "include/heightfield.h"
#include "include/superellipsoid.h"
#include "include/fog.h"

#include <algorithm>

//**************************************
// Définitions
//**************************************
const char *select_buttons_icons[MAX_BUTTONS] =
	{ GNOME_STOCK_PIXMAP_CLOSE, GNOME_STOCK_PIXMAP_COPY, GNOME_STOCK_PIXMAP_SAVE, GNOME_STOCK_PIXMAP_REVERT, GNOME_STOCK_PIXMAP_UP, GNOME_STOCK_PIXMAP_DOWN,  GNOME_STOCK_PIXMAP_CUT,  GNOME_STOCK_PIXMAP_COPY, GNOME_STOCK_PIXMAP_PASTE};
const char *select_buttons_tooltips[MAX_BUTTONS] =
	{ N_("Delete"), N_("Duplicate"), N_("Save"), N_("Load"), N_("Up"), N_("Down"), N_("Cut"), N_("Copy"), N_("Paste") };

const char *select_lbuttons_icons[MAX_BUTTONS] =
	{ GNOME_STOCK_PIXMAP_NEW, GNOME_STOCK_PIXMAP_CLOSE, GNOME_STOCK_PIXMAP_COPY, GNOME_STOCK_PIXMAP_PROPERTIES, GNOME_STOCK_PIXMAP_UP, GNOME_STOCK_PIXMAP_DOWN };
const char *select_lbuttons_tooltips[MAX_BUTTONS] =
	{ N_("New"), N_("Delete"), N_("Duplicate"), N_("Edit"),  N_("Up"), N_("Down") };


//	Drag & Drop
#define TV_DRAG_OBJECT_ID  194687685
GtkTargetEntry objects_source_target = {
	"TVOBJBIN",
	 GTK_TARGET_SAME_APP,
	 TV_DRAG_OBJECT_ID
};
Object3D *draged_object3D;
	

	
// Lampes
#ifndef GL_LIGHT8 
	#define GL_LIGHT8 (GLenum)-1 
#endif
#ifndef GL_LIGHT9
	#define GL_LIGHT9 (GLenum)-1 
#endif
#ifndef GL_LIGHT10
	#define GL_LIGHT10 (GLenum)-1 
#endif
#ifndef GL_LIGHT10
	#define GL_LIGHT10 (GLenum)-1 
#endif
#ifndef GL_LIGHT11
	#define GL_LIGHT11 (GLenum)-1 
#endif
#ifndef GL_LIGHT12
	#define GL_LIGHT12 (GLenum)-1 
#endif
#ifndef GL_LIGHT13
	#define GL_LIGHT13 (GLenum)-1 
#endif
#ifndef GL_LIGHT14
	#define GL_LIGHT14 (GLenum)-1 
#endif
#ifndef GL_LIGHT15
	#define GL_LIGHT15 (GLenum)-1 
#endif
#ifndef GL_LIGHT16
	#define GL_LIGHT16 (GLenum)-1 
#endif
#ifndef GL_LIGHT17
	#define GL_LIGHT17 (GLenum)-1 
#endif
#ifndef GL_LIGHT18
	#define GL_LIGHT18 (GLenum)-1 
#endif
#ifndef GL_LIGHT19
	#define GL_LIGHT19 (GLenum)-1 
#endif

const int max_lights = 20;
GLenum light_def[max_lights] = { GL_LIGHT0, GL_LIGHT1 ,GL_LIGHT2 ,GL_LIGHT3, GL_LIGHT4, GL_LIGHT5,
								GL_LIGHT6, GL_LIGHT7 ,GL_LIGHT8 ,GL_LIGHT9, GL_LIGHT10, GL_LIGHT11,
								GL_LIGHT13, GL_LIGHT14 ,GL_LIGHT15 ,GL_LIGHT16, GL_LIGHT17, GL_LIGHT18,
								GL_LIGHT19 };


GtkStyle *ObjectList::old_item_style = NULL;
GtkStyle *ObjectList::new_item_style = NULL;
GdkColor item_col = { 0xFFFF, 0xEEEE, 0xEEEE, 0xEEEE };


//**************************************
// ObjectList
//**************************************

// Constructeur
ObjectList::ObjectList( app_objs *appref )
{
// Initialisation
app_ref = appref;
app_ref->obj3dlist = this;
cut_or_copied = NULL;
is_cut = false;

// Lampes
//glGetIntegerv( GL_MAX_LIGHTS, &light_num );
light_num = 8;
light_list = new bool[ light_num ];
for ( register int i = 0 ; i < light_num ; i++ ) light_list[i] = false;
light_list[0] = true;

// Boite d'édition de l'objet courant
edit_widget = NULL;
edit_box = NULL;
current = NULL;
current_param = NULL;

// Base Layer
ObjectLayer *layer1 = new ObjectLayer( app_ref );
Layers.push_back( layer1 );

// Camera
MainCamera  =  new Camera( app_ref );
layer1->add_object( MainCamera );

// Lumiére
PointLight *lum = new PointLight( app_ref );
layer1->add_object( lum );

// Boite
Sphere *box = new Sphere( app_ref );
layer1->add_object( box );

// Atmosphere
//Background *bkgd = new Background( app_ref );
//layer1->add_object( bkgd );
}


// Supprime un objet de la liste générale
void ObjectList::remove_object( Object3D *obj )
{
vector<Object3D*>::iterator object;

for ( object = object_liste.begin() ; object != object_liste.end() ; object++ )
	{
	if ( *object != obj ) continue;	
	object_liste.erase( object );
	break;
	}
}


//**************************************
// Gestion des noms
//**************************************
bool ObjectList::test_name_exist( char *name )
{
bool exist = false;
char *res;
for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
	{
	res = object_liste[i]->get_name();
	if ( res == NULL ) continue;
	if ( strcmp( res, name ) == 0 ) { exist = true; break; }
	}
return exist;
}

char *ObjectList::create_name( char *name )
{
if ( !test_name_exist( name ) ) return NULL;
int num = 2;
int len = strlen(name);
if ( ( name[len-1] >= 0x30 ) && ( name[len-1] <= 0x39 ) )
	{
	int i;
	for ( i = len ; i > 0 ; i-- )
		if ( name[i] == '#' ) break;
	if ( i > 0 ) { sscanf( name+i-1, "#%u", &num ); num++ ; len -= len - i; }
	}

char buffer[ len + 30 ];
char num_buffer[30];
strcpy( buffer, name );
do 	{
	sprintf( num_buffer, "#%u", num++ );
	memcpy( buffer+len, num_buffer, strlen(num_buffer)+1 );
	} while ( test_name_exist(buffer) );

char *res = new char[ strlen(buffer) + 1 ];
strcpy( res, buffer );
return res;
}


//**************************************
// Output - sortie povray
//**************************************
void ObjectList::output_to_povray( ofstream & file )
{
flush();
file << "\n\n//---------------------------------------------";
file << "\n//   Objects";
file << "\n//---------------------------------------------";

for ( unsigned int i = 0 ; i < Layers.size() ; i++ )
	Layers[i]->output_to_povray( file );
}



//***************************************
// Display - Affichage
//***************************************
void ObjectList::display( glview *view )
{
for ( unsigned int i = 0 ; i < Layers.size() ; i++ ) Layers[i]->display( view );
}

void ObjectList::invalidate_lists()
{
//for ( unsigned int i = 0 ; i < lum_liste.size() ; i++ ) lum_liste[i]->invalidate_list();
for ( unsigned int i = 0 ; i < object_liste.size() ; i++ ) object_liste[i]->invalidate_list();
//MainCamera->invalidate_list();
}



//***************************************
// Refresh edit
//***************************************
void ObjectList::refresh_edit()
{
if ( edit_widget == NULL ) return;
if ( current != NULL )
	{
	if ( edit_box != NULL ) current->destroy_editor();
	edit_box = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(edit_widget), edit_box, TRUE, TRUE, 0 );
	current->edit_widget(edit_box);
	}
}



//***************************************
// Widget de sélection Layer / Objets
//***************************************
void ObjectList::set_select_widget( GtkWidget *wid )
{
PREF_DEF
select_widget = wid;
current_param = NULL;

// Boite de haut niveau
GtkWidget *vbox = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(wid), vbox, TRUE, TRUE, 4 );

// Manipulation des layers
GtkWidget *scrolled = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
gtk_box_pack_start( GTK_BOX(vbox), scrolled, TRUE, TRUE, 4 );
GtkWidget *viewp = gtk_viewport_new( NULL, NULL );
gtk_viewport_set_shadow_type( GTK_VIEWPORT(viewp), GTK_SHADOW_ETCHED_IN );
gtk_container_add( GTK_CONTAINER(scrolled), viewp );

gchar *titles[] = { N_("Layers"), NULL, NULL, NULL };
layer_clist = gtk_clist_new_with_titles( 4, titles );
gtk_clist_set_column_width( GTK_CLIST(layer_clist), 0, 160 );
gtk_clist_set_column_width( GTK_CLIST(layer_clist), 1, 18 );
gtk_clist_set_column_width( GTK_CLIST(layer_clist), 2, 18 );
gtk_clist_set_column_width( GTK_CLIST(layer_clist), 2, 18 );
gtk_container_add( GTK_CONTAINER(viewp), layer_clist );
gtk_signal_connect( GTK_OBJECT(layer_clist), "select_row", GTK_SIGNAL_FUNC(sign_layer_select), (gpointer)this );
gtk_signal_connect( GTK_OBJECT(layer_clist), "unselect_row", GTK_SIGNAL_FUNC(sign_layer_unselect), (gpointer)this );
for ( unsigned int i = 0 ; i < Layers.size() ; i ++ )
	Layers[i]->add_to_list( layer_clist, -1 );
	
GtkTargetEntry targets[1] ={ objects_source_target };
gtk_drag_dest_set( layer_clist,  (GtkDestDefaults)(GTK_DEST_DEFAULT_ALL), targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
gtk_signal_connect( GTK_OBJECT(layer_clist), "drag_data_received", GTK_SIGNAL_FUNC(sign_object_drop_on_layer), this );
gtk_signal_connect( GTK_OBJECT(layer_clist), "drag_motion", GTK_SIGNAL_FUNC(sign_object_motion_on_layer), this );

 // Bouttons de manipulation des layers
//INTERF_DEF
GtkWidget *hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 2 );
//GtkWidget *win = interf->get_gtkwin();
GtkWidget *pix;
for ( int i = 0 ; i < MAX_LBUTTONS ; i ++ )
	{
	select_lbuttons[i] = gtk_button_new();
	gtk_box_pack_start( GTK_BOX(hbox), select_lbuttons[i], TRUE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT(select_lbuttons[i]), "clicked", GTK_SIGNAL_FUNC(sign_select_lbuttons_clicked), this );	
	//pix = gnome_stock_pixmap_widget( win, select_lbuttons_icons[i] );
	pix = gtk_image_new_from_stock( select_lbuttons_icons[i],  GTK_ICON_SIZE_BUTTON );
	gtk_container_add( GTK_CONTAINER(select_lbuttons[i]), pix );
	select_ltooltips[i] = gtk_tooltips_new();
	gtk_tooltips_set_tip( select_ltooltips[i], select_lbuttons[i], select_lbuttons_tooltips[i], NULL );
	if ( !pref->tooltips->value() ) gtk_tooltips_disable( select_ltooltips[i] );
	}

// Arbre
scrolled = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
gtk_box_pack_start( GTK_BOX(vbox), scrolled, TRUE, TRUE, 4 );
viewp = gtk_viewport_new( NULL, NULL );
gtk_viewport_set_shadow_type( GTK_VIEWPORT(viewp), GTK_SHADOW_ETCHED_IN );
gtk_container_add( GTK_CONTAINER(scrolled), viewp );
gchar *titres[] = { NULL, NULL, N_("Object type"), N_("Name") };
select_tree = gtk_ctree_new_with_titles( 4, 2, titres );
gtk_ctree_set_expander_style( GTK_CTREE(select_tree), GTK_CTREE_EXPANDER_CIRCULAR );
gtk_clist_set_selection_mode( GTK_CLIST(select_tree), GTK_SELECTION_BROWSE );
gtk_container_add( GTK_CONTAINER(viewp), select_tree );

selected_layer = NULL;
gtk_clist_select_row( GTK_CLIST(layer_clist), 0, 0 );
if ( selected_layer == NULL ) selected_layer = Layers[0];
current = selected_layer->get_first_object();
edit_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(edit_widget), edit_box, TRUE, TRUE, 0 );
if ( current != NULL )
	{
	current->select();
	current->edit_widget(edit_box);
	}

gtk_signal_connect( GTK_OBJECT(select_tree), "tree_select_row", GTK_SIGNAL_FUNC(sign_object_select), this );
gtk_clist_set_column_width( GTK_CLIST(select_tree), 2, 115 );
gtk_clist_set_column_width( GTK_CLIST(select_tree), 0, 18 );
gtk_clist_set_column_width( GTK_CLIST(select_tree), 1, 18 );
gtk_drag_source_set( select_tree,  GDK_BUTTON1_MASK, targets, 1,  (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
gtk_signal_connect( GTK_OBJECT(select_tree), "drag_data_get", GTK_SIGNAL_FUNC(sign_object_drag), this );
gtk_drag_dest_set( select_tree,  (GtkDestDefaults)(GTK_DEST_DEFAULT_ALL), targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
gtk_signal_connect( GTK_OBJECT(select_tree), "drag_data_received", GTK_SIGNAL_FUNC(sign_object_drop_on_tree), this );
gtk_signal_connect( GTK_OBJECT(select_tree), "drag_motion", GTK_SIGNAL_FUNC(sign_object_motion_on_tree), this );
if ( old_item_style == NULL )
	{
	old_item_style = gtk_widget_get_style( select_tree );
	new_item_style = gtk_style_copy( old_item_style );
	GdkColormap *cmap = gdk_colormap_get_system();
	gdk_colormap_alloc_color( cmap, &item_col, FALSE, TRUE );
	for (int i = 0 ; i < 4 ; i ++ )
		{
		new_item_style->bg[i] = item_col;
		new_item_style->base[i] = item_col;		
		//new_item_style->fg[i] = item_col;		
		}
	}
drag_focus = NULL;
drag_layer_focus = -1;


// Bouttons de manipulation des objets
hbox = gtk_hbox_new( FALSE, 0 );
GtkWidget *hbox2 = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 2 );
gtk_box_pack_start( GTK_BOX(vbox), hbox2, FALSE, TRUE, 1 );
for ( int i = 0 ; i < MAX_BUTTONS ; i ++ )
	{
	select_buttons[i] = gtk_button_new();
	gtk_box_pack_start( GTK_BOX( ( i < 6 ) ? hbox : hbox2 ), select_buttons[i], TRUE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT(select_buttons[i]), "clicked", GTK_SIGNAL_FUNC(sign_select_buttons_clicked), this );	
	//pix = gnome_stock_pixmap_widget( win, select_buttons_icons[i] );
	pix = gtk_image_new_from_stock( select_buttons_icons[i], GTK_ICON_SIZE_BUTTON );
	gtk_container_add( GTK_CONTAINER(select_buttons[i]), pix );
	select_tooltips[i] = gtk_tooltips_new();
	gtk_tooltips_set_tip( select_tooltips[i], select_buttons[i], select_buttons_tooltips[i], NULL );
	if ( !pref->tooltips->value() ) gtk_tooltips_disable( select_tooltips[i] );
	}
}


// Callback de sélection de layer
void ObjectList::select_layer( gint row, gint col )
{
//cout << "\nSelect row " << row; cout.flush();
if ( selected_layer != NULL ) selected_layer->save_tree();
selected_layer = (ObjectLayer*)gtk_clist_get_row_data( GTK_CLIST(layer_clist), row );
Object3D *old = current;
unselect_object();
if ( old != NULL ) { VMAN_DEF vmanager->refresh(); }
gtk_clist_clear( GTK_CLIST(select_tree) );
selected_layer->display_content( select_tree );
current = selected_layer->get_first_object();
	
if ( col == 0 ) return;
else
	{
    ((ObjectLayer*)(gtk_clist_get_row_data( GTK_CLIST(layer_clist), row )))->toggle_property( layer_clist, row, col );
    //cout << " -> untoggling"; cout.flush();
    SCENE_DEF
    scene->set_modified();
	}
//cout << "\nselection callback : Done !"; cout.flush();
}


void ObjectList::unselect_layer( gint row, gint col  )
{
/*cout << "\nUnselect row " << row << " & col " << col;
//cout << "\nWidget = " << wid;
cout.flush();	
if ( col == 0 ) return;
ObjectLayer *layer = (ObjectLayer*)gtk_clist_get_row_data( GTK_CLIST(layer_clist), row );
if ( layer != selected_layer ) { cout << " -> layer diff"; cout.flush(); return; }
//((ObjectLayer*)(gtk_clist_get_row_data( GTK_CLIST(layer_clist), row )))->toggle_property( layer_clist, row, col );
layer->toggle_property( layer_clist, row, col );    */
}


// button layer clicked
void  ObjectList::select_lbuttons_clicked( GtkWidget *wid )
{
int button = -1;
for ( int i = 0 ; i < MAX_BUTTONS ; i ++ )
	if ( wid == select_lbuttons[i] ) { button = i; break; }

SCENE_DEF
UNDO_DEF
if ( button == -1 ) return;
switch( button )
	{
	// Create new layer
	case 0:
		{
		scene->set_modified();
		ObjectLayer *new_layer = new ObjectLayer( app_ref );
		Layers.push_back( new_layer );
		new_layer->add_to_list( layer_clist, -1 );
		gtk_clist_select_row( GTK_CLIST(layer_clist), Layers.size()-1, 0 );
		undoman->push( TV_UNDO_LAYER_CREATE, new_layer );
		}
		break;

	// Delete layer
	case 1:
		{
		scene->set_modified();
		undoman->push( TV_UNDO_LAYER_DELETE, selected_layer, get_layer_index(selected_layer) );
		remove_layer( selected_layer );
		 }
		break;
		
	// Duplicate
		case 2:
			{
			scene->set_modified();
			vector<ObjectLayer*>::iterator layer;
			unsigned int i = 0;
			for ( layer = Layers.begin() ; layer != Layers.end() ; layer++ )
				{
				i++;
				if ( *layer != selected_layer ) continue;
				ObjectLayer *target = *layer++;
				ObjectLayer *copy = new ObjectLayer( *target );
				Layers.insert( layer, copy );
				copy->add_to_list( layer_clist, i );
				undoman->push( TV_UNDO_LAYER_CREATE, copy );
				break;
				}			
			gtk_clist_select_row( GTK_CLIST(layer_clist), i, 0 );
			 }
			break;
			
	// Edit layer properties :
		case 3:
		{
		scene->set_modified();
		vector<ObjectLayer*>::iterator layer;
		for ( layer = Layers.begin() ; layer != Layers.end() ; layer++ )
			{
			if ( *layer != selected_layer ) continue;
			ObjectLayer *target = *layer;
			target->raise_edit_box();
			break;
			}
		 }		
		break;
		
		// Move Up
		case 4:
			{
			scene->set_modified();
			undoman->push( TV_UNDO_LAYER_MOVE, selected_layer, 2 );
			move_layer( selected_layer, 1 );
			}
			break;
			
 		// Move Down
		case 5:
			{
			scene->set_modified();
			undoman->push( TV_UNDO_LAYER_MOVE, selected_layer, 1 );
			move_layer( selected_layer, 2 );
			}
			break;
			
	default:
		break;
	}
}

void ObjectList::move_layer( ObjectLayer *lay, int sens )
{
vector<ObjectLayer*>::iterator layer;
unsigned int i = 0;
for ( layer = Layers.begin() ; layer != Layers.end() ; layer++ )
	{
	i++;
	if ( *layer != lay ) continue;
	if ( sens == 1 && i == 1 ) return;
	if ( sens == 2 && i == Layers.size() ) return;
				
	ObjectLayer *target = *layer;
	if ( sens == 1 ) target->move_up_in_clist();
	else target->move_down_in_clist();
	vector<ObjectLayer*>::iterator layer2 = layer;
	if ( sens == 1 ) layer2--; else layer2++;
	Layers.erase( layer );
	Layers.insert( layer2, target );
	break;
	}
}			

void ObjectList::remove_layer( ObjectLayer *lay )
{
// Find layer in list
vector<ObjectLayer*>::iterator layer;
unsigned int i = 0;
for ( layer = Layers.begin() ; layer != Layers.end() ; layer++ )
	{
	i++;
	if ( *layer != lay ) continue;
	ObjectLayer *target = *layer;
	if ( ! target->can_be_destroyed() )
		{
		app_warning( _("Cannot destroy the layer owning the camera !") );
		return;
		}
	Layers.erase( layer );
	gtk_clist_remove( GTK_CLIST(layer_clist), --i );
	//delete target;
	break;
	}
// Managing tree		
current = NULL;
selected_layer = NULL;
if ( i < 0 ) i = 0;
if ( i > Layers.size() - 1 ) i = Layers.size() - 1;
gtk_clist_select_row( GTK_CLIST(layer_clist), i, 0 );
}


void ObjectList::insert_layer( ObjectLayer *lay, int position )
{
// Find layer
vector<ObjectLayer*>::iterator layer;
int i = 0;
for ( layer = Layers.begin() ; i < position ; layer++ )
	i++;
if ( i == (int)Layers.size() ) i = -1;
	
if ( i != -1 ) Layers.insert( ++layer, lay );
else Layers.push_back( lay );
lay->add_to_list( layer_clist, i );
gtk_clist_select_row( GTK_CLIST(layer_clist), position, 0 );
}


gint ObjectList::get_layer_index( ObjectLayer *layer )			
{
vector<ObjectLayer*>::iterator target;
unsigned int i = 0;
for ( target = Layers.begin() ; target != Layers.end() ; target++ )
	{
	i++;
	if ( *target == layer ) return i;
	}
return -1;
}


bool ObjectList::layer_name_exist( char *name, ObjectLayer *layer )
{
for ( unsigned int i = 0 ; i < Layers.size() ; i++ )
	{
	if ( layer == Layers[i] ) continue;
	if ( ! Layers[i]->test_name( name ) ) return true;
	}
return false;
}



//***************************************
// Select object
//***************************************
void  ObjectList::select_object( GtkCTree *tree, GtkCTreeNode *node, gint col )
{
VMAN_DEF
SCENE_DEF
Object3D *sel = (Object3D*)gtk_ctree_node_get_row_data( tree, node );
if ( sel == NULL ) return;

if ( current_param != NULL )
	{ 
	current_param = NULL;
	vmanager->set_pointer_mode( TV_PMODE_TRACKBALL );
	}
	
if ( current != NULL )
	{
	current->unselect();
	if ( edit_box != NULL ) current->destroy_editor();
	}
edit_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(edit_widget), edit_box, TRUE, TRUE, 0 );
current = sel;
current->select();
current->edit_widget(edit_box);

if ( col == 0 ) { sel->toggle_visibility(); scene->set_modified(); }
else if ( col == 1 )  { sel->toggle_render();  scene->set_modified();  }
else  { vmanager->refresh();     }
}


void ObjectList::unselect_object()
{
if ( current != NULL )
	{
	current->unselect();
	if ( edit_box != NULL ) current->destroy_editor();
    }
current = NULL;
current_param = NULL;
}


void ObjectList::set_current_param( ObjParam* param )
{ 
current_param = param;
VMAN_DEF 
vmanager->refresh(); 
}		


//***************************************
// Select button clicked
//***************************************

void ObjectList::load_object()
{
filebox = gtk_file_selection_new( _("Load object") );
GtkFileSelection *box = GTK_FILE_SELECTION(filebox);
//gtk_window_set_modal( & box->window, TRUE );
if ( default_path != NULL ) gtk_file_selection_set_filename( box, default_path );
gtk_signal_connect( GTK_OBJECT(box->ok_button), "clicked", GTK_SIGNAL_FUNC(sign_obj_load_with_fbox), this );
gtk_signal_connect( GTK_OBJECT(box->cancel_button), "clicked", GTK_SIGNAL_FUNC(sign_obj_del_filebox), this );
gtk_widget_show( filebox );
}

		
void ObjectList::save_object()		
{
filebox = gtk_file_selection_new( _("Save object") );
GtkFileSelection *box = GTK_FILE_SELECTION(filebox);
//gtk_window_set_modal( & box->window, TRUE );
if ( default_path != NULL ) gtk_file_selection_set_filename( box, default_path );
gtk_signal_connect( GTK_OBJECT(box->ok_button), "clicked", GTK_SIGNAL_FUNC(sign_obj_save_with_fbox), this );
gtk_signal_connect( GTK_OBJECT(box->cancel_button), "clicked", GTK_SIGNAL_FUNC(sign_obj_del_filebox), this );
gtk_widget_show( filebox );
}


void  ObjectList::select_buttons_clicked( GtkWidget *wid )
{
int button = -1;
for ( int i = 0 ; i < MAX_BUTTONS ; i ++ )
	if ( wid == select_buttons[i] ) { button = i; break; }

SCENE_DEF
UNDO_DEF
if ( button == -1 ) return;
switch( button ) 
	{
	// Delete
	case 0:
		if ( current == NULL ) break;
		scene->set_modified();
		undoman->push( TV_UNDO_OBJ_DELETE, current, selected_layer );		
		selected_layer->delete_object( current );
		break;

	// Duplicate
	case 1:
		scene->set_modified();
		selected_layer->duplicate_object( current );
		break;
		 		
	// Save
	case 2:
		save_object();
		break;

	// Load object		
	case 3:
		load_object();
		break;


	// Move object up		
	case 4:
		scene->set_modified();
		undoman->push( TV_UNDO_OBJ_MOVE, current, selected_layer );				
		selected_layer->move_object_up( current );
		break;	
	
	// Move object down		
	case 5:
		scene->set_modified();
		undoman->push( TV_UNDO_OBJ_MOVE, current, selected_layer );				
		selected_layer->move_object_down( current );
		break;
	
	// Cut
	case 6:
		scene->set_modified();
		if ( current->get_type() == TV_OBJ3D_CAMERA ||   current->get_category() == TV_OBJ3D_ATMOS1 )
			{
			app_warning( _("Cannot cut this object.") );
			break;
			}
    	if ( is_cut && cut_or_copied != NULL ) delete cut_or_copied;
    	cut_or_copied = current;
    	is_cut = true;
    	selected_layer->remove_object( current );
		break;
		
	// Copy
    case 7:
    	scene->set_modified();
		if ( current->get_type() == TV_OBJ3D_CAMERA ||   current->get_category() == TV_OBJ3D_ATMOS1 )
			{
			app_warning( _("Cannot copy this object.") );
			break;
			}
    	if ( is_cut && cut_or_copied != NULL ) delete cut_or_copied;
    	if ( current != NULL ) cut_or_copied = current->duplicate_yourself();
    	is_cut = false;
    	break;
    	
    // Paste
    case 8:
    	if ( cut_or_copied == NULL ) break;
    	selected_layer->paste_object( cut_or_copied, current );
    	is_cut = false;
    	cut_or_copied = cut_or_copied->duplicate_yourself();
    	break;
	
	default:
		break;
	}
VMAN_DEF 
vmanager->refresh(); 
}


//************************************************************
// Drag & Drop
//************************************************************
void ObjectList::object_drag( GdkDragContext *dc, GtkSelectionData *selection_data )
{
draged_object3D = current;
guint data_size = sizeof( Object3D* );
gtk_selection_data_set( selection_data,  GDK_SELECTION_TYPE_STRING, 8,  (guchar*)draged_object3D, data_size );
}

void ObjectList::object_drop_on_layer( GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info )
{
if ( info != TV_DRAG_OBJECT_ID ) return;
if ( selection_data == NULL ) return;
if ( selection_data->length != sizeof(Object3D*) ) return;
SCENE_DEF
UNDO_DEF
scene->set_modified();
gint row, col;
gint res =  gtk_clist_get_selection_info( GTK_CLIST(layer_clist), x, y, &row, &col );
row --;
if (  res == 1  ) { 	if ( row < 0 ) row = 0; }
else 	row = GTK_CLIST(layer_clist)->rows-1;
if ( selected_layer == Layers[row] ) return;
bool move =  ( dc->action == GDK_ACTION_COPY )  ? false : true;
gtk_clist_set_row_style( GTK_CLIST(layer_clist),  row, old_item_style );
drag_layer_focus = -1;
drag_focus = NULL;

if ( move )
	{
	undoman->push( TV_UNDO_OBJ_MOVE, draged_object3D, selected_layer );				
	selected_layer->remove_object( draged_object3D );
	if ( selected_layer == Layers[row] ) selected_layer->append_object( draged_object3D );
	else Layers[row]->add_object( draged_object3D );
	}
// copy
else
	{
	Object3D *newobj = draged_object3D->duplicate_yourself();
	if ( selected_layer == Layers[row] ) { selected_layer->append_object( newobj ); }
	else { Layers[row]->add_object( newobj );   }
	undoman->push( TV_UNDO_OBJ_CREATE, newobj, Layers[row] );				
	}
}


void ObjectList::object_motion_on_layer( GdkDragContext *dc, gint x, gint y )
{
gint row, col;
gint res =  gtk_clist_get_selection_info( GTK_CLIST(layer_clist), x, y, &row, &col );
row--;
if (  res == 1  ) { 	if ( row < 0 ) row = 0; }
else row = GTK_CLIST(layer_clist)->rows - 1;

if ( row == drag_layer_focus ) return;
if ( drag_layer_focus != -1 ) gtk_clist_set_row_style( GTK_CLIST(layer_clist), drag_layer_focus, old_item_style );
if ( selected_layer == Layers[row] ) { drag_layer_focus = -1; return; }
drag_layer_focus = row;
gtk_clist_set_row_style( GTK_CLIST(layer_clist), drag_layer_focus , new_item_style);
}



void ObjectList::object_drop_on_tree( GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info )
{
if ( info != TV_DRAG_OBJECT_ID ) return;
if ( selection_data == NULL ) return;
if ( selection_data->length != sizeof(Object3D*) ) return;
SCENE_DEF
UNDO_DEF
scene->set_modified();
gint row, col;
gtk_clist_get_selection_info( GTK_CLIST(select_tree), x, y, &row, &col );
row--;
if ( row > GTK_CLIST(select_tree)->rows ) row =  GTK_CLIST(select_tree)->rows- 1;
GtkCTreeNode *node = gtk_ctree_node_nth( GTK_CTREE(select_tree), row );
Object3D *destination = (Object3D*)gtk_ctree_node_get_row_data(  GTK_CTREE(select_tree), node );
destination->set_list_item_style( old_item_style );
if ( drag_layer_focus != -1 ) gtk_clist_set_row_style( GTK_CLIST(layer_clist),  drag_layer_focus, old_item_style );
drag_layer_focus = -1;

if ( destination == NULL ) return;
if ( destination->is_group() == false ) { /*cout << "\nDestination is not a group !"; cout.flush();*/ return; }
if ( destination == draged_object3D ) return;
if ( destination->has_as_ancester( draged_object3D ) )  { /*cout << "\nDestination is an ancestor !"; cout.flush(); */ return; }

bool move =  ( dc->action == GDK_ACTION_COPY )  ? false : true;
if ( move )
	{
	undoman->push( TV_UNDO_OBJ_MOVE, draged_object3D, selected_layer );				
	selected_layer->remove_object( draged_object3D );
	destination->paste_object( draged_object3D );
	}
else
	{
	Object3D *newobj = draged_object3D->duplicate_yourself();
	destination->paste_object( newobj );
	undoman->push( TV_UNDO_OBJ_CREATE, newobj, selected_layer );				
	}
}


void ObjectList::object_motion_on_tree( GdkDragContext *dc, gint x, gint y )
{
gint row, col;
gtk_clist_get_selection_info( GTK_CLIST(select_tree), x, y, &row, &col );
row--;
if ( row > GTK_CLIST(select_tree)->rows ) row =  GTK_CLIST(select_tree)->rows- 1;
GtkCTreeNode *node = gtk_ctree_node_nth( GTK_CTREE(select_tree), row );
Object3D *destination = (Object3D*)gtk_ctree_node_get_row_data(  GTK_CTREE(select_tree), node );
if ( drag_focus == destination ) return;
if ( drag_focus != NULL ) drag_focus->set_list_item_style( old_item_style );
if ( destination == NULL ) return;
drag_focus = destination;
drag_focus->set_list_item_style( new_item_style );
}


//***************************************
// Pref Changed !
//***************************************
void  ObjectList::pref_changed()
{
PREF_DEF
for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
object_liste[i]->pref_changed();

bool tt = pref->tooltips->value();
for ( int i = 0 ; i < MAX_BUTTONS ; i ++ )
	if ( tt ) gtk_tooltips_enable( select_tooltips[i] );
	else gtk_tooltips_disable( select_tooltips[i] );
}


//***************************************
// set create widget
//***************************************
void ObjectList::set_create_widget( GtkWidget *wid )
{
//PREF_DEF
INTERF_DEF
//gchar *pixmap = gnome_pixmap_file( "Truevision/object_default.xpm" );  
char *pixmap = tv_get_pixmap( "object_default.xpm" );

GdkColor transparent;
create_def_pix = gdk_pixmap_create_from_xpm( interf->get_gdkwin(), &create_def_mask, &transparent, pixmap );
delete pixmap;

// Arbre
GtkWidget *scrolled = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
gtk_box_pack_start( GTK_BOX(wid), scrolled, TRUE, TRUE, 4 );
GtkWidget *viewp = gtk_viewport_new( NULL, NULL );
gtk_viewport_set_shadow_type( GTK_VIEWPORT(viewp), GTK_SHADOW_ETCHED_IN );
gtk_container_add( GTK_CONTAINER(scrolled), viewp );

gchar *titres[] = { N_("Available Objects") }; 
create_tree = gtk_ctree_new_with_titles( 1, 0, titres );
gtk_ctree_set_expander_style( GTK_CTREE(create_tree), GTK_CTREE_EXPANDER_CIRCULAR );
gtk_container_add( GTK_CONTAINER(viewp), create_tree );
	add_subtree( _("Atmospherics"), NULL, Atmos_tree, Atmos_tree_num );
	add_subtree( _("Lights"), "object_light.xpm", Lights_tree, Lights_tree_num );
	add_subtree( _("Infinite solid primitives"), "object_plane.xpm", ISP_tree, ISP_tree_num );
	add_subtree( _("Finite solid primitives"), "object_box.xpm", FSP_tree, FSP_tree_num );	
	add_subtree( _("Finite patch primitives"), NULL, FPP_tree, FPP_tree_num );	
	add_subtree( _("CSG operators"), "object_csg.xpm", CSG_tree, CSG_tree_num );
gtk_signal_connect( GTK_OBJECT(create_tree), "tree_select_row", GTK_SIGNAL_FUNC(sign_create_tree_select), this );
gtk_signal_connect( GTK_OBJECT(create_tree), "select_row", GTK_SIGNAL_FUNC(sign_create_tree_click), this );

//descr_text = gtk_text_new( NULL,NULL );
//gtk_box_pack_start( GTK_BOX(wid), descr_text, FALSE, TRUE, 4 );

GtkWidget *button = gtk_button_new_with_label( _("Create") );
gtk_box_pack_start( GTK_BOX(wid), button, FALSE, FALSE, 4 );
gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_create_button_clicked), this );	
}

//****************************************
// Add subtree
//****************************************
void ObjectList::add_subtree( const char *name, const char *icon, Object3DDef *defs, const int def_num )
{
gchar *text[1] = { (gchar*)_(name) };
INTERF_DEF
GdkColor transparent;

GdkPixmap *pix, *mask;
if ( icon == NULL ) { pix = create_def_pix; mask = create_def_mask; }
else
	{
	//gchar *pixmap = gnome_pixmap_file( icon ); 
	char *pixmap = tv_get_pixmap(  (char*)icon );
	pix = gdk_pixmap_create_from_xpm( interf->get_gdkwin(), &mask, &transparent, pixmap );
	delete pixmap;
	}
GtkCTreeNode *root = gtk_ctree_insert_node( GTK_CTREE(create_tree), NULL, NULL, text, 10, pix, mask, pix, mask, FALSE, FALSE );
gtk_ctree_node_set_row_data( GTK_CTREE(create_tree), root, (gpointer)NULL );

GtkCTreeNode *node;
for ( int i = 0 ; i < def_num ; i++ )
	{
	text[0] = (gchar*)defs[i].name;
	if ( defs[i].icon == NULL ) { pix = create_def_pix; mask = create_def_mask; }
	else
		{
		//gchar *pixmap = gnome_pixmap_file( defs[i].icon ); 
		char *pixmap = tv_get_pixmap( (char*)defs[i].icon );		
		pix = gdk_pixmap_create_from_xpm( interf->get_gdkwin(), &mask, &transparent, pixmap );
		delete pixmap;
		}
	node = gtk_ctree_insert_node( GTK_CTREE(create_tree), root, NULL, text, 10, pix, mask, pix, mask, TRUE, FALSE );
	gtk_ctree_node_set_row_data( GTK_CTREE(create_tree), node, (gpointer)&defs[i] );
	}
}

//****************************************
// Create tree select
//****************************************
void ObjectList::create_tree_select( GtkCTree *tree, GtkCTreeNode *node, gint col )
{
current_objdef = (Object3DDef*)gtk_ctree_node_get_row_data( tree, node );
//gtk_text_backward_delete( GTK_TEXT(descr_text), gtk_text_get_length(GTK_TEXT(descr_text)) );

//if ( current_objdef != NULL )
//	gtk_text_insert( GTK_TEXT(descr_text), NULL, NULL, NULL, current_objdef->description, -1 );
}

void ObjectList::create_tree_click( GdkEventButton *ev )
{
if ( ev->type ==  GDK_2BUTTON_PRESS ) create_button_clicked();
}

//****************************************
// Create button clicked
//****************************************
void ObjectList::create_button_clicked()
{
if ( current_objdef == NULL ) return;

Object3D *newbie;
switch( current_objdef->type )
	{
	case TV_OBJ3D_POINTLIGHT:
		newbie = new PointLight( app_ref );
		break;

	case TV_OBJ3D_SPOTLIGHT:
		newbie = new SpotLight( app_ref );
		break;

	case TV_OBJ3D_CYLLIGHT:
		newbie = new CylindricalLight( app_ref );
		break;
		
	case TV_OBJ3D_AREALIGHT:
		newbie = new AreaLight( app_ref );
		break;

	case TV_OBJ3D_BOX:
		newbie = new Box( app_ref );
		break;

	case TV_OBJ3D_SPHERE:
		newbie = new Sphere( app_ref );
		break;

	case TV_OBJ3D_CYLINDER:
		newbie = new Cylinder( app_ref );
		break;

	case TV_OBJ3D_CONE:
		newbie = new Cone( app_ref );
		break;		
	
	case TV_OBJ3D_PLANE:
		newbie = new Plane( app_ref );
		break;		
	
	case TV_OBJ3D_TORUS:
		newbie = new Torus( app_ref );
		break;		

	case TV_OBJ3D_BKGD:
		{
		bool exist = false;
		for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
			if ( object_liste[i]->get_type() == TV_OBJ3D_BKGD ) { exist = true; break; }
		if ( exist == true ) newbie = NULL;
		else newbie = new Background( app_ref );
		}
		break;
	
	case TV_OBJ3D_SKY:
		{
		bool exist = false;
		for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
			if ( object_liste[i]->get_type() == TV_OBJ3D_SKY ) { exist = true; break; }
		if ( exist == true ) newbie = NULL;
		else newbie = new SkySphere( app_ref );
		}
		break;
	
	case TV_OBJ3D_AMEDIA:
		newbie = new AtmosMedia( app_ref );
		break;
	
	case TV_OBJ3D_DISC:
		newbie = new Disc( app_ref );
		break;
	
	case TV_OBJ3D_HEIGHTFIELD:
		newbie = new Heightfield( app_ref );
		break;
	
	case TV_OBJ3D_SUPERELLIPSOID:
		newbie = new Superellipsoid( app_ref );
		break;
		
	case TV_OBJ3D_FOG:
		newbie = new Fog( app_ref );
		break;
		
	case TV_OBJ3D_CSGUNION:
		newbie = new CsgUnion( app_ref );
		break;

	case TV_OBJ3D_CSGMERGE:
		newbie = new CsgMerge( app_ref );
		break;
		
	case TV_OBJ3D_CSGINTERSECTION:
		newbie = new CsgIntersection( app_ref );
		break;
		
	case TV_OBJ3D_CSGDIFFERENCE:
		newbie = new CsgDifference( app_ref );
		break;
				
	default:
		newbie = NULL;
	}

if ( newbie == NULL ) return;
selected_layer->append_object( newbie );
newbie->ctree_node_select();
SCENE_DEF
scene->set_modified();
UNDO_DEF
undoman->push( TV_UNDO_OBJ_CREATE, newbie, selected_layer );
}


Object3D *ObjectList::create_object( char *tag )
{
if ( ! strcmp( tag,  "BOX" ) )  return new  Box( app_ref );
if ( ! strcmp( tag,  "CAMERA" ) )  return new  Camera( app_ref );
if ( ! strcmp( tag,  "SPHERE" ) )  return new  Sphere( app_ref );
if ( ! strcmp( tag,  "POINTLIGHT" ) )  return new  PointLight( app_ref );
if ( ! strcmp( tag,  "CYLINDERLIGHT" ) )  return new  CylindricalLight( app_ref );
if ( ! strcmp( tag,  "SPOTLIGHT" ) )  return new  SpotLight( app_ref );
if ( ! strcmp( tag,  "CONE" ) )  return new  Cone( app_ref );
if ( ! strcmp( tag,  "CYLINDER" ) )  return new  Cylinder( app_ref );
if ( ! strcmp( tag,  "CSGUNION" ) )  return new  CsgUnion( app_ref );
if ( ! strcmp( tag,  "CSGMERGE" ) )  return new  CsgMerge( app_ref );
if ( ! strcmp( tag,  "CSGDIFF" ) )  return new  CsgDifference( app_ref );
if ( ! strcmp( tag,  "CSGINTER" ) )  return new  CsgIntersection( app_ref );
if ( ! strcmp( tag,  "PLANE" ) )  return new Plane( app_ref );
if ( ! strcmp( tag,  "AREALIGHT" ) )  return new AreaLight( app_ref );
if ( ! strcmp( tag,  "TORUS" ) )  return new Torus( app_ref );
if ( ! strcmp( tag,  "AMEDIA" ) )  return new AtmosMedia( app_ref );
if ( ! strcmp( tag,  "DISC" ) )  return new Disc( app_ref );
if ( ! strcmp( tag,  "HEIGHTFIELD" ) )  return new Heightfield( app_ref );
if ( ! strcmp( tag,  "SUPERELLIPSOID" ) )  return new Superellipsoid( app_ref );
if ( ! strcmp( tag,  "FOG" ) )  return new Fog( app_ref );

if ( ! strcmp( tag,  "SKY" ) )	
	{
	bool exist = false;
	for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
		if ( object_liste[i]->get_type() == TV_OBJ3D_SKY ) { exist = true; break; }
	if ( exist == true ) return NULL;
	else return new  SkySphere( app_ref );
	}
	
if ( ! strcmp( tag,  "BACKGROUND" ) )	
	{
	bool exist = false;
	for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
		if ( object_liste[i]->get_type() == TV_OBJ3D_BKGD ) { exist = true; break; }
	if ( exist == true ) return NULL;
	else return new  Background( app_ref );
	}

return NULL;
}


//****************************************
// Save
//****************************************
void ObjectList::save( ofstream & file )
{
if ( current != NULL ) current->flush();
if ( selected_layer != NULL ) selected_layer->save_tree();
file << "\nOBJECTS{";
for ( unsigned int i = 0 ; i < Layers.size() ; i++ )
	{
	file << "\n";
	Layers[i]->save( file );
	}
file << "\n}\n";
}


bool ObjectList::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "OBJECTS" ) ) return false;

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
    ObjectLayer *layer = new ObjectLayer( app_ref );
    if ( layer->load( file, tag ) )
    	{
    	Layers.push_back( layer );
    	layer->add_to_list( layer_clist, -1 );
    	continue;
    	}
    else { delete layer; }
		
	tvio_skip_section(file );
	} while ( tag != NULL );
	
gtk_clist_select_row( GTK_CLIST(layer_clist), 0, 0 );
//VMAN_DEF
//vmanager->force_refresh();
return true;
}


void ObjectList::clear()
{
for ( unsigned int i = 0 ; i < Layers.size() ; i++ )
	delete Layers[i];
Layers.clear();
gtk_clist_clear( GTK_CLIST(layer_clist) );
gtk_clist_clear( GTK_CLIST(select_tree) );
MainCamera = NULL;
object_liste.clear();
selected_layer = NULL;
}


void ObjectList::new_scene()
{
for ( register int i = 1 ; i < light_num ; i++ ) light_list[i] = false;

// Boite d'édition de l'objet courant
current = NULL;
current_param = NULL;

// Base Layer
ObjectLayer *layer1 = new ObjectLayer( app_ref );
Layers.push_back( layer1 );
layer1->add_to_list( layer_clist, -1 );

// Camera
MainCamera  =  new Camera( app_ref );
layer1->add_object( MainCamera );

// Lumiére
PointLight *lum = new PointLight( app_ref );
layer1->add_object( lum );

gtk_clist_select_row( GTK_CLIST(layer_clist), 0, 0 );
VMAN_DEF
vmanager->refresh();
}



//****************************************
// Lamp
//****************************************
int ObjectList::light_request( )
{
for ( register int i = 0 ; i < light_num ; i++ )
	if ( ! light_list[i] )
		{
		light_list[i] = true;
		return i;
		}
return -1;
}

void ObjectList::light_set_properties( int lamp_id, float *location, float *color )
{
//cout << "\nSetting light properties for lamp ->" << lamp_id;
GLfloat ambient[4]= { 0.00025, 0.00025, 0.00025, 0 };
GLfloat specular[4] = { 0.2, 0.2, 0.2, 0 };
if ( lamp_id == -1 ) { /*cout << "\nLight id NULL"; */ cout.flush(); return; }
glLightfv( light_def[lamp_id], GL_POSITION, location );
glLightfv( light_def[lamp_id], GL_DIFFUSE, color );
glLightfv( light_def[lamp_id], GL_AMBIENT, ambient );
glLightfv( light_def[lamp_id], GL_SPECULAR, specular );
//cout << "\nlocation-> " << location[0] << ", " << location[1] << ", " << location[2];
//cout << "\ncolor-> " << color[0] << ", " << color [1] << ", " << color [2] << ", " << color [3];
//GLboolean bools;
//glGetBooleanv( GL_LIGHT_MODEL_TWO_SIDE, &bools );
//cout << "\nmodel two side = " << (float)bools;
//glGetBooleanv( GL_LIGHT_MODEL_LOCAL_VIEWER, &bools );
//cout << "\nmodel local viewer = " << (float)bools;
//GLfloat colora[4];
//glGetFloatv( GL_LIGHT_MODEL_AMBIENT, colora );
//cout << "\nambient-> " << colora[0] << ", " << colora [1] << ", " << colora [2] << ", " << colora [3];

//glGetLightfv( GL_LIGHT1, GL_POSITION, colora );
//cout << "\nposition-> " << colora[0] << ", " << colora [1] << ", " << colora [2];

//cout << "\nLight 0 enabled = " <<  ( (glIsEnabled( GL_LIGHT0 )==GL_TRUE) ? "true" : "false" );
//cout << "\nLight 1 enabled = " <<  ( (glIsEnabled( GL_LIGHT1 )==GL_TRUE) ? "true" : "false" );
//cout << "\nLight 2 enabled = " <<  ( (glIsEnabled( GL_LIGHT2 )==GL_TRUE) ? "true" : "false" );
//cout.flush();
}

void ObjectList::light_set_properties( int lamp_id, float *location, float *pointat, float cutoff, float *color )
{
if ( lamp_id == -1 ) return;
float direction[] = { pointat[0] - location[0],  pointat[1] - location[1],  pointat[2] - location[2] };

glLightfv( light_def[lamp_id], GL_POSITION, location );
glLightf( light_def[lamp_id], GL_SPOT_CUTOFF, cutoff );
glLightfv( light_def[lamp_id], GL_SPOT_DIRECTION, direction );
glLightfv( light_def[lamp_id], GL_DIFFUSE, color );
glLightfv( light_def[lamp_id], GL_SPECULAR, color );
//cout << "\nlocation-> " << location[0] << ", " << location[1] << ", " << location[2];
//cout << "\ndirection-> " << direction[0] << ", " << direction[1] << ", " << direction[2];
}

void ObjectList::light_enable( int lamp_id ) { if ( lamp_id != -1 ) glEnable( light_def[lamp_id] );}
void ObjectList::light_disable( int lamp_id ) { if ( lamp_id != -1 ) glDisable( light_def[lamp_id] );}


void ObjectList::light_disable_all()
{
for ( int i = 0 ; i <  light_num ; i++ )
	glDisable( light_def[ i ] );
}



//*************************************************
// Sauvegarde / Chargement d'objets unitaires
//*************************************************
void ObjectList::object_save()
{
if ( current == NULL ) return;

// Creation du nom de fichier
char *filename;
bool added_ext = false;
gchar *fname = (gchar*)gtk_file_selection_get_filename( GTK_FILE_SELECTION(filebox) );
store_default_path( fname );
int offset = strlen( fname ) - 4;
if ( offset < 1 || ( strcmp( fname + offset, ".tvo" ) && strcmp( fname + offset, ".TVO" ) ) )
	{
	filename = new char[ offset + 9 ];
	strcpy( filename, fname );
	strcat( filename, ".tvo" );
	added_ext = true;
	}
else filename = fname;

// Test existance
ifstream ifile( filename, ios::binary );
if ( ifile != NULL )
	{
	GtkWidget *question = gnome_question_dialog_modal( _("File already exists, overwrite ?"), NULL, NULL );
	if ( gnome_dialog_run_and_close( GNOME_DIALOG(question) ) != 0 ) { delete_filebox(); return; }
	}
ifile.close();

// Ouverture du fichier
ofstream file( filename, ios::out );
file.setf( ios::fixed, ios::floatfield );
if ( file == NULL ) { app_warning( _("Cannot open : "), filename );  delete_filebox(); return; }

// Ecriture
file << "TRUEVISION OBJECT";
file << "\nVERSION " << VERSION;

vector<Material*> matlist;
current->get_materials( matlist );

file << "\nMATERIALS{";
for ( unsigned int i = 0 ; i < matlist.size() ; i++ )
	matlist[i]->save( file );
file << "\n}\n";
current->save( file );

file.close();
delete_filebox();
if ( added_ext ) delete filename;
}


void ObjectList::object_load()
{
gchar *fname = (gchar*)gtk_file_selection_get_filename( GTK_FILE_SELECTION(filebox) );
store_default_path( fname );
ifstream file( fname, ios::binary );
if ( file == NULL )
	{
	app_warning( _("Cannot open : "), fname );
	delete_filebox();
	return;
	}
if ( ! tvio_check_file_sig( file, "TRUEVISION OBJECT" ) )
	{
	app_warning( _("File type unknown : "), fname );
	delete_filebox();
	return;
	}
tvio_get_soft_version( file );

TEXLIST_DEF
vector<Material*> mlist;
char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;

	if ( ! strcmp( tag, "MATERIALS" ) )
		{
		char * ltag = NULL;
		do {
			ltag = tvio_get_next_tag( file );
			if ( ltag == NULL ) break;
		    Material *tex = (Material*)(new PovMaterial( app_ref ));
		    if ( tex->load( file, ltag ) )
		    	{
		    	mlist.push_back( tex );
		    	continue;
		    	}
		    else { delete tex; }			
		    tvio_skip_section(file );	
		    } while ( ltag != NULL );
		 texlist->set_load_list( &mlist );
		 continue;
		 }
		
	Object3D *obj = create_object( tag );
	if ( obj != NULL )
		{
		if ( obj->load( file, tag ) )
			{
			selected_layer->append_object( obj );			
			texlist->flush_load_list();
			break;
			}
		else delete obj;
		}
		
	tvio_skip_section(file );
	} while ( tag != NULL );	
	
file.close();
delete_filebox();  		
SCENE_DEF
scene->set_modified();
VMAN_DEF
vmanager->refresh();
}


void ObjectList::store_default_path( char *path )
{
if ( default_path != NULL ) delete default_path;
int len = strlen( path );
while ( len > 0 && path[len] != '/' ) len--;
if ( len == 0 ) return;
default_path = new char[ len +2 ];
strncpy( default_path, path, len+1 );
default_path[len+1] = '\0';
}



//****************************************
// Picking
//****************************************
void ObjectList::init_pick_names()
{
current_pick_name = 1;
glInitNames();
glPushName(0);
}

void ObjectList::select_picked( int name )
{
Object3D *res = NULL;
for ( unsigned int i = 0 ; i < object_liste.size() ; i++ )
	{
	if ( object_liste[i]->pick_test( name ) )
		{
		res = object_liste[i];
		break;
		}
	}
if ( res == NULL ) { return; }

if ( res == current ) return;
VMAN_DEF
if ( current != NULL )
	{
	current->unselect();
	if ( edit_box != NULL ) current->destroy_editor();
	}
edit_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(edit_widget), edit_box, TRUE, TRUE, 0 );
current = res;
current->select();
current->edit_widget(edit_box);

vmanager->refresh();
}
