//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// glview.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/glview.h"
#include "include/preferences.h"
#include "include/viewmanager.h"
#include "include/objectlist.h"
#include "include/scene.h"
#include <math.h>
		
drag_info glview::drag;                                 // Drag & drop infos

//*******************************************
// Définition
//*******************************************
//int attrlist[] = { GDK_GL_RGBA, GDK_GL_DOUBLEBUFFER,  GDK_GL_DEPTH_SIZE, 24, GDK_GL_ATTRIB_LIST_NONE };

const float axis_width = 2;
/* Some <math.h> files do not define M_PI... */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

GlDisplayList glview::grid_lists[3];
GlDisplayList glview::axis_list;
app_objs *glview::app_ref = NULL;
GdkGC *glview::tbar_gc = NULL;
GdkGLContext *glview::glcontext = NULL;
GdkGLConfig *glview::glconfig = NULL;
//const float nulvect[3] = {0, 0, 0};

TvWidget_point *glview::common_lookat = NULL;

gchar *glview::but_pixmaps_fnames[glview::but_pixmaps_num];
const char *but_pixmaps_path[glview::but_pixmaps_num] = { 
	"view_menu.png", 
	"view_rollup.png", 
	"view_rolldown.png", 
	"view_maximize.png", 
	"view_restore.png" };
	
	

