Added 'filter' support to position function; Made sure sort script can depend on the value of the field itself.

Cleaned up some things, why is a blank image not thread safe?

git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@548 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
twanvl
2007-07-10 18:57:41 +00:00
parent 7676c0b6b6
commit 8833d07c4a
20 changed files with 229 additions and 142 deletions
+16
View File
@@ -37,6 +37,10 @@ Field::Field()
Field::~Field() {}
void Field::initDependencies(Context& ctx, const Dependency& dep) const {
sort_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(Field) {
REFLECT_IF_NOT_READING {
String type = typeName();
@@ -215,6 +219,18 @@ bool Value::equals(const Value* that) {
return this == that;
}
bool Value::update(Context& ctx) {
updateAge();
updateSortValue(ctx);
return false;
}
void Value::updateAge() {
last_script_update.update();
}
void Value::updateSortValue(Context& ctx) {
sort_value = fieldP->sort_script.invoke(ctx)->toString();
}
void init_object(const FieldP& field, ValueP& value) {
if (!value)
value = field->newValue(field);
+12 -12
View File
@@ -68,9 +68,7 @@ class Field : public IntrusivePtrVirtualBase {
virtual String typeName() const = 0;
/// Add the given dependency to the dependet_scripts list for the variables this field depends on
inline virtual void initDependencies(Context& ctx, const Dependency& dep) const {
sort_script.initDependencies(ctx, dep);
}
virtual void initDependencies(Context& ctx, const Dependency& dep) const;
private:
DECLARE_REFLECTION_VIRTUAL();
@@ -186,7 +184,7 @@ class Value : public IntrusivePtrVirtualBase {
const FieldP fieldP; ///< Field this value is for, should have the right type!
Age last_script_update; ///< When where the scripts last updated? (by calling update)
ScriptValueP sortValue; ///< How this should be sorted.
String sort_value; ///< How this should be sorted.
/// Get a copy of this value
virtual ValueP clone() const = 0;
@@ -194,11 +192,7 @@ class Value : public IntrusivePtrVirtualBase {
/// Convert this value to a string for use in tables
virtual String toString() const = 0;
/// Apply scripts to this value, return true if the value has changed
inline virtual bool update(Context& ctx) {
sortValue = fieldP->sort_script.invoke(ctx);
last_script_update.update();
return false;
}
virtual bool update(Context& ctx);
/// This value has been updated by an action
/** Does nothing for most Values, only FakeValues can update underlying data */
virtual void onAction(Action& a, bool undone) {}
@@ -208,11 +202,17 @@ class Value : public IntrusivePtrVirtualBase {
*/
virtual bool equals(const Value* that);
/// Get the sort key for this value.
inline String getSortKey () const {
return sortValue == script_nil ? *sortValue : toString();
/// Get the key to use for sorting this value
inline String getSortKey() const {
return fieldP->sort_script ? sort_value : toString();
}
protected:
/// update() split into two functions;.
/** Derived classes should put their stuff in between if they need the age in scripts */
void updateAge();
void updateSortValue(Context& ctx);
private:
DECLARE_REFLECTION_VIRTUAL();
};
+3 -3
View File
@@ -8,7 +8,6 @@
#include <data/field/choice.hpp>
#include <util/io/package.hpp>
#include <wx/spinctrl.h>
#include <wx/imaglist.h>
DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP);
@@ -312,9 +311,10 @@ String ChoiceValue::toString() const {
return value();
}
bool ChoiceValue::update(Context& ctx) {
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
Value::update(ctx);
return field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(ChoiceValue) {
+3 -2
View File
@@ -91,9 +91,10 @@ String ColorValue::toString() const {
return _("<color>");
}
bool ColorValue::update(Context& ctx) {
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
Value::update(ctx);
return field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(ColorValue) {
+3 -2
View File
@@ -66,9 +66,10 @@ String InfoValue::toString() const {
return value;
}
bool InfoValue::update(Context& ctx) {
Value::update(ctx);
if (value.empty()) value = field().name;
return field().script.invokeOn(ctx, value);
bool change = field().script.invokeOn(ctx, value);
Value::update(ctx);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(InfoValue) {
+2 -1
View File
@@ -127,12 +127,13 @@ String TextValue::toString() const {
return untag_hide_sep(value());
}
bool TextValue::update(Context& ctx) {
Value::update(ctx);
updateAge();
WITH_DYNAMIC_ARG(last_update_age, last_update.get());
WITH_DYNAMIC_ARG(value_being_updated, this);
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
if (change) last_update.update();
updateSortValue(ctx);
return change;
}
+25 -5
View File
@@ -208,23 +208,43 @@ void reflect_set_info_get_member(GetMember& tag, const IndexMap<FieldP, ValueP>&
REFLECT_NAMELESS(data);
}
int Set::positionOfCard(const CardP& card, const ScriptValueP& order_by) {
int Set::positionOfCard(const CardP& card, const ScriptValueP& order_by, const ScriptValueP& filter) {
// TODO : Lock the map?
assert(order_by);
OrderCacheP& order = order_cache[order_by];
OrderCacheP& order = order_cache[make_pair(order_by,filter)];
if (!order) {
// 1. make a list of the order value for each card
vector<String> values; values.reserve(cards.size());
vector<int> keep; if(filter) keep.reserve(cards.size());
FOR_EACH_CONST(c, cards) {
values.push_back(*order_by->eval(getContext(c)));
Context& ctx = getContext(c);
values.push_back(*order_by->eval(ctx));
if (filter) {
keep.push_back(*filter->eval(ctx));
}
}
// 2. initialize order cache
order = new_intrusive2<OrderCache<CardP> >(cards, values);
// 3. initialize order cache
order = new_intrusive3<OrderCache<CardP> >(cards, values, filter ? &keep : nullptr);
}
return order->find(card);
}
int Set::numberOfCards(const ScriptValueP& filter) {
if (!filter) return (int)cards.size();
map<ScriptValueP,int>::const_iterator it = filter_cache.find(filter);
if (it !=filter_cache.end()) {
return it->second;
} else {
int n = 0;
FOR_EACH_CONST(c, cards) {
if (*filter->eval(getContext(c))) ++n;
}
filter_cache.insert(make_pair(filter,n));
return n;
}
}
void Set::clearOrderCache() {
order_cache.clear();
filter_cache.clear();
}
// ----------------------------------------------------------------------------- : SetView
+5 -2
View File
@@ -106,7 +106,9 @@ class Set : public Packaged {
}
/// Find the position of a card in this set, when the card list is sorted using the given cirterium
int positionOfCard(const CardP& card, const ScriptValueP& order_by);
int positionOfCard(const CardP& card, const ScriptValueP& order_by, const ScriptValueP& filter);
/// Find the number of cards that match the given filter
int numberOfCards(const ScriptValueP& filter);
/// Clear the order_cache used by positionOfCard
void clearOrderCache();
@@ -122,7 +124,8 @@ class Set : public Packaged {
/// Object for executing scripts from the thumbnail thread
scoped_ptr<SetScriptContext> thumbnail_script_context;
/// Cache of cards ordered by some criterion
map<ScriptValueP,OrderCacheP> order_cache;
map<pair<ScriptValueP,ScriptValueP>,OrderCacheP> order_cache;
map<ScriptValueP,int> filter_cache;
};
inline String type_name(const Set&) {
+5 -1
View File
@@ -65,7 +65,11 @@ class BlankImage : public GeneratedImage {
public:
virtual Image generate(const Options&) const;
virtual bool operator == (const GeneratedImage& that) const;
virtual bool threadSafe() const { return false; };
// Why is this not thread safe? What is GTK smoking?
#ifdef __WXGTK__
virtual bool threadSafe() const { return false; }
#endif
};
// ----------------------------------------------------------------------------- : LinearBlendImage
+1 -1
View File
@@ -11,8 +11,8 @@
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
#include <wx/spinctrl.h>
class wxSpinCtrl;
class SymmetryMoveAction;
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor
+9 -5
View File
@@ -50,7 +50,11 @@ typedef shared_ptr<wxMemoryDC> MemoryDCP;
// Return a temporary DC with the same size as the parameter
MemoryDCP getTempDC(DC& dc) {
wxSize s = dc.GetSize();
Bitmap buffer(s.GetWidth(), s.GetHeight(), 24);
#ifdef __WXMSW__
Bitmap buffer(s.GetWidth(), s.GetHeight(), 1);
#else
Bitmap buffer(s.GetWidth(), s.GetHeight(), 24);
#endif
MemoryDCP newDC(new wxMemoryDC);
newDC->SelectObject(buffer);
clearDC(*newDC, *wxBLACK_BRUSH);
@@ -161,10 +165,10 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
// Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
//
// ref * rot
// /cos b sin b\ /cos a -sin a\;
// = \sin b -cos b/ \sin a cos a/;s
// = /cos(a+b) sin(a+b)\;
// \sin(a+b) -cos(a+b)/;
// [ cos b sin b ! [ cos a -sin a !
// = ! sin b -cos b ] ! sin a cos a ]
// = [ cos(a+b) sin(a+b) !
// ! sin(a+b) -cos(a+b) ]
Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b));
multiply.mx = rot.mx * old_m;
multiply.my = rot.my * old_m;
+1 -1
View File
@@ -145,7 +145,7 @@ struct TextElementsFromString {
// ignore other tags
}
} else {
if (c == _('\1')) c = _('<'); // unescape
c = untag_char(c); // unescape
// A character of normal text, add to the last text element (if possible)
SimpleTextElement* e = nullptr;
if (!te.elements.empty()) e = dynamic_cast<SimpleTextElement*>(te.elements.back().get());
+23 -8
View File
@@ -173,18 +173,18 @@ bool equal(const ScriptValue& a, const ScriptValue& b) {
/// position of some element in a vector
/** 0 based index, -1 if not found */
int position_in_vector(const ScriptValueP& of, const ScriptValueP& in, const ScriptValueP& order_by) {
int position_in_vector(const ScriptValueP& of, const ScriptValueP& in, const ScriptValueP& order_by, const ScriptValueP& filter) {
ScriptType of_t = of->type(), in_t = in->type();
if (of_t == SCRIPT_STRING || in_t == SCRIPT_STRING) {
// string finding
return (int)of->toString().find(in->toString()); // (int)npos == -1
} else if (order_by) {
} else if (order_by || filter) {
ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>* >(in.get());
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(of.get());
if (s && c) {
return s->getValue()->positionOfCard(c->getValue(), order_by);
return s->getValue()->positionOfCard(c->getValue(), order_by, filter);
} else {
throw ScriptError(_("position: using 'order_by' is only supported for finding cards in the set"));
throw ScriptError(_("position: using 'order_by' or 'filter' is only supported for finding cards in the set"));
}
} else {
// unordered position
@@ -231,12 +231,15 @@ SCRIPT_FUNCTION_WITH_DEP(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));
ScriptValueP filter = ctx.getVariableOpt(_("filter"));
if (filter == script_nil) filter = ScriptValueP();
SCRIPT_RETURN(position_in_vector(of, in, order_by, filter));
}
SCRIPT_FUNCTION_DEPENDENCIES(position_of) {
ScriptValueP of = ctx.getVariable(_("of"));
ScriptValueP in = ctx.getVariable(_("in"));
ScriptValueP order_by = ctx.getVariableOpt(_("order by"));
ScriptValueP filter = ctx.getVariableOpt(_("filter"));
ScriptObject<Set*>* s = dynamic_cast<ScriptObject<Set*>* >(in.get());
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(of.get());
if (s && c) {
@@ -246,13 +249,25 @@ SCRIPT_FUNCTION_DEPENDENCIES(position_of) {
// dependency on order_by function
order_by->dependencies(ctx, dep.makeCardIndependend());
}
if (filter && filter != script_nil) {
// dependency on filter function
filter->dependencies(ctx, dep.makeCardIndependend());
}
}
return dependency_dummy;
};
// finding sizes
SCRIPT_FUNCTION(number_of_items) {
SCRIPT_RETURN(ctx.getVariable(_("in"))->itemCount());
SCRIPT_PARAM(ScriptValueP, in);
if (ScriptObject<Set*>* setobj = dynamic_cast<ScriptObject<Set*>*>(in.get())) {
Set* set = setobj->getValue();
SCRIPT_OPTIONAL_PARAM_(ScriptValueP, filter);
if (filter == script_nil) filter = ScriptValueP();
SCRIPT_RETURN(set->numberOfCards(filter));
} else {
SCRIPT_RETURN(in->itemCount());
}
}
// filtering items from a list
@@ -465,7 +480,7 @@ ScriptValueP filter_rule(Context& ctx) {
SCRIPT_FUNCTION(filter_rule) {
return filter_rule(ctx);
}
SCRIPT_FUNCTION(filter) {
SCRIPT_FUNCTION(filter_text) {
return filter_rule(ctx)->eval(ctx);
}
@@ -704,7 +719,7 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("keyword usage"), script_keyword_usage);
// advanced string rules
ctx.setVariable(_("replace"), script_replace);
ctx.setVariable(_("filter"), script_filter);
ctx.setVariable(_("filter text"), script_filter_text);
ctx.setVariable(_("match"), script_match);
ctx.setVariable(_("sort"), script_sort);
ctx.setVariable(_("sort list"), script_sort);
+3 -6
View File
@@ -225,7 +225,8 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font, double symb
if (symbol.opened > 0 && symbol_font) {
symbols += c; // write as symbols instead
} else {
if (c == _('\1')) { // escape <
c = untag_char(c);
if (c == _('<')) { // escape <
ret += _("&lt;");
} else if (c == _('&')) { // escape &
ret += _("&amp;");
@@ -314,11 +315,7 @@ String to_bbcode(const String& str_in) {
// if (symbol.opened > 0 && symbol_font) {
// symbols += c; // write as symbols instead
// } else {
if (c == _('\1')) { // unescape <
ret += _("<");
} else {
ret += c;
}
ret += untag_char(c);
// }
}
}
+6 -5
View File
@@ -229,7 +229,8 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap<FieldP,StyleP>&
s->tellListeners(only_content_dependent);
}
} catch (const ScriptError& e) {
handle_error(ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'")));
// NOTE: don't handle errors now, we are likely in an onPaint handler
handle_error(ScriptError(e.what() + _("\n while updating styles for '") + s->fieldP->name + _("'")), false, false);
}
}
}
@@ -267,7 +268,7 @@ void SetScriptManager::updateAll() {
try {
v->update(ctx);
} catch (const ScriptError& e) {
handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")));
handle_error(ScriptError(e.what() + _("\n while updating set value '") + v->fieldP->name + _("'")), false, true);
}
}
// update card data of all cards
@@ -277,7 +278,7 @@ void SetScriptManager::updateAll() {
try {
v->update(ctx);
} catch (const ScriptError& e) {
handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")));
handle_error(ScriptError(e.what() + _("\n while updating card value '") + v->fieldP->name + _("'")), false, true);
}
}
}
@@ -307,11 +308,11 @@ void SetScriptManager::updateToUpdate(const ToUpdate& u, deque<ToUpdate>& to_upd
Age age = u.value->last_script_update;
if (starting_age < age) return; // this value was already updated
Context& ctx = getContext(u.card);
bool changes;
bool changes = false;
try {
changes = u.value->update(ctx);
} catch (const ScriptError& e) {
handle_error(ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'")));
handle_error(ScriptError(e.what() + _("\n while updating value '") + u.value->fieldP->name + _("'")), false, true);
}
if (changes) {
// changed, send event
+9 -4
View File
@@ -19,8 +19,10 @@ template <typename T>
class OrderCache : public IntrusivePtrBase<OrderCache<T> > {
public:
/// Initialize the order cache, ordering the keys by their string values from the other vector
/** @pre keys.size() == values.size() */
OrderCache(const vector<T>& keys, const vector<String>& values);
/** Optionally filter the list using a vector of booleans of items to keep (note: vector<bool> is evil)
* @pre keys.size() == values.size()
*/
OrderCache(const vector<T>& keys, const vector<String>& values, vector<int>* keep = nullptr);
/// Find the position of the given key in the cache, returns -1 if not found
int find(const T& key) const;
@@ -52,13 +54,16 @@ struct OrderCache<T>::CompareValues {
};
template <typename T>
OrderCache<T>::OrderCache(const vector<T>& keys, const vector<String>& values) {
OrderCache<T>::OrderCache(const vector<T>& keys, const vector<String>& values, vector<int>* keep) {
assert(keys.size() == values.size());
assert(!keep || keep->size() == keys.size());
// initialize positions, use pos to point back to the values vector
positions.reserve(keys.size());
int i = 0;
for (typename vector<T>::const_iterator it = keys.begin() ; it != keys.end() ; ++it, ++i) {
positions.push_back(KV(&**it, i));
if (!keep || (*keep)[i]) {
positions.push_back(KV(&**it, i));
}
}
// sort the KVs by the values
sort(positions.begin(), positions.end(), CompareValues(values));
+9 -7
View File
@@ -68,6 +68,7 @@ void writeUTF8(wxTextOutputStream& stream, const String& str);
#define RIGHT_SINGLE_QUOTE _('\x2019')
#define LEFT_DOUBLE_QUOTE _('\x201C')
#define RIGHT_DOUBLE_QUOTE _('\x201D')
#define CONNECTION_SPACE _('\xEB00') // in private use are, untags to ' '
#else
#define LEFT_ANGLE_BRACKET _("<")
#define RIGHT_ANGLE_BRACKET _(">")
@@ -75,18 +76,19 @@ void writeUTF8(wxTextOutputStream& stream, const String& str);
#define RIGHT_SINGLE_QUOTE _('\'')
#define LEFT_DOUBLE_QUOTE _('\"')
#define RIGHT_DOUBLE_QUOTE _('\"')
#define CONNECTION_SPACE _(' ') // too bad
#endif
// ----------------------------------------------------------------------------- : Char functions
// Character set tests
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace(c) ); }
inline bool isAlpha(Char c) { return IF_UNICODE( iswalpha(c) , isalpha(c) ); }
inline bool isDigit(Char c) { return IF_UNICODE( iswdigit(c) , isdigit(c) ); }
inline bool isAlnum(Char c) { return IF_UNICODE( iswalnum(c) , isalnum(c) ); }
inline bool isUpper(Char c) { return IF_UNICODE( iswupper(c) , isupper(c) ); }
inline bool isLower(Char c) { return IF_UNICODE( iswlower(c) , islower(c) ); }
inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct(c) ); }
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace((unsigned char)c) ) || c == CONNECTION_SPACE; }
inline bool isAlpha(Char c) { return IF_UNICODE( iswalpha(c) , isalpha((unsigned char)c) ); }
inline bool isDigit(Char c) { return IF_UNICODE( iswdigit(c) , isdigit((unsigned char)c) ); }
inline bool isAlnum(Char c) { return IF_UNICODE( iswalnum(c) , isalnum((unsigned char)c) ); }
inline bool isUpper(Char c) { return IF_UNICODE( iswupper(c) , isupper((unsigned char)c) ); }
inline bool isLower(Char c) { return IF_UNICODE( iswlower(c) , islower((unsigned char)c) ); }
inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct((unsigned char)c) ); }
// Character conversions
#ifdef _MSC_VER
#define CHAR_FUNCTIONS_ARE_SLOW
+14 -3
View File
@@ -11,12 +11,24 @@
// ----------------------------------------------------------------------------- : Conversion to/from normal string
Char untag_char(Char c) {
if (c == _('\1')) return _('<');
else if (c == CONNECTION_SPACE) return _(' ');
else return c;
}
Char tag_char(Char c) {
if (c == _('<')) return _('\1');
else return c;
}
String untag(const String& str) {
bool intag = false;
String ret; ret.reserve(str.size());
FOR_EACH_CONST(c, str) {
if (c==_('<')) intag = true;
if (!intag) ret += c==_('\1') ? _('<') : c;
if (!intag) ret += untag_char(c);
if (c==_('>')) intag = false;
}
return ret;
@@ -40,8 +52,7 @@ String untag_hide_sep(const String& str) {
String escape(const String& str) {
String ret; ret.reserve(str.size());
FOR_EACH_CONST(c, str) {
if (c==_('<')) ret += _('\1');
else ret += c;
ret += tag_char(c);
}
return ret;
}
+3
View File
@@ -18,6 +18,9 @@
// ----------------------------------------------------------------------------- : Conversion to/from normal string
Char untag_char(Char c);
Char tag_char(Char c);
/// Remove all tags from a string and convert escaped '<' back to normal '<'
/** e.g. "<sym>R</> something <i>(note)</>"
* becomes "R something (note)"