mirror of https://github.com/odrling/Aegisub
Originally committed to SVN as r3205.
This commit is contained in:
parent
af5df13f00
commit
31657f94da
|
@ -1,62 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
use Aegisub; # don't forget this
|
||||
|
||||
$script_name = "Add/remove edgeblur macro (Perl version)";
|
||||
$script_description = "A demo macro showing how to do simple macros in Auto4-Perl";
|
||||
$script_author = "Karl Blomster";
|
||||
$script_version = "1";
|
||||
|
||||
|
||||
# this is a line-by-line translation of the Lua macro of the same name.
|
||||
sub add_edgeblur {
|
||||
# get the arguments; they are all references (important later)
|
||||
my ($subtitles, $selected_lines, $active_line) = @_;
|
||||
|
||||
# loop over the selected lines (note the dereferencing)
|
||||
foreach my $lineno ( @{$selected_lines} ) {
|
||||
# $line now contains a reference to the line we're working on.
|
||||
# Note that the "line" is actually a hash with the dialogue line fields as keys.
|
||||
my $line = $subtitles->[$lineno];
|
||||
# Tack on {\be1} to the start of the "text" field...
|
||||
$line->{"text"} = '{\\be1}' . $line->{"text"};
|
||||
# And write our $line back to the file.
|
||||
$subtitles->[$lineno] = $line;
|
||||
}
|
||||
|
||||
# This ain't implemented yet :(
|
||||
# Aegisub::Script::set_undo_point("Add edgeblur");
|
||||
}
|
||||
|
||||
|
||||
# This routine is NOT a Lua translation and may therefore seem more perlish. :>
|
||||
sub remove_edgeblur {
|
||||
# same as above
|
||||
my ($subtitles, $selected_lines, $active_line) = @_;
|
||||
|
||||
foreach my $lineno ( @{$selected_lines} ) {
|
||||
# Since we're only going to change the text field of the line,
|
||||
# why bother copying the entire line? We copy only the text field instead.
|
||||
# We could also do stuff directly on $subtitles->[$lineno]->{"text"} but
|
||||
# that's too long to write and is also risky if you blow something up.
|
||||
my $text = $subtitles->[$lineno]->{"text"};
|
||||
|
||||
# remove any \be1 tags contained in the first {} block
|
||||
$text =~ s!^\{(.*?)\\be1(.*?)\}!\{${1}${2}\}!;
|
||||
# if that leaves nothing in it, remove it
|
||||
$text =~ s!^\{\}!!;
|
||||
|
||||
# write back
|
||||
$subtitles->[$lineno]->{"text"} = $text;
|
||||
}
|
||||
|
||||
# Still not implemented :/
|
||||
# Aegisub::Script::set_undo_point("Remove edgeblur");
|
||||
}
|
||||
|
||||
|
||||
# Register macros with Aegisub
|
||||
Aegisub::Script::register_macro("Add edgeblur (Perl)", "Adds \\be1 tags to all selected lines", \&add_edgeblur);
|
||||
Aegisub::Script::register_macro("Remove edgeblur (Perl)", "Removes \\be1 tags from the start of all selected lines", \&remove_edgeblur);
|
|
@ -1,62 +0,0 @@
|
|||
load 'karaoke.rb'
|
||||
load 'utils.rb'
|
||||
|
||||
include Aegisub
|
||||
|
||||
$script_name = "simple k-replacer"
|
||||
$script_description = "k-replacer test"
|
||||
$script_author = "Pomyk"
|
||||
$script_version = "1"
|
||||
|
||||
register_macro("Simple k-replacer", "k-replacer macro", :k_replace_macro, nil)
|
||||
register_filter("Simple k-replacer", "k-replacer filter", 100, :k_replace_filter, :k_replace_cfg)
|
||||
|
||||
|
||||
def k_replace_macro(subs, sel, act)
|
||||
|
||||
cfg = k_replace_cfg(subs, nil)
|
||||
ok, opt = display_dialog(cfg, nil)
|
||||
return if not ok # cancelled
|
||||
|
||||
write_options(subs, {$script_name => opt})
|
||||
subs.each do |l|
|
||||
k_replace(l, opt[:templ], opt[:strip]) if l[:class] == :dialogue && # replace if its dialogue
|
||||
(opt[:style] =="" || l[:style] == opt[:style]) # and has the right style
|
||||
end
|
||||
return subs
|
||||
end
|
||||
|
||||
def k_replace_filter(subs, opt)
|
||||
subs.each do |l|
|
||||
k_replace(l, opt[:templ], opt[:strip]) if l[:class] == :dialogue && # replace if its dialogue
|
||||
opt[:style] =="" || l[:style] == opt[:style] # and has the right style
|
||||
end
|
||||
return subs
|
||||
end
|
||||
|
||||
def k_replace_cfg(subs, opt)
|
||||
styles = []
|
||||
subs.each { |l| # read style names
|
||||
styles << l[:name] if l[:class] == :style
|
||||
break if l[:class] == :dialogue
|
||||
}
|
||||
header_text = <<-head
|
||||
Expressions are enclosed in % pairs.
|
||||
Variables:
|
||||
$start = Start-time of syllable (ms)
|
||||
$end = End-time of syllable (ms)
|
||||
$mid = Time midways through the syllable (ms)
|
||||
$dur = Duration of syllable (cs)
|
||||
Calculation example:
|
||||
\\t($start,%$start+$dur*2%,\\fscx110)
|
||||
\\t(%$start+$dur*2%,$end,\\fscx90)
|
||||
head
|
||||
opt ||= {}
|
||||
cfg = ScriptCfg.new # helper class for building dialogs
|
||||
cfg.header header_text, :x => 1, :width => 1
|
||||
cfg.edit :templ, "template", :text => opt[:templ]
|
||||
cfg.dropdown :style, "Style", :items => styles, :value => opt[:style]
|
||||
cfg.checkbox :strip, "", :label => "Strip tags?", :value => (opt[:strip] == "true" ? true : false)
|
||||
cfg.to_ary # convert to array
|
||||
end
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# Perl console script
|
||||
# by Simone Cociancich
|
||||
# This script simply call the registration function for the builtin perl console
|
||||
# the perl console is chiefly intended as a development and debug tool
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
Aegisub::Script::set_info(
|
||||
'Perl console',
|
||||
"\nThis script provides a console for messing with the perl engine \\^^/
|
||||
(if you break something don't complain >:)",
|
||||
'ShB');
|
||||
|
||||
use Aegisub::PerlConsole;
|
||||
register_console();
|
|
@ -1,41 +0,0 @@
|
|||
package Aegisub;
|
||||
use Exporter 'import';
|
||||
|
||||
@EXPORT = qw( text_extents
|
||||
log_fatal log_error log_warning log_hint log_debug log_trace log_message );
|
||||
@EXPORT_OK = qw( LOG_FATAL LOG_ERROR LOG_WARNING LOG_HINT LOG_DEBUG LOG_TRACE LOG_MESSAGE
|
||||
LOG_WX
|
||||
log warn );
|
||||
|
||||
# Constants
|
||||
sub LOG_FATAL { 0 }
|
||||
sub LOG_ERROR { 1 }
|
||||
sub LOG_WARNING { 2 }
|
||||
sub LOG_HINT { 3 }
|
||||
sub LOG_DEBUG { 4 }
|
||||
sub LOG_TRACE { 5 }
|
||||
sub LOG_MESSAGE { 6 }
|
||||
|
||||
sub LOG_WX { 8 }
|
||||
|
||||
# Shortcut functions
|
||||
sub log_fatal { Aegisub::log LOG_FATAL, @_; }
|
||||
sub log_error { Aegisub::log LOG_ERROR, @_; }
|
||||
sub log_warning { Aegisub::log LOG_WARNING, @_; }
|
||||
sub log_hint { Aegisub::log LOG_HINT, @_; }
|
||||
sub log_debug { Aegisub::log LOG_DEBUG, @_; }
|
||||
sub log_trace { Aegisub::log LOG_TRACE, @_; }
|
||||
sub log_message { Aegisub::log LOG_MESSAGE, @_; }
|
||||
|
||||
# wxLog variety
|
||||
sub wxlog {
|
||||
if($_[0] =~ /^\d+$/) {
|
||||
$_[0] |= 0x8;
|
||||
}
|
||||
else {
|
||||
unshift @_, LOG_MESSAGE | LOG_WX;
|
||||
}
|
||||
Aegisub::log @_;
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,6 +0,0 @@
|
|||
package Aegisub::PerlConsole;
|
||||
use Exporter 'import';
|
||||
|
||||
@EXPORT = qw( echo register_console );
|
||||
|
||||
1;
|
|
@ -1,11 +0,0 @@
|
|||
package Aegisub::Progress;
|
||||
use Exporter 'import';
|
||||
|
||||
@EXPORT = qw( set_progress set_task set_title is_cancelled );
|
||||
@EXPORT_OK = qw( set task title );
|
||||
|
||||
sub set { set_progress @_ }
|
||||
sub task { set_task @_ }
|
||||
sub title { set_title @_ }
|
||||
|
||||
1;
|
|
@ -1,6 +0,0 @@
|
|||
package Aegisub::Script;
|
||||
use Exporter 'import';
|
||||
|
||||
@EXPORT = qw( register_macro set_info );
|
||||
|
||||
1;
|
|
@ -1,251 +0,0 @@
|
|||
#/usr/bin/perl
|
||||
|
||||
#########
|
||||
#
|
||||
# Written by Karl Blomster (TheFluff) 2008.
|
||||
# (OK, mostly just a translation of utils-auto4.lua.)
|
||||
#
|
||||
# This script is hereby given into the public domain.
|
||||
# If that is not possible according to local laws, I, the author, hereby grant
|
||||
# anyone the right to use this script for any purpose.
|
||||
#
|
||||
#########
|
||||
|
||||
package Auto4Utils;
|
||||
require Exporter;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
use feature ":5.10";
|
||||
use utf8; # just to be safe
|
||||
use POSIX (); # gah, we only need floor(), no need to import all of IEEE 1003.1
|
||||
|
||||
|
||||
# Export everything by default
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(extract_color alpha_from_style color_from_style HSV_to_RGB HSL_to_RGB interpolate_color interpolate_alpha
|
||||
ass_color ass_alpha ass_style_color string_trim clamp interpolate);
|
||||
|
||||
|
||||
# Given 3 integers R,G,B, returns ASS formatted &HBBGGRR& string
|
||||
sub ass_color {
|
||||
my ($r, $g, $b) = @_;
|
||||
return(sprintf("&H%02X%02X%02X&", $b, $g, $r));
|
||||
}
|
||||
# Perlier version of that:
|
||||
# sub ass_color { return sprintf "&H%02X%02X%02X&", reverse }
|
||||
# I don't think reverse reverses @_ by default, rats :(
|
||||
|
||||
# Convert decimal alpha value to &H00& form
|
||||
sub ass_alpha {
|
||||
return(sprintf("&H%02X&", shift(@_)));
|
||||
}
|
||||
|
||||
# Given 4 integers R,G,B,A, returns a v4+ formatted style color string
|
||||
# (note no terminating &)
|
||||
sub ass_style_color {
|
||||
my ($r, $g, $b, $a) = @_;
|
||||
return(sprintf("&H%02X%02X%02X%02X", $a, $b, $g, $r));
|
||||
}
|
||||
|
||||
# Tries its best to convert a string to 4 integers R,G,B,A.
|
||||
# Returns them in that order if it succeeds, or undef if it can't do it.
|
||||
# Useless in scalar context.
|
||||
sub extract_color {
|
||||
my $string = shift(@_);
|
||||
|
||||
# This here thingie is a switch statement. Magic!
|
||||
given ( $string ) {
|
||||
# try v4+ style (ABGR)
|
||||
when ( /\&H([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})/i ) {
|
||||
return(hex($4), hex($3), hex($2), hex($1));
|
||||
}
|
||||
# color override? (BGR)
|
||||
when ( /\&H([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})\&H/i ) {
|
||||
return(0, hex($3), hex($2), hex($1));
|
||||
}
|
||||
# alpha override? (A)
|
||||
# (bug: bogus results with \c&H<hex>& with the first four zeros omitted)
|
||||
when ( /\&H([[:xdigit:]]{2})\&/i ) {
|
||||
return(hex($1), 0, 0, 0);
|
||||
}
|
||||
# try HTML format for laffs (RGB)
|
||||
when ( /\#([[:xdigit:]]{2})([[:xdigit:]]{2})?([[:xdigit:]]{2})?/i ) {
|
||||
return(0, (hex($2) or 0), (hex($2) or 0), (hex($3) or 0));
|
||||
}
|
||||
default {
|
||||
return(undef, undef, undef, undef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Given a a style color string, returns the alpha part formatted as override
|
||||
sub alpha_from_style {
|
||||
my $color_string = shift(@_);
|
||||
my ($r, $g, $b, $a) = extract_color($color_string);
|
||||
return(ass_alpha($a or 0));
|
||||
}
|
||||
|
||||
# Given a style color string, returns the color part formatted as override
|
||||
sub color_from_style {
|
||||
my $color_string = shift(@_);
|
||||
my ($r, $g, $b, $a) = extract_color($color_string);
|
||||
return(ass_color(($r or 0), ($g or 0), ($b or 0)));
|
||||
}
|
||||
|
||||
|
||||
# Converts 3 integers H, S, V (hue, saturation, value) to R, G, B
|
||||
sub HSV_to_RGB {
|
||||
my ($H, $S, $V) = @_;
|
||||
my ($r, $g, $b);
|
||||
|
||||
# saturation is zero, make grey
|
||||
if ($S == 0) {
|
||||
$r = $V * 255;
|
||||
$r = clamp($r, 0, 255);
|
||||
($g, $b) = ($r, $r);
|
||||
}
|
||||
# else calculate color
|
||||
else {
|
||||
# calculate subvalues
|
||||
$H = $H % 360; # put $h in range [0,360]
|
||||
my $Hi = POSIX::floor($H/60);
|
||||
my $f = $H/60 - $Hi;
|
||||
my $p = $V * (1 - $S);
|
||||
my $q = $V * (1 - $f * $S);
|
||||
my $t = $V * (1 - (1 - $f) * $S);
|
||||
|
||||
# do math based on hue index
|
||||
if ($Hi == 0) { $r = $V*255; $g = $t*255; $b = $p*255; }
|
||||
elsif ($Hi == 1) { $r = $q*255; $g = $V*255; $b = $p*255; }
|
||||
elsif ($Hi == 2) { $r = $p*255; $g = $V*255; $b = $t*255; }
|
||||
elsif ($Hi == 3) { $r = $p*255; $g = $q*255; $b = $V*255; }
|
||||
elsif ($Hi == 4) { $r = $t*255; $g = $p*255; $b = $V*255; }
|
||||
elsif ($Hi == 5) { $r = $V*255; $g = $p*255; $b = $q*255; }
|
||||
# TODO: replace this with Aegisub::Script::debug_out() or whatever it is
|
||||
else { warn("HSV_to_RGB: Hi got an unexpected value: $Hi"); }
|
||||
}
|
||||
|
||||
$r = POSIX::floor($r);
|
||||
$g = POSIX::floor($g);
|
||||
$b = POSIX::floor($b);
|
||||
return($r, $g, $b);
|
||||
}
|
||||
|
||||
|
||||
# Converts 3 integers H, S, L (hue, saturation, luminance) to R, G, B
|
||||
# NOTE: THE OUTPUT AND S,V INPUT IS IN THE RANGE [0,1]!
|
||||
# Routine is best performed to "The HSL Song" by Diablo-D3 and the #darkhold idlers.
|
||||
# The lyrics are as follows:
|
||||
# I see a little silluetto of a man
|
||||
# It's in color, its in color, can you convert to HSL?
|
||||
# Cyan, yellow and magenta, very very outdated now
|
||||
# Alvy Smith, Alvy Smith, Alvy Smith, Alvy Smith, Fiigaarrooo
|
||||
# I'm just a poor boy, stuck with RGB
|
||||
# (He's just a poor boy, from a poor colorspace, spare him his eyes from this monstrosity)
|
||||
#
|
||||
# Easy come, easy go, will you let me HSL?
|
||||
# (No! We will not let you HSL!)
|
||||
# Let him HSL!
|
||||
# (No! We will not let you HSL!)
|
||||
# Let him HSL!
|
||||
# (No! We will not let you HSL!)
|
||||
# Let me HSL!
|
||||
# (Will not HSL!)
|
||||
# Let me HSL!
|
||||
# (Will not HSL!)
|
||||
# Let me HSL! Let me HSL!
|
||||
# (Never never never never never!)
|
||||
# Let me HHHHHSSSSSLLLLL!
|
||||
# (No no no no no no no!)
|
||||
#
|
||||
# [70's rock/bad humour segment ends here. We now return to your regularily scheduled Perl hacking...]
|
||||
sub HSL_to_RGB {
|
||||
my ($H, $S, $L) = @_;
|
||||
my ($r, $g, $b, $Q);
|
||||
|
||||
# make sure input is in range
|
||||
$H = $H % 360;
|
||||
$S = clamp($S, 0, 1);
|
||||
$L = clamp($L, 0, 1);
|
||||
|
||||
# simple case if saturation is 0, all grey
|
||||
if ($S == 0) {
|
||||
($r, $g, $b) = ($L, $L, $L);
|
||||
}
|
||||
# more common case, saturated color
|
||||
else {
|
||||
if ($L < 0.5) { $Q = $L * (1 + $S); }
|
||||
else { $Q = $L + $S - ($L * $S); }
|
||||
|
||||
my $P = 2 * $L - $Q;
|
||||
my $Hk = $H / 360;
|
||||
|
||||
my ($Tr, $Tg, $Tb);
|
||||
$Tg = $Hk;
|
||||
if ($Hk < 1/3) { $Tr = $Hk + 1/3; $Tb = $Hk + 2/3; }
|
||||
elsif ($Hk > 2/3) { $Tr = $Hk - 2/3; $Tb = $Hk - 1/3; }
|
||||
else { $Tr = $Hk + 1/3; $Tb = $Hk - 1/3; }
|
||||
|
||||
# anonymous subroutine required for closure reasons
|
||||
my $get_component = sub {
|
||||
my $T = shift(@_);
|
||||
if ($T < 1/6) { return($P + (($Q - $P) * 6 * $T)) }
|
||||
elsif (1/6 <= $T and $T < 1/2) { return($Q) }
|
||||
elsif (1/2 <= $T and $T < 2/3) { return($P + (($Q - $P) * (2/3 - $T) * 6)) }
|
||||
else { return($P) }
|
||||
};
|
||||
|
||||
$r = $get_component->($Tr);
|
||||
$g = $get_component->($Tg);
|
||||
$b = $get_component->($Tb);
|
||||
}
|
||||
|
||||
return($r, $g, $b);
|
||||
}
|
||||
|
||||
|
||||
# Removes whitespace at the start and end of a string
|
||||
# (will anyone ever use this in a perl program?)
|
||||
sub string_trim {
|
||||
my $string = shift(@_);
|
||||
$string =~ s!^\s*(.+?)\s*$!$1!;
|
||||
return($string);
|
||||
}
|
||||
|
||||
|
||||
# Clamp a numeric value to a range
|
||||
sub clamp {
|
||||
my ($val, $min, $max) = @_;
|
||||
if ($val < $min) { return($min) }
|
||||
elsif ($val > $max) { return($max) }
|
||||
else { return($val) }
|
||||
}
|
||||
|
||||
# interpolate between two numbers
|
||||
sub interpolate {
|
||||
my ($pct, $min, $max) = @_;
|
||||
if ($pct <= 0) { return($min) }
|
||||
elsif ($pct >= 1) { return($max) }
|
||||
else { return($pct * ($max - $min) + $min) }
|
||||
}
|
||||
|
||||
# interpolate between two color values (given as &HBBGGRR strings)
|
||||
# returns string formatted with \c&H override format
|
||||
sub interpolate_color {
|
||||
my ($pct, $start, $end) = @_;
|
||||
my ($r1, $g1, $b1) = extract_color($start);
|
||||
my ($r2, $g2, $b2) = extract_color($end);
|
||||
my ($r, $g, $b) =
|
||||
(interpolate($pct, $r1, $r2), interpolate($pct, $g1, $g2), interpolate($pct, $b1, $b2));
|
||||
return(ass_color($r, $g, $b));
|
||||
}
|
||||
|
||||
# interpolate between two alpha values (given as either override or part of style color strings)
|
||||
# returns string formatted with \c&H override format
|
||||
sub interpolate_alpha {
|
||||
my ($pct, $start, $end) = @_;
|
||||
my ($r1, $g1, $b1, $a1) = extract_color($start);
|
||||
my ($r2, $g2, $b2, $a2) = extract_color($end);
|
||||
return(ass_alpha(interpolate($pct, $a1, $a2)));
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
module Aegisub
|
||||
|
||||
|
||||
# parsing karaoke line
|
||||
# should work more or less like the lua version
|
||||
# input: dialogue line with karaoke tags
|
||||
# output: number of syllables in karaoke
|
||||
def parse_karaoke(line)
|
||||
return 0 if line[:class] != :dialogue
|
||||
return line[:karaoke].size if line[:karaoke].class == Array
|
||||
karaoke = []
|
||||
time = 0
|
||||
line[:text].scan(/(?:{.*?\\(K|k[fto]?)(\d+).*?}([^{]*))|({.*?})([^{]*)/) do |k|
|
||||
if $1 # karaoke tag
|
||||
ktag = $1
|
||||
kdur = $2.to_i
|
||||
syl = Hash.new
|
||||
syl[:start_time] = time
|
||||
if ktag == 'kt'
|
||||
time = kdur*10
|
||||
syl[:duration] = 0
|
||||
else
|
||||
time += kdur*10
|
||||
syl[:duration] = kdur
|
||||
end
|
||||
syl[:end_time] = time
|
||||
syl[:tag] = ktag
|
||||
syl[:text] = $&
|
||||
syl[:text_stripped] = $3
|
||||
karaoke << syl
|
||||
else # no karaoke - append to the last syllable
|
||||
tag = $4
|
||||
text = $5
|
||||
if not karaoke.empty?
|
||||
karaoke.last[:text] << tag << text
|
||||
karaoke.last[:text_stripped] << text if text and tag !~ /\\p\d/ # no drawings
|
||||
end
|
||||
end
|
||||
end
|
||||
line[:karaoke] = karaoke
|
||||
return karaoke.size
|
||||
end
|
||||
|
||||
# replaces matched pattern in the line with an evaluated template
|
||||
# input: line, template (string), strip (bool), pattern (regexp or string)
|
||||
# output: line with karaoke effect
|
||||
def k_replace(line, template, strip, pattern = /\\(:?K|k[fo]?\d+)/) # default pattern = any karaoke tag
|
||||
return if parse_karaoke(line) == 0
|
||||
|
||||
res = ""
|
||||
t = template.gsub(/\$(start|end|dur|mid|text|i|kind)/, '_\1')
|
||||
_i = 0
|
||||
line[:karaoke].each do |s|
|
||||
_start = s[:start_time]
|
||||
_end = s[:end_time]
|
||||
_dur = s[:duration]
|
||||
_mid = _start + _dur*5
|
||||
_text = s[:text_stripped]
|
||||
_kind = s[:tag]
|
||||
ev = t.gsub(/(_(:?start|end|dur|mid|text|i|kind))/) { |m| eval($1).to_s } # evalute variables
|
||||
ev.gsub!(/\%([^%]+)\%/) { |m| eval($1).to_s } # evaluate expressions
|
||||
res << (strip ? "{" << ev << "}" << s[:text_stripped] : s[:text].gsub!(pattern, ev) )
|
||||
_i += 1
|
||||
end
|
||||
line[:text] = res
|
||||
end
|
||||
|
||||
end
|
|
@ -1,114 +0,0 @@
|
|||
#include Aegisub
|
||||
|
||||
class Object
|
||||
def deep_clone
|
||||
Marshal.load(Marshal.dump(self))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module Aegisub
|
||||
|
||||
class ScriptCfg
|
||||
def initialize # constructor
|
||||
@opt = []
|
||||
@x = 0
|
||||
@y = 0
|
||||
@labels = true
|
||||
@width = 1 # TODO
|
||||
@height = 1
|
||||
end
|
||||
|
||||
private
|
||||
def control(type, name, opt = {})
|
||||
@opt << {:class => type, :name => name, :x => @x, :y => @y,
|
||||
:width => 1, :height => 1}.merge!(opt)
|
||||
end
|
||||
|
||||
# some meta-programming :]
|
||||
def self.create_functions(*arr)
|
||||
arr.each do |a|
|
||||
class_eval(%Q[
|
||||
def #{a.to_s}(name, text, opt = {})
|
||||
if @labels; label text, opt; @x += 1; end
|
||||
control "#{a.to_s}", name, opt
|
||||
@y += 1
|
||||
@x = 0
|
||||
end
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
create_functions *[:edit, :intedit, :floatedit, :textbox,
|
||||
:dropdown, :checkbox, :color, :coloralpha, :alpha ]
|
||||
|
||||
def no_labels; @labels = false; end
|
||||
|
||||
def label(text, opt = {})
|
||||
control :label, text, opt.merge({:label => text})
|
||||
end
|
||||
|
||||
def header(text, opt = {})
|
||||
label text, opt.merge!({:width => 2})
|
||||
@y += 1
|
||||
end
|
||||
|
||||
def to_ary # conversion to array
|
||||
@opt
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# inserts lines with options into [Script Info] section
|
||||
def write_options(subs, opt, sep = "~~")
|
||||
subs.collect! do |l|
|
||||
if l[:class] == :info
|
||||
info = true
|
||||
value = opt.delete(l[:key])
|
||||
l[:value] = value.instance_of?(Hash) ? value.to_a.flatten!.join(sep) : value.to_s if value
|
||||
l
|
||||
elsif info
|
||||
r = [l]
|
||||
opt.each do |key, val|
|
||||
r << {:class => :info, :key => key,
|
||||
:value => value.instance_of?(Hash) ? value.to_a.flatten!.join(sep) : value.to_s,
|
||||
:section => "[Script Info]"}
|
||||
end
|
||||
info = false
|
||||
r
|
||||
else
|
||||
l
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# returns a hash with options from [Script Info] section
|
||||
def read_options(subs, name, sep = "~~")
|
||||
opt = {}
|
||||
subs.each { |l| opt[l[:key].to_sym] = l[:value] if l[:class] == :info }
|
||||
n_sym = name.to_sym
|
||||
if opt[n_sym] # parsing of script specific options
|
||||
a = opt[n_sym].split(sep)
|
||||
h = {}
|
||||
(a.size/2).times { |j| h[a[2*j].to_sym] = a[2*j+1] }
|
||||
opt[n_sym] = h
|
||||
end
|
||||
return opt
|
||||
end
|
||||
|
||||
def rgb_to_ssa(*c)
|
||||
res = "&H"
|
||||
c.reverse_each {|v| res << "%02X" % v}
|
||||
res << "&"
|
||||
return res
|
||||
end
|
||||
|
||||
def ssa_to_rgb(col)
|
||||
res = []
|
||||
col.scan(/[0-9a-fA-F]{2}/) { res.unshift $1.to_i(16) }
|
||||
res
|
||||
end
|
||||
end
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
------------------------------------
|
||||
Quick reference on Perl engine's API
|
||||
------------------------------------
|
||||
|
||||
All the packages that form the perl interface to Aegisub are automatically
|
||||
loaded, however none of their symbols are exported initially. If you want to
|
||||
import them you can use the usual 'use' mechanism; if you call it without a
|
||||
list of imports it will import more or less everything in your script's
|
||||
package. Wether they are exported or not is indicated in the following
|
||||
reference by <--EXPORTED--> (exported by default within a plain 'use'
|
||||
statement) and <--EXPORTABLE--> (can be imported specifying them explicitely in
|
||||
the 'use' statement) tags. Finally, <--NOT EXPORTABLE--> indicates symbols that
|
||||
can't be exported through 'use'.
|
||||
|
||||
|
||||
====================================
|
||||
package Aegisub
|
||||
|
||||
------------------------------------
|
||||
Constants defined:
|
||||
<--EXPORTABLE-->
|
||||
|
||||
LOG_FATAL == 0
|
||||
LOG_ERROR == 1
|
||||
LOG_WARNING == 2
|
||||
LOG_HINT == 3
|
||||
LOG_DEBUG == 4
|
||||
LOG_TRACE == 5
|
||||
LOG_MESSAGE == 6
|
||||
Log levels, to be used with the 'log' function.
|
||||
|
||||
LOG_WX == 8
|
||||
Flag to force logging through wxWidgets facilites.
|
||||
|
||||
------------------------------------
|
||||
Subroutines defined:
|
||||
<--EXPORTED-->
|
||||
|
||||
text_extents STYLE, TEXT
|
||||
Computes the metric for a string of text, based on a specific style.
|
||||
Arguments:
|
||||
STYLE The style to use, as a string or ref to a style line.
|
||||
TEXT Text for which to compute the metrics.
|
||||
Returns:
|
||||
WIDTH The width of the text (if called in scalar context, only this is returned).
|
||||
ASCENT The ascent, i.e. the distance from the baseline to the top of the letters.
|
||||
DESCENT Descent, i.e. the distance from the baseline to the bottom.
|
||||
EXTLEADING External leading, i.e. the distance between to lines of text.
|
||||
|
||||
log_fatal LIST
|
||||
...
|
||||
log_message LIST
|
||||
These are shortcuts for 'log(LOG_FATAL, LIST)' to 'log(LOG_MESSAGE, LIST)'
|
||||
(see below).
|
||||
|
||||
<--EXPORTABLE-->
|
||||
|
||||
log LEVEL, LIST
|
||||
log LIST
|
||||
Prints a log message inside the progress window, if LEVEL is less or equal
|
||||
to the tracelevel set inside automation options. If called from outside a
|
||||
callback (i.e. during script loading) prints through the wxWidgets logging
|
||||
mechanism. 'log(LIST)' is equal to 'log(Aegisub::LOG_MESSAGE, LIST)'. The
|
||||
short form is used whenever there are at least two arguments and the first
|
||||
one cannot be read as an integer; it is always used when given only one
|
||||
argument. This function is not exported by default (review man perlfunc to
|
||||
understand why :).
|
||||
|
||||
Arguments:
|
||||
LEVEL The debug level, may be one of the following (the descriptions are
|
||||
indicative):
|
||||
0 Fatal error, for vital errors;
|
||||
1 Error, for serious but not too much threatening errors;
|
||||
2 Warning, for something that's apparently going wrong;
|
||||
3 Hint, for indicating something peculiar is happening;
|
||||
4 Debug, for debugging!
|
||||
5 Trace, for really verbose debugging;
|
||||
6 Message, always printed.
|
||||
If you OR one of these values with the flag LOG_WX the log message will
|
||||
be delivered though wxWidgets regardless of wether there is a progress
|
||||
window displayed (you won't normally need this feature, though).
|
||||
LIST List of arguments to print.
|
||||
|
||||
warn LIST
|
||||
Prints a warning through the GUI log facilities (it is equivalent to
|
||||
'log(Aegisub::LOG_WARNING, LIST)'). It is automatically hooked to the
|
||||
global 'warn' function during script execution, thus it is not exported by
|
||||
default.
|
||||
Arguments:
|
||||
LIST List of arguments to print.
|
||||
|
||||
<--NOT EXPORTABLE-->
|
||||
|
||||
wxlog LEVEL, LIST
|
||||
wxlog LIST
|
||||
Similar to 'log', but with the LOG_WX flag implicitely set. This function
|
||||
is top-secret.
|
||||
|
||||
|
||||
====================================
|
||||
package Aegisub::PerlConsole
|
||||
------------------------------------
|
||||
This package contains the perl console, a debug tool not intended for normal
|
||||
use by normal users (it's not even enabled in release builds). They are shown
|
||||
here for completeness.
|
||||
|
||||
------------------------------------
|
||||
Subroutines defined:
|
||||
<--EXPORTED-->
|
||||
|
||||
echo LIST
|
||||
Prints a list of arguments on the console, or on STDOUT if no console is
|
||||
registered, a trailing \n is printed too.
|
||||
Arguments:
|
||||
LIST List of arguments to print.
|
||||
|
||||
register_console NAME, DESC
|
||||
Registers an instance of the console, as a macro. You don't want to know
|
||||
any more because in fact you'll never have to do with this. >:)
|
||||
Arguments:
|
||||
NAME Set the name for the macro. (optional)
|
||||
DESC Set the macro's description. (optional)
|
||||
|
||||
|
||||
====================================
|
||||
package Aegisub::Progress
|
||||
------------------------------------
|
||||
This package provides an interface to the progress window automatically showed
|
||||
during the execution of a feature. Its functions are somewhat different to
|
||||
those available in lua because of clarity, however aliases are given.
|
||||
|
||||
------------------------------------
|
||||
Subroutines defined:
|
||||
<--EXPORTED-->
|
||||
|
||||
set_progress VALUE
|
||||
Sets the value of the progress bar. It accepts values comprised in [0, 1]
|
||||
OR (1, 100] (for instance, a value of 0.86 is equivalent to a value of 86:
|
||||
they both represent '86%'). You should really always use values in the
|
||||
range [0, 1] if you don't wanna be mocked by your friends and relatives
|
||||
(and normally they're more immediately computable).
|
||||
Arguments:
|
||||
VALUE The value for the progress bar.
|
||||
|
||||
set_task DESC
|
||||
Sets the description for the current task inside progress window (just
|
||||
below the progress bar).
|
||||
Arguments:
|
||||
DESC The description for the current task.
|
||||
|
||||
set_title TITLE
|
||||
Sets the title for the progress window (which is not actually the window's
|
||||
title, but a flashier label below it). The default title is 'Executing ...'
|
||||
(with the ellpsis possibly replaced by the feature's name).
|
||||
Arguments:
|
||||
TITLE The title to set.
|
||||
|
||||
is_cancelled
|
||||
Returns: A boolean indicating wether the cancel button on the progress
|
||||
window where pressed in the near past.
|
||||
|
||||
<--EXPORTABLE-->
|
||||
|
||||
set VALUE
|
||||
Synonym for 'set_progress(VALUE)'.
|
||||
|
||||
task DESC
|
||||
Synonym for 'set_desc(DESC)',
|
||||
|
||||
title TITLE
|
||||
Synonym for 'set_title(TITLE)'.
|
||||
|
||||
|
||||
====================================
|
||||
package Aegisub::Script
|
||||
|
||||
------------------------------------
|
||||
Subroutines defined:
|
||||
<--EXPORTED-->
|
||||
|
||||
register_macro NAME, DESC, PROC_SUB, VAL_SUB
|
||||
Register a new macro.
|
||||
Arguments:
|
||||
NAME The name of the macro.
|
||||
DESC A description for the macro.
|
||||
PROC_SUB A ref to a subroutine to be used as the macro processing function
|
||||
(see the callbacks section). Please, really use a reference and not
|
||||
just the name of the sub, because of the script 'pacakging' described
|
||||
below.
|
||||
VAL_SUB A ref to a subroutine to be used as the macro validation function
|
||||
(see callbacks)(optional, if not defined will be considered as always true).
|
||||
|
||||
set_info NAME, DESC, AUTHOR, VERSION
|
||||
You can set all of the script's info values with a call to this
|
||||
function. (Otherwise you can set the corresponding predefined script
|
||||
variables individually.)
|
||||
Arguments: see the parts about script variables, anything is optional.
|
||||
|
||||
|
||||
====================================
|
||||
package Aegisub::Script::pxxxxxxxx
|
||||
------------------------------------
|
||||
Every script that's loaded gets its code evaluated inside a different package -
|
||||
whose name is chosen at 'random' - whereas the perl interpreter is unique, so
|
||||
all the scripts see the same global package, and can possibly access other
|
||||
scripts' packages. Therefore is recommended to ALWAYS declare all of the
|
||||
script's local variables with 'my', and of course to 'use strict' to check on
|
||||
this. You can still declare another package for your script; the script's
|
||||
predefined variables should be still visible from it without any change in the
|
||||
code (they're declared as 'our'), however this is discouraged.
|
||||
|
||||
------------------------------------
|
||||
Variables defined:
|
||||
|
||||
$script_author
|
||||
Holds the script author's name. Default is the user executing aegisub.
|
||||
|
||||
$script_description
|
||||
Holds a description for the script. Default is 'Perl script'.
|
||||
|
||||
$script_name
|
||||
Holds the script's name. Default is the script's filename.
|
||||
|
||||
$script_version
|
||||
Holds the script's version. Default is current aegisub version.
|
||||
|
||||
$_script_path
|
||||
The full path to the script's file. Any change to this variable is ignored
|
||||
and overwritten.
|
||||
|
||||
$_script_package
|
||||
The full script package as a string. Any change to this variable is
|
||||
currently ignored and overwritten, and may be so forever.
|
||||
|
||||
|
||||
------------------------------------
|
||||
Callbacks definable:
|
||||
|
||||
macro_processing_function LINES, SELECTED, ACTIVE
|
||||
A function to be used as a callback for Aegisub::Script::register_macro().
|
||||
This function will be called when the user selects the corresponding macro
|
||||
in the Automation menu. The first two arguments can be modified, and the
|
||||
modifications will be reflected in the subtitles file.
|
||||
Arguments:
|
||||
LINES A reference to the list containing the subtitle file lines.
|
||||
Each element of the list is a reference to a hash that represents a
|
||||
single subtitle line. For the hash keys refer to lua documentation,
|
||||
they are basically the same.
|
||||
Example:
|
||||
|
||||
my $lines = $_[0]; # DON'T shift @_ (unless you reconstruct it
|
||||
# afterwards) or you'll break everything and
|
||||
# your hard disk be erased >:)
|
||||
# The first selected line's index
|
||||
my $first = $_[1][0];
|
||||
# An entire line
|
||||
my $l = $lines->[$first];
|
||||
# The text field of a dialogue line
|
||||
my $text = $lines->[$first]->{"text"};
|
||||
|
||||
SELECTED A ref to an array of ints, showing the currently selected
|
||||
lines in the file.
|
||||
ACTIVE Index of the currently active line in the subtitle file (sic).
|
||||
|
||||
macro_validation_function LINES, SELECTED, ACTIVE
|
||||
A function to be used as a callback for Aegisub::Script::register_macro().
|
||||
This function will be called whenever the Automation menu is opened to
|
||||
decide what macros are applicable to the current script.
|
||||
Arguments: same as macro_processing_function; however any change to the
|
||||
first two ones will be ignored upon function return.
|
||||
Returns:
|
||||
VALID A 'boolean' value to indicate if the macro is applicable to this
|
||||
particular subtitles file.
|
|
@ -563,62 +563,6 @@
|
|||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Ruby"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_ruby.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_ruby.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_ruby_assfile.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_ruby_dialog.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Perl"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl_ass.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl_console.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl_console.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl_dialogs.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perl_script.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\auto4_perldata.inc"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Wrappers"
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioPropertySheet
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="delayload_perl_32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
DelayLoadDLLs="perl510.dll"
|
||||
/>
|
||||
</VisualStudioPropertySheet>
|
|
@ -1,598 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_PERL
|
||||
|
||||
|
||||
#include "auto4_perl.h"
|
||||
#include "auto4_perl_console.h"
|
||||
#include "auto4_perl_factory.h"
|
||||
#include "options.h"
|
||||
#include "ass_style.h"
|
||||
|
||||
#ifdef __VISUALC__
|
||||
#pragma warning(disable: 4800)
|
||||
#pragma warning(disable: 4706)
|
||||
#endif
|
||||
|
||||
#define COLLECT_PV(buf, s, e) \
|
||||
buf = wxString(SvPV_nolen(ST(s)), pl2wx);\
|
||||
for(int ARG_i = s+1; ARG_i <= e; ARG_i++) {\
|
||||
buf << _T(" ") << wxString(SvPV_nolen(ST(ARG_i)), pl2wx);\
|
||||
}
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
static PerlInterpreter *perl_interpreter = NULL;
|
||||
|
||||
///////////////////////////////////
|
||||
// Perl -> C++ interface (XSUBS)
|
||||
//
|
||||
|
||||
/* package Aegisub */
|
||||
XS(perl_log)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::log);
|
||||
dXSARGS;
|
||||
IV level = 6;
|
||||
|
||||
int start = 0;
|
||||
if(items >= 2 && SvIOK(ST(0))) {
|
||||
level = SvIV(ST(0));
|
||||
start = 1;
|
||||
}
|
||||
wxString msg;
|
||||
COLLECT_PV(msg, start, items-1);
|
||||
PerlLog(level, msg);
|
||||
}
|
||||
|
||||
XS(perl_warning)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::warn);
|
||||
dXSARGS;
|
||||
|
||||
if(items >= 1) {
|
||||
wxString buf;
|
||||
COLLECT_PV(buf, 0, items-1);
|
||||
PerlLogWarning(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XS(perl_text_extents)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::text_extents);
|
||||
dXSARGS;
|
||||
|
||||
// Read the parameters
|
||||
SV *style; wxString text;
|
||||
if(items >= 2) {
|
||||
// Enough of them
|
||||
style = sv_mortalcopy(ST(0));
|
||||
text = wxString(SvPV_nolen(ST(1)), pl2wx);
|
||||
}
|
||||
else {
|
||||
PerlLogWarning(_("Not enough parameters for Aegisub::text_extents()"));
|
||||
// We needed 2 parameters at least!
|
||||
XSRETURN_UNDEF;
|
||||
}
|
||||
|
||||
// Get the AssStyle
|
||||
AssStyle *s;
|
||||
if(SvROK(style)) {
|
||||
// Create one from the hassh
|
||||
s = PerlAss::MakeAssStyle((HV*)SvRV(style));
|
||||
}
|
||||
else {
|
||||
// It's the name of the style
|
||||
wxString sn(SvPV_nolen(style), pl2wx);
|
||||
// We get it from the AssFile::top
|
||||
s = AssFile::top->GetStyle(sn);
|
||||
/* TODO maybe: make it dig from the current hassh's styles */
|
||||
if(!s)
|
||||
XSRETURN_UNDEF;
|
||||
}
|
||||
|
||||
// The return parameters
|
||||
double width, height, descent, extlead;
|
||||
// The actual calculation
|
||||
if(!CalculateTextExtents(s, text, width, height, descent, extlead)) {
|
||||
/* TODO: diagnose error */
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
// Returns
|
||||
switch(GIMME_V) {
|
||||
case G_SCALAR:
|
||||
// Scalar context
|
||||
XSRETURN_NV(width);
|
||||
break;
|
||||
default:
|
||||
case G_ARRAY:
|
||||
// List context
|
||||
EXTEND(SP, 4);
|
||||
XST_mNV(0, width);
|
||||
XST_mNV(1, height);
|
||||
XST_mNV(2, descent);
|
||||
XST_mNV(3, extlead);
|
||||
XSRETURN(4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Aegisub::Script */
|
||||
XS(perl_script_set_info)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Script::set_info);
|
||||
dXSARGS;
|
||||
|
||||
PerlScript *active = PerlScript::GetScript();
|
||||
if(active) {
|
||||
// Update the object's vars
|
||||
active->ReadVars();
|
||||
|
||||
// We want at most 4 parameters :P
|
||||
if(items > 4) items = 4;
|
||||
// Set script info vars
|
||||
switch (items) {
|
||||
case 4:
|
||||
active->SetVersion(wxString(SvPV_nolen(ST(3)), pl2wx));
|
||||
case 3:
|
||||
active->SetAuthor(wxString(SvPV_nolen(ST(2)), pl2wx));
|
||||
case 2:
|
||||
active->SetDescription(wxString(SvPV_nolen(ST(1)), pl2wx));
|
||||
case 1:
|
||||
active->SetName(wxString(SvPV_nolen(ST(0)), pl2wx));
|
||||
}
|
||||
|
||||
// Update the package's vars
|
||||
active->WriteVars();
|
||||
}
|
||||
}
|
||||
|
||||
XS(perl_script_register_macro)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Script::register_macro);
|
||||
dXSARGS;
|
||||
|
||||
PerlScript *active = PerlScript::GetScript();
|
||||
if(active && items >= 3) {
|
||||
wxString name, description;
|
||||
SV *proc_sub = NULL, *val_sub = NULL;
|
||||
|
||||
if(items > 4) items = 4;
|
||||
switch (items) {
|
||||
case 4:
|
||||
val_sub = sv_mortalcopy(ST(3));
|
||||
case 3:
|
||||
proc_sub = sv_mortalcopy(ST(2));
|
||||
description = wxString(SvPV_nolen(ST(1)), pl2wx);
|
||||
name = wxString(SvPV_nolen(ST(0)), pl2wx);
|
||||
}
|
||||
if(proc_sub) {
|
||||
active->AddFeature(new PerlFeatureMacro(name, description, active, proc_sub, val_sub));
|
||||
XSRETURN_YES;
|
||||
}
|
||||
}
|
||||
XSRETURN_UNDEF;
|
||||
}
|
||||
|
||||
XS(perl_script_set_undo_point)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Script::set_undo_point);
|
||||
dXSARGS;
|
||||
|
||||
wxString desc;
|
||||
if(items > 0)
|
||||
desc = wxString(SvPV_nolen(ST(0)), pl2wx);
|
||||
else
|
||||
desc = _T("Auto4Perl");
|
||||
|
||||
AssFile::top->FlagAsModified(desc);
|
||||
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
/* Aegisub::Progress */
|
||||
XS(perl_progress_set)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Progress::set_progress);
|
||||
dXSARGS;
|
||||
|
||||
PerlProgressSink *ps = PerlProgressSink::GetProgressSink();
|
||||
if(ps && items >= 1) {
|
||||
NV pc = SvNV(ST(0));
|
||||
if(pc <= 1) pc *= 100;
|
||||
if(pc > 100) pc = 100;
|
||||
ps->SetProgress(pc);
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
}
|
||||
|
||||
XS(perl_progress_task)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Progress::set_task);
|
||||
dXSARGS;
|
||||
|
||||
PerlProgressSink *ps = PerlProgressSink::GetProgressSink();
|
||||
if(ps && items >= 1) {
|
||||
wxString task;
|
||||
COLLECT_PV(task, 0, items-1);
|
||||
ps->SetTask(task);
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
}
|
||||
|
||||
XS(perl_progress_title)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Progress::set_title);
|
||||
dXSARGS;
|
||||
|
||||
PerlProgressSink *ps = PerlProgressSink::GetProgressSink();
|
||||
if(ps && items >= 1) {
|
||||
wxString title;
|
||||
COLLECT_PV(title, 0, items-1);
|
||||
ps->SetTitle(title);
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
}
|
||||
|
||||
XS(perl_progress_cancelled)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::Progress::is_cancelled);
|
||||
dMARK; dAX;
|
||||
|
||||
if(PerlProgressSink *ps = PerlProgressSink::GetProgressSink()) {
|
||||
if(ps->IsCancelled()) XSRETURN_YES;
|
||||
else XSRETURN_NO;
|
||||
}
|
||||
else {
|
||||
XSRETURN_UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Aegisub::PerlConsole */
|
||||
XS(perl_console_register)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::PerlConsole::register_console);
|
||||
#ifdef WITH_PERLCONSOLE
|
||||
dXSARGS;
|
||||
|
||||
PerlScript *script = PerlScript::GetScript();
|
||||
if(script) {
|
||||
wxString name = _T("Perl console");
|
||||
wxString desc = _T("Show the Perl console");
|
||||
switch (items) {
|
||||
case 2:
|
||||
desc = wxString(SvPV_nolen(ST(1)), pl2wx);
|
||||
case 1:
|
||||
name = wxString(SvPV_nolen(ST(0)), pl2wx);
|
||||
}
|
||||
|
||||
if(!PerlConsole::GetConsole())
|
||||
// If there's no registered console
|
||||
script->AddFeature(new PerlConsole(name, desc, script));
|
||||
}
|
||||
XSRETURN_YES;
|
||||
#else
|
||||
dMARK; dAX;
|
||||
PerlLogWarning(_("Tried to register PerlConsole, but support for it was disabled in this version.")); // Warning or Hint?
|
||||
XSRETURN_UNDEF;
|
||||
#endif
|
||||
}
|
||||
|
||||
XS(perl_console_echo)
|
||||
{
|
||||
wxTRACE_FUNC(Aegisub::PerlConsole::echo);
|
||||
dXSARGS;
|
||||
|
||||
// We should get some parameters
|
||||
if(items == 0) return;
|
||||
|
||||
// Join the params in a unique string :S
|
||||
wxString buffer = wxString(SvPV_nolen(ST(0)), pl2wx);
|
||||
for(int i = 1; i < items; i++) {
|
||||
buffer << _T(" ") << wxString(SvPV_nolen(ST(i)), pl2wx);
|
||||
}
|
||||
|
||||
#ifdef WITH_PERLCONSOLE
|
||||
if(PerlConsole::GetConsole()) {
|
||||
// If there's a console echo to it
|
||||
PerlConsole::Echo(buffer);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
// Otherwise print on stdout
|
||||
PerlIO_printf(PerlIO_stdout(), "%s\n", buffer.mb_str(wxConvLocal).data());
|
||||
// (through perl io system)
|
||||
}
|
||||
|
||||
/* Universal loader */
|
||||
EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
|
||||
|
||||
/* XS registration */
|
||||
EXTERN_C void xs_perl_main(pTHX)
|
||||
{
|
||||
dXSUB_SYS;
|
||||
|
||||
/* DynaLoader is a special case */
|
||||
newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
|
||||
|
||||
// My XSUBS ^^
|
||||
newXS("Aegisub::log", perl_log, __FILE__);
|
||||
newXS("Aegisub::warn", perl_warning, __FILE__);
|
||||
newXS("Aegisub::text_extents", perl_text_extents, __FILE__);
|
||||
newXS("Aegisub::Script::set_info", perl_script_set_info, __FILE__);
|
||||
newXS("Aegisub::Script::register_macro", perl_script_register_macro, __FILE__);
|
||||
newXS("Aegisub::Script::set_undo_point", perl_script_set_undo_point, __FILE__);
|
||||
newXS("Aegisub::Progress::set_progress", perl_progress_set, __FILE__);
|
||||
newXS("Aegisub::Progress::set_task", perl_progress_task, __FILE__);
|
||||
newXS("Aegisub::Progress::set_title", perl_progress_title, __FILE__);
|
||||
newXS("Aegisub::Progress::is_cancelled", perl_progress_cancelled, __FILE__);
|
||||
newXS("Aegisub::PerlConsole::echo", perl_console_echo, __FILE__);
|
||||
newXS("Aegisub::PerlConsole::register_console", perl_console_register, __FILE__);
|
||||
}
|
||||
|
||||
|
||||
/////////////
|
||||
// PerlLog
|
||||
//
|
||||
void PerlLog(unsigned int level, const wxString &msg)
|
||||
{
|
||||
PerlProgressSink *ps = PerlProgressSink::GetProgressSink();
|
||||
if(!(level & 0x8) && ps) {
|
||||
wxString _msg;
|
||||
// Prepend a description of the log line
|
||||
switch(level) {
|
||||
case 0: _msg = _("Fatal error: ");
|
||||
break;
|
||||
case 1: _msg = _("Error: ");
|
||||
break;
|
||||
case 2: _msg = _("Warning: ");
|
||||
break;
|
||||
case 3: _msg = _("Hint: ");
|
||||
break;
|
||||
case 4: _msg = _("Debug: ");
|
||||
break;
|
||||
case 5: _msg = _("Trace: ");
|
||||
}
|
||||
// Print onto the progress window
|
||||
ps->Log(level >= 6 ? -1 : level, _msg+msg+_T("\n"));
|
||||
}
|
||||
else {
|
||||
level &= 0x7;
|
||||
// Use the wx log functions
|
||||
switch(level) {
|
||||
case 0: wxLogFatalError(msg);
|
||||
break;
|
||||
case 1: wxLogError(msg);
|
||||
break;
|
||||
case 2: wxLogWarning(msg);
|
||||
break;
|
||||
case 3: wxLogVerbose(msg);
|
||||
break;
|
||||
case 4: wxLogDebug(msg);
|
||||
break;
|
||||
case 5: wxLogTrace(wxTRACE_AutoPerl, msg);
|
||||
break;
|
||||
default:
|
||||
case 6: wxLogMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
// PerlThread
|
||||
//
|
||||
|
||||
PerlThread::PerlThread():
|
||||
wxThread(wxTHREAD_JOINABLE)
|
||||
{
|
||||
pv = NULL; sv = NULL;
|
||||
}
|
||||
|
||||
PerlThread::PerlThread(const char *sub_name, I32 flags, bool type):
|
||||
wxThread(wxTHREAD_JOINABLE)
|
||||
{
|
||||
wxTRACE_METH(PerlThread);
|
||||
if(type == CALL) Call(sub_name, flags);
|
||||
if(type == EVAL) Eval(sub_name, flags);
|
||||
}
|
||||
|
||||
PerlThread::PerlThread(SV *sv, I32 flags, bool type):
|
||||
wxThread(wxTHREAD_JOINABLE)
|
||||
{
|
||||
wxTRACE_METH(PerlThread);
|
||||
if(type == CALL) Call(sv, flags);
|
||||
if(type == EVAL) Eval(sv, flags);
|
||||
}
|
||||
|
||||
wxThreadError PerlThread::launch()
|
||||
{
|
||||
wxThreadError e = Create();
|
||||
if(e != wxTHREAD_NO_ERROR) return e;
|
||||
|
||||
switch(Options.AsInt(_T("Automation Thread Priority"))) {
|
||||
case 2: SetPriority(10);
|
||||
break;
|
||||
case 1: SetPriority(30);
|
||||
break;
|
||||
default:
|
||||
case 0: SetPriority(50); // fallback normal
|
||||
}
|
||||
|
||||
wxTRACE_RET(PerlThread);
|
||||
return Run();
|
||||
}
|
||||
|
||||
wxThreadError PerlThread::Call(const char *sub_name, I32 _flags)
|
||||
{
|
||||
type = CALL; pv = sub_name; flags = _flags;
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("type = CALL, pv = '%s', flags = %u"), wxString(pv, pl2wx).c_str(), flags);
|
||||
return launch();
|
||||
}
|
||||
wxThreadError PerlThread::Call(SV *_sv, I32 _flags)
|
||||
{
|
||||
type = CALL; sv = _sv; flags = _flags;
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("type = CALL, sv = %p, flags = %u"), sv, flags);
|
||||
return launch();
|
||||
}
|
||||
|
||||
wxThreadError PerlThread::Eval(const char* p, I32 croak_on_error)
|
||||
{
|
||||
type = EVAL; pv = p; flags = croak_on_error;
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("type = EVAL, pv = '%s', flags = %u"), wxString(pv, pl2wx).c_str(), flags);
|
||||
return launch();
|
||||
}
|
||||
wxThreadError PerlThread::Eval(SV* _sv, I32 _flags)
|
||||
{
|
||||
type = EVAL; sv = _sv; flags = _flags;
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("type = EVAL, sv = %p, flags = %u"), sv, flags);
|
||||
return launch();
|
||||
}
|
||||
|
||||
wxThread::ExitCode PerlThread::Entry()
|
||||
{
|
||||
wxTRACE_METH(Entry);
|
||||
|
||||
PerlProgressSink *ps;
|
||||
if(ps = PerlProgressSink::GetProgressSink()) {
|
||||
// If there's a progress sink...
|
||||
while(!ps->has_inited);
|
||||
// ...wait for it to have inited
|
||||
}
|
||||
|
||||
PERL_SET_CONTEXT(perl_interpreter);
|
||||
ExitCode ec = NULL;
|
||||
switch(type) {
|
||||
case CALL:
|
||||
if(sv) ec = (ExitCode)((size_t)call_sv(sv, flags));
|
||||
else if(pv) ec = (ExitCode)((size_t)call_pv(pv, flags));
|
||||
break;
|
||||
case EVAL:
|
||||
if(sv) ec = (ExitCode)((size_t)eval_sv(sv, flags));
|
||||
else if(pv) ec = (ExitCode)((size_t)eval_pv(pv, flags));
|
||||
}
|
||||
|
||||
if(SvTRUE(ERRSV)) {
|
||||
// Log $@ in case of error
|
||||
PerlLogError(wxString(SvPV_nolen(ERRSV), pl2wx));
|
||||
}
|
||||
|
||||
if(ps) {
|
||||
ps->script_finished = true;
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
|
||||
wxTRACE_RET(Entry);
|
||||
return ec;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// PerlScriptFactory
|
||||
//
|
||||
|
||||
PerlScriptFactory::PerlScriptFactory()
|
||||
{
|
||||
#ifdef WXTRACE_AUTOPERL
|
||||
// Add tracing of perl engine operations
|
||||
wxLog::AddTraceMask(wxTRACE_AutoPerl);
|
||||
#endif
|
||||
|
||||
// Script engine properties
|
||||
loaded = false;
|
||||
engine_name = _T("Perl");
|
||||
filename_pattern = _T("*") _T(PERL_SCRIPT_EXTENSION);
|
||||
}
|
||||
|
||||
void PerlScriptFactory::RegisterFactory()
|
||||
{
|
||||
// On Visual Studio, first check if the dll is available
|
||||
// This needs to be done because it is set to delay loading of this dll
|
||||
#ifdef __VISUALC__
|
||||
HMODULE dll = LoadLibrary(_T("perl510.dll"));
|
||||
if (!dll) return;
|
||||
FreeLibrary(dll);
|
||||
#endif
|
||||
|
||||
// Perl interpreter initialization (ONE FOR ALL THE SCRIPTS)
|
||||
int argc = 3;
|
||||
char *argv[3] = { "aegisub", "-e", "0" };
|
||||
char** env = NULL;
|
||||
char **argv2 = (char**) argv; // VC++ wants this °_°
|
||||
PERL_SYS_INIT3(&argc,&argv2,&env);
|
||||
parser = perl_alloc();
|
||||
perl_construct(parser);
|
||||
perl_parse(parser, xs_perl_main,
|
||||
argc, argv,
|
||||
NULL);
|
||||
//free(argv);
|
||||
// (That was pretty magic o_O)
|
||||
|
||||
// Let's register the perl script factory \o/
|
||||
perl_interpreter = parser;
|
||||
Register(this);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
PerlScriptFactory::~PerlScriptFactory()
|
||||
{
|
||||
// Perl interpreter deinitialization
|
||||
if (loaded) {
|
||||
perl_destruct(parser);
|
||||
perl_free(parser);
|
||||
PERL_SYS_TERM();
|
||||
}
|
||||
}
|
||||
|
||||
Script* PerlScriptFactory::Produce(const wxString &filename) const
|
||||
{
|
||||
if(filename.EndsWith(_T(PERL_SCRIPT_EXTENSION))) {
|
||||
return new PerlScript(filename);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //WITH_PERL
|
|
@ -1,245 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef _AUTO4_PERL_H
|
||||
#define _AUTO4_PERL_H
|
||||
|
||||
|
||||
#include "auto4_base.h"
|
||||
#include <wx/window.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include "ass_file.h"
|
||||
|
||||
#undef _
|
||||
#include <EXTERN.h>
|
||||
#include <perl.h>
|
||||
#include <XSUB.h>
|
||||
|
||||
#include "auto4_perldata.inc" // Perl variables manipulation macros
|
||||
#undef bool
|
||||
|
||||
// the fucking perl.h redefines _() -.-
|
||||
#undef _
|
||||
#define _(s) wxGetTranslation(_T(s))
|
||||
|
||||
|
||||
// String conversions between wxWidgets and Perl
|
||||
#define wx2pl wxConvUTF8
|
||||
#define pl2wx wxConvUTF8
|
||||
|
||||
|
||||
#define PERL_SCRIPT_EXTENSION ".pl" /* TODO maybe: make it multi-extension */
|
||||
|
||||
|
||||
// Debug support
|
||||
/* define the following to activate tracing for the perl engine */
|
||||
#define WXTRACE_AUTOPERL
|
||||
#define wxTRACE_AutoPerl _T("auto4_perl")
|
||||
|
||||
#define wxTRACE_METH(name) \
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("\t=== %p::%s() ==="), this, _T(#name))
|
||||
|
||||
#define wxTRACE_FUNC(name) \
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("\t=== %s() ==="), _T(#name))
|
||||
|
||||
#define wxTRACE_RET(name) \
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("\t___ %s() returned ___"), _T(#name))
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
/////////////
|
||||
// PerlLog
|
||||
//
|
||||
#define LOG_FATAL 0
|
||||
#define LOG_ERROR 1
|
||||
#define LOG_WARNING 2
|
||||
#define LOG_HINT 3
|
||||
#define LOG_DEBUG 4
|
||||
#define LOG_TRACE 5
|
||||
#define LOG_MESSAGE 6
|
||||
|
||||
#define LOG_WX 8
|
||||
|
||||
#define PerlLogFatal(str) PerlLog(LOG_FATAL, str)
|
||||
#define PerlLogFatalError(str) PerlLog(LOG_FATAL, str)
|
||||
#define PerlLogError(str) PerlLog(LOG_ERROR, str)
|
||||
#define PerlLogWarning(str) PerlLog(LOG_WARNING, str)
|
||||
#define PerlLogHint(str) PerlLog(LOG_HINT, str)
|
||||
#define PerlLogVerbose(str) PerlLog(LOG_HINT, str)
|
||||
#define PerlLogDebug(str) PerlLog(LOG_DEBUG, str)
|
||||
#define PerlLogTrace(str) PerlLog(LOG_TRACE, str)
|
||||
#define PerlLogMessage(str) PerlLog(LOG_MESSAGE, str)
|
||||
|
||||
void PerlLog(unsigned int level, const wxString &msg);
|
||||
|
||||
|
||||
////////////////
|
||||
// PerlThread
|
||||
//
|
||||
class PerlThread : public wxThread {
|
||||
private:
|
||||
const char *pv;
|
||||
SV *sv;
|
||||
I32 flags;
|
||||
|
||||
bool type;
|
||||
|
||||
wxThreadError launch();
|
||||
|
||||
public:
|
||||
enum { EVAL = 0, CALL = 1 };
|
||||
|
||||
PerlThread();
|
||||
PerlThread(const char *sub_name, I32 flags, bool type = CALL);
|
||||
PerlThread(SV *sv, I32 flags, bool type = CALL);
|
||||
|
||||
wxThreadError Call(const char *sub_name, I32 flags);
|
||||
wxThreadError Call(SV *sv, I32 flags);
|
||||
wxThreadError Eval(const char* p, I32 croak_on_error);
|
||||
wxThreadError Eval(SV* sv, I32 flags);
|
||||
|
||||
virtual ExitCode Entry();
|
||||
};
|
||||
|
||||
|
||||
///////////////////
|
||||
// Script object
|
||||
//
|
||||
class PerlScript : public Script {
|
||||
private:
|
||||
static PerlScript *active; // The active script (at any given time)
|
||||
|
||||
AV *inc_saved;
|
||||
wxString package; // Every script resides in a package named at random
|
||||
|
||||
bool reload; // Automatically reload if source file has changed
|
||||
time_t mtime; // The mtime of the loaded source file
|
||||
|
||||
void load(); // It doas all the script initialization
|
||||
void unload(); // It does all the script disposing
|
||||
|
||||
static void activate(PerlScript *script); // Set the active script
|
||||
static void deactivate(); // Unset the active script
|
||||
|
||||
public:
|
||||
PerlScript(const wxString &filename);
|
||||
virtual ~PerlScript();
|
||||
static PerlScript *GetScript() { return active; } // Query the value of the active script
|
||||
|
||||
virtual void Reload(); // Reloading of a loaded script
|
||||
|
||||
void Activate() { activate(this); } // Set the script as active
|
||||
void Deactivate() const { deactivate(); } // Unset the active script
|
||||
|
||||
/* TODO maybe: move to tied scalars */
|
||||
void ReadVars(); // Sync the script's vars from perl package to script object
|
||||
void WriteVars() const; // Sync the script's vars from script object to perl package
|
||||
|
||||
void AddFeature(Feature *feature);
|
||||
void DeleteFeature(Feature *feature);
|
||||
|
||||
const wxString& GetPackage() const { return package; } // The perl package containing script code
|
||||
void SetName(const wxString &str) { name = str; }
|
||||
void SetDescription(const wxString &str) { description = str; }
|
||||
void SetAuthor(const wxString &str) { author = str; }
|
||||
void SetVersion(const wxString &str) { version = str; }
|
||||
};
|
||||
|
||||
|
||||
//////////////////
|
||||
// Macro object
|
||||
//
|
||||
class PerlFeatureMacro : public FeatureMacro {
|
||||
private:
|
||||
SV *processing_sub; // May be reference or name of sub
|
||||
SV *validation_sub; // here too
|
||||
|
||||
protected:
|
||||
PerlScript *script; // The owner script
|
||||
|
||||
public:
|
||||
PerlFeatureMacro(const wxString &name, const wxString &description, PerlScript *perl_script, SV *proc_sub, SV *val_sub);
|
||||
virtual ~PerlFeatureMacro();
|
||||
|
||||
virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active);
|
||||
virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent);
|
||||
};
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PerlProgressSink
|
||||
//
|
||||
class PerlProgressSink : public ProgressSink {
|
||||
private:
|
||||
static PerlProgressSink *sink;
|
||||
public:
|
||||
PerlProgressSink(wxWindow *parent, const wxString &title = _T("..."));
|
||||
~PerlProgressSink();
|
||||
static PerlProgressSink *GetProgressSink() { return sink; }
|
||||
|
||||
bool IsCancelled() const { return cancelled; }
|
||||
void Log(int level, const wxString &message) { if(level <= trace_level) AddDebugOutput(message); }
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// Conversion between aegisub data and perl data
|
||||
//
|
||||
class PerlAss {
|
||||
private:
|
||||
|
||||
public:
|
||||
static wxString GetEntryClass(AssEntry *entry);
|
||||
|
||||
static HV *MakeHasshEntry(AssEntry *entry);
|
||||
static HV *MakeHasshStyle(AssStyle *style);
|
||||
static HV *MakeHasshDialogue(AssDialogue *diag);
|
||||
static AV *MakeHasshLines(AV *lines, AssFile *ass);
|
||||
|
||||
static AssEntry *MakeAssEntry(HV *entry);
|
||||
static AssStyle *MakeAssStyle(HV *style);
|
||||
static AssDialogue *MakeAssDialogue(HV *diag);
|
||||
static AssFile *MakeAssLines(AssFile *ass, AV *lines);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,483 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_PERL
|
||||
|
||||
|
||||
#include "auto4_perl.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_entry.h"
|
||||
#include "ass_style.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_attachment.h"
|
||||
|
||||
|
||||
// Disable warning
|
||||
#ifdef __VISUALC__
|
||||
#pragma warning(disable: 4800)
|
||||
#endif
|
||||
|
||||
// For wxString::Trim
|
||||
#define right true
|
||||
#define left false
|
||||
|
||||
|
||||
#define HASSH_BASIC_INIT(ae, he) \
|
||||
HV_TOUCH(he, "raw", 3)\
|
||||
HV_STORE(newSVpv(ae->GetEntryData().mb_str(wx2pl), 0));\
|
||||
HV_TOUCH(he, "section", 7)\
|
||||
HV_STORE(newSVpv(ae->group.mb_str(wx2pl), 0));\
|
||||
wxString he ## _class = GetEntryClass(ae);\
|
||||
HV_TOUCH(he, "class", 5)\
|
||||
HV_STORE(newSVpv(he ## _class.mb_str(wx2pl), 0))
|
||||
|
||||
#define ASS_BASIC_INIT(he, ae) \
|
||||
HV_FETCH(he, "raw", 3)\
|
||||
ae->SetEntryData(wxString(SvPV_nolen(HV_VAL), pl2wx));\
|
||||
HV_FETCH(he, "section", 7)\
|
||||
ae->group = wxString(SvPV_nolen(HV_VAL), pl2wx)
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
wxString PerlAss::GetEntryClass(AssEntry *entry)
|
||||
{
|
||||
switch(entry->GetType()) {
|
||||
case ENTRY_DIALOGUE: return _T("dialogue");
|
||||
case ENTRY_STYLE:
|
||||
return _T("style");
|
||||
/* TODO: add stylex recognition */
|
||||
break;
|
||||
case ENTRY_ATTACHMENT: return _T("attachment");
|
||||
default:
|
||||
case ENTRY_BASE:
|
||||
wxString data(entry->GetEntryData());
|
||||
if(data.Trim(left).StartsWith(_T(";"))) return _T("comment");
|
||||
else {
|
||||
if(entry->group == _T("[Script Info]") && data.Matches(_T("*:*"))) return _T("info");
|
||||
if(data == entry->group) return _T("head");
|
||||
if(data.StartsWith(_T("Format:"))) return _T("format");
|
||||
if(data.IsEmpty()) return _T("clear");
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return _T("unknown");
|
||||
}
|
||||
|
||||
|
||||
HV *PerlAss::MakeHasshEntry(AssEntry *e)
|
||||
{
|
||||
switch(e->GetType()) {
|
||||
case ENTRY_DIALOGUE:
|
||||
return MakeHasshDialogue(AssEntry::GetAsDialogue(e));
|
||||
|
||||
case ENTRY_STYLE:
|
||||
return MakeHasshStyle(AssEntry::GetAsStyle(e));
|
||||
|
||||
case ENTRY_ATTACHMENT:
|
||||
|
||||
default:
|
||||
case ENTRY_BASE:
|
||||
dHV;
|
||||
HV *entry = newHV();
|
||||
HASSH_BASIC_INIT(e, entry);
|
||||
|
||||
if(entry_class == _T("info")) {
|
||||
// Info
|
||||
HV_TOUCH(entry, "key", 3) {
|
||||
wxString _text = e->GetEntryData().BeforeFirst(_T(':')).Strip(wxString::both);
|
||||
HV_STORE(newSVpv(_text.mb_str(wx2pl), 0));
|
||||
}
|
||||
|
||||
HV_TOUCH(entry, "value", 5) {
|
||||
wxString _text = e->GetEntryData().AfterFirst(_T(':')).Strip(wxString::both);
|
||||
HV_STORE(newSVpv(_text.mb_str(wx2pl), 0));
|
||||
}
|
||||
}
|
||||
else if(entry_class == _T("format")) {
|
||||
// Format °_°
|
||||
HV_TOUCH(entry, "fields", 6) {
|
||||
AV *fields_av = newAV();
|
||||
HV_STORE(newRV_noinc((SV*)fields_av));
|
||||
for(wxString fields_buf = e->GetEntryData().AfterFirst(_T(':')).Trim(left);
|
||||
!fields_buf.IsEmpty();
|
||||
fields_buf = fields_buf.AfterFirst(_T(',')).Trim(left)) {
|
||||
av_push(fields_av, newSVpv(fields_buf.BeforeFirst(_T(',')).Trim(right).mb_str(wx2pl), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(entry_class == _T("comment")) {
|
||||
// Comment
|
||||
HV_TOUCH(entry, "text", 4) {
|
||||
wxString _text = e->GetEntryData().AfterFirst(_T(';'));
|
||||
HV_STORE(newSVpv(_text.mb_str(wx2pl), 0));
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
HV *PerlAss::MakeHasshStyle(AssStyle *s)
|
||||
{
|
||||
dHV;
|
||||
|
||||
// Create new empty hash
|
||||
HV *style = newHV();
|
||||
|
||||
// Add fields
|
||||
HASSH_BASIC_INIT(s, style);
|
||||
|
||||
HV_TOUCH(style, "name", 4)
|
||||
HV_STORE(newSVpv(s->name.mb_str(wx2pl), 0));
|
||||
|
||||
HV_TOUCH(style, "font", 4)
|
||||
HV_STORE(newSVpv(s->font.mb_str(wx2pl), 0));
|
||||
|
||||
HV_FAS(style, "fontsize", 8, nv, s->fontsize);
|
||||
|
||||
HV_TOUCH(style, "color1", 6)
|
||||
HV_STORE(newSVpv(s->primary.GetASSFormatted(true).mb_str(wx2pl), 0));
|
||||
HV_TOUCH(style, "color2", 6)
|
||||
HV_STORE(newSVpv(s->secondary.GetASSFormatted(true).mb_str(wx2pl), 0));
|
||||
HV_TOUCH(style, "color3", 6)
|
||||
HV_STORE(newSVpv(s->outline.GetASSFormatted(true).mb_str(wx2pl), 0));
|
||||
HV_TOUCH(style, "color4", 6)
|
||||
HV_STORE(newSVpv(s->shadow.GetASSFormatted(true).mb_str(wx2pl), 0));
|
||||
|
||||
HV_TOUCH(style, "bold", 4) HV_STORE(newSViv(s->bold));
|
||||
|
||||
HV_FAS(style, "italic", 6, iv, s->italic);
|
||||
HV_FAS(style, "underline", 9, iv, s->underline);
|
||||
HV_FAS(style, "strikeout", 9, iv, s->strikeout);
|
||||
|
||||
HV_FAS(style, "scale_x", 7, nv, s->scalex);
|
||||
HV_FAS(style, "scale_y", 7, nv, s->scaley);
|
||||
|
||||
HV_FAS(style, "spacing", 7, nv, s->spacing);
|
||||
|
||||
HV_FAS(style, "angle", 5, nv, s->angle);
|
||||
|
||||
HV_FAS(style, "borderstyle", 11, iv, s->borderstyle);
|
||||
|
||||
HV_FAS(style, "outline", 7, nv, s->outline_w);
|
||||
|
||||
HV_FAS(style, "shadow", 6, nv, s->shadow_w);
|
||||
|
||||
HV_FAS(style, "align", 5, iv, s->alignment);
|
||||
|
||||
HV_FAS(style, "margin_l", 8, iv, s->Margin[0]);
|
||||
HV_FAS(style, "margin_r", 8, iv, s->Margin[1]);
|
||||
HV_FAS(style, "margin_t", 8, iv, s->Margin[2]);
|
||||
HV_FAS(style, "margin_b", 8, iv, s->Margin[3]);
|
||||
|
||||
HV_FAS(style, "encoding", 8, iv, s->encoding);
|
||||
HV_FAS(style, "relative_to", 11, iv, s->relativeTo);
|
||||
|
||||
// Return the hassh style
|
||||
return style;
|
||||
}
|
||||
|
||||
HV *PerlAss::MakeHasshDialogue(AssDialogue *d)
|
||||
{
|
||||
dHV;
|
||||
|
||||
// Create new hash
|
||||
HV *diag = newHV();
|
||||
|
||||
// Copy the values from the AssDialogue
|
||||
HASSH_BASIC_INIT(d, diag);
|
||||
|
||||
HV_FAS(diag, "comment", 7, iv, d->Comment);
|
||||
|
||||
HV_FAS(diag, "layer", 5, iv, d->Layer);
|
||||
|
||||
HV_FAS(diag, "start_time", 10, iv, d->Start.GetMS());
|
||||
HV_FAS(diag, "end_time", 8, iv, d->End.GetMS());
|
||||
|
||||
HV_TOUCH(diag, "style", 5)
|
||||
HV_STORE(newSVpv(d->Style.mb_str(wx2pl), 0));
|
||||
|
||||
HV_TOUCH(diag, "actor", 5)
|
||||
HV_STORE(newSVpv(d->Actor.mb_str(wx2pl), 0));
|
||||
|
||||
HV_FAS(diag, "margin_l", 8, iv, d->Margin[0]);
|
||||
HV_FAS(diag, "margin_r", 8, iv, d->Margin[1]);
|
||||
HV_FAS(diag, "margin_t", 8, iv, d->Margin[2]);
|
||||
HV_FAS(diag, "margin_b", 8, iv, d->Margin[3]);
|
||||
|
||||
HV_TOUCH(diag, "effect", 6)
|
||||
HV_STORE(newSVpv(d->Effect.mb_str(wx2pl), 0));
|
||||
|
||||
HV_TOUCH(diag, "text", 4)
|
||||
HV_STORE(newSVpv(d->Text.mb_str(wx2pl), 0));
|
||||
|
||||
// Return the dialogue
|
||||
return diag;
|
||||
}
|
||||
|
||||
/* TODO: report progress */
|
||||
AV *PerlAss::MakeHasshLines(AV *lines, AssFile *ass)
|
||||
{
|
||||
if(!lines) {
|
||||
lines = newAV();
|
||||
}
|
||||
|
||||
dAV;
|
||||
I32 i = 0; I32 lines_len = av_len(lines);
|
||||
for(std::list<AssEntry*>::iterator it = ass->Line.begin(); it != ass->Line.end(); it++) {
|
||||
if(i <= lines_len && av_exists(lines, i))
|
||||
av_delete(lines, i, G_DISCARD);
|
||||
AV_TOUCH(lines, i++)
|
||||
AV_STORE(newRV_noinc((SV*)MakeHasshEntry(*it)));
|
||||
}
|
||||
for(; i <= lines_len; i++) {
|
||||
if(av_exists(lines, i))
|
||||
av_delete(lines, i, G_DISCARD);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
AssEntry *PerlAss::MakeAssEntry(HV *entry)
|
||||
{
|
||||
dHV;
|
||||
|
||||
if(!entry) {
|
||||
// Create an empty line, if NULL
|
||||
entry = newHV();
|
||||
}
|
||||
|
||||
// The fallback class
|
||||
wxString cl(_T("unknown"));
|
||||
// Let's get the actual class of the hassh
|
||||
HV_FETCH(entry, "class", 5)
|
||||
cl = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
// We trust the value of entry{class}
|
||||
if(cl == _T("dialogue")) {
|
||||
// It seems to be a dialogue, let's call the specialized function
|
||||
return MakeAssDialogue(entry);
|
||||
}
|
||||
else if(cl == _T("style")) {
|
||||
// It seems to be a style, let's call the specialized function
|
||||
return MakeAssStyle(entry);
|
||||
}
|
||||
else if(cl == _T("attachment")) {
|
||||
/* TODO */
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
// A base entry
|
||||
AssEntry *e = new AssEntry();
|
||||
|
||||
ASS_BASIC_INIT(entry, e);
|
||||
|
||||
if(cl == _T("info")) {
|
||||
wxString key, value;
|
||||
HV_FETCH(entry, "key", 3) {
|
||||
key = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
HV_FETCH(entry, "value", 5) {
|
||||
value = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
e->SetEntryData(key + _T(": ") + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not necessary, format customization isn't even supported by aegi (atm? °_°)
|
||||
/*else if(cl == _T("format")) {
|
||||
HV_FETCH(entry, "fields", 6) {
|
||||
AV *fields = (AV*)SvRV(HV_VAL);
|
||||
for(int i = 0; i < av_len(fields); i++) {
|
||||
SV ** field = av_fetch(fields, i, 0);
|
||||
if(field) {
|
||||
wxString field(SvPV_nolen(*field), pl2wx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
else if(cl == _T("comment")) {
|
||||
HV_FETCH(entry, "text", 4) {
|
||||
e->SetEntryData(_T(";") + wxString(SvPV_nolen(HV_VAL), pl2wx));
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
AssStyle *PerlAss::MakeAssStyle(HV *style)
|
||||
{
|
||||
dHV;
|
||||
|
||||
// Create a default style
|
||||
AssStyle *s = new AssStyle();
|
||||
|
||||
// Fill it with the values from the hassh
|
||||
ASS_BASIC_INIT(style, s);
|
||||
|
||||
HV_FETCH(style, "name", 4)
|
||||
s->name = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
HV_FETCH(style, "font", 4)
|
||||
s->font = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
HV_FAA(style, "fontsize", 8, NV, s->fontsize);
|
||||
|
||||
HV_FETCH(style, "color1", 6)
|
||||
s->primary.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx));
|
||||
HV_FETCH(style, "color2", 6)
|
||||
s->secondary.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx));
|
||||
HV_FETCH(style, "color3", 6)
|
||||
s->outline.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx));
|
||||
HV_FETCH(style, "color4", 6)
|
||||
s->shadow.Parse(wxString(SvPV_nolen(HV_VAL), pl2wx));
|
||||
|
||||
HV_FAA(style, "bold", 4, IV, s->bold);
|
||||
|
||||
HV_FAA(style, "italic", 6, IV, s->italic);
|
||||
HV_FAA(style, "underline", 9, IV, s->underline);
|
||||
HV_FAA(style, "strikeout", 9, IV, s->strikeout);
|
||||
|
||||
HV_FAA(style, "scale_x", 7, NV, s->scalex);
|
||||
HV_FAA(style, "scale_y", 7, NV, s->scaley);
|
||||
|
||||
HV_FAA(style, "spacing", 7, NV, s->spacing);
|
||||
|
||||
HV_FAA(style, "angle", 5, NV, s->angle);
|
||||
|
||||
HV_FAA(style, "borderstyle", 11, IV, s->borderstyle);
|
||||
|
||||
HV_FAA(style, "outline", 7, NV, s->outline_w);
|
||||
|
||||
HV_FAA(style, "shadow", 6, NV, s->shadow_w);
|
||||
|
||||
HV_FAA(style, "align", 5, IV, s->alignment);
|
||||
|
||||
HV_FAA(style, "margin_l", 8, IV, s->Margin[0]);
|
||||
HV_FAA(style, "margin_r", 8, IV, s->Margin[1]);
|
||||
HV_FAA(style, "margin_t", 8, IV, s->Margin[2]);
|
||||
HV_FAA(style, "margin_b", 8, IV, s->Margin[3]);
|
||||
|
||||
HV_FAA(style, "encoding", 8, IV, s->encoding);
|
||||
HV_FAA(style, "relative_to", 11, IV, s->relativeTo);
|
||||
|
||||
// Return the style
|
||||
return s;
|
||||
}
|
||||
|
||||
AssDialogue *PerlAss::MakeAssDialogue(HV *diag)
|
||||
{
|
||||
dHV;
|
||||
|
||||
// Create a default dialogue
|
||||
AssDialogue *d = new AssDialogue();
|
||||
|
||||
ASS_BASIC_INIT(diag, d);
|
||||
|
||||
HV_FAA(diag, "comment", 7, IV, d->Comment);
|
||||
|
||||
HV_FAA(diag, "layer", 5, IV, d->Layer);
|
||||
|
||||
HV_FETCH(diag, "start_time", 10)
|
||||
d->Start.SetMS(SvIV(HV_VAL));
|
||||
HV_FETCH(diag, "end_time", 8)
|
||||
d->End.SetMS(SvIV(HV_VAL));
|
||||
|
||||
HV_FETCH(diag, "style", 5)
|
||||
d->Style = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
HV_FETCH(diag, "actor", 5)
|
||||
d->Actor = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
HV_FAA(diag, "margin_l", 8, IV, d->Margin[0]);
|
||||
HV_FAA(diag, "margin_r", 8, IV, d->Margin[1]);
|
||||
HV_FAA(diag, "margin_t", 8, IV, d->Margin[2]);
|
||||
HV_FAA(diag, "margin_b", 8, IV, d->Margin[3]);
|
||||
|
||||
HV_FETCH(diag, "effect", 6)
|
||||
d->Effect = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
HV_FETCH(diag, "text", 4)
|
||||
d->Text = wxString(SvPV_nolen(HV_VAL), pl2wx);
|
||||
|
||||
// Return the dialogue
|
||||
return d;
|
||||
}
|
||||
|
||||
/* TODO: report progress */
|
||||
AssFile *PerlAss::MakeAssLines(AssFile *ass, AV *lines)
|
||||
{
|
||||
if(!ass) {
|
||||
/* TODO: create new AssFile if NULL */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// There may be a progress sink to report to
|
||||
PerlProgressSink *ps = PerlProgressSink::GetProgressSink();
|
||||
|
||||
dAV;
|
||||
std::list<AssEntry*>::iterator it = ass->Line.begin();
|
||||
I32 len = av_len(lines);
|
||||
for(I32 i = 0; i <= len; i++) {
|
||||
if(!av_exists(lines, i)) continue;
|
||||
if(i < (I32)ass->Line.size()) {
|
||||
if(*it) delete *it;
|
||||
AV_FETCH(lines, i)
|
||||
*it++ = MakeAssEntry((HV*)SvRV(AV_VAL));
|
||||
}
|
||||
else {
|
||||
AV_FETCH(lines, i)
|
||||
ass->Line.push_back(MakeAssEntry((HV*)SvRV(AV_VAL)));
|
||||
}
|
||||
|
||||
// Report progress
|
||||
if(ps) ps->SetProgress((i+1)/(len+1) * 100);
|
||||
}
|
||||
|
||||
for(; it != ass->Line.end();) {
|
||||
it = ass->Line.erase(it);
|
||||
}
|
||||
|
||||
return ass;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //WITH_PERL
|
|
@ -1,244 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_PERL
|
||||
#ifdef WITH_PERLCONSOLE
|
||||
|
||||
|
||||
#include "auto4_perl.h"
|
||||
#include "auto4_perl_console.h"
|
||||
#include "main.h"
|
||||
#include "frame_main.h"
|
||||
#include "subs_grid.h"
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// PerlConsole::Dialog
|
||||
//
|
||||
|
||||
inline PerlConsole::Dialog::Dialog()
|
||||
{
|
||||
txt_out = NULL;
|
||||
}
|
||||
|
||||
inline bool PerlConsole::Dialog::Create(wxWindow* parent, wxWindowID id, const wxString& title,
|
||||
const wxPoint& pos, const wxSize& size,
|
||||
long style, const wxString& name)
|
||||
{
|
||||
wxDialog::Create(parent, id, title, pos, size, style, name);
|
||||
|
||||
// The text controls in the console
|
||||
txt_out = new wxTextCtrl(this, -1, _T(""), wxDefaultPosition, wxSize(300,200),
|
||||
wxTE_MULTILINE | wxTE_READONLY | wxTE_CHARWRAP | wxTE_RICH);
|
||||
|
||||
txt_hist = new wxTextCtrl(this, -1, _T(""), wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_MULTILINE | wxTE_READONLY | wxTE_CHARWRAP | wxTE_RICH);
|
||||
txt_in = new wxTextCtrl(this, -1, _T(""), wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_MULTILINE | wxTE_CHARWRAP | wxTE_PROCESS_ENTER);
|
||||
|
||||
// The right panel
|
||||
wxBoxSizer *rightpanel = new wxBoxSizer(wxVERTICAL);
|
||||
rightpanel->Add(txt_hist, 1, wxEXPAND);
|
||||
rightpanel->Add(txt_in, 0, wxEXPAND);
|
||||
// And the event handler for the input box
|
||||
Connect(txt_in->GetId(), wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(PerlConsole::Dialog::InputEnter));
|
||||
|
||||
// The whole dialog
|
||||
wxBoxSizer *mainpanel = new wxBoxSizer(wxHORIZONTAL);
|
||||
mainpanel->Add(txt_out, 1, wxEXPAND | wxRIGHT, 2);
|
||||
mainpanel->Add(rightpanel, 1, wxEXPAND | wxLEFT, 2);
|
||||
|
||||
// Getting it to work
|
||||
SetSizer(mainpanel);
|
||||
mainpanel->SetSizeHints(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void PerlConsole::Dialog::InputEnter(wxCommandEvent& evt)
|
||||
{
|
||||
if(txt_in->GetInsertionPoint() == txt_in->GetLastPosition() &&
|
||||
txt_in->GetLineLength(txt_in->GetNumberOfLines()-1) == 0) {
|
||||
// If an empty line have been entered...
|
||||
/* TODO: implement an actual command history */
|
||||
*txt_hist << txt_in->GetValue() << PerlConsole::Evaluate(txt_in->GetValue()) << _T("\n");
|
||||
|
||||
// Resetting the input box
|
||||
txt_in->ChangeValue(_T(""));
|
||||
}
|
||||
else {
|
||||
// Just a normal line with text
|
||||
txt_in->WriteText(_T("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PerlConsole
|
||||
//
|
||||
PerlConsole *PerlConsole::registered = NULL;
|
||||
|
||||
PerlConsole::PerlConsole(const wxString &name, const wxString &desc, PerlScript *script):
|
||||
Feature(SCRIPTFEATURE_MACRO, name),
|
||||
/*FeatureMacro(name, description),*/
|
||||
PerlFeatureMacro(name, desc, script, NULL, NULL)
|
||||
{
|
||||
parent_window = NULL;
|
||||
dialog = new Dialog();
|
||||
|
||||
// Remove any previously registered console °_°
|
||||
if(registered) {
|
||||
registered->script->DeleteFeature(registered);
|
||||
}
|
||||
registered = this;
|
||||
}
|
||||
|
||||
PerlConsole::~PerlConsole()
|
||||
{
|
||||
if(dialog) dialog->Destroy();
|
||||
|
||||
/* TODO: Free something? */
|
||||
|
||||
// Delete the registered console
|
||||
registered = NULL;
|
||||
}
|
||||
|
||||
void PerlConsole::Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent)
|
||||
{
|
||||
if(!parent_window) {
|
||||
// Create the console's dialog if it doesn't already exist
|
||||
parent_window = progress_parent;
|
||||
dialog->Create(parent_window, -1, GetName(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
}
|
||||
// Show the console
|
||||
dialog->Show(true);
|
||||
// and return, the console will stay visible and permit running other macros
|
||||
// the console will 'just' emulate the execution of a macro whenever some code will be evaluated
|
||||
}
|
||||
|
||||
wxString PerlConsole::evaluate(const wxString &str)
|
||||
{
|
||||
/* This mimics FrameMain::OnAutomationMacro */
|
||||
|
||||
// Get a hold of the SubsBox
|
||||
SubtitlesGrid *sb = wxGetApp().frame->SubsBox;
|
||||
sb->BeginBatch();
|
||||
|
||||
// Create the @_ (global <.<)
|
||||
AV *AT = get_av("_", 1);
|
||||
av_clear(AT);
|
||||
// $_[0]
|
||||
AV *lines = PerlAss::MakeHasshLines(NULL, AssFile::top);
|
||||
av_push(AT, newRV_noinc((SV*)lines));
|
||||
// $_[1]
|
||||
std::vector<int> selected_lines = sb->GetAbsoluteSelection();
|
||||
AV *selected_av = newAV();
|
||||
VECTOR_AV(selected_lines, selected_av, int, iv);
|
||||
av_push(AT, newRV_noinc((SV*)selected_av));
|
||||
// $_[2]
|
||||
int first_sel = sb->GetFirstSelRow();
|
||||
av_push(AT, newSViv(first_sel));
|
||||
|
||||
// Clear all maps from the subs grid before running the macro
|
||||
// The stuff done by the macro might invalidate some of the iterators held by the grid, which will cause great crashing
|
||||
sb->Clear();
|
||||
|
||||
// Here we go
|
||||
script->WriteVars();
|
||||
// Box the code into the right package
|
||||
wxString code = _T("package ") + script->GetPackage() + _T(";\n");
|
||||
// Add the user's code
|
||||
code << str;
|
||||
// Evaluate the code
|
||||
SV *e = eval_pv(code.mb_str(wx2pl), 0);
|
||||
/* TODO: use threaded calls */
|
||||
/*PerlThread eval(code.mb_str(wx2pl), 1, PerlThread::EVAL);
|
||||
e = (SV*)eval.Wait();*/
|
||||
/* TODO: check for errors */
|
||||
script->ReadVars();
|
||||
|
||||
// Recreate the top assfile from perl hassh
|
||||
//AssFile::top->FlagAsModified(GetName());
|
||||
PerlAss::MakeAssLines(AssFile::top, lines);
|
||||
av_undef(lines);
|
||||
// And reset selection vector
|
||||
selected_lines.clear();
|
||||
AV_VECTOR(selected_av, selected_lines, IV);
|
||||
CHOP_SELECTED(AssFile::top, selected_lines);
|
||||
av_undef(selected_av);
|
||||
|
||||
// Have the grid update its maps, this properly refreshes it to reflect the changed subs
|
||||
sb->UpdateMaps();
|
||||
sb->SetSelectionFromAbsolute(selected_lines);
|
||||
sb->CommitChanges(true, false);
|
||||
sb->EndBatch();
|
||||
|
||||
// The eval's return
|
||||
return wxString(SvPV_nolen(e), pl2wx);
|
||||
}
|
||||
|
||||
wxString PerlConsole::Evaluate(const wxString &str)
|
||||
{
|
||||
if(registered) {
|
||||
return registered->evaluate(str);
|
||||
}
|
||||
else {
|
||||
/* TODO: print error */
|
||||
return _T("");
|
||||
}
|
||||
}
|
||||
|
||||
void PerlConsole::Echo(const wxString &str)
|
||||
{
|
||||
if(registered && registered->dialog->txt_out) {
|
||||
*(registered->dialog->txt_out) << str << _T("\n");
|
||||
}
|
||||
else {
|
||||
PerlIO_printf(PerlIO_stdout(), "%s\n", str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //WITH_PERLCONSOLE
|
||||
#endif //WITH_PERL
|
|
@ -1,94 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef _AUTO4_PERL_CONSOLE_H
|
||||
#define _AUTO4_PERL_CONSOLE_H
|
||||
|
||||
|
||||
#include "auto4_perl.h"
|
||||
#include <wx/textctrl.h>
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
/////////////////
|
||||
// PerlConsole
|
||||
//
|
||||
class PerlConsole : public PerlFeatureMacro {
|
||||
private:
|
||||
static PerlConsole *registered;
|
||||
|
||||
// Nested classes are messy, therefore we use them :)
|
||||
class Dialog : public wxDialog {
|
||||
private:
|
||||
friend class PerlConsole;
|
||||
wxTextCtrl *txt_out, *txt_hist, *txt_in;
|
||||
|
||||
public:
|
||||
Dialog();
|
||||
|
||||
bool Create(wxWindow* parent, wxWindowID id, const wxString& title,
|
||||
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
|
||||
long style = wxDEFAULT_DIALOG_STYLE, const wxString& name = _T("console_dialog"));
|
||||
|
||||
void InputEnter(wxCommandEvent& evt);
|
||||
};
|
||||
|
||||
Dialog *dialog;
|
||||
wxWindow *parent_window;
|
||||
|
||||
wxString evaluate(const wxString &str);
|
||||
|
||||
public:
|
||||
PerlConsole(const wxString &name, const wxString &desc, PerlScript *script);
|
||||
virtual ~PerlConsole();
|
||||
|
||||
static PerlConsole *GetConsole() { return registered; }
|
||||
|
||||
virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active) { return true; }
|
||||
virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent);
|
||||
|
||||
static wxString Evaluate(const wxString &str);
|
||||
static void Echo(const wxString &str);
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_PERL
|
||||
|
||||
|
||||
#include "auto4_perl.h"
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PerlProgressSink
|
||||
//
|
||||
PerlProgressSink *PerlProgressSink::sink;
|
||||
|
||||
PerlProgressSink::PerlProgressSink(wxWindow* parent, const wxString &title):
|
||||
ProgressSink(parent)
|
||||
{
|
||||
if(sink) {
|
||||
sink->Destroy();
|
||||
}
|
||||
sink = this;
|
||||
SetTitle(_("Executing ") + title);
|
||||
}
|
||||
|
||||
PerlProgressSink::~PerlProgressSink()
|
||||
{
|
||||
sink = NULL;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //WITH_PERL
|
|
@ -1,67 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef _AUTO4_PERL_FACTORY_H
|
||||
#define _AUTO4_PERL_FACTORY_H
|
||||
|
||||
|
||||
#include "auto4_base.h"
|
||||
struct interpreter;
|
||||
typedef interpreter PerlInterpreter;
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
///////////////////////
|
||||
// PerlScriptFactory
|
||||
//
|
||||
class PerlScriptFactory : public ScriptFactory {
|
||||
private:
|
||||
PerlInterpreter *parser;
|
||||
bool loaded;
|
||||
|
||||
public:
|
||||
PerlScriptFactory();
|
||||
~PerlScriptFactory();
|
||||
|
||||
void RegisterFactory();
|
||||
|
||||
Script* Produce(const wxString &filename) const;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,475 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_PERL
|
||||
|
||||
|
||||
#include "auto4_perl.h"
|
||||
#include "version.h"
|
||||
#include "standard_paths.h"
|
||||
#include <wx/filename.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
|
||||
#ifdef __VISUALC__
|
||||
#pragma warning(disable: 4800)
|
||||
#pragma warning(disable: 4706)
|
||||
#endif
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PerlScript class
|
||||
//
|
||||
PerlScript *PerlScript::active = NULL;
|
||||
|
||||
PerlScript::PerlScript(const wxString &filename):
|
||||
Script(filename)
|
||||
{
|
||||
// Create a package name for the script
|
||||
package.Printf(_T("Aegisub::Script::p%lx"), this);
|
||||
|
||||
// local @INC; # lol
|
||||
inc_saved = newAV();
|
||||
|
||||
// Buggy
|
||||
reload = false;
|
||||
mtime = 0;
|
||||
|
||||
// Load the script
|
||||
load();
|
||||
}
|
||||
|
||||
PerlScript::~PerlScript()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
void PerlScript::Reload()
|
||||
{
|
||||
unload();
|
||||
reload = false;
|
||||
load();
|
||||
}
|
||||
|
||||
void PerlScript::load()
|
||||
{
|
||||
wxTRACE_METH(load);
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("filename = '%s', package = '%s'"), GetFilename().c_str(), package.c_str());
|
||||
|
||||
// Feed some defaults into the script info
|
||||
name = GetPrettyFilename().BeforeLast(_T('.'));
|
||||
description = _("Perl script");
|
||||
author = wxGetUserId();
|
||||
version = GetAegisubShortVersionString();
|
||||
|
||||
wxFileName fn(GetFilename());
|
||||
wxDateTime mod;
|
||||
fn.GetTimes(NULL,&mod,NULL);
|
||||
mtime = mod.GetTicks();
|
||||
|
||||
// Create the script's package
|
||||
gv_stashpv(package.mb_str(wx2pl), 1);
|
||||
// Set this script as active
|
||||
activate(this);
|
||||
|
||||
// 'Enclose' the script into its package
|
||||
wxString _script = _T("package ") + package + _T(";\n")
|
||||
_T("require Aegisub; require Aegisub::Script; require Aegisub::Progress;") // Core modules
|
||||
_T("our ($_script_reload, $_script_path, $_script_package);\n") // Internal vars
|
||||
_T("our ($script_name, $script_description, $script_author, $script_version);\n") // Package info
|
||||
_T("open SCRIPT, $_script_path;\n") // Open the script file
|
||||
_T("local @_source = <SCRIPT>;\n") // read the source
|
||||
_T("close SCRIPT;\n") // close the file
|
||||
_T("eval \"@{_source}\n1;\" || die $@;"); // eval the source
|
||||
|
||||
// Let's eval the 'boxed' script
|
||||
eval_pv(_script.mb_str(wx2pl), 0);
|
||||
SV *_err = newSVsv(ERRSV); // We need this later
|
||||
// Done running
|
||||
deactivate();
|
||||
// and check on errors
|
||||
if(SvTRUE(_err)) {
|
||||
description = wxString(SvPV_nolen(_err), pl2wx);
|
||||
loaded = false;
|
||||
}
|
||||
else {
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
wxTRACE_RET(load);
|
||||
}
|
||||
|
||||
void PerlScript::unload() {
|
||||
wxTRACE_METH(unload);
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("name = '%s' package = '%s'"), name.c_str(), package.c_str());
|
||||
|
||||
// Deinstantiate(?) all features and clear the vector
|
||||
for(; !features.empty(); features.pop_back()) {
|
||||
delete (Feature*) features.back();
|
||||
}
|
||||
features.clear();
|
||||
|
||||
// Dismiss the package's stash
|
||||
hv_undef((HV*)gv_stashpv(package.mb_str(wx2pl), 0));
|
||||
|
||||
// Officially finished with unloading
|
||||
wxLogDebug(_T("'%s' (%s) unloaded"), name.c_str(), package.c_str());
|
||||
loaded = false;
|
||||
wxTRACE_RET(unload);
|
||||
}
|
||||
|
||||
void PerlScript::activate(PerlScript *script)
|
||||
{
|
||||
wxTRACE_FUNC(PerlScript::activate);
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("name = '%s', package = '%s'"), script->GetName().c_str(), script->GetPackage().c_str());
|
||||
|
||||
// Hooking $SIG{__WARN__}
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("$SIG{__WARN__} = \\&Aegisub::warn"));
|
||||
eval_pv("$SIG{__WARN__} = \\&Aegisub::warn", 1);
|
||||
|
||||
// Add the script's includes to @INC
|
||||
AV *inc_av = get_av("main::INC", 0);
|
||||
if(inc_av) {
|
||||
dAV;
|
||||
|
||||
// Save the previous includes
|
||||
AV_COPY(inc_av, script->inc_saved);
|
||||
|
||||
// Make room in @INC
|
||||
I32 inc_count = script->include_path.GetCount();
|
||||
av_unshift(inc_av, inc_count);
|
||||
// Add the automation include paths
|
||||
for(I32 i = 0; i < inc_count; i++) {
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("$INC[%d] = '%s'"), i, script->include_path.Item(i).c_str());
|
||||
AV_TOUCH(inc_av, i)
|
||||
AV_STORE(newSVpv(script->include_path.Item(i).mb_str(wx2pl), 0));
|
||||
}
|
||||
wxLogDebug(_T("@INC = ( %s )"), wxString(SvPV_nolen(eval_pv("\"@INC\"", 1)), pl2wx).c_str());
|
||||
}
|
||||
else {
|
||||
PerlLogWarning(_("Unable to add the automation include path(s) to @INC: the script's code may not compile or execute properly."));
|
||||
}
|
||||
|
||||
// Set the values of script vars
|
||||
script->WriteVars();
|
||||
|
||||
active = script;
|
||||
wxLogDebug(_T("'%s' (%p) activated"), active->GetName().c_str(), active);
|
||||
}
|
||||
|
||||
void PerlScript::deactivate()
|
||||
{
|
||||
wxTRACE_FUNC(PerlScript::deactivate);
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("name = '%s', package = '%s'"), active->GetName().c_str(), active->GetPackage().c_str());
|
||||
|
||||
// Revert @INC to its value before the script activation
|
||||
AV *inc_av = get_av("main::INC", 0);
|
||||
if(inc_av) {
|
||||
dAV;
|
||||
|
||||
// Reset @INC
|
||||
if(av_len(active->inc_saved) >= 0) {
|
||||
// If there's a saved one
|
||||
AV_COPY(active->inc_saved, inc_av);
|
||||
wxLogDebug(_T("@INC = ( %s )"), wxString(SvPV_nolen(eval_pv("\"@INC\"", 1)), pl2wx).c_str());
|
||||
av_clear(active->inc_saved);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the values of script vars
|
||||
active->ReadVars();
|
||||
|
||||
// If reload flag is set...
|
||||
/* STILL BROKEN :< */
|
||||
if(active->reload) {
|
||||
// check if the source file on disk changed
|
||||
wxFileName fn(active->GetFilename());
|
||||
wxDateTime mod;
|
||||
fn.GetTimes(NULL,&mod,NULL);
|
||||
if(active->mtime != mod.GetTicks()) {
|
||||
// and reload the script
|
||||
PerlLogVerbose(wxString::Format(_("Reloading %s because the file on disk (%s) changed."), active->GetName().c_str(), active->GetFilename().c_str()));
|
||||
active->Reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Unhooking $SIG{__WARN__}
|
||||
wxLogTrace(wxTRACE_AutoPerl, _T("undef $SIG{__WARN__}"));
|
||||
eval_pv("undef $SIG{__WARN__}", 1);
|
||||
|
||||
wxLogDebug(_T("%s(%p) deactivated"), active->GetName().c_str(), active);
|
||||
active = NULL;
|
||||
}
|
||||
|
||||
void PerlScript::AddFeature(Feature *feature)
|
||||
{
|
||||
wxTRACE_METH(AddFeature);
|
||||
features.push_back(feature);
|
||||
wxLogDebug(_T("Added '%s' to '%s'(%s)'s features"), feature->GetName().c_str(), name.c_str(), package.c_str());
|
||||
}
|
||||
|
||||
void PerlScript::DeleteFeature(Feature *feature)
|
||||
{
|
||||
wxTRACE_METH(DeleteFeature);
|
||||
for(std::vector<Feature*>::iterator it = features.begin(); it != features.end(); it++)
|
||||
if(*it == feature) {
|
||||
delete feature;
|
||||
wxLogDebug(_T("Deleted '%s' from '%s'(%s)'s features"), feature->GetName().c_str(), name.c_str(), package.c_str());
|
||||
features.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void PerlScript::ReadVars()
|
||||
{
|
||||
wxTRACE_METH(ReadVars);
|
||||
// This will get anything inside it °_°
|
||||
SV *whore = NULL;
|
||||
// All the vars' names will stick to it #_#
|
||||
wxString bitch;
|
||||
|
||||
bitch = package + _T("::script_name");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 0);
|
||||
if(whore) name = wxString(SvPV_nolen(whore), pl2wx);
|
||||
|
||||
bitch = package + _T("::script_description");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 0);
|
||||
if(whore) description = wxString(SvPV_nolen(whore), pl2wx);
|
||||
|
||||
bitch = package + _T("::script_author");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 0);
|
||||
if(whore) author = wxString(SvPV_nolen(whore), pl2wx);
|
||||
|
||||
bitch = package + _T("::script_version");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 0);
|
||||
if(whore) version = wxString(SvPV_nolen(whore), pl2wx);
|
||||
|
||||
//bitch = package + _T("::_script_reload");
|
||||
//whore = get_sv(bitch.mb_str(wx2pl), 0);
|
||||
//if(whore) reload = SvTRUE(whore);
|
||||
}
|
||||
|
||||
void PerlScript::WriteVars() const
|
||||
{
|
||||
wxTRACE_METH(WriteVars);
|
||||
// Somewhat as above
|
||||
SV *whore = NULL;
|
||||
wxString bitch;
|
||||
|
||||
bitch = package + _T("::_script_package");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setpv(whore, package.mb_str(wx2pl));
|
||||
|
||||
bitch = package + _T("::_script_path");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setpv(whore, GetFilename().mb_str(wx2pl));
|
||||
|
||||
bitch = package + _T("::_script_reload");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setiv(whore, int(reload));
|
||||
|
||||
bitch = package + _T("::script_name");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setpv(whore, name.mb_str(wx2pl));
|
||||
|
||||
bitch = package + _T("::script_description");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setpv(whore, description.mb_str(wx2pl));
|
||||
|
||||
bitch = package + _T("::script_author");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setpv(whore, author.mb_str(wx2pl));
|
||||
|
||||
bitch = package + _T("::script_version");
|
||||
whore = get_sv(bitch.mb_str(wx2pl), 1);
|
||||
sv_setpv(whore, version.mb_str(wx2pl));
|
||||
}
|
||||
|
||||
|
||||
//////////////////////
|
||||
// PerlFeatureMacro
|
||||
//
|
||||
|
||||
PerlFeatureMacro::PerlFeatureMacro(const wxString &name, const wxString &description, PerlScript *own_script, SV *proc_sub, SV *val_sub):
|
||||
Feature(SCRIPTFEATURE_MACRO, name),
|
||||
FeatureMacro(name, description)
|
||||
{
|
||||
// We know what script we belong to ^_^
|
||||
script = own_script;
|
||||
|
||||
// And not surprisingly we have some callbacks too
|
||||
processing_sub = newSVsv(proc_sub);
|
||||
validation_sub = newSVsv(val_sub);
|
||||
}
|
||||
|
||||
PerlFeatureMacro::~PerlFeatureMacro() {
|
||||
// The macro subroutines get undefined
|
||||
CV *cv = Nullcv;
|
||||
HV *hv = NULL;
|
||||
GV *gv = NULL;
|
||||
if(processing_sub) {
|
||||
cv = sv_2cv(processing_sub, &hv, &gv, 1);
|
||||
cv_undef(cv);
|
||||
if(hv) hv_undef(hv);
|
||||
}
|
||||
if(validation_sub) {
|
||||
cv = sv_2cv(validation_sub, &hv, &gv, 1);
|
||||
cv_undef(cv);
|
||||
if(hv) hv_undef(hv);
|
||||
}
|
||||
};
|
||||
|
||||
bool PerlFeatureMacro::Validate(AssFile *subs, const std::vector<int> &selected, int active)
|
||||
{
|
||||
// If there's no validation subroutine defined simply return true
|
||||
if(!validation_sub) return true;
|
||||
// otherwise...
|
||||
|
||||
// Sub lines
|
||||
AV *lines = PerlAss::MakeHasshLines(NULL, subs);
|
||||
// Selection array
|
||||
AV *selected_av = newAV();
|
||||
VECTOR_AV(selected, selected_av, int, iv);
|
||||
|
||||
// Activate the owner script
|
||||
script->Activate();
|
||||
|
||||
bool ret = false;
|
||||
int c = 0;
|
||||
|
||||
// Prepare the stack
|
||||
dSP;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
||||
// Push the parameters on the stack
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(sv_2mortal(newRV_noinc((SV*)lines)));
|
||||
XPUSHs(sv_2mortal(newRV_noinc((SV*)selected_av)));
|
||||
XPUSHs(sv_2mortal(newSViv(active)));
|
||||
PUTBACK;
|
||||
|
||||
// Call back the callback
|
||||
c = call_sv(validation_sub, G_EVAL | G_SCALAR);
|
||||
SPAGAIN;
|
||||
|
||||
if(SvTRUE(ERRSV)) {
|
||||
wxLogVerbose(wxString(SvPV_nolen(ERRSV), pl2wx));
|
||||
ret = false;
|
||||
}
|
||||
else {
|
||||
SV *wtf = sv_mortalcopy(POPs);
|
||||
ret = SvTRUE(wtf);
|
||||
}
|
||||
|
||||
// Tidy up everything
|
||||
PUTBACK;
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
|
||||
// Deactivate the script
|
||||
script->Deactivate();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PerlFeatureMacro::Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent)
|
||||
{
|
||||
/* TODO: extend the progress window 'coverage' */
|
||||
// Convert the AssFile::Line to perl stuff
|
||||
AV *lines = PerlAss::MakeHasshLines(NULL, subs);
|
||||
// Same with the selection array
|
||||
AV *selected_av = newAV();
|
||||
VECTOR_AV(selected, selected_av, int, iv);
|
||||
|
||||
// Prepare the stack
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
||||
// Push the arguments onto the stack
|
||||
PUSHMARK(SP);
|
||||
SV* lines_ref = sv_2mortal(newRV_noinc((SV*)lines));
|
||||
XPUSHs(lines_ref);
|
||||
SV* selected_ref = sv_2mortal(newRV_noinc((SV*)selected_av));
|
||||
XPUSHs(selected_ref);
|
||||
XPUSHs(sv_2mortal(newSViv(active)));
|
||||
PUTBACK;
|
||||
|
||||
// Create a progress window
|
||||
PerlProgressSink *ps = new PerlProgressSink(progress_parent, GetName());
|
||||
// Start the callback thread
|
||||
script->Activate();
|
||||
PerlThread call(processing_sub, G_EVAL | G_VOID);
|
||||
// Show the progress window until it is dismissed
|
||||
ps->ShowModal();
|
||||
// Now wait the thread to return
|
||||
call.Wait();
|
||||
script->Deactivate();
|
||||
|
||||
if(!SvTRUE(ERRSV)) {
|
||||
// Show progress sink again
|
||||
ps->Show(true);
|
||||
ps->SetTask(_("Saving changes"));
|
||||
|
||||
// Recreate the ass :S
|
||||
subs->FlagAsModified(GetName());
|
||||
PerlAss::MakeAssLines(subs, (AV*)SvRV(lines_ref));
|
||||
// And reset selection vector
|
||||
selected.clear();
|
||||
AV_VECTOR((AV*)SvRV(selected_ref), selected, IV);
|
||||
CHOP_SELECTED(subs, selected);
|
||||
|
||||
ps->Hide();
|
||||
}
|
||||
// Delete the progress sink
|
||||
ps->Destroy();
|
||||
|
||||
// Clean the call stack
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //WITH_PERL
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright (c) 2008, Simone Cociancich
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:jiifurusu@gmail.com
|
||||
//
|
||||
|
||||
|
||||
//#include <assert.h>
|
||||
|
||||
|
||||
// Discards values of selected that are past the end of AssFile::Line
|
||||
#define CHOP_SELECTED(ass, sel) \
|
||||
for(; sel.back() >= (int)ass->Line.size(); sel.pop_back())
|
||||
|
||||
|
||||
// Conversions between std::vector<v_t> and AVs
|
||||
#define VECTOR_AV(v, av, v_t, s_t) \
|
||||
for(std::vector<v_t>::const_iterator it = v.begin(); it != v.end(); it++) \
|
||||
av_push(av, newSV ## s_t(*it))
|
||||
|
||||
#define AV_VECTOR(av, v, s_t) \
|
||||
for(int i = 0; i > -1 && i <= av_len(av); i++) { \
|
||||
SV **_val_ptr = av_fetch(av, i, 0); \
|
||||
if(_val_ptr) v.push_back(Sv ## s_t(*_val_ptr)); \
|
||||
}
|
||||
|
||||
|
||||
// Utilities to manipolate hash elements
|
||||
#define dHV \
|
||||
SV **HV_r;\
|
||||
HV *HV_tb; const char *HV_KEY; I32 HV_klen
|
||||
|
||||
#define HV_TOUCH(hv, k, kl) \
|
||||
HV_tb = hv;\
|
||||
HV_KEY = k;\
|
||||
HV_klen = kl;\
|
||||
HV_r = hv_fetch(HV_tb, HV_KEY, HV_klen, 1);\
|
||||
if(HV_r)
|
||||
|
||||
#define HV_FETCH(hv, k, kl) \
|
||||
HV_tb = hv;\
|
||||
HV_KEY = k;\
|
||||
HV_klen = kl;\
|
||||
HV_r = hv_fetch(HV_tb, HV_KEY, HV_klen, 0);\
|
||||
if(HV_r)
|
||||
|
||||
#define HV_VAL (*HV_r)
|
||||
|
||||
#define HV_STORE(si) \
|
||||
hv_store(HV_tb, HV_KEY, HV_klen, si, 0)
|
||||
|
||||
#define HV_FAS(hv, k, kl, vt, v) \
|
||||
HV_TOUCH(hv, k, kl) HV_STORE(newSV ## vt (v))
|
||||
#define HV_FAA(hv, k, kl, vt, a) \
|
||||
HV_FETCH(hv, k, kl) a = Sv ## vt (HV_VAL)
|
||||
|
||||
|
||||
// Utilities to manipulate list elements
|
||||
#define dAV \
|
||||
SV **AV_r;\
|
||||
AV *AV_ar; I32 AV_KEY
|
||||
|
||||
#define AV_TOUCH(av, k) \
|
||||
AV_ar = av;\
|
||||
AV_KEY = k;\
|
||||
AV_r = av_fetch(AV_ar, AV_KEY, 1);\
|
||||
if(AV_r)
|
||||
|
||||
#define AV_FETCH(av, k) \
|
||||
AV_ar = av;\
|
||||
AV_KEY = k;\
|
||||
AV_r = av_fetch(AV_ar, AV_KEY, 0);\
|
||||
if(AV_r)
|
||||
|
||||
#define AV_VAL (*AV_r)
|
||||
|
||||
#define AV_STORE(si) \
|
||||
av_store(AV_ar, AV_KEY, si)
|
||||
|
||||
#define AV_FAS(av, k, vt, v) \
|
||||
AV_TOUCH(av, k, kl) AV_STORE(newSV ## vt (v))
|
||||
#define AV_FAA(av, k, vt, a) \
|
||||
AV_FETCH(av, k, kl) a = Sv ## vt (AV_VAL)
|
||||
|
||||
#define AV_COPY(av_src, av_dst) \
|
||||
av_clear(av_dst);\
|
||||
for(I32 i = 0; i <= av_len(av_src); i++) {\
|
||||
AV_FETCH(av_src, i) {\
|
||||
SV *src = AV_VAL;\
|
||||
AV_TOUCH(av_dst, i)\
|
||||
AV_STORE(newSVsv(src));\
|
||||
}\
|
||||
}
|
|
@ -1,717 +0,0 @@
|
|||
// Copyright (c) 2007, Patryk Pomykalski
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:pomyk@go2.pl
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_RUBY
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4003)
|
||||
#endif
|
||||
|
||||
#include "auto4_ruby.h"
|
||||
#include "auto4_ruby_factory.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_style.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_override.h"
|
||||
#include "text_file_reader.h"
|
||||
#include "options.h"
|
||||
#include "vfr.h"
|
||||
#include "video_context.h"
|
||||
#include "main.h"
|
||||
#include "frame_main.h"
|
||||
#include "subs_grid.h"
|
||||
#include <ruby.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/filefn.h>
|
||||
#include <wx/window.h>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
RubyObjects *RubyObjects::inst = NULL;
|
||||
RubyScript * RubyScript::inst = NULL; // current Ruby Script
|
||||
RubyProgressSink* RubyProgressSink::inst = NULL;
|
||||
RubyThread* ruby_thread = NULL;
|
||||
wxSemaphore* ruby_thread_sem = NULL;
|
||||
wxSemaphore* ruby_script_sem = NULL;
|
||||
VALUE RubyAegisub = Qfalse;
|
||||
wxString backtrace = _T("");
|
||||
wxString error = _T("");
|
||||
|
||||
// RubyScript
|
||||
|
||||
RubyScript::RubyScript(const wxString &filename)
|
||||
: Script(filename)
|
||||
{
|
||||
try {
|
||||
Create();
|
||||
}
|
||||
catch (wxChar *e) {
|
||||
description = e;
|
||||
loaded = false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void RubyThread::CallFunction(RubyCallArguments* arg, VALUE *res)
|
||||
{
|
||||
args = arg;
|
||||
result = res;
|
||||
action = CALL_FUNCTION;
|
||||
}
|
||||
|
||||
void RubyThread::LoadFile(const char *f)
|
||||
{
|
||||
file = f;
|
||||
action = LOAD_FILE;
|
||||
}
|
||||
|
||||
RubyScript::~RubyScript()
|
||||
{
|
||||
}
|
||||
|
||||
void RubyScript::Create()
|
||||
{
|
||||
Destroy();
|
||||
RubyScript::inst = this;
|
||||
|
||||
try {
|
||||
if(ruby_thread == NULL)
|
||||
{
|
||||
ruby_thread_sem = new wxSemaphore(0, 1);
|
||||
ruby_script_sem = new wxSemaphore(0, 1);
|
||||
ruby_thread = new RubyThread(include_path);
|
||||
ruby_script_sem->Wait();
|
||||
}
|
||||
|
||||
wxCharBuffer buf = GetFilename().mb_str(wxConvISO8859_1);
|
||||
const char *t = buf.data();
|
||||
ruby_thread->LoadFile(t);
|
||||
ruby_thread_sem->Post();
|
||||
ruby_script_sem->Wait();
|
||||
if(ruby_thread->GetStatus())
|
||||
RubyScript::RubyError();
|
||||
|
||||
VALUE global_var = rb_gv_get("$script_name");
|
||||
if(TYPE(global_var) == T_STRING)
|
||||
name = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||
global_var = rb_gv_get("$script_description");
|
||||
if(TYPE(global_var) == T_STRING)
|
||||
description = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||
global_var = rb_gv_get("$script_author");
|
||||
if(TYPE(global_var) == T_STRING)
|
||||
author = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||
global_var = rb_gv_get("$script_version");
|
||||
if(TYPE(global_var) == T_STRING)
|
||||
version = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||
loaded = true;
|
||||
}
|
||||
catch (...) {
|
||||
Destroy();
|
||||
loaded = false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void RubyScript::Destroy()
|
||||
{
|
||||
|
||||
// remove features
|
||||
for (int i = 0; i < (int)features.size(); i++) {
|
||||
Feature *f = features[i];
|
||||
delete f;
|
||||
}
|
||||
features.clear();
|
||||
loaded = false;
|
||||
RubyScript::inst = NULL;
|
||||
}
|
||||
|
||||
void RubyScript::Reload()
|
||||
{
|
||||
Destroy();
|
||||
Create();
|
||||
}
|
||||
|
||||
RubyScript* RubyScript::GetScriptObject()
|
||||
{
|
||||
return RubyScript::inst;
|
||||
}
|
||||
|
||||
|
||||
VALUE RubyScript::RubyTextExtents(VALUE /*self*/, VALUE _style, VALUE _text)
|
||||
{
|
||||
if(TYPE(_style) != T_HASH)
|
||||
rb_raise(rb_eRuntimeError, "text_extents: Style parameter must be a hash");
|
||||
|
||||
AssEntry *et = RubyAssFile::RubyToAssEntry(_style);
|
||||
AssStyle *st = dynamic_cast<AssStyle*>(et);
|
||||
if (!st) {
|
||||
delete et; // Make sure to delete the "live" pointer
|
||||
rb_raise(rb_eRuntimeError, "Not a style entry");
|
||||
}
|
||||
|
||||
wxString text(StringValueCStr(_text), wxConvUTF8);
|
||||
|
||||
double width, height, descent, extlead;
|
||||
if (!CalculateTextExtents(st, text, width, height, descent, extlead)) {
|
||||
delete st;
|
||||
rb_raise(rb_eRuntimeError, "Some internal error occurred calculating text_extents");
|
||||
}
|
||||
delete st;
|
||||
|
||||
VALUE result = rb_ary_new3(4, rb_float_new(width), rb_float_new(height), rb_float_new(descent), rb_float_new(extlead));
|
||||
return result;
|
||||
}
|
||||
|
||||
VALUE RubyScript::RubyFrameToTime(VALUE /*self*/, VALUE frame)
|
||||
{
|
||||
if(TYPE(frame) == T_FIXNUM && VFR_Output.IsLoaded())
|
||||
{
|
||||
return INT2FIX(VFR_Output.GetTimeAtFrame(FIX2INT(frame), true));
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
VALUE RubyScript::RubyTimeToFrame(VALUE /*self*/, VALUE time)
|
||||
{
|
||||
if(TYPE(time) == T_FIXNUM && VFR_Output.IsLoaded())
|
||||
{
|
||||
return INT2FIX(VFR_Output.GetFrameAtTime(FIX2INT(time), true));
|
||||
}
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// output: [[keyframe indices], [keyframe times in ms]]
|
||||
VALUE RubyScript::RubyKeyFrames(VALUE /*self*/)
|
||||
{
|
||||
if(!VideoContext::Get()->KeyFramesLoaded())
|
||||
return Qnil;
|
||||
|
||||
wxArrayInt key_frames = VideoContext::Get()->GetKeyFrames();
|
||||
|
||||
VALUE frames = rb_ary_new();
|
||||
VALUE times = rb_ary_new();
|
||||
|
||||
for(unsigned int i = 0; i < key_frames.size(); ++i)
|
||||
{
|
||||
rb_ary_push(frames, INT2FIX(key_frames[i]));
|
||||
rb_ary_push(times, INT2FIX(VFR_Output.GetTimeAtFrame(key_frames[i], true)));
|
||||
}
|
||||
VALUE res = rb_ary_new();
|
||||
rb_ary_push(res, frames);
|
||||
rb_ary_push(res, times);
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString RubyScript::GetError()
|
||||
{
|
||||
return wxString(error + _T("\n") + backtrace);
|
||||
}
|
||||
|
||||
void RubyScript::RubyError()
|
||||
{
|
||||
wxMessageBox(RubyScript::inst->GetError(), _T("Error"),wxICON_ERROR | wxOK);
|
||||
error = _T("");
|
||||
backtrace = _T("");
|
||||
}
|
||||
|
||||
|
||||
// RubyFeature
|
||||
|
||||
RubyFeature::RubyFeature(ScriptFeatureClass _featureclass, const wxString &_name)
|
||||
: Feature(_featureclass, _name)
|
||||
{
|
||||
}
|
||||
|
||||
void RubyFeature::RegisterFeature()
|
||||
{
|
||||
RubyScript::GetScriptObject()->features.push_back(this);
|
||||
|
||||
// get the index+1 it was pushed into
|
||||
myid = (int)RubyScript::GetScriptObject()->features.size()-1;
|
||||
}
|
||||
|
||||
VALUE RubyFeature::CreateIntegerArray(const std::vector<int> &ints)
|
||||
{
|
||||
VALUE res = rb_ary_new2(ints.size());
|
||||
// create an array-style table with an integer vector in it
|
||||
for (unsigned int i = 0; i < ints.size(); ++i) {
|
||||
int k = ints[i];
|
||||
rb_ary_push(res, rb_int2inum(k));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void RubyFeature::ThrowError()
|
||||
{
|
||||
// wxString err(_T("Error running script") + RubyScript::inst->GetError());
|
||||
// wxLogError(err);
|
||||
}
|
||||
|
||||
|
||||
// RubyFeatureMacro
|
||||
|
||||
VALUE RubyFeatureMacro::RubyRegister(VALUE /*self*/, VALUE name, VALUE description, VALUE macro_function, VALUE validate_function)
|
||||
{
|
||||
wxString _name(StringValueCStr(name), wxConvUTF8);
|
||||
wxString _description(StringValueCStr(description), wxConvUTF8);
|
||||
RubyFeatureMacro *macro = new RubyFeatureMacro(_name, _description, macro_function, validate_function);
|
||||
(void)macro;
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
RubyFeatureMacro::RubyFeatureMacro(const wxString &_name, const wxString &_description, VALUE macro_function, VALUE validate_function)
|
||||
: Feature(SCRIPTFEATURE_MACRO, _name)
|
||||
, FeatureMacro(_name, _description)
|
||||
, RubyFeature(SCRIPTFEATURE_MACRO, _name)
|
||||
, macro_fun(macro_function)
|
||||
, validation_fun(validate_function)
|
||||
{
|
||||
no_validate = validate_function == Qnil;
|
||||
RegisterFeature();
|
||||
}
|
||||
|
||||
bool RubyFeatureMacro::Validate(AssFile *subs, const std::vector<int> &selected, int active)
|
||||
{
|
||||
if (no_validate)
|
||||
return true;
|
||||
|
||||
RubyProgressSink::inst = NULL;
|
||||
RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
|
||||
VALUE *argv = ALLOCA_N(VALUE, 3);
|
||||
argv[0] = subsobj->rbAssFile;
|
||||
argv[1] = CreateIntegerArray(selected); // selected items;
|
||||
argv[2] = INT2FIX(active);
|
||||
RubyCallArguments arg(rb_mKernel, rb_to_id(validation_fun), 3, argv);
|
||||
VALUE result;
|
||||
ruby_thread->CallFunction(&arg, &result);
|
||||
ruby_thread_sem->Post();
|
||||
ruby_script_sem->Wait();
|
||||
if(ruby_thread->GetStatus())
|
||||
RubyScript::RubyError();
|
||||
if(result != Qnil && result != Qfalse) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RubyFeatureMacro::Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent)
|
||||
{
|
||||
delete RubyProgressSink::inst;
|
||||
RubyProgressSink::inst = new RubyProgressSink(progress_parent, false);
|
||||
RubyProgressSink::inst->SetTitle(GetName());
|
||||
|
||||
// do call
|
||||
RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
|
||||
VALUE *argv = ALLOCA_N(VALUE, 3);
|
||||
argv[0] = subsobj->rbAssFile;
|
||||
argv[1] = CreateIntegerArray(selected); // selected items;
|
||||
argv[2] = INT2FIX(active);
|
||||
RubyCallArguments arg(rb_mKernel, rb_to_id(macro_fun), 3, argv);
|
||||
VALUE result;
|
||||
ruby_thread->CallFunction(&arg, &result);
|
||||
ruby_thread_sem->Post();
|
||||
RubyProgressSink::inst->ShowModal();
|
||||
ruby_script_sem->Wait();
|
||||
delete RubyProgressSink::inst;
|
||||
RubyProgressSink::inst = NULL;
|
||||
if(ruby_thread->GetStatus())
|
||||
RubyScript::RubyError();
|
||||
else if(TYPE(result) == T_ARRAY)
|
||||
{
|
||||
rb_gc_disable();
|
||||
bool end = false;
|
||||
for(int i = 0; i < RARRAY(result)->len && !end; ++i)
|
||||
{
|
||||
VALUE p = RARRAY(result)->ptr[i]; // some magic in code below to allow variable output
|
||||
if(TYPE(p) != T_ARRAY) {
|
||||
p = result;
|
||||
end = true;
|
||||
}
|
||||
|
||||
switch(TYPE(RARRAY(p)->ptr[0])) {
|
||||
|
||||
case T_HASH: // array of hashes = subs
|
||||
subsobj->RubyUpdateAssFile(p);
|
||||
break;
|
||||
|
||||
case T_FIXNUM: // array of ints = selection
|
||||
// i hope this works, can't test it -jfs
|
||||
int num = RARRAY(p)->len;
|
||||
selected.clear();
|
||||
selected.reserve(num);
|
||||
for(int i = 0; i < num; ++i) {
|
||||
selected.push_back(FIX2INT(RARRAY(p)->ptr[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
rb_gc_enable();
|
||||
}
|
||||
delete subsobj;
|
||||
}
|
||||
|
||||
// RubyThread
|
||||
void RubyThread::InitRuby()
|
||||
{
|
||||
#if defined(NT)
|
||||
int argc = 0;
|
||||
char **argv = 0;
|
||||
NtInitialize(&argc, &argv);
|
||||
#endif
|
||||
ruby_init();
|
||||
ruby_init_loadpath();
|
||||
error = _T("");
|
||||
backtrace = _T("");
|
||||
if(!RubyAegisub) {
|
||||
RubyAegisub = rb_define_module("Aegisub");
|
||||
rb_define_module_function(RubyAegisub, "register_macro",reinterpret_cast<RB_HOOK>(&RubyFeatureMacro::RubyRegister), 4);
|
||||
rb_define_module_function(RubyAegisub, "register_filter",reinterpret_cast<RB_HOOK>(&RubyFeatureFilter::RubyRegister), 5);
|
||||
rb_define_module_function(RubyAegisub, "text_extents",reinterpret_cast<RB_HOOK>(&RubyScript::RubyTextExtents), 2);
|
||||
rb_define_module_function(RubyAegisub, "frame_to_time",reinterpret_cast<RB_HOOK>(&RubyScript::RubyFrameToTime), 1);
|
||||
rb_define_module_function(RubyAegisub, "time_to_frame",reinterpret_cast<RB_HOOK>(&RubyScript::RubyTimeToFrame), 1);
|
||||
rb_define_module_function(RubyAegisub, "key_frames",reinterpret_cast<RB_HOOK>(&RubyScript::RubyKeyFrames), 0);
|
||||
rb_define_module_function(rb_eException, "set_backtrace",reinterpret_cast<RB_HOOK>(&RubyScript::backtrace_hook), 1);
|
||||
rb_define_module_function(RubyAegisub, "progress_set",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubySetProgress), 1);
|
||||
rb_define_module_function(RubyAegisub, "progress_task",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubySetTask), 1);
|
||||
rb_define_module_function(RubyAegisub, "progress_title",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubySetTitle), 1);
|
||||
rb_define_module_function(RubyAegisub, "debug_out",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubyDebugOut), -1);
|
||||
rb_define_module_function(RubyAegisub, "get_cancelled",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubyGetCancelled), 0);
|
||||
rb_define_module_function(RubyAegisub, "display_dialog",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubyDisplayDialog), 2);
|
||||
}
|
||||
VALUE paths = rb_gv_get("$:");
|
||||
for(unsigned int i = 0; i < include_path.GetCount(); i++)
|
||||
{
|
||||
rb_ary_push(paths, rb_str_new2(include_path[i].mb_str(wxConvISO8859_1)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RubyThread::RubyThread(wxPathList paths)
|
||||
: wxThread(wxTHREAD_JOINABLE)
|
||||
,include_path(paths)
|
||||
,action(NOTHING)
|
||||
{
|
||||
int prio = Options.AsInt(_T("Automation Thread Priority"));
|
||||
if (prio == 0) prio = 50; // normal
|
||||
else if (prio == 1) prio = 30; // below normal
|
||||
else if (prio == 2) prio = 10; // lowest
|
||||
else prio = 50; // fallback normal
|
||||
Create();
|
||||
SetPriority(prio);
|
||||
Run();
|
||||
}
|
||||
|
||||
wxThread::ExitCode RubyThread::Entry()
|
||||
{
|
||||
InitRuby();
|
||||
ruby_script_sem->Post();
|
||||
do {
|
||||
ruby_thread_sem->Wait();
|
||||
status = 0;
|
||||
switch(action)
|
||||
{
|
||||
case LOAD_FILE:
|
||||
rb_protect(rbLoadWrapper, rb_str_new2(file), &status);
|
||||
break;
|
||||
case CALL_FUNCTION:
|
||||
*result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(args), &status);
|
||||
if(RubyProgressSink::inst)
|
||||
{
|
||||
RubyProgressSink::inst->script_finished = true;
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
break;
|
||||
}
|
||||
ruby_script_sem->Post();
|
||||
}while(1);
|
||||
}
|
||||
|
||||
// RubyFeatureFilter
|
||||
RubyFeatureFilter::RubyFeatureFilter(const wxString &_name, const wxString &_description,
|
||||
int merit, VALUE _filter_fun, VALUE _dialog_fun)
|
||||
: Feature(SCRIPTFEATURE_FILTER, _name)
|
||||
, FeatureFilter(_name, _description, merit)
|
||||
, RubyFeature(SCRIPTFEATURE_FILTER, _name)
|
||||
, filter_fun(_filter_fun)
|
||||
, dialog_fun(_dialog_fun)
|
||||
{
|
||||
has_config = _dialog_fun != Qnil;
|
||||
// Works the same as in RubyFeatureMacro
|
||||
RegisterFeature();
|
||||
}
|
||||
|
||||
void RubyFeatureFilter::Init()
|
||||
{
|
||||
// Don't think there's anything to do here... (empty in auto3)
|
||||
}
|
||||
|
||||
VALUE RubyFeatureFilter::RubyRegister(VALUE /*self*/, VALUE name, VALUE description, VALUE merit, VALUE function, VALUE dialog)
|
||||
{
|
||||
wxString _name(StringValueCStr(name), wxConvUTF8);
|
||||
wxString _description(StringValueCStr(description), wxConvUTF8);
|
||||
int _merit = rb_num2long(merit);
|
||||
RubyFeatureFilter *filter = new RubyFeatureFilter(_name, _description, _merit, function, dialog);
|
||||
(void)filter;
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
void RubyFeatureFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
|
||||
{
|
||||
|
||||
try {
|
||||
VALUE cfg = 0;
|
||||
if (has_config && config_dialog) {
|
||||
cfg = config_dialog->RubyReadBack();
|
||||
// TODO, write back stored options here
|
||||
}
|
||||
RubyProgressSink::inst = new RubyProgressSink(export_dialog, false);
|
||||
RubyProgressSink::inst->SetTitle(GetName());
|
||||
|
||||
RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/);
|
||||
VALUE *argv = ALLOCA_N(VALUE, 2);
|
||||
argv[0] = subsobj->rbAssFile;
|
||||
argv[1] = cfg; // config
|
||||
RubyCallArguments arg(rb_mKernel, rb_to_id(filter_fun), 2, argv);
|
||||
VALUE result;
|
||||
ruby_thread->CallFunction(&arg, &result);
|
||||
ruby_thread_sem->Post();
|
||||
RubyProgressSink::inst->ShowModal();
|
||||
ruby_script_sem->Wait();
|
||||
if(ruby_thread->GetStatus())
|
||||
RubyScript::RubyError();
|
||||
RubyProgressSink::inst = NULL;
|
||||
delete RubyProgressSink::inst;
|
||||
if(TYPE(result) == T_ARRAY)
|
||||
{
|
||||
rb_gc_disable();
|
||||
subsobj->RubyUpdateAssFile(result);
|
||||
rb_gc_enable();
|
||||
}
|
||||
delete subsobj;
|
||||
} catch (const char* e) {
|
||||
wxString *err = new wxString(e, wxConvUTF8);
|
||||
wxMessageBox(*err, _T("Error running filter"),wxICON_ERROR | wxOK);
|
||||
}
|
||||
}
|
||||
|
||||
ScriptConfigDialog* RubyFeatureFilter::GenerateConfigDialog(wxWindow *parent)
|
||||
{
|
||||
if (!has_config)
|
||||
return 0;
|
||||
|
||||
delete RubyProgressSink::inst;
|
||||
RubyProgressSink::inst = new RubyProgressSink(parent, false);
|
||||
RubyProgressSink::inst->SetTitle(GetName());
|
||||
|
||||
// prepare function call
|
||||
// subtitles (don't allow any modifications during dialog creation, ideally the subs aren't even accessed)
|
||||
RubyAssFile *subsobj = new RubyAssFile(AssFile::top, false/*allow modifications*/, false/*disallow undo*/);
|
||||
|
||||
VALUE *argv = ALLOCA_N(VALUE, 2);
|
||||
argv[0] = subsobj->rbAssFile;
|
||||
argv[1] = Qnil; // TODO: stored options
|
||||
RubyCallArguments arg(rb_mKernel, rb_to_id(dialog_fun), 2, argv);
|
||||
VALUE dialog_data;
|
||||
ruby_thread->CallFunction(&arg, &dialog_data);
|
||||
ruby_thread_sem->Post();
|
||||
RubyProgressSink::inst->ShowModal();
|
||||
ruby_script_sem->Wait();
|
||||
if(ruby_thread->GetStatus())
|
||||
RubyScript::RubyError();
|
||||
delete RubyProgressSink::inst;
|
||||
RubyProgressSink::inst = NULL;
|
||||
|
||||
return config_dialog = new RubyConfigDialog(dialog_data, Qnil, false);
|
||||
}
|
||||
|
||||
|
||||
// RubyProgressSink
|
||||
|
||||
RubyProgressSink::RubyProgressSink(wxWindow *parent, bool /*allow_config_dialog*/)
|
||||
: ProgressSink(parent)
|
||||
{
|
||||
}
|
||||
|
||||
RubyProgressSink::~RubyProgressSink()
|
||||
{
|
||||
}
|
||||
|
||||
VALUE RubyProgressSink::RubySetProgress(VALUE /*self*/, VALUE progress)
|
||||
{
|
||||
float _progr = rb_num2dbl(progress);
|
||||
RubyProgressSink::inst->SetProgress(_progr);
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
VALUE RubyProgressSink::RubySetTask(VALUE /*self*/, VALUE task)
|
||||
{
|
||||
wxString _t(StringValueCStr(task), wxConvUTF8);
|
||||
RubyProgressSink::inst->SetTask(_t);
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
VALUE RubyProgressSink::RubySetTitle(VALUE /*self*/, VALUE title)
|
||||
{
|
||||
wxString _t(StringValueCStr(title), wxConvUTF8);
|
||||
RubyProgressSink::inst->SetTitle(_t);
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
VALUE RubyProgressSink::RubyGetCancelled(VALUE /*self*/)
|
||||
{
|
||||
if(RubyProgressSink::inst->cancelled)
|
||||
return Qtrue;
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
VALUE RubyProgressSink::RubyDebugOut(int argc, VALUE *args, VALUE /*self*/)
|
||||
{
|
||||
if(argc > 1 && TYPE(args[0]) == T_FIXNUM)
|
||||
{
|
||||
if(FIX2INT(args[0]) > RubyProgressSink::inst->trace_level)
|
||||
return Qnil;
|
||||
}
|
||||
else args[1] = args[0];
|
||||
wxString _m(StringValueCStr(args[1]), wxConvUTF8);
|
||||
RubyProgressSink::inst->AddDebugOutput(_m);
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
VALUE RubyProgressSink::RubyDisplayDialog(VALUE /*self*/, VALUE dialog_data, VALUE buttons)
|
||||
{
|
||||
// Send the "show dialog" event
|
||||
ShowConfigDialogEvent evt;
|
||||
|
||||
RubyConfigDialog dlg(dialog_data, buttons, true); // magically creates the config dialog structure etc
|
||||
evt.config_dialog = &dlg;
|
||||
|
||||
wxSemaphore sema(0, 1);
|
||||
evt.sync_sema = &sema;
|
||||
RubyProgressSink::inst->AddPendingEvent(evt);
|
||||
sema.Wait();
|
||||
return dlg.RubyReadBack();
|
||||
}
|
||||
|
||||
RubyObjects::RubyObjects()
|
||||
{
|
||||
objects = rb_ary_new();
|
||||
rb_gc_register_address(&objects);
|
||||
}
|
||||
|
||||
RubyObjects::~RubyObjects()
|
||||
{
|
||||
rb_gc_unregister_address(&objects);
|
||||
}
|
||||
|
||||
RubyObjects *RubyObjects::Get()
|
||||
{
|
||||
if(inst)
|
||||
return inst;
|
||||
else
|
||||
inst = new RubyObjects;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void RubyObjects::Register(VALUE obj) {
|
||||
rb_ary_push(objects, obj);
|
||||
}
|
||||
|
||||
void RubyObjects::Unregister(VALUE obj) {
|
||||
rb_ary_delete(objects, obj);
|
||||
}
|
||||
|
||||
RubyCallArguments::RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv)
|
||||
:id(_id), n(_n), argv(_argv)
|
||||
{
|
||||
recv = _recv;
|
||||
};
|
||||
|
||||
VALUE rbCallWrapper(VALUE arg)
|
||||
{
|
||||
RubyCallArguments &a = *reinterpret_cast<RubyCallArguments*>(arg);
|
||||
return rb_funcall2(a.recv, a.id, a.n, a.argv);
|
||||
}
|
||||
VALUE rbExecWrapper(VALUE /*arg*/){return ruby_exec();}
|
||||
VALUE rbLoadWrapper(VALUE arg){rb_load(arg, 0); return Qtrue;}
|
||||
VALUE rbGcWrapper(VALUE /*arg*/){rb_gc_start(); return Qtrue;}
|
||||
VALUE rbAss2RbWrapper(VALUE arg){return RubyAssFile::AssEntryToRuby(reinterpret_cast<AssEntry*>(arg));}
|
||||
VALUE rb2AssWrapper(VALUE arg){return reinterpret_cast<VALUE>(RubyAssFile::RubyToAssEntry(arg));}
|
||||
|
||||
VALUE RubyScript::backtrace_hook(VALUE self, VALUE backtr)
|
||||
{
|
||||
int len = RARRAY(backtr)->len;
|
||||
VALUE err = rb_funcall(self, rb_intern("to_s"), 0);
|
||||
error = wxString(StringValueCStr(err), wxConvUTF8);
|
||||
for(int i = 0; i < len; ++i)
|
||||
{
|
||||
VALUE str = RARRAY(backtr)->ptr[i];
|
||||
wxString line(StringValueCStr(str), wxConvUTF8);
|
||||
backtrace.Append(line + _T("\n"));
|
||||
}
|
||||
return backtr;
|
||||
}
|
||||
|
||||
RubyScriptFactory::RubyScriptFactory()
|
||||
{
|
||||
engine_name = _T("Ruby");
|
||||
filename_pattern = _T("*.rb");
|
||||
Register(this);
|
||||
}
|
||||
|
||||
RubyScriptFactory::~RubyScriptFactory()
|
||||
{
|
||||
}
|
||||
|
||||
Script* RubyScriptFactory::Produce(const wxString &filename) const
|
||||
{
|
||||
// Just check if file extension is .rb
|
||||
// Reject anything else
|
||||
if (filename.Right(3).Lower() == _T(".rb")) {
|
||||
return new RubyScript(filename);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // WITH_RUBY
|
|
@ -1,279 +0,0 @@
|
|||
// Copyright (c) 2007, Patryk Pomykalski
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:pomyk@go2.pl
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _AUTO4_RUBY_H
|
||||
#define _AUTO4_RUBY_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4311 4312)
|
||||
#endif
|
||||
|
||||
#include "auto4_base.h"
|
||||
#include <wx/thread.h>
|
||||
#include <wx/event.h>
|
||||
#include <ruby.h>
|
||||
|
||||
class wxWindow;
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
// Provides access to an AssFile object (and all lines contained) for a Ruby script
|
||||
class RubyAssFile {
|
||||
private:
|
||||
AssFile *ass;
|
||||
|
||||
bool can_modify;
|
||||
bool can_set_undo;
|
||||
|
||||
// keep a cursor of last accessed item to avoid walking over the entire file on every access
|
||||
std::list<AssEntry*>::iterator last_entry_ptr;
|
||||
int last_entry_id;
|
||||
|
||||
static int RubyParseTagData();
|
||||
static int RubyUnparseTagData();
|
||||
static int RubySetUndoPoint();
|
||||
|
||||
public:
|
||||
void RubyUpdateAssFile(VALUE subtitles);
|
||||
static VALUE AssEntryToRuby(AssEntry *e); // makes a Ruby representation of AssEntry
|
||||
static AssEntry *RubyToAssEntry(VALUE ass_entry); // creates an AssEntry object from a Ruby representation
|
||||
RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo);
|
||||
~RubyAssFile();
|
||||
|
||||
static RubyAssFile *raf;
|
||||
VALUE rbAssFile;
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Provides progress UI and control functions for a Ruby script
|
||||
class RubyProgressSink : public ProgressSink {
|
||||
private:
|
||||
|
||||
public:
|
||||
RubyProgressSink(wxWindow *parent, bool allow_config_dialog = true);
|
||||
virtual ~RubyProgressSink();
|
||||
static RubyProgressSink* inst;
|
||||
static VALUE RubySetProgress(VALUE self, VALUE progress);
|
||||
static VALUE RubySetTask(VALUE self, VALUE task);
|
||||
static VALUE RubySetTitle(VALUE self, VALUE title);
|
||||
static VALUE RubyGetCancelled(VALUE self);
|
||||
static VALUE RubyDebugOut(int argc, VALUE *args, VALUE self);
|
||||
static VALUE RubyDisplayDialog(VALUE self, VALUE cfg, VALUE buttons);
|
||||
};
|
||||
|
||||
|
||||
// Provides Config UI functions for a Ruby script
|
||||
class RubyConfigDialogControl {
|
||||
public:
|
||||
wxControl *cw; // control window
|
||||
wxString name, hint;
|
||||
int x, y, width, height;
|
||||
VALUE name_sym;
|
||||
|
||||
virtual wxControl *Create(wxWindow *parent) = 0;
|
||||
virtual void ControlReadBack() = 0;
|
||||
virtual VALUE RubyReadBack() = 0;
|
||||
|
||||
RubyConfigDialogControl();
|
||||
RubyConfigDialogControl(VALUE opts);
|
||||
virtual ~RubyConfigDialogControl() { }
|
||||
};
|
||||
|
||||
class RubyConfigDialog : public ScriptConfigDialog {
|
||||
private:
|
||||
std::vector<RubyConfigDialogControl*> controls;
|
||||
std::vector<wxString> buttons;
|
||||
bool use_buttons;
|
||||
|
||||
class ButtonEventHandler : public wxEvtHandler {
|
||||
public:
|
||||
int *button_pushed;
|
||||
void OnButtonPush(wxCommandEvent &evt);
|
||||
};
|
||||
|
||||
ButtonEventHandler *button_event;
|
||||
int button_pushed;
|
||||
|
||||
protected:
|
||||
wxWindow* CreateWindow(wxWindow *parent);
|
||||
|
||||
public:
|
||||
RubyConfigDialog(VALUE cfg, VALUE buttons, bool show_buttons);
|
||||
virtual ~RubyConfigDialog();
|
||||
VALUE RubyReadBack(); // read back internal structure to Ruby hash
|
||||
|
||||
void ReadBack(); // from auto4 base
|
||||
};
|
||||
|
||||
|
||||
// Second base-class for Ruby implemented Features
|
||||
class RubyFeature : public virtual Feature {
|
||||
protected:
|
||||
|
||||
int myid;
|
||||
|
||||
RubyFeature(ScriptFeatureClass _featureclass, const wxString &_name);
|
||||
|
||||
void RegisterFeature();
|
||||
VALUE CreateIntegerArray(const std::vector<int> &ints);
|
||||
void ThrowError();
|
||||
};
|
||||
|
||||
|
||||
// Class of Ruby scripts
|
||||
class RubyScript : public Script {
|
||||
friend class RubyFeature;
|
||||
friend class RubyProgressSink;
|
||||
|
||||
private:
|
||||
|
||||
void Create(); // load script and create internal structures etc.
|
||||
void Destroy(); // destroy internal structures, unreg features and delete environment
|
||||
|
||||
static RubyScript* GetScriptObject();
|
||||
public:
|
||||
static VALUE RubyTextExtents(VALUE self, VALUE style, VALUE text);
|
||||
static VALUE RubyFrameToTime(VALUE self, VALUE frame);
|
||||
static VALUE RubyTimeToFrame(VALUE self, VALUE time);
|
||||
static VALUE RubyKeyFrames(VALUE self);
|
||||
static VALUE backtrace_hook(VALUE self, VALUE backtr);
|
||||
|
||||
RubyScript(const wxString &filename);
|
||||
static void RubyError();
|
||||
static wxString GetError();
|
||||
virtual ~RubyScript();
|
||||
virtual void Reload();
|
||||
static RubyScript* inst;
|
||||
};
|
||||
|
||||
|
||||
// Implementation of the Macro Feature for Ruby scripts
|
||||
class RubyFeatureMacro : public FeatureMacro, RubyFeature {
|
||||
private:
|
||||
bool no_validate;
|
||||
VALUE macro_fun;
|
||||
VALUE validation_fun;
|
||||
|
||||
protected:
|
||||
RubyFeatureMacro(const wxString &_name, const wxString &_description, VALUE macro_function, VALUE validate_function);
|
||||
public:
|
||||
static VALUE RubyRegister(VALUE self, VALUE name, VALUE description, VALUE macro_function, VALUE validate_function);
|
||||
virtual ~RubyFeatureMacro() { }
|
||||
|
||||
virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active);
|
||||
virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent);
|
||||
};
|
||||
|
||||
|
||||
// Implementation of the Export Filter Feature for Ruby scripts
|
||||
class RubyFeatureFilter : public FeatureFilter, RubyFeature {
|
||||
private:
|
||||
bool has_config;
|
||||
RubyConfigDialog *config_dialog;
|
||||
VALUE filter_fun;
|
||||
VALUE dialog_fun;
|
||||
|
||||
protected:
|
||||
RubyFeatureFilter(const wxString &_name, const wxString &_description, int merit, VALUE function, VALUE dialog);
|
||||
|
||||
ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent);
|
||||
|
||||
void Init();
|
||||
public:
|
||||
static VALUE RubyRegister(VALUE self, VALUE name, VALUE description, VALUE merit, VALUE macro_function, VALUE validate_function);
|
||||
|
||||
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
|
||||
};
|
||||
|
||||
// class for registering ruby objects created in c++
|
||||
// so garbage collector doesn't destroy them
|
||||
class RubyObjects {
|
||||
private:
|
||||
VALUE objects;
|
||||
static RubyObjects *inst;
|
||||
RubyObjects();
|
||||
public:
|
||||
~RubyObjects();
|
||||
static RubyObjects *Get();
|
||||
void Register(VALUE object);
|
||||
void Unregister(VALUE object);
|
||||
};
|
||||
|
||||
// stuff for safe calling of ruby functions
|
||||
struct RubyCallArguments {
|
||||
VALUE recv;
|
||||
ID id;
|
||||
int n;
|
||||
VALUE *argv;
|
||||
RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv);
|
||||
};
|
||||
|
||||
// Separate thread for ruby interpreter
|
||||
class RubyThread : public wxThread {
|
||||
private:
|
||||
enum {NOTHING, CALL_FUNCTION, LOAD_FILE};
|
||||
int action;
|
||||
int status;
|
||||
RubyCallArguments *args;
|
||||
const char* file;
|
||||
VALUE *result;
|
||||
void InitRuby();
|
||||
wxPathList include_path;
|
||||
public:
|
||||
RubyThread(wxPathList include_path);
|
||||
void CallFunction(RubyCallArguments* arg, VALUE *res);
|
||||
void LoadFile(const char* file);
|
||||
int GetStatus() {return status;};
|
||||
virtual ExitCode Entry();
|
||||
};
|
||||
|
||||
VALUE rbCallWrapper(VALUE arg);
|
||||
VALUE rbExecWrapper(VALUE arg);
|
||||
VALUE rbLoadWrapper(VALUE arg);
|
||||
VALUE rbGcWrapper(VALUE arg);
|
||||
VALUE rbAss2RbWrapper(VALUE arg);
|
||||
VALUE rb2AssWrapper(VALUE arg);
|
||||
VALUE rbError(VALUE arg);
|
||||
typedef VALUE (*RB_HOOK)(...);
|
||||
typedef VALUE (*RB_HOOK2)(VALUE);
|
||||
|
||||
#define STR2SYM(x) ID2SYM(rb_intern(x))
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,439 +0,0 @@
|
|||
// Copyright (c) 2007, Patryk Pomykalski
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:pomyk@go2.pl
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_RUBY
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4003)
|
||||
#endif
|
||||
|
||||
#include "auto4_ruby.h"
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_style.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_override.h"
|
||||
#include "utils.h"
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <ruby.h>
|
||||
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
// LuaAssFile
|
||||
|
||||
VALUE RubyAssFile::AssEntryToRuby(AssEntry *e)
|
||||
{
|
||||
VALUE ass_entry;
|
||||
ass_entry = rb_hash_new();
|
||||
|
||||
wxString section(e->group);
|
||||
rb_hash_aset(ass_entry, STR2SYM("section"), rb_str_new2(e->group.mb_str(wxConvUTF8)));
|
||||
wxString raw(e->GetEntryData());
|
||||
if(!raw.IsEmpty())
|
||||
rb_hash_aset(ass_entry, STR2SYM("raw"), rb_str_new2(e->GetEntryData().mb_str(wxConvUTF8)));
|
||||
VALUE entry_class;
|
||||
|
||||
if (StringEmptyOrWhitespace(raw)) {
|
||||
entry_class = STR2SYM("clear");
|
||||
|
||||
} else if (raw[0] == _T(';')) {
|
||||
// "text" field, same as "raw" but with semicolon stripped
|
||||
wxString text(raw, 1, raw.size()-1);
|
||||
rb_hash_aset(ass_entry, STR2SYM("text"), rb_str_new2(text.mb_str(wxConvUTF8)));
|
||||
entry_class = STR2SYM("comment");
|
||||
} else if (raw[0] == _T('[')) {
|
||||
entry_class = STR2SYM("head");
|
||||
|
||||
} else if (section.Lower() == _T("[script info]")) {
|
||||
// assumed "info" class
|
||||
// first "key"
|
||||
wxString key = raw.BeforeFirst(_T(':'));
|
||||
rb_hash_aset(ass_entry, STR2SYM("key"), rb_str_new2(key.mb_str(wxConvUTF8)));
|
||||
|
||||
// then "value"
|
||||
wxString value = raw.AfterFirst(_T(':'));
|
||||
value.Trim(false);
|
||||
rb_hash_aset(ass_entry, STR2SYM("value"), rb_str_new2(value.mb_str(wxConvUTF8)));
|
||||
entry_class = STR2SYM("info");
|
||||
|
||||
} else if (raw.Left(7).Lower() == _T("format:")) {
|
||||
// TODO: parse the format line; just use a tokenizer
|
||||
entry_class = STR2SYM("format");
|
||||
|
||||
} else if (e->GetType() == ENTRY_DIALOGUE) {
|
||||
AssDialogue *dia = e->GetAsDialogue(e);
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("comment"), dia->Comment ? Qtrue : Qfalse);
|
||||
rb_hash_aset(ass_entry, STR2SYM("layer"), rb_int2inum(dia->Layer));
|
||||
rb_hash_aset(ass_entry, STR2SYM("start_time"), rb_int2inum(dia->Start.GetMS()));
|
||||
rb_hash_aset(ass_entry, STR2SYM("end_time"), rb_int2inum(dia->End.GetMS()));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("style"), rb_str_new2(dia->Style.mb_str(wxConvUTF8)));
|
||||
rb_hash_aset(ass_entry, STR2SYM("actor"), rb_str_new2(dia->Actor.mb_str(wxConvUTF8)));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_l"), rb_int2inum(dia->Margin[0]));
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_r"), rb_int2inum(dia->Margin[1]));
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_t"), rb_int2inum(dia->Margin[2]));
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_b"), rb_int2inum(dia->Margin[3]));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("effect"), rb_str_new2(dia->Effect.mb_str(wxConvUTF8)));
|
||||
// rb_hash_aset(ass_entry, STR2SYM("userdata"), rb_str_new(""));
|
||||
rb_hash_aset(ass_entry, STR2SYM("text"), rb_str_new2(dia->Text.mb_str(wxConvUTF8)));
|
||||
|
||||
entry_class = STR2SYM("dialogue");
|
||||
|
||||
} else if (e->GetType() == ENTRY_STYLE) {
|
||||
AssStyle *sty = e->GetAsStyle(e);
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("name"), rb_str_new2(sty->name.mb_str(wxConvUTF8)));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("fontname"), rb_str_new2(sty->font.mb_str(wxConvUTF8)));
|
||||
rb_hash_aset(ass_entry, STR2SYM("fontsize"), rb_int2inum(sty->fontsize));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("color1"), rb_str_new2(sty->primary.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||
rb_hash_aset(ass_entry, STR2SYM("color2"), rb_str_new2(sty->secondary.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||
rb_hash_aset(ass_entry, STR2SYM("color3"), rb_str_new2(sty->outline.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||
rb_hash_aset(ass_entry, STR2SYM("color4"), rb_str_new2(sty->shadow.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("bold"), rb_int2inum(sty->bold));
|
||||
rb_hash_aset(ass_entry, STR2SYM("italic"), rb_int2inum(sty->italic));
|
||||
rb_hash_aset(ass_entry, STR2SYM("underline"), rb_int2inum(sty->underline));
|
||||
rb_hash_aset(ass_entry, STR2SYM("strikeout"), rb_int2inum(sty->strikeout));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("scale_x"), rb_int2inum(sty->scalex));
|
||||
rb_hash_aset(ass_entry, STR2SYM("scale_y"), rb_int2inum(sty->scaley));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("spacing"), rb_int2inum(sty->spacing));
|
||||
rb_hash_aset(ass_entry, STR2SYM("angle"), rb_int2inum(sty->angle));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("borderstyle"), rb_int2inum(sty->borderstyle));
|
||||
rb_hash_aset(ass_entry, STR2SYM("outline"), rb_int2inum(sty->outline_w));
|
||||
rb_hash_aset(ass_entry, STR2SYM("shadow"), rb_int2inum(sty->shadow_w));
|
||||
rb_hash_aset(ass_entry, STR2SYM("align"), rb_int2inum(sty->alignment));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_l"), rb_int2inum(sty->Margin[0]));
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_r"), rb_int2inum(sty->Margin[1]));
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_t"), rb_int2inum(sty->Margin[2]));
|
||||
rb_hash_aset(ass_entry, STR2SYM("margin_b"), rb_int2inum(sty->Margin[3]));
|
||||
|
||||
rb_hash_aset(ass_entry, STR2SYM("encoding"), rb_int2inum(sty->encoding));
|
||||
|
||||
// From STS.h: "0: window, 1: video, 2: undefined (~window)"
|
||||
rb_hash_aset(ass_entry, STR2SYM("relative_to"), rb_int2inum(2));
|
||||
rb_hash_aset(ass_entry, STR2SYM("vertical"), Qfalse);
|
||||
|
||||
entry_class = STR2SYM("style");
|
||||
|
||||
} else {
|
||||
entry_class = STR2SYM("unknown");
|
||||
}
|
||||
// store class of item; last thing done for each class specific code must be pushing the class name
|
||||
rb_hash_aset(ass_entry, STR2SYM("class"), entry_class);
|
||||
|
||||
return ass_entry;
|
||||
}
|
||||
|
||||
AssEntry *RubyAssFile::RubyToAssEntry(VALUE ass_entry)
|
||||
{
|
||||
VALUE entry_class = rb_hash_aref(ass_entry, STR2SYM("class"));
|
||||
|
||||
wxString lclass(rb_id2name(SYM2ID(entry_class)), wxConvUTF8);
|
||||
lclass.MakeLower();
|
||||
|
||||
VALUE _section = rb_hash_aref(ass_entry, STR2SYM("section"));
|
||||
wxString section(StringValueCStr(_section), wxConvUTF8);
|
||||
|
||||
AssEntry *result;
|
||||
if (lclass == _T("clear")) {
|
||||
result = new AssEntry(_T(""));
|
||||
result->group = section;
|
||||
|
||||
} else if (lclass == _T("comment")) {
|
||||
// GETSTRING(raw, "text", "comment")
|
||||
VALUE _text = rb_hash_aref(ass_entry, STR2SYM("text"));
|
||||
wxString raw(StringValueCStr(_text), wxConvUTF8);
|
||||
raw.Prepend(_T(";"));
|
||||
result = new AssEntry(raw);
|
||||
result->group = section;
|
||||
|
||||
} else if (lclass == _T("head")) {
|
||||
result = new AssEntry(section);
|
||||
result->group = section;
|
||||
|
||||
} else if (lclass == _T("info")) {
|
||||
VALUE _key = rb_hash_aref(ass_entry, STR2SYM("key"));
|
||||
wxString key(StringValueCStr(_key), wxConvUTF8);
|
||||
VALUE _value = rb_hash_aref(ass_entry, STR2SYM("value"));
|
||||
wxString value(StringValueCStr(_value), wxConvUTF8);
|
||||
result = new AssEntry(wxString::Format(_T("%s: %s"), key.c_str(), value.c_str()));
|
||||
result->group = _T("[Script Info]"); // just so it can be read correctly back
|
||||
|
||||
} else if (lclass == _T("format")) {
|
||||
// ohshi- ...
|
||||
// *FIXME* maybe ignore the actual data and just put some default stuff based on section?
|
||||
result = new AssEntry(_T("Format: Auto4,Is,Broken"));
|
||||
result->group = section;
|
||||
|
||||
} else if (lclass == _T("style")) {
|
||||
VALUE _name = rb_hash_aref(ass_entry, STR2SYM("name"));
|
||||
wxString name(StringValueCStr(_name), wxConvUTF8);
|
||||
VALUE _fontname = rb_hash_aref(ass_entry, STR2SYM("fontname"));
|
||||
wxString fontname(StringValueCStr(_fontname), wxConvUTF8);
|
||||
VALUE _fontsize = rb_hash_aref(ass_entry, STR2SYM("fontsize"));
|
||||
float fontsize = rb_num2dbl(_fontsize);
|
||||
VALUE _color1 = rb_hash_aref(ass_entry, STR2SYM("color1"));
|
||||
wxString color1(StringValueCStr(_color1), wxConvUTF8);
|
||||
VALUE _color2 = rb_hash_aref(ass_entry, STR2SYM("color2"));
|
||||
wxString color2(StringValueCStr(_color2), wxConvUTF8);
|
||||
VALUE _color3 = rb_hash_aref(ass_entry, STR2SYM("color3"));
|
||||
wxString color3(StringValueCStr(_color3), wxConvUTF8);
|
||||
VALUE _color4 = rb_hash_aref(ass_entry, STR2SYM("color4"));
|
||||
wxString color4(StringValueCStr(_color4), wxConvUTF8);
|
||||
VALUE _bold = rb_hash_aref(ass_entry, STR2SYM("bold"));
|
||||
bool bold = rb_num2long(_bold) == 1;
|
||||
VALUE _italic = rb_hash_aref(ass_entry, STR2SYM("italic"));
|
||||
bool italic = rb_num2long(_italic) == 1;
|
||||
VALUE _underline = rb_hash_aref(ass_entry, STR2SYM("underline"));
|
||||
bool underline = rb_num2long(_underline) == 1;
|
||||
VALUE _strikeout = rb_hash_aref(ass_entry, STR2SYM("strikeout"));
|
||||
bool strikeout = rb_num2long(_strikeout) == 1;
|
||||
VALUE _scale_x = rb_hash_aref(ass_entry, STR2SYM("scale_x"));
|
||||
float scale_x = rb_num2dbl(_scale_x);
|
||||
VALUE _scale_y = rb_hash_aref(ass_entry, STR2SYM("scale_y"));
|
||||
float scale_y = rb_num2dbl(_scale_y);
|
||||
VALUE _spacing = rb_hash_aref(ass_entry, STR2SYM("spacing"));
|
||||
int spacing = rb_num2long(_spacing);
|
||||
VALUE _angle = rb_hash_aref(ass_entry, STR2SYM("angle"));
|
||||
float angle = rb_num2dbl(_angle);
|
||||
VALUE _borderstyle = rb_hash_aref(ass_entry, STR2SYM("borderstyle"));
|
||||
int borderstyle = rb_num2long(_borderstyle);
|
||||
VALUE _outline = rb_hash_aref(ass_entry, STR2SYM("outline"));
|
||||
float outline = rb_num2dbl(_outline);
|
||||
VALUE _shadow = rb_hash_aref(ass_entry, STR2SYM("shadow"));
|
||||
float shadow = rb_num2dbl(_shadow);
|
||||
VALUE _align = rb_hash_aref(ass_entry, STR2SYM("align"));
|
||||
int align = rb_num2long(_align);
|
||||
VALUE _margin_l = rb_hash_aref(ass_entry, STR2SYM("margin_l"));
|
||||
int margin_l = rb_num2long(_margin_l);
|
||||
VALUE _margin_r = rb_hash_aref(ass_entry, STR2SYM("margin_r"));
|
||||
int margin_r = rb_num2long(_margin_r);
|
||||
VALUE _margin_t = rb_hash_aref(ass_entry, STR2SYM("margin_t"));
|
||||
int margin_t = rb_num2long(_margin_t);
|
||||
VALUE _margin_b = rb_hash_aref(ass_entry, STR2SYM("margin_b"));
|
||||
int margin_b = rb_num2long(_margin_b);
|
||||
VALUE _encoding = rb_hash_aref(ass_entry, STR2SYM("encoding"));
|
||||
int encoding = rb_num2long(_encoding);
|
||||
// leaving out relative_to and vertical
|
||||
|
||||
AssStyle *sty = new AssStyle();
|
||||
sty->name = name;
|
||||
sty->font = fontname;
|
||||
sty->fontsize = fontsize;
|
||||
sty->primary.Parse(color1);
|
||||
sty->secondary.Parse(color2);
|
||||
sty->outline.Parse(color3);
|
||||
sty->shadow.Parse(color4);
|
||||
sty->bold = bold;
|
||||
sty->italic = italic;
|
||||
sty->underline = underline;
|
||||
sty->strikeout = strikeout;
|
||||
sty->scalex = scale_x;
|
||||
sty->scaley = scale_y;
|
||||
sty->spacing = spacing;
|
||||
sty->angle = angle;
|
||||
sty->borderstyle = borderstyle;
|
||||
sty->outline_w = outline;
|
||||
sty->shadow_w = shadow;
|
||||
sty->alignment = align;
|
||||
sty->Margin[0] = margin_l;
|
||||
sty->Margin[1] = margin_r;
|
||||
sty->Margin[2] = margin_t;
|
||||
sty->Margin[3] = margin_b;
|
||||
sty->encoding = encoding;
|
||||
sty->UpdateData();
|
||||
|
||||
result = sty;
|
||||
} else if (lclass == _T("styleex")) {
|
||||
rb_raise(rb_eRuntimeError, "Found line with class 'styleex' which is not supported. Wait until AS5 is a reality.");
|
||||
|
||||
} else if (lclass == _T("dialogue")) {
|
||||
VALUE _comment = rb_hash_aref(ass_entry, STR2SYM("comment"));
|
||||
bool comment = _comment == Qfalse ? false : true;
|
||||
VALUE _layer = rb_hash_aref(ass_entry, STR2SYM("layer"));
|
||||
int layer = rb_num2long(_layer);
|
||||
VALUE _start_time = rb_hash_aref(ass_entry, STR2SYM("start_time"));
|
||||
int start_time = rb_num2long(_start_time);
|
||||
VALUE _end_time = rb_hash_aref(ass_entry, STR2SYM("end_time"));
|
||||
int end_time = rb_num2long(_end_time);
|
||||
VALUE _style = rb_hash_aref(ass_entry, STR2SYM("style"));
|
||||
wxString style(StringValueCStr(_style), wxConvUTF8);
|
||||
VALUE _actor = rb_hash_aref(ass_entry, STR2SYM("actor"));
|
||||
wxString actor(StringValueCStr(_actor), wxConvUTF8);
|
||||
VALUE _margin_l = rb_hash_aref(ass_entry, STR2SYM("margin_l"));
|
||||
int margin_l = rb_num2long(_margin_l);
|
||||
VALUE _margin_r = rb_hash_aref(ass_entry, STR2SYM("margin_r"));
|
||||
int margin_r = rb_num2long(_margin_r);
|
||||
VALUE _margin_t = rb_hash_aref(ass_entry, STR2SYM("margin_t"));
|
||||
int margin_t = rb_num2long(_margin_t);
|
||||
VALUE _margin_b = rb_hash_aref(ass_entry, STR2SYM("margin_b"));
|
||||
int margin_b = rb_num2long(_margin_b);
|
||||
VALUE _effect = rb_hash_aref(ass_entry, STR2SYM("effect"));
|
||||
wxString effect(StringValueCStr(_effect), wxConvUTF8);
|
||||
VALUE _text = rb_hash_aref(ass_entry, STR2SYM("text"));
|
||||
wxString text(StringValueCStr(_text), wxConvUTF8);
|
||||
//GETSTRING(userdata, "userdata", "dialogue")
|
||||
|
||||
AssDialogue *dia = new AssDialogue();
|
||||
dia->Comment = comment;
|
||||
dia->Layer = layer;
|
||||
dia->Start.SetMS(start_time);
|
||||
dia->End.SetMS(end_time);
|
||||
dia->Style = style;
|
||||
dia->Actor = actor;
|
||||
dia->Margin[0] = margin_l;
|
||||
dia->Margin[1] = margin_r;
|
||||
dia->Margin[2] = margin_t;
|
||||
dia->Margin[3] = margin_b;
|
||||
dia->Effect = effect;
|
||||
dia->Text = text;
|
||||
dia->UpdateData();
|
||||
|
||||
result = dia;
|
||||
|
||||
} else {
|
||||
rb_raise(rb_eRuntimeError, "Found line with unknown class: %s", lclass.mb_str(wxConvUTF8).data());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updates the AssFile with data returned by macro/filter
|
||||
// If the first line is dialogue we leave header from the original (styles, info, etc)
|
||||
void RubyAssFile::RubyUpdateAssFile(VALUE subtitles)
|
||||
{
|
||||
int size = RARRAY(subtitles)->len;
|
||||
|
||||
if(size <= 0) return; // empty - leave the original
|
||||
|
||||
VALUE rbEntry;
|
||||
AssEntry* new_entry;
|
||||
int status = 0;
|
||||
do {
|
||||
rbEntry = rb_ary_shift(subtitles);
|
||||
new_entry = reinterpret_cast<AssEntry*>(rb_protect(rb2AssWrapper, rbEntry, &status));
|
||||
--size;
|
||||
}while(status != 0); // broken lines at the beginning?
|
||||
|
||||
entryIter e = ass->Line.begin();
|
||||
if(new_entry->GetType() == ENTRY_DIALOGUE) // check if the first line is a dialogue
|
||||
{
|
||||
while(e != ass->Line.end() && (*e)->GetType() != ENTRY_DIALOGUE) ++e;
|
||||
}
|
||||
while(e != ass->Line.end()) // delete the old lines backwards
|
||||
{
|
||||
delete (*e);
|
||||
e = ass->Line.erase(e);
|
||||
}
|
||||
ass->Line.push_back(new_entry);
|
||||
for(int i = 0; i < size; i++) // insert new lines
|
||||
{
|
||||
rbEntry = rb_ary_shift(subtitles);
|
||||
new_entry = reinterpret_cast<AssEntry*>(rb_protect(rb2AssWrapper, rbEntry, &status));
|
||||
if(status == 0) ass->Line.push_back(new_entry);
|
||||
}
|
||||
|
||||
if (can_set_undo) {
|
||||
AssFile::top->FlagAsModified(_T(""));
|
||||
}
|
||||
}
|
||||
|
||||
int RubyAssFile::RubyParseTagData()
|
||||
{
|
||||
// TODO
|
||||
return 1;
|
||||
}
|
||||
|
||||
int RubyAssFile::RubyUnparseTagData()
|
||||
{
|
||||
// TODO
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int RubyAssFile::RubySetUndoPoint()
|
||||
{
|
||||
// I can think of two things to do here:
|
||||
// One is to read in all of the subs from Ruby and cobvert back to AssEntry, inserting it into the file, then set undo point
|
||||
// Another is to just scrap it and only support one undo point per macro execution, and simply save the message, using it
|
||||
// to set the description when the actual undo point it set after execution.
|
||||
// -jfs
|
||||
return 0;
|
||||
}
|
||||
|
||||
RubyAssFile::~RubyAssFile()
|
||||
{
|
||||
RubyObjects::Get()->Unregister(rbAssFile);
|
||||
}
|
||||
|
||||
RubyAssFile::RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo)
|
||||
: ass(_ass)
|
||||
, can_modify(_can_modify)
|
||||
, can_set_undo(_can_set_undo)
|
||||
{
|
||||
rb_gc_disable();
|
||||
rbAssFile = rb_ary_new2(ass->Line.size());
|
||||
RubyObjects::Get()->Register(rbAssFile);
|
||||
|
||||
std::list<AssEntry*>::iterator entry;
|
||||
int status;
|
||||
for(entry = ass->Line.begin(); entry != ass->Line.end(); ++entry)
|
||||
{
|
||||
VALUE res = rb_protect(rbAss2RbWrapper, reinterpret_cast<VALUE>(*entry), &status);
|
||||
if(status == 0) rb_ary_push(rbAssFile, res);
|
||||
}
|
||||
rb_gc_enable();
|
||||
// TODO
|
||||
//rb_define_module_function(RubyScript::RubyAegisub, "parse_tag_data",reinterpret_cast<RB_HOOK>(&RubyParseTagData), 1);
|
||||
//rb_define_module_function(RubyScript::RubyAegisub, "unparse_tag_data",reinterpret_cast<RB_HOOK>(&RubyUnparseTagData), 1);
|
||||
//rb_define_module_function(RubyScript::RubyAegisub, "set_undo_point",reinterpret_cast<RB_HOOK>(&RubySetUndoPoint), 1);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // WITH_RUBY
|
|
@ -1,596 +0,0 @@
|
|||
// Copyright (c) 2006, 2007, Niels Martin Hansen, Patryk Pomykalski
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:pomyk@go2.pl
|
||||
//
|
||||
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef WITH_RUBY
|
||||
#include "auto4_ruby.h"
|
||||
#include <ruby.h>
|
||||
#include <wx/window.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/validate.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Automation4 {
|
||||
|
||||
|
||||
// RubyConfigDialogControl
|
||||
|
||||
RubyConfigDialogControl::RubyConfigDialogControl(VALUE opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("name"));
|
||||
name_sym = val;
|
||||
if(TYPE(val) == T_STRING) {
|
||||
name = wxString(StringValueCStr(val), wxConvUTF8);
|
||||
} else if(TYPE(val) == T_SYMBOL) {
|
||||
name = wxString(rb_id2name(SYM2ID(val)), wxConvUTF8);
|
||||
} else name = _T("");
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("x"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
x = FIX2INT(val);
|
||||
if (x < 0) x = 0;
|
||||
}
|
||||
else x = 0;
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("y"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
y = FIX2INT(val);
|
||||
if (y < 0) y = 0;
|
||||
}
|
||||
else y = 0;
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("width"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
width = FIX2INT(val);
|
||||
if (width < 1) width = 1;
|
||||
}
|
||||
else width = 1;
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("height"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
height = FIX2INT(val);
|
||||
if (height < 1) width = 1;
|
||||
}
|
||||
else height = 1;
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("hint"));
|
||||
if(TYPE(val) == T_STRING)
|
||||
hint = wxString(StringValueCStr(val), wxConvUTF8);
|
||||
else hint = _T("");
|
||||
|
||||
wxLogDebug(_T("created control: '%s', (%d,%d)(%d,%d), '%s'"), name.c_str(), x, y, width, height, hint.c_str());
|
||||
}
|
||||
|
||||
namespace RubyControl {
|
||||
|
||||
// Label
|
||||
|
||||
class Label : public RubyConfigDialogControl {
|
||||
public:
|
||||
wxString label;
|
||||
|
||||
Label(){};
|
||||
Label(VALUE opts)
|
||||
: RubyConfigDialogControl(opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("label"));
|
||||
if(TYPE(val) == T_STRING)
|
||||
label = wxString(StringValueCStr(val), wxConvUTF8);
|
||||
else label = _T("");
|
||||
}
|
||||
|
||||
virtual ~Label() { }
|
||||
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
return cw = new wxStaticText(parent, -1, label);
|
||||
}
|
||||
|
||||
void ControlReadBack()
|
||||
{
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
VALUE RubyReadBack()
|
||||
{
|
||||
return Qnil;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Basic edit
|
||||
|
||||
class Edit : public RubyConfigDialogControl {
|
||||
public:
|
||||
wxString text;
|
||||
|
||||
Edit(){};
|
||||
Edit(VALUE opts)
|
||||
: RubyConfigDialogControl(opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("text"));
|
||||
if(TYPE(val) == T_STRING)
|
||||
text = wxString(StringValueCStr(val), wxConvUTF8);
|
||||
else text = _T("");
|
||||
}
|
||||
|
||||
virtual ~Edit() { }
|
||||
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
return cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, 0);
|
||||
}
|
||||
|
||||
void ControlReadBack()
|
||||
{
|
||||
text = ((wxTextCtrl*)cw)->GetValue();
|
||||
}
|
||||
|
||||
VALUE RubyReadBack()
|
||||
{
|
||||
// if(text.IsEmpty()) return rb_str_new("", 0);
|
||||
return rb_str_new2(text.mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Multiline edit
|
||||
|
||||
class Textbox : public Edit {
|
||||
public:
|
||||
|
||||
Textbox(){};
|
||||
Textbox(VALUE opts)
|
||||
: Edit(opts)
|
||||
{
|
||||
// Nothing more
|
||||
}
|
||||
|
||||
virtual ~Textbox() { }
|
||||
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
|
||||
cw->SetMinSize(wxSize(0, 30));
|
||||
return cw;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Integer only edit
|
||||
|
||||
class IntEdit : public Edit {
|
||||
public:
|
||||
int value;
|
||||
bool hasspin;
|
||||
int min, max;
|
||||
|
||||
IntEdit(){};
|
||||
IntEdit(VALUE opts)
|
||||
: Edit(opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("value"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
value = FIX2INT(val);
|
||||
}
|
||||
|
||||
hasspin = false;
|
||||
val = rb_hash_aref(opts, STR2SYM("min"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
min = FIX2INT(val);
|
||||
}
|
||||
else return;
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("max"));
|
||||
if(TYPE(val) == T_FIXNUM) {
|
||||
max = FIX2INT(val);
|
||||
hasspin = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~IntEdit() { }
|
||||
|
||||
typedef wxValidator IntTextValidator; // TODO
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
if (hasspin) {
|
||||
return cw = new wxSpinCtrl(parent, -1, wxString::Format(_T("%d"), value), wxDefaultPosition, wxDefaultSize, min, max, value);
|
||||
} else {
|
||||
return cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, 0); //, IntTextValidator());
|
||||
}
|
||||
}
|
||||
|
||||
void ControlReadBack()
|
||||
{
|
||||
if (hasspin) {
|
||||
value = ((wxSpinCtrl*)cw)->GetValue();
|
||||
} else {
|
||||
long newval;
|
||||
text = ((wxTextCtrl*)cw)->GetValue();
|
||||
if (text.ToLong(&newval)) {
|
||||
value = newval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VALUE RubyReadBack()
|
||||
{
|
||||
return INT2FIX(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Float only edit
|
||||
|
||||
class FloatEdit : public Edit {
|
||||
public:
|
||||
float value;
|
||||
// FIXME: Can't support spin button atm
|
||||
|
||||
FloatEdit(){};
|
||||
FloatEdit(VALUE opts)
|
||||
: Edit(opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("value"));
|
||||
if(TYPE(val) == T_FLOAT) {
|
||||
value = NUM2DBL(val);
|
||||
} else if (TYPE(val) == T_FIXNUM) {
|
||||
value = FIX2INT(val);
|
||||
}
|
||||
// TODO: spin button support
|
||||
}
|
||||
|
||||
virtual ~FloatEdit() { }
|
||||
|
||||
typedef wxValidator FloatTextValidator;
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
return cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, 0); //, FloatTextValidator());
|
||||
}
|
||||
|
||||
void ControlReadBack()
|
||||
{
|
||||
double newval;
|
||||
text = ((wxTextCtrl*)cw)->GetValue();
|
||||
if (text.ToDouble(&newval)) {
|
||||
value = newval;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE RubyReadBack()
|
||||
{
|
||||
return rb_float_new(value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Dropdown
|
||||
|
||||
class Dropdown : public RubyConfigDialogControl {
|
||||
public:
|
||||
wxArrayString items;
|
||||
wxString value;
|
||||
|
||||
Dropdown(){};
|
||||
Dropdown(VALUE opts)
|
||||
: RubyConfigDialogControl(opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("value"));
|
||||
if(TYPE(val) == T_STRING)
|
||||
value = wxString(StringValueCStr(val), wxConvUTF8);
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("items"));
|
||||
if(TYPE(val) == T_ARRAY)
|
||||
{
|
||||
long len = RARRAY(val)->len;
|
||||
VALUE *ptr = RARRAY(val)->ptr;
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
if(TYPE(ptr[i]) == T_STRING)
|
||||
items.Add(wxString(StringValueCStr(ptr[i]), wxConvUTF8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Dropdown() { }
|
||||
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
return cw = new wxComboBox(parent, -1, value, wxDefaultPosition, wxDefaultSize, items, wxCB_READONLY);
|
||||
}
|
||||
|
||||
void ControlReadBack()
|
||||
{
|
||||
value = ((wxComboBox*)cw)->GetValue();
|
||||
}
|
||||
|
||||
VALUE RubyReadBack()
|
||||
{
|
||||
return rb_str_new2(value.mb_str(wxConvUTF8));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Checkbox
|
||||
|
||||
class Checkbox : public RubyConfigDialogControl {
|
||||
public:
|
||||
wxString label;
|
||||
bool value;
|
||||
|
||||
Checkbox(){};
|
||||
Checkbox(VALUE opts)
|
||||
: RubyConfigDialogControl(opts)
|
||||
{
|
||||
VALUE val = rb_hash_aref(opts, STR2SYM("label"));
|
||||
if(TYPE(val) == T_STRING)
|
||||
label = wxString(StringValueCStr(val), wxConvUTF8);
|
||||
|
||||
val = rb_hash_aref(opts, STR2SYM("value"));
|
||||
if(val == Qtrue) value = true;
|
||||
else value = false;
|
||||
}
|
||||
|
||||
virtual ~Checkbox() { }
|
||||
|
||||
wxControl *Create(wxWindow *parent)
|
||||
{
|
||||
cw = new wxCheckBox(parent, -1, label);
|
||||
((wxCheckBox*)cw)->SetValue(value);
|
||||
return cw;
|
||||
}
|
||||
|
||||
void ControlReadBack()
|
||||
{
|
||||
value = ((wxCheckBox*)cw)->GetValue();
|
||||
}
|
||||
|
||||
VALUE RubyReadBack()
|
||||
{
|
||||
if(value) return Qtrue;
|
||||
return Qfalse;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
// RubyConfigDialog
|
||||
|
||||
RubyConfigDialog::RubyConfigDialog(VALUE config, VALUE btn_data, bool include_buttons)
|
||||
: use_buttons(include_buttons)
|
||||
{
|
||||
wxLogDebug(_T("creating RubyConfigDialog, this addr is %p"), this);
|
||||
button_pushed = 0;
|
||||
|
||||
if(include_buttons && TYPE(btn_data) == T_ARRAY)
|
||||
{
|
||||
long len = RARRAY(btn_data)->len;
|
||||
VALUE *ptr = RARRAY(btn_data)->ptr;
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
if(rb_respond_to(ptr[i], rb_intern("to_s")))
|
||||
{
|
||||
ptr[i] = rb_funcall(ptr[i], rb_intern("to_s"), 0);
|
||||
wxString s(StringValueCStr(ptr[i]), wxConvUTF8);
|
||||
buttons.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(TYPE(config) != T_ARRAY) {
|
||||
if(rb_respond_to(config, rb_intern("to_ary")))
|
||||
config = rb_funcall(config, rb_intern("to_ary"), 0);
|
||||
else throw "Cannot create config dialog from something non-table";
|
||||
}
|
||||
|
||||
long len = RARRAY(config)->len;
|
||||
VALUE *ptr = RARRAY(config)->ptr;
|
||||
for(int i = 0; i < len; i++)
|
||||
{
|
||||
if(TYPE(ptr[i]) != T_HASH)
|
||||
continue; // skip invalid entry
|
||||
|
||||
VALUE ctrlclass = rb_hash_aref(ptr[i], STR2SYM("class"));
|
||||
|
||||
const char *cls_name;
|
||||
if (TYPE(ctrlclass) == T_SYMBOL) {
|
||||
cls_name = rb_id2name(SYM2ID(ctrlclass));
|
||||
} else if (TYPE(ctrlclass) == T_STRING) {
|
||||
cls_name = StringValueCStr(ctrlclass);
|
||||
} else continue;
|
||||
wxString controlclass(cls_name, wxConvUTF8);
|
||||
|
||||
RubyConfigDialogControl *ctl;
|
||||
|
||||
// Check control class and create relevant control
|
||||
if (controlclass == _T("label")) {
|
||||
ctl = new RubyControl::Label(ptr[i]);
|
||||
} else if (controlclass == _T("edit")) {
|
||||
ctl = new RubyControl::Edit(ptr[i]);
|
||||
} else if (controlclass == _T("intedit")) {
|
||||
ctl = new RubyControl::IntEdit(ptr[i]);
|
||||
} else if (controlclass == _T("floatedit")) {
|
||||
ctl = new RubyControl::FloatEdit(ptr[i]);
|
||||
} else if (controlclass == _T("textbox")) {
|
||||
ctl = new RubyControl::Textbox(ptr[i]);
|
||||
} else if (controlclass == _T("dropdown")) {
|
||||
ctl = new RubyControl::Dropdown(ptr[i]);
|
||||
} else if (controlclass == _T("checkbox")) {
|
||||
ctl = new RubyControl::Checkbox(ptr[i]);
|
||||
} else if (controlclass == _T("color")) {
|
||||
// FIXME
|
||||
ctl = new RubyControl::Edit(ptr[i]);
|
||||
} else if (controlclass == _T("coloralpha")) {
|
||||
// FIXME
|
||||
ctl = new RubyControl::Edit(ptr[i]);
|
||||
} else if (controlclass == _T("alpha")) {
|
||||
// FIXME
|
||||
ctl = new RubyControl::Edit(ptr[i]);
|
||||
} else continue; // skip
|
||||
|
||||
controls.push_back(ctl);
|
||||
}
|
||||
}
|
||||
|
||||
RubyConfigDialog::~RubyConfigDialog()
|
||||
{
|
||||
for (size_t i = 0; i < controls.size(); ++i)
|
||||
delete controls[i];
|
||||
}
|
||||
|
||||
wxWindow* RubyConfigDialog::CreateWindow(wxWindow *parent)
|
||||
{
|
||||
wxWindow *w = new wxPanel(parent);
|
||||
wxGridBagSizer *s = new wxGridBagSizer(4, 4);
|
||||
|
||||
for (size_t i = 0; i < controls.size(); ++i) {
|
||||
RubyConfigDialogControl *c = controls[i];
|
||||
c->Create(w);
|
||||
if (dynamic_cast<RubyControl::Label*>(c)) {
|
||||
s->Add(c->cw, wxGBPosition(c->y, c->x), wxGBSpan(c->height, c->width), wxALIGN_CENTRE_VERTICAL|wxALIGN_LEFT);
|
||||
} else {
|
||||
s->Add(c->cw, wxGBPosition(c->y, c->x), wxGBSpan(c->height, c->width), wxEXPAND);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_buttons) {
|
||||
wxStdDialogButtonSizer *bs = new wxStdDialogButtonSizer();
|
||||
if (buttons.size() > 0) {
|
||||
wxLogDebug(_T("creating user buttons"));
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
wxLogDebug(_T("button '%s' gets id %d"), buttons[i].c_str(), 1001+(wxWindowID)i);
|
||||
bs->Add(new wxButton(w, 1001+(wxWindowID)i, buttons[i]));
|
||||
}
|
||||
} else {
|
||||
wxLogDebug(_T("creating default buttons"));
|
||||
bs->Add(new wxButton(w, wxID_OK));
|
||||
bs->Add(new wxButton(w, wxID_CANCEL));
|
||||
}
|
||||
bs->Realize();
|
||||
|
||||
button_event = new ButtonEventHandler();
|
||||
button_event->button_pushed = &button_pushed;
|
||||
// passing button_event as userdata because wx will then delete it
|
||||
w->Connect(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(RubyConfigDialog::ButtonEventHandler::OnButtonPush), button_event, button_event);
|
||||
wxLogDebug(_T("set event handler, this addr is %p"), this);
|
||||
|
||||
wxBoxSizer *ms = new wxBoxSizer(wxVERTICAL);
|
||||
ms->Add(s, 0, wxBOTTOM, 5);
|
||||
ms->Add(bs);
|
||||
w->SetSizerAndFit(ms);
|
||||
} else {
|
||||
w->SetSizerAndFit(s);
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
VALUE RubyConfigDialog::RubyReadBack()
|
||||
{
|
||||
VALUE cfg = rb_hash_new();
|
||||
|
||||
for (size_t i = 0; i < controls.size(); ++i) {
|
||||
VALUE v = controls[i]->RubyReadBack();
|
||||
if(v != Qnil)
|
||||
rb_hash_aset(cfg, controls[i]->name_sym, v);
|
||||
}
|
||||
if (use_buttons) {
|
||||
VALUE res = rb_ary_new();
|
||||
|
||||
wxLogDebug(_T("reading back button_pushed"));
|
||||
int btn = button_pushed;
|
||||
if (btn == 0) {
|
||||
wxLogDebug(_T("was zero, cancelled"));
|
||||
// Always cancel/closed
|
||||
rb_ary_push(res, Qfalse);
|
||||
} else {
|
||||
wxLogDebug(_T("nonzero, something else: %d"), btn);
|
||||
if (buttons.size() > 0) {
|
||||
wxLogDebug(_T("user button: %s"), buttons[btn-1].c_str());
|
||||
// button_pushed is index+1 to reserve 0 for Cancel
|
||||
rb_ary_push(res, rb_str_new2(buttons[btn-1].mb_str(wxConvUTF8)));
|
||||
} else {
|
||||
wxLogDebug(_T("default button, must be Ok"));
|
||||
// Cancel case already covered, must be Ok then
|
||||
rb_ary_push(res, Qtrue);
|
||||
}
|
||||
}
|
||||
rb_ary_push(res, cfg); // return array [button, hash with config]
|
||||
return res;
|
||||
}
|
||||
|
||||
return cfg; // if no buttons return only hash with config
|
||||
}
|
||||
|
||||
void RubyConfigDialog::ReadBack()
|
||||
{
|
||||
for (size_t i = 0; i < controls.size(); ++i) {
|
||||
controls[i]->ControlReadBack();
|
||||
}
|
||||
}
|
||||
|
||||
void RubyConfigDialog::ButtonEventHandler::OnButtonPush(wxCommandEvent &evt)
|
||||
{
|
||||
// Let button_pushed == 0 mean "cancelled", such that pushing Cancel or closing the dialog
|
||||
// will both result in button_pushed == 0
|
||||
if (evt.GetId() == wxID_OK) {
|
||||
wxLogDebug(_T("was wxID_OK"));
|
||||
*button_pushed = 1;
|
||||
} else if (evt.GetId() == wxID_CANCEL) {
|
||||
wxLogDebug(_T("was wxID_CANCEL"));
|
||||
*button_pushed = 0;
|
||||
} else {
|
||||
wxLogDebug(_T("was user button"));
|
||||
// Therefore, when buttons are numbered from 1001 to 1000+n, make sure to set it to i+1
|
||||
*button_pushed = evt.GetId() - 1000;
|
||||
evt.SetId(wxID_OK); // hack to make sure the dialog will be closed
|
||||
}
|
||||
wxLogDebug(_T("button_pushed set to %d"), *button_pushed);
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // WITH_RUBY
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) 2007, Patryk Pomykalski
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// AEGISUB
|
||||
//
|
||||
// Website: http://aegisub.cellosoft.com
|
||||
// Contact: mailto:pomyk@go2.pl
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "auto4_base.h"
|
||||
|
||||
namespace Automation4 {
|
||||
// Factory class for Ruby scripts
|
||||
class RubyScriptFactory : public ScriptFactory {
|
||||
public:
|
||||
RubyScriptFactory();
|
||||
~RubyScriptFactory();
|
||||
Script* Produce(const wxString &filename) const;
|
||||
};
|
||||
};
|
|
@ -112,18 +112,6 @@
|
|||
|
||||
|
||||
|
||||
/////////////// LOW PRIORITY ////////////
|
||||
|
||||
// Enable Perl scripting
|
||||
// Requires: perl library (ActivePerl comes with one for Visual C++ under lib\core\)
|
||||
//#define WITH_PERL
|
||||
|
||||
// Enable PerlConsole (a debug tool for the perl engine)
|
||||
// You don't want it
|
||||
//#define WITH_PERLCONSOLE
|
||||
|
||||
|
||||
|
||||
///////////// NOT RECOMMENDED /////////////
|
||||
|
||||
// Enable FontConfig
|
||||
|
@ -136,11 +124,6 @@
|
|||
//#define WITH_LIBASS
|
||||
|
||||
|
||||
// Enable Ruby support for Automation
|
||||
// Requires: Ruby 1.9
|
||||
//#define WITH_RUBY
|
||||
|
||||
|
||||
// Enable PortAudio audio player
|
||||
// Requires PortAudio release 18
|
||||
//#define WITH_PORTAUDIO
|
||||
|
|
Loading…
Reference in New Issue