//*******************************************
// Constructeur
//*******************************************
glview::glview( app_objs *appref, GtkWidget *box, GtkWidget *aframe, GtkWidget *tbframe )
{
	
// Initialisations
	app_ref = appref;
	PREF_DEF
	tooltips = pref->tooltips->value();
	container = box;
	drag.view = this;
	drag.rotation = &rotate;
	tbar_focus = false;
	old_zoom = 1.11;

// Paramétres
visible = true;
local_lookat = new TvWidget_point( N_("Look at"), "LOOKA", NULL, app_ref );
local_lookat->set( 0, 0, 0 );
tv_widgets.push_back( local_lookat );
if ( common_lookat == NULL ) common_lookat = new TvWidget_point( N_("Common look at"), "CLOOKA", NULL, app_ref );
common_lookat->set( 0, 0, 0 );
tv_widgets.push_back( common_lookat );

lookat  = ( pref->nav2D->value() ) ? common_lookat : local_lookat;
maximized = new TvWidget_bool( N_("Maximize"), "MAXI", NULL, app_ref, false );
tv_widgets.push_back( maximized );
rolled_up = new TvWidget_bool( N_("Roll"), "ROLLU", NULL, app_ref, false );
tv_widgets.push_back( rolled_up );
gl_solid = new TvWidget_bool( N_("Solid"), "SOL", NULL, app_ref, false );
tv_widgets.push_back( gl_solid );
gl_lighting = new TvWidget_bool( N_("Lightning"), "LIGHT", NULL, app_ref, false );
tv_widgets.push_back(  gl_lighting );
gl_smooth = new TvWidget_bool( N_("Smooth"), "SMOOTH", NULL, app_ref, true );
tv_widgets.push_back( gl_smooth );

axis = new TvWidget_bool( N_("Axis"), "AXIS", NULL, app_ref, false );
tv_widgets.push_back( axis );
label = new TvWidget_bool( N_("Label"), "LAB", NULL, app_ref, false );
tv_widgets.push_back( label );
grid_scale  = new TvWidget_float( N_("Grid scale"), "GSCALE", NULL, app_ref, 0.4 );
tv_widgets.push_back( grid_scale );
grid_major = new TvWidget_int( N_("Major grid lines"), "GMAJOR", NULL, app_ref, 10 );
tv_widgets.push_back( grid_major );

// Test OpenGL
if ( glconfig == NULL ) 
	{
	if ( gdk_gl_query_extension() == FALSE ) 	app_fatal_err( N_("OpenGL not supported !") );
	glconfig = gdk_gl_config_new_by_mode( (GdkGLConfigMode)(GDK_GL_MODE_RGB    |	GDK_GL_MODE_DEPTH  |	GDK_GL_MODE_DOUBLE) );
	}

// Pixmap gnomes
if ( but_pixmaps_fnames[0] == NULL )
	for ( register int i = 0 ; i < but_pixmaps_num ; i++ )
		but_pixmaps_fnames[i] = tv_get_pixmap( (char*)but_pixmaps_path[i] );

// Barre de titre
if ( tbframe == NULL )
	{
	tframe = gtk_frame_new( NULL );
	gtk_box_pack_start( GTK_BOX(container), tframe, FALSE, FALSE, 0 );
	gtk_container_set_border_width( GTK_CONTAINER(tframe), 0 );
	gtk_frame_set_shadow_type( GTK_FRAME(tframe), GTK_SHADOW_OUT );	
	gtk_widget_set_usize( tframe, -1, 20 );
	}
else tframe = tbframe;
tbbox = gtk_hbox_new( FALSE, 0 );
gtk_container_set_border_width( GTK_CONTAINER(tbbox), 0 );
gtk_container_add( GTK_CONTAINER(tframe), tbbox );
	//Menu
	title_bar_button_new( tbbox, 0, 0, _("Menu"), GTK_SIGNAL_FUNC(sign_pop_menu) );
	
	// Nom de la vue
	tbar_focus = false;
	tbar_str = NULL;
	tbar = gtk_drawing_area_new();
	gtk_widget_set_events( tbar, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_FOCUS_CHANGE_MASK | GDK_KEY_PRESS_MASK );
   gtk_box_pack_start( GTK_BOX(tbbox), tbar, TRUE, TRUE, 0 );
	gtk_signal_connect( GTK_OBJECT(tbar), "expose_event", GTK_SIGNAL_FUNC(sign_glview_tbar_expose), this );
	gtk_signal_connect( GTK_OBJECT(tbar), "configure_event", GTK_SIGNAL_FUNC(sign_glview_tbar_configure), this );
	gtk_signal_connect( GTK_OBJECT(tbar), "button_press_event", GTK_SIGNAL_FUNC(sign_glview_tbar_mouse_click), this );
 	gtk_signal_connect( GTK_OBJECT(tbar), "focus_in_event", GTK_SIGNAL_FUNC(sign_glview_tbar_focus_in), this );
   gtk_signal_connect( GTK_OBJECT(tbar), "focus_out_event", GTK_SIGNAL_FUNC(sign_glview_tbar_focus_out), this );
   gtk_signal_connect( GTK_OBJECT(tbar), "key_press_event", GTK_SIGNAL_FUNC(sign_glview_tbar_key_press), this );

	// Bouttons
	title_bar_button_new( tbbox, 1, 1, N_("Roll"), GTK_SIGNAL_FUNC(sign_roll) );
	title_bar_button_new( tbbox, 2, 3, N_("Maximize/Restore"), GTK_SIGNAL_FUNC(sign_maximize) );


// Frame
	if ( aframe == NULL )
		{
		frame = gtk_frame_new( NULL );
		gtk_box_pack_start( GTK_BOX(container), frame, TRUE, TRUE, 0 );
		gtk_frame_set_shadow_type( GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT );	
 		gtk_container_set_resize_mode (GTK_CONTAINER (frame), GTK_RESIZE_IMMEDIATE);
		gtk_container_set_reallocate_redraws (GTK_CONTAINER (frame), TRUE);
 		}		
	else frame = aframe;	

// Aire OpenGL
area = gtk_drawing_area_new();
if ( area == NULL ) app_fatal_err( N_("Can't create GtkGlExt object !") );
gtk_widget_set_events( area, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_SCROLL_MASK );

if ( glcontext == NULL ) 
	{
	gtk_widget_set_gl_capability( area, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE );
	gtk_signal_connect_after( GTK_OBJECT(area), "realize", GTK_SIGNAL_FUNC(initgl), this );
	gtk_signal_connect( GTK_OBJECT(area), "configure_event", GTK_SIGNAL_FUNC(reshape), this );
	gtk_signal_connect( GTK_OBJECT(area), "expose_event", GTK_SIGNAL_FUNC(display), this );
	gtk_signal_connect( GTK_OBJECT(area), "button_press_event", GTK_SIGNAL_FUNC(sign_mouse_button_pressed), this );
	gtk_signal_connect( GTK_OBJECT(area), "motion_notify_event", GTK_SIGNAL_FUNC(sign_mouse_moved), this );
	gtk_signal_connect( GTK_OBJECT(area), "scroll_event", GTK_SIGNAL_FUNC(sign_mouse_scroll), this );
		
	//GtkWidget *vbox2 = gtk_hbox_new( TRUE, 0 );
	gtk_container_add( GTK_CONTAINER(frame), area );
	//gtk_box_pack_start( GTK_BOX(frame), area, TRUE, TRUE, 0 );
	gtk_widget_show( area );
	gtk_widget_realize( area );
	glcontext = gtk_widget_get_gl_context( area );
	}
else 
	{ 
	gtk_widget_set_gl_capability( area, glconfig, glcontext, TRUE, GDK_GL_RGBA_TYPE );	
	gtk_signal_connect_after( GTK_OBJECT(area), "realize", GTK_SIGNAL_FUNC(initgl), this );
	gtk_signal_connect( GTK_OBJECT(area), "configure_event", GTK_SIGNAL_FUNC(reshape), this );
	gtk_signal_connect( GTK_OBJECT(area), "expose_event", GTK_SIGNAL_FUNC(display), this );
	gtk_signal_connect( GTK_OBJECT(area), "button_press_event", GTK_SIGNAL_FUNC(sign_mouse_button_pressed), this );
	gtk_signal_connect( GTK_OBJECT(area), "motion_notify_event", GTK_SIGNAL_FUNC(sign_mouse_moved), this );
	gtk_signal_connect( GTK_OBJECT(area), "scroll_event", GTK_SIGNAL_FUNC(sign_mouse_scroll), this );
	gtk_container_add( GTK_CONTAINER(frame), area );
	gtk_widget_show( area );
	}


gtk_widget_show_all( frame );
gtk_widget_show_all( tframe );
}

