From 8833d07c4a37ae48306822ddeb663c33c51c03ad Mon Sep 17 00:00:00 2001 From: twanvl Date: Tue, 10 Jul 2007 18:57:41 +0000 Subject: [PATCH] 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 --- data/magic.mse-game/game | 151 +++++++++++++++-------------- src/data/field.cpp | 16 +++ src/data/field.hpp | 24 ++--- src/data/field/choice.cpp | 6 +- src/data/field/color.cpp | 5 +- src/data/field/information.cpp | 5 +- src/data/field/text.cpp | 3 +- src/data/set.cpp | 30 +++++- src/data/set.hpp | 7 +- src/gfx/generated_image.hpp | 6 +- src/gui/symbol/symmetry_editor.hpp | 2 +- src/render/symbol/viewer.cpp | 14 ++- src/render/text/element.cpp | 2 +- src/script/functions/basic.cpp | 31 ++++-- src/script/functions/export.cpp | 9 +- src/script/script_manager.cpp | 11 ++- src/util/order_cache.hpp | 13 ++- src/util/string.hpp | 16 +-- src/util/tagged_string.cpp | 17 +++- src/util/tagged_string.hpp | 3 + 20 files changed, 229 insertions(+), 142 deletions(-) diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index 6791542d..be0b6ddf 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -1,4 +1,4 @@ -mse version: 0.3.4 +mse version: 0.3.4 short name: Magic full name: Magic the Gathering icon: card-back.png @@ -126,72 +126,15 @@ init script: else "land, multicolor" ) }; - - # Index for sorting, white cards are first, so white->A, blue->B, .. , - # multi->F, hybrid->G, splits -> H, arti->I, land->K, basic land->J - is_multicolor := { chosen(choice: "multicolor") and input != "artifact, multicolor" } - is_hybrid := { chosen(choice: "hybrid") } - is_land := { chosen(choice: "land") } - is_multicolor_cost := { - colors := color_filter() - count := number_of_items(in: colors) - if count >= 2 then "yes" - else "no" - } - is_null_cost := { - input == "" or input == "0" - }; - is_color := { - name := color_name(); - contains(object.casting_cost, match:input) or (chosen(choice: name, object.card_color) and is_null_cost(object.casting_cost)) - }; - monocolor_sort := { - if is_color(object:input, "W") then "A" - else if is_color(object:input, "U") then "B" - else if is_color(object:input, "B") then "C" - else if is_color(object:input, "R") then "D" - else if is_color(object:input, "G") then "E" - else false - }; - - sort_index := { - if input.casting_cost_2 != "" and input.card_color != input.card_color_2 then "H" # multicolor splits - else if is_land (input.card_color) then (if input.rarity != "basic land" then "J" else "K") # lands, both basic and non-basic - else if contains(input.casting_cost, match:"/") then "G" # generic multicolor cards - else if is_multicolor_cost(input.casting_cost) == "yes" then "F" # multicolors (including artifacts) - else if is_multicolor(input.card_color) and is_null_cost(input.casting_cost) then "F" # costless multicolor cards - else if is_hybrid (input.card_color) and is_null_cost(input.casting_cost) then "G" # costless hybrid cards - else if monocolor_sort() != false then monocolor_sort() # regular monocolor cards - }; - rarity_sort := { - if set.special_rarity_sort == "normally" or input.rarity != "special" then sort_index() - else "Z" + sort_index() - }; - collation_set := { - if set.special_rarity_sort != "separate numbering" then filter := { true } - else if card.rarity == "special" then filter := { input.rarity == "special" } - else filter := {input.rarity != "special"} - filter_list (set, filter: filter) - }; - - card_number := { - sort_set := sort_list (collation_set(), order_by: {rarity_sort() + input.name} ) - position ( - of: card - in: sort_set - ) + 1 - + "/" + - number_of_items(in: sort_set) - }; - + # The color of a card is_artifact := match_rule(match: "(?i)Artifact") is_land := match_rule(match: "(?i)Land") card_color := { # usually the color of mana mana_color := mana_to_color(casting_cost); - if mana_color == "colorless" and is_land (input: card.super_type) then land_to_color() - else if mana_color == "colorless" and is_artifact(input: card.super_type) then "artifact" + if mana_color == "colorless" and is_land (card.super_type) then land_to_color() + else if mana_color == "colorless" and is_artifact(card.super_type) then "artifact" else mana_color }; @@ -218,6 +161,66 @@ init script: # needed by all style files anyway include file: /magic-blends.mse-include/new-blends + ############################################################## Card number + + # Index for sorting, white cards are first, so white->A, blue->B, .. , + # multi->F, hybrid->G, splits -> H, arti->I, land->K, basic land->J + is_multicolor := { chosen(choice: "multicolor") and input != "artifact, multicolor" } + is_null_cost := { input == "" or input == "0" } + sort_index := { + card_color := card.card_color + casting_cost := card.casting_cost + if card.casting_cost_2 != "" and + card_color != card.card_color_2 then "H" # multicolor splits + else if chosen(choice: "land", card_color) then ( + # land + if card.rarity != "basic land" then "J" # basic land + else "K" # non-basic land + ) else if is_null_cost(casting_cost) then ( + # no casting cost; use frame + if chosen(choice: "hybrid", card_color) then "G" # Hybrid frame + else if is_multicolor(card_color) then "F" # Multicolor frame + else if chosen(choice:"white", card_color) then "A" # White + else if chosen(choice:"blue", card_color) then "B" # Blue + else if chosen(choice:"black", card_color) then "C" # Black + else if chosen(choice:"red", card_color) then "D" # Red + else if chosen(choice:"white", card_color) then "E" # Green + else "I" # Non of the above = Colorless/artifact + ) else ( + # use the casting cost + colors := sort_rule(casting_cost, order: "") + if colors == "" then "I" # Colorless + else if contains(casting_cost, match:"/") then "G" # Hybrid cost + else if casting_cost == "W" then "A" # White + else if casting_cost == "U" then "B" # Blue + else if casting_cost == "B" then "C" # Black + else if casting_cost == "R" then "D" # Red + else if casting_cost == "G" then "E" # Green + else "F" # non of the above = multicolor + ) + } + + rarity_sort := { + if set.sort_special_rarity == "with the rest" or card.rarity != "special" then "A" else "Z" + } + set_filter := { + if set.sort_special_rarity != "separate numbering" then nil + else if card.rarity == "special" then { card.rarity == "special" } + else { card.rarity != "special" } + } + + card_number := { + position ( + of: card + in: set + order_by: { rarity_sort() + sort_index() + card.name } + filter: set_filter() + ) + 1 + } + card_count := { + number_of_items(in: set, filter: set_filter()) + } + ############################################################## Utilities for keywords # replaces — correctly @@ -529,6 +532,14 @@ set field: type: boolean name: automatic card numbers description: Should card numbers be shown on the cards? +set field: + type: choice + name: sort special rarity + description: Determines how cards with special rarity are sorted. + choice: with the rest + choice: after other cards + choice: separate numbering + initial: after other cards set field: type: boolean name: mark errors @@ -541,14 +552,6 @@ set field: choice: no description: Use gradients on multicolor cards by default, you can always change it be clicking on the card border. initial: no -set field: - type: choice - name: special rarity sort - description: Determines how cards with special rarity are sorted. - choice: normally - choice: last - choice: separate numbering - initial: last ############################# Default style @@ -561,12 +564,14 @@ default set style: variation: name: common border radius: 0.10 + #max aspect ratio: 2.5 fill type: solid fill color: rgb(0,0,0) border color: rgb(255,255,255) variation: name: uncommon border radius: 0.05 + #max aspect ratio: 2.5 fill type: linear gradient fill color 1: rgb(224,224,224) fill color 2: rgb(84, 84, 84) @@ -826,10 +831,8 @@ card field: type: text name: card number save value: false - script: - card_number() - sort script: - rarity_sort(card) + card.name + script: card_number() + "/" + card_count() + sort script: rarity_sort() + card.card_number card list visible: true card list column: 10 card list width: 50 @@ -1415,7 +1418,7 @@ keyword parameter type: name: prefix description: Prefix for things like "walk" optional: false - match: [A-Z,a-z][A-Z,a-z ]* + match: [A-Z][a-z, ]*([A-Z][a-z, ]*\xEB00) example: Forest ############################# All Magic keywords diff --git a/src/data/field.cpp b/src/data/field.cpp index 58f58719..807084ed 100644 --- a/src/data/field.cpp +++ b/src/data/field.cpp @@ -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); diff --git a/src/data/field.hpp b/src/data/field.hpp index 3776578d..90671d54 100644 --- a/src/data/field.hpp +++ b/src/data/field.hpp @@ -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(); }; diff --git a/src/data/field/choice.cpp b/src/data/field/choice.cpp index c6c5cb15..73cd3f62 100644 --- a/src/data/field/choice.cpp +++ b/src/data/field/choice.cpp @@ -8,7 +8,6 @@ #include #include -#include #include 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) { diff --git a/src/data/field/color.cpp b/src/data/field/color.cpp index 21953f75..6f259588 100644 --- a/src/data/field/color.cpp +++ b/src/data/field/color.cpp @@ -91,9 +91,10 @@ String ColorValue::toString() const { return _(""); } 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) { diff --git a/src/data/field/information.cpp b/src/data/field/information.cpp index 78e0c9dc..5d05ffee 100644 --- a/src/data/field/information.cpp +++ b/src/data/field/information.cpp @@ -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) { diff --git a/src/data/field/text.cpp b/src/data/field/text.cpp index edf5164e..b680b0ce 100644 --- a/src/data/field/text.cpp +++ b/src/data/field/text.cpp @@ -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; } diff --git a/src/data/set.cpp b/src/data/set.cpp index a67c0125..d6a8634e 100644 --- a/src/data/set.cpp +++ b/src/data/set.cpp @@ -208,23 +208,43 @@ void reflect_set_info_get_member(GetMember& tag, const IndexMap& 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 values; values.reserve(cards.size()); + vector 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 >(cards, values); + // 3. initialize order cache + order = new_intrusive3 >(cards, values, filter ? &keep : nullptr); } return order->find(card); } +int Set::numberOfCards(const ScriptValueP& filter) { + if (!filter) return (int)cards.size(); + map::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 diff --git a/src/data/set.hpp b/src/data/set.hpp index 425771c8..6607243b 100644 --- a/src/data/set.hpp +++ b/src/data/set.hpp @@ -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 thumbnail_script_context; /// Cache of cards ordered by some criterion - map order_cache; + map,OrderCacheP> order_cache; + map filter_cache; }; inline String type_name(const Set&) { diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp index e36b1af1..9a05d2b1 100644 --- a/src/gfx/generated_image.hpp +++ b/src/gfx/generated_image.hpp @@ -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 diff --git a/src/gui/symbol/symmetry_editor.hpp b/src/gui/symbol/symmetry_editor.hpp index cc88f586..5697e046 100644 --- a/src/gui/symbol/symmetry_editor.hpp +++ b/src/gui/symbol/symmetry_editor.hpp @@ -11,8 +11,8 @@ #include #include -#include +class wxSpinCtrl; class SymmetryMoveAction; // ----------------------------------------------------------------------------- : SymbolSymmetryEditor diff --git a/src/render/symbol/viewer.cpp b/src/render/symbol/viewer.cpp index 528448ff..c305ff35 100644 --- a/src/render/symbol/viewer.cpp +++ b/src/render/symbol/viewer.cpp @@ -50,7 +50,11 @@ typedef shared_ptr 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; diff --git a/src/render/text/element.cpp b/src/render/text/element.cpp index 9366e2e1..59c8fabc 100644 --- a/src/render/text/element.cpp +++ b/src/render/text/element.cpp @@ -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(te.elements.back().get()); diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index 809d7bb5..a083822d 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -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* s = dynamic_cast* >(in.get()); ScriptObject* c = dynamic_cast*>(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* s = dynamic_cast* >(in.get()); ScriptObject* c = dynamic_cast*>(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* setobj = dynamic_cast*>(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); diff --git a/src/script/functions/export.cpp b/src/script/functions/export.cpp index d477db8e..0f97b088 100644 --- a/src/script/functions/export.cpp +++ b/src/script/functions/export.cpp @@ -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 += _("<"); } else if (c == _('&')) { // escape & ret += _("&"); @@ -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); // } } } diff --git a/src/script/script_manager.cpp b/src/script/script_manager.cpp index fb0aefec..aa887be2 100644 --- a/src/script/script_manager.cpp +++ b/src/script/script_manager.cpp @@ -229,7 +229,8 @@ void SetScriptManager::updateStyles(Context& ctx, const IndexMap& 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& 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 diff --git a/src/util/order_cache.hpp b/src/util/order_cache.hpp index 1c9659f4..f7305c4b 100644 --- a/src/util/order_cache.hpp +++ b/src/util/order_cache.hpp @@ -19,8 +19,10 @@ template class OrderCache : public IntrusivePtrBase > { public: /// Initialize the order cache, ordering the keys by their string values from the other vector - /** @pre keys.size() == values.size() */ - OrderCache(const vector& keys, const vector& values); + /** Optionally filter the list using a vector of booleans of items to keep (note: vector is evil) + * @pre keys.size() == values.size() + */ + OrderCache(const vector& keys, const vector& values, vector* 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::CompareValues { }; template -OrderCache::OrderCache(const vector& keys, const vector& values) { +OrderCache::OrderCache(const vector& keys, const vector& values, vector* 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::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)); diff --git a/src/util/string.hpp b/src/util/string.hpp index ce4820ca..5b1c1f11 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -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 diff --git a/src/util/tagged_string.cpp b/src/util/tagged_string.cpp index eb7bfd55..34b2477f 100644 --- a/src/util/tagged_string.cpp +++ b/src/util/tagged_string.cpp @@ -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; } diff --git a/src/util/tagged_string.hpp b/src/util/tagged_string.hpp index d6e190a7..018d9dcc 100644 --- a/src/util/tagged_string.hpp +++ b/src/util/tagged_string.hpp @@ -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. "R something (note)" * becomes "R something (note)"