use strict;
use warnings;

$| = 1;

MyApp->new->MainLoop;

package MyApp;

use strict;
use warnings;

use Wx;
use base 'Wx::App';

sub OnInit {
   my $self = shift;

   my $frame = MyFrame->new;
   $self->SetTopWindow($frame);

   return 1;
}

package MyFrame;

use strict;
use warnings;

use Wx qw( :everything );
use base 'Wx::Frame';

sub new {
   my $class = shift;

   my $self = $class->SUPER::new( undef, -1, 'Test', [ 100, 100 ], [ 200, 200 ] );

   $self->{grid} = MyGrid->new($self);

   $self->{sizer} = Wx::BoxSizer->new( wxVERTICAL );
   $self->{sizer}->Add( $self->{grid}, 1, wxGROW );

   $self->SetSizer( $self->{sizer} );
   $self->{sizer}->SetSizeHints($self);

   $self->Show(1);

   return $self;
}

package MyGrid;

use strict;
use warnings;

use Wx qw( :everything );
use base 'Wx::Grid';

sub new {
   my $class  = shift;
   my $parent = shift;

   my $self = $class->SUPER::new( $parent, -1 );

   $self->CreateGrid( 3, 3 );

   # editor that pushes the event handler
   $self->SetCellValue(  0, 0, "Default Cell Editor -->" );
   $self->SetCellValue(  0, 1, "ESC cancels edit, grid navigation works -->" );
   $self->SetCellEditor( 0, 2, Wx::GridCellTextEditor->new );

   # editor that pushes the event handler
   $self->SetCellValue(  1, 0, "MyGridCellTextEditor1 -->" );
   $self->SetCellValue(  1, 1, "ESC does not reach our event handler, but grid navigation works -->" );
   $self->SetCellEditor( 1, 2, MyGridCellTextEditor1->new('.*') );

   # editor that does not push the event handler
   $self->SetCellValue(  2, 0, "MyGridCellTextEditor2 -->" );
   $self->SetCellValue(  2, 1, "ESC cancels the edit, but grid navigation does not work -->" );
   $self->SetCellEditor( 2, 2, MyGridCellTextEditor2->new('.*') );

   $self->SetColSize( 0, 150 );
   $self->SetColSize( 1, 350 );
   $self->SetColSize( 2, 200 );
   $self->SetRowSize( 0, 25 );
   $self->SetRowSize( 1, 25 );
   $self->SetRowSize( 2, 25 );

   return $self;
}

package MyGridCellTextEditor1;

use strict;
use warnings;

use Wx qw( :everything );
use base 'Wx::PlGridCellEditor';

use Wx::Event qw( EVT_TEXT EVT_TEXT_ENTER EVT_CHAR );

sub new {
  my $class = shift;
  my $self = $class->SUPER::new;

  return $self;
}

sub Validate {
   my $self = shift;
}

sub Create {
   my ( $self, $parent, $id, $evthandler ) = @_;
   my $textctrl = Wx::TextCtrl->new(
      $parent, $id, '',
      [ -1, -1 ],
      [ -1, -1 ],
   );

   $self->SetControl($textctrl);
   $self->GetControl->PushEventHandler($evthandler);

   my @control_keys = (
      WXK_TAB, WXK_RETURN, WXK_ESCAPE, WXK_SPACE, WXK_END, WXK_HOME, WXK_LEFT,
      WXK_UP, WXK_RIGHT, WXK_DOWN,
   );
   EVT_CHAR(
      $textctrl,
      sub {
         warn "inside EVT_CHAR event handler\n";

         my ( $ctrl, $evt ) = @_;
         my $code = $evt->GetKeyCode();
         if ( WXK_ESCAPE == $code ) {
            warn "ESC pressed\n";

            # need to reset value to original value
            $self->GetControl->SetValue( $self->{_current_value} );
            $evt->Skip();
         }
         elsif ( grep { $code == $_ } @control_keys ) {
            warn "Control Key other than ESC pressed\n";

            $evt->Skip();
         }
         else {
            warn "non Control Key pressed\n";

            $evt->Skip();
         }
      }
   );
}

