Commit e9a8e675 authored by twanvl's avatar twanvl

Two changes:

 * Added after_reading function that is called by Reader after reading a complete object.
   This generalizes Packaged::validate, which is now also called via this mechanism.
 * Use a single 'last_modified' Age, instead of a separate one for scripts. The counter should now only go up by 1 for each Action performed. In theory the counter could also be made local to ScriptManager.
parent effea43c
...@@ -48,9 +48,9 @@ inline void swap_value(ColorValue& a, ColorValue ::ValueType& b ...@@ -48,9 +48,9 @@ inline void swap_value(ColorValue& a, ColorValue ::ValueType& b
#if USE_SCRIPT_VALUE_VALUE #if USE_SCRIPT_VALUE_VALUE
inline void swap_value(AnyValue& a, AnyValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(AnyValue& a, AnyValue ::ValueType& b) { swap(a.value, b); }
#endif #endif
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); }
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); }
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); }
inline void swap_value(PackageChoiceValue& a, PackageChoiceValue ::ValueType& b) { swap(a.package_name, b); } inline void swap_value(PackageChoiceValue& a, PackageChoiceValue ::ValueType& b) { swap(a.package_name, b); }
/// A ValueAction that swaps between old and new values /// A ValueAction that swaps between old and new values
...@@ -99,13 +99,11 @@ ValueAction* value_action(const PackageChoiceValueP& value, const String& ...@@ -99,13 +99,11 @@ ValueAction* value_action(const PackageChoiceValueP& value, const String&
// ----------------------------------------------------------------------------- : MultipleChoice // ----------------------------------------------------------------------------- : MultipleChoice
/* #if USE_SCRIPT_VALUE_CHOICE
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) { ValueAction* value_action(const MultipleChoiceValueP& value, const ScriptValueP& new_value, const String& last_change) {
MultipleChoiceValue::ValueType v = { new_value, last_change }; #else
return new SimpleValueAction<MultipleChoiceValue, false>(value, v);
}
*/
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) { ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) {
#endif
return new MultipleChoiceValueAction(value,new_value,last_change); return new MultipleChoiceValueAction(value,new_value,last_change);
} }
...@@ -243,7 +241,6 @@ void TextToggleReminderAction::perform(bool to_undo) { ...@@ -243,7 +241,6 @@ void TextToggleReminderAction::perform(bool to_undo) {
if (end != String::npos && end + 5 < val.size()) { if (end != String::npos && end + 5 < val.size()) {
val[end + 5] = c; // </kw-c> val[end + 5] = c; // </kw-c>
} }
value.last_update.update();
value.onAction(*this, to_undo); // notify value value.onAction(*this, to_undo); // notify value
} }
......
...@@ -17,13 +17,19 @@ ...@@ -17,13 +17,19 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <util/action_stack.hpp> #include <util/action_stack.hpp>
#include <util/defaultable.hpp> #include <util/defaultable.hpp>
#include <data/field.hpp>
class Card; class Card;
class StyleSheet; class StyleSheet;
DECLARE_POINTER_TYPE(Set); DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(Style); DECLARE_POINTER_TYPE(Style);
#if !USE_SCRIPT_VALUE_TEXT
DECLARE_POINTER_TYPE(TextValue); DECLARE_POINTER_TYPE(TextValue);
#else
typedef AnyValue TextValue;
typedef AnyValueP TextValueP;
#endif
#if !USE_SCRIPT_VALUE_CHOICE #if !USE_SCRIPT_VALUE_CHOICE
DECLARE_POINTER_TYPE(ChoiceValue); DECLARE_POINTER_TYPE(ChoiceValue);
#endif #endif
...@@ -31,9 +37,15 @@ DECLARE_POINTER_TYPE(MultipleChoiceValue); ...@@ -31,9 +37,15 @@ DECLARE_POINTER_TYPE(MultipleChoiceValue);
#if !USE_SCRIPT_VALUE_COLOR #if !USE_SCRIPT_VALUE_COLOR
DECLARE_POINTER_TYPE(ColorValue); DECLARE_POINTER_TYPE(ColorValue);
#endif #endif
#if !USE_SCRIPT_VALUE_IMAGE
DECLARE_POINTER_TYPE(ImageValue); DECLARE_POINTER_TYPE(ImageValue);
#endif
#if !USE_SCRIPT_VALUE_SYMBOL
DECLARE_POINTER_TYPE(SymbolValue); DECLARE_POINTER_TYPE(SymbolValue);
#endif
#if !USE_SCRIPT_VALUE_PACKAGE
DECLARE_POINTER_TYPE(PackageChoiceValue); DECLARE_POINTER_TYPE(PackageChoiceValue);
#endif
DECLARE_POINTER_TYPE(AnyValue); DECLARE_POINTER_TYPE(AnyValue);
// ----------------------------------------------------------------------------- : ValueAction (based class) // ----------------------------------------------------------------------------- : ValueAction (based class)
...@@ -62,6 +74,9 @@ class ValueAction : public Action { ...@@ -62,6 +74,9 @@ class ValueAction : public Action {
/// Action that updates a Value to a new value /// Action that updates a Value to a new value
#if !USE_SCRIPT_VALUE_CHOICE #if !USE_SCRIPT_VALUE_CHOICE
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, const String& last_change);
#else
ValueAction* value_action(const MultipleChoiceValueP& value, const ScriptValueP& new_value, const String& last_change);
#endif #endif
#if !USE_SCRIPT_VALUE_COLOR #if !USE_SCRIPT_VALUE_COLOR
ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value); ValueAction* value_action(const ColorValueP& value, const Defaultable<Color>& new_value);
...@@ -72,13 +87,16 @@ ValueAction* value_action(const PackageChoiceValueP& value, const String& ...@@ -72,13 +87,16 @@ ValueAction* value_action(const PackageChoiceValueP& value, const String&
#if USE_SCRIPT_VALUE_VALUE #if USE_SCRIPT_VALUE_VALUE
ValueAction* value_action(const AnyValueP& value, const ScriptValueP& new_value); ValueAction* value_action(const AnyValueP& value, const ScriptValueP& new_value);
#endif #endif
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change);
// ----------------------------------------------------------------------------- : MultipleChoice // ----------------------------------------------------------------------------- : MultipleChoice
class MultipleChoiceValueAction : public ValueAction { class MultipleChoiceValueAction : public ValueAction {
public: public:
#if USE_SCRIPT_VALUE_CHOICE
inline MultipleChoiceValueAction(const ValueP& value, const ScriptValueP& new_value, const String& changed_choice)
#else
inline MultipleChoiceValueAction(const ValueP& value, const Defaultable<String>& new_value, const String& changed_choice) inline MultipleChoiceValueAction(const ValueP& value, const Defaultable<String>& new_value, const String& changed_choice)
#endif
: ValueAction(value), new_value(new_value), changed_choice(changed_choice) : ValueAction(value), new_value(new_value), changed_choice(changed_choice)
{} {}
...@@ -86,7 +104,11 @@ class MultipleChoiceValueAction : public ValueAction { ...@@ -86,7 +104,11 @@ class MultipleChoiceValueAction : public ValueAction {
const String changed_choice; ///< What choice was toggled by this action (if any) const String changed_choice; ///< What choice was toggled by this action (if any)
private: private:
#if USE_SCRIPT_VALUE_CHOICE
ScriptValueP new_value;
#else
Defaultable<String> new_value; Defaultable<String> new_value;
#endif
}; };
// ----------------------------------------------------------------------------- : Text // ----------------------------------------------------------------------------- : Text
......
...@@ -49,7 +49,6 @@ IMPLEMENT_REFLECTION(Field) { ...@@ -49,7 +49,6 @@ IMPLEMENT_REFLECTION(Field) {
REFLECT(type); REFLECT(type);
} }
REFLECT(name); REFLECT(name);
REFLECT_IF_READING name = canonical_name_form(name);
REFLECT(caption); REFLECT(caption);
REFLECT(description); REFLECT(description);
REFLECT_N("icon", icon_filename); REFLECT_N("icon", icon_filename);
...@@ -65,8 +64,12 @@ IMPLEMENT_REFLECTION(Field) { ...@@ -65,8 +64,12 @@ IMPLEMENT_REFLECTION(Field) {
REFLECT(card_list_name); REFLECT(card_list_name);
REFLECT(sort_script); REFLECT(sort_script);
REFLECT_N("card_list_alignment", card_list_align); REFLECT_N("card_list_alignment", card_list_align);
REFLECT_IF_READING if(caption.empty()) caption = name_to_caption(name); }
REFLECT_IF_READING if(card_list_name.empty()) card_list_name = capitalize(caption);
void Field::after_reading(Version ver) {
name = canonical_name_form(name);
if(caption.empty()) caption = name_to_caption(name);
if(card_list_name.empty()) card_list_name = capitalize(caption);
} }
template <> template <>
...@@ -276,13 +279,9 @@ bool Value::equals(const Value* that) { ...@@ -276,13 +279,9 @@ bool Value::equals(const Value* that) {
} }
bool Value::update(Context& ctx, const Action*) { bool Value::update(Context& ctx, const Action*) {
updateAge();
updateSortValue(ctx); updateSortValue(ctx);
return false; return false;
} }
void Value::updateAge() {
last_script_update.update();
}
void Value::updateSortValue(Context& ctx) { void Value::updateSortValue(Context& ctx) {
sort_value = fieldP->sort_script.invoke(ctx)->toString(); sort_value = fieldP->sort_script.invoke(ctx)->toString();
} }
...@@ -312,7 +311,6 @@ void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String& ...@@ -312,7 +311,6 @@ void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String&
DECLARE_DYNAMIC_ARG(Field const*, field_for_reading); DECLARE_DYNAMIC_ARG(Field const*, field_for_reading);
IMPLEMENT_DYNAMIC_ARG(Field const*, field_for_reading, nullptr); IMPLEMENT_DYNAMIC_ARG(Field const*, field_for_reading, nullptr);
ScriptValueP parse_script_value(String::const_iterator& it, String::const_iterator const& end) {
// possible values: // possible values:
// * "quoted string" # a string // * "quoted string" # a string
// * 123.456 # a number // * 123.456 # a number
...@@ -321,27 +319,7 @@ ScriptValueP parse_script_value(String::const_iterator& it, String::const_iterat ...@@ -321,27 +319,7 @@ ScriptValueP parse_script_value(String::const_iterator& it, String::const_iterat
// * nil # nil // * nil # nil
// * true/false # a boolean // * true/false # a boolean
// * mark_default(value) # a value marked as being default // * mark_default(value) # a value marked as being default
throw "TODO";
// reader.warning();
}
ScriptValueP parse_script_value(String const& str) {
String::const_iterator it = str.begin();
ScriptValueP val = parse_script_value(it, str.end());
// stuff after the value
while (it != str.end()) {
if (*it == _('#')) {
// comment until end of line
while (it != str.end() && *it != _('\n')) ++it;
} else if (isSpace(*it)) {
// ignore whitespace
++it;
} else {
//reader.warning(_(""));
break;
}
}
return val;
}
void parse_errors_to_reader_warnings(Reader& reader, vector<ScriptParseError> const& errors); void parse_errors_to_reader_warnings(Reader& reader, vector<ScriptParseError> const& errors);
void Reader::handle(ScriptValueP& value) { void Reader::handle(ScriptValueP& value) {
...@@ -383,7 +361,7 @@ void Reader::handle(ScriptValueP& value) { ...@@ -383,7 +361,7 @@ void Reader::handle(ScriptValueP& value) {
handle(unparsed); handle(unparsed);
value = parse_value(unparsed, this->getPackage(), errors); value = parse_value(unparsed, this->getPackage(), errors);
if (!value) { if (!value) {
value = script_nil; value = script_default_nil;
} }
parse_errors_to_reader_warnings(*this, errors); parse_errors_to_reader_warnings(*this, errors);
} }
...@@ -396,9 +374,10 @@ void Writer::handle(ScriptValueP const& value) { ...@@ -396,9 +374,10 @@ void Writer::handle(ScriptValueP const& value) {
// ----------------------------------------------------------------------------- : AnyField // ----------------------------------------------------------------------------- : AnyField
#if USE_SCRIPT_VALUE_VALUE #if USE_SCRIPT_VALUE_VALUE
ScriptValueP script_default_nil() { static ScriptValueP x(new ScriptDefault(script_nil)); return x; } AnyField::AnyField()
: default_name(_("Default"))
AnyField::AnyField() : initial(script_default_nil()) {} , initial(script_default_nil)
{}
void AnyField::initDependencies(Context& ctx, const Dependency& dep) const { void AnyField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep); Field ::initDependencies(ctx, dep);
...@@ -410,6 +389,7 @@ IMPLEMENT_REFLECTION(AnyField) { ...@@ -410,6 +389,7 @@ IMPLEMENT_REFLECTION(AnyField) {
REFLECT_BASE(Field); REFLECT_BASE(Field);
REFLECT(script); REFLECT(script);
REFLECT_N("default", default_script); REFLECT_N("default", default_script);
REFLECT(default_name);
WITH_DYNAMIC_ARG(field_for_reading, this); WITH_DYNAMIC_ARG(field_for_reading, this);
REFLECT(initial); REFLECT(initial);
} }
...@@ -418,16 +398,19 @@ IMPLEMENT_REFLECTION(AnyField) { ...@@ -418,16 +398,19 @@ IMPLEMENT_REFLECTION(AnyField) {
AnyValue::AnyValue(AnyFieldP const& field) AnyValue::AnyValue(AnyFieldP const& field)
: Value(field), value(field->initial) : Value(field), value(field->initial)
{} {
assert(value);
}
AnyValue::AnyValue(AnyFieldP const& field, ScriptValueP const& value) AnyValue::AnyValue(AnyFieldP const& field, ScriptValueP const& value)
: Value(field), value(value) : Value(field), value(value)
{} {
assert(value);
}
/*// TODO: conflict with ColorValue::clone, from IMPLEMENT_FIELD
ValueP AnyValue::clone() const { ValueP AnyValue::clone() const {
return intrusive(new AnyValue(*this)); return intrusive(new AnyValue(*this));
}*/ }
String AnyValue::toString() const { String AnyValue::toString() const {
return value->toString(); return value->toString();
} }
...@@ -435,10 +418,10 @@ String AnyValue::toString() const { ...@@ -435,10 +418,10 @@ String AnyValue::toString() const {
bool AnyValue::update(Context& ctx, const Action* act) { bool AnyValue::update(Context& ctx, const Action* act) {
bool change = false; bool change = false;
if (ScriptDefault const* dv = dynamic_cast<ScriptDefault*>(value.get())) { if (ScriptDefault const* dv = dynamic_cast<ScriptDefault*>(value.get())) {
ScriptValueP dvv = dv->value; ScriptValueP dvv = dv->un_default;
change = field().default_script.invokeOn(ctx, dvv); change = field().default_script.invokeOn(ctx, dvv);
change |= field().script.invokeOn(ctx, dvv); change |= field().script.invokeOn(ctx, dvv);
if (change) value = intrusive(new ScriptDefault(dvv)); if (change) value = make_default(dvv);
} else { } else {
change = field().script.invokeOn(ctx, value); change = field().script.invokeOn(ctx, value);
} }
......
...@@ -35,9 +35,16 @@ DECLARE_POINTER_TYPE(ValueEditor); ...@@ -35,9 +35,16 @@ DECLARE_POINTER_TYPE(ValueEditor);
DECLARE_DYNAMIC_ARG(Value*, value_being_updated); DECLARE_DYNAMIC_ARG(Value*, value_being_updated);
// experimental: use ScriptValue to store any kind of value // experimental: use ScriptValue to store any kind of value
#define USE_SCRIPT_VALUE_VALUE 0 // TODO: get rid of the conditional stuff
#define USE_SCRIPT_VALUE_COLOR 0 // TODO2: mergre Any{Field,Value} into {Field,Value}
#define USE_SCRIPT_VALUE_CHOICE 0 #define USE_SCRIPT_VALUE_VALUE 0
#define USE_SCRIPT_VALUE_COLOR 0
#define USE_SCRIPT_VALUE_CHOICE 0
#define USE_SCRIPT_VALUE_INFO 0
#define USE_SCRIPT_VALUE_TEXT 0
#define USE_SCRIPT_VALUE_SYMBOL 0
#define USE_SCRIPT_VALUE_IMAGE 0
#define USE_SCRIPT_VALUE_PACKAGE 0
// ----------------------------------------------------------------------------- : Field // ----------------------------------------------------------------------------- : Field
...@@ -76,6 +83,7 @@ class Field : public IntrusivePtrVirtualBase { ...@@ -76,6 +83,7 @@ class Field : public IntrusivePtrVirtualBase {
/// Add the given dependency to the dependet_scripts list for the variables this field depends on /// Add the given dependency to the dependet_scripts list for the variables this field depends on
virtual void initDependencies(Context& ctx, const Dependency& dep) const; virtual void initDependencies(Context& ctx, const Dependency& dep) const;
virtual void after_reading(Version ver);
private: private:
DECLARE_REFLECTION_VIRTUAL(); DECLARE_REFLECTION_VIRTUAL();
}; };
...@@ -86,6 +94,10 @@ inline void update_index(FieldP& f, size_t index) { ...@@ -86,6 +94,10 @@ inline void update_index(FieldP& f, size_t index) {
f->index = index; f->index = index;
} }
inline void after_reading(Field& f, Version ver) {
f.after_reading(ver);
}
inline String type_name(const Field&) { inline String type_name(const Field&) {
return _TYPE_("field"); return _TYPE_("field");
} }
...@@ -213,7 +225,8 @@ class Value : public IntrusivePtrVirtualBase { ...@@ -213,7 +225,8 @@ class Value : public IntrusivePtrVirtualBase {
virtual ~Value(); virtual ~Value();
const FieldP fieldP; ///< Field this value is for, should have the right type! 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) Age last_modified; ///< When was the value last modified? Note: this also goes up on undo.
///< This variable is used by ScriptManager. It will be incremented by each round of script updates.
String sort_value; ///< How this should be sorted. String sort_value; ///< How this should be sorted.
/// Get a copy of this value /// Get a copy of this value
...@@ -243,7 +256,6 @@ class Value : public IntrusivePtrVirtualBase { ...@@ -243,7 +256,6 @@ class Value : public IntrusivePtrVirtualBase {
protected: protected:
/// update() split into two functions;. /// update() split into two functions;.
/** Derived classes should put their stuff in between if they need the age in scripts */ /** Derived classes should put their stuff in between if they need the age in scripts */
void updateAge();
void updateSortValue(Context& ctx); void updateSortValue(Context& ctx);
private: private:
...@@ -261,7 +273,7 @@ inline String type_name(const Value&) { ...@@ -261,7 +273,7 @@ inline String type_name(const Value&) {
// ----------------------------------------------------------------------------- : Utilities // ----------------------------------------------------------------------------- : Utilities
#define DECLARE_FIELD_TYPE(Type) \ #define DECLARE_FIELD_TYPE() \
DECLARE_REFLECTION(); public: \ DECLARE_REFLECTION(); public: \
virtual ValueP newValue(); \ virtual ValueP newValue(); \
virtual StyleP newStyle(); \ virtual StyleP newStyle(); \
...@@ -275,14 +287,15 @@ inline String type_name(const Value&) { ...@@ -275,14 +287,15 @@ inline String type_name(const Value&) {
ValueP Type ## Field::newValue() { \ ValueP Type ## Field::newValue() { \
return intrusive(new Type ## Value(intrusive_from_existing(this))); \ return intrusive(new Type ## Value(intrusive_from_existing(this))); \
} \ } \
StyleP Type ## Style::clone() const { \ StyleP Type ## Style::clone() const { \
return intrusive(new Type ## Style(*this)); \ return intrusive(new Type ## Style(*this)); \
} \ } \
ValueP Type ## Value::clone() const { \ String Type ## Field::typeName() const { \
return intrusive(new Type ## Value(*this)); \ return _(NAME); \
} \ }
String Type ## Field::typeName() const { \ #define IMPLEMENT_VALUE_CLONE(Type) \
return _(NAME); \ ValueP Type ## Value::clone() const { \
return intrusive(new Type ## Value(*this)); \
} }
#define DECLARE_STYLE_TYPE(Type) \ #define DECLARE_STYLE_TYPE(Type) \
...@@ -316,11 +329,12 @@ class AnyField : public Field { ...@@ -316,11 +329,12 @@ class AnyField : public Field {
AnyField(); AnyField();
OptionalScript script; ///< Script to apply to all values OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value OptionalScript default_script; ///< Script that generates the default value, can be empty if there is no default
String default_name; ///< Name of the 'default' choice
ScriptValueP initial; ///< Initial choice of a new value, if not set the first choice is used ScriptValueP initial; ///< Initial choice of a new value, if not set the first choice is used
virtual void initDependencies(Context&, const Dependency&) const;
DECLARE_REFLECTION_VIRTUAL(); DECLARE_REFLECTION_VIRTUAL();
virtual void initDependencies(Context&, const Dependency&) const;
}; };
DECLARE_POINTER_TYPE(AnyField) DECLARE_POINTER_TYPE(AnyField)
......
...@@ -15,15 +15,22 @@ BooleanField::BooleanField() { ...@@ -15,15 +15,22 @@ BooleanField::BooleanField() {
choices->choices.push_back(intrusive(new Choice(_("yes")))); choices->choices.push_back(intrusive(new Choice(_("yes"))));
choices->choices.push_back(intrusive(new Choice(_("no")))); choices->choices.push_back(intrusive(new Choice(_("no"))));
choices->initIds(); choices->initIds();
#if USE_SCRIPT_VALUE_CHOICE
initial = script_true;
#endif
} }
IMPLEMENT_FIELD_TYPE(Boolean, "boolean"); IMPLEMENT_FIELD_TYPE(Boolean, "boolean");
IMPLEMENT_REFLECTION(BooleanField) { IMPLEMENT_REFLECTION(BooleanField) {
#if USE_SCRIPT_VALUE_CHOICE
REFLECT_BASE(AnyField); // NOTE: don't reflect as a ChoiceField, TODO: why was that?
#else
REFLECT_BASE(Field); // NOTE: don't reflect as a ChoiceField REFLECT_BASE(Field); // NOTE: don't reflect as a ChoiceField
REFLECT(script); REFLECT(script);
REFLECT_N("default", default_script); REFLECT_N("default", default_script);
REFLECT(initial); REFLECT(initial);
#endif
} }
// ----------------------------------------------------------------------------- : BooleanStyle // ----------------------------------------------------------------------------- : BooleanStyle
...@@ -41,9 +48,3 @@ BooleanStyle::BooleanStyle(const ChoiceFieldP& field) ...@@ -41,9 +48,3 @@ BooleanStyle::BooleanStyle(const ChoiceFieldP& field)
IMPLEMENT_REFLECTION(BooleanStyle) { IMPLEMENT_REFLECTION(BooleanStyle) {
REFLECT_BASE(ChoiceStyle); REFLECT_BASE(ChoiceStyle);
} }
// ----------------------------------------------------------------------------- : BooleanValue
IMPLEMENT_REFLECTION_NAMELESS(BooleanValue) {
REFLECT_BASE(ChoiceValue);
}
...@@ -16,13 +16,12 @@ ...@@ -16,13 +16,12 @@
DECLARE_POINTER_TYPE(BooleanField); DECLARE_POINTER_TYPE(BooleanField);
DECLARE_POINTER_TYPE(BooleanStyle); DECLARE_POINTER_TYPE(BooleanStyle);
DECLARE_POINTER_TYPE(BooleanValue);
/// A field whos value is either true or false /// A field whos value is either true or false
class BooleanField : public ChoiceField { class BooleanField : public ChoiceField {
public: public:
BooleanField(); BooleanField();
DECLARE_FIELD_TYPE(Boolean); DECLARE_FIELD_TYPE();
// no extra data // no extra data
}; };
...@@ -44,18 +43,8 @@ class BooleanStyle : public ChoiceStyle { ...@@ -44,18 +43,8 @@ class BooleanStyle : public ChoiceStyle {
// ----------------------------------------------------------------------------- : BooleanValue // ----------------------------------------------------------------------------- : BooleanValue
/// The Value in a BooleanField typedef ChoiceValue BooleanValue;
class BooleanValue : public ChoiceValue { typedef ChoiceValueP BooleanValueP;
public:
inline BooleanValue(const ChoiceFieldP& field) : ChoiceValue(field) {}
DECLARE_HAS_FIELD(Boolean);
virtual ValueP clone() const;
// no extra data
private:
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <data/field/choice.hpp> #include <data/field/choice.hpp>
#include <data/field/multiple_choice.hpp>
#include <util/io/package.hpp> #include <util/io/package.hpp>
#include <util/defaultable.hpp> #include <util/defaultable.hpp>
#include <wx/imaglist.h> #include <wx/imaglist.h>
...@@ -41,16 +42,27 @@ IMPLEMENT_REFLECTION(ChoiceField) { ...@@ -41,16 +42,27 @@ IMPLEMENT_REFLECTION(ChoiceField) {
REFLECT_BASE(Field); REFLECT_BASE(Field);
REFLECT(script); REFLECT(script);
REFLECT_N("default", default_script); REFLECT_N("default", default_script);
REFLECT(default_name);
REFLECT(initial); REFLECT(initial);
#endif #endif
REFLECT_N("choices", choices->choices); REFLECT_N("choices", choices->choices);
REFLECT(default_name);
REFLECT_IF_READING { REFLECT_IF_READING {
choices->initIds();
} }
REFLECT(choice_colors); REFLECT(choice_colors);
REFLECT(choice_colors_cardlist); REFLECT(choice_colors_cardlist);
} }
void ChoiceField::after_reading(Version ver) {
#if USE_SCRIPT_VALUE_CHOICE
AnyField::after_reading(ver);
choices->initIds();
if(initial == script_default_nil && !dynamic_cast<MultipleChoiceField*>(this)) {
initial = make_default(to_script(choices->choiceName(0)));
}
#else
Field::after_reading(ver);
choices->initIds();
#endif
}
// ----------------------------------------------------------------------------- : ChoiceField::Choice // ----------------------------------------------------------------------------- : ChoiceField::Choice
...@@ -294,6 +306,8 @@ IMPLEMENT_REFLECTION(ChoiceStyle) { ...@@ -294,6 +306,8 @@ IMPLEMENT_REFLECTION(ChoiceStyle) {
#if !USE_SCRIPT_VALUE_CHOICE #if !USE_SCRIPT_VALUE_CHOICE
// ----------------------------------------------------------------------------- : ChoiceValue // ----------------------------------------------------------------------------- : ChoiceValue
IMPLEMENT_VALUE_CLONE(Choice);
ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice) ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice)
: Value(field) : Value(field)
, value( !field->initial.empty() ? field->initial , value( !field->initial.empty() ? field->initial
......
...@@ -34,13 +34,12 @@ class ChoiceField : public Field { ...@@ -34,13 +34,12 @@ class ChoiceField : public Field {
#endif #endif
public: public:
ChoiceField(); ChoiceField();
DECLARE_FIELD_TYPE(Choice); DECLARE_FIELD_TYPE();
class Choice; class Choice;
typedef intrusive_ptr<Choice> ChoiceP; typedef intrusive_ptr<Choice> ChoiceP;
ChoiceP choices; ///< A choice group of possible choices ChoiceP choices; ///< A choice group of possible choices
String default_name; ///< Name of "default" value
map<String,Color> choice_colors; ///< Colors for the various choices (when color_cardlist) map<String,Color> choice_colors; ///< Colors for the various choices (when color_cardlist)
map<String,Color> choice_colors_cardlist; ///< Colors for the various choices, for in the card list map<String,Color> choice_colors_cardlist; ///< Colors for the various choices, for in the card list
...@@ -48,8 +47,11 @@ class ChoiceField : public Field { ...@@ -48,8 +47,11 @@ class ChoiceField : public Field {
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
OptionalScript script; ///< Script to apply to all values OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value OptionalScript default_script; ///< Script that generates the default value
String default_name; ///< Name of "default" value
String initial; ///< Initial choice of a new value, or "" String initial; ///< Initial choice of a new value, or ""
#endif #endif
virtual void after_reading(Version ver);
}; };
......
...@@ -16,7 +16,9 @@ DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP); ...@@ -16,7 +16,9 @@ DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP);
ColorField::ColorField() ColorField::ColorField()
: allow_custom(true) : allow_custom(true)
#if !USE_SCRIPT_VALUE_COLOR
, default_name(_("Default")) , default_name(_("Default"))
#endif
{} {}
IMPLEMENT_FIELD_TYPE(Color, "color"); IMPLEMENT_FIELD_TYPE(Color, "color");
...@@ -36,9 +38,9 @@ IMPLEMENT_REFLECTION(ColorField) { ...@@ -36,9 +38,9 @@ IMPLEMENT_REFLECTION(ColorField) {
REFLECT_BASE(Field); REFLECT_BASE(Field);
REFLECT(script); REFLECT(script);
REFLECT_N("default", default_script); REFLECT_N("default", default_script);
REFLECT(default_name);
REFLECT(initial); REFLECT(initial);
#endif #endif
REFLECT(default_name);
REFLECT(allow_custom); REFLECT(allow_custom);
REFLECT(choices); REFLECT(choices);
} }
...@@ -83,6 +85,8 @@ int ColorStyle::update(Context& ctx) { ...@@ -83,6 +85,8 @@ int ColorStyle::update(Context& ctx) {
#if !USE_SCRIPT_VALUE_COLOR #if !USE_SCRIPT_VALUE_COLOR
IMPLEMENT_VALUE_CLONE(Color);
ColorValue::ColorValue(const ColorFieldP& field) ColorValue::ColorValue(const ColorFieldP& field)
: Value(field) : Value(field)
, value( !field->initial.isDefault() ? field->initial() , value( !field->initial.isDefault() ? field->initial()
......
...@@ -31,19 +31,19 @@ class ColorField : public Field { ...@@ -31,19 +31,19 @@ class ColorField : public Field {
#endif #endif
public: public:
ColorField(); ColorField();
DECLARE_FIELD_TYPE(Color); DECLARE_FIELD_TYPE();
class Choice; class Choice;
typedef intrusive_ptr<Choice> ChoiceP; typedef intrusive_ptr<Choice> ChoiceP;
vector<ChoiceP> choices; ///< Color choices available vector<ChoiceP> choices; ///< Color choices available
bool allow_custom; ///< Are colors not in the list of choices allowed? bool allow_custom; ///< Are colors not in the list of choices allowed?
String default_name; ///< Name of "default" value
#if !USE_SCRIPT_VALUE_COLOR #if !USE_SCRIPT_VALUE_COLOR
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
OptionalScript script; ///< Script to apply to all values OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value OptionalScript default_script; ///< Script that generates the default value
String default_name; ///< Name of "default" value
Defaultable<Color> initial; ///< Initial choice of a new value, or "" Defaultable<Color> initial; ///< Initial choice of a new value, or ""
#endif #endif
}; };
...@@ -77,7 +77,7 @@ class ColorStyle : public Style { ...@@ -77,7 +77,7 @@ class ColorStyle : public Style {
// ----------------------------------------------------------------------------- : ColorValue // ----------------------------------------------------------------------------- : ColorValue
#if USE_SCRIPT_VALUE_CHOICE #if USE_SCRIPT_VALUE_COLOR
typedef AnyValue ColorValue; typedef AnyValue ColorValue;
typedef AnyValueP ColorValueP; typedef AnyValueP ColorValueP;
#else #else
......
...@@ -33,6 +33,8 @@ int ImageStyle::update(Context& ctx) { ...@@ -33,6 +33,8 @@ int ImageStyle::update(Context& ctx) {
// ----------------------------------------------------------------------------- : ImageValue // ----------------------------------------------------------------------------- : ImageValue
IMPLEMENT_VALUE_CLONE(Image);
String ImageValue::toString() const { String ImageValue::toString() const {
return filename.empty() ? wxEmptyString : _("<image>"); return filename.empty() ? wxEmptyString : _("<image>");
} }
...@@ -48,5 +50,5 @@ void ImageValue::reflect(Writer& reflector) { ...@@ -48,5 +50,5 @@ void ImageValue::reflect(Writer& reflector) {
void ImageValue::reflect(GetMember& reflector) {} void ImageValue::reflect(GetMember& reflector) {}
void ImageValue::reflect(GetDefaultMember& reflector) { void ImageValue::reflect(GetDefaultMember& reflector) {
// convert to ScriptImageP for scripting // convert to ScriptImageP for scripting
reflector.handle( (ScriptValueP)intrusive(new ImageValueToImage(filename, last_update)) ); reflector.handle( (ScriptValueP)intrusive(new ImageValueToImage(filename)) );
} }
...@@ -18,13 +18,19 @@ ...@@ -18,13 +18,19 @@
DECLARE_POINTER_TYPE(ImageField); DECLARE_POINTER_TYPE(ImageField);
DECLARE_POINTER_TYPE(ImageStyle); DECLARE_POINTER_TYPE(ImageStyle);
#if !USE_SCRIPT_VALUE_IMAGE
DECLARE_POINTER_TYPE(ImageValue); DECLARE_POINTER_TYPE(ImageValue);
#endif
/// A field for image values /// A field for image values
#if USE_SCRIPT_VALUE_IMAGE
class ImageField : public AnyField {
#else
class ImageField : public Field { class ImageField : public Field {
#endif
public: public:
// no extra data // no extra data
DECLARE_FIELD_TYPE(Image); DECLARE_FIELD_TYPE();
}; };
// ----------------------------------------------------------------------------- : ImageStyle // ----------------------------------------------------------------------------- : ImageStyle
...@@ -42,6 +48,10 @@ class ImageStyle : public Style { ...@@ -42,6 +48,10 @@ class ImageStyle : public Style {
// ----------------------------------------------------------------------------- : ImageValue // ----------------------------------------------------------------------------- : ImageValue
#if USE_SCRIPT_VALUE_IMAGE
typedef AnyValue ImageValue;
typedef AnyValueP ImageValueP;
#else
/// The Value in a ImageField, i.e. an image /// The Value in a ImageField, i.e. an image
class ImageValue : public Value { class ImageValue : public Value {
public: public:
...@@ -49,8 +59,8 @@ class ImageValue : public Value { ...@@ -49,8 +59,8 @@ class ImageValue : public Value {
DECLARE_VALUE_TYPE(Image, FileName); DECLARE_VALUE_TYPE(Image, FileName);
ValueType filename; ///< Filename of the image (in the current package), or "" ValueType filename; ///< Filename of the image (in the current package), or ""
Age last_update; ///< When was the image last changed?
}; };
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -14,6 +14,12 @@ ...@@ -14,6 +14,12 @@
IMPLEMENT_FIELD_TYPE(Info, "info"); IMPLEMENT_FIELD_TYPE(Info, "info");
#if USE_SCRIPT_VALUE_INFO
IMPLEMENT_REFLECTION(InfoField) {
REFLECT_BASE(AnyField);
REFLECT_IF_READING if(initial == script_default_nil) initial = to_script(caption);
}
#else
void InfoField::initDependencies(Context& ctx, const Dependency& dep) const { void InfoField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep); Field ::initDependencies(ctx, dep);
script. initDependencies(ctx, dep); script. initDependencies(ctx, dep);
...@@ -24,6 +30,7 @@ IMPLEMENT_REFLECTION(InfoField) { ...@@ -24,6 +30,7 @@ IMPLEMENT_REFLECTION(InfoField) {
REFLECT_BASE(Field); REFLECT_BASE(Field);
REFLECT(script); REFLECT(script);
} }
#endif
// ----------------------------------------------------------------------------- : InfoStyle // ----------------------------------------------------------------------------- : InfoStyle
...@@ -57,8 +64,11 @@ IMPLEMENT_REFLECTION(InfoStyle) { ...@@ -57,8 +64,11 @@ IMPLEMENT_REFLECTION(InfoStyle) {
REFLECT(background_color); REFLECT(background_color);
} }
#if !USE_SCRIPT_VALUE_INFO
// ----------------------------------------------------------------------------- : InfoValue // ----------------------------------------------------------------------------- : InfoValue
IMPLEMENT_VALUE_CLONE(Info);
String InfoValue::toString() const { String InfoValue::toString() const {
return value; return value;
} }
...@@ -72,3 +82,4 @@ bool InfoValue::update(Context& ctx, const Action*) { ...@@ -72,3 +82,4 @@ bool InfoValue::update(Context& ctx, const Action*) {
IMPLEMENT_REFLECTION_NAMELESS(InfoValue) { IMPLEMENT_REFLECTION_NAMELESS(InfoValue) {
// never save // never save
} }
#endif
...@@ -19,20 +19,30 @@ ...@@ -19,20 +19,30 @@
DECLARE_POINTER_TYPE(InfoField); DECLARE_POINTER_TYPE(InfoField);
DECLARE_POINTER_TYPE(InfoStyle); DECLARE_POINTER_TYPE(InfoStyle);
#if !USE_SCRIPT_VALUE_INFO
DECLARE_POINTER_TYPE(InfoValue); DECLARE_POINTER_TYPE(InfoValue);
#endif
/// A field for informational values. /// A field for informational values.
/** These values are not editable, they are just headers, icons, labels, etc. /** These values are not editable, they are just headers, icons, labels, etc.
*/ */
#if USE_SCRIPT_VALUE_INFO
class InfoField : public AnyField {
public:
InfoField() { editable = false; }
DECLARE_FIELD_TYPE();
};
#else
class InfoField : public Field { class InfoField : public Field {
public: public:
InfoField() { editable = false; } InfoField() { editable = false; }
DECLARE_FIELD_TYPE(Text); DECLARE_FIELD_TYPE();
OptionalScript script; ///< Script to apply to all values OptionalScript script; ///< Script to apply to all values
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
}; };
#endif
// ----------------------------------------------------------------------------- : InfoStyle // ----------------------------------------------------------------------------- : InfoStyle
...@@ -54,6 +64,10 @@ class InfoStyle : public Style { ...@@ -54,6 +64,10 @@ class InfoStyle : public Style {
// ----------------------------------------------------------------------------- : InfoValue // ----------------------------------------------------------------------------- : InfoValue
#if USE_SCRIPT_VALUE_INFO
typedef AnyValue InfoValue;
typedef AnyValueP InfoValueP;
#else
/// The Value in a InfoField /// The Value in a InfoField
class InfoValue : public Value { class InfoValue : public Value {
public: public:
...@@ -64,6 +78,7 @@ class InfoValue : public Value { ...@@ -64,6 +78,7 @@ class InfoValue : public Value {
virtual bool update(Context&, const Action* = nullptr); virtual bool update(Context&, const Action* = nullptr);
}; };
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -48,25 +48,28 @@ int MultipleChoiceStyle::update(Context& ctx) { ...@@ -48,25 +48,28 @@ int MultipleChoiceStyle::update(Context& ctx) {
// ----------------------------------------------------------------------------- : MultipleChoiceValue // ----------------------------------------------------------------------------- : MultipleChoiceValue
IMPLEMENT_VALUE_CLONE(MultipleChoice);
IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) { IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) {
REFLECT_BASE(ChoiceValue); REFLECT_BASE(ChoiceValue);
} }
bool MultipleChoiceValue::update(Context& ctx, const Action* act) { bool MultipleChoiceValue::update(Context& ctx, const Action* act) {
String old_value = value(); String old_value = toString();
if (const MultipleChoiceValueAction* mvca = dynamic_cast<const MultipleChoiceValueAction*>(act)) { if (const MultipleChoiceValueAction* mvca = dynamic_cast<const MultipleChoiceValueAction*>(act)) {
ctx.setVariable(_("last_change"), to_script(mvca->changed_choice)); ctx.setVariable(_("last_change"), to_script(mvca->changed_choice));
} }
ChoiceValue::update(ctx,act); ChoiceValue::update(ctx,act);
normalForm(); normalForm();
return value() != old_value; return toString() != 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();
bool is_new = true; bool is_new = true;
FOR_EACH_CONST(c, value()) { String val = toString();
FOR_EACH_CONST(c, val) {
if (c == _(',')) { if (c == _(',')) {
is_new = true; is_new = true;
} else if (is_new) { } else if (is_new) {
...@@ -82,7 +85,7 @@ void MultipleChoiceValue::get(vector<String>& out) const { ...@@ -82,7 +85,7 @@ void MultipleChoiceValue::get(vector<String>& out) const {
} }
void MultipleChoiceValue::normalForm() { void MultipleChoiceValue::normalForm() {
String& val = value.mutateDontChangeDefault(); String val = toString();
// which choices are active? // which choices are active?
vector<bool> seen(field().choices->lastId()); vector<bool> seen(field().choices->lastId());
for (size_t pos = 0 ; pos < val.size() ; ) { for (size_t pos = 0 ; pos < val.size() ; ) {
...@@ -114,4 +117,10 @@ void MultipleChoiceValue::normalForm() { ...@@ -114,4 +117,10 @@ void MultipleChoiceValue::normalForm() {
if (val.empty()) { if (val.empty()) {
val = field().empty_choice; val = field().empty_choice;
} }
// store
#if USE_SCRIPT_VALUE_CHOICE
value = with_defaultness_of(value, to_script(val));
#else
value.assignDontChangeDefault(val);
#endif
} }
...@@ -22,7 +22,7 @@ DECLARE_POINTER_TYPE(MultipleChoiceValue); ...@@ -22,7 +22,7 @@ DECLARE_POINTER_TYPE(MultipleChoiceValue);
class MultipleChoiceField : public ChoiceField { class MultipleChoiceField : public ChoiceField {
public: public:
MultipleChoiceField(); MultipleChoiceField();
DECLARE_FIELD_TYPE(MultipleChoiceField); DECLARE_FIELD_TYPE();
UInt minimum_selection, maximum_selection; ///< How many choices can be selected simultaniously? UInt minimum_selection, maximum_selection; ///< How many choices can be selected simultaniously?
String empty_choice; ///< Name to use when nothing is selected String empty_choice; ///< Name to use when nothing is selected
...@@ -50,7 +50,11 @@ class MultipleChoiceStyle : public ChoiceStyle { ...@@ -50,7 +50,11 @@ class MultipleChoiceStyle : public ChoiceStyle {
*/ */
class MultipleChoiceValue : public ChoiceValue { class MultipleChoiceValue : public ChoiceValue {
public: public:
#if USE_SCRIPT_VALUE_CHOICE
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field) {}
#else
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, false) {} inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, false) {}
#endif
DECLARE_HAS_FIELD(MultipleChoice); DECLARE_HAS_FIELD(MultipleChoice);
virtual ValueP clone() const; virtual ValueP clone() const;
......
...@@ -50,6 +50,8 @@ IMPLEMENT_REFLECTION(PackageChoiceStyle) { ...@@ -50,6 +50,8 @@ IMPLEMENT_REFLECTION(PackageChoiceStyle) {
// ----------------------------------------------------------------------------- : PackageChoiceValue // ----------------------------------------------------------------------------- : PackageChoiceValue
IMPLEMENT_VALUE_CLONE(PackageChoice);
String PackageChoiceValue::toString() const { String PackageChoiceValue::toString() const {
PackagedP pack = getPackage(); PackagedP pack = getPackage();
if (pack.get()) return pack->short_name; if (pack.get()) return pack->short_name;
......
...@@ -20,21 +20,29 @@ DECLARE_POINTER_TYPE(Packaged); ...@@ -20,21 +20,29 @@ DECLARE_POINTER_TYPE(Packaged);
DECLARE_POINTER_TYPE(PackageChoiceField); DECLARE_POINTER_TYPE(PackageChoiceField);
DECLARE_POINTER_TYPE(PackageChoiceStyle); DECLARE_POINTER_TYPE(PackageChoiceStyle);
#if !USE_SCRIPT_VALUE_PACKAGE
DECLARE_POINTER_TYPE(PackageChoiceValue); DECLARE_POINTER_TYPE(PackageChoiceValue);
#endif
/// A field for PackageChoice values, it contains a list of choices for PackageChoices /// A field for PackageChoice values, it contains a list of choices for PackageChoices
#if USE_SCRIPT_VALUE_PACKAGE
class PackageChoiceField : public AnyField {
#else
class PackageChoiceField : public Field { class PackageChoiceField : public Field {
#endif
public: public:
PackageChoiceField() : required(true), empty_name(_("none")) {} PackageChoiceField() : required(true), empty_name(_("none")) {}
DECLARE_FIELD_TYPE(PackageChoice); DECLARE_FIELD_TYPE();
OptionalScript script; ///< Script to apply to all values String match; ///< Glob of package filenames to match
String match; ///< Package filenames to match
String initial; ///< Initial value
bool required; ///< Is selecting a package required? bool required; ///< Is selecting a package required?
String empty_name; ///< Displayed name for the empty value (if !required) String empty_name; ///< Displayed name for the empty value (if !required)
#if !USE_SCRIPT_VALUE_PACKAGE
OptionalScript script; ///< Script to apply to all values
String initial; ///< Initial value
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
#endif
}; };
// ----------------------------------------------------------------------------- : PackageChoiceStyle // ----------------------------------------------------------------------------- : PackageChoiceStyle
...@@ -52,6 +60,10 @@ class PackageChoiceStyle : public Style { ...@@ -52,6 +60,10 @@ class PackageChoiceStyle : public Style {
// ----------------------------------------------------------------------------- : PackageChoiceValue // ----------------------------------------------------------------------------- : PackageChoiceValue
#if USE_SCRIPT_VALUE_PACKAGE
typedef AnyValue PackageChoiceValue;
typedef AnyValueP PackageChoiceValueP;
#else
/// The Value in a PackageChoiceField /// The Value in a PackageChoiceField
class PackageChoiceValue : public Value { class PackageChoiceValue : public Value {
public: public:
...@@ -65,6 +77,7 @@ class PackageChoiceValue : public Value { ...@@ -65,6 +77,7 @@ class PackageChoiceValue : public Value {
virtual bool update(Context&, const Action* = nullptr); virtual bool update(Context&, const Action* = nullptr);
}; };
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -47,6 +47,8 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(SymbolVariation) { ...@@ -47,6 +47,8 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(SymbolVariation) {
// ----------------------------------------------------------------------------- : SymbolValue // ----------------------------------------------------------------------------- : SymbolValue
IMPLEMENT_VALUE_CLONE(Symbol);
String SymbolValue::toString() const { String SymbolValue::toString() const {
return filename.empty() ? wxEmptyString : _("<symbol>"); return filename.empty() ? wxEmptyString : _("<symbol>");
} }
......
...@@ -20,12 +20,18 @@ DECLARE_POINTER_TYPE(SymbolVariation); ...@@ -20,12 +20,18 @@ DECLARE_POINTER_TYPE(SymbolVariation);
DECLARE_POINTER_TYPE(SymbolField); DECLARE_POINTER_TYPE(SymbolField);
DECLARE_POINTER_TYPE(SymbolStyle); DECLARE_POINTER_TYPE(SymbolStyle);
#if !USE_SCRIPT_VALUE_SYMBOL
DECLARE_POINTER_TYPE(SymbolValue); DECLARE_POINTER_TYPE(SymbolValue);
#endif
/// A field for image values /// A field for image values
#if USE_SCRIPT_VALUE_SYMBOL
class SymbolField : public AnyField {
#else
class SymbolField : public Field { class SymbolField : public Field {
#endif
public: public:
DECLARE_FIELD_TYPE(Symbol); DECLARE_FIELD_TYPE();
// no extra data // no extra data
}; };
...@@ -62,6 +68,10 @@ class SymbolVariation : public IntrusivePtrBase<SymbolVariation> { ...@@ -62,6 +68,10 @@ class SymbolVariation : public IntrusivePtrBase<SymbolVariation> {
// ----------------------------------------------------------------------------- : SymbolValue // ----------------------------------------------------------------------------- : SymbolValue
#if USE_SCRIPT_VALUE_SYMBOL
typedef AnyValue SymbolValue;
typedef AnyValueP SymbolValueP;
#else
/// The Value in a SymbolField, i.e. a symbol /// The Value in a SymbolField, i.e. a symbol
class SymbolValue : public Value { class SymbolValue : public Value {
public: public:
...@@ -69,8 +79,8 @@ class SymbolValue : public Value { ...@@ -69,8 +79,8 @@ class SymbolValue : public Value {
DECLARE_VALUE_TYPE(Symbol, FileName); DECLARE_VALUE_TYPE(Symbol, FileName);
ValueType filename; ///< Filename of the symbol (in the current package) ValueType filename; ///< Filename of the symbol (in the current package)
Age last_update; ///< When was the symbol last changed?
}; };
#endif
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -132,16 +132,16 @@ IMPLEMENT_REFLECTION(TextStyle) { ...@@ -132,16 +132,16 @@ IMPLEMENT_REFLECTION(TextStyle) {
// ----------------------------------------------------------------------------- : TextValue // ----------------------------------------------------------------------------- : TextValue
IMPLEMENT_VALUE_CLONE(Text);
String TextValue::toString() const { String TextValue::toString() const {
return untag_hide_sep(value()); return untag_hide_sep(value());
} }
bool TextValue::update(Context& ctx, const Action*) { bool TextValue::update(Context& ctx, const Action*) {
updateAge(); WITH_DYNAMIC_ARG(last_update_age, last_modified.get());
WITH_DYNAMIC_ARG(last_update_age, last_update.get());
WITH_DYNAMIC_ARG(value_being_updated, this); WITH_DYNAMIC_ARG(value_being_updated, this);
bool change = field().default_script.invokeOnDefault(ctx, value) bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value); | field(). script.invokeOn(ctx, value);
if (change) last_update.update();
updateSortValue(ctx); updateSortValue(ctx);
return change; return change;
} }
......
...@@ -24,23 +24,30 @@ ...@@ -24,23 +24,30 @@
DECLARE_POINTER_TYPE(TextField); DECLARE_POINTER_TYPE(TextField);
DECLARE_POINTER_TYPE(TextStyle); DECLARE_POINTER_TYPE(TextStyle);
#if !USE_SCRIPT_VALUE_TEXT
DECLARE_POINTER_TYPE(TextValue); DECLARE_POINTER_TYPE(TextValue);
DECLARE_POINTER_TYPE(TextBackground); #endif
/// A field for values containing tagged text /// A field for values containing tagged text
#if USE_SCRIPT_VALUE_TEXT
class TextField : public AnyField {
#else
class TextField : public Field { class TextField : public Field {
#endif
public: public:
TextField(); TextField();
DECLARE_FIELD_TYPE(Text); DECLARE_FIELD_TYPE();
bool multi_line; ///< Are newlines allowed in the text?
#if !USE_SCRIPT_VALUE_TEXT
OptionalScript script; ///< Script to apply to all values OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value OptionalScript default_script; ///< Script that generates the default value
//%OptionalScript view_script; ///< Script to apply before viewing //%OptionalScript view_script; ///< Script to apply before viewing
//%OptionalScript unview_script; ///< Script to apply after changes to the view //%OptionalScript unview_script; ///< Script to apply after changes to the view
bool multi_line; ///< Are newlines allowed in the text?
String default_name; ///< Name of "default" value String default_name; ///< Name of "default" value
virtual void initDependencies(Context&, const Dependency&) const; virtual void initDependencies(Context&, const Dependency&) const;
#endif
}; };
// ----------------------------------------------------------------------------- : TextStyle // ----------------------------------------------------------------------------- : TextStyle
...@@ -83,17 +90,21 @@ class TextStyle : public Style { ...@@ -83,17 +90,21 @@ class TextStyle : public Style {
// ----------------------------------------------------------------------------- : TextValue // ----------------------------------------------------------------------------- : TextValue
#if USE_SCRIPT_VALUE_TEXT
typedef AnyValue TextValue;
typedef AnyValueP TextValueP;
#else
/// The Value in a TextField /// The Value in a TextField
class TextValue : public Value { class TextValue : public Value {
public: public:
inline TextValue(const TextFieldP& field) : Value(field), last_update(1) {} inline TextValue(const TextFieldP& field) : Value(field) {}
DECLARE_VALUE_TYPE(Text, Defaultable<String>); DECLARE_VALUE_TYPE(Text, Defaultable<String>);
ValueType value; ///< The text of this value ValueType value; ///< The text of this value
Age last_update; ///< When was the text last changed?
virtual bool update(Context&, const Action* = nullptr); virtual bool update(Context&, const Action* = nullptr);
}; };
#endif
// ----------------------------------------------------------------------------- : TextValue // ----------------------------------------------------------------------------- : TextValue
......
...@@ -43,19 +43,19 @@ IMPLEMENT_REFLECTION(PackType) { ...@@ -43,19 +43,19 @@ IMPLEMENT_REFLECTION(PackType) {
REFLECT(select); REFLECT(select);
REFLECT(filter); REFLECT(filter);
REFLECT(items); REFLECT(items);
REFLECT_IF_READING { }
if (select == SELECT_AUTO) { void after_reading(PackType& pt, Version) {
if (filter) select = SELECT_NO_REPLACE; if (pt.select == SELECT_AUTO) {
else if (!items.empty()) select = SELECT_ALL; if (pt.filter) pt.select = SELECT_NO_REPLACE;
} else if (!pt.items.empty()) pt.select = SELECT_ALL;
if (indeterminate(summary)) { }
if (filter) summary = true; if (indeterminate(pt.summary)) {
else if (!items.empty()) summary = false; if (pt.filter) pt.summary = true;
} else if (!pt.items.empty()) pt.summary = false;
if (indeterminate(selectable)) { }
if (filter) selectable = false; if (indeterminate(pt.selectable)) {
else if (!items.empty()) selectable = true; if (pt.filter) pt.selectable = false;
} else if (!pt.items.empty()) pt.selectable = true;
} }
} }
......
...@@ -57,6 +57,7 @@ class PackType : public IntrusivePtrBase<PackType> { ...@@ -57,6 +57,7 @@ class PackType : public IntrusivePtrBase<PackType> {
private: private:
DECLARE_REFLECTION(); DECLARE_REFLECTION();
}; };
void after_reading(PackType&, Version);
/// An item in a PackType /// An item in a PackType
class PackItem : public IntrusivePtrBase<PackItem> { class PackItem : public IntrusivePtrBase<PackItem> {
......
...@@ -484,8 +484,8 @@ bool SymbolToImage::operator == (const GeneratedImage& that) const { ...@@ -484,8 +484,8 @@ bool SymbolToImage::operator == (const GeneratedImage& that) const {
// ----------------------------------------------------------------------------- : ImageValueToImage // ----------------------------------------------------------------------------- : ImageValueToImage
ImageValueToImage::ImageValueToImage(const String& filename, Age age) ImageValueToImage::ImageValueToImage(const String& filename)
: filename(filename), age(age) : filename(filename)
{} {}
ImageValueToImage::~ImageValueToImage() {} ImageValueToImage::~ImageValueToImage() {}
...@@ -504,6 +504,5 @@ Image ImageValueToImage::generate(const Options& opt) const { ...@@ -504,6 +504,5 @@ Image ImageValueToImage::generate(const Options& opt) const {
} }
bool ImageValueToImage::operator == (const GeneratedImage& that) const { bool ImageValueToImage::operator == (const GeneratedImage& that) const {
const ImageValueToImage* that2 = dynamic_cast<const ImageValueToImage*>(&that); const ImageValueToImage* that2 = dynamic_cast<const ImageValueToImage*>(&that);
return that2 && filename == that2->filename return that2 && filename == that2->filename;
&& age == that2->age;
} }
...@@ -375,7 +375,7 @@ class SymbolToImage : public GeneratedImage { ...@@ -375,7 +375,7 @@ class SymbolToImage : public GeneratedImage {
/// Use an image from an ImageValue as an image /// Use an image from an ImageValue as an image
class ImageValueToImage : public GeneratedImage { class ImageValueToImage : public GeneratedImage {
public: public:
ImageValueToImage(const String& filename, Age age); ImageValueToImage(const String& filename);
~ImageValueToImage(); ~ImageValueToImage();
virtual Image generate(const Options& opt) const; virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const; virtual bool operator == (const GeneratedImage& that) const;
...@@ -383,7 +383,6 @@ class ImageValueToImage : public GeneratedImage { ...@@ -383,7 +383,6 @@ class ImageValueToImage : public GeneratedImage {
private: private:
ImageValueToImage(const ImageValueToImage&); // copy ctor ImageValueToImage(const ImageValueToImage&); // copy ctor
String filename; String filename;
Age age; ///< Age the symbol was last updated
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -39,8 +39,10 @@ SCRIPT_FUNCTION(new_card) { ...@@ -39,8 +39,10 @@ SCRIPT_FUNCTION(new_card) {
// set the value // set the value
if (TextValue* tvalue = dynamic_cast<TextValue*>(value)) { if (TextValue* tvalue = dynamic_cast<TextValue*>(value)) {
tvalue->value = v->toString(); tvalue->value = v->toString();
#if !USE_SCRIPT_VALUE_CHOICE
} else if (ChoiceValue* cvalue = dynamic_cast<ChoiceValue*>(value)) { } else if (ChoiceValue* cvalue = dynamic_cast<ChoiceValue*>(value)) {
cvalue->value = v->toString(); cvalue->value = v->toString();
#endif
} else if (PackageChoiceValue* pvalue = dynamic_cast<PackageChoiceValue*>(value)) { } else if (PackageChoiceValue* pvalue = dynamic_cast<PackageChoiceValue*>(value)) {
pvalue->package_name = v->toString(); pvalue->package_name = v->toString();
#if !USE_SCRIPT_VALUE_COLOR #if !USE_SCRIPT_VALUE_COLOR
......
...@@ -82,12 +82,11 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) { ...@@ -82,12 +82,11 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) {
// update the values if our input value is newer? // update the values if our input value is newer?
Age new_value_update = last_update_age(); Age new_value_update = last_update_age();
FOR_EACH_2(v, values, nv, value_parts) { FOR_EACH_2(v, values, nv, value_parts) {
//if (v->value() != nv.first && v->last_update < new_value_update) { if (v->last_modified < new_value_update) {
if (v->last_update < new_value_update) {
bool changed = v->value() != nv.first; bool changed = v->value() != nv.first;
v->value.assign(nv.first); v->value.assign(nv.first);
v->last_modified = new_value_update;
changed |= v->update(ctx); changed |= v->update(ctx);
v->last_update = new_value_update;
if (changed) { // notify of change if (changed) { // notify of change
SCRIPT_OPTIONAL_PARAM_(CardP, card); SCRIPT_OPTIONAL_PARAM_(CardP, card);
SCRIPT_PARAM(Set*, set); SCRIPT_PARAM(Set*, set);
...@@ -204,10 +203,14 @@ SCRIPT_FUNCTION(primary_choice) { ...@@ -204,10 +203,14 @@ SCRIPT_FUNCTION(primary_choice) {
if (!value) { if (!value) {
throw ScriptError(_("Argument to 'primary_choice' should be a choice value")); throw ScriptError(_("Argument to 'primary_choice' should be a choice value"));
} }
ChoiceFieldP field = dynamic_pointer_cast<ChoiceField>(value->fieldP);
if (!field) {
throw ScriptError(_("Argument to 'primary_choice' should be a choice value"));
}
// determine choice // determine choice
int id = value->field().choices->choiceId(value->value); int id = field->choices->choiceId(value->toString());
// find the last group that still contains id // find the last group that still contains id
const vector<ChoiceField::ChoiceP>& choices = value->field().choices->choices; const vector<ChoiceField::ChoiceP>& choices = field->choices->choices;
FOR_EACH_CONST_REVERSE(c, choices) { FOR_EACH_CONST_REVERSE(c, choices) {
if (id >= c->first_id) { if (id >= c->first_id) {
SCRIPT_RETURN(c->name); SCRIPT_RETURN(c->name);
......
...@@ -166,7 +166,7 @@ SCRIPT_FUNCTION(symbol_variation) { ...@@ -166,7 +166,7 @@ SCRIPT_FUNCTION(symbol_variation) {
FOR_EACH(v, style->variations) { FOR_EACH(v, style->variations) {
if (v->name == variation) { if (v->name == variation) {
// found it // found it
return intrusive(new SymbolToImage(value, filename, value->last_update, v)); return intrusive(new SymbolToImage(value, filename, value->last_modified, v));
} }
} }
throw ScriptError(_("Variation of symbol not found ('") + variation + _("')")); throw ScriptError(_("Variation of symbol not found ('") + variation + _("')"));
...@@ -200,7 +200,7 @@ SCRIPT_FUNCTION(symbol_variation) { ...@@ -200,7 +200,7 @@ SCRIPT_FUNCTION(symbol_variation) {
} else { } else {
throw ScriptError(_("Unknown fill type for symbol_variation: ") + fill_type); throw ScriptError(_("Unknown fill type for symbol_variation: ") + fill_type);
} }
return intrusive(new SymbolToImage(value, filename, value ? value->last_update : Age(0), var)); return intrusive(new SymbolToImage(value, filename, value ? value->last_modified : Age(), var));
} }
} }
......
...@@ -260,9 +260,10 @@ void SetScriptManager::updateDelayed() { ...@@ -260,9 +260,10 @@ void SetScriptManager::updateDelayed() {
} }
void SetScriptManager::updateValue(Value& value, const CardP& card, Action const* action) { void SetScriptManager::updateValue(Value& value, const CardP& card, Action const* action) {
Age starting_age; // the start of the update process Age starting_age = Age::next(); // the start of the update process, use next(), so the modified value also gets a chance to be updated
deque<ToUpdate> to_update; deque<ToUpdate> to_update;
// execute script for initial changed value // execute script for initial changed value
value.last_modified = starting_age;
value.update(getContext(card), action); value.update(getContext(card), action);
#ifdef LOG_UPDATES #ifdef LOG_UPDATES
wxLogDebug(_("Start: %s"), value.fieldP->name); wxLogDebug(_("Start: %s"), value.fieldP->name);
...@@ -314,7 +315,7 @@ void SetScriptManager::updateAll() { ...@@ -314,7 +315,7 @@ void SetScriptManager::updateAll() {
void SetScriptManager::updateAllDependend(const vector<Dependency>& dependent_scripts, const CardP& card) { void SetScriptManager::updateAllDependend(const vector<Dependency>& dependent_scripts, const CardP& card) {
deque<ToUpdate> to_update; deque<ToUpdate> to_update;
Age starting_age; Age starting_age = Age::next();
alsoUpdate(to_update, dependent_scripts, card); alsoUpdate(to_update, dependent_scripts, card);
updateRecursive(to_update, starting_age); updateRecursive(to_update, starting_age);
} }
...@@ -329,8 +330,9 @@ void SetScriptManager::updateRecursive(deque<ToUpdate>& to_update, Age starting_ ...@@ -329,8 +330,9 @@ void SetScriptManager::updateRecursive(deque<ToUpdate>& to_update, Age starting_
} }
void SetScriptManager::updateToUpdate(const ToUpdate& u, deque<ToUpdate>& to_update, Age starting_age) { void SetScriptManager::updateToUpdate(const ToUpdate& u, deque<ToUpdate>& to_update, Age starting_age) {
Age age = u.value->last_script_update; Age& age = u.value->last_modified;
if (starting_age < age) return; // this value was already updated if (starting_age < age) return; // this value was already updated
age = starting_age; // mark as updated
Context& ctx = getContext(u.card); Context& ctx = getContext(u.card);
bool changes = false; bool changes = false;
try { try {
......
...@@ -19,20 +19,17 @@ ...@@ -19,20 +19,17 @@
/** Age is counted using a global variable */ /** Age is counted using a global variable */
class Age { class Age {
public: public:
/// Construct a new age value /// Default constructor: gives 'start of time'
Age() { Age() : age(0) {}
update(); Age(AtomicIntEquiv a) : age(a) {}
}
/// Create a special age
/** 0: dummy value, used for other purposes
* 1: before 'beginning of time', the age conceptually just before program start
* 2..: normal ages
*/
Age(AtomicIntEquiv age) : age(age) {}
/// Update the age to become the newest one /// Get the next age, larger than all previous ages
inline void update() { inline static Age next() {
age = ++new_age; return Age(++new_age);
}
/// Get the current age, without incrementing the global age counter
inline static Age current() {
return Age(new_age);
} }
/// Compare two ages, smaller means earlier /// Compare two ages, smaller means earlier
...@@ -52,12 +49,7 @@ class Age { ...@@ -52,12 +49,7 @@ class Age {
/// Age the object currently being processed was last updated /// Age the object currently being processed was last updated
/** NOTE: DECLARE_DYNAMIC_ARG(AtomicIntEquiv, last_update_age);
* 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 (AtomicIntEquiv, last_update_age);
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -544,7 +544,6 @@ void Packaged::open(const String& package, bool just_header) { ...@@ -544,7 +544,6 @@ void Packaged::open(const String& package, bool just_header) {
try { try {
JustAsPackageProxy proxy(this); JustAsPackageProxy proxy(this);
reader.handle_greedy(proxy); reader.handle_greedy(proxy);
Packaged::validate(reader.file_app_version);
} catch (const ParseError& err) { } catch (const ParseError& err) {
throw FileParseError(err.what(), absoluteFilename() + _("/") + typeName()); // more detailed message throw FileParseError(err.what(), absoluteFilename() + _("/") + typeName()); // more detailed message
} }
...@@ -558,7 +557,6 @@ void Packaged::loadFully() { ...@@ -558,7 +557,6 @@ void Packaged::loadFully() {
Reader reader(*stream, this, absoluteFilename() + _("/") + typeName()); Reader reader(*stream, this, absoluteFilename() + _("/") + typeName());
try { try {
reader.handle_greedy(*this); reader.handle_greedy(*this);
validate(reader.file_app_version);
fully_loaded = true; // only after loading and validating succeeded, be careful with recursion! fully_loaded = true; // only after loading and validating succeeded, be careful with recursion!
} catch (const ParseError& err) { } catch (const ParseError& err) {
throw FileParseError(err.what(), absoluteFilename() + _("/") + typeName()); // more detailed message throw FileParseError(err.what(), absoluteFilename() + _("/") + typeName()); // more detailed message
......
...@@ -261,15 +261,20 @@ class Packaged : public Package { ...@@ -261,15 +261,20 @@ class Packaged : public Package {
virtual void validate(Version file_app_version); virtual void validate(Version file_app_version);
/// What file version should be used for writing files? /// What file version should be used for writing files?
virtual Version fileVersion() const = 0; virtual Version fileVersion() const = 0;
DECLARE_REFLECTION_VIRTUAL(); DECLARE_REFLECTION_VIRTUAL();
friend void after_reading(Packaged& p, Version file_app_version);
private: private:
bool fully_loaded; ///< Is the package fully loaded? bool fully_loaded; ///< Is the package fully loaded?
friend struct JustAsPackageProxy; friend struct JustAsPackageProxy;
friend class Installer; friend class Installer;
}; };
inline void after_reading(Packaged& p, Version file_app_version) {
p.validate(file_app_version);
}
// ----------------------------------------------------------------------------- : IncludePackage // ----------------------------------------------------------------------------- : IncludePackage
/// A package that just contains a bunch of files that are used from other packages /// A package that just contains a bunch of files that are used from other packages
......
...@@ -24,6 +24,9 @@ class Packaged; ...@@ -24,6 +24,9 @@ class Packaged;
typedef wxInputStream InputStream; typedef wxInputStream InputStream;
typedef shared_ptr<wxInputStream> InputStreamP; typedef shared_ptr<wxInputStream> InputStreamP;
// Overload to perform extra stuff after reading
template <typename T> inline void after_reading(T&, Version) {}
/// The Reader can be used for reading (deserializing) objects /// The Reader can be used for reading (deserializing) objects
/** This class makes use of the reflection functionality, in effect /** This class makes use of the reflection functionality, in effect
* an object tells the Reader what fields it would like to read. * an object tells the Reader what fields it would like to read.
...@@ -68,6 +71,7 @@ class Reader { ...@@ -68,6 +71,7 @@ class Reader {
if (state != HANDLED) unknownKey(object); if (state != HANDLED) unknownKey(object);
state = OUTSIDE; state = OUTSIDE;
} while (indent >= expected_indent); } while (indent >= expected_indent);
after_reading(object, file_app_version);
} }
/// Handle an object: read it if it's name matches /// Handle an object: read it if it's name matches
...@@ -196,6 +200,11 @@ intrusive_ptr<T> read_new(Reader& reader) { ...@@ -196,6 +200,11 @@ intrusive_ptr<T> read_new(Reader& reader) {
return intrusive(new T()); return intrusive(new T());
} }
template <typename T>
inline void after_reading(intrusive_ptr<T>& x, Version ver) {
after_reading(*x,ver);
}
/// Update the 'index' member of a value for use by IndexMap /// Update the 'index' member of a value for use by IndexMap
template <typename T> void update_index(T&, size_t index) {} template <typename T> void update_index(T&, size_t index) {}
......
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