//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// objectlist.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/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 "include/blob.h"
#include "include/text.h"
#include "include/lathe.h"
#include "include/linkobj.h"
#include "include/prism.h"
#include "include/isosurface.h"
#include "include/julia.h"
#include "include/parametric.h"
#include "include/povscript.h"
#include "include/spheresweep.h"
#include "include/bicubic.h"
#include "include/scriptobj.h"

#include <algorithm>

//**************************************
// Constants
//**************************************
	// Objects manipulation buttons definitions
	const char *select_buttons_icons[OBJ_BUTTONS] = { 
		GTK_STOCK_DELETE, GTK_STOCK_COPY, GTK_STOCK_SAVE, 
		GTK_STOCK_OPEN, GTK_STOCK_GO_BACK, 
		GTK_STOCK_GO_UP, GTK_STOCK_GO_DOWN,  
		GTK_STOCK_CUT,  GTK_STOCK_COPY, 
		GTK_STOCK_PASTE
		};
	const char *select_buttons_tooltips[OBJ_BUTTONS] = { 
		N_("Delete"), N_("Duplicate"), N_("Save"), N_("Load"), N_("Unparent"), 
		N_("Up"), N_("Down"), N_("Cut"), N_("Copy"), N_("Paste") 
		};

	// Layers manipulation buttons definitions
	const char *select_lbuttons_icons[LAY_BUTTONS] = { 
		GTK_STOCK_NEW, GTK_STOCK_CLOSE, 
		GTK_STOCK_COPY, GTK_STOCK_PROPERTIES, 
		GTK_STOCK_GO_UP, GTK_STOCK_GO_DOWN 
		};
	const char *select_lbuttons_tooltips[LAY_BUTTONS] =	{ 
		N_("New"), N_("Delete"), N_("Duplicate"), N_("Edit"),  
		N_("Up"), N_("Down") 
		};

	// Default reference list
	const char *obj_ref_list_def[] = { N_("None") };

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

	
	// OpenGL Lights
	#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 };


	// Unlinkable objects list
	const int UnlinkableObjects_num = 8;
	const Object3DType UnlinkableObject[ UnlinkableObjects_num ] = { 
		TV_OBJ3D_CAMERA, TV_OBJ3D_LINK, TV_OBJ3D_BKGD,
		TV_OBJ3D_SKY, TV_OBJ3D_AMEDIA, TV_OBJ3D_BLOBSPHERE, 
		TV_OBJ3D_BLOBCYLINDER, TV_OBJ3D_POVSCRIPT 
		};
		
	// Linkable test macros
	bool tvobj3d_is_linkable( Object3DType type )
	{
	for ( int i = 0 ; i < UnlinkableObjects_num ; i++ )
		if ( type == UnlinkableObject[i] ) 
			return FALSE;
	return TRUE;
	}
	bool tvobj3d_is_linkable( Object3D *obj ) { return  tvobj3d_is_linkable( obj->get_type() ); }
	
	
	
	
//**************************************************************
// Class ObjectList
//**************************************************************

//**************************************************************
// Constructor
//
// Lists initialisation
//**************************************************************
ObjectList::ObjectList( app_objs *appref )
{
	// Initialisation
	app_ref = appref;
	app_ref->obj3dlist = this;
	cut_or_copied = NULL;
	is_cut = false;
	default_path = NULL;
	obj_name_list = NULL;
	current_ref = NULL;

	// OpenGL Lights
	// Mesa answers 0 lights at this time, disable test
	//glGetIntegerv( GL_MAX_LIGHTS, &light_num );
	//cout << "\nmax lights = " << light_num; cout.flush();
	light_num = 8;
	light_list = new bool[ light_num ];
	for ( register int i = 0 ; i < light_num ; i++ ) 
		light_list[i] = false;

	// Current object edit box
	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, NULL );

	// Add a point light to default scene
	PointLight *lum = new PointLight( app_ref );
	float loc[3] = { 0.6, 0.6, 0 };
	lum->set_location( loc );
	layer1->add_object( lum, NULL );
}



//*****************************************************************
// Add object
//
// Add an Object3D to the general list
// Called from layers only
//*****************************************************************
void ObjectList::add_object( Object3D *obj )
{
	object_liste.push_back( obj );
	update_ref_list();
}


//*****************************************************************
// Remove object
//
// Remove an object from the general list 
// Called from layers onlyénérale
//*****************************************************************
void ObjectList::remove_object( Object3D *obj )
{
	if ( obj->is_group() )
		for ( int i = 0 ; i < obj->get_children_num() ; i++ )
			remove_object( obj->get_children( i ) );	
	
	vector<Object3D*>::iterator object;
	for ( object = object_liste.begin() ; object != object_liste.end() ; object++ ) {
		if ( *object != obj ) 
			continue;	
		object_liste.erase( object );
		break;
	}
	update_ref_list();
}


//****************************************************************
// Update ref list
//
// Update the references list
// Shall be called at every list modification
//*****************************************************************
void ObjectList::update_ref_list()
{
	if ( obj_name_list != NULL ) {
		for ( int i = 0 ; i < obj_name_list_size ; i++ )
			delete obj_name_list[i];
		delete obj_name_list;
	}
	
	obj_name_list = new char*[ object_liste.size() + 1 ];
	obj_name_list[0] = new char[ strlen( obj_ref_list_def[0] ) + 1 ];
	strcpy( obj_name_list[0], (char*)obj_ref_list_def[0] );
	obj_name_list_size = 1;
	for ( unsigned int i = 0 ; i < object_liste.size() ; i ++ ) {
		if ( ! tvobj3d_is_linkable( object_liste[i] ) ) 
			continue;
		char *str = object_liste[i]->get_name();
		obj_name_list[obj_name_list_size] = new char[ strlen( str ) + 1 ];
		strcpy( obj_name_list[obj_name_list_size], str );
		obj_name_list_size++;
	}
	if ( current_ref !=NULL )
		current_ref->update_ref_list();
}


//****************************************************************
// Get index by pointer
//
// Return an index in reference list from an Object3D pointer
// For object referencers only
//*****************************************************************
int ObjectList::get_index_by_pointer( Object3D *obj )
{
	int index = 0;
	for ( unsigned int i = 0 ; i < object_liste.size() ; i ++ ) {
		if ( object_liste[i] == obj ) 
			return index;
		if ( ! tvobj3d_is_linkable( object_liste[i] ) ) 
			continue;
		index++;
	}
	return -1;
}

//****************************************************************
// Get pointer by index
//
// Return a pointer to an Object3D from its index in reference list
// For object referencers only
//*****************************************************************
Object3D * ObjectList::get_pointer_by_index( int index )
{
	int ind = 1;
	int size = object_liste.size();
	for (  int i = 0 ; i < size ; i ++ ) {
		if ( ! tvobj3d_is_linkable( object_liste[i] ) ) 
			continue;
		if ( index == ind ) {
			i++;
			while ( i < size && tvobj3d_is_linkable( object_liste[i] ) == FALSE ) 
				i++;
			return object_liste[i];
		}
		ind++;
		}
	return NULL;
}


//****************************************************************
// Get pointer by name
//
// Return a pointer to an Object3D from its name
// For object referencers only
//*****************************************************************
Object3D * ObjectList::get_pointer_by_name( char *str )
{
	for ( unsigned int i = 0 ; i < object_liste.size() ; i ++ )
		if ( ! strcmp( str, object_liste[i]->get_name()  ) ) 
			return object_liste[i];	
	return NULL;
}





//****************************************************************
// Create name
//
// Create a unique object name
// Called from Object3D 
//*****************************************************************
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;
}

//**************************************
// Test name exists
//
// Check wether an object name already exists
//**************************************
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;
}

//**************************************
// Layer name exists
//
// Check wether a layer name already exists
//**************************************
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;
}


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

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

	file << "\n\n//---------------------------------------------";
	file << "\n//   Scene description";
	file << "\n//---------------------------------------------";
	for ( unsigned int i = 0 ; i < Layers.size() ; i++ )
		Layers[i]->output_to_povray_pass2( file );
}



//****************************************************
// Display 
//
// Add every objects to the OpenGL preview
//****************************************************
void ObjectList::display( glview *view )
{
	for ( unsigned int i = 0 ; i < Layers.size() ; i++ ) 
		Layers[i]->display( view );
}


//****************************************************
// Invalidate lists
//
// Call invalidate list for every object ( force refresh )
//****************************************************
void ObjectList::invalidate_lists()
{
	for ( unsigned int i = 0 ; i < object_liste.size() ; i++ ) 
		object_liste[i]->invalidate_list();
}



//****************************************************
// Refresh edit
//
// Refresh the object edition panel
//****************************************************
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);
	}
}



//***********************************************************
// Set select widget
//
// Get the layer / object selection / manipulation panel
//***********************************************************
void ObjectList::set_select_widget( GtkWidget *wid )
{
	PREF_DEF
	select_widget = wid;
	current_param = NULL;

	// Top level box
	GtkWidget *vbox = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(wid), vbox, TRUE, TRUE, 4 );
	
	// Layer selection tree header
	GtkWidget *frame = gtk_frame_new(NULL);
	gtk_box_pack_start( GTK_BOX(vbox), frame, FALSE, TRUE, 0 );
	GtkWidget *label = gtk_label_new( N_("Layers") );
	gtk_container_add( GTK_CONTAINER(frame), label );
	
	// Layers	manipulation tree
	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 );
	layer_list_store = gtk_list_store_new( 5, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER, -1 );
	layer_list_view = gtk_tree_view_new_with_model( GTK_TREE_MODEL(layer_list_store) );
	gtk_container_add( GTK_CONTAINER(scrolled), layer_list_view );

	GtkCellRenderer *renderer1 = gtk_cell_renderer_pixbuf_new ();
	GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new ();
	layer_list_column2 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 0, NULL);
		gtk_tree_view_column_set_fixed_width( layer_list_column2, 25 ); 
		gtk_tree_view_column_set_sizing( layer_list_column2, GTK_TREE_VIEW_COLUMN_FIXED );
		gtk_tree_view_append_column( GTK_TREE_VIEW(layer_list_view), layer_list_column2 );
	layer_list_column3 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 1, NULL);
		gtk_tree_view_column_set_fixed_width( layer_list_column3, 25 );
		gtk_tree_view_column_set_sizing( layer_list_column3, GTK_TREE_VIEW_COLUMN_FIXED );
		gtk_tree_view_append_column( GTK_TREE_VIEW(layer_list_view), layer_list_column3 );
	layer_list_column4 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 2, NULL);
		gtk_tree_view_column_set_fixed_width( layer_list_column4, 25 );
		gtk_tree_view_column_set_sizing( layer_list_column4, GTK_TREE_VIEW_COLUMN_FIXED );
		gtk_tree_view_append_column( GTK_TREE_VIEW(layer_list_view), layer_list_column4 );
	GtkTreeViewColumn *column1 = gtk_tree_view_column_new_with_attributes(  N_("Layer name"), renderer2, "text", 3, NULL); 
		gtk_tree_view_append_column( GTK_TREE_VIEW(layer_list_view), column1 );
	for ( unsigned int i = 0 ; i < Layers.size() ; i ++ )
		Layers[i]->add_to_list( layer_list_store );

	layer_list_selection = gtk_tree_view_get_selection( GTK_TREE_VIEW (layer_list_view) );
	gtk_tree_selection_set_mode ( layer_list_selection, GTK_SELECTION_SINGLE );
	g_signal_connect( G_OBJECT(layer_list_selection), "changed", G_CALLBACK(sign_layer_select), this );
	gtk_signal_connect( GTK_OBJECT(layer_list_view), "button-press-event", GTK_SIGNAL_FUNC(sign_layer_view_clicked), (gpointer)this );
	
	GtkTargetEntry targets[1] ={ objects_source_target };
	gtk_drag_dest_set( layer_list_view,  (GtkDestDefaults)(GTK_DEST_DEFAULT_ALL), targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
	gtk_signal_connect( GTK_OBJECT(layer_list_view), "drag_data_received", GTK_SIGNAL_FUNC(sign_object_drop_on_layer), this );
	gtk_signal_connect( GTK_OBJECT(layer_list_view), "drag_motion", GTK_SIGNAL_FUNC(sign_object_motion_on_layer), this );

 	// Layers manipulation buttons
	GtkWidget *hbox = gtk_hbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 2 );
	GtkWidget *pix;
	for ( int i = 0 ; i < LAY_BUTTONS ; i ++ ) {
		select_lbuttons[i] = gtk_button_new();
		gtk_button_set_relief( GTK_BUTTON(select_lbuttons[i]), GTK_RELIEF_NONE );
		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 = 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] );
	}


	// Object selection tree header
	frame = gtk_frame_new(NULL);
	gtk_box_pack_start( GTK_BOX(vbox), frame, FALSE, TRUE, 0 );
	label = gtk_label_new( N_("Objects") );
	gtk_container_add( GTK_CONTAINER(frame), label );

	// Objects manipulation tree
	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 );
	object_tree_store = gtk_tree_store_new( 6, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, -1 );
	object_tree_view = gtk_tree_view_new_with_model( GTK_TREE_MODEL(object_tree_store) );
	gtk_container_add( GTK_CONTAINER(scrolled), object_tree_view );

	GtkTreeViewColumn *object_tree_column3 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 0, NULL);
		gtk_tree_view_append_column( GTK_TREE_VIEW(object_tree_view), object_tree_column3 );
	object_tree_column1 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 1, NULL);
		gtk_tree_view_column_set_fixed_width( object_tree_column1, 25 ); 
		gtk_tree_view_column_set_sizing( object_tree_column1, GTK_TREE_VIEW_COLUMN_FIXED );
		gtk_tree_view_append_column( GTK_TREE_VIEW(object_tree_view), object_tree_column1 );
	object_tree_column2 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 2, NULL);
		gtk_tree_view_column_set_fixed_width( object_tree_column2, 25 );
		gtk_tree_view_column_set_sizing( object_tree_column2, GTK_TREE_VIEW_COLUMN_FIXED );
		gtk_tree_view_append_column( GTK_TREE_VIEW(object_tree_view), object_tree_column2 );
	GtkTreeViewColumn *column5 = gtk_tree_view_column_new_with_attributes(  N_("Type"), renderer2, "text", 3, NULL); 
		gtk_tree_view_append_column( GTK_TREE_VIEW(object_tree_view), column5 );
	GtkTreeViewColumn *column6 = gtk_tree_view_column_new_with_attributes(  N_("Object name"), renderer2, "text", 4, NULL); 
		gtk_tree_view_append_column( GTK_TREE_VIEW(object_tree_view), column6 );
	
	object_tree_selection = gtk_tree_view_get_selection( GTK_TREE_VIEW (object_tree_view) );
	gtk_tree_selection_set_mode ( object_tree_selection, GTK_SELECTION_SINGLE );
	gtk_signal_connect( GTK_OBJECT(object_tree_view), "button-press-event", GTK_SIGNAL_FUNC(sign_object_view_clicked), (gpointer)this );
	gtk_signal_connect( GTK_OBJECT(object_tree_view), "row-activated", GTK_SIGNAL_FUNC(sign_object_view_double_clicked), (gpointer)this );
	
	gtk_drag_source_set( object_tree_view,  GDK_BUTTON1_MASK, targets, 1,  (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
	gtk_signal_connect( GTK_OBJECT(object_tree_view), "drag_data_get", GTK_SIGNAL_FUNC(sign_object_drag), this );
	gtk_drag_dest_set( object_tree_view,  (GtkDestDefaults)(GTK_DEST_DEFAULT_ALL), targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE) );
	gtk_signal_connect( GTK_OBJECT(object_tree_view), "drag_data_received", GTK_SIGNAL_FUNC(sign_object_drop_on_tree), this );
	gtk_signal_connect( GTK_OBJECT(object_tree_view), "drag_motion", GTK_SIGNAL_FUNC(sign_object_motion_on_tree), this );
	
	selected_layer = NULL;
	select_layer_in_list( 0 );
	if ( selected_layer == NULL ) 
		selected_layer = Layers[0];
	g_signal_connect( G_OBJECT( object_tree_selection), "changed", G_CALLBACK(sign_object_select), this );
	
	edit_box = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(edit_widget), edit_box, TRUE, TRUE, 0 );
	current = selected_layer->get_first_object();
	current->select();
	current->edit_widget(edit_box);


	// Objects manipulation buttons
	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, FALSE, 1 );
	for ( int i = 0 ; i < OBJ_BUTTONS ; i ++ ) {
		select_buttons[i] = gtk_button_new();
		gtk_button_set_relief( GTK_BUTTON(select_buttons[i]), GTK_RELIEF_NONE );
		//gtk_box_pack_start( GTK_BOX( ( i < 6 ) ? hbox : hbox2 ), select_buttons[i], TRUE, FALSE, 0 );
		gtk_box_pack_start( GTK_BOX(hbox), select_buttons[i], TRUE, FALSE, 0 );
		gtk_signal_connect( GTK_OBJECT(select_buttons[i]), "clicked", GTK_SIGNAL_FUNC(sign_select_buttons_clicked), this );	
		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] );
	}		

	// Add some accelerators
	INTERF_DEF	
	gtk_widget_add_accelerator( select_buttons[0], "clicked", interf->get_accels(), GDK_Delete, (GdkModifierType)0, (GtkAccelFlags)0 );

	gtk_widget_show_all( wid );
}


//******************************************************
// Select layer
//
// Layer selection changed callback
//******************************************************
void ObjectList::select_layer( GtkTreeSelection *selection )
{
	GtkTreeIter iter;
	GtkTreeModel *model;
	gpointer layer_pointer = NULL;
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
	    gtk_tree_model_get (model, &iter, 4, &layer_pointer, -1);
	if ( layer_pointer == NULL ) 
		return;

	if ( selected_layer != NULL )
		selected_layer->save_tree();
	selected_layer = (ObjectLayer*)layer_pointer;

	Object3D *old = current;
	unselect_object();
	if ( old != NULL ) {
		VMAN_DEF 
		vmanager->refresh();
	}
	gtk_tree_store_clear( object_tree_store );
	selected_layer->display_content( object_tree_view, object_tree_store, object_tree_selection );
	current = selected_layer->get_first_object();
	gtk_widget_queue_draw( object_tree_view );
}

//******************************************************
// Layer view clicked
//
// Layer tree button press callback
//******************************************************
void ObjectList::layer_view_clicked( GdkEventButton *ev )
{
	if ( selected_layer == NULL ) 
		return;
	GtkTreeViewColumn *column;
	GtkTreePath *path;
	gtk_tree_view_get_path_at_pos( GTK_TREE_VIEW(layer_list_view),(gint) ev->x, (gint)ev->y, &path, &column, NULL, NULL );
	if ( path == NULL ) 
		return;
	gint *indices = gtk_tree_path_get_indices( path );
	if ( selected_layer != Layers[ indices[0] ] ) 
		return;
	if ( column == layer_list_column2 ) selected_layer->toggle_property( 1 );
	if ( column == layer_list_column3 ) selected_layer->toggle_property( 2 );
	if ( column == layer_list_column4 ) selected_layer->toggle_property( 3 );
}



//******************************************************
// Layer buttons clicked
//
// Layer tree button press callback
//******************************************************
void  ObjectList::layers_buttons_clicked( GtkWidget *wid )
{
	int button = -1;
	for ( int i = 0 ; i < OBJ_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_list_store );
			select_layer_in_list( Layers.size() - 1 );
			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_list_store, i );
				undoman->push( TV_UNDO_LAYER_CREATE, copy );
				break;
			}			
			select_layer_in_list( i );
		}
		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;
	}
}


//**********************************************************
// Move  layer
//
// Move a layer in list 
// 1 -> up
// 2 -> down
//**********************************************************
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 ) {
		GtkTreeIter *iter = Layers[i-2]->get_node_iter();
		target->move_before_in_list( iter );
	} else {
		GtkTreeIter *iter = Layers[i]->get_node_iter();
		target->move_after_in_list( iter );
	}
		
	vector<ObjectLayer*>::iterator layer2 = layer;
	if ( sens == 1 ) 
		layer2--; 
	else 
		layer2++;
	Layers.erase( layer );
	Layers.insert( layer2, target );
	break;
	}
}			

//**********************************************************
// Remove  layer
//
// Remove a layer
//**********************************************************
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;
		}
		target->remove_from_layer_list();
		Layers.erase( layer );
		//delete target;
	break;
	}
	
	// Managing tree		
	current = NULL;
	selected_layer = NULL;
	if ( i < 0 ) 
		i = 0;
	if ( i > Layers.size() - 1 ) 
		i = Layers.size() - 1;
	select_layer_in_list( 0 );
}

//**********************************************************
// Insert  layer
//
// insert a layer at given position
//**********************************************************
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;
	
	// Add layer and select it
	if ( i != -1 ) 
		Layers.insert( ++layer, lay );
	else 
		Layers.push_back( lay );
	lay->add_to_list( layer_list_store, i );
	select_layer_in_list( position );
}

//**********************************************************
// Get  layer index
//
// get a layer position
//**********************************************************
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;
}



//***************************************
// Select object
//
// Object tree selection callback
//***************************************
void  ObjectList::select_object( GtkTreeSelection *selection )
{
	VMAN_DEF
	GtkTreeIter iter;
	GtkTreeModel *model;
	gpointer object_pointer = NULL;
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
	    gtk_tree_model_get (model, &iter, 5, &object_pointer, -1);
	if ( object_pointer == current ) 
		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();
		current = NULL;
	}
	if ( object_pointer == NULL ) 
		return;

	edit_box = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(edit_widget), edit_box, TRUE, TRUE, 0 );
	current = (Object3D*)object_pointer;
	current->select();
	current->edit_widget(edit_box);
	
	vmanager->refresh();

	// Set Help on menu..
	if ( GTK_IS_BIN(help_on_obj_wid) ) {
		GtkWidget *lab = gtk_bin_get_child( GTK_BIN(help_on_obj_wid) );
		char *obj_str = current->get_type_name();
		char *str = new char[ 20 + strlen( obj_str) ];
		sprintf( str, N_("Help on %s ..."), obj_str );
		gtk_label_set_text( GTK_LABEL(lab), str );
		delete str;
	}
}


//***************************************
// Object view clicked
//
// Object tree button press event callback
//***************************************
void ObjectList::object_view_clicked( GdkEventButton *ev )
{
	if ( current == NULL ) 
		return;
	GtkTreeViewColumn *column;
	GtkTreePath *path;
	gtk_tree_view_get_path_at_pos( GTK_TREE_VIEW(object_tree_view),(gint) ev->x, (gint)ev->y, &path, &column, NULL, NULL );
	if ( path == NULL ) 
		return;
	GtkTreePath *current_path = gtk_tree_model_get_path( GTK_TREE_MODEL(object_tree_store), current->get_node() );
	if ( gtk_tree_path_compare( path, current_path ) != 0 ) { 
		gtk_tree_path_free( current_path ); 
		return; 
	}
	gtk_tree_path_free( current_path );

	VMAN_DEF
	SCENE_DEF
	if ( column == object_tree_column1 ) { 
		current->toggle_visibility(); 
		scene->set_modified(); 
		vmanager->refresh(); 
	}
	if ( column == object_tree_column2 ) { 
		current->toggle_render();  
		scene->set_modified();  
	}
}