//*****************************************************
// Destructeur
//*****************************************************
glview::~glview()
{
gtk_container_remove( GTK_CONTAINER(frame), area );
gtk_container_remove( GTK_CONTAINER(tframe), tbbox );
//if ( tbar_gc != NULL ) gdk_gc_unref( tbar_gc );

delete  local_lookat;
common_lookat->set( 0, 0, 0 );
delete maximized;
delete rolled_up;
delete gl_solid;
delete gl_lighting;
delete gl_smooth;

delete axis;
delete label;
delete grid_scale;
delete grid_major; 
}


//*****************************************************
// Titlebar Buttons
//*****************************************************
GtkWidget *glview::title_bar_button_new( GtkWidget *hbox, int num, int pix,const gchar *tt, GtkSignalFunc func )
{
buttons_widget[num] = gtk_button_new();
gtk_button_set_relief( GTK_BUTTON(buttons_widget[num]), GTK_RELIEF_NONE );
GTK_WIDGET_UNSET_FLAGS( buttons_widget[num], GTK_CAN_FOCUS );	
gtk_container_set_border_width( GTK_CONTAINER(buttons_widget[num]), 0 );
gtk_box_pack_start( GTK_BOX(hbox), buttons_widget[num], FALSE, TRUE, 0 );	
if ( tt != NULL ) 
	{
	buttons_tooltips[num] = gtk_tooltips_new();
	gtk_tooltips_set_tip( buttons_tooltips[num], buttons_widget[num], tt, NULL );
	}
else buttons_tooltips[num] = NULL;
if ( !tooltips ) gtk_tooltips_disable( buttons_tooltips[num] );
buttons_gpix[num] = gtk_image_new_from_file( but_pixmaps_fnames[pix] );
gtk_container_add( GTK_CONTAINER(buttons_widget[num]), buttons_gpix[num] );
gtk_signal_connect( GTK_OBJECT(buttons_widget[num]), "clicked", func, this );
return buttons_widget[num];
}


void glview::tbar_expose( GdkEventExpose *ev )
{
if ( tbar_str == NULL ) return;
PREF_DEF
GdkColor col;
if ( tbar_focus ) pref->tbar_selected->gdk_col( col );
else  pref->tbar_unselected->gdk_col( col );
gdk_colormap_alloc_color( gtk_widget_get_colormap( tbar ), &col, FALSE, TRUE );
gdk_gc_set_foreground( tbar_gc,  &col );

gdk_draw_rectangle( tbar->window, tbar_gc, TRUE, 0, 0, tbar->allocation.width, tbar->allocation.height );
gint x = ( tbar->allocation.width - gdk_string_width( gtk_style_get_font( tbar->style ), tbar_str ) ) / 2;
gint h =   gdk_string_height( gtk_style_get_font( tbar->style ), tbar_str );
gint y = ( tbar->allocation.height - h ) / 2 + h;
gdk_draw_string( tbar->window, gtk_style_get_font( tbar->style ), tbar->style->fg_gc[tbar->state], x, y, tbar_str );
}


void glview::tbar_configure()
{
if ( tbar_gc == NULL ) tbar_gc = gdk_gc_new( tbar->window );
GTK_WIDGET_SET_FLAGS( tbar,  GTK_CAN_FOCUS );
}

void glview::tbar_mouse_click( GdkEventButton *ev )
{
if ( tbar_focus ) return;
gtk_widget_grab_focus( tbar );
}

void glview::tbar_redraw()
{
GdkRectangle rect;
rect.x = 0; rect.y = 0; rect.width = tbar->allocation.width; rect.height = tbar->allocation.height;
gtk_widget_draw( tbar, &rect );
}




//******************************************************
// Pref Changed !
//******************************************************
void glview::pref_changed()
{
PREF_DEF

// Listes
axis_list.invalidate();
for ( int i = 0 ; i < 3 ; i++ )
	grid_lists[i].invalidate();
	
// Antialias
if ( pref->line_antialias->value() ) glEnable( GL_LINE_SMOOTH );
else glDisable( GL_LINE_SMOOTH );

// Tooltips
tooltips = pref->tooltips->value();
if ( tooltips )
	for ( register int i = 0 ; i < buttons_num ; i++ )
		gtk_tooltips_enable( buttons_tooltips[i] );
else
	for ( register int i = 0 ; i < buttons_num ; i++ )
		gtk_tooltips_disable( buttons_tooltips[i] );		

 lookat = ( pref->nav2D->value() ) ? common_lookat : local_lookat;

// Grid'n'Snap
grid_scale->set( pref->grid_spacing->value() );
grid_major->set( pref->grid_major_lines->value() );
 
tbar_redraw();
}

//******************************************************
// Attach Popup
//******************************************************
void glview::attach_popup()
{
popup = gtk_menu_new();
	
	// Rafraichir
	GtkWidget *item = gtk_menu_item_new_with_label( N_("Refresh") );
	gtk_signal_connect( GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(sign_view_refresh), this );
	gtk_menu_append( GTK_MENU(popup), item );
	// Home
	item = gtk_menu_item_new_with_label( N_("Home") );
	gtk_signal_connect( GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(sign_view_home), this );
	gtk_menu_append( GTK_MENU(popup), item );

	// Séparateur
	item = gtk_menu_item_new();
	gtk_menu_append( GTK_MENU(popup), item );

	// Types de Vues
	GSList *radio = NULL;
	const gchar *vlabel[view_type_num] = { N_("Top"), N_("Front"), N_("Right"), N_("Perspective"), N_("Camera") };
	for ( int i = 0 ; i < view_type_num ; i++ )
		{	
		menu_view_types[i] = gtk_radio_menu_item_new_with_label( radio, vlabel[i] );	
		radio = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM(menu_view_types[i]) );
		gtk_menu_append( GTK_MENU(popup), menu_view_types[i] );
		GTK_CHECK_MENU_ITEM(menu_view_types[i])->active = ( i == (int)type ) ? TRUE : FALSE;
		gtk_signal_connect( GTK_OBJECT(menu_view_types[i]), "activate", GTK_SIGNAL_FUNC(sign_change_view_type), this ); 
		}

	// Parametres OpenGL
	item = gtk_menu_item_new();
	gtk_menu_append( GTK_MENU(popup), item );
	add_menu_item_bool( &menu_gl_solid, N_("Solid"), GTK_SIGNAL_FUNC(sign_toggle_solid), gl_solid->value() );
	add_menu_item_bool( &menu_gl_lighting, N_("Lighting"), GTK_SIGNAL_FUNC(sign_toggle_lighting), gl_lighting->value() );
	add_menu_item_bool( &menu_gl_smooth, N_("Smooth shading"), GTK_SIGNAL_FUNC(sign_toggle_smooth), gl_smooth->value() );
	
	// Axes
	item = gtk_menu_item_new();
	gtk_menu_append( GTK_MENU(popup), item );
	add_menu_item_bool( &menu_axis, N_("Show axis"), GTK_SIGNAL_FUNC(sign_toggle_axis), axis->value() );
	
add_menu();
gtk_menu_attach_to_widget( GTK_MENU(popup), area, popup_detacher );
gtk_widget_show_all( popup );	
}

void glview::add_menu_item_bool( GtkWidget **wid, const gchar *text, GtkSignalFunc func, bool state )
{
*wid = gtk_check_menu_item_new_with_label( text );
gtk_signal_connect( GTK_OBJECT(*wid), "activate", func, this );
gtk_check_menu_item_set_show_toggle( GTK_CHECK_MENU_ITEM(*wid), TRUE );
GTK_CHECK_MENU_ITEM(*wid)->active =  state;
gtk_menu_append( GTK_MENU(popup), *wid );
}


//******************************************************
// Init
//******************************************************
void glview::init_view()
{
//cout << "\nEnter init... "; cout.flush();
PREF_DEF
if ( !gdk_gl_drawable_gl_begin( get_drawable(), get_context() )  ) return;
glViewport( 0, 0, (GLint)area->allocation.width, (GLint)area->allocation.height );
if ( pref->line_antialias->value() ) glEnable( GL_LINE_SMOOTH );

//background->set_bkgd();

set_gl_variables();
gdk_gl_drawable_gl_end( get_drawable() );
}


//******************************************************
// Reshape
//******************************************************
void glview::reshape_view( GdkEventConfigure *ev )
{
//cout << "\nEnter shape... "; cout.flush();
if ( !gdk_gl_drawable_gl_begin( get_drawable(), get_context() ) ) return;
glViewport( 0, 0, (GLint)(area->allocation.width), (GLint)(area->allocation.height) );
//cout << "Done."; cout.flush();
//cout << "\n\twidth = " << (GLint)(area->allocation.width);
//cout << "\n\theight = " << (GLint)(area->allocation.height);
//cout.flush();
set_gl_variables();
if ( GTK_IS_WIDGET(get_drawable()) ) background->set_bkgd();
gdk_gl_drawable_gl_end( get_drawable() );
}


//******************************************************
// Display
//******************************************************
void glview::display_view( GdkEventExpose *event )
{
if ( event != NULL ) if ( event->count > 0 ) return;
refresh();
}


//******************************************************
// Refresh
//******************************************************
void glview::refresh()
{
//cout << "\nEnter refresh...glview "; cout.flush();
if ( ! GTK_WIDGET_REALIZED( area ) ) return;
GdkGLDrawable *drawable = get_drawable();
if ( ! GDK_IS_GL_DRAWABLE( drawable ) ) return;
if ( ! gdk_gl_drawable_gl_begin( drawable, get_context() ) ) return;
background->set_bkgd();
set_gl_variables();
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
//cout << "Done."; cout.flush();
gdk_gl_drawable_gl_end( drawable );
}

void glview::force_refresh() 
{ 
OBJLIST_DEF 
objlist->invalidate_lists();
refresh(); 
}

void glview::invalidate()
{
GdkRectangle rect;
rect.x = 0; rect.y = 0; rect.width = area->allocation.width; rect.height = area->allocation.height;
//cout << "\ninvalidate = " << rect.width << " - " << rect.height; cout.flush();
gdk_window_invalidate_rect( area->window, &rect, TRUE );
}

//*******************************************************
// Set GL Variables
//*******************************************************
GLfloat ambient[] = { 0.6, 0.6, 0.6, 1.0 };

void glview::set_gl_variables()
{
if ( gl_solid->value() == false ) glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
else glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
GLfloat ambient[] = { 0.2, 0.2, 0.2,  0 };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, ambient );
//glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE );

if ( gl_lighting->value() ) glEnable( GL_LIGHTING );
else glDisable( GL_LIGHTING );

if ( gl_smooth->value() ) glShadeModel( GL_SMOOTH );
else glShadeModel( GL_FLAT );
glLineWidth( LINE_WIDTH );
glPointSize( POINT_SIZE );
glEnable( GL_DEPTH_TEST );
glEnable( GL_NORMALIZE );
glDisable( GL_CULL_FACE );
//glEnable( GL_BLEND );
//glBlendFunc( GL_SRC_ALPHA, GL_DST_ALPHA );
}


//*******************************************************
// Click
//*******************************************************
void glview::click( GdkEventButton *ev )
{
tbar_mouse_click(NULL);
VMAN_DEF
drag.first_click = true;
drag.view = this;
drag.rotation = &rotate;


		
switch ( ev->button )
	{
	case 1:
		{
		GdkModifierType state;
		int x,y;
		gdk_window_get_pointer( ev->window, &x, &y, &state );
		drag.origin_x = drag.previous_x = drag.current_x = x;
		drag.origin_y = drag.previous_y = drag.current_y = y;

		OBJLIST_DEF
		float *centre3d, centre2d[2];
		Object3D *current =  objlist->get_current();
		if ( current == NULL ) return;
		centre3d = current->get_location();
		if ( centre3d == NULL )
			{ drag.center_x = x; drag.center_y = y; }
		else
			{
			project_point( centre2d, centre3d );
			drag.center_x = (int)centre2d[0];
			drag.center_y = (int)centre2d[1];
			}
		SCENE_DEF
		scene->set_modified();
		if  ( vmanager->get_pointer_mode() == TV_PMODE_SELECT && ev->button != 3)
			{
			pick_object( ev );
			return;
			}
		}
		break;

	case 3:
		gtk_menu_popup( GTK_MENU(popup), NULL, NULL, NULL, NULL, ev->button, ev->time );
		break;

	default:
		break;
	}
}




//***************************************************
// Picking
//***************************************************
void glview::pick_object( GdkEventButton *ev )
{
//cout << "\nTry to select something !"; cout.flush();
OBJLIST_DEF
int x,y;
GdkModifierType state;
gdk_window_get_pointer( ev->window, &x, &y, &state );
	
GLuint select_buffer[512];
GLint hits;
glSelectBuffer( 512, select_buffer );
glRenderMode( GL_SELECT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
gluPickMatrix( (GLdouble)x, (GLdouble)(viewport[3] - y ), 5.0, 5.0, viewport );

set_viewport();

objlist->init_pick_names();
objlist->display( this );	
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glFlush();	

bool last = ( state & GDK_SHIFT_MASK ) ? true : false;
int selected = -1;	
float zmin = 100;
float zmax = 0;
hits = glRenderMode( GL_RENDER );
GLuint names, *ptr;
ptr = (GLuint *)select_buffer;
for ( int i = 0 ; i < hits ; i++ )
	{
	names = *ptr;
	//cout << "\n\t\thit n°" << i ;
	//cout << "\n\t\tnumber of names for this hit = " << names;
	ptr++;
	float z1 =  (float)*ptr/0x7fffffff;
	//cout << "\n\t\tz1 is " << (float)*ptr/0x7fffffff;
	ptr++;
	//cout << "\n\t\tz2 is " << (float)*ptr/0x7fffffff;
	ptr++;
	//cout << "\n\t\tnames are -> ";
	for ( unsigned int j = 0 ; j < names; j++ )
		{
		//cout << *ptr << ' ';
		//if ( j == 0 )  selected = *ptr;
		if ( z1 < zmin && ! last ) { zmin = z1; selected = *ptr; }
		if ( z1 > zmax && last ) { zmax = z1; selected = *ptr; }
		
		ptr++;
		}
	}
//cout.flush();

if ( selected == -1 ) return;
objlist->select_picked( selected );
}


//******************************************************
// Draw axis
//******************************************************
void glview::draw_axis()
{
PREF_DEF

if ( ! axis_list.exec() )
	{
	axis_list.begin();
	glLineWidth( axis_width );
	glBegin( GL_LINES );
	pref->axis_color->gl_set_rgb();
	// Axes
	glVertex3f( 0, 0, 0 );
	glVertex3f( 1, 0, 0 );
	glVertex3f( 0, 0, 0 );
	glVertex3f( 0, 1, 0 );
	glVertex3f( 0, 0, 0 );
	glVertex3f( 0, 0, 1 );
	
	glEnd();
	glLineWidth( LINE_WIDTH );
	axis_list.end();
	}
}


//******************************************************
// Draw grid
//******************************************************
void glview::draw_grid( int mode )
{
	draw_grid( mode, 1.0);
}
	
void glview::draw_grid( int mode , float zoom)
{
	int j = 0;
	PREF_DEF
		
		/*if (( ! grid_lists[mode].exec() ) || (old_zoom != zoom)) */ {
		
		
		//grid_lists[mode].begin();
		
		TvWidget_color *collist[3] = { pref->gridxz_color, pref->gridxy_color, pref->gridyz_color };
		collist[mode]->gl_set_rgb_for_wireframe();
		
		glLineWidth (0.5);
		glBegin (GL_LINES);
		
		float tmp_coord = 1.1 * zoom;

		
		if(tmp_coord < 1.1)
			tmp_coord = 1.1;

		/* Trackball offset */
		switch( mode ) {
		default:
			break;
		case TV_VIEW_TOP:
			tmp_coord += fmax(fabs(lookat->get(0)), fabs(lookat->get(2)));
			break;
			
		case TV_VIEW_FRONT:
			tmp_coord += fmax(fabs(lookat->get(0)), fabs(lookat->get(1)));
			break;
			
		case TV_VIEW_RIGHT:
			tmp_coord += fmax(fabs(lookat->get(1)), fabs(lookat->get(2)));
			break;
		}

		// some loop-unrolling. :-) Also make sure a "major line" goes along the axis
		for ( float i = 0.0 ; i < tmp_coord ; i += grid_scale->value() ) {
			if ( j % grid_major->value() == 0)
				glColor3f (0.0F, 0.0F, 0.0F);
			else
				collist[mode]->gl_set_rgb_for_wireframe();
			j++;
			
			switch( mode ) {
			default:
			case TV_VIEW_TOP:           // XZ
				glVertex3f( i, 0, -tmp_coord );
				glVertex3f( i, 0, tmp_coord );
				glVertex3f( -tmp_coord, 0, i );
				glVertex3f( tmp_coord, 0, i );
				
				glVertex3f( -i, 0, -tmp_coord );
				glVertex3f( -i, 0, tmp_coord );
				glVertex3f( -tmp_coord, 0, -i );
				glVertex3f( tmp_coord, 0, -i );
				break;
				
			case TV_VIEW_FRONT:           // XY
				glVertex3f( i, -tmp_coord, 0 );
				glVertex3f( i, tmp_coord, 0 );
				glVertex3f( -tmp_coord, i, 0 );
				glVertex3f( tmp_coord, i, 0 );
				
				glVertex3f( -i, -tmp_coord, 0 );
				glVertex3f( -i, tmp_coord, 0 );
				glVertex3f( -tmp_coord, -i, 0 );
				glVertex3f( tmp_coord, -i, 0 );
				break;
				
			case TV_VIEW_RIGHT:           // YZ
				glVertex3f( 0, -tmp_coord, i );
				glVertex3f( 0, tmp_coord, i );
				glVertex3f( 0, i, tmp_coord );
				glVertex3f( 0, i, -tmp_coord );
				
				glVertex3f( 0, -tmp_coord, -i );
				glVertex3f( 0, tmp_coord, -i );
				glVertex3f( 0, -i, tmp_coord );
				glVertex3f( 0, -i, -tmp_coord );
				break;
			}
		}
		glEnd ();
		//grid_lists[mode].end();
	}
	old_zoom = zoom;
}


//****************************************************
// Hide / Show
//****************************************************
void glview::RollUp()
{
if ( rolled_up->value() )
	{
	gtk_widget_show_all( frame );
	gtk_image_set_from_file( GTK_IMAGE(buttons_gpix[1]), but_pixmaps_fnames[1] );
	rolled_up->set( false );
	visible = true;
	tbar_mouse_click( NULL );
	}
else
	{
	gtk_widget_hide_all( frame );
	gtk_image_set_from_file( GTK_IMAGE(buttons_gpix[1]), but_pixmaps_fnames[2] );
	rolled_up->set( true );
	visible = false;
	}
}

//****************************************************
// Maximize / Restore
//****************************************************
void glview::MaximizeRestore()
{	
VMAN_DEF
if ( maximized->value() )
	{
	vmanager->unhide_sisters();	
	gtk_image_set_from_file( GTK_IMAGE(buttons_gpix[2]), but_pixmaps_fnames[3] );
	maximized->set( false );
	tbar_mouse_click( NULL );
	}
else 
	{
	vmanager->hide_sisters( this, container );	
	gtk_image_set_from_file( GTK_IMAGE(buttons_gpix[2]), but_pixmaps_fnames[4] );
	maximized->set( true );
	tbar_mouse_click( NULL );
	}
}

//****************************************************
// Hide + Show
//****************************************************
void glview::Hide()
{
SCENE_DEF
scene->set_modified();
gtk_widget_hide_all( tframe );
gtk_widget_hide_all( frame );
visible = false;
}

void glview::Show()
{
SCENE_DEF
scene->set_modified();
gtk_widget_show_all( tframe );
if ( !rolled_up->value() )
	{
	gtk_widget_show_all( frame );
	visible = true;
	tbar_mouse_click( NULL );
	}
}

//****************************************************
// Mutate
//****************************************************
void glview::mutate( GtkWidget *sender )
{
if ( sender == menu_view_types[type] ) { return; }
SCENE_DEF
scene->set_modified();

int new_type = 0;
for ( int i = 0 ; i < view_type_num ; i++ )
	if ( GTK_CHECK_MENU_ITEM(menu_view_types[i])->active ) new_type = i;
	
change_type( (ViewType)new_type );
}


void glview::change_type( ViewType new_type )
{
if ( type == new_type ) return;
// de la 2D vers une autre 2D
if ( new_type != TV_VIEW_3D && type != TV_VIEW_3D && new_type != TV_VIEW_CAMERA && type != TV_VIEW_CAMERA )
	{
	reset_view( new_type );
	return;
	}
else
	{ 	
	VMAN_DEF
	vmanager->change_view( this, (ViewType)new_type, container, frame, tframe, maximized->value() );	
	return;
	}
}


//****************************************************
// Project point
//****************************************************
bool glview::project_point( float *point2d, float *point3d )
{
if ( !gdk_gl_drawable_gl_begin( get_drawable(), get_context() ) ) return false;

GLint viewport[4];
GLdouble mvmatrix[16], projmatrix[16];
glGetIntegerv( GL_VIEWPORT, viewport );
glGetDoublev( GL_MODELVIEW_MATRIX, mvmatrix );
glGetDoublev( GL_PROJECTION_MATRIX, projmatrix );

GLdouble winx, winy, winz;
bool res = gluProject( (GLdouble)point3d[0], (GLdouble)point3d[0], (GLdouble)point3d[0], 
						mvmatrix, projmatrix, viewport, 
						&winx, &winy, &winz );
point2d[0] = winx;
point2d[1] = winy;
gdk_gl_drawable_gl_end( get_drawable() ); 
return res;
}


//*******************************************************
// Save
//*******************************************************
void glview::save_basics( ofstream & file )
{
TvWidget_int wtype( N_("Type"), "TYPE", NULL, app_ref, type );
wtype.save( file );
local_lookat->save( file );
common_lookat->save( file );
file << " ROT"; rotate.save( file );
maximized->save( file );
rolled_up->save( file );
gl_solid->save( file );
gl_smooth->save( file );
//gl_antialias->save( file );
//gl_depthcue->save( file );
gl_lighting->save( file );
axis->save( file );
label->save( file );
grid_scale->save( file );
}


bool glview::load_basics( ifstream & file, char *tag )
{
if ( local_lookat->load( file, tag ) ) return true;
if ( common_lookat->load( file, tag ) ) return true;
if ( gl_solid->load( file, tag ) ) return true;
if ( gl_smooth->load( file, tag ) ) return true;
if ( gl_lighting->load( file, tag ) ) return true;
if ( axis->load( file, tag ) ) return true;
if ( label->load( file, tag ) ) return true;
if ( grid_scale->load( file, tag ) ) return true;
	
if ( ! strcmp( "ROT", tag ) )
	{
	rotate.load( file );
	return true;
	}
return false;
}


void glview::clear( ViewType cl_type )
{
PREF_DEF
// Paramétres
//visible = true;
local_lookat->set( 0, 0, 0 );
common_lookat->set( 0, 0, 0 );
lookat  = ( pref->nav2D->value() ) ? common_lookat : local_lookat;

if ( maximized->value() ) MaximizeRestore();
if ( rolled_up->value() ) RollUp();

gl_solid->set( false );
gl_lighting->set( false );
gl_smooth->set( true );
GTK_CHECK_MENU_ITEM(menu_gl_solid)->active =  gl_solid->value();
GTK_CHECK_MENU_ITEM(menu_gl_lighting)->active =  gl_lighting->value();
GTK_CHECK_MENU_ITEM(menu_gl_smooth)->active =  gl_smooth->value();
GTK_CHECK_MENU_ITEM(menu_axis)->active =  axis->value();

axis->set( false );
label->set( false );
if ( !gdk_gl_drawable_gl_begin( get_drawable(), get_context() ) ) return;
OBJLIST_DEF
objlist->light_disable_all();
gdk_gl_drawable_gl_end( get_drawable() );
}


bool glview::handle_base_key_press( GdkEventKey *ev )
{
VMAN_DEF
switch ( ev->keyval )
	{
	// GL_SOLID
	case GDK_Z:	
	case GDK_z:
		gl_solid->toggle();
		GTK_CHECK_MENU_ITEM(menu_gl_solid)->active =  gl_solid->value();
		refresh();
		return true;
		
	// GL_LIGHT
	case GDK_L:	
	case GDK_l:
		gl_lighting->toggle();
		GTK_CHECK_MENU_ITEM(menu_gl_lighting)->active =  gl_lighting->value();
		refresh();
		return true;
	
	// GL_SMOOTH
	case GDK_O:	
	case GDK_o:
		gl_smooth->toggle();
		GTK_CHECK_MENU_ITEM(menu_gl_smooth)->active =  gl_smooth->value();
		refresh();
		return true;

	// GL_AXIS	
	case GDK_A:	
	case GDK_a:
		axis->toggle();
		GTK_CHECK_MENU_ITEM(menu_axis)->active =  axis->value();
		refresh();
		return true;

	// GL_REFRESH		
	case GDK_E:
	case GDK_e:
		force_refresh();
		return true;
	
	// HOME
	case GDK_H:
	case GDK_h:
		reset_home();
		return true;
	
	// FRONT
	case 65457:
		for ( int i = 0 ; i < view_type_num ; i++ )
			GTK_CHECK_MENU_ITEM(menu_view_types[i])->active = ( i == TV_VIEW_FRONT ) ? TRUE : FALSE;
		change_type( TV_VIEW_FRONT );
		return true;
	
	// TOP
	case 65463:
		for ( int i = 0 ; i < view_type_num ; i++ )
			GTK_CHECK_MENU_ITEM(menu_view_types[i])->active = ( i == TV_VIEW_TOP ) ? TRUE : FALSE;
		change_type( TV_VIEW_TOP );
		return true;
		
	// RIGHT
	case 65459:
		for ( int i = 0 ; i < view_type_num ; i++ )
			GTK_CHECK_MENU_ITEM(menu_view_types[i])->active = ( i == TV_VIEW_RIGHT ) ? TRUE : FALSE;
		change_type( TV_VIEW_RIGHT );
		return true;
		
	// PERSPECTIVE
	case 65461:
		for ( int i = 0 ; i < view_type_num ; i++ )
			GTK_CHECK_MENU_ITEM(menu_view_types[i])->active = ( i == TV_VIEW_3D ) ? TRUE : FALSE;
		change_type( TV_VIEW_3D );
		return true;
	
	// CAMERA (2)
	case 65458:
		for ( int i = 0 ; i < view_type_num ; i++ )
			GTK_CHECK_MENU_ITEM(menu_view_types[i])->active = ( i == TV_VIEW_CAMERA ) ? TRUE : FALSE;
		change_type( TV_VIEW_CAMERA );
		return true;
		
		
	// POINTER TRACKBALL
	case GDK_T:
	case GDK_t:
		vmanager->set_pointer_mode( TV_PMODE_TRACKBALL );
		break;
	
	// POINTER SELECT
	case GDK_P:
	case GDK_p:
		vmanager->set_pointer_mode( TV_PMODE_SELECT );
		break;
		
	// POINTER SCALE
	case GDK_S:
	case GDK_s:
		vmanager->set_pointer_mode( TV_PMODE_SCALE );
		break;
	
	// POINTER ROTATE
	case GDK_R:
	case GDK_r:
		vmanager->set_pointer_mode( TV_PMODE_ROTATE );
		break;
				
	// POINTER TRANSLATE
	case GDK_M:
	case GDK_m:
		vmanager->set_pointer_mode( TV_PMODE_TRANSLATE );
		break;
				
	default:
		return false;
		break;
	}
return false;
}
