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