sub Destroy {
   my $self = shift;
   $self->GetControl->PopEventHandler(0);
   $self->GetControl->Destroy if $self->GetControl;
   $self->SetControl( undef );
   $self->SUPER::Destroy(@_);
}

sub SetSize {
   my( $self, $size ) = @_;
   $self->GetControl->SetSize( $size );
}

sub Show {
   my( $self, $show, $attr ) = @_;
   $self->GetControl->Show( $show );
}

sub BeginEdit {
   my( $self, $row, $col, $grid ) = @_;
   my $currValue = $grid->GetCellValue( $row, $col );
   $self->{_current_value} = $currValue;
   $self->GetControl->SetValue($currValue);
   $self->GetControl->SetFocus;

   warn "set current value to $currValue\n";
}

sub EndEdit {
   my( $self, $row, $col, $grid ) = @_;
   my $value = $self->GetControl->GetValue;
   my $oldValue = $grid->GetCellValue( $row, $col );
   my $changed =  $value ne $oldValue;
   if( $changed ) { $grid->SetCellValue( $row, $col, $value ) }
   $self->SetControl( undef );

   return $changed;
}

package MyGridCellTextEditor2;

use strict;
use warnings;

use base 'Wx::PlGridCellEditor';

use Wx qw( :everything );
use Wx::Event qw( EVT_TEXT EVT_TEXT_ENTER EVT_CHAR );

sub new {
  my $class = shift;
  my $self = $class->SUPER::new;

  return $self;
}

sub Validate {
   my $self = shift;
}

sub Create {
   my ( $self, $parent, $id, $evthandler ) = @_;
   my $textctrl = Wx::TextCtrl->new(
      $parent, $id, '',
      [ -1, -1 ],
      [ -1, -1 ],
   );

   $self->SetControl($textctrl);

   # comment this out to get control key events we want
   #$self->GetControl->PushEventHandler($evthandler);

   my @control_keys = (
      WXK_TAB, WXK_RETURN, WXK_ESCAPE, WXK_SPACE, WXK_END, WXK_HOME, WXK_LEFT,
      WXK_UP, WXK_RIGHT, WXK_DOWN,
   );
   EVT_CHAR(
      $textctrl,
      sub {
         warn "inside EVT_CHAR event handler\n";

         my ( $ctrl, $evt ) = @_;
         my $code = $evt->GetKeyCode();
         if ( WXK_ESCAPE == $code ) {
            warn "ESC pressed\n";

            # want to reset value to original value
            $self->GetControl->SetValue( $self->{_current_value} );

            # now we want to pass on this event to the grid
            # skip does not do it
            $evt->Skip();
            # things I've tried:
            $evthandler->ProcessEvent($evt);
            #$parent->GetEventHandler()->ProcessEvent($evt);
         }
         elsif ( grep { $code == $_ } @control_keys ) {
            warn "Control Key other than ESC pressed\n";

            # this is where we want to pass on the event to grid
            # skip does not do it
            $evt->Skip();
         }
         else {
            warn "non Control Key pressed\n";

            $evt->Skip();
         }
      }
   );
}

sub Destroy {
   my $self = shift;
   $self->GetControl->PopEventHandler(0);
   $self->GetControl->Destroy if $self->GetControl;
   $self->SetControl( undef );
   $self->SUPER::Destroy(@_);
}

sub SetSize {
   my( $self, $size ) = @_;
   $self->GetControl->SetSize( $size );
}

sub Show {
   my( $self, $show, $attr ) = @_;
   $self->GetControl->Show( $show );
}

sub BeginEdit {
   my( $self, $row, $col, $grid ) = @_;
   my $currValue = $grid->GetCellValue( $row, $col );
   $self->{_current_value} = $currValue;
   $self->GetControl->SetValue($currValue);
   $self->GetControl->SetFocus;

   warn "set current value to $currValue\n";
}

sub EndEdit {
   my( $self, $row, $col, $grid ) = @_;
   my $value = $self->GetControl->GetValue;
   my $oldValue = $grid->GetCellValue( $row, $col );
   my $changed =  $value ne $oldValue;
   if( $changed ) { $grid->SetCellValue( $row, $col, $value ) }
   $self->SetControl( undef );

   return $changed;
}