//***********************************************************
// Object view double clicked
//
// Object tree activate event callback
//***********************************************************
void ObjectList::object_view_double_clicked()
{
	PROPPANEL_DEF
	proppanel->set_page(0);
}


//***************************************
// Select button clicked
//
// Object manipulation buttons callback
//***************************************
void  ObjectList::select_buttons_clicked( GtkWidget *wid )
{
	// Get the calling button number
	int button = -1;
	for ( int i = 0 ; i < OBJ_BUTTONS ; i ++ )
		if ( wid == select_buttons[i] ) { 
			button = i; 
			break; 
		}

	SCENE_DEF
	UNDO_DEF
	if ( button == -1 ) 
		return;
	
	// Action
	switch( button ) {
		// Delete
		case 0:
			if ( current == NULL ) 
				break;
			scene->set_modified();
			undoman->push( TV_UNDO_OBJ_DELETE, current, selected_layer );	
			if ( edit_box != NULL ) { 
				current->destroy_editor(); 
				edit_box = NULL; 
			}
			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;

		// Unparent		
		case 4:
			scene->set_modified();
			undoman->push( TV_UNDO_OBJ_MOVE, current, selected_layer );				
			selected_layer->unparent_object( current );
			break;	

		// Move object up		
		case 5:
			scene->set_modified();
			undoman->push( TV_UNDO_OBJ_MOVE, current, selected_layer );				
			selected_layer->move_object_up( current );
			break;	
	
		// Move object down		
		case 6:
			scene->set_modified();
			undoman->push( TV_UNDO_OBJ_MOVE, current, selected_layer );				
			selected_layer->move_object_down( current );
			break;
	
		// Cut
		case 7:
			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 8:
	    	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 9:
	    	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(); 
}


//***********************************************************
// Unselect object
//
// unselect current object ( selection changed )
//***********************************************************
void ObjectList::unselect_object()
{
	if ( current != NULL ) {
		current->unselect();
		if ( edit_box != NULL ) 
			current->destroy_editor();
    }
	current = NULL;
	current_param = NULL;
}


//***********************************************************
// Set current param
//
// Set the current param, ie the selected Objparam
// ( if interactive ) for current object
//***********************************************************
void ObjectList::set_current_param( ObjParam* param )
{ 
	current_param = param;
	VMAN_DEF 
	vmanager->refresh(); 
}		


//************************************************************
// Object drag
//
// Object drag from tree callback
//************************************************************
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 );
}


//************************************************************
// Object drop on layer
//
// Object drop on layer callback
//************************************************************
void ObjectList::object_drop_on_layer( GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info )
{
	// Check is dropped object is valid
	if ( info != TV_DRAG_OBJECT_ID ) 
		return;
	if ( selection_data == NULL ) 
		return;
	if ( selection_data->length != sizeof(Object3D*) ) 
		return;
	if ( draged_object3D->get_type() == TV_OBJ3D_CAMERA ) 
		return;
	SCENE_DEF
	UNDO_DEF
	scene->set_modified();

	// Get layer
	GtkTreePath *path; 
	GtkTreeViewDropPosition pos;
	gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW(layer_list_view), x, y, &path, &pos);
	if ( path == NULL ) 
		return;
	gint *indices = gtk_tree_path_get_indices( path );
	gint row = indices[0];
	
	if ( selected_layer == Layers[row] ) 
		return;
	bool move =  ( dc->action == GDK_ACTION_COPY )  ? false : true;

	// LMove or copy object ti this layer
	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, NULL );
	}
	// copy
	else {
		Object3D *newobj = draged_object3D->duplicate_yourself();
		if ( selected_layer == Layers[row] )
			selected_layer->append_object( newobj );
		else 
			Layers[row]->add_object( newobj, NULL ); 
		undoman->push( TV_UNDO_OBJ_CREATE, newobj, Layers[row] );				
	}
}

//************************************************************
// Object motion on layer
//
// DND motion on layer callback
//************************************************************
void ObjectList::object_motion_on_layer( GdkDragContext *dc, gint x, gint y )
{
	GtkTreePath *path; 
	GtkTreeViewDropPosition pos;
	gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW(layer_list_view), x, y, &path, &pos);
	if ( path == NULL ) 
		return;
	gtk_tree_view_set_drag_dest_row ( GTK_TREE_VIEW(layer_list_view), path, pos );
}


//****************************************************************************
// Object drop on tree
//
// An object was dropped on the object tree view
//****************************************************************************
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();

	// Get destination ( if dropped on a group )
	GtkTreePath *path; 
	GtkTreeViewDropPosition pos;
	gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW(object_tree_view), x, y, &path, &pos);
	gpointer object_pointer = NULL;
	if ( path != NULL ) {
		GtkTreeIter iter;
		gtk_tree_model_get_iter( GTK_TREE_MODEL(object_tree_store), &iter, path );
		gtk_tree_model_get (GTK_TREE_MODEL(object_tree_store), &iter, 5, &object_pointer, -1);
	}
	Object3D *destination = (Object3D*)object_pointer;

	// Check if destination is valid, otherwise drop on root
	bool drop_on_root = false;
	if ( destination == NULL )
		drop_on_root = true;
	else if (destination->is_group() == false ) drop_on_root = true;
	
	// Check if dropped object is valid, otherwise return
	if ( destination == draged_object3D ) return;	
	if ( draged_object3D->get_type() == TV_OBJ3D_CAMERA ) 
		return;
	if ( destination != NULL ) 
		if ( destination->has_as_ancester( draged_object3D ) ) 
			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 );
		if ( drop_on_root )
			selected_layer->add_object( draged_object3D, NULL );
		else
			destination->paste_object( draged_object3D );
	}
	else {
		Object3D *newobj = draged_object3D->duplicate_yourself();
		undoman->push( TV_UNDO_OBJ_CREATE, newobj, selected_layer );				
		if ( drop_on_root )
			selected_layer->add_object( newobj, NULL );
		else
			destination->paste_object( newobj );
	}
}


