The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
###  $Id: GUIThing.pm 322 2008-07-19 22:32:21Z duncan $
####------------------------------------------
## @file
# Define GUIThing Class
# GUI Widgets and related capabilities
#

## @class GUIThing
# Base class for the widgets implemented in the GUI interface for OpenGL.

package OpenGL::QEng::GUIThing;

use strict;
use warnings;
use OpenGL ':all';
use File::ShareDir;
use OpenGL::QEng::Event;
use OpenGL::QEng::TextureList;

use base qw/OpenGL::QEng::OUtil/;

#--------------------------------------------------
## @cmethod % new()
# Create a GUIThing
#
sub new {
  my ($class,@props) = @_;

  @props = %{$props[0]} if (scalar(@props) == 1);

  my $self = {event     => OpenGL::QEng::Event->new,
	      x         => 0,
	      y         => 0,
	      width     => 0,
	      height    => 0,
              color     => 'pink',
              texture   => undef,
	      textColor => 'purple',
	      font      => GLUT_BITMAP_HELVETICA_10,
	      children  => undef,
	     };
  bless($self,$class);
  $self->passedArgs({@props});
  $self->create_accessors;

  $self;
}

######
###### Public Instance Methods
######

#------------------------------------------
sub adopt {
  my ($self,$child) = @_;
  push (@{$self->{children}},$child);
}

#-------------------------------------
## @method   send_event(%event)
#signal an event
sub send_event {
  $_[0]->{event}->yell(@_)
}

#---------------------------------------------------------------------------
sub inside {
  my ($self, $x, $y) = @_;

  if ($x > $self->{x}                &&
      $x < $self->{x}+$self->{width} &&
      $y > $self->{y}                &&
      $y < $self->{y}+$self->{height} ) {
    return 1;
  }
  0;
}

#-----------------------------------------------------------------------------
sub buttonPress {
  my ($self, $x, $y) = @_;

  for my $child (@{$self->{children}}) {
    if ($child->inside($x,$y)) {
      $child->buttonPress($x,$y);
      return;
    }
  }
  # still here, try us...
  $self->{state} = 1; # mouse cursor was in this button
  if ($self->{pressCallback}) {
    if (ref($self->{pressCallback}) eq 'ARRAY') {
      my @pcb = @{$self->{pressCallback}};
      my $cref = shift @pcb;
      $cref->(@pcb);
    } else {
      $self->{pressCallback}($self);
    }
  }
}

#---------------------------------------------------------------------------
sub buttonRelease {
  my ($self, $x, $y) = @_;

  for my $child (@{$self->{children}}) {
    if ($child->inside($self->{mouse}{xpress}, $self->{mouse}{ypress}) &&
	$child->inside($x,$y)) {
      $child->{mouse} = $self->{mouse};
      $child->buttonRelease($x,$y);
      return;
    }
  }
  # still here, try us...
  if ($self->{clickCallback}) {
    if (ref($self->{clickCallback}) eq 'ARRAY') {
      my @pcb = @{$self->{clickCallback}};
      my $cref = shift @pcb;
      $cref->(@pcb);
    } else {
      $self->{clickCallback}($self);
    }
    $self->{state} = 0;
  }
}

#---------------------------------------------------------------------------
sub buttonPassive {
  my ($self, $x, $y) = @_;

  for my $child (@{$self->{children}}) {
    if ($child->inside($x,$y)) {
      $child->buttonPassive($x,$y);
      return;
    }
  }
  # still here, try us...
  if (($self->{highlighted}||0) == 0) {
    $self->{highlighted} = 1;
    #$needRedraw = 1;
    glutPostRedisplay();
  }
}

#---------------------------------------------------------------------------
sub setFont {
  my ($self, $font) = @_;
  if ( $font == GLUT_BITMAP_9_BY_15 ||
       $font == GLUT_BITMAP_8_BY_13 ||
       $font == GLUT_BITMAP_TIMES_ROMAN_10 ||
       $font == GLUT_BITMAP_TIMES_ROMAN_24 ||
       $font == GLUT_BITMAP_HELVETICA_10 ||
       $font == GLUT_BITMAP_HELVETICA_12 ||
       $font == GLUT_BITMAP_HELVETICA_18) {
    $self->{font} = $font;
  } else {
    print STDERR "$font is not a recognized glut bitmap font name\n";
  }
}

#---------------------------------------------------------------------------
# \brief This function draws a text string to the screen using glut
#        bitmap fonts.
# \param font	-	the font to use. it can be one of the following :
# 		GLUT_BITMAP_9_BY_15		
# 		GLUT_BITMAP_8_BY_13			
# 		GLUT_BITMAP_TIMES_ROMAN_10	
# 		GLUT_BITMAP_TIMES_ROMAN_24	
# 		GLUT_BITMAP_HELVETICA_10	
# 		GLUT_BITMAP_HELVETICA_12	
# 		GLUT_BITMAP_HELVETICA_18	
#
# \param text	-	the text string to output
# \param x	-	the x co-ordinate
# \param y	-	the y co-ordinate
#
#-----------------------------------------------------------
sub write {
  my ($self, $font, $text, $x, $y) = @_;

  glRasterPos2i($x, $y);
  for my $c (split //, $text) {
    glutBitmapCharacter($font, ord($c));
  }
}

#-----------------------------------------------------------
## Provide function glut seems to lack
sub glutBitmapLength {
  my ($self, $font, $text) = @_;

  my $size = 0;
  for my $c (split //, $text) {
    $size += glutBitmapWidth($font, ord($c));
  }
  $size;
}

