# Part of the StreamDVD project package StreamDVD::Gui; use strict; use vars qw($VERSION @ISA @EXPORT); require Exporter; @ISA = qw(Exporter); @EXPORT = qw( ); $VERSION = '0.1'; use Tk; # needed modules use Tk::BrowseEntry; use Tk::LabFrame; use Tk::Photo; use Tk::JPEG::Lite; use StreamDVD::Defs; use StreamDVD::Calc; use strict; sub new { # create new gui object my $class = shift; my $self = bless {}, $class; my ($conf, $conf_ok) = @_; # get config object and status, if settings could be loaded $self->{CONFIG} = $conf; $self->_init($conf_ok); return $self; } sub status { # update status line my $self = shift; my ($text) = @_; if( defined $text ) { $self->{STATUS} = $text; $self->{WIN_BASE}->update(); } return $self->{STATUS}; } sub refresh { # refresh gui display, needed after data updates my $self = shift; $self->{WIN_BASE}->update(); } sub show_calc { # display calculator window my $self = shift; my $Calc = new StreamDVD::Calc($self->{WIN_BASE}); } sub show_config { # display configuration window my $self = shift; $self->{CONFIG}->dialog($self->{WIN_BASE}); } sub enable_buttonbar { # enable the buttons in buttonbar my $self = shift; foreach my $key (keys %{$self->{BUTTON}}) { my $btn = $self->{BUTTON}{$key}; $btn->configure(-state=>"normal"); } } sub disable_buttonbar { # disable the buttons in buttonbar my $self = shift; foreach my $key (keys %{$self->{BUTTON}}) { my $btn = $self->{BUTTON}{$key}; $btn->configure(-state=>"disabled"); } } sub show_details { # display title details my $self = shift; my $selectednum = $self->{LIST}->curselection; my $selectedtext = $self->{LIST}->get($selectednum); my $t = $self->{TITLE}[$selectednum]; my @audio_select; my @subtitle_select; my @chap_select; my $content = ""; my $linenum = 9; my $bla; $self->{PROJECT}->title($selectednum); # set displayed title as project title $content .= $selectedtext . "\n\n"; $content .= "Aspect Ratio : " . $t->aspect() . " " . $t->resolution() . "\n"; # display source data $content .= "Frames per Sec. : " . $t->fps() . "\n"; $content .= "Runtime in Sec. : " . $t->length() . "\n"; $content .= "Num. of Chapters: " . $t->chapter_count() . "\n"; $content .= "\nAudio Tracks:\n"; # list of audio tracks for( my $i=0 ; $i<$t->audio_count() ; $i++ ) { my $a = $t->audio($i); $content .= " " . $a->langcode() . " " . $a->channel() . "ch " . $a->format() . " (" . $a->content() . ")\n"; } $content .= "\nSubtitles:\n"; # list of subtitles for( my $i=0 ; $i<$t->subtitle_count() ; $i++ ) { my $s = $t->subtitle($i); $content .= " " . $s->language() . " (" . $s->content() . ")\n"; } $self->{DATA}->configure(-state=>"normal"); # set textbox 'writable" $self->{DATA}->delete('1.0', 'end'); # empty textbox $self->{DATA}->insert('end', $content); # insert movie data for( my $i=0 ; $i<$t->audio_count() ; $i++ ) { # create audio check buttons my $index = $linenum . ".0"; my $idx = $i; $bla = $self->{DATA}->Checkbutton(-variable=>\$audio_select[$idx], -command=> sub{ if( $audio_select[$idx] ) { $self->{PROJECT}->audio_select($idx); } else { $self->{PROJECT}->audio_unselect($idx); } }); $self->{DATA}->windowCreate($index, -window=>$bla); # place checkbtn in data window my $a = $t->audio($i); $audio_select[$i] = 1; # preselect it $self->{PROJECT}->audio_select($i); $linenum++; } $linenum += 2; for( my $i=0 ; $i<$t->subtitle_count() ; $i++ ) { # create subtitle check buttons my $index = $linenum . ".0"; my $idx = $i; $bla = $self->{DATA}->Checkbutton(-variable=>\$subtitle_select[$idx], -command=> sub{ if( $subtitle_select[$idx] ) { $self->{PROJECT}->subtitle_select($idx); } else { $self->{PROJECT}->subtitle_unselect($idx); } }); $self->{DATA}->windowCreate($index, -window=>$bla); # place checkbtn in data window $subtitle_select[$idx] = 0; # unselect by default $self->{PROJECT}->subtitle_unselect($idx); $linenum++; } $self->{DATA}->insert('end', "\nChapter:\n"); # build preview pic list for( my $i=0 ; $i<$t->chapter_count() ; $i++ ) { # display chapter select buttons my $text = $i + 1; my $idx = $i; my $btn = $self->{DATA}->Checkbutton(-text=>$text, -variable=>\$chap_select[$idx], -command=> sub{ if( $chap_select[$idx] ) { $self->{PROJECT}->chapter_select($idx); } else { $self->{PROJECT}->chapter_unselect($idx); } $self->chapter_adjust(\@chap_select); }); $chap_select[$i] = 1; $self->{PROJECT}->chapter_select($i); # preselect all chapters $self->{DATA}->windowCreate('end', -window=>$btn); $self->{DATA}->insert('end', "\n"); } $self->{DATA}->configure(-state=>"disabled"); } sub chapter_adjust { # adjust chapter selection my $self = shift; # chapters are autom. selected to get a my ($refarr) = @_; # complete block, not some single chapters my $start; # 2,3,4,5,6 = ok 1,2,5,6 = not ok, 5,6 are deactivated autom. my $end; my $i = 0; while( not $refarr->[$i] ) { # scan for the first selected chapter $i++; last if($i > $#{$refarr}); } $start = $i; while( $refarr->[$i] ) { # scan until the next unselected chapter was found $i++; last if($i > $#{$refarr}); } $end = $i; while( $i <= $#{$refarr} ) { # unselect all following chapters $refarr->[$i] = 0; $self->{PROJECT}->chapter_unselect($i) if(defined $self->{PROJECT}->chapter_selected($i)); $i++; } } sub dvd_auto { # auto process all steps my $self = shift; my $ok = 1; if( ($ok) && ($self->{PROJECT}->state() == STATE_READ) ) { $ok = $self->dvd_build(); } if( ($ok) && ($self->{PROJECT}->state() == STATE_BUILD) ) { $ok = $self->dvd_burn(); } } sub dvd_burn { # create image or write dvd directly my $self = shift; my $p = $self->{PROJECT}; my $titlenum = $p->title(); my $titleidx = $titlenum; my $ok = 1; return 0 unless($self->{PROJECT}->state() == STATE_BUILD); $titlenum++; $self->disable_buttonbar(); if( $self->{CONFIG}->burn() ) { $self->status("Writing DVD ... "); if( main::dvd_burn($titleidx, \$self->{STATUS}) ) { $self->status("Writing DVD ... done"); } else { $self->status("Writing DVD ... failed"); $ok = 0; } } else { if( not main::create_dir($p->dir().DIR_ISO) ) { # check/create data subdir $self->status("ERROR: cannot create data directory"); $self->enable_buttonbar(); return 0; } $self->status("Creating ISO Image ... "); # create video dvd image if( main::dvd_image($titleidx, \$self->{STATUS}) ) { $self->status("Creating ISO Image ... done"); $self->{PROJECT}->state(STATE_BURN); } else { $self->status("Creating ISO Image ... failed"); $ok = 0; } } $self->enable_buttonbar(); return $ok; } sub dvd_build { # read video stream and build dvd structure my $self = shift; my $p = $self->{PROJECT}; my $titlenum = $p->title(); my $titleidx = $titlenum; my $ok = 1; return 0 unless($self->{PROJECT}->state() == STATE_READ); $titlenum++; $self->disable_buttonbar(); $self->status("Analyzing Selection ... "); if( not main::dvd_analyze($titleidx, \$self->{STATUS}) ) { $self->status("Analyzing Selection ... failed"); $ok = 0; } else { $self->status("Analyzing Selection ... done, using factor " . $p->factor() ); } if( $ok && (not main::create_dir($p->dir().DIR_DVD)) ) { # check/create data dir $self->status("ERROR: cannot create data directory"); $ok = 0; } $self->status("Recoding Movie ... ") if( $ok ); if( $ok && (not main::dvd_build($titleidx, \$self->{STATUS})) ) { $self->status("Recoding Movie ... failed"); $ok = 0; } else { $self->status("Recoding Movie ... done"); $self->{PROJECT}->state(STATE_BUILD); } $self->enable_buttonbar(); return $ok; } sub dvd_probe { # read dvd title my $self = shift; my $maintitle; $self->disable_buttonbar(); $self->status("reading dvd info ... "); $maintitle = main::dvd_probe(\$self->{STATUS}); if( $maintitle == -1 ) { $self->status("reading dvd info ... failed"); } else { $self->status("reading dvd info ... done"); $self->{PROJECT}->title($maintitle); for( my $i=0 ; $i<=$#{$self->{TITLE}} ; $i++ ) { $self->{LIST}->insert('end', "Title " . ($i+1)); } $self->{LIST}->selectionSet($maintitle,$maintitle); # auto select main titleset $self->{PROJECT}->state(STATE_READ); $self->show_details(); } $self->enable_buttonbar(); } sub new_project { # create new project my $self = shift; my ($name, $dir) = @_; $self->{PROJECT} = main::new_project($name, $dir); $self->{TITLE} = main::new_titlelist(); $self->{PROJECT}->state(STATE_NEW); } sub Exit { # leave program my $self = shift; my $Wmain = $self->{WIN_BASE}; $Wmain->destroy; } sub _init { # process startup stuff my $self = shift; my ($conf_ok) = @_; $self->_init_basewindow(); $self->_init_new_project_dlg(); $self->show_config() unless $conf_ok; } sub _init_basewindow { my $self = shift; my ($name) = @_; my $Wmain = new MainWindow; $Wmain->title(PROG_NAME . " v" . PROG_VERSION); my $FRMbutton = $Wmain->Frame(-borderwidth=>2)->pack(-side=>"top", -fill=>"x"); $self->{BUTTON}{EXIT} = $FRMbutton->Button(-text=>"Close",-command=>sub{ $Wmain->destroy;} )->pack(-side=>"left"); $self->{BUTTON}{READ} = $FRMbutton->Button(-text=>"Read DVD",-command=>sub{ $self->dvd_probe;} )->pack(-side=>"left"); $self->{BUTTON}{RECODE} = $FRMbutton->Button(-text=>"Build DVD",-command=>sub{ $self->dvd_build;} )->pack(-side=>"left"); $self->{BUTTON}{BURN} = $FRMbutton->Button(-text=>"Write DVD",-command=>sub{ $self->dvd_auto;} )->pack(-side=>"left"); $self->{BUTTON}{SETTING} = $FRMbutton->Button(-text=>"Settings", -command=>sub{ $self->show_config();} )->pack(-side=>"right"); $self->{BUTTON}{CALC} = $FRMbutton->Button(-text=>"Calc",-command=>sub{ $self->show_calc;} )->pack(-side=>"right"); my $FRMdata = $Wmain->Frame(-borderwidth=>2)->pack(-side=>"top", -expand=>1, -fill=>"both"); my $Titlelist = $FRMdata->Scrolled("Listbox", -scrollbars=>"se", -selectmode=>"single", -width=>25)->pack(-side=>"left", -fill=>"y", -expand=>1); $Titlelist->eventAdd('<>'=>''); $Titlelist->bind('Tk::Listbox', '<>' => sub{ $self->show_details;} ); $Titlelist->bind('<4>' => sub { $Titlelist->yview('scroll', -3, 'units') unless $Tk::strictMotif; }); $Titlelist->bind('<5>' => sub { $Titlelist->yview('scroll', +3, 'units') unless $Tk::strictMotif; }); $self->{LIST} = $Titlelist; my $Titledata = $FRMdata->Scrolled("Text", -scrollbars=>"se", -wrap=>"none", -state=>"disabled", -width=>60, -height=>25)->pack(-side=>"left", -fill=>"both", -expand =>1); $Titledata->bind('<4>' => sub { $Titledata->yview('scroll', -2, 'units') unless $Tk::strictMotif; }); $Titledata->bind('<5>' => sub { $Titledata->yview('scroll', +2, 'units') unless $Tk::strictMotif; }); $self->{DATA} = $Titledata; my $FRMstatus = $Wmain->Frame(-borderwidth=>2)->pack(-side=>"bottom", -expand=>1, -fill=>"x"); $FRMstatus->Entry(-textvariable => \$self->{STATUS}, -state=>"disabled")->pack(-side=>"left", -expand=>1, -fill=>"x"); $self->{WIN_BASE} = $Wmain; } sub _init_new_project_dlg { # dialog, create new project my $self = shift; my $name = PROJECT_NAME; my $path = $self->{CONFIG}->data_dir(); my $ok = 0; $self->disable_buttonbar(); my $w = $self->{WIN_BASE}->Toplevel(-title=>"New Project"); my $frm1 = $w->Frame(-borderwidth=>5)->pack(-side=>"top", -fill=>"x"); $frm1->Label(-text=>"Project Name: ")->pack(-side=>"left"); $frm1->Entry(-textvariable => \$name, -width=>20)->pack(-side=>"left", -fill=>"x"); my $frm2 = $w->Frame(-borderwidth=>5)->pack(-side=>"top", -fill=>"x"); $frm2->Label(-text=>"Save Dir.")->pack(-side=>"left"); $frm2->Entry(-textvariable => \$path, -width=>30)->pack(-side=>"left", -fill=>"x"); my $frm3 = $w->Frame(-borderwidth=>5)->pack(-side=>"top", -fill=>"x"); $frm3->Button(-text=>"OK", -command=> sub{ $ok=1; $w->destroy; } )->pack(-side=>"left"); $frm3->Button(-text=>"Cancel", -command=> sub{ $w->destroy; } )->pack(-side=>"left"); $self->status("New project settings"); $w->waitWindow(); if( $ok ) { $self->new_project($name, $path); $self->enable_buttonbar(); } else { $self->Exit(); } } 1;