The removal of Auto4 Ruby and Auto4 Perl code. Updates #665 and #938.

Originally committed to SVN as r3205.
This commit is contained in:
Niels Martin Hansen 2009-07-23 01:39:57 +00:00
parent af5df13f00
commit 31657f94da
28 changed files with 0 additions and 5473 deletions

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -1,6 +0,0 @@
package Aegisub::PerlConsole;
use Exporter 'import';
@EXPORT = qw( echo register_console );
1;

View File

@ -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;

View File

@ -1,6 +0,0 @@
package Aegisub::Script;
use Exporter 'import';
@EXPORT = qw( register_macro set_info );
1;

View File

@ -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)));
}

View File

@ -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

View File

@ -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

View File

@ -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.

View 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"

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));\
}\
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
};
};

View File

@ -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