//************************************************************
// Object motion on tree
//
// DND motion on object tree callback
//************************************************************
void ObjectList::object_motion_on_tree( GdkDragContext *dc, gint x, gint y )
{
	GtkTreePath *path; 
	GtkTreeViewDropPosition pos;
	gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW(object_tree_view), x, y, &path, &pos);
	if ( path == NULL ) 
		return;
	gtk_tree_view_set_drag_dest_row ( GTK_TREE_VIEW(object_tree_view), path, pos );
}



//***************************************
// Pref Changed !
//
// Preferences changed app signal
//***************************************
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 < OBJ_BUTTONS ; i ++ )
		if ( tt ) 
			gtk_tooltips_enable( select_tooltips[i] );
		else 
			gtk_tooltips_disable( select_tooltips[i] );
}



//***************************************
// set create widget
//
// Get the object creation panel
//***************************************
void ObjectList::set_create_widget( GtkWidget *wid )
{	
	
	// Object creation tree header
	GtkWidget *frame = gtk_frame_new(NULL);
	gtk_box_pack_start( GTK_BOX(wid), frame, FALSE, TRUE, 0 );
	GtkWidget *label = gtk_label_new( N_("Objects creation") );
	gtk_container_add( GTK_CONTAINER(frame), label );

	// Object creation tree
	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 );
	create_tree_store = gtk_tree_store_new( 3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER );
	create_tree = gtk_tree_view_new_with_model( GTK_TREE_MODEL(create_tree_store) );
	gtk_container_add( GTK_CONTAINER(scrolled), create_tree );

	GtkCellRenderer *renderer1 = gtk_cell_renderer_pixbuf_new ();
	GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new ();
	GtkTreeViewColumn *column1 = gtk_tree_view_column_new_with_attributes ( NULL, renderer1, "pixbuf", 0, NULL);
	GtkTreeViewColumn *column2 = gtk_tree_view_column_new_with_attributes ( N_("Name"), renderer2, "text", 1, NULL);
	gtk_tree_view_append_column( GTK_TREE_VIEW(create_tree), column1 );
	gtk_tree_view_append_column( GTK_TREE_VIEW(create_tree), column2 );
	
	create_tree_selection = gtk_tree_view_get_selection( GTK_TREE_VIEW (create_tree) );
	gtk_tree_selection_set_mode ( create_tree_selection, GTK_SELECTION_SINGLE );
	g_signal_connect( G_OBJECT(create_tree_selection), "changed", G_CALLBACK(sign_create_tree_select), this );
	gtk_signal_connect( GTK_OBJECT(create_tree), "row-activated", GTK_SIGNAL_FUNC(sign_create_tree_click), this );

	// Add objects definitions
	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 );
	add_subtree( _("Misc objects"), "object_default.xpm", Misc_tree, Misc_tree_num );

	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 );
	gtk_widget_show_all( wid );
}



//****************************************
// Add subtree
//
// Add an object category to the tree
//****************************************
void ObjectList::add_subtree( const char *name, const char *icon, Object3DDef *defs, const int def_num )
{
	// Get the category pixmap
	GdkPixbuf *pixbuf = NULL;
	char *pixmap = NULL;
	if ( icon == NULL ) 
		pixmap = tv_get_pixmap( "object_default.xpm" );
	else 
		pixmap = tv_get_pixmap( (char*)icon );
	pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;

	
	GtkTreeIter   iter; 
	gtk_tree_store_append( create_tree_store, &iter, NULL );
	gtk_tree_store_set( create_tree_store, &iter, 0, pixbuf, 1, name, 2, NULL, -1 );

	// Add objects definitions
	for ( int i = 0 ; i < def_num ; i++ ) {
		GdkPixbuf *pixbuf = NULL;
		char *pixmap = NULL;
		if ( defs[i].icon == NULL ) pixmap = tv_get_pixmap( "object_default.xpm" );
		else pixmap = tv_get_pixmap( (char*)defs[i].icon );
		pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
		delete pixmap;

		GtkTreeIter   iter2; 
		gtk_tree_store_append( create_tree_store, &iter2, &iter );
		gtk_tree_store_set( create_tree_store, &iter2, 0, pixbuf, 1, defs[i].name, 2,  (gpointer)&defs[i], -1 );
	}
}



//***************************************************
// Create tree select
//
// crete tree selection changed callbacks
//***************************************************
void ObjectList::create_tree_select( GtkTreeSelection *selection )
{
	GtkTreeIter iter;
	GtkTreeModel *model;
	gpointer objdef_ptr;
	if (gtk_tree_selection_get_selected (selection, &model, &iter))
	    gtk_tree_model_get (model, &iter, 2, &objdef_ptr, -1);
	if ( objdef_ptr != NULL ) 
		current_objdef = (Object3DDef*)objdef_ptr;
	else 
		current_objdef = NULL;
}

//***********************************************
// Create tree double clicked
//
// double click on the create tree callback
//***********************************************
void ObjectList::create_tree_click()
{
	if ( current_objdef != NULL )
		create_button_clicked();
	else {
		GtkTreeIter iter;
		GtkTreeModel *model;
		gtk_tree_selection_get_selected (create_tree_selection, &model, &iter);
		GtkTreePath *path = gtk_tree_model_get_path( model, &iter );
		bool isexp = gtk_tree_view_row_expanded( GTK_TREE_VIEW(create_tree), path );
		if ( isexp ) 
			gtk_tree_view_collapse_row( GTK_TREE_VIEW(create_tree), path );			
		else
			gtk_tree_view_expand_row( GTK_TREE_VIEW(create_tree), path, FALSE );
	}
}


//****************************************
// Create button clicked
//
// create button callback
//****************************************
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;

		case TV_OBJ3D_LIGHTGROUP:
			newbie = new LightGroup( app_ref );
			break;

		case TV_OBJ3D_BLOB:
			newbie = new Blob( app_ref );
			break;
	
		case TV_OBJ3D_TEXT:
			newbie = new Text( app_ref );
			break;

		case TV_OBJ3D_LATHE:
			newbie = new Lathe( app_ref );
			break;
	
		case TV_OBJ3D_PRISM:
			newbie = new Prism( app_ref );
			break;

		case TV_OBJ3D_LINK:
			newbie = new LinkObj( app_ref );
			break;

		case TV_OBJ3D_GROUP:
			newbie = new CsgGroup( app_ref );
			break;

		case TV_OBJ3D_ISOSURFACE:
			newbie = new IsoSurface( app_ref );
			break;

		case TV_OBJ3D_PARAMETRIC:
			newbie = new Parametric( app_ref );
			break;

		case TV_OBJ3D_JULIA:
			newbie = new Julia( app_ref );
			break;

		case TV_OBJ3D_POVSCRIPT:
			newbie = new PovScript( app_ref );
			break;

		case TV_OBJ3D_SPHERESWEEP:
			newbie = new SphereSweep( app_ref );
			break;

		case TV_OBJ3D_BICUBIC:
			newbie = new Bicubic( app_ref );
			break;
	
		default:
			newbie = NULL;
	}
	
	if ( newbie == NULL ) 
		return;
	selected_layer->add_object( newbie, current );
	SCENE_DEF
	scene->set_modified();
	UNDO_DEF
	undoman->push( TV_UNDO_OBJ_CREATE, newbie, selected_layer );

	PREF_DEF
	if ( pref->auto_obj_edit->value() ) {
		PROPPANEL_DEF
		proppanel->set_page( 0 );
	}
}


//************************************************************************
//  Create Object
//
// Create an object from its tag name
//************************************************************************
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,  "LIGHTGROUP" ) )  return new LightGroup( app_ref );
	if ( ! strcmp( tag,  "BLOB" ) )  return new Blob( app_ref );
	if ( ! strcmp( tag,  "BLOBSPHERE" ) )  return new BlobSphere( app_ref );
	if ( ! strcmp( tag,  "BLOBCYLINDER" ) )  return new BlobCylinder( app_ref );
	if ( ! strcmp( tag,  "TEXT" ) )  return new Text( app_ref );
	if ( ! strcmp( tag,  "LATHE" ) )  return new Lathe( app_ref );
	if ( ! strcmp( tag,  "LINK" ) )  return new LinkObj( app_ref );
	if ( ! strcmp( tag,  "GROUP" ) )  return new CsgGroup( app_ref );
	if ( ! strcmp( tag,  "PRISM" ) )  return new Prism( app_ref );
	if ( ! strcmp( tag,  "ISOSURFACE" ) )  return new IsoSurface( app_ref );
	if ( ! strcmp( tag,  "JULIA" ) )  return new IsoSurface( app_ref );
	if ( ! strcmp( tag,  "PARAMETRIC" ) )  return new Parametric( app_ref );
	if ( ! strcmp( tag,  "POVSCRIPT" ) )  return new PovScript( app_ref );
	if ( ! strcmp( tag,  "SPHERESWEEP" ) )  return new SphereSweep( app_ref );
	if ( ! strcmp( tag,  "BICUBIC" ) )  return new Bicubic( app_ref );
	if ( ! strcmp( tag,  "SCRIPTOBJ" ) )  return new ScriptObj( 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 
//
// save object list to a tv scene file
//****************************************
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";
}


//****************************************
// Load
//
// load object list from a tv scene file
//****************************************
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_list_store );
    		continue;
    	}
    	else 
			delete layer;
		
		tvio_skip_section(file );
	} while ( tag != NULL );

	current_ref = NULL;
	current = NULL;
	current_param = NULL;

	select_layer_in_list( 0 );
	return true;
}


//****************************************
// Clear
//
// clear object list 
//****************************************
void ObjectList::clear()
{
	for ( unsigned int i = 0 ; i < Layers.size() ; i++ )
		delete Layers[i];
	Layers.clear();
	selected_layer = NULL;
	gtk_list_store_clear( layer_list_store );
	gtk_tree_store_clear( object_tree_store );
	MainCamera = NULL;
	object_liste.clear();
	for ( register int i = 0 ; i < light_num ; i++ )
		light_list[i] = false;
}


//****************************************
// New Scene
//
// clear object list and create deafault scene list
//****************************************
void ObjectList::new_scene()
{
	for ( register int i = 1 ; i < light_num ; i++ ) 
		light_list[i] = false;

	// Current object edit box
	current = NULL;
	current_param = NULL;

	// Base Layer
	ObjectLayer *layer1 = new ObjectLayer( app_ref );
	Layers.push_back( layer1 );
	layer1->add_to_list( layer_list_store, -1 );
	
	// Camera
	MainCamera  =  new Camera( app_ref );
	layer1->add_object( MainCamera, NULL );
	
	// Light
	PointLight *lum = new PointLight( app_ref );
	layer1->add_object( lum, NULL );

	select_layer_in_list( 0 );
	VMAN_DEF
	vmanager->refresh();
}



//****************************************
// Light request
//
// free opengl light number request
// Called from light objects
//****************************************
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;
}

//****************************************
// Light set properties
//
// set opengl light properties
// Called from light objects
//****************************************
void ObjectList::light_set_properties( int lamp_id, float *location, float *color )
{
	GLfloat ambient[4]= { 0.00025, 0.00025, 0.00025, 0 };
	GLfloat specular[4] = { 0.2, 0.2, 0.2, 0 };
	GLfloat position[4] = { location[0], location[1], location[2], 1.0};
	if ( lamp_id == -1 ) { return; }
	glLightfv( light_def[lamp_id], GL_POSITION, position );
	glLightfv( light_def[lamp_id], GL_DIFFUSE, color );
	glLightfv( light_def[lamp_id], GL_AMBIENT, ambient );
	glLightfv( light_def[lamp_id], GL_SPECULAR, specular );
	glLightf( light_def[lamp_id], GL_SPOT_CUTOFF, 180.0 );
}

// For light spots
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 );
}



//****************************************
// Light enable / disable
//
// set light on / off
//****************************************
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 ] );
}



//*************************************************
// Load object
// 
// Load object from a file
// uses a file chooser to ask for file
//*************************************************
void ObjectList::load_object()
{
	// file chooser filters
	GtkFileFilter *filter1 = gtk_file_filter_new();
		gtk_file_filter_add_pattern( filter1, "*.tvo" );	
		gtk_file_filter_set_name( filter1, "Truevision object file" );	
	GtkFileFilter *filter2 = gtk_file_filter_new();
		gtk_file_filter_add_pattern( filter2, "*.*" );	
		gtk_file_filter_set_name( filter2, "All files" );	
	
	// File chooser
	INTERF_DEF
	filebox = gtk_file_chooser_dialog_new( _("Load object..."), (GtkWindow*)interf->get_gtkwin(), 
	GTK_FILE_CHOOSER_ACTION_OPEN,  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,  NULL);
	if ( default_path != NULL ) 
		gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(filebox), default_path );
	gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(filebox), filter1 );
	gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(filebox), filter2 );

	if (gtk_dialog_run (GTK_DIALOG (filebox)) == GTK_RESPONSE_ACCEPT) {
  		char *fname;
  		fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filebox));
		if ( fname == NULL ) {
			g_free( fname ); 
			return;
		}
		store_default_path( fname );
	
		// Open the object file
		ifstream file( fname, ios::binary );
		if ( ! tvio_check_file_sig( file, "TRUEVISION OBJECT" ) ) 	{
			app_warning( _("File type unknown : "), fname );
			gtk_widget_destroy( filebox );
			g_free( fname );
			return;
		}
		tvio_get_soft_version( file );

		MATLIST_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, "MATERIAL" ));
		    		if ( tex->load( file, ltag ) ) {
			    		mlist.push_back( tex );
		    			continue;
		    		}
		    		else 
						delete tex;
		    		tvio_skip_section(file );	
		    		} while ( ltag != NULL );
		 		matlist->set_load_list( &mlist );
		 		continue;
		 		}
		
			Object3D *obj = create_object( tag );
			if ( obj != NULL ) {
				if ( obj->get_type() == TV_OBJ3D_CAMERA ) {
					app_warning( _("Cannot load a camera as a single object") );
					delete obj;
					break;
				}
					
				if ( obj->load( file, tag ) ) {
					selected_layer->append_object( obj );			
					matlist->flush_load_list();
					break;
				} else 
					delete obj;
			}
		
			tvio_skip_section(file );
			} while ( tag != NULL );	
	
			file.close();
			SCENE_DEF
			scene->set_modified();
			VMAN_DEF
			vmanager->refresh();	
  			g_free (fname);
	  	}
	gtk_widget_destroy( filebox );		
}


//*************************************************
// Save object
// 
// save object in a tv object file
// uses a file chooser to ask for file
//*************************************************		
void ObjectList::save_object()		
{
	if ( current == NULL ) {
		app_warning( _("Please select an object to save...") );
		return;
	}
	
	if ( current->get_type() == TV_OBJ3D_CAMERA ) {
		app_warning( _("Cannot save camera as a single object.") );
		return;
	}
	
	// File chooser
	INTERF_DEF
	filebox = gtk_file_chooser_dialog_new( _("Save object..."), (GtkWindow*)interf->get_gtkwin(), 
	GTK_FILE_CHOOSER_ACTION_SAVE,  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,  NULL);
	if ( default_path != NULL ) 
		gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(filebox), default_path );

	if (gtk_dialog_run (GTK_DIALOG (filebox)) == GTK_RESPONSE_ACCEPT) {
  		char *fname;
  		fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filebox));
		if ( fname == NULL ) { 
			g_free( fname ); 
			return; 
		}
		store_default_path( fname );
	
		// Create file name
		char *filename;
		bool added_ext = false;
		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 name existence
		ifstream ifile( filename, ios::binary );
		if ( ifile != NULL ) {
			GtkWidget *dialog =  gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("File already exists, overwrite ?"));	
			gint button = gtk_dialog_run( GTK_DIALOG(dialog));
			gtk_widget_destroy( dialog );
			if ( button == GTK_RESPONSE_NO ) { gtk_widget_destroy(filebox);  g_free( fname ); return; }
		}
		ifile.close();

		// Open file ( create )
		ofstream file( filename, ios::out );
		file.setf( ios::fixed, ios::floatfield );
		if ( file == NULL ) { 
			app_warning( _("Cannot open : "), filename );  
			gtk_widget_destroy(filebox);  
			g_free( fname ); 
			return; 
		}

		// Write object to file
		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();
		if ( added_ext ) 
			delete filename;	
  		g_free (fname);
  	}
	gtk_widget_destroy( filebox );	
}


//*************************************************
// Store default path
// 
// store ( for the session ) the path were user
// load / save objects
//*************************************************		
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';
}



//****************************************
// Init pick names
//
// Reset pick names list
// for opengl picking ( feedback ) system
//****************************************
void ObjectList::init_pick_names()
{
	current_pick_name = 1;
	glInitNames();
	glPushName(0);
}

//****************************************
// select picked
//
// Get the selected object from the pick
// name returned by OpenGL
//****************************************
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;

	ObjectLayer *layer = res->get_layer();	
	if ( selected_layer != layer ) {
		GtkTreeIter *iter = layer->get_node_iter();
		gtk_tree_selection_select_iter( layer_list_selection, iter );
	}
	res->tree_node_select();
	
	VMAN_DEF
	vmanager->refresh();
}
