/usr/local/CPAN/dvdrip/Video/DVDRip/GUI/Project/ClipZoom.pm


# $Id: ClipZoom.pm 2344 2007-08-09 21:37:41Z 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::Project::ClipZoom;

use base qw( Video::DVDRip::GUI::Base );

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

use Carp;
use strict;
use Video::DVDRip::GUI::FormFactory::ClipImage;

sub get_preview_hbox_width      { shift->{preview_hbox_width}           }
sub get_preview_hbox_height     { shift->{preview_hbox_height}          }
sub get_preview_image_sizes     { shift->{preview_image_sizes}          }

sub set_preview_hbox_width      { shift->{preview_hbox_width}   = $_[1] }
sub set_preview_hbox_height     { shift->{preview_hbox_height}  = $_[1] }
sub set_preview_image_sizes     { shift->{preview_image_sizes}  = $_[1] }

sub new {
    my $class = shift;

    my $self = $class->SUPER::new(@_);

    $self->{preview_hbox_width}  => undef;
    $self->{preview_hbox_height} => undef;
    $self->{preview_image_sizes} => {};

    # { clip1 => { mtime => $mtime, width => $width, height => $height } }

    $self->get_context->set_object( "clip_zoom" => $self );

    return $self;
}

sub build_factory {
    my $self = shift;

    my $context = $self->get_context;

    return Gtk2::Ex::FormFactory::VBox->new(
        $self->get_optimum_screen_size_options("page"),
        title       => '[gtk-zoom-in]'.__ "Clip & Zoom",
        object      => "title",
        active_cond => sub {
            $self->selected_title
                && $self->project
                && $self->project->created;
        },
        active_depends => "project.created",
        no_frame       => 1,
        content        => [
            Video::DVDRip::GUI::Main->build_selected_title_factory,
            $self->build_preview_images,
            $self->build_adjust_form,
        ],
    );
}

sub build_preview_images {
    my $self = shift;

    my $update_timeout;
    my $preview_image_hbox;
    return Gtk2::Ex::FormFactory::VBox->new(
        expand  => 1,
        title   => __ "Preview images",
        object  => "title",
        content => [
            Gtk2::Ex::FormFactory::HBox->new(
                spacing => 5,
                content => [
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr  => "title.preview_frame_nr",
                        label => __ "Grab preview frame #",
                        rules => "integer",
                        width => 80,
                        rules => [ "positive-integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::CheckButton->new(
                        attr  => "title.tc_force_slow_grabbing",
                        label => __ "Force slow grabbing",
                    ),
                    Gtk2::Ex::FormFactory::Button->new(
                        object       => "title",
                        label        => __ "Grab frame",
                        stock        => "gtk-redo",
                        clicked_hook => sub { $self->grab_preview_frame },
                        active_cond    => sub { !$self->progress_is_active },
                        active_depends => "progress.is_active",
                    ),
                    Gtk2::Ex::FormFactory::Button->new(
                        object       => "title",
                        label        => __ "Show video from here",
                        stock        => "dvdrip-play-movie",
                        clicked_hook => sub { $self->preview_video },
                        active_cond  => sub {
                            return unless $self->project;
                            return 0 if $self->project->rip_mode ne 'rip';
                            return !$self->progress_is_active;
                        },
                        active_depends => [ "project.rip_mode", "progress.is_active" ],
                    ),
                ],
            ),
            $preview_image_hbox = Gtk2::Ex::FormFactory::HBox->new(
                homogenous => 1,
                expand     => 1,
                content    => [
                    Gtk2::Ex::FormFactory::VBox->new(
                        content => [
                            Gtk2::Ex::FormFactory::Image->new(
                                name       => "preview_image_clip1",
                                expand     => 1,
                                attr       => "title.preview_filename_clip1",
                                bgcolor    => "#ffffff",
                                scale_hook =>
                                    sub { $self->get_preview_image_scale },
                                properties => { xalign => 0, yalign => 0 },
                                with_frame => 1,
                                signal_connect => {
                                    button_release_event => sub {
                                        $self->open_preview(
                                            type => "clip1" );
                                    },
                                    size_allocate => sub {
                                        return
                                            if $self->get_preview_hbox_width
                                            == $_[1]->width * 3
                                            and $self->get_preview_hbox_height
                                            == $_[1]->height;
                                        $self->set_preview_hbox_width(
                                            $_[1]->width * 3 );
                                        $self->set_preview_hbox_height(
                                            $_[1]->height );
                                        Glib::Source->remove($update_timeout)
                                            if $update_timeout;
                                        $update_timeout = Glib::Timeout->add(
                                            500,
                                            sub {
                                                $_->get_content->[0]->update
                                                    for @{ $preview_image_hbox
                                                        ->get_content };
                                                $update_timeout = undef;
                                                0;
                                            }
                                        );
                                        0;
                                    },
                                    realize => sub {
                                        $_[0]->window->set_cursor(
                                            Gtk2::Gdk::Cursor->new(
                                                'GDK_HAND1')
                                        );
                                        1;
                                    },
                                },
                            ),
                            Gtk2::Ex::FormFactory::Label->new(
                                name        => "preview_label_clip1",
                                attr        => "title.preview_label_clip1",
                                with_markup => 1,
                            ),
                        ],
                    ),
                    Gtk2::Ex::FormFactory::VBox->new(
                        content => [
                            Gtk2::Ex::FormFactory::Image->new(
                                name       => "preview_image_zoom",
                                expand     => 1,
                                attr       => "title.preview_filename_zoom",
                                bgcolor    => "#ffffff",
                                scale_hook =>
                                    sub { $self->get_preview_image_scale },
                                properties => { xalign => 0, yalign => 0 },
                                with_frame => 1,
                                signal_connect => {
                                    button_release_event => sub {
                                        $self->open_preview( type => "zoom" );
                                    },
                                    realize => sub {
                                        $_[0]->window->set_cursor(
                                            Gtk2::Gdk::Cursor->new(
                                                'GDK_HAND1')
                                        );
                                        1;
                                    },
                                },
                            ),
                            Gtk2::Ex::FormFactory::Label->new(
                                name        => "preview_label_zoom",
                                attr        => "title.preview_label_zoom",
                                with_markup => 1,
                            ),
                        ],
                    ),
                    Gtk2::Ex::FormFactory::VBox->new(
                        content => [
                            Gtk2::Ex::FormFactory::Image->new(
                                name       => "preview_image_clip2",
                                expand     => 1,
                                attr       => "title.preview_filename_clip2",
                                bgcolor    => "#ffffff",
                                scale_hook =>
                                    sub { $self->get_preview_image_scale },
                                properties => { xalign => 0, yalign => 0 },
                                with_frame => 1,
                                signal_connect => {
                                    button_release_event => sub {
                                        $self->open_preview(
                                            type => "clip2" );
                                    },
                                    realize => sub {
                                        $_[0]->window->set_cursor(
                                            Gtk2::Gdk::Cursor->new(
                                                'GDK_HAND1')
                                        );
                                        1;
                                    },
                                },
                            ),
                            Gtk2::Ex::FormFactory::Label->new(
                                name        => "preview_label_clip2",
                                attr        => "title.preview_label_clip2",
                                with_markup => 1,
                            ),
                        ],
                    ),
                ],
            ),
        ],
    );
}

sub build_adjust_form {
    my $self = shift;

    return Gtk2::Ex::FormFactory::VBox->new(
        title   => __ "Adjust clip and zoom parameters",
        object  => "title",
        content => [
            Gtk2::Ex::FormFactory::Table->new(
                layout => "
+--------+---------------+--+-----------------+
| Preset | Popup         |XX| Apply           |
+--------+---------------+--+-----------------+
|        | Separator     |  |                 |
+--------+---+---+---+---+--+-----------------+
|        | T | B | L | R |  |                 |
+--------+---+---+---+---+--+-----------------+
| 1stCl  | X | X | X | X |  | Generate        |
+--------+---+---+---+---+--+-----------------+
| 2ndCl  | X | X | X | X |  | 2nd to 1st      |
+--------+---+---+---+---+--+-----------------+
|        | Separator     |  |                 |
+--------+---+---+---+---+--+-----------------+
|        | W | H | V | B |  |                 |
+--------+---+---+---+---+--+--------+--------+
| Zoom   | X | X | X | X |  | CalcW  | CalcH  |
+--------+---+---+---+---+--+--------+--------+
| FastR  | X             |  | ZoomCalculator  |
+--------+---------------+--+-----------------+
",

               #		    properties => { column_spacing => 5, row_spacing => 5 },
                content => [

                    #-- Presets
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Presets",
                        widget_group => "clip_zoom_widgets",
                    ),
                    Gtk2::Ex::FormFactory::Popup->new(
                        attr => "title.preset",
                    ),
                    Gtk2::Ex::FormFactory::Label->new( label => "   ", ),
                    Gtk2::Ex::FormFactory::Button->new(
                        label        => __ "Apply preset values",
                        stock        => "gtk-apply",
                        clicked_hook => sub { $self->apply_preset_values },
                        active_cond    => sub { !$self->progress_is_active },
                        active_depends => "progress.is_active",
                        tip            => __"Overwrites all Clip & Zoom settings from the selected ".
                                            "preset and regenerates the preview images.",
                    ),

                    #-- Separator
                    Gtk2::Ex::FormFactory::HSeparator->new(),

                    #-- Clipping labels
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Top",
                        widget_group => "clip_zoom_widgets",
                    ),
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Bottom",
                        widget_group => "clip_zoom_widgets",
                    ),
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Left",
                        widget_group => "clip_zoom_widgets",
                    ),
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Right",
                        widget_group => "clip_zoom_widgets",
                    ),

                    #-- 1st Clipping
                    Gtk2::Ex::FormFactory::Label->new(
                        label       => __ "1st clipping",
                        label_group => "clip_zoom_labels",
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip1_top",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip1_bottom",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip1_left",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip1_right",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),

                    #-- Generate preview images button
                    Gtk2::Ex::FormFactory::Button->new(
                        label        => __ "Generate preview images",
                        stock        => "gtk-refresh",
                        clicked_hook => sub { $self->make_previews },
                        active_cond    => sub { !$self->progress_is_active },
                        active_depends => "progress.is_active",
                        tip          => __"Generates new preview images based ".
                                          "on the actual settings",
                    ),

                    #-- 2nd Clipping
                    Gtk2::Ex::FormFactory::Label->new(
                        label       => __ "2nd clipping",
                        label_group => "clip_zoom_labels",
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip2_top",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip2_bottom",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip2_left",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_clip2_right",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "integer", "or-empty" ],
                    ),

                    #-- Move clipping values button
                    Gtk2::Ex::FormFactory::Button->new(
                        label        => __ "Move 2nd clipping to 1st",
                        stock        => "dvdrip-clip-move",
                        clicked_hook => sub { $self->move_clip2_to_clip1 },
                        tip          => __"Turns second clipping into first clipping. ".
                                          "This is possible only with high quality scaling."
                    ),

                    #-- Separator
                    Gtk2::Ex::FormFactory::HSeparator->new(),

                    #-- Zoom titles
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Width",
                        widget_group => "clip_zoom_widgets",
                    ),
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "Height",
                        widget_group => "clip_zoom_widgets",
                    ),

                    #-- V-Bitrate / BPP Titles
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "V-Rate",
                        widget_group => "clip_zoom_widgets",
                    ),
                    Gtk2::Ex::FormFactory::Label->new(
                        label        => __ "BPP",
                        widget_group => "clip_zoom_widgets",
                    ),

                    #-- Zoom widgets
                    Gtk2::Ex::FormFactory::Label->new(
                        label       => __ "Zoom",
                        label_group => "clip_zoom_labels",
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_zoom_width",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "positive-integer", "or-empty" ],
                    ),
                    Gtk2::Ex::FormFactory::Entry->new(
                        attr         => "title.tc_zoom_height",
                        widget_group => "clip_zoom_widgets",
                        width        => 10,
                        rules        => [ "positive-integer", "or-empty" ],
                    ),

                    Gtk2::Ex::FormFactory::Label->new(
                        attr => "title.tc_video_bitrate",
                    ),
                    Gtk2::Ex::FormFactory::Label->new(
                        attr => "title.tc_video_bpp",
                    ),

                    Gtk2::Ex::FormFactory::Button->new(
                        label        => __ "Calc height",
                        stock        => "dvdrip-calc-height",
                        clicked_hook =>
                            sub { $self->calc_zoom( height => 1 ) },
                        tip          => __"Calculates the height based on the given width ".
                                          "with correct aspect ratio",
                    ),
                    Gtk2::Ex::FormFactory::Button->new(
                        label        => __ "Calc width",
                        stock        => "dvdrip-calc-width",
                        clicked_hook => sub { $self->calc_zoom( width => 1 ) },
                        tip          => __"Calculates the width based on the given height ".
                                          "with correct aspect ratio",
                    ),

                    #-- Fast resizing
                    Gtk2::Ex::FormFactory::Label->new(
                        label       => __ "Use fast resizing",
                        label_group => "clip_zoom_labels",
                    ),
                    Gtk2::Ex::FormFactory::YesNo->new(
                        attr => "title.tc_fast_resize",
                        true_label => __"Yes",
                        false_label  => __"No",
                    ),
                    Gtk2::Ex::FormFactory::Button->new(
                        label        => __ "Open zoom calculator",
                        stock        => "gtk-zoom-in",
                        clicked_hook => sub {
                            $self->open_zoom_calculator;
                        },
                    ),
                ],
            ),
        ],
    );
}