#-----------------------------------------------------------
## Determine how much of a line will fit
sub glutWhatFits {
  my ($self, $font, $text, $max) = @_;

  my $size = 0;
  my $char = 0;
  for my $c (split //, $text) {
    $size += glutBitmapWidth($font, ord($c));
    if ($size>$max) {
      return $char;
    } else {
      $char++;
    }
  }
  return -1;
}

#-----------------------------------------------------------
## @method $ tErr
# print any pending OpenGL error
sub tErr {
  my ($self, $w) = @_;

  while (my $e = glGetError()) {
    print "$e, ",gluErrorString($e)," \@:$w\n";
  }
}

#----------------------------------------------------
{;
 my $textList;

## @method $ pickTexture($key)
# Set the texture from a texture name string
 sub pickTexture {
   my ($self,$key) = @_;

   unless (defined $textList) {
     my $idir = File::ShareDir::dist_dir('Games-Quest3D');
     $idir .= '/images';
     $textList = OpenGL::QEng::TextureList->new($idir);
   }
   $textList->pickTexture($key);
 }
}

#---???---???---???---???---???---???---???---???---???---???---???---???
##### Duplicate of capabilities in Thing.  Need to Find the "right" location
# for them

## select a color by name

{;# @map_item Current colors are:
 my %colors;

 sub make_color_map {
   %colors = ('blue'     =>[0.0,0.0,1.0],
	      'purple'   =>[160.0/255.0, 23.0/255.0, 240.0/255.0],
	      'pink'     =>[1.0,0.733,0.870],
	      'pink'     =>[1.0,192.0/255.0,203.0/255.0],
	      'red'      =>[1.0,0.0,0.0],
	      'magenta'  =>[1.0,0.0,1.0],
	      'yellow'   =>[1.0,1.0,0.0],
	      'white'    =>[1.0,1.0,1.0],
	      'cyan'     =>[0.0,1.0,1.0],
	      'green'    =>[0.0,1.0,0.0],
	      'beige'    =>[245.0/255.0,245.0/255.0,135.0/255.0],
	      'brown'    =>[141.0/255.0, 76.0/255.0, 47.0/255.0],
	      'orange'   =>[255.0/255.0,165.0/255.0,0.0/255.0],
	      'gold'     =>[255.0/255.0,215.0/255.0,0.0/255.0],
	      'gray'     =>[64.0/255.0,64.0/255.0,64.0/255.0],
	      'gray75'   =>[191.0/255.0,191.0/255.0,191.0/255.0],
	      'slate gray'=>[112.0/255.0,128.0/255.0,144.0/255.0],
	      'darkgray' =>[47.0/255.0,79.0/255.0,79.0/255.0],
	      'medgray'  =>[192.0/255.0,192.0/255.0,192.0/255.0],
	      'lightgray'=>[211.0/255.0,211.0/255.0,211.0/255.0],
	      'black'    =>[0.0,0.0,0.0],
	      'cream'    =>[250.0/255.0,240.0/255.0,230.0/255.0],
	      'light green' =>[144.0/255.0,238.0/255.0,144.0/255.0],
	      'light blue' =>[173.0/255.0,216.0/255.0,230.0/255.0],
	     );
   my $path = 'rgb.txt';
   for my $p ('/etc/X11/rgb.txt',
	      '/usr/share/X11/rgb.txt',
	      '/usr/X11R6/lib/X11/rgb.txt',
	      '/usr/openwin/lib/X11/rgb.txt',
	     ) {
     ($path=$p, last) if -f $p;
   }
   if (open my $rgb,'<',$path) {
     while (my $line = <$rgb>) {
       my ($r,$g,$b,$name);
       next unless ($r,$g,$b,$name) =
	 $line =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\w.*\w)\s*$/;
       $colors{lc $name} = [$r/255.0,$g/255.0,$b/255.0,];
     }
     close $rgb;
   }
 }

#-------------------------------------
## @method setColor($color)
# set the color from a text name
 sub setColor {
   my ($self,$color) = @_;

   make_color_map() unless $colors{red};
   $color = lc $color;
   if ($color eq 'clear'){
     glColor4f(0.0,0.0,0.0,1.0);
   } elsif (defined($colors{$color})) {
     glColor4f($colors{$color}[0],$colors{$color}[1],$colors{$color}[2],1.0);
   } else {
     print "unknown color $color\n";
   }
 }

#-------------------------------------
## @method @ getColor($color)
# get the color value triplet from a text name
sub getColor {
   my ($self,$color) = @_;

   make_color_map() unless $colors{red};
   $color = lc $color;
   if (defined $colors{$color}) {
     return @{$colors{$color}};
   }
   print "unknown color $color\n";
 }
} # end closure

#==================================================================
###
### Test Driver for GUIThing Object
###
if (not defined caller()) {
  package main;

  use OpenGL qw\:all\;


}

#------------------------------------------------------------------------------
1;

__END__

=head1 NAME

GUIThing -- Base class for the OpenGL GUI widgets.

=head1 AUTHORS

John D. Overmars E<lt>F<overmars@jdovermarsa.com>E<gt>,
and Rob Duncan E<lt>F<duncan@jdovermarsa.com>E<gt>

=head1 COPYRIGHT

Copyright 2008 John D. Overmars and Rob Duncan, All rights reserved.

=head1 LICENSE

This is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut