Commit 33b6aec9 authored by twanvl's avatar twanvl

Choice rendering now uses "style.image()" instead of "style.choice_images[value]";

Added script functions for working with multiple choice values;
Added in_context support for filter_rule;
Optimized toUpper/toLower because they are slow on windows (they use thread local storage and mutexes)
parent baea47bd
...@@ -26,11 +26,14 @@ String ValueAction::getName(bool to_undo) const { ...@@ -26,11 +26,14 @@ String ValueAction::getName(bool to_undo) const {
/// Swap the value in a Value object with a new one /// Swap the value in a Value object with a new one
inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); }
inline void swap_value(MultipleChoiceValue& a, MultipleChoiceValue::ValueType& b) { swap(a.value, b); }
inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); }
inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); } inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); } inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); } inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); }
inline void swap_value(MultipleChoiceValue& a, MultipleChoiceValue::ValueType& b) {
swap(a.value, b.value);
swap(a.last_change, b.last_change);
}
/// A ValueAction that swaps between old and new values /// A ValueAction that swaps between old and new values
template <typename T, bool ALLOW_MERGE> template <typename T, bool ALLOW_MERGE>
...@@ -62,10 +65,13 @@ class SimpleValueAction : public ValueAction { ...@@ -62,10 +65,13 @@ class SimpleValueAction : public ValueAction {
}; };
ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) { return new SimpleValueAction<ChoiceValue, true> (value, new_value); } ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) { return new SimpleValueAction<ChoiceValue, true> (value, new_value); }
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value) { return new SimpleValueAction<MultipleChoiceValue, false>(value, new_value); }
ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value) { return new SimpleValueAction<ColorValue, true> (value, new_value); } ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value) { return new SimpleValueAction<ColorValue, true> (value, new_value); }
ValueAction* value_action(const ImageValueP& value, const FileName& new_value) { return new SimpleValueAction<ImageValue, false>(value, new_value); } ValueAction* value_action(const ImageValueP& value, const FileName& new_value) { return new SimpleValueAction<ImageValue, false>(value, new_value); }
ValueAction* value_action(const SymbolValueP& value, const FileName& new_value) { return new SimpleValueAction<SymbolValue, false>(value, new_value); } ValueAction* value_action(const SymbolValueP& value, const FileName& new_value) { return new SimpleValueAction<SymbolValue, false>(value, new_value); }
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) {
MultipleChoiceValue::ValueType v = { new_value, last_change };
return new SimpleValueAction<MultipleChoiceValue, false>(value, v);
}
// ----------------------------------------------------------------------------- : Text // ----------------------------------------------------------------------------- : Text
......
...@@ -45,7 +45,7 @@ class ValueAction : public Action { ...@@ -45,7 +45,7 @@ class ValueAction : public Action {
/// Action that updates a Value to a new value /// Action that updates a Value to a new value
ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value); ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value);
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value); ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change);
ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value); ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value);
ValueAction* value_action(const ImageValueP& value, const FileName& new_value); ValueAction* value_action(const ImageValueP& value, const FileName& new_value);
ValueAction* value_action(const SymbolValueP& value, const FileName& new_value); ValueAction* value_action(const SymbolValueP& value, const FileName& new_value);
......
...@@ -179,6 +179,35 @@ ChoiceStyle::~ChoiceStyle() { ...@@ -179,6 +179,35 @@ ChoiceStyle::~ChoiceStyle() {
delete thumbnails; delete thumbnails;
} }
void ChoiceStyle::initImage() {
if (image.isSet() || choice_images.empty()) return;
// for, for example:
// choice images:
// a: {uvw}
// b: {xyz}
// generate the script:
// [a: {uvw}, b: {xyz}][input]() or else nil
// or in bytecode
// PUSH_CONST [a: {uvw}, b: {xyz}]
// GET_VAR input
// MEMBER
// CALL 0
// PUSH_CONST nil
// OR_ELSE
// RET
intrusive_ptr<ScriptCustomCollection> lookup(new ScriptCustomCollection());
FOR_EACH(ci, choice_images) {
lookup->key_value[ci.first] = ci.second.getScriptP();
}
Script& script = image.getScript();
script.addInstruction(I_PUSH_CONST, lookup);
script.addInstruction(I_GET_VAR, string_to_variable(_("input")));
script.addInstruction(I_BINARY, I_MEMBER);
script.addInstruction(I_CALL, 0);
script.addInstruction(I_PUSH_CONST, script_nil);
script.addInstruction(I_BINARY, I_OR_ELSE);
script.addInstruction(I_RET);
}
bool ChoiceStyle::update(Context& ctx) { bool ChoiceStyle::update(Context& ctx) {
// Don't update the choice images, leave that to invalidate() // Don't update the choice images, leave that to invalidate()
...@@ -256,6 +285,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) { ...@@ -256,6 +285,7 @@ IMPLEMENT_REFLECTION(ChoiceStyle) {
REFLECT(alignment); REFLECT(alignment);
REFLECT(angle); REFLECT(angle);
REFLECT(font); REFLECT(font);
REFLECT(image);
REFLECT(choice_images); REFLECT(choice_images);
} }
......
...@@ -138,6 +138,7 @@ class ChoiceStyle : public Style { ...@@ -138,6 +138,7 @@ class ChoiceStyle : public Style {
ChoicePopupStyle popup_style; ///< Style of popups/menus ChoicePopupStyle popup_style; ///< Style of popups/menus
ChoiceRenderStyle render_style; ///< Style of rendering ChoiceRenderStyle render_style; ///< Style of rendering
Font font; ///< Font for drawing text (when RENDER_TEXT) Font font; ///< Font for drawing text (when RENDER_TEXT)
ScriptableImage image; ///< Image to draw (when RENDER_IMAGE)
map<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE) map<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE)
bool choice_images_initialized; bool choice_images_initialized;
Scriptable<String> mask_filename; ///< Filename of an additional mask over the images Scriptable<String> mask_filename; ///< Filename of an additional mask over the images
...@@ -150,6 +151,8 @@ class ChoiceStyle : public Style { ...@@ -150,6 +151,8 @@ class ChoiceStyle : public Style {
/// Load the mask image, if it's not already done /// Load the mask image, if it's not already done
void loadMask(Package& pkg); void loadMask(Package& pkg);
/// Initialize image from choice_images
void initImage();
virtual bool update(Context&); virtual bool update(Context&);
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
......
...@@ -48,6 +48,14 @@ IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) { ...@@ -48,6 +48,14 @@ IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) {
REFLECT_BASE(ChoiceValue); REFLECT_BASE(ChoiceValue);
} }
bool MultipleChoiceValue::update(Context& ctx) {
String old_value = value();
ctx.setVariable(_("last change"), to_script(last_change));
ChoiceValue::update(ctx);
normalForm();
return value() != old_value;
}
void MultipleChoiceValue::get(vector<String>& out) const { void MultipleChoiceValue::get(vector<String>& out) const {
// split the value // split the value
out.clear(); out.clear();
...@@ -66,3 +74,38 @@ void MultipleChoiceValue::get(vector<String>& out) const { ...@@ -66,3 +74,38 @@ void MultipleChoiceValue::get(vector<String>& out) const {
} }
} }
} }
void MultipleChoiceValue::normalForm() {
String& val = value.mutateDontChangeDefault();
// which choices are active?
vector<bool> seen(field().choices->lastId());
for (size_t pos = 0 ; pos < val.size() ; ) {
if (val.GetChar(pos) == _(' ')) {
++pos; // ingore whitespace
} else {
// does this choice match the one asked about?
size_t end = val.find_first_of(_(','), pos);
if (end == String::npos) end = val.size();
// find this choice
for (size_t i = 0 ; i < seen.size() ; ++i) {
if (is_substr(val, pos, field().choices->choiceName((int)i))) {
seen[i] = true;
break;
}
}
pos = end + 1;
}
}
// now put them back in the right order
val.clear();
for (size_t i = 0 ; i < seen.size() ; ++i) {
if (seen[i]) {
if (!val.empty()) val += _(", ");
val += field().choices->choiceName((int)i);
}
}
// empty choice name
if (val.empty()) {
val = field().empty_choice;
}
}
...@@ -57,13 +57,24 @@ class MultipleChoiceValue : public ChoiceValue { ...@@ -57,13 +57,24 @@ class MultipleChoiceValue : public ChoiceValue {
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, true) {} inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, true) {}
DECLARE_HAS_FIELD(MultipleChoice); DECLARE_HAS_FIELD(MultipleChoice);
String last_choice; ///< Which of the choices was selected/deselected last? String last_change; ///< Which of the choices was selected/deselected last?
// for SimpleValueAction
struct ValueType {
ChoiceValue::ValueType value;
String last_change;
};
/// Splits the value, stores the selected choices in the out parameter /// Splits the value, stores the selected choices in the out parameter
void get(vector<String>& out) const; void get(vector<String>& out) const;
virtual bool update(Context&);
private: private:
DECLARE_REFLECTION(); DECLARE_REFLECTION();
/// Put the value in normal form (all choices ordered, empty_name
void normalForm();
}; };
// ----------------------------------------------------------------------------- : Utilities // ----------------------------------------------------------------------------- : Utilities
......
...@@ -35,8 +35,7 @@ DropDownMultipleChoiceList::DropDownMultipleChoiceList ...@@ -35,8 +35,7 @@ DropDownMultipleChoiceList::DropDownMultipleChoiceList
void DropDownMultipleChoiceList::select(size_t item) { void DropDownMultipleChoiceList::select(size_t item) {
MultipleChoiceValueEditor& mcve = dynamic_cast<MultipleChoiceValueEditor&>(cve); MultipleChoiceValueEditor& mcve = dynamic_cast<MultipleChoiceValueEditor&>(cve);
if (isFieldDefault(item)) { if (isFieldDefault(item)) {
// make default mcve.toggleDefault();
mcve.getSet().actions.add(value_action(mcve.valueP(), Defaultable<String>()));
} else { } else {
ChoiceField::ChoiceP choice = getChoice(item); ChoiceField::ChoiceP choice = getChoice(item);
mcve.toggle(choice->first_id); mcve.toggle(choice->first_id);
...@@ -143,6 +142,7 @@ void MultipleChoiceValueEditor::onValueChange() { ...@@ -143,6 +142,7 @@ void MultipleChoiceValueEditor::onValueChange() {
void MultipleChoiceValueEditor::toggle(int id) { void MultipleChoiceValueEditor::toggle(int id) {
String new_value; String new_value;
String toggled_choice;
// old selection // old selection
vector<String> selected; vector<String> selected;
value().get(selected); value().get(selected);
...@@ -157,7 +157,12 @@ void MultipleChoiceValueEditor::toggle(int id) { ...@@ -157,7 +157,12 @@ void MultipleChoiceValueEditor::toggle(int id) {
if (!new_value.empty()) new_value += _(", "); if (!new_value.empty()) new_value += _(", ");
new_value += choice; new_value += choice;
} }
if (i == id) toggled_choice = choice;
} }
// store value // store value
getSet().actions.add(value_action(valueP(), new_value)); getSet().actions.add(value_action(valueP(), new_value, toggled_choice));
}
void MultipleChoiceValueEditor::toggleDefault() {
getSet().actions.add(value_action(valueP(), Defaultable<String>(value().value(), !value().value.isDefault()), _("")));
} }
...@@ -38,6 +38,8 @@ class MultipleChoiceValueEditor : public MultipleChoiceValueViewer, public Value ...@@ -38,6 +38,8 @@ class MultipleChoiceValueEditor : public MultipleChoiceValueViewer, public Value
DropDownList& initDropDown(); DropDownList& initDropDown();
/// Toggle a choice or on or off /// Toggle a choice or on or off
void toggle(int id); void toggle(int id);
/// Toggle defaultness or on or off
void toggleDefault();
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -19,9 +19,15 @@ void ChoiceValueViewer::draw(RotatedDC& dc) { ...@@ -19,9 +19,15 @@ void ChoiceValueViewer::draw(RotatedDC& dc) {
double margin = 0; double margin = 0;
if (style().render_style & RENDER_IMAGE) { if (style().render_style & RENDER_IMAGE) {
// draw image // draw image
map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(value().value())); // map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(value().value()));
if (it != style().choice_images.end() && it->second.isReady()) { // if (it != style().choice_images.end() && it->second.isReady()) {
ScriptableImage& img = it->second; // ScriptableImage& img = it->second;
style().initImage();
ScriptableImage& img = style().image;
Context& ctx = viewer.getContext();
ctx.setVariable(_("input"), to_script(value().value()));
img.update(ctx);
if (img.isReady()) {
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet()); GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
if (nativeLook()) { if (nativeLook()) {
img_options.width = img_options.height = 16; img_options.width = img_options.height = 16;
......
...@@ -43,9 +43,12 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) { ...@@ -43,9 +43,12 @@ void MultipleChoiceValueViewer::draw(RotatedDC& dc) {
double margin = 0; double margin = 0;
if (style().render_style & RENDER_IMAGE) { if (style().render_style & RENDER_IMAGE) {
// draw image // draw image
map<String,ScriptableImage>::iterator it = style().choice_images.find(cannocial_name_form(value().value())); style().initImage();
if (it != style().choice_images.end() && it->second.isReady()) { ScriptableImage& img = style().image;
ScriptableImage& img = it->second; Context& ctx = viewer.getContext();
ctx.setVariable(_("input"), to_script(value().value()));
img.update(ctx);
if (img.isReady()) {
GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet()); GeneratedImage::Options img_options(0,0, viewer.stylesheet.get(), &getSet());
if (nativeLook()) { if (nativeLook()) {
img_options.width = img_options.height = 16; img_options.width = img_options.height = 16;
......
...@@ -35,6 +35,13 @@ SCRIPT_FUNCTION(to_title) { ...@@ -35,6 +35,13 @@ SCRIPT_FUNCTION(to_title) {
SCRIPT_RETURN(capitalize(input)); SCRIPT_RETURN(capitalize(input));
} }
// reverse a string
SCRIPT_FUNCTION(reverse) {
SCRIPT_PARAM(String, input);
reverse(input.begin(), input.end());
SCRIPT_RETURN(input);
}
// extract a substring // extract a substring
SCRIPT_FUNCTION(substring) { SCRIPT_FUNCTION(substring) {
SCRIPT_PARAM(String, input); SCRIPT_PARAM(String, input);
...@@ -360,13 +367,19 @@ class ScriptFilterRule : public ScriptValue { ...@@ -360,13 +367,19 @@ class ScriptFilterRule : public ScriptValue {
size_t start, len; size_t start, len;
bool ok = regex.GetMatch(&start, &len, 0); bool ok = regex.GetMatch(&start, &len, 0);
assert(ok); assert(ok);
ret += input.substr(start, len); // the match String inside = input.substr(start, len); // the match
input = input.substr(start + len); // everything after the match String next_input = input.substr(start + len); // everything after the match
if (!context.IsValid() || context.Matches(input.substr(0,start) + _("<match>") + next_input)) {
// no context or context match
ret += inside;
}
input = next_input;
} }
SCRIPT_RETURN(ret); SCRIPT_RETURN(ret);
} }
wxRegEx regex; ///< Regex to match wxRegEx regex; ///< Regex to match
wxRegEx context; ///< Match only in a given context, optional
}; };
// Create a regular expression rule for filtering strings // Create a regular expression rule for filtering strings
...@@ -377,6 +390,12 @@ ScriptValueP filter_rule(Context& ctx) { ...@@ -377,6 +390,12 @@ ScriptValueP filter_rule(Context& ctx) {
if (!ret->regex.Compile(match, wxRE_ADVANCED)) { if (!ret->regex.Compile(match, wxRE_ADVANCED)) {
throw ScriptError(_("Error while compiling regular expression: '")+match+_("'")); throw ScriptError(_("Error while compiling regular expression: '")+match+_("'"));
} }
// 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; return ret;
} }
...@@ -592,6 +611,7 @@ void init_script_basic_functions(Context& ctx) { ...@@ -592,6 +611,7 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("to upper"), script_to_upper); ctx.setVariable(_("to upper"), script_to_upper);
ctx.setVariable(_("to lower"), script_to_lower); ctx.setVariable(_("to lower"), script_to_lower);
ctx.setVariable(_("to title"), script_to_title); ctx.setVariable(_("to title"), script_to_title);
ctx.setVariable(_("reverse"), script_reverse);
ctx.setVariable(_("substring"), script_substring); ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains); ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("format"), script_format); ctx.setVariable(_("format"), script_format);
......
...@@ -157,10 +157,40 @@ SCRIPT_FUNCTION(primary_choice) { ...@@ -157,10 +157,40 @@ SCRIPT_FUNCTION(primary_choice) {
// ----------------------------------------------------------------------------- : Multiple choice values // ----------------------------------------------------------------------------- : Multiple choice values
/// Is the given choice active?
bool chosen(const String& choice, const String& input) {
for (size_t pos = 0 ; pos < input.size() ; ) {
if (input.GetChar(pos) == _(' ')) {
++pos; // ingore whitespace
} else {
// does this choice match the one asked about?
size_t end = input.find_first_of(_(','), pos);
if (end == String::npos) end = input.size();
if (end - pos == choice.size() && is_substr(input, pos, choice)) {
return true;
}
pos = end + 1;
}
}
return false;
}
// is the given choice active? // is the given choice active?
SCRIPT_FUNCTION(chosen) { SCRIPT_FUNCTION(chosen) {
SCRIPT_PARAM(String,choice); SCRIPT_PARAM(String,choice);
SCRIPT_PARAM(String,input); SCRIPT_PARAM(String,input);
SCRIPT_RETURN(chosen(choice, input));
}
/// Filter the choices
/** Keep at most max elements of choices,
* and at least min. Use prefered as choice to add/keep in case of conflicts.
*/
String filter_choices(const String& input, const vector<String>& choices, int min, int max, String prefered) {
if (choices.empty()) return input; // do nothing, shouldn't happen, but better make sure
String ret;
int count = 0;
vector<bool> seen(choices.size()); // which choices have we seen?
for (size_t pos = 0 ; pos < input.size() ; ) { for (size_t pos = 0 ; pos < input.size() ; ) {
if (input.GetChar(pos) == _(' ')) { if (input.GetChar(pos) == _(' ')) {
++pos; // ingore whitespace ++pos; // ingore whitespace
...@@ -168,33 +198,116 @@ SCRIPT_FUNCTION(chosen) { ...@@ -168,33 +198,116 @@ SCRIPT_FUNCTION(chosen) {
// does this choice match the one asked about? // does this choice match the one asked about?
size_t end = input.find_first_of(_(','), pos); size_t end = input.find_first_of(_(','), pos);
if (end == String::npos) end = input.size(); if (end == String::npos) end = input.size();
if (end - pos == choice.size() && is_substr(input, pos, choice)) { // is this choice in choices
SCRIPT_RETURN(true); bool in_choices = false;
for (size_t i = 0 ; i < choices.size() ; ++i) {
if (!seen[i] && is_substr(input, pos, choices[i])) {
seen[i] = true; ++count;
in_choices = true;
break;
}
}
// is this choice unaffected?
if (!in_choices) {
if (!ret.empty()) ret += _(", ");
ret += input.substr(pos, end - pos);
} }
pos = end + 1; pos = end + 1;
} }
} }
SCRIPT_RETURN(false); // keep more choices
if (count < min) {
// add prefered choice
for (size_t i = 0 ; i < choices.size() ; ++i) {
if (choices[i] == prefered) {
if (!seen[i]) {
seen[i] = true; ++count;
}
break;
}
}
// add more choices
for (size_t i = 0 ; i < choices.size() ; ++i) {
if (count >= min) break;
if (!seen[i]) {
seen[i] = true; ++count;
}
}
}
// keep less choices
if (count > max) {
for (size_t i = choices.size() - 1 ; i >= 0 ; --i) {
if (count <= max) break;
if (seen[i]) {
if (max > 0 && choices[i] == prefered) continue; // we would rather not remove prefered choice
seen[i] = false; --count;
}
}
}
// add the 'seen' choices to ret
for (size_t i = 0 ; i < choices.size() ; ++i) {
if (seen[i]) {
if (!ret.empty()) ret += _(", ");
ret += choices[i];
}
}
return ret;
}
// read 'choice#' arguments
void read_choices_param(Context& ctx, vector<String>& choices) {
for (int i = 0 ; ; ++i) {
String name = _("choice"); if (i > 0) name = name << i;
SCRIPT_OPTIONAL_PARAM_N(String, name, choice) {
choices.push_back(choice);
} else if (i > 0) break;
}
} }
// add the given choice if it is not already active // add the given choice if it is not already active
SCRIPT_FUNCTION(require_choice) { SCRIPT_FUNCTION(require_choice) {
SCRIPT_PARAM(ValueP,input); SCRIPT_PARAM(String,input);
MultipleChoiceValueP value = dynamic_pointer_cast<MultipleChoiceValue>(input); SCRIPT_OPTIONAL_PARAM_N_(String,_("last change"),last_change);
if (!value) { vector<String> choices;
throw ScriptError(_("Argument 'input' to 'require_choice' should be a multiple choice value")); read_choices_param(ctx, choices);
} SCRIPT_RETURN(filter_choices(input, choices, 1, (int)choices.size(), last_change));
SCRIPT_PARAM(String,choice); }
// TODO
SCRIPT_RETURN(input); // make sure at most one of the choices is active
SCRIPT_FUNCTION(exclusive_choice) {
SCRIPT_PARAM(String,input);
SCRIPT_OPTIONAL_PARAM_N_(String,_("last change"),last_change);
vector<String> choices;
read_choices_param(ctx, choices);
SCRIPT_RETURN(filter_choices(input, choices, 0, 1, last_change));
}
// make sure exactly one of the choices is active
SCRIPT_FUNCTION(require_exclusive_choice) {
SCRIPT_PARAM(String,input);
SCRIPT_OPTIONAL_PARAM_N_(String,_("last change"),last_change);
vector<String> choices;
read_choices_param(ctx, choices);
SCRIPT_RETURN(filter_choices(input, choices, 1, 1, last_change));
}
// make sure none of the choices are active
SCRIPT_FUNCTION(remove_choice) {
SCRIPT_PARAM(String,input);
vector<String> choices;
read_choices_param(ctx, choices);
SCRIPT_RETURN(filter_choices(input, choices, 0, 0, _("")));
} }
// ----------------------------------------------------------------------------- : Init // ----------------------------------------------------------------------------- : Init
void init_script_editor_functions(Context& ctx) { void init_script_editor_functions(Context& ctx) {
ctx.setVariable(_("forward editor"), script_combined_editor); // combatability ctx.setVariable(_("forward editor"), script_combined_editor); // compatability
ctx.setVariable(_("combined editor"), script_combined_editor); ctx.setVariable(_("combined editor"), script_combined_editor);
ctx.setVariable(_("primary choice"), script_primary_choice); ctx.setVariable(_("primary choice"), script_primary_choice);
ctx.setVariable(_("chosen"), script_chosen); ctx.setVariable(_("chosen"), script_chosen);
ctx.setVariable(_("require choice"), script_require_choice); ctx.setVariable(_("require choice"), script_require_choice);
ctx.setVariable(_("exclusive choice"), script_exclusive_choice);
ctx.setVariable(_("require exclusive choice"), script_require_exclusive_choice);
ctx.setVariable(_("remove choice"), script_remove_choice);
} }
...@@ -61,15 +61,15 @@ ScriptValueP Script::dependencies(Context& ctx, const Dependency& dep) const { ...@@ -61,15 +61,15 @@ ScriptValueP Script::dependencies(Context& ctx, const Dependency& dep) const {
} }
void Script::addInstruction(InstructionType t) { void Script::addInstruction(InstructionType t) {
//if (t == I_MEMBER_V && !instructions.empty() && instructions.back().instr == I_PUSH_CONST) {
// // optimize: push x ; member_v --> member x
// instructions.back().instr = I_MEMBER;
//} else {
Instruction i = {t, {0}}; Instruction i = {t, {0}};
instructions.push_back(i); instructions.push_back(i);
//}
} }
void Script::addInstruction(InstructionType t, unsigned int d) { void Script::addInstruction(InstructionType t, unsigned int d) {
if (t == I_BINARY && d == I_MEMBER && !instructions.empty() && instructions.back().instr == I_PUSH_CONST) {
// optimize: push x ; member --> member_c x
instructions.back().instr = I_MEMBER_C;
return;
}
Instruction i = {t, {d}}; Instruction i = {t, {d}};
instructions.push_back(i); instructions.push_back(i);
} }
......
...@@ -46,13 +46,13 @@ class Defaultable { ...@@ -46,13 +46,13 @@ class Defaultable {
is_default = false; is_default = false;
return value; return value;
} }
/// Get access to the value, for changing it, don't change the defaultness
inline T& mutateDontChangeDefault() { return value; }
/// Is this value in the default state? /// Is this value in the default state?
inline bool isDefault() const { return is_default; } inline bool isDefault() const { return is_default; }
/// Set the defaultness to true /// Set the defaultness to d
inline void makeDefault() { is_default = true; } inline void makeDefault(bool d = true) { is_default = d; }
/// Set the defaultness to false
inline void unsetDefault() { is_default = false; }
/// Compare the values, ignore defaultness /// Compare the values, ignore defaultness
/** used by scriptable to check for changes */ /** used by scriptable to check for changes */
......
...@@ -41,6 +41,30 @@ void writeUTF8(wxTextOutputStream& stream, const String& str) { ...@@ -41,6 +41,30 @@ void writeUTF8(wxTextOutputStream& stream, const String& str) {
#endif #endif
} }
// ----------------------------------------------------------------------------- : Char functions
#ifdef CHAR_FUNCTIONS_ARE_SLOW
Char toLower(Char c) {
if (c <= 128) {
if (c >= _('A') && c <= _('Z')) return c + (_('a') - _('A'));
else return c;
} else {
return IF_UNICODE( towlower(c) , tolower(c) );
}
}
Char toUpper(Char c) {
if (c <= 128) {
if (c >= _('a') && c <= _('z')) return c + (_('A') - _('a'));
else return c;
} else {
return IF_UNICODE( towupper(c) , toupper(c) );
}
}
#endif
// ----------------------------------------------------------------------------- : String utilities // ----------------------------------------------------------------------------- : String utilities
String trim(const String& s){ String trim(const String& s){
...@@ -180,7 +204,6 @@ bool smart_less(const String& as, const String& bs) { ...@@ -180,7 +204,6 @@ bool smart_less(const String& as, const String& bs) {
bool eq = true; // so far is everything equal? bool eq = true; // so far is everything equal?
FOR_EACH_2_CONST(a, as, b, bs) { FOR_EACH_2_CONST(a, as, b, bs) {
bool na = isDigit(a), nb = isDigit(b); bool na = isDigit(a), nb = isDigit(b);
Char la = toLower(a), lb = toLower(b);
if (na && nb) { if (na && nb) {
// compare numbers // compare numbers
in_num = true; in_num = true;
...@@ -198,6 +221,7 @@ bool smart_less(const String& as, const String& bs) { ...@@ -198,6 +221,7 @@ bool smart_less(const String& as, const String& bs) {
return lt; return lt;
} else { } else {
// compare characters // compare characters
Char la = toLower(a), lb = toLower(b);
if (la < lb) return true; if (la < lb) return true;
if (la > lb) return false; if (la > lb) return false;
} }
......
...@@ -88,8 +88,18 @@ inline bool isUpper(Char c) { return IF_UNICODE( iswupper(c) , isupper(c) ); } ...@@ -88,8 +88,18 @@ 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 isLower(Char c) { return IF_UNICODE( iswlower(c) , islower(c) ); }
inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct(c) ); } inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct(c) ); }
// Character conversions // Character conversions
inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); } #ifdef _MSC_VER
inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); } #define CHAR_FUNCTIONS_ARE_SLOW
#endif
#ifdef CHAR_FUNCTIONS_ARE_SLOW
// These functions are slow as hell on msvc.
// If also in other compilers, they can also use these routines.
Char toLower(Char c);
Char toUpper(Char c);
#else
inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }
inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); }
#endif
// ----------------------------------------------------------------------------- : String utilities // ----------------------------------------------------------------------------- : String utilities
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment