/*
  RGB Color Combo program.
  - This program features three slider bars that
    determine what color a GL polygon will be.
  - The program also uses a gtkgl widget.
  Tony Zuliani <zuliani@cs.unr.edu>
  Date: 3/14/00
*/

/* VERY IMPORTANT:
   This file must be included when creating a GTK program
   Also, the gtkglarea.h file contains all the information 
   needed to create and use a gtkgl widget */

#include <gtk/gtk.h>
#include <gtkgl/gtkglarea.h>
#include <gdk/gdk.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>

/* -------------------------------------------------------------- */
/* Function prototypes */
gint initDrawArea( GtkWidget * );

gint reshapeDrawArea( GtkWidget *, GdkEventConfigure * );

gint renderDrawArea();

gint redraw( GtkWidget *, GdkEventExpose * );

gint update_red( GtkWidget *, GtkAdjustment * );

gint update_green( GtkWidget *, GtkAdjustment * );

gint update_blue( GtkWidget *, GtkAdjustment * );

/* -------------------------------------------------------------- */
/* global variables */
GLfloat red, green, blue;
GtkWidget *glarea;

/* -------------------------------------------------------------- */
/* initDrawArea initializes the drawing area */
gint initDrawArea( GtkWidget *widget )
{
  /* OpenGL functions can be called only if makecurrent returns true */
  if ( gtk_gl_area_make_current(GTK_GL_AREA(widget)) ) {

    /* Set clearing color (black) and shade model (GL_FLAT) */
    glClearColor( 0.0, 0.0, 0.0, 0.0 );
    glShadeModel( GL_FLAT );
   
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glOrtho( 0.0, 1.0, 0.0, 1.0, -1.0, 1.0 );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
  }
  return TRUE;
}

/* -------------------------------------------------------------- */
/* When glarea widget size changes, the viewport size must match
   the new size */
gint reshapeDrawArea( GtkWidget *widget, GdkEventConfigure *event )
{
  /* OpenGL functions can be called only if make_current returns true */
  if ( gtk_gl_area_make_current(GTK_GL_AREA(widget)) ) {
    glViewport( 0, 0, 
		widget->allocation.width, 
		widget->allocation.height );
  }
  return renderDrawArea();
}

/* -------------------------------------------------------------- */
/* renderDrawArea updates the drawing area */
gint renderDrawArea()
{
  /* Clear the buffer */
  glClear( GL_COLOR_BUFFER_BIT );

  /* Set drawing color */
  glColor3f( red, green, blue );

  /* Draw the rectangular region */
  glRectf( 0.30, 0.30, 0.70, 0.70 );

  glFlush();

  return TRUE;
}

/* -------------------------------------------------------------- */
/* When the GL widget is exposed, its contents are redrawn */
gint redraw( GtkWidget *widget, GdkEventExpose *event )
{
  /* Draw only last expose */
  if ( event->count > 0 )
    return TRUE;

  if ( gtk_gl_area_make_current(GTK_GL_AREA(widget)) )
    renderDrawArea();

  /* Swap backbuffers to front */
  gtk_gl_area_swapbuffers( GTK_GL_AREA(widget) );

  return TRUE;
}

/* -------------------------------------------------------------- */
gint update_red( GtkWidget *widget, GtkAdjustment *adj )
{
  red = (GLfloat) ( adj->value / 255.0 );
  /* Expose GL widget by emitting signal */
  renderDrawArea();
  gtk_gl_area_swapbuffers( GTK_GL_AREA(glarea) );  
  return TRUE;
}

/* -------------------------------------------------------------- */
gint update_green( GtkWidget *widget, GtkAdjustment *adj )
{
  green = (GLfloat) ( adj->value / 255.0 );
  /* Expose GL widget by emitting signal */  
  renderDrawArea();
  gtk_gl_area_swapbuffers( GTK_GL_AREA(glarea) );  
  return TRUE;
}

/* -------------------------------------------------------------- */
gint update_blue( GtkWidget *widget, GtkAdjustment *adj )
{
  blue = (GLfloat) ( adj->value / 255.0 );
  /* Expose GL widget by emitting signal*/
  renderDrawArea();
  gtk_gl_area_swapbuffers( GTK_GL_AREA(glarea) );
  return TRUE;
}