sub get_max_preview_image_size {
    my $self = shift;

    my $context = $self->get_context;
    my $title   = $context->get_object("title");

    my $preview_image_sizes = $self->get_preview_image_sizes;

    my ( $max_width, $max_height ) = ( -999999, -999999 );

    foreach my $type ( "clip1", "zoom", "clip2" ) {
        my $filename = $title->preview_filename( type => $type );
        my $mtime = ( stat $filename )[9];
        my ( $width, $height );
        if (   not $preview_image_sizes->{$type}
            or not $preview_image_sizes->{$type}->{$filename}
            or $preview_image_sizes->{$type}->{$filename}->{mtime} <= $mtime )
        {
            my $gtk_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($filename);
            $preview_image_sizes->{$type}->{$filename} = {
                mtime  => $mtime,
                width  => ( $width = $gtk_pixbuf->get_width ),
                height => ( $height = $gtk_pixbuf->get_height ),
            };
        }
        else {
            $width  = $preview_image_sizes->{$type}->{$filename}->{width};
            $height = $preview_image_sizes->{$type}->{$filename}->{height};
        }
        $max_width  = $width  if $width > $max_width;
        $max_height = $height if $height > $max_height;
    }

    return ( $max_width, $max_height );
}

sub get_preview_image_scale {
    my $self = shift;

    my ( $max_image_width, $max_image_height )
        = $self->get_max_preview_image_size;

    my $widget_width  = int( $self->get_preview_hbox_width / 3 ) - 5;
    my $widget_height = $self->get_preview_hbox_height - 5;

    my $width_scale  = $widget_width / $max_image_width;
    my $height_scale = $widget_height / $max_image_height;

    my $scale = $width_scale < $height_scale ? $width_scale : $height_scale;

    return $scale;
}

sub open_preview {
    my $self   = shift;
    my %par    = @_;
    my ($type) = @par{'type'};

    my $image;
    if ( $type =~ /clip/ ) {
        my $file_type = $type eq 'clip1' ? "orig" : "zoom";
        $image = Video::DVDRip::GUI::FormFactory::ClipImage->new(
            attr        => "title.preview_filename_$file_type",
            attr_left   => "tc_${type}_left",
            attr_right  => "tc_${type}_right",
            attr_top    => "tc_${type}_top",
            attr_bottom => "tc_${type}_bottom",
            image_type  => $type,
        );
    }
    else {
        $image = Video::DVDRip::GUI::FormFactory::ClipImage->new(
            attr    => "title.preview_filename_$type",
            no_clip => 1
        );
    }

    my $ff;
    my $images_generated = 0;

    $ff = Gtk2::Ex::FormFactory->new(
        sync    => 1,
        context => $self->get_context,
        content => [
            Gtk2::Ex::FormFactory::Window->new(
                title => "dvd::rip "
                    . __x( "Preview image: {type}", type => $type ),
                customize_hook => sub {
                    $_[0]->parent->set_resizable(0);
                },
                signal_connect_after => {
                    "destroy" => sub {
                        my ($widget) = @_;
                        $self->make_previews
                            if $type ne 'zoom'
                               && !$images_generated
                               && $image->get_clipping_changed;
                        }
                },
                content => [
                    $image,
                    Gtk2::Ex::FormFactory::DialogButtons->new(
                        clicked_hook => sub {
                            my ($widget) = @_;
                            $ff->change_mouse_cursor("watch");
                            $self->make_previews
                                if $type ne 'zoom' &&
                                   $image->get_clipping_changed;
                            $images_generated = 1;
                            1;
                        },
                    ),
                ],
            ),
        ],
    );

    $ff->open;
    $ff->update;

    1;
}

sub show_preview_images {
    my $self = shift;
    my %par = @_;
    my ($type) = @par{'type'};

    my $title = $self->selected_title;
    return 1 if not $title;

    my ( $image, @types, $filename );

    if ($type) {
        push @types, $type;
    }
    else {
        @types = qw ( clip1 zoom clip2 );
    }

    my $form_factory = $self->get_form_factory;

    foreach $type (@types) {
        $form_factory->get_widget("preview_image_$type")->update;
        $form_factory->get_widget("preview_label_$type")->update;
    }

    1;
}

sub make_previews {
    my $self = shift;

    my $title = $self->selected_title;
    return 1 if not $title;
    return 1 if $self->progress_is_active;

    my $job_planner   = $self->get_context->get_object("job_planner");
    my $exec_flow_gui = $self->get_context->get_object("exec_flow_gui");
    my $job           = $job_planner->build_make_previews_job($title);

    $job->get_post_callbacks->add(sub {
        $self->get_context->update_object_widgets("title");
        $self->get_context->update_object_widgets("bitrate_calc");
    });

    $exec_flow_gui->start_job($job);

    1;
}

sub grab_preview_frame {
    my $self = shift;

    my $title = $self->selected_title;
    return 1 if not $title;
    return 1 if $self->progress_is_active;

    my $job_planner   = $self->get_context->get_object("job_planner");
    my $exec_flow_gui = $self->get_context->get_object("exec_flow_gui");
    my $job           = $job_planner->build_grab_preview_frame_job($title);

    $job->get_post_callbacks->add(sub {
        $self->get_context->update_object_widgets("title");
        $self->get_context->update_object_widgets("bitrate_calc");
    });

    $exec_flow_gui->start_job($job);

    1;
}

sub apply_preset_values {
    my $self = shift;

    my $title = $self->selected_title;
    return 1 if not $title;
    return 1 if not $title->is_ripped;

    my $job_planner   = $self->get_context->get_object("job_planner");
    my $exec_flow_gui = $self->get_context->get_object("exec_flow_gui");
    my $job           = $job_planner->build_apply_preset_job($title);

    $job->get_post_callbacks->add(sub {
        $self->get_context->update_object_widgets("title");
        $self->get_context->update_object_widgets("bitrate_calc");
    });

    $exec_flow_gui->start_job($job);

    1;
}

sub preview_video {
    my $self = shift;

    my $title = $self->selected_title;
    return 1 if not $title;

    my $frame_nr = $title->preview_frame_nr;
    my $filename = $title->preview_filename( type => 'orig' );

    return 1 if not defined $frame_nr;

    if ( not $title->is_ripped ) {
        $self->message_window(
            message => __ "You first have to rip this title." );
        return 1;
    }

    if ( $title->project->rip_mode ne 'rip' ) {
        $self->message_window( message => __
                "This function is only avaiable for ripped DVD's." );
        return 1;
    }

    if ( $frame_nr > $title->frames or $frame_nr !~ /^\d+/ ) {
        $self->message_window(
            message => __ "Illegal frame number. Maximum is "
                . ( $title->frames - 1 ) );
        return 1;
    }

    my $command = $title->get_view_stdin_command(
        command_tmpl => $self->config('play_stdin_command'), );

    $self->log(
        __x("Executing command for video preview: {command}",
            command => $command
        )
    );

    system("$command &");

    1;
}

sub move_clip2_to_clip1 {
    my $self = shift;

    my $title = $self->selected_title;
    return 1 if not $title;

    if ( not $title->is_ripped ) {
        $self->message_window(
            message => __ "You first have to rip this title." );
        return 1;
    }

    if ( $title->tc_fast_resize ) {
        $self->message_window( message => __
                "This is not possible because\nfast resizing is enabled." );
        return 1;
    }

    $title->move_clip2_to_clip1;

    $self->make_previews;

    1;
}

sub calc_zoom {
    my $self = shift;
    my %par  = @_;
    my ( $width, $height ) = @par{ 'width', 'height' };

    my $title = $self->selected_title;
    return 1 if not $title;

    if ( not $title->is_ripped ) {
        $self->message_window(
            message => __ "You first have to rip this title." );
        return 1;
    }

    $title->calc_zoom(
        width  => $width,
        height => $height,
    );

    $self->make_previews;

    1;
}

sub open_zoom_calculator {
    my $self = shift;

    my $title = $self->selected_title;
    return 1 if not $title;

    if ( not $title->is_ripped ) {
        $self->message_window(
            message => __ "You first have to rip this title." );
        return 1;
    }

    require Video::DVDRip::GUI::ZoomCalculator;

    my $calculator = Video::DVDRip::GUI::ZoomCalculator->new(
        form_factory => $self->get_form_factory, );
    $calculator->open_window;

    1;
}

1;