mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 13:06:59 -04:00
combined_editor function, and improved dependency handling (removing duplicates), viewer refreshes on events from script manager
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@147 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -168,7 +168,7 @@ void Context::setVariable(const String& name, const ScriptValueP& value) {
|
||||
|
||||
void Context::setVariable(int name, const ScriptValueP& value) {
|
||||
Variable& var = variables[name];
|
||||
if (var.value && var.level < level) {
|
||||
if (var.level < level) {
|
||||
// keep shadow copy
|
||||
Binding bind = {name, var};
|
||||
shadowed.push_back(bind);
|
||||
|
||||
@@ -60,6 +60,7 @@ class Context {
|
||||
public:// public for FOR_EACH
|
||||
/// Record of a variable
|
||||
struct Variable {
|
||||
Variable() : level(0) {}
|
||||
unsigned int level; ///< Scope level on which this variable was set
|
||||
ScriptValueP value; ///< Value of this variable
|
||||
};
|
||||
|
||||
@@ -38,6 +38,25 @@ class Dependency {
|
||||
inline Dependency makeCardIndependend() const {
|
||||
return Dependency(type == DEP_CARD_FIELD ? DEP_CARDS_FIELD : type, index, data);
|
||||
}
|
||||
|
||||
inline bool operator == (const Dependency& d) const {
|
||||
return type == d.type && index == d.index && data == d.data;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Dependencies
|
||||
|
||||
/// A list of dependencies
|
||||
class Dependencies : public vector<Dependency> {
|
||||
public:
|
||||
/// Add a dependency, prevents duplicates
|
||||
inline void add(const Dependency& d) {
|
||||
if (find(begin(),end(),d) == end()) {
|
||||
push_back(d);
|
||||
}
|
||||
}
|
||||
private:
|
||||
using vector<Dependency>::push_back;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
|
||||
+120
-1
@@ -11,9 +11,14 @@
|
||||
#include <script/dependency.hpp>
|
||||
#include <util/tagged_string.hpp>
|
||||
#include <data/set.hpp>
|
||||
#include <data/game.hpp>
|
||||
#include <data/field/text.hpp>
|
||||
#include <wx/regex.h>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(UInt);
|
||||
DECLARE_TYPEOF_COLLECTION(FieldP);
|
||||
DECLARE_TYPEOF_COLLECTION(TextValue*);
|
||||
DECLARE_TYPEOF_COLLECTION(String);
|
||||
|
||||
/** @file script/functions.cpp
|
||||
*
|
||||
@@ -90,7 +95,7 @@ SCRIPT_FUNCTION(replace_rule) {
|
||||
ret->replacement = (String)*replace;
|
||||
}
|
||||
// in_context
|
||||
SCRIPT_OPTIONAL_PARAM_N(String, "in context", 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+_("'"));
|
||||
}
|
||||
@@ -453,6 +458,118 @@ SCRIPT_FUNCTION(number_of_items) {
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Combined editor
|
||||
|
||||
SCRIPT_FUNCTION_DEP(combined_editor) {
|
||||
// read 'field#' arguments
|
||||
vector<TextValue*> values;
|
||||
for (int i = 0 ; ; ++i) {
|
||||
String name = _("field"); if (i > 0) name = name << i;
|
||||
SCRIPT_OPTIONAL_PARAM_N(ValueP, name, value) {
|
||||
TextValue* text_value = dynamic_cast<TextValue*>(value.get());
|
||||
if (!text_value) throw ScriptError(_("Argument '")+name+_("' should be a text field"));
|
||||
values.push_back(text_value);
|
||||
} else if (i > 0) break;
|
||||
}
|
||||
if (values.empty()) {
|
||||
throw ScriptError(_("No fields specified for combined_editor"));
|
||||
}
|
||||
// read 'separator#' arguments
|
||||
vector<String> separators;
|
||||
for (int i = 0 ; ; ++i) {
|
||||
String name = _("separator"); if (i > 0) name = name << i;
|
||||
SCRIPT_OPTIONAL_PARAM_N(String, name, separator) {
|
||||
separators.push_back(separator);
|
||||
} else if (i > 0) break;
|
||||
}
|
||||
if (separators.size() < values.size() - 1) {
|
||||
throw ScriptError(String::Format(_("Not enough separators for combine_editor, expected %d"), values.size()-1));
|
||||
}
|
||||
// split the value
|
||||
SCRIPT_PARAM(String, value);
|
||||
vector<String> value_parts;
|
||||
size_t pos = value.find(_("<sep"));
|
||||
while (pos != String::npos) {
|
||||
value_parts.push_back(value.substr(0, pos));
|
||||
value = value.substr(min(skip_tag(value,match_close_tag(value,pos)), value.size()));
|
||||
pos = value.find(_("<sep"));
|
||||
}
|
||||
value_parts.push_back(value);
|
||||
if (value_parts.size() < values.size()) value_parts.resize(values.size());
|
||||
// update the values if our input value is newer?
|
||||
Age new_value_update = last_update_age();
|
||||
FOR_EACH_2(v, values, nv, value_parts) {
|
||||
if (v->value() != nv && v->last_update < new_value_update) {
|
||||
// TODO : type over
|
||||
v->value.assign(nv);
|
||||
v->update(ctx);
|
||||
}
|
||||
nv = v->value();
|
||||
}
|
||||
// options
|
||||
SCRIPT_PARAM_DEFAULT_N(bool, _("hide when empty"), hide_when_empty, false);
|
||||
SCRIPT_PARAM_DEFAULT_N(bool, _("soft before empty"), soft_before_empty, false);
|
||||
// recombine the parts
|
||||
String new_value = value_parts.front();
|
||||
for (size_t i = 1 ; i < value_parts.size() ; ++i) {
|
||||
if (value_parts[i].empty() && new_value.empty() && hide_when_empty) {
|
||||
// no separator
|
||||
} else if (value_parts[i].empty() && soft_before_empty) {
|
||||
// soft separator
|
||||
new_value += _("<sep-soft>") + separators[i - 1] + _("</sep-soft>");
|
||||
} else {
|
||||
// normal separator
|
||||
new_value += _("<sep>") + separators[i - 1] + _("</sep>");
|
||||
new_value += value_parts[i];
|
||||
}
|
||||
}
|
||||
SCRIPT_RETURN(new_value);
|
||||
}
|
||||
|
||||
ScriptValueP ScriptBuildin_combined_editor::dependencies(Context& ctx, const Dependency& dep) const {
|
||||
// read 'field#' arguments
|
||||
vector<FieldP> fields;
|
||||
for (int i = 0 ; ; ++i) {
|
||||
String name = _("field"); if (i > 0) name = name << i;
|
||||
SCRIPT_OPTIONAL_PARAM_N(ValueP, name, value) {
|
||||
fields.push_back(value->fieldP);
|
||||
} else if (i > 0) break;
|
||||
}
|
||||
// Find the target field
|
||||
SCRIPT_PARAM(Set*, set);
|
||||
GameP game = set->game;
|
||||
FieldP target_field;
|
||||
if (dep.type == DEP_CARD_FIELD) target_field = game->card_fields[dep.index];
|
||||
else if (dep.type == DEP_SET_FIELD) target_field = game->set_fields[dep.index];
|
||||
else throw InternalError(_("Finding dependencies of combined error for non card/set field"));
|
||||
// Add dependencies, from target_field on field#
|
||||
// For card fields
|
||||
size_t j = 0;
|
||||
FOR_EACH(f, game->card_fields) {
|
||||
Dependency dep(DEP_CARD_COPY_DEP, j++);
|
||||
FOR_EACH(fn, fields) {
|
||||
if (f == fn) {
|
||||
target_field->dependent_scripts.add(dep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// For set fields
|
||||
j = 0;
|
||||
FOR_EACH(f, game->set_fields) {
|
||||
Dependency dep(DEP_SET_COPY_DEP, j++);
|
||||
FOR_EACH(fn, fields) {
|
||||
if (f == fn) {
|
||||
target_field->dependent_scripts.add(dep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SCRIPT_RETURN(dependency_dummy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Initialize functions
|
||||
|
||||
void init_script_functions(Context& ctx) {
|
||||
@@ -471,5 +588,7 @@ void init_script_functions(Context& ctx) {
|
||||
ctx.setVariable(_("tag remove rule"), script_tag_remove_rule);
|
||||
ctx.setVariable(_("position"), script_position_of);
|
||||
ctx.setVariable(_("number of items"), script_number_of_items);
|
||||
ctx.setVariable(_("forward editor"), script_combined_editor);
|
||||
ctx.setVariable(_("combined editor"), script_combined_editor);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,6 @@
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(SymbolStyle::VariationP);
|
||||
|
||||
// image generating functions have two modes
|
||||
// if last_update_age > 0 they return whether the image is still up to date
|
||||
// if last_update_age == 0 they generate the image
|
||||
DECLARE_DYNAMIC_ARG (long, last_update_age);
|
||||
IMPLEMENT_DYNAMIC_ARG(long, last_update_age, 0);
|
||||
IMPLEMENT_DYNAMIC_ARG(Package*, load_images_from, nullptr);
|
||||
|
||||
// ----------------------------------------------------------------------------- : ScriptImage
|
||||
|
||||
@@ -30,6 +30,8 @@ DECLARE_TYPEOF_NO_REV(IndexMap_FieldP_ValueP);
|
||||
void init_script_functions(Context& ctx);
|
||||
void init_script_image_functions(Context& ctx);
|
||||
|
||||
#define LOG_UPDATES
|
||||
|
||||
// ----------------------------------------------------------------------------- : SetScriptContext : initialization
|
||||
|
||||
SetScriptContext::SetScriptContext(Set& set)
|
||||
@@ -151,7 +153,13 @@ void SetScriptManager::onAction(const Action& action, bool undone) {
|
||||
// note: fallthrough
|
||||
}
|
||||
TYPE_CASE_(action, CardListAction) {
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("Card dependencies"));
|
||||
#endif
|
||||
updateAllDependend(set.game->dependent_scripts_cards);
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("-------------------------------\n"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,12 +182,21 @@ void SetScriptManager::updateValue(Value& value, const CardP& card) {
|
||||
deque<ToUpdate> to_update;
|
||||
// execute script for initial changed value
|
||||
value.update(getContext(card));
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("Start: %s"), value.fieldP->name);
|
||||
#endif
|
||||
// update dependent scripts
|
||||
alsoUpdate(to_update, value.fieldP->dependent_scripts, card);
|
||||
updateRecursive(to_update, starting_age);
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("-------------------------------\n"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetScriptManager::updateAll() {
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("Update all"));
|
||||
#endif
|
||||
// update set data
|
||||
Context& ctx = getContext(set.stylesheet);
|
||||
FOR_EACH(v, set.data) {
|
||||
@@ -194,6 +211,9 @@ void SetScriptManager::updateAll() {
|
||||
}
|
||||
// update things that depend on the card list
|
||||
updateAllDependend(set.game->dependent_scripts_cards);
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("-------------------------------\n"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetScriptManager::updateAllDependend(const vector<Dependency>& dependent_scripts) {
|
||||
@@ -221,7 +241,14 @@ void SetScriptManager::updateToUpdate(const ToUpdate& u, deque<ToUpdate>& to_upd
|
||||
set.actions.tellListeners(change, false);
|
||||
// u.value has changed, also update values with a dependency on u.value
|
||||
alsoUpdate(to_update, u.value->fieldP->dependent_scripts, u.card);
|
||||
#ifdef LOG_UPDATES
|
||||
wxLogDebug(_("Changed: %s"), u.value->fieldP->name);
|
||||
#endif
|
||||
}
|
||||
#ifdef LOG_UPDATES
|
||||
else
|
||||
wxLogDebug(_("Same: %s"), u.value->fieldP->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetScriptManager::alsoUpdate(deque<ToUpdate>& to_update, const vector<Dependency>& deps, const CardP& card) {
|
||||
|
||||
@@ -355,6 +355,7 @@ template <> inline ScriptValueP getParam<ScriptValueP>(const ScriptValueP& value
|
||||
template <> inline String getParam<String> (const ScriptValueP& value) { return *value; }
|
||||
template <> inline int getParam<int> (const ScriptValueP& value) { return *value; }
|
||||
template <> inline double getParam<double> (const ScriptValueP& value) { return *value; }
|
||||
template <> inline bool getParam<bool> (const ScriptValueP& value) { return (int)*value; }
|
||||
|
||||
/// Retrieve an optional parameter
|
||||
/** Usage:
|
||||
@@ -367,23 +368,25 @@ template <> inline double getParam<double> (const ScriptValueP& value
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define SCRIPT_OPTIONAL_PARAM(Type, name) SCRIPT_OPTIONAL_PARAM_N(Type, #name, name)
|
||||
#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)); \
|
||||
ScriptValueP name##_ = ctx.getVariableOpt(str); \
|
||||
Type name = name##_ ? getParam<Type>(name##_) : Type(); \
|
||||
if (name##_)
|
||||
|
||||
/// Retrieve an optional parameter, can't be used as an if statement
|
||||
#define SCRIPT_OPTIONAL_PARAM_(Type, name) SCRIPT_OPTIONAL_PARAM_N_(Type, #name, name)
|
||||
#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)); \
|
||||
ScriptValueP name##_ = ctx.getVariableOpt(str); \
|
||||
Type name = name##_ ? getParam<Type>(name##_) : Type();
|
||||
|
||||
/// Retrieve an optional parameter with a default value
|
||||
#define SCRIPT_PARAM_DEFAULT(Type, name, def) \
|
||||
ScriptValueP name##_ = ctx.getVariableOpt(_(#name)); \
|
||||
#define SCRIPT_PARAM_DEFAULT(Type, name, def) SCRIPT_PARAM_DEFAULT_N(Type, _(#name), name, def)
|
||||
/// Retrieve an optional parameter with a default value
|
||||
#define SCRIPT_PARAM_DEFAULT_N(Type, str, name, def) \
|
||||
ScriptValueP name##_ = ctx.getVariableOpt(str); \
|
||||
Type name = name##_ ? getParam<Type>(name##_) : def
|
||||
|
||||
/// Return a value from a SCRIPT_FUNCTION
|
||||
|
||||
Reference in New Issue
Block a user