/* -------------------------------------------------------------- */
int main(int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *quitButton;
  GtkWidget *r_label;
  GtkWidget *g_label;
  GtkWidget *b_label;

  GtkAdjustment *red_adj;
  GtkAdjustment *green_adj;
  GtkAdjustment *blue_adj;

  GtkWidget *red_scale;
  GtkWidget *green_scale;
  GtkWidget *blue_scale;

  /* Attribute list for gtkglarea widget. Specifies a
     list of Boolean attributes and enum/integer
     attribute/value pairs. The last attribute must be
     GDK_GL_NONE. See glXChooseVisual manpage for further
     explanation.
  */
  int attrlist[] = {
    GDK_GL_RGBA,
    GDK_GL_DOUBLEBUFFER,
    GDK_GL_NONE
  };
  
  /* Initialize GTK */
  gtk_init( &argc, &argv );

  /* Initial color is white */
  red = green = blue = 1.0;

  /* Check if OpenGL (GLX extension) is supported. */
  if (gdk_gl_query() == FALSE) {
    g_print("OpenGL not supported\n");
    return 0;
  }

  /* Create a new top-level window */
  window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
  
  /* Set the window's title */
  gtk_window_set_title( GTK_WINDOW (window), "RGB Color Combo" );

  /* Quit the application if a delete event occurs */
  gtk_signal_connect( GTK_OBJECT (window), "delete_event",
		      GTK_SIGNAL_FUNC (gtk_main_quit), GTK_OBJECT(window) );

  /* You should always delete gtk_gl_area widgets before exit or else
     GLX contexts are left undeleted, this may cause problems (=core dump)
     in some systems.
     Destroy method of objects is not automatically called on exit.
     You need to manually enable this feature. Do gtk_quit_add_destroy()
     for all your top level windows unless you are certain that they get
     destroy signal by other means.
  */
  gtk_quit_add_destroy( 1, GTK_OBJECT (window) );

  /* Create a new vertical box container with no space around it */
  vbox = gtk_vbox_new( FALSE, 0 );
  gtk_container_set_border_width( GTK_CONTAINER(vbox), 10 );

  /* Create a new OpenGL widget */
  glarea = GTK_WIDGET( gtk_gl_area_new(attrlist) );

  /* Events for the GL widget have to be set before the window is created */
  gtk_widget_set_events( GTK_WIDGET (glarea),
			 GDK_EXPOSURE_MASK );

  /* Set default size of the GL widget */
  gtk_gl_area_size( GTK_GL_AREA (glarea), 300, 200 );

  /* Connect the event handlers to their respective functions */
  /* Redraw GL widget when exposed. */
  gtk_signal_connect( GTK_OBJECT(glarea), "expose_event",
		      GTK_SIGNAL_FUNC(redraw), NULL );

  /* When window is resized viewport needs to be resized also */
  gtk_signal_connect( GTK_OBJECT(glarea), "configure_event",
		      GTK_SIGNAL_FUNC(reshapeDrawArea), NULL);

  /* Do initialization when widget has been realized */
  gtk_signal_connect( GTK_OBJECT(glarea), "realize",
		      GTK_SIGNAL_FUNC(initDrawArea), NULL );

  /* Now, construct the widget hierarchy */
  gtk_box_pack_start( GTK_BOX(vbox), glarea, TRUE, TRUE, 20 );

  /* Construct the slider bars for the RGB color components */
  /* For each color:
     1. Create a label describing the slider
     2. Add the label to the packing box
     3. Create a new adjustment variable
     4. Create a new horizontal scale that models that adjustment
     5. Create a callback function to handle updating of the scale
     6. Add the scale and adjustment to the packing box */

  r_label = gtk_label_new( "Red" );
  gtk_box_pack_start( GTK_BOX(vbox), r_label, FALSE, FALSE, 0 );
  red_adj = ( GtkAdjustment * ) gtk_adjustment_new( 255.0, 0.0, 256.0,
						    1.0, 5.0, 1.0 );
  red_scale = gtk_hscale_new( GTK_ADJUSTMENT(red_adj) );
  gtk_signal_connect( GTK_OBJECT(red_adj), "value_changed",
		      GTK_SIGNAL_FUNC(update_red), (gpointer) red_adj );
  gtk_box_pack_start( GTK_BOX(vbox), red_scale, FALSE, FALSE, 10 );

  g_label = gtk_label_new( "Green" );
  gtk_box_pack_start( GTK_BOX(vbox), g_label, FALSE, FALSE, 0 );
  green_adj = ( GtkAdjustment * ) gtk_adjustment_new( 255.0, 0.0, 256.0,
						      1.0, 5.0, 1.0 );
  green_scale = gtk_hscale_new( GTK_ADJUSTMENT(green_adj) );
  gtk_signal_connect( GTK_OBJECT(green_adj), "value_changed",
		      GTK_SIGNAL_FUNC(update_green), (gpointer) green_adj );
  gtk_box_pack_start( GTK_BOX(vbox), green_scale, FALSE, FALSE, 10 );

  b_label = gtk_label_new( "Blue" );
  gtk_box_pack_start( GTK_BOX(vbox), b_label, FALSE, FALSE, 0 );
  blue_adj = ( GtkAdjustment * ) gtk_adjustment_new( 255.0, 0.0, 256.0,
						     1.0, 5.0, 1.0 );
  blue_scale = gtk_hscale_new( GTK_ADJUSTMENT(blue_adj) );
  gtk_signal_connect( GTK_OBJECT(blue_adj), "value_changed",
		      GTK_SIGNAL_FUNC(update_blue), (gpointer) blue_adj );
  gtk_box_pack_start( GTK_BOX(vbox), blue_scale, FALSE, FALSE, 10 );

  /* Now, add the quit button at the bottom of the vertical packing box */
  quitButton = gtk_button_new_with_label( "Quit" );

  /* Create an event handler for the button when it is clicked;
     This event handler will close the application as well */
  gtk_signal_connect( GTK_OBJECT (quitButton), "clicked",
		      GTK_SIGNAL_FUNC (gtk_main_quit), GTK_OBJECT(window) );
  
  /* Pack the button into the vbox */
  gtk_box_pack_start( GTK_BOX(vbox), quitButton, FALSE, FALSE, 10 );

  /* Show all the widgets */
  gtk_widget_show( GTK_WIDGET(glarea) );
  gtk_widget_show( r_label );
  gtk_widget_show( red_scale );
  gtk_widget_show( g_label );
  gtk_widget_show( green_scale );
  gtk_widget_show( b_label );
  gtk_widget_show( blue_scale );
  gtk_widget_show( quitButton );
  gtk_container_add( GTK_CONTAINER (window), GTK_WIDGET(vbox) );
  gtk_widget_show( GTK_WIDGET(vbox) );
  gtk_widget_show( window );
  
  /* Now call gtk_main to process events */
  gtk_main();

  return 0;
}

