/usr/local/CPAN/dvdrip/Video/DVDRip/GUI/FormFactory/ClipImage.pm


# $Id: ClipImage.pm 2187 2006-08-16 19:34:38Z joern $

#-----------------------------------------------------------------------
# Copyright (C) 2001-2006 Jörn Reder <joern AT zyn.de>.
# All Rights Reserved. See file COPYRIGHT for details.
#
# This module is part of Video::DVDRip, which is free software; you can
# redistribute it and/or modify it under the same terms as Perl itself.
#-----------------------------------------------------------------------

package Video::DVDRip::GUI::FormFactory::ClipImage;

use strict;

use base qw( Gtk2::Ex::FormFactory::Widget );

use Locale::TextDomain qw (video.dvdrip);

(
    top    => __"Top",
    bottom => __"Bottom",
    left   => __"Left",
    right  => __"Right",
);

__"Conformance check:";

sub get_type { "dvdrip_clip_image" }

sub has_additional_attrs { shift->{get_no_clip} ? [] : [qw( left right top bottom )] }

sub get_image_type              { shift->{image_type}                   }
sub get_attr_left		{ shift->{attr_left}			}
sub get_attr_right		{ shift->{attr_right}			}
sub get_attr_top		{ shift->{attr_top}			}
sub get_attr_bottom		{ shift->{attr_bottom}			}
sub get_no_clip			{ shift->{no_clip}			}
sub get_file_error		{ shift->{file_error}			}

sub set_image_type              { shift->{image_type}           = $_[1] }
sub set_attr_left		{ shift->{attr_left}		= $_[1]	}
sub set_attr_right		{ shift->{attr_right}		= $_[1]	}
sub set_attr_top		{ shift->{attr_top}		= $_[1]	}
sub set_attr_bottom		{ shift->{attr_bottom}		= $_[1]	}
sub set_no_clip			{ shift->{no_clip}		= $_[1]	}
sub set_file_error		{ shift->{file_error}		= $_[1]	}

sub get_gtk_pixbuf		{ shift->{gtk_pixbuf}			}
sub get_gtk_v_cursor		{ shift->{gtk_v_cursor}			}
sub get_gtk_h_cursor		{ shift->{gtk_h_cursor}			}
sub get_gtk_n_cursor		{ shift->{gtk_n_cursor}			}
sub get_clip_lines		{ shift->{clip_lines}			}
sub get_line_under_cursor	{ shift->{line_under_cursor}		}
sub get_dragged_line		{ shift->{dragged_line}			}
sub get_clipping_changed        { shift->{clipping_changed}             }

sub set_gtk_pixbuf		{ shift->{gtk_pixbuf}		= $_[1]	}
sub set_gtk_v_cursor		{ shift->{gtk_v_cursor}		= $_[1]	}
sub set_gtk_h_cursor		{ shift->{gtk_h_cursor}		= $_[1]	}
sub set_gtk_n_cursor		{ shift->{gtk_n_cursor}		= $_[1]	}
sub set_clip_lines		{ shift->{clip_lines}		= $_[1]	}
sub set_line_under_cursor	{ shift->{line_under_cursor}	= $_[1]	}
sub set_dragged_line		{ shift->{dragged_line}		= $_[1]	}
sub set_clipping_changed        { shift->{clipping_changed}     = $_[1] }

sub get_gtk_entries             { shift->{gtk_entries}                  }
sub get_gtk_status_label        { shift->{gtk_status_label}             }

sub get_in_entry_update         { shift->{in_entry_update}              }
sub set_gtk_status_label        { shift->{gtk_status_label}     = $_[1] }

sub set_gtk_entries             { shift->{gtk_entries}          = $_[1] }
sub set_in_entry_update         { shift->{in_entry_update}      = $_[1] }

sub new {
    my $class = shift;
    my %par = @_;
    my  ($image_type, $attr_left, $attr_right, $attr_top) =
    @par{'image_type','attr_left','attr_right','attr_top'};
    my  ($attr_bottom, $no_clip) =
    @par{'attr_bottom','no_clip'};

    my $self = $class->SUPER::new(%par);

    $self->set_image_type($image_type);
    $self->set_attr_left($attr_left);
    $self->set_attr_right($attr_right);
    $self->set_attr_top($attr_top);
    $self->set_attr_bottom($attr_bottom);
    $self->set_no_clip($no_clip);

    return $self;
}

sub build_widget {
    my $self = shift;

    my $gtk_drawing_area = Gtk2::DrawingArea->new;
    my $gtk_event_box    = Gtk2::EventBox->new;

    $gtk_event_box->add($gtk_drawing_area);
    $gtk_event_box->modify_bg( "normal", Gtk2::Gdk::Color->parse("#ffffff") );

    $gtk_drawing_area->signal_connect(
        expose_event => sub { $self->draw( $_[1] ) }, );

    $self->set_gtk_n_cursor( Gtk2::Gdk::Cursor->new('GDK_CROSSHAIR') );
    $self->set_gtk_v_cursor(
        Gtk2::Gdk::Cursor->new('GDK_SB_V_DOUBLE_ARROW') );
    $self->set_gtk_h_cursor(
        Gtk2::Gdk::Cursor->new('GDK_SB_H_DOUBLE_ARROW') );

    if ( $self->get_no_clip ) {
        $self->set_gtk_widget($gtk_drawing_area);
        $self->set_gtk_parent_widget($gtk_event_box);
        return;
    }

    $gtk_event_box->set_events(
        [ 'button_press_mask', 'pointer-motion-mask' ] );

    $gtk_event_box->signal_connect(
        button_press_event => sub { $self->button_press( $_[1] ) }, );
    $gtk_event_box->signal_connect(
        button_release_event => sub { $self->button_release() }, );
    $gtk_event_box->signal_connect(
        motion_notify_event => sub { $self->motion_notify( $_[1] ) }, );

    my $gtk_vbox = Gtk2::VBox->new;
    $gtk_vbox->pack_start($gtk_event_box, 0, 1, 0);

    my $gtk_hbox = Gtk2::HBox->new;
    $gtk_vbox->pack_start($gtk_hbox, 0, 1, 0);

    my %fields = (
        top    => __"Top",
        bottom => __"Bottom",
        left   => __"Left",
        right  => __"Right",
    );

    my %gtk_entries;

    foreach my $field ( qw(top bottom left right ) ) {
        my $gtk_label = Gtk2::Label->new($fields{$field});
        $gtk_hbox->pack_start($gtk_label, 0, 1, 0);
        my $gtk_entry = Gtk2::Entry->new();
        $gtk_entry->set_width_chars(5);
        $gtk_hbox->pack_start($gtk_entry, 0, 1, 0);
        $gtk_entries{$field} = $gtk_entry;
        $gtk_entry->signal_connect (
            changed => sub {
                return if $self->get_in_entry_update;
                my $attr_method = "get_attr_".$field;
                $self->set_object_value( $self->$attr_method, $gtk_entry->get_text );
                $self->set_clipping_changed(1);
                1;
            },
        );
    }

    my $gtk_label = Gtk2::Label->new(__"Conformance check:");
    $gtk_hbox->pack_start($gtk_label, 0, 1, 0);

    my $gtk_status_label = Gtk2::Label->new("");
    $gtk_hbox->pack_start($gtk_status_label, 0, 1, 0);

    $self->set_gtk_entries(\%gtk_entries);
    $gtk_vbox->show_all;
    
    $self->set_gtk_status_label($gtk_status_label);
    $self->set_gtk_widget($gtk_drawing_area);
    $self->set_gtk_parent_widget($gtk_vbox);

    1;
}

sub cleanup_xxx {
    my $self = shift;
    
    $self->set_gtk_status_label(undef);
    $self->set_gtk_entries(undef);
    $self->set_gtk_pixbuf(undef);
    $self->set_gtk_v_cursor(undef);
    $self->set_gtk_h_cursor(undef);
    $self->set_gtk_n_cursor(undef);
    
    1;
}

sub object_to_widget {
    my $self = shift;

    my $filename = $self->get_object_value;

    if ( !-f $filename ) {
        $self->set_file_error(1);
        $self->empty_widget;
        return 1;
    }

    $self->set_file_error(0);

    my $gtk_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($filename);
    $self->set_gtk_pixbuf($gtk_pixbuf);

    my $image_width  = $gtk_pixbuf->get_width;
    my $image_height = $gtk_pixbuf->get_height;

    $self->get_gtk_widget->set_size_request( $image_width + 1,
        $image_height + 1 );

    $self->calc_clip_lines;

    if ( $self->get_dragged_line ) {
        $self->draw_clip_lines;
    }
    else {
        $self->draw;
    }

    1;
}

sub empty_widget {
    my $self = shift;

    my $gtk_drawing_area = $self->get_gtk_widget;
    my $gtk_pixbuf = Gtk2::Gdk::Pixbuf->new( 'rgb', 0, 8, 2048, 2048 );
    $self->set_gtk_pixbuf($gtk_pixbuf);
    $self->draw;

    1;
}

sub draw {
    my $self = shift;
    my ($event) = @_;

    my $gtk_drawing_area = $self->get_gtk_widget;
    my $gtk_pixbuf       = $self->get_gtk_pixbuf;

    my $drawable = $gtk_drawing_area->window;
    my $black_gc = $gtk_drawing_area->style->black_gc;
    my $white_gc = $gtk_drawing_area->style->white_gc;

    my ( $x, $y, $width, $height );

    if ($event) {
        $x      = $event->area->x;
        $y      = $event->area->y;
        $width  = $event->area->width;
        $height = $event->area->height;
    }
    else {
        $x = $y = 0;
        $width  = $gtk_pixbuf->get_width;
        $height = $gtk_pixbuf->get_height;
    }

    if ( $x + $width > $gtk_pixbuf->get_width ) {
        $width = $gtk_pixbuf->get_width - $x;
    }

    if ( $y + $height > $gtk_pixbuf->get_height ) {
        $height = $gtk_pixbuf->get_height - $y;
    }

    $gtk_pixbuf->render_to_drawable( $drawable, $black_gc, $x, $y, $x, $y,
        $width, $height, "none", 0, 0 );

    return if $self->get_file_error;

    $self->draw_clip_lines unless $self->get_no_clip;

    1;
}

sub draw_clip_lines {
    my $self = shift;

    my $gtk_drawing_area = $self->get_gtk_widget;
    my $gtk_pixbuf       = $self->get_gtk_pixbuf;

    my $drawable = $gtk_drawing_area->window;
    my $white_gc = $gtk_drawing_area->style->white_gc;

    my $clip_lines  = $self->get_clip_lines;
    my $gtk_entries = $self->get_gtk_entries;

    my $proxy = $self->get_proxy;

    my $line;
    foreach my $type ( keys %{$clip_lines} ) {
        $line = $clip_lines->{$type};
        $drawable->draw_line( $white_gc, @{$line} );
        my $value = $self->calc_clip_value($type);
        $self->set_in_entry_update(1);
        $gtk_entries->{$type}->set_text($value);
        my $attr_method = "get_attr_".$type;
        $proxy->set_attr($self->$attr_method(), $value, 1);
        $self->set_in_entry_update(0);
    }

    my $title = $proxy->get_object;

    my $text = $title->preview_label(
        type    => $self->get_image_type,
        details => 1,
    );

    $self->get_gtk_status_label->set_markup($text);

    1;
}

sub clear_clip_line {
    my $self = shift;
    my ($type) = @_;

    my $gtk_drawing_area = $self->get_gtk_widget;
    my $gtk_pixbuf       = $self->get_gtk_pixbuf;

    my $drawable = $gtk_drawing_area->window;
    my $black_gc = $gtk_drawing_area->style->black_gc;

    my $clip_lines = $self->get_clip_lines;

    my $line = $clip_lines->{$type};

    my ( $src_x, $src_y, $dst_x, $dst_y, $width, $height );

    if ( $type eq 'top' or $type eq 'bottom' ) {
        $src_x = $dst_x = 0;
        $src_y = $dst_y = $line->[1];
        $width  = $gtk_pixbuf->get_width;
        $height = 1;
        return if $src_y >= $gtk_pixbuf->get_height;
    }
    else {
        $src_x = $dst_x = $line->[0];
        $src_y = $dst_y = 0;
        $width = 1;
        $height = $gtk_pixbuf->get_height;
        return if $src_x >= $gtk_pixbuf->get_width;
    }

    $gtk_pixbuf->render_to_drawable(
        $drawable, $black_gc, $src_x, $src_y, $src_x, $src_y,
        $width,    $height,   "none", 0,      0
    );

    1;
}

sub calc_clip_lines {
    my $self = shift;

    my $top    = $self->get_object_value( $self->get_attr_top );
    my $bottom = $self->get_object_value( $self->get_attr_bottom );
    my $left   = $self->get_object_value( $self->get_attr_left );
    my $right  = $self->get_object_value( $self->get_attr_right );

    my $gtk_pixbuf = $self->get_gtk_pixbuf;
    my $width      = $gtk_pixbuf->get_width;
    my $height     = $gtk_pixbuf->get_height;

    $self->set_clip_lines(
        {   top    => [ 0, $top,              $width - 1, $top ],
            bottom => [ 0, $height - $bottom, $width - 1, $height - $bottom ],
            left  => [ $left,           0, $left,           $height - 1 ],
            right => [ $width - $right, 0, $width - $right, $height - 1 ],
        }
    );

    1;
}

sub motion_notify {
    my $self = shift;
    my ($event) = @_;

    return if $self->get_file_error;

    if ( $self->get_dragged_line ) {
        $self->move_dragged_line($event);
        return 1;
    }

    my $threshold = 5;

    my $x = $event->x;
    my $y = $event->y;

    my $clip_lines = $self->get_clip_lines;

    my $line_under_cursor = "";

    foreach my $type ( "top", "bottom" ) {
        my $line = $clip_lines->{$type};
        if (   $y >= $line->[1] - $threshold
            && $y <= $line->[1] + $threshold ) {
            $self->get_gtk_widget->window->set_cursor(
                $self->get_gtk_v_cursor, );
            $line_under_cursor = $type;
            last;
        }
    }

    foreach my $type ( "left", "right" ) {
        my $line = $clip_lines->{$type};
        if (   $x >= $line->[0] - $threshold
            && $x <= $line->[0] + $threshold ) {
            $self->get_gtk_widget->window->set_cursor(
                $self->get_gtk_h_cursor, );
            $line_under_cursor = $type;
            last;
        }
    }

    if ( not $line_under_cursor
        and $line_under_cursor ne $self->get_line_under_cursor ) {
        $self->get_gtk_widget->window->set_cursor( $self->get_gtk_n_cursor, );
    }

    $self->set_line_under_cursor($line_under_cursor);

    1;
}

sub button_press {
    my $self = shift;
    my ($event) = @_;

    my $line_under_cursor = $self->get_line_under_cursor;
    return if not $line_under_cursor;

    $self->set_dragged_line($line_under_cursor);

    1;
}

sub move_dragged_line {
    my $self = shift;
    my ($event) = @_;

    my $type = $self->get_dragged_line;
    my $x    = $event->x;
    my $y    = $event->y;

    my $gtk_pixbuf = $self->get_gtk_pixbuf;
    my $width      = $gtk_pixbuf->get_width;
    my $height     = $gtk_pixbuf->get_height;

    $x = $width  if $x > $width;
    $y = $height if $y > $height;

    $x = 0 if $x < 0;
    $y = 0 if $y < 0;

    my $clip_lines = $self->get_clip_lines;

    $self->clear_clip_line($type);

    $x = int( $x / 2 ) * 2;
    $y = int( $y / 2 ) * 2;

    if ( $type eq 'top' or $type eq 'bottom' ) {
        $clip_lines->{$type}->[1] = $y;
        $clip_lines->{$type}->[3] = $y;

    }
    elsif ( $type eq 'left' or $type eq 'right' ) {
        $clip_lines->{$type}->[0] = $x;
        $clip_lines->{$type}->[2] = $x;
    }

    $self->draw_clip_lines;

    $self->set_clipping_changed(1);

    1;
}

sub button_release {
    my $self = shift;
    my ($type) = @_;

    $type ||= $self->get_dragged_line;

    my $value = $self->calc_clip_value($type);

    if ( $type eq 'top' ) {
        $self->set_object_value( $self->get_attr_top, $value );
    }
    elsif ( $type eq 'bottom' ) {
        $self->set_object_value( $self->get_attr_bottom, $value );
    }
    elsif ( $type eq 'left' ) {
        $self->set_object_value( $self->get_attr_left, $value );
    }
    elsif ( $type eq 'right' ) {
        $self->set_object_value( $self->get_attr_right, $value );
    }

    $self->set_dragged_line(undef);

    1;
}

sub calc_clip_value {
    my $self = shift;
    my ($type) = @_;

    $type ||= $self->get_dragged_line;

    my $clip_lines = $self->get_clip_lines;

    my $value;
    if ( $type eq 'top' ) {
        $value = $clip_lines->{$type}->[1];
    }
    elsif ( $type eq 'bottom' ) {
        $value = $self->get_gtk_pixbuf->get_height - $clip_lines->{$type}->[1];
    }
    elsif ( $type eq 'left' ) {
        $value = $clip_lines->{$type}->[0];
    }
    elsif ( $type eq 'right' ) {
        $value = $self->get_gtk_pixbuf->get_width - $clip_lines->{$type}->[0];
    }
    
    return $value;
}


1;