package Gtk2::Ex::Datasheet::DBI::CellRendererText;

# Originally from Odot, with bits and pieces from the CellRendererSpinButton example,
# and even some of my own stuff worked in :)

use constant x_padding => 2;
use constant y_padding => 3;

use strict;
use warnings;

use Gtk2;
use Gtk2::Gdk::Keysyms;
use Glib qw(TRUE FALSE);

use Glib::Object::Subclass
  Gtk2::CellRendererText::,
  properties => [
                    Glib::ParamSpec->object(
                                                    "editable-widget",
                                                    "Editable widget",
                                                    "The editable that's used for cell editing.",
                                                    Gtk2::Ex::Datasheet::DBI::CellEditableText::,
                                                    [ qw( readable writable ) ]
                                           ),
                    Glib::ParamSpec->boolean(
                                                    "number",
                                                    "Number",
                                                    "Should I apply number formatting to the data?",
                                                    0,
                                                    [ qw( readable writable ) ]
                                            ),
                    Glib::ParamSpec->string(
                                                    "decimals",
                                                    "Decimals",
                                                    "How many decimal places should be displayed?",
                                                    -1,
                                                    [ qw( readable writable ) ]
                                           ),
                    Glib::ParamSpec->boolean(
                                                    "currency",
                                                    "Currency",
                                                    "Should I prepend a dollar sign to the data?",
                                                    0,
                                                    [ qw( readable writable ) ]
                                            )
                ];

sub INIT_INSTANCE {
    
    my ( $cell ) = @_;
    
    my $editable = Gtk2::Ex::Datasheet::DBI::CellEditableText->new();
    
    $editable->set( border_width => $cell->get("ypad") );
    
    $editable->signal_connect( key_press_event => sub {
        
        my ( $editable, $event ) = @_;
        
        if (
            $event -> keyval == $Gtk2::Gdk::Keysyms{ Return } ||
            $event -> keyval == $Gtk2::Gdk::Keysyms{ KP_Enter }
            and not $event -> state & qw(control-mask)
           )
        {
            
            # Grab parent
            my $parent = $editable->get_parent;
            
            $editable->{ _editing_canceled } = FALSE;
            $editable->editing_done();
            $editable->remove_widget();
            
            my ( $path, $focus_column ) = $parent->get_cursor;
            my @cols = $parent->get_columns;
            my $next_col = undef;
            
            foreach my $i (0..$#cols) {
                if ($cols[$i] == $focus_column) {
                    if ($event->state >= 'shift-mask') {
                        # go backwards
                        $next_col = $cols[$i-1] if $i > 0;
                    } else {
                        # go forwards
                        $next_col = $cols[$i+1] if $i < $#cols;
                    }
                    last;
                }
            }
            
            $parent->set_cursor ($path, $next_col, 1)
                if $next_col;
            
            return TRUE;
            
        }
        
        return FALSE;
        
    });
    
    $editable->signal_connect( editing_done => sub {
        
        my ( $editable ) = @_;
        
        # gtk+ changed semantics in 2.6.  you now need to call stop_editing().
        if ( Gtk2->CHECK_VERSION( 2, 6, 0 ) ) {
            $cell->stop_editing( $editable->{ _editing_canceled } );
        }
        
        # if gtk+ < 2.4.0, emit the signal regardless of whether editing was
        # canceled to make undo/redo work.
        
        my $new = Gtk2->CHECK_VERSION( 2, 4, 0 );
        
        if ( ! $new || ( $new && ! $editable->{ _editing_canceled } ) ) {
            $cell->signal_emit( edited => $editable->{ _path }, $editable -> get_text() );
        } else {
            $cell->editing_canceled();
        }
    });
    
    $cell->set( editable_widget => $editable );
    
}

sub START_EDITING {
    
    my ( $cell, $event, $view, $path, $background_area, $cell_area, $flags ) = @_;
    
    if ( $event ) {
        return unless ( $event->button == 1 );
    }
    
    my $editable = $cell->get( "editable-widget" );
    
    $editable->{ _editing_canceled } = FALSE;
    $editable->{ _path } = $path;
    
    $editable->set_text( $cell->get( "text" ) );
    $editable->select_all();
    $editable->show();
    
    # *** TODO ***
    #
    # This is an attempt at fixing the data entry issues in custom text renderers.
    # If someone types something in a cell, then hits the 'apply' button without first exiting the editable somehow
    # ( ie hitting ENTER or clicking inside the treeview somewhere else ), then the 'edited' signal is not fired
    # HOWEVER: things go haywire when this is enabled ...
    #
    #$editable -> signal_connect(
    #        'focus-out-event'       => sub {
    #                                                        my ( $event_box, $event ) = @_;
    #                                                        $cell->signal_emit( edited => $path, $editable->get_text );
    #        }
    #                           );
    #
    # *** TODO ***
    
    return $editable;
    
}

sub get_layout {
    
    my ( $cell, $widget ) = @_;
    
    return $widget -> create_pango_layout("");
    
}

sub calc_size {
    
    my ( $cell, $layout ) = @_;
    
    my ( $width, $height ) = $layout -> get_pixel_size();
    
    return (
                0,
                0,
                $width + x_padding * 2,
                $height + y_padding * 2
           );
    
}

# *** TODO ***

# It seems that at least for now, overriding the default RENDER method is not a good idea.
# We will loose a lot of nice features such as alignment, colouring, and probably a lot more as well.
# When I ( or someone else ... hint, hint ) figures out how to do this stuff and more, we can go
# down the path of providing our own RENDER method. The goal here would be to do on-the-fly numeric and
# currency formatting, colouring, etc, all inside the CellRenderer, so we don't have to rely on the current
# method of chaining render functions to modify the actual data, which is pretty ugly, but works for now ...

# As starting points, in response to the alignment problem, Torsten Schoenfeld says:

#    I think you'll have to override GET_SIZE and return an appropriate
#    x-offset value, depending on the desired alignment.  See `perldoc
#    Gtk2::CellRenderer´ / DERIVING NEW CELL RENDERERS.  For an example, take
#    a look at gtkcellrenderertext.c in gtk+'s source, function get_size().

# Colouring can be done via pango markup

# What else do we need to support?

# *** TODO ***

#sub RENDER {
#    
#    my ( $cell, $window, $widget, $background_area, $cell_area, $expose_area, $flags ) = @_;
#    
#    my $state;
#    
#    if ( $flags & 'selected' ) {
#        $state = $widget->has_focus()
#            ? 'selected'
#            : 'active';
#    } else {
#        $state = $widget->state() eq 'insensitive'
#            ? 'insensitive'
#            : 'normal';
#    }
#    
#    my $layout = $cell->get_layout( $widget );
#    
#    my $original_text = $cell->get( "text" ) || '';
#    my ( $final_text, $markup_left, $markup_right );
#    
#    $final_text = $original_text;
#        
#    if ( $cell->get('number') ) {
#        
#        my $decimals = $cell->get('decimals');
#        my $decimal_fill = FALSE;
#        
#        if ( $decimals ne "-1" ) {
#            $decimal_fill = TRUE;
#        }
#        
#        my $formatter = Number::Format->new(
#                                                thousands_sep   => ',',
#                                                decimal_point   => '.',
#                                                decimal_fill    => $decimal_fill
#                                           );
#        
#        if ( $decimals ne "-1" ) {
#            $final_text = $formatter->round( $final_text, $decimals );
#        }
#        
#        $final_text = $formatter->format_number( $final_text );
#        
#    }
#    
#    # If we're a currency renderer, prepend a dollar sign,
#    # but only if there is data ... otherwise we don't want to render anything
#        
#    if ( $cell->get('currency') ) {
#        if ( $original_text ) {
#            $final_text = '$' . $original_text;
#        } else {
#            $final_text = '';
#        }
#    }
#    
#    # Neither xalign property nor set_alignment() seem to work here ...
#    if ( $cell->get('xalign') == 1 ) {
#        $layout->set_alignment( 'right' );
#    }
#    
#    $layout->set_text ( $final_text );
#    
#    my ( $x_offset, $y_offset, $width, $height ) = $cell->calc_size( $layout );
#    
#    $widget->get_style->paint_layout(
#                                        $window,
#                                        $state,
#                                        1,
#                                        $cell_area,
#                                        $widget,
#                                        "cellrenderertext",
#                                        $cell_area->x() + $x_offset + x_padding,
#                                        $cell_area->y() + $y_offset + y_padding,
#                                        $layout
#                                    );
#    
#}

1;
