mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-11 13:17:00 -04:00
made a start with script functions
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@62 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+2
-2
@@ -76,8 +76,8 @@ shared_ptr<Field> read_new<Field>(Reader& reader) {
|
|||||||
Style::Style(const FieldP& field)
|
Style::Style(const FieldP& field)
|
||||||
: fieldP(field)
|
: fieldP(field)
|
||||||
, z_index(0)
|
, z_index(0)
|
||||||
, left(0), width (1)
|
, left(0), width (0)
|
||||||
, top (0), height(1)
|
, top (0), height(0)
|
||||||
, visible(true)
|
, visible(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|||||||
@@ -546,6 +546,12 @@ void SetWindow::onChildMenu(wxCommandEvent& ev) {
|
|||||||
current_panel->onCommand(ev.GetId());
|
current_panel->onCommand(ev.GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetWindow::onIdle(wxIdleEvent& ev) {
|
||||||
|
// Stuff that must be done in the main thread
|
||||||
|
handle_pending_errors();
|
||||||
|
// showUpdateDialog(this);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Event table
|
// ----------------------------------------------------------------------------- : Event table
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(SetWindow, wxFrame)
|
BEGIN_EVENT_TABLE(SetWindow, wxFrame)
|
||||||
@@ -584,7 +590,6 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame)
|
|||||||
// EVT_FIND_REPLACE (wxID_ANY, SetWindow::onReplace)
|
// EVT_FIND_REPLACE (wxID_ANY, SetWindow::onReplace)
|
||||||
// EVT_FIND_REPLACE_ALL(wxID_ANY, SetWindow::onReplaceAll)
|
// EVT_FIND_REPLACE_ALL(wxID_ANY, SetWindow::onReplaceAll)
|
||||||
EVT_CLOSE ( SetWindow::onClose)
|
EVT_CLOSE ( SetWindow::onClose)
|
||||||
// EVT_TIMER (wxID_ANY, SetWindow::onTick)
|
EVT_IDLE ( SetWindow::onIdle)
|
||||||
// EVT_IDLE ( SetWindow::onIdle)
|
|
||||||
EVT_CARD_SELECT (wxID_ANY, SetWindow::onCardSelect)
|
EVT_CARD_SELECT (wxID_ANY, SetWindow::onCardSelect)
|
||||||
END_EVENT_TABLE ()
|
END_EVENT_TABLE ()
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ class SetWindow : public wxFrame, public SetView {
|
|||||||
// --------------------------------------------------- : Window events - other
|
// --------------------------------------------------- : Window events - other
|
||||||
|
|
||||||
void onChildMenu (wxCommandEvent&);
|
void onChildMenu (wxCommandEvent&);
|
||||||
|
|
||||||
|
void onIdle (wxIdleEvent&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
|||||||
+4
-1
@@ -178,7 +178,7 @@
|
|||||||
Name="VCCustomBuildTool"/>
|
Name="VCCustomBuildTool"/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCLinkerTool"
|
Name="VCLinkerTool"
|
||||||
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26ud.lib wxmsw26ud_core.lib wxjpegd.lib wxpngd.lib wxtiffd.lib wxzlibd.lib"
|
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26ud.lib wxmsw26ud_core.lib wxjpegd.lib wxpngd.lib wxtiffd.lib wxzlibd.lib wxregexd.lib"
|
||||||
OutputFile="$(OutDir)/mse.exe"
|
OutputFile="$(OutDir)/mse.exe"
|
||||||
LinkIncremental="2"
|
LinkIncremental="2"
|
||||||
IgnoreDefaultLibraryNames="libcd.lib,libcid.lib"
|
IgnoreDefaultLibraryNames="libcd.lib,libcid.lib"
|
||||||
@@ -1131,6 +1131,9 @@
|
|||||||
<File
|
<File
|
||||||
RelativePath=".\script\dependency.hpp">
|
RelativePath=".\script\dependency.hpp">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\script\functions.cpp">
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\script\image.cpp">
|
RelativePath=".\script\image.cpp">
|
||||||
<FileConfiguration
|
<FileConfiguration
|
||||||
|
|||||||
@@ -74,7 +74,9 @@ void DataViewer::setStyles(IndexMap<FieldP,StyleP>& styles) {
|
|||||||
// create viewers
|
// create viewers
|
||||||
viewers.clear();
|
viewers.clear();
|
||||||
FOR_EACH(s, styles) {
|
FOR_EACH(s, styles) {
|
||||||
if (s->visible || s->visible.isScripted()) {
|
if ((s->visible || s->visible.isScripted()) &&
|
||||||
|
(s->width || s->width .isScripted()) &&
|
||||||
|
(s->height || s->height .isScripted())) {
|
||||||
// no need to make a viewer for things that are always invisible
|
// no need to make a viewer for things that are always invisible
|
||||||
viewers.push_back(makeViewer(s));
|
viewers.push_back(makeViewer(s));
|
||||||
// REMOVEME //TODO //%%%
|
// REMOVEME //TODO //%%%
|
||||||
|
|||||||
+22
-3
@@ -183,7 +183,7 @@ ScriptValueP Context::getVariable(const String& name) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptValueP Context::getVariableOrNil(const String& name) {
|
ScriptValueP Context::getVariableOpt(const String& name) {
|
||||||
return variables[stringToVariable(name)].value;
|
return variables[stringToVariable(name)].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +246,25 @@ void instrUnary (UnaryInstructionType i, ScriptValueP& a) {
|
|||||||
} \
|
} \
|
||||||
break
|
break
|
||||||
|
|
||||||
|
/// Composition of two functions
|
||||||
|
class ScriptCompose : public ScriptValue {
|
||||||
|
public:
|
||||||
|
ScriptCompose(ScriptValueP a, ScriptValueP b) : a(a), b(b) {}
|
||||||
|
|
||||||
|
virtual ScriptType type() const { return SCRIPT_FUNCTION; }
|
||||||
|
virtual String typeName() const { return _("replace_rule"); }
|
||||||
|
virtual ScriptValueP eval(Context& ctx) const {
|
||||||
|
ctx.setVariable(_("input"), a->eval(ctx));
|
||||||
|
return b->eval(ctx);
|
||||||
|
}
|
||||||
|
virtual ScriptValueP dependencies(Context& ctx, const Dependency& dep) const {
|
||||||
|
ctx.setVariable(_("input"), a->dependencies(ctx, dep));
|
||||||
|
return b->dependencies(ctx, dep);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
ScriptValueP a,b;
|
||||||
|
};
|
||||||
|
|
||||||
void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& b) {
|
void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP& b) {
|
||||||
ScriptType at = a->type(), bt = b->type();
|
ScriptType at = a->type(), bt = b->type();
|
||||||
switch (i) {
|
switch (i) {
|
||||||
@@ -260,8 +279,8 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
|
|||||||
a = b;
|
a = b;
|
||||||
} else if (bt == SCRIPT_NIL) {
|
} else if (bt == SCRIPT_NIL) {
|
||||||
// a = a;
|
// a = a;
|
||||||
//} else if (a->likesFunction() && b->likesFunction()) {
|
} else if (at == SCRIPT_FUNCTION && bt == SCRIPT_FUNCTION) {
|
||||||
// a = compose(a, b);
|
a = new_intrusive2<ScriptCompose>(a, b);
|
||||||
} else if (at == SCRIPT_STRING || bt == SCRIPT_STRING) {
|
} else if (at == SCRIPT_STRING || bt == SCRIPT_STRING) {
|
||||||
a = toScript((String)*a + (String)*b);
|
a = toScript((String)*a + (String)*b);
|
||||||
} else if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) {
|
} else if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) {
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ class Context {
|
|||||||
|
|
||||||
/// Get the value of a variable, throws if it not set
|
/// Get the value of a variable, throws if it not set
|
||||||
ScriptValueP getVariable(const String& name);
|
ScriptValueP getVariable(const String& name);
|
||||||
/// Get the value of a variable, returns nil if it is not set
|
/// Get the value of a variable, returns ScriptValue() if it is not set
|
||||||
ScriptValueP getVariableOrNil(const String& name);
|
ScriptValueP getVariableOpt(const String& name);
|
||||||
|
|
||||||
public:// public for FOR_EACH
|
public:// public for FOR_EACH
|
||||||
/// Record of a variable
|
/// Record of a variable
|
||||||
|
|||||||
@@ -0,0 +1,258 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||||
|
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||||
|
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <script/value.hpp>
|
||||||
|
#include <script/context.hpp>
|
||||||
|
#include <wx/regex.h>
|
||||||
|
|
||||||
|
DECLARE_TYPEOF_COLLECTION(UInt);
|
||||||
|
|
||||||
|
/** @file script/functions.cpp
|
||||||
|
*
|
||||||
|
* @brief Functions used in scripts
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rules : regex replace
|
||||||
|
|
||||||
|
class ScriptReplaceRule : public ScriptValue {
|
||||||
|
public:
|
||||||
|
virtual ScriptType type() const { return SCRIPT_FUNCTION; }
|
||||||
|
virtual String typeName() const { return _("replace_rule"); }
|
||||||
|
virtual ScriptValueP eval(Context& ctx) const {
|
||||||
|
throw "TODO";
|
||||||
|
}
|
||||||
|
|
||||||
|
wxRegEx regex; ///< Regex to match
|
||||||
|
wxRegEx context; ///< Match only in a given context, optional
|
||||||
|
String replacement; ///< Replacement
|
||||||
|
ScriptValueP replacement_function; ///< Replacement function instead of a simple string, optional
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a regular expression rule for replacing in strings
|
||||||
|
SCRIPT_FUNCTION(replace_rule) {
|
||||||
|
intrusive_ptr<ScriptReplaceRule> ret(new ScriptReplaceRule);
|
||||||
|
// match
|
||||||
|
SCRIPT_PARAM(String, match);
|
||||||
|
if (!ret->regex.Compile(match, wxRE_ADVANCED)) {
|
||||||
|
throw ScriptError(_("Error while compiling regular expression: '")+match+_("'"));
|
||||||
|
}
|
||||||
|
// replace
|
||||||
|
ScriptValueP replace = ctx.getVariable(_("replace"));
|
||||||
|
if (replace->type() == SCRIPT_FUNCTION) {
|
||||||
|
ret->replacement_function = replace;
|
||||||
|
} else {
|
||||||
|
ret->replacement = (String)*replace;
|
||||||
|
}
|
||||||
|
// in_context
|
||||||
|
SCRIPT_OPTIONAL_PARAM_N(String, "in context", in_context) {
|
||||||
|
if (!ret->context.Compile(in_context, wxRE_ADVANCED)) {
|
||||||
|
throw ScriptError(_("Error while compiling regular expression: '")+in_context+_("'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rules : regex filter
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Rules : sort
|
||||||
|
|
||||||
|
/// Sort a string using a specification using the shortest cycle metric, see spec_sort
|
||||||
|
String cycle_sort(const String& spec, const String& input) {
|
||||||
|
size_t size = spec.size();
|
||||||
|
vector<UInt> counts;
|
||||||
|
// count occurences of each char in spec
|
||||||
|
FOR_EACH_CONST(s, spec) {
|
||||||
|
UInt c = 0;
|
||||||
|
FOR_EACH_CONST(i, input) {
|
||||||
|
if (s == i) c++;
|
||||||
|
}
|
||||||
|
counts.push_back(c);
|
||||||
|
}
|
||||||
|
// determine best start point
|
||||||
|
size_t best_start = 0;
|
||||||
|
UInt best_start_score = 0xffffffff;
|
||||||
|
for (size_t start = 0 ; start < size ; ++start) {
|
||||||
|
// score of a start position, can be considered as:
|
||||||
|
// - count saturated to binary
|
||||||
|
// - rotated left by start
|
||||||
|
// - interpreted as a binary number, but without trailing 0s
|
||||||
|
UInt score = 0, mul = 1;
|
||||||
|
for (size_t i = 0 ; i < size ; ++i) {
|
||||||
|
mul *= 2;
|
||||||
|
if (counts[(start + i) % size]) {
|
||||||
|
score = score * mul + 1;
|
||||||
|
mul = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (score < best_start_score) {
|
||||||
|
best_start_score = score;
|
||||||
|
best_start = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return string
|
||||||
|
String ret;
|
||||||
|
for (size_t i = 0 ; i < size ; ++i) {
|
||||||
|
size_t pos = (best_start + i) % size;
|
||||||
|
ret.append(counts[pos], spec[pos]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sort a string using a sort specification
|
||||||
|
/** The specificatio can contain:
|
||||||
|
* - a = all 'a's go here
|
||||||
|
* - [abc] = 'a', 'b' and 'c' go here, in the same order as in the input
|
||||||
|
* - <abc> = 'a', 'b' and 'c' go here in that order, and only zero or one time.
|
||||||
|
* - (abc) = 'a', 'b' and 'c' go here, in the shortest order
|
||||||
|
* consider the specified characters as a clockwise circle
|
||||||
|
* then returns the input in the order that:
|
||||||
|
* 1. takes the shortest clockwise path over this circle.
|
||||||
|
* 2. has _('holes') early, a hole means a character that is in the specification
|
||||||
|
* but not in the input
|
||||||
|
* 3. prefer the one that comes the earliest in the expression (a in this case)
|
||||||
|
*
|
||||||
|
* example:
|
||||||
|
* spec_sort("XYZ<0123456789>(WUBRG)",..) // used by magic
|
||||||
|
* "W1G") -> "1GW" // could be "W...G" or "...GW", second is shorter
|
||||||
|
* "GRBUWWUG") -> "WWUUBRGG" // no difference by rule 1,2, could be "WUBRG", "UBRGW", etc.
|
||||||
|
* // becomes _("WUBRG") by rule 3
|
||||||
|
* "WUR") -> "RWU" // by rule 1 could be "R WU" or "WU R", "RUW" has an earlier hole
|
||||||
|
*/
|
||||||
|
String spec_sort(const String& spec, const String& input) {
|
||||||
|
String ret;
|
||||||
|
for (size_t pos = 0 ; pos < spec.size() ; ++pos) {
|
||||||
|
Char c = spec.GetChar(pos);
|
||||||
|
|
||||||
|
if (c == _('<')) { // keep only a single copy
|
||||||
|
for ( ; pos < spec.size() ; ++pos) {
|
||||||
|
Char c = spec.GetChar(pos);
|
||||||
|
if (c == _('>')) break;
|
||||||
|
if (input.find_first_of(c) != String::npos) {
|
||||||
|
ret += c; // input contains c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos == String::npos) throw ParseError(_("Expected '>' in sort_rule specification"));
|
||||||
|
|
||||||
|
} else if (c == _('[')) { // in any order
|
||||||
|
size_t end = spec.find_first_of(_(']'));
|
||||||
|
if (end == String::npos) throw ParseError(_("Expected ']' in sort_rule specification"));
|
||||||
|
FOR_EACH_CONST(d, input) {
|
||||||
|
size_t in_spec = spec.find_first_of(d, pos);
|
||||||
|
if (in_spec < end) {
|
||||||
|
ret += d; // d is in the part between [ and ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos = end;
|
||||||
|
|
||||||
|
} else if (c == _('(')) { // in a cycle
|
||||||
|
size_t end = spec.find_first_of(_(')'));
|
||||||
|
if (end == String::npos) throw ParseError(_("Expected ')' in sort_rule specification"));
|
||||||
|
ret += cycle_sort(spec.substr(pos, end - pos - 1), input);
|
||||||
|
pos = end;
|
||||||
|
|
||||||
|
} else { // single char
|
||||||
|
FOR_EACH_CONST(d, input) {
|
||||||
|
if (c == d) ret += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptSortRule : public ScriptValue {
|
||||||
|
public:
|
||||||
|
ScriptSortRule(const String& order) : order(order) {}
|
||||||
|
|
||||||
|
virtual ScriptType type() const { return SCRIPT_FUNCTION; }
|
||||||
|
virtual String typeName() const { return _("sort_rule"); }
|
||||||
|
virtual ScriptValueP eval(Context& ctx) const {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
SCRIPT_RETURN(spec_sort(order, input));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
String order;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a rule for spec_sorting strings
|
||||||
|
SCRIPT_FUNCTION(sort_rule) {
|
||||||
|
SCRIPT_PARAM(String, order);
|
||||||
|
return new_intrusive1<ScriptSortRule>(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : String stuff
|
||||||
|
|
||||||
|
// convert a string to upper case
|
||||||
|
SCRIPT_FUNCTION(to_upper) {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
SCRIPT_RETURN(input.Upper());
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a string to lower case
|
||||||
|
SCRIPT_FUNCTION(to_lower) {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
SCRIPT_RETURN(input.Lower());
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a string to title case
|
||||||
|
SCRIPT_FUNCTION(to_title) {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
SCRIPT_RETURN(capitalize(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract a substring
|
||||||
|
SCRIPT_FUNCTION(substring) {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
SCRIPT_PARAM_DEFAULT(int, begin, 0);
|
||||||
|
SCRIPT_PARAM_DEFAULT(int, end, INT_MAX);
|
||||||
|
if (begin < 0) begin = 0;
|
||||||
|
if (end < 0) end = 0;
|
||||||
|
if (begin >= end || (size_t)begin >= input.size()) {
|
||||||
|
SCRIPT_RETURN(wxEmptyString);
|
||||||
|
} else if ((size_t)end >= input.size()) {
|
||||||
|
SCRIPT_RETURN(input.substr(begin));
|
||||||
|
} else {
|
||||||
|
SCRIPT_RETURN(input.substr(begin, end - begin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Vector stuff
|
||||||
|
|
||||||
|
/// position of some element in a vector
|
||||||
|
/** 1 based index, 0 if not found */
|
||||||
|
int position_in_vector(const ScriptValueP& of, const ScriptValueP& in, const ScriptValueP& order_by) {
|
||||||
|
return 0;// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// finding positions
|
||||||
|
SCRIPT_FUNCTION(position_of) {
|
||||||
|
ScriptValueP of = ctx.getVariable(_("of"));
|
||||||
|
ScriptValueP in = ctx.getVariable(_("in"));
|
||||||
|
ScriptValueP order_by = ctx.getVariableOpt(_("order by"));
|
||||||
|
SCRIPT_RETURN(position_in_vector(of, in, order_by));
|
||||||
|
}
|
||||||
|
|
||||||
|
// finding sizes
|
||||||
|
SCRIPT_FUNCTION(number_of_items) {
|
||||||
|
SCRIPT_RETURN(ctx.getVariable(_("in"))->itemCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Initialize functions
|
||||||
|
|
||||||
|
void init_script_functions(Context& ctx) {
|
||||||
|
ctx.setVariable(_("replace rule"), script_replace_rule);
|
||||||
|
ctx.setVariable(_("sort rule"), script_sort_rule);
|
||||||
|
ctx.setVariable(_("to upper"), script_to_upper);
|
||||||
|
ctx.setVariable(_("to lower"), script_to_lower);
|
||||||
|
ctx.setVariable(_("to title"), script_to_title);
|
||||||
|
ctx.setVariable(_("substring"), script_substring);
|
||||||
|
ctx.setVariable(_("position"), script_position_of);
|
||||||
|
ctx.setVariable(_("number of items"), script_number_of_items);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ ScriptImageP ScriptableImage::generate(Context& ctx, UInt width, UInt height, Pr
|
|||||||
|
|
||||||
ScriptImageP ScriptableImage::update(Context& ctx, UInt width, UInt height, PreserveAspect preserve_aspect, bool saturate) {
|
ScriptImageP ScriptableImage::update(Context& ctx, UInt width, UInt height, PreserveAspect preserve_aspect, bool saturate) {
|
||||||
// up to date?
|
// up to date?
|
||||||
if (!cache || (UInt)cache->image.GetWidth() != width || (UInt)cache->image.GetHeight() == height || !upToDate(ctx, last_update)) {
|
if (!cache || (UInt)cache->image.GetWidth() != width || (UInt)cache->image.GetHeight() != height || !upToDate(ctx, last_update)) {
|
||||||
// cache must be updated
|
// cache must be updated
|
||||||
cache = generate(ctx, width, height, preserve_aspect, saturate);
|
cache = generate(ctx, width, height, preserve_aspect, saturate);
|
||||||
last_update.update();
|
last_update.update();
|
||||||
@@ -120,8 +120,12 @@ ScriptImageP ScriptableImage::update(Context& ctx, UInt width, UInt height, Pres
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptableImage::upToDate(Context& ctx, Age age) const {
|
bool ScriptableImage::upToDate(Context& ctx, Age age) const {
|
||||||
WITH_DYNAMIC_ARG(last_update_age, age.get());
|
try {
|
||||||
return (int)*script.invoke(ctx);
|
WITH_DYNAMIC_ARG(last_update_age, age.get());
|
||||||
|
return (int)*script.invoke(ctx);
|
||||||
|
} catch (Error e) {
|
||||||
|
return true; // script gives errors, don't update
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Reflection
|
// ----------------------------------------------------------------------------- : Reflection
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ String variableToString(unsigned int v) {
|
|||||||
// ----------------------------------------------------------------------------- : Script
|
// ----------------------------------------------------------------------------- : Script
|
||||||
|
|
||||||
ScriptType Script::type() const {
|
ScriptType Script::type() const {
|
||||||
return SCRIPT_SCRIPT_FUN;
|
return SCRIPT_FUNCTION;
|
||||||
}
|
}
|
||||||
String Script::typeName() const {
|
String Script::typeName() const {
|
||||||
return _("function");
|
return _("function");
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ DECLARE_TYPEOF(Contexts);
|
|||||||
DECLARE_TYPEOF_COLLECTION(FieldP);
|
DECLARE_TYPEOF_COLLECTION(FieldP);
|
||||||
DECLARE_TYPEOF_NO_REV(IndexMap_FieldP_StyleP);
|
DECLARE_TYPEOF_NO_REV(IndexMap_FieldP_StyleP);
|
||||||
|
|
||||||
|
// initialize functions, from functions.cpp
|
||||||
|
void init_script_functions(Context& ctx);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : ScriptManager : initialization
|
// ----------------------------------------------------------------------------- : ScriptManager : initialization
|
||||||
|
|
||||||
ScriptManager::ScriptManager(Set& set)
|
ScriptManager::ScriptManager(Set& set)
|
||||||
@@ -48,10 +51,10 @@ Context& ScriptManager::getContext(const StyleSheetP& stylesheet) {
|
|||||||
// variables
|
// variables
|
||||||
// NOTE: do not use a smart pointer for the pointer to the set, because the set owns this
|
// NOTE: do not use a smart pointer for the pointer to the set, because the set owns this
|
||||||
// which would lead to a reference cycle.
|
// which would lead to a reference cycle.
|
||||||
|
init_script_functions(*ctx);
|
||||||
ctx->setVariable(_("set"), new_intrusive1<ScriptObject<Set*> >(&set));
|
ctx->setVariable(_("set"), new_intrusive1<ScriptObject<Set*> >(&set));
|
||||||
ctx->setVariable(_("game"), toScript(set.game));
|
ctx->setVariable(_("game"), toScript(set.game));
|
||||||
ctx->setVariable(_("stylesheet"), toScript(stylesheet));
|
ctx->setVariable(_("stylesheet"), toScript(stylesheet));
|
||||||
//ctx->style->object = style;
|
|
||||||
//ctx->setVariable(_("styling"), toScript(set->extraStyleData(style)));
|
//ctx->setVariable(_("styling"), toScript(set->extraStyleData(style)));
|
||||||
try {
|
try {
|
||||||
// perform init scripts
|
// perform init scripts
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class Scriptable {
|
|||||||
inline operator const T& () const { return value; }
|
inline operator const T& () const { return value; }
|
||||||
inline bool isScripted() const { return script; }
|
inline bool isScripted() const { return script; }
|
||||||
|
|
||||||
// Updates the value by executing the script, returns true if the value has changed
|
/// Updates the value by executing the script, returns true if the value has changed
|
||||||
inline bool update(Context& ctx) {
|
inline bool update(Context& ctx) {
|
||||||
return script.invokeOn(ctx, value);
|
return script.invokeOn(ctx, value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ ScriptValueP ScriptValue::getMember(const String& name) const
|
|||||||
{ throw (typeName() + _(" has no member '") + name + _("'")); }
|
{ throw (typeName() + _(" has no member '") + name + _("'")); }
|
||||||
ScriptValueP ScriptValue::next() { throw InternalError(_("Can't convert from ")+typeName()+_(" to iterator")); }
|
ScriptValueP ScriptValue::next() { throw InternalError(_("Can't convert from ")+typeName()+_(" to iterator")); }
|
||||||
ScriptValueP ScriptValue::makeIterator() const { throw ScriptError( _("Can't convert from ")+typeName()+_(" to collection")); }
|
ScriptValueP ScriptValue::makeIterator() const { throw ScriptError( _("Can't convert from ")+typeName()+_(" to collection")); }
|
||||||
|
int ScriptValue::itemCount() const { throw ScriptError( _("Can't convert from ")+typeName()+_(" to collection")); }
|
||||||
|
|
||||||
void ScriptValue::signalDependent(Context&, const Dependency&, const String& name) {}
|
void ScriptValue::signalDependent(Context&, const Dependency&, const String& name) {}
|
||||||
ScriptValueP ScriptValue::dependencies( Context&, const Dependency&) const { return script_nil; }
|
ScriptValueP ScriptValue::dependencies( Context&, const Dependency&) const { return script_nil; }
|
||||||
@@ -167,6 +168,7 @@ class ScriptString : public ScriptValue {
|
|||||||
throw ScriptError(_("Not a number: '") + value + _("'"));
|
throw ScriptError(_("Not a number: '") + value + _("'"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual int itemCount() const { return (int)value.size(); }
|
||||||
private:
|
private:
|
||||||
String value;
|
String value;
|
||||||
};
|
};
|
||||||
|
|||||||
+30
-4
@@ -36,8 +36,7 @@ enum ScriptType
|
|||||||
, SCRIPT_STRING
|
, SCRIPT_STRING
|
||||||
, SCRIPT_COLOR
|
, SCRIPT_COLOR
|
||||||
, SCRIPT_IMAGE
|
, SCRIPT_IMAGE
|
||||||
, SCRIPT_BUILDIN_FUN
|
, SCRIPT_FUNCTION
|
||||||
, SCRIPT_SCRIPT_FUN
|
|
||||||
, SCRIPT_OBJECT
|
, SCRIPT_OBJECT
|
||||||
, SCRIPT_DUMMY
|
, SCRIPT_DUMMY
|
||||||
};
|
};
|
||||||
@@ -77,6 +76,8 @@ class ScriptValue {
|
|||||||
virtual ScriptValueP makeIterator() const;
|
virtual ScriptValueP makeIterator() const;
|
||||||
/// Return the next item for this iterator, or ScriptValueP() if there is no such item
|
/// Return the next item for this iterator, or ScriptValueP() if there is no such item
|
||||||
virtual ScriptValueP next();
|
virtual ScriptValueP next();
|
||||||
|
/// Return the number of items in this value (assuming it is a collection)
|
||||||
|
virtual int itemCount() const;
|
||||||
|
|
||||||
/// Signal that a script depends on a member of this value
|
/// Signal that a script depends on a member of this value
|
||||||
virtual void signalDependent(Context&, const Dependency&, const String& name);
|
virtual void signalDependent(Context&, const Dependency&, const String& name);
|
||||||
@@ -166,6 +167,7 @@ class ScriptCollection : public ScriptValue {
|
|||||||
virtual ScriptValueP makeIterator() const {
|
virtual ScriptValueP makeIterator() const {
|
||||||
return new_intrusive1<ScriptCollectionIterator<Collection> >(value);
|
return new_intrusive1<ScriptCollectionIterator<Collection> >(value);
|
||||||
}
|
}
|
||||||
|
virtual int itemCount() const { return (int)value->size(); }
|
||||||
private:
|
private:
|
||||||
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
|
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
|
||||||
const Collection* value;
|
const Collection* value;
|
||||||
@@ -203,6 +205,7 @@ class ScriptMap : public ScriptValue {
|
|||||||
virtual ScriptValueP getMember(const String& name) const {
|
virtual ScriptValueP getMember(const String& name) const {
|
||||||
return get_member(*value, name);
|
return get_member(*value, name);
|
||||||
}
|
}
|
||||||
|
virtual int itemCount() const { return (int)value->size(); }
|
||||||
private:
|
private:
|
||||||
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
|
/// Store a pointer to a collection, collections are only ever used for structures owned outside the script
|
||||||
const Collection* value;
|
const Collection* value;
|
||||||
@@ -271,7 +274,7 @@ inline ScriptValueP toScript(const shared_ptr<T>& v) { return new_intrusive1<Scr
|
|||||||
class ScriptBuildin_##name : public ScriptValue { \
|
class ScriptBuildin_##name : public ScriptValue { \
|
||||||
dep \
|
dep \
|
||||||
/* virtual */ ScriptType type() const \
|
/* virtual */ ScriptType type() const \
|
||||||
{ return SCRIPT_BUILDIN_FUN; } \
|
{ return SCRIPT_FUNCTION; } \
|
||||||
virtual String typeName() const \
|
virtual String typeName() const \
|
||||||
{ return _("build in function '") _(#name) _("'"); } \
|
{ return _("build in function '") _(#name) _("'"); } \
|
||||||
virtual ScriptValueP eval(Context&) const; \
|
virtual ScriptValueP eval(Context&) const; \
|
||||||
@@ -284,7 +287,7 @@ inline ScriptValueP toScript(const shared_ptr<T>& v) { return new_intrusive1<Scr
|
|||||||
* @code
|
* @code
|
||||||
* SCRIPT_FUNCTION(my_function) {
|
* SCRIPT_FUNCTION(my_function) {
|
||||||
* SCRIPT_PARAM(String, my_string_param);
|
* SCRIPT_PARAM(String, my_string_param);
|
||||||
* ...
|
* ... my_string_param ...
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
* Throws an error if the parameter is not found.
|
* Throws an error if the parameter is not found.
|
||||||
@@ -292,6 +295,29 @@ inline ScriptValueP toScript(const shared_ptr<T>& v) { return new_intrusive1<Scr
|
|||||||
#define SCRIPT_PARAM(Type, name) \
|
#define SCRIPT_PARAM(Type, name) \
|
||||||
Type name = *ctx.getVariable(_(#name))
|
Type name = *ctx.getVariable(_(#name))
|
||||||
|
|
||||||
|
/// Retrieve an optional parameter
|
||||||
|
/** Usage:
|
||||||
|
* @code
|
||||||
|
* SCRIPT_FUNCTION(my_function) {
|
||||||
|
* SCRIPT_OPTIONAL_PARAM(String, my_string_param) {
|
||||||
|
* ... my_string_param ...
|
||||||
|
* }
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define SCRIPT_OPTIONAL_PARAM(Type, name) SCRIPT_OPTIONAL_PARAM_N(Type, name, #name)
|
||||||
|
|
||||||
|
#define SCRIPT_OPTIONAL_PARAM_N(Type, str, name) \
|
||||||
|
ScriptValueP name##_ = ctx.getVariableOpt(_(str)); \
|
||||||
|
Type name = name##_ ? *name##_ : Type(); \
|
||||||
|
if (name##_)
|
||||||
|
|
||||||
|
/// Retrieve an optional parameter with a default value
|
||||||
|
#define SCRIPT_PARAM_DEFAULT(Type, name, def) \
|
||||||
|
ScriptValueP name##_ = ctx.getVariableOpt(_(#name)); \
|
||||||
|
Type name = name##_ ? *name##_ : def
|
||||||
|
|
||||||
/// Return a value from a SCRIPT_FUNCTION
|
/// Return a value from a SCRIPT_FUNCTION
|
||||||
#define SCRIPT_RETURN(value) return toScript(value)
|
#define SCRIPT_RETURN(value) return toScript(value)
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -37,7 +37,8 @@ void handle_error(const String& e, bool allow_duplicate = true, bool now = true)
|
|||||||
}
|
}
|
||||||
// Only show errors in the main thread
|
// Only show errors in the main thread
|
||||||
if (!now || !wxThread::IsMain()) {
|
if (!now || !wxThread::IsMain()) {
|
||||||
pending_error = e;
|
if (!pending_error.empty()) pending_error += _("\n\n");
|
||||||
|
pending_error += e;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// show message
|
// show message
|
||||||
@@ -47,3 +48,11 @@ void handle_error(const String& e, bool allow_duplicate = true, bool now = true)
|
|||||||
void handle_error(const Error& e, bool allow_duplicate, bool now) {
|
void handle_error(const Error& e, bool allow_duplicate, bool now) {
|
||||||
handle_error(e.what(), allow_duplicate, now);
|
handle_error(e.what(), allow_duplicate, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handle_pending_errors() {
|
||||||
|
assert(wxThread::IsMain());
|
||||||
|
if (!pending_error.empty()) {
|
||||||
|
handle_error(pending_error);
|
||||||
|
pending_error.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ void Reader::showWarnings() {
|
|||||||
bool Reader::enterBlock(const Char* name) {
|
bool Reader::enterBlock(const Char* name) {
|
||||||
if (just_opened) moveNext(); // on the key of the parent block, first move inside it
|
if (just_opened) moveNext(); // on the key of the parent block, first move inside it
|
||||||
if (indent != expected_indent) return false; // not enough indentation
|
if (indent != expected_indent) return false; // not enough indentation
|
||||||
if (name == key) {
|
if (cannocial_name_compare(key, name)) {
|
||||||
just_opened = true;
|
just_opened = true;
|
||||||
expected_indent += 1; // the indent inside the block must be at least this much
|
expected_indent += 1; // the indent inside the block must be at least this much
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class Reader {
|
|||||||
/** Maybe the key is "include file" */
|
/** Maybe the key is "include file" */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void unknownKey(T& v) {
|
void unknownKey(T& v) {
|
||||||
if (key == _("include_file")) {
|
if (key == _("include file")) {
|
||||||
Reader reader(value);
|
Reader reader(value);
|
||||||
reader.handle(v);
|
reader.handle(v);
|
||||||
moveNext();
|
moveNext();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ void Writer::enterBlock(const Char* name) {
|
|||||||
}
|
}
|
||||||
// don't write the key yet
|
// don't write the key yet
|
||||||
indentation += 1;
|
indentation += 1;
|
||||||
opened_key = name;
|
opened_key = cannocial_name_form(name);
|
||||||
just_opened = true;
|
just_opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +47,6 @@ void Writer::exitBlock() {
|
|||||||
|
|
||||||
void Writer::writeKey() {
|
void Writer::writeKey() {
|
||||||
writeIndentation();
|
writeIndentation();
|
||||||
// Use ' ' instead of '_' because it is more human readable
|
|
||||||
FOR_EACH(c, opened_key) if (c == _('_')) c = _(' ');
|
|
||||||
writeUTF8(stream, opened_key);
|
writeUTF8(stream, opened_key);
|
||||||
}
|
}
|
||||||
void Writer::writeIndentation() {
|
void Writer::writeIndentation() {
|
||||||
|
|||||||
+10
-1
@@ -123,7 +123,7 @@ String cannocial_name_form(const String& str) {
|
|||||||
bool leading = true;
|
bool leading = true;
|
||||||
FOR_EACH_CONST(c, str) {
|
FOR_EACH_CONST(c, str) {
|
||||||
if ((c == _('_') || c == _(' ')) && !leading) {
|
if ((c == _('_') || c == _(' ')) && !leading) {
|
||||||
ret += _('_');
|
ret += _(' ');
|
||||||
} else if (isAlnum(c)) {
|
} else if (isAlnum(c)) {
|
||||||
ret += toLower(c);
|
ret += toLower(c);
|
||||||
leading = false;
|
leading = false;
|
||||||
@@ -195,3 +195,12 @@ bool is_substr(const String& str, size_t pos, const Char* cmp) {
|
|||||||
}
|
}
|
||||||
return *cmp == _('\0');
|
return *cmp == _('\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cannocial_name_compare(const String& as, const Char* b) {
|
||||||
|
const Char* a = as.c_str();
|
||||||
|
while (true) {
|
||||||
|
if (*a != *b && !(*a == _(' ') && *b == _('_'))) return false;
|
||||||
|
if (*a == _('\0')) return true;
|
||||||
|
a++; b++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+4
-1
@@ -100,7 +100,7 @@ String capitalize(const String&);
|
|||||||
String capitalize_sentence(const String&);
|
String capitalize_sentence(const String&);
|
||||||
|
|
||||||
/// Convert a field name to cannocial form
|
/// Convert a field name to cannocial form
|
||||||
/** - lower case and '_' instead of ' '.
|
/** - lower case and ' ' instead of '_'.
|
||||||
* - non alphanumeric characters are droped
|
* - non alphanumeric characters are droped
|
||||||
* - "camalCase" is converted to words "camel case" (TODO)
|
* - "camalCase" is converted to words "camel case" (TODO)
|
||||||
*/
|
*/
|
||||||
@@ -127,5 +127,8 @@ bool starts_with(const String& str, const String& start);
|
|||||||
/// Return whether str contains the string cmp at position pos
|
/// Return whether str contains the string cmp at position pos
|
||||||
bool is_substr(const String& str, size_t pos, const Char* cmp);
|
bool is_substr(const String& str, size_t pos, const Char* cmp);
|
||||||
|
|
||||||
|
/// Compare two strings for equality, b may contain '_' where a contains ' '
|
||||||
|
bool cannocial_name_compare(const String& a, const Char* b);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user