Change tabs to two spaces.

This commit is contained in:
Lymia Aluysia
2017-01-18 08:43:21 -06:00
parent d7f5f0dc3b
commit d2c635f739
329 changed files with 41307 additions and 41496 deletions
+61 -61
View File
@@ -25,91 +25,91 @@ enum AddingOrRemoving {ADD, REMOVE};
template <typename T>
class GenericAddAction {
public:
GenericAddAction(AddingOrRemoving, const T& item, const vector<T>& container);
GenericAddAction(AddingOrRemoving, const vector<T>& items, const vector<T>& container);
String getName() const;
void perform(vector<T>& container, bool to_undo) const;
/// A step of removing/adding
struct Step {
inline Step(size_t pos, const T& item) : pos(pos), item(item) {}
size_t pos;
T item;
};
bool adding; ///< Were objects added? (as opposed to removed)
vector<Step> steps; ///< Added/removed objects, sorted by ascending pos
GenericAddAction(AddingOrRemoving, const T& item, const vector<T>& container);
GenericAddAction(AddingOrRemoving, const vector<T>& items, const vector<T>& container);
String getName() const;
void perform(vector<T>& container, bool to_undo) const;
/// A step of removing/adding
struct Step {
inline Step(size_t pos, const T& item) : pos(pos), item(item) {}
size_t pos;
T item;
};
bool adding; ///< Were objects added? (as opposed to removed)
vector<Step> steps; ///< Added/removed objects, sorted by ascending pos
};
// ----------------------------------------------------------------------------- : Implementation
template <typename T>
bool contains(const vector<T>& items, const T& item) {
return find(items.begin(), items.end(), item) != items.end();
return find(items.begin(), items.end(), item) != items.end();
}
template <typename T>
GenericAddAction<T>::GenericAddAction(AddingOrRemoving ar, const T& item, const vector<T>& container)
: adding(ar == ADD)
: adding(ar == ADD)
{
if (ar == ADD) {
size_t pos = container.size();
steps.push_back(Step(pos, item));
} else {
for (size_t pos = 0 ; pos < container.size() ; ++pos) {
if (container[pos] == item) {
steps.push_back(Step(pos, item));
return;
}
}
throw InternalError(_("Item to remove not found in container"));
}
if (ar == ADD) {
size_t pos = container.size();
steps.push_back(Step(pos, item));
} else {
for (size_t pos = 0 ; pos < container.size() ; ++pos) {
if (container[pos] == item) {
steps.push_back(Step(pos, item));
return;
}
}
throw InternalError(_("Item to remove not found in container"));
}
}
template <typename T>
GenericAddAction<T>::GenericAddAction(AddingOrRemoving ar, const vector<T>& items, const vector<T>& container)
: adding(ar == ADD)
: adding(ar == ADD)
{
if (ar == ADD) {
size_t pos = container.size();
for (typename vector<T>::const_iterator it = items.begin() ; it != items.end() ; ++it) {
steps.push_back(Step(pos++, *it));
}
} else {
for (size_t pos = 0 ; pos < container.size() ; ++pos) {
if (contains(items, container[pos])) {
steps.push_back(Step(pos, container[pos]));
}
}
if (steps.size() != items.size()) {
throw InternalError(_("Item to remove not found in container"));
}
}
if (ar == ADD) {
size_t pos = container.size();
for (typename vector<T>::const_iterator it = items.begin() ; it != items.end() ; ++it) {
steps.push_back(Step(pos++, *it));
}
} else {
for (size_t pos = 0 ; pos < container.size() ; ++pos) {
if (contains(items, container[pos])) {
steps.push_back(Step(pos, container[pos]));
}
}
if (steps.size() != items.size()) {
throw InternalError(_("Item to remove not found in container"));
}
}
}
template <typename T>
String GenericAddAction<T>::getName() const {
String type = type_name(steps.front().item) + (steps.size() == 1 ? _("") : _("s"));
return adding ? _ACTION_1_("add item", type) : _ACTION_1_("remove item", type);
String type = type_name(steps.front().item) + (steps.size() == 1 ? _("") : _("s"));
return adding ? _ACTION_1_("add item", type) : _ACTION_1_("remove item", type);
}
template <typename T>
void GenericAddAction<T>::perform(vector<T>& container, bool to_undo) const {
if (adding != to_undo) {
// (re)insert the items
// ascending order, this is the reverse of removal
FOR_EACH_CONST(s, steps) {
assert(s.pos <= container.size());
container.insert(container.begin() + s.pos, s.item);
}
} else {
// remove the items
// descending order, because earlier removals shift the rest of the vector
FOR_EACH_CONST_REVERSE(s, steps) {
assert(s.pos < container.size());
container.erase(container.begin() + s.pos);
}
}
if (adding != to_undo) {
// (re)insert the items
// ascending order, this is the reverse of removal
FOR_EACH_CONST(s, steps) {
assert(s.pos <= container.size());
container.insert(container.begin() + s.pos, s.item);
}
} else {
// remove the items
// descending order, because earlier removals shift the rest of the vector
FOR_EACH_CONST_REVERSE(s, steps) {
assert(s.pos < container.size());
container.erase(container.begin() + s.pos);
}
}
}
// ----------------------------------------------------------------------------- : EOF
+148 -148
View File
@@ -20,191 +20,191 @@ DECLARE_TYPEOF_COLLECTION(KeywordModeP);
// ----------------------------------------------------------------------------- : Add Keyword
AddKeywordAction::AddKeywordAction(Set& set)
: KeywordListAction(set)
, action(ADD, intrusive(new Keyword()), set.keywords)
: KeywordListAction(set)
, action(ADD, intrusive(new Keyword()), set.keywords)
{
Keyword& keyword = *action.steps.front().item;
// find default mode
FOR_EACH(mode, set.game->keyword_modes) {
if (mode->is_default) {
keyword.mode = mode->name;
break;
}
}
Keyword& keyword = *action.steps.front().item;
// find default mode
FOR_EACH(mode, set.game->keyword_modes) {
if (mode->is_default) {
keyword.mode = mode->name;
break;
}
}
}
AddKeywordAction::AddKeywordAction(AddingOrRemoving ar, Set& set, const KeywordP& keyword)
: KeywordListAction(set)
, action(ar, keyword, set.keywords)
: KeywordListAction(set)
, action(ar, keyword, set.keywords)
{}
/*AddKeywordAction::AddKeywordAction(AddingOrRemoving ar, Set& set, const vector<KeywordP>& keyword)
: KeywordListAction(set)
, action(ar, keywords, set.keywords)
: KeywordListAction(set)
, action(ar, keywords, set.keywords)
{}*/
String AddKeywordAction::getName(bool to_undo) const {
return action.getName();
return action.getName();
}
void AddKeywordAction::perform(bool to_undo) {
action.perform(set.keywords, to_undo);
set.keyword_db.clear();
action.perform(set.keywords, to_undo);
set.keyword_db.clear();
}
// ----------------------------------------------------------------------------- : Changing keywords
KeywordReminderTextValue::KeywordReminderTextValue(Set& set, const TextFieldP& field, Keyword* keyword, bool editable)
: KeywordTextValue(field, keyword, &keyword->reminder.getMutableUnparsed(), editable)
, set(set)
, keyword(*keyword)
: KeywordTextValue(field, keyword, &keyword->reminder.getMutableUnparsed(), editable)
, set(set)
, keyword(*keyword)
{}
void KeywordReminderTextValue::store() {
if (!editable) {
retrieve();
return;
}
// new value
String new_value = untag(value);
// Try to parse the script
vector<ScriptParseError> parse_errors;
ScriptP new_script = parse(new_value, nullptr, true, parse_errors);
if (parse_errors.empty()) {
// parsed okay
if (checkScript(new_script)) {
// also runs okay, assign
keyword.reminder.setScriptP(new_script);
keyword.reminder.setUnparsed(new_value);
}
} else {
// parse errors, report
errors = ScriptParseErrors(parse_errors).what();
}
// re-highlight input, show errors
highlight(new_value, parse_errors);
if (!editable) {
retrieve();
return;
}
// new value
String new_value = untag(value);
// Try to parse the script
vector<ScriptParseError> parse_errors;
ScriptP new_script = parse(new_value, nullptr, true, parse_errors);
if (parse_errors.empty()) {
// parsed okay
if (checkScript(new_script)) {
// also runs okay, assign
keyword.reminder.setScriptP(new_script);
keyword.reminder.setUnparsed(new_value);
}
} else {
// parse errors, report
errors = ScriptParseErrors(parse_errors).what();
}
// re-highlight input, show errors
highlight(new_value, parse_errors);
}
void KeywordReminderTextValue::retrieve() {
vector<ScriptParseError> no_errors;
highlight(*underlying, no_errors);
vector<ScriptParseError> no_errors;
highlight(*underlying, no_errors);
}
void KeywordReminderTextValue::highlight(const String& code, const vector<ScriptParseError>& errors) {
// Add tags to indicate code / syntax highlight
// i.e. bla {if code "x" } bla
// becomes:
// bla <code>{<code-kw>if</code-kw> code "<code-string>x</code-string>" } bla
String new_value;
vector<int> in_brace; // types of braces we are in, 0 for code brace, 1 for string escapes
bool in_string = true;
vector<ScriptParseError>::const_iterator error = errors.begin();
for (size_t pos = 0 ; pos < code.size() ; ) {
// error underlining
while (error != errors.end() && error->start == error->end) ++error;
if (error != errors.end()) {
if (error->start == pos) {
new_value += _("<error>");
}
if (error->end == pos) {
++error;
if (error == errors.end() || error->start > pos) {
new_value += _("</error>");
} else {
// immediatly open again
}
}
}
// process a character
Char c = code.GetChar(pos);
if (c == _('<')) {
new_value += _('\1'); // escape
++pos;
} else if (c == _('{')) {
if (in_string) {
new_value += _("<code>");
in_brace.push_back(1);
in_string = false;
} else {
in_brace.push_back(0);
}
new_value += c;
++pos;
} else if (c == _('}') && !in_string) {
new_value += c;
if (!in_brace.empty() && in_brace.back() == 1) {
new_value += _("</code>");
in_string = true;
}
if (!in_brace.empty()) in_brace.pop_back();
++pos;
} else if (c == _('"')) {
if (in_string) {
in_string = false;
new_value += _("\"</code-str><code>");
} else {
in_string = true;
new_value += _("</code><code-str>\"");
}
++pos;
} else if (c == _('\\') && in_string && pos + 1 < code.size()) {
new_value += c + code.GetChar(pos + 1); // escape code
pos += 2;
} else if (is_substr(code, pos, _("if ")) && !in_string) {
new_value += _("<code-kw>if</code-kw> ");
pos += 3;
} else if (is_substr(code, pos, _("then ")) && !in_string) {
new_value += _("<code-kw>then</code-kw> ");
pos += 5;
} else if (is_substr(code, pos, _("else ")) && !in_string) {
new_value += _("<code-kw>else</code-kw> ");
pos += 5;
} else if (is_substr(code, pos, _("for ")) && !in_string) {
new_value += _("<code-kw>for</code-kw> ");
pos += 4;
} else if (is_substr(code, pos, _("param")) && !in_string) {
// parameter reference
size_t end = code.find_first_not_of(_("0123456789"), pos + 5);
if (end == String::npos) end = code.size();
String param = code.substr(pos, end-pos);
new_value += _("<ref-") + param + _(">") + param + _("</ref-") + param + _(">");
pos = end;
} else {
new_value += c;
++pos;
}
}
// set
value = new_value;
// Add tags to indicate code / syntax highlight
// i.e. bla {if code "x" } bla
// becomes:
// bla <code>{<code-kw>if</code-kw> code "<code-string>x</code-string>" } bla
String new_value;
vector<int> in_brace; // types of braces we are in, 0 for code brace, 1 for string escapes
bool in_string = true;
vector<ScriptParseError>::const_iterator error = errors.begin();
for (size_t pos = 0 ; pos < code.size() ; ) {
// error underlining
while (error != errors.end() && error->start == error->end) ++error;
if (error != errors.end()) {
if (error->start == pos) {
new_value += _("<error>");
}
if (error->end == pos) {
++error;
if (error == errors.end() || error->start > pos) {
new_value += _("</error>");
} else {
// immediatly open again
}
}
}
// process a character
Char c = code.GetChar(pos);
if (c == _('<')) {
new_value += _('\1'); // escape
++pos;
} else if (c == _('{')) {
if (in_string) {
new_value += _("<code>");
in_brace.push_back(1);
in_string = false;
} else {
in_brace.push_back(0);
}
new_value += c;
++pos;
} else if (c == _('}') && !in_string) {
new_value += c;
if (!in_brace.empty() && in_brace.back() == 1) {
new_value += _("</code>");
in_string = true;
}
if (!in_brace.empty()) in_brace.pop_back();
++pos;
} else if (c == _('"')) {
if (in_string) {
in_string = false;
new_value += _("\"</code-str><code>");
} else {
in_string = true;
new_value += _("</code><code-str>\"");
}
++pos;
} else if (c == _('\\') && in_string && pos + 1 < code.size()) {
new_value += c + code.GetChar(pos + 1); // escape code
pos += 2;
} else if (is_substr(code, pos, _("if ")) && !in_string) {
new_value += _("<code-kw>if</code-kw> ");
pos += 3;
} else if (is_substr(code, pos, _("then ")) && !in_string) {
new_value += _("<code-kw>then</code-kw> ");
pos += 5;
} else if (is_substr(code, pos, _("else ")) && !in_string) {
new_value += _("<code-kw>else</code-kw> ");
pos += 5;
} else if (is_substr(code, pos, _("for ")) && !in_string) {
new_value += _("<code-kw>for</code-kw> ");
pos += 4;
} else if (is_substr(code, pos, _("param")) && !in_string) {
// parameter reference
size_t end = code.find_first_not_of(_("0123456789"), pos + 5);
if (end == String::npos) end = code.size();
String param = code.substr(pos, end-pos);
new_value += _("<ref-") + param + _(">") + param + _("</ref-") + param + _(">");
pos = end;
} else {
new_value += c;
++pos;
}
}
// set
value = new_value;
}
bool KeywordReminderTextValue::checkScript(const ScriptP& script) {
try {
Context& ctx = set.cards.empty() ? set.getContext() : set.getContext(set.cards.front());
LocalScope scope(ctx);
for (size_t i = 0 ; i < keyword.parameters.size() ; ++i) {
const KeywordParam& kwp = *keyword.parameters[i];
String param_name = String(_("param")) << (int)(i+1);
String param_value = _("<atom-kwpph>") + (kwp.placeholder.empty() ? kwp.name : kwp.placeholder) + _("</atom-kwpph>");
ctx.setVariable(param_name, intrusive(new KeywordParamValue(kwp.name, _(""), _(""), param_value)));
}
script->eval(ctx);
errors.clear();
return true;
} catch (const Error& e) {
errors = e.what();
return false;
}
try {
Context& ctx = set.cards.empty() ? set.getContext() : set.getContext(set.cards.front());
LocalScope scope(ctx);
for (size_t i = 0 ; i < keyword.parameters.size() ; ++i) {
const KeywordParam& kwp = *keyword.parameters[i];
String param_name = String(_("param")) << (int)(i+1);
String param_value = _("<atom-kwpph>") + (kwp.placeholder.empty() ? kwp.name : kwp.placeholder) + _("</atom-kwpph>");
ctx.setVariable(param_name, intrusive(new KeywordParamValue(kwp.name, _(""), _(""), param_value)));
}
script->eval(ctx);
errors.clear();
return true;
} catch (const Error& e) {
errors = e.what();
return false;
}
}
// ----------------------------------------------------------------------------- : Changing keywords : mode
ChangeKeywordModeAction::ChangeKeywordModeAction(Keyword& keyword, const String& new_mode)
: keyword(keyword), mode(new_mode)
: keyword(keyword), mode(new_mode)
{}
String ChangeKeywordModeAction::getName(bool to_undo) const {
return _("Keyword mode");
return _("Keyword mode");
}
void ChangeKeywordModeAction::perform(bool to_undo) {
swap(keyword.mode, mode);
swap(keyword.mode, mode);
}
+40 -40
View File
@@ -29,23 +29,23 @@ DECLARE_TYPEOF_COLLECTION(GenericAddAction<KeywordP>::Step);
/// An Action the changes the keyword list of a set
class KeywordListAction : public Action {
public:
inline KeywordListAction(Set& set) : set(set) {}
inline KeywordListAction(Set& set) : set(set) {}
protected:
Set& set; // the set owns this action, so the set will not be destroyed before this
// therefore we don't need a smart pointer
Set& set; // the set owns this action, so the set will not be destroyed before this
// therefore we don't need a smart pointer
};
/// Adding or removing a keyword from a set
class AddKeywordAction : public KeywordListAction {
public:
AddKeywordAction(Set& set);
AddKeywordAction(AddingOrRemoving, Set& set, const KeywordP& keyword);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const GenericAddAction<KeywordP> action;
AddKeywordAction(Set& set);
AddKeywordAction(AddingOrRemoving, Set& set, const KeywordP& keyword);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const GenericAddAction<KeywordP> action;
};
// ----------------------------------------------------------------------------- : Changing keywords
@@ -59,46 +59,46 @@ class AddKeywordAction : public KeywordListAction {
*/
class KeywordTextValue : public FakeTextValue {
public:
KeywordTextValue(const TextFieldP& field, Keyword* keyword, String* underlying, bool editable, bool untagged = false)
: FakeTextValue(field, underlying, editable, untagged)
, keyword(*keyword)
{}
Keyword& keyword; ///< The keyword that is being edited
KeywordTextValue(const TextFieldP& field, Keyword* keyword, String* underlying, bool editable, bool untagged = false)
: FakeTextValue(field, underlying, editable, untagged)
, keyword(*keyword)
{}
Keyword& keyword; ///< The keyword that is being edited
};
/// A FakeTextValue that is used to edit reminder text scripts
class KeywordReminderTextValue : public KeywordTextValue {
public:
KeywordReminderTextValue(Set& set, const TextFieldP& field, Keyword* keyword, bool editable);
String errors; ///< Errors in the script
Set& set; ///< Set this keyword is in (for script checking)
Keyword& keyword; ///< The keyword we are the reminder text of
/// Try to compile the script
virtual void store();
/// Add some tags, so the script looks nice
virtual void retrieve();
/// Syntax highlight, and store in value
void highlight(const String& code, const vector<ScriptParseError>& errors);
/// Check the script for errors
bool checkScript(const ScriptP& script);
KeywordReminderTextValue(Set& set, const TextFieldP& field, Keyword* keyword, bool editable);
String errors; ///< Errors in the script
Set& set; ///< Set this keyword is in (for script checking)
Keyword& keyword; ///< The keyword we are the reminder text of
/// Try to compile the script
virtual void store();
/// Add some tags, so the script looks nice
virtual void retrieve();
/// Syntax highlight, and store in value
void highlight(const String& code, const vector<ScriptParseError>& errors);
/// Check the script for errors
bool checkScript(const ScriptP& script);
};
/// Changing the mode of a keyword
class ChangeKeywordModeAction : public Action {
public:
ChangeKeywordModeAction(Keyword& keyword, const String& new_mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
ChangeKeywordModeAction(Keyword& keyword, const String& new_mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
//private:
Keyword& keyword;
String mode;
Keyword& keyword;
String mode;
};
// ----------------------------------------------------------------------------- : EOF
+67 -67
View File
@@ -22,147 +22,147 @@ DECLARE_TYPEOF_COLLECTION(int);
// ----------------------------------------------------------------------------- : Add card
AddCardAction::AddCardAction(Set& set)
: CardListAction(set)
, action(ADD, intrusive(new Card(*set.game)), set.cards)
: CardListAction(set)
, action(ADD, intrusive(new Card(*set.game)), set.cards)
{}
AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const CardP& card)
: CardListAction(set)
, action(ar, card, set.cards)
: CardListAction(set)
, action(ar, card, set.cards)
{}
AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector<CardP>& cards)
: CardListAction(set)
, action(ar, cards, set.cards)
: CardListAction(set)
, action(ar, cards, set.cards)
{}
String AddCardAction::getName(bool to_undo) const {
return action.getName();
return action.getName();
}
void AddCardAction::perform(bool to_undo) {
action.perform(set.cards, to_undo);
action.perform(set.cards, to_undo);
}
// ----------------------------------------------------------------------------- : Reorder cards
ReorderCardsAction::ReorderCardsAction(Set& set, size_t card_id1, size_t card_id2)
: CardListAction(set), card_id1(card_id1), card_id2(card_id2)
: CardListAction(set), card_id1(card_id1), card_id2(card_id2)
{}
String ReorderCardsAction::getName(bool to_undo) const {
return _("Reorder cards");
return _("Reorder cards");
}
void ReorderCardsAction::perform(bool to_undo) {
#ifdef _DEBUG
assert(card_id1 < set.cards.size());
assert(card_id2 < set.cards.size());
#endif
if (card_id1 >= set.cards.size() || card_id2 < set.cards.size()) {
// TODO : Too lazy to fix this right now.
return;
}
swap(set.cards[card_id1], set.cards[card_id2]);
#ifdef _DEBUG
assert(card_id1 < set.cards.size());
assert(card_id2 < set.cards.size());
#endif
if (card_id1 >= set.cards.size() || card_id2 < set.cards.size()) {
// TODO : Too lazy to fix this right now.
return;
}
swap(set.cards[card_id1], set.cards[card_id2]);
}
// ----------------------------------------------------------------------------- : Change stylesheet
String DisplayChangeAction::getName(bool to_undo) const {
assert(false);
return _("");
assert(false);
return _("");
}
void DisplayChangeAction::perform(bool to_undo) {
assert(false);
assert(false);
}
ChangeCardStyleAction::ChangeCardStyleAction(const CardP& card, const StyleSheetP& stylesheet)
: card(card), stylesheet(stylesheet), has_styling(false) // styling_data(empty)
: card(card), stylesheet(stylesheet), has_styling(false) // styling_data(empty)
{}
String ChangeCardStyleAction::getName(bool to_undo) const {
return _("Change style");
return _("Change style");
}
void ChangeCardStyleAction::perform(bool to_undo) {
swap(card->stylesheet, stylesheet);
swap(card->has_styling, has_styling);
swap(card->styling_data, styling_data);
swap(card->stylesheet, stylesheet);
swap(card->has_styling, has_styling);
swap(card->styling_data, styling_data);
}
ChangeSetStyleAction::ChangeSetStyleAction(Set& set, const CardP& card)
: set(set), card(card)
: set(set), card(card)
{}
String ChangeSetStyleAction::getName(bool to_undo) const {
return _("Change style (all cards)");
return _("Change style (all cards)");
}
void ChangeSetStyleAction::perform(bool to_undo) {
if (!to_undo) {
// backup has_styling
has_styling.clear();
FOR_EACH(card, set.cards) {
has_styling.push_back(card->has_styling);
if (!card->stylesheet) {
card->has_styling = false; // this card has custom style options for the default stylesheet
}
}
stylesheet = set.stylesheet;
set.stylesheet = card->stylesheet;
card->stylesheet = StyleSheetP();
} else {
card->stylesheet = set.stylesheet;
set.stylesheet = stylesheet;
// restore has_styling
FOR_EACH_2(card, set.cards, has, has_styling) {
card->has_styling = has;
}
}
if (!to_undo) {
// backup has_styling
has_styling.clear();
FOR_EACH(card, set.cards) {
has_styling.push_back(card->has_styling);
if (!card->stylesheet) {
card->has_styling = false; // this card has custom style options for the default stylesheet
}
}
stylesheet = set.stylesheet;
set.stylesheet = card->stylesheet;
card->stylesheet = StyleSheetP();
} else {
card->stylesheet = set.stylesheet;
set.stylesheet = stylesheet;
// restore has_styling
FOR_EACH_2(card, set.cards, has, has_styling) {
card->has_styling = has;
}
}
}
ChangeCardHasStylingAction::ChangeCardHasStylingAction(Set& set, const CardP& card)
: set(set), card(card)
: set(set), card(card)
{
if (!card->has_styling) {
// copy of the set's styling data
styling_data.cloneFrom( set.stylingDataFor(set.stylesheetFor(card)) );
} else {
// the new styling data is empty
}
if (!card->has_styling) {
// copy of the set's styling data
styling_data.cloneFrom( set.stylingDataFor(set.stylesheetFor(card)) );
} else {
// the new styling data is empty
}
}
String ChangeCardHasStylingAction::getName(bool to_undo) const {
return _("Use custom style");
return _("Use custom style");
}
void ChangeCardHasStylingAction::perform(bool to_undo) {
card->has_styling = !card->has_styling;
swap(card->styling_data, styling_data);
card->has_styling = !card->has_styling;
swap(card->styling_data, styling_data);
}
// ----------------------------------------------------------------------------- : Pack types
AddPackAction::AddPackAction(AddingOrRemoving ar, Set& set, const PackTypeP& pack)
: PackTypesAction(set)
, action(ar, pack, set.pack_types)
: PackTypesAction(set)
, action(ar, pack, set.pack_types)
{}
String AddPackAction::getName(bool to_undo) const {
return action.getName();
return action.getName();
}
void AddPackAction::perform(bool to_undo) {
action.perform(set.pack_types, to_undo);
action.perform(set.pack_types, to_undo);
}
ChangePackAction::ChangePackAction(Set& set, size_t pos, const PackTypeP& pack)
: PackTypesAction(set)
, pack(pack), pos(pos)
: PackTypesAction(set)
, pack(pack), pos(pos)
{}
String ChangePackAction::getName(bool to_undo) const {
return _ACTION_1_("change",type_name(pack));
return _ACTION_1_("change",type_name(pack));
}
void ChangePackAction::perform(bool to_undo) {
swap(set.pack_types.at(pos), pack);
swap(set.pack_types.at(pos), pack);
}
+66 -66
View File
@@ -32,25 +32,25 @@ DECLARE_TYPEOF_COLLECTION(GenericAddAction<PackTypeP>::Step);
/// An Action the changes the card list of a set
class CardListAction : public Action {
public:
inline CardListAction(Set& set) : set(set) {}
inline CardListAction(Set& set) : set(set) {}
protected:
Set& set; // the set owns this action, so the set will not be destroyed before this
// therefore we don't need a smart pointer
Set& set; // the set owns this action, so the set will not be destroyed before this
// therefore we don't need a smart pointer
};
/// Adding a new card to a set
class AddCardAction : public CardListAction {
public:
/// Add a newly allocated card
AddCardAction(Set& set);
AddCardAction(AddingOrRemoving, Set& set, const CardP& card);
AddCardAction(AddingOrRemoving, Set& set, const vector<CardP>& cards);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const GenericAddAction<CardP> action;
/// Add a newly allocated card
AddCardAction(Set& set);
AddCardAction(AddingOrRemoving, Set& set, const CardP& card);
AddCardAction(AddingOrRemoving, Set& set, const vector<CardP>& cards);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const GenericAddAction<CardP> action;
};
// ----------------------------------------------------------------------------- : Reorder cards
@@ -58,13 +58,13 @@ class AddCardAction : public CardListAction {
/// Change the position of a card in the card list by swapping two cards
class ReorderCardsAction : public CardListAction {
public:
ReorderCardsAction(Set& set, size_t card_id1, size_t card_id2);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
ReorderCardsAction(Set& set, size_t card_id1, size_t card_id2);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
//private:
const size_t card_id1, card_id2; ///< Positions of the two cards to swap
const size_t card_id1, card_id2; ///< Positions of the two cards to swap
};
// ----------------------------------------------------------------------------- : Change stylesheet
@@ -72,53 +72,53 @@ class ReorderCardsAction : public CardListAction {
/// An action that affects the rendering/display/look of a set or cards in the set
class DisplayChangeAction : public Action {
public:
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
};
/// Changing the style of a a card
class ChangeCardStyleAction : public DisplayChangeAction {
public:
ChangeCardStyleAction(const CardP& card, const StyleSheetP& stylesheet);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
ChangeCardStyleAction(const CardP& card, const StyleSheetP& stylesheet);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
//private:
CardP card; ///< The affected card
StyleSheetP stylesheet; ///< Its old stylesheet
bool has_styling; ///< Its old has_styling
IndexMap<FieldP,ValueP> styling_data; ///< Its old styling data
CardP card; ///< The affected card
StyleSheetP stylesheet; ///< Its old stylesheet
bool has_styling; ///< Its old has_styling
IndexMap<FieldP,ValueP> styling_data; ///< Its old styling data
};
/// Changing the style of a set to that of a card
class ChangeSetStyleAction : public DisplayChangeAction {
public:
ChangeSetStyleAction(Set& set, const CardP& card);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
ChangeSetStyleAction(Set& set, const CardP& card);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
Set& set; ///< The affected set
CardP card; ///< The card whos stylesheet is copied to the set
StyleSheetP stylesheet; ///< The old stylesheet of the set
vector<int> has_styling; ///< The old has_styling values of all cards (vector<bool> is evil)
Set& set; ///< The affected set
CardP card; ///< The card whos stylesheet is copied to the set
StyleSheetP stylesheet; ///< The old stylesheet of the set
vector<int> has_styling; ///< The old has_styling values of all cards (vector<bool> is evil)
};
/// Changing the styling of a card to become custom/non-custom
/** i.e. toggle card->has_styling */
class ChangeCardHasStylingAction : public DisplayChangeAction {
public:
ChangeCardHasStylingAction(Set& set, const CardP& card);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
ChangeCardHasStylingAction(Set& set, const CardP& card);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
//private:
Set& set; ///< The set to copy styling from
CardP card; ///< The affected card
IndexMap<FieldP,ValueP> styling_data; ///< The old styling of the card
Set& set; ///< The set to copy styling from
CardP card; ///< The affected card
IndexMap<FieldP,ValueP> styling_data; ///< The old styling of the card
};
// ----------------------------------------------------------------------------- : Pack types
@@ -126,37 +126,37 @@ class ChangeCardHasStylingAction : public DisplayChangeAction {
/// An Action the changes the pack types of a set
class PackTypesAction : public Action {
public:
inline PackTypesAction(Set& set) : set(set) {}
inline PackTypesAction(Set& set) : set(set) {}
protected:
Set& set; // the set owns this action, so the set will not be destroyed before this
// therefore we don't need a smart pointer
Set& set; // the set owns this action, so the set will not be destroyed before this
// therefore we don't need a smart pointer
};
/// Adding/removing a pack from a Set
class AddPackAction : public PackTypesAction {
public:
/// Add a newly allocated card
AddPackAction(AddingOrRemoving, Set& set, const PackTypeP& pack);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const GenericAddAction<PackTypeP> action;
/// Add a newly allocated card
AddPackAction(AddingOrRemoving, Set& set, const PackTypeP& pack);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const GenericAddAction<PackTypeP> action;
};
/// Updating a pack in a Set
class ChangePackAction : public PackTypesAction {
public:
/// Add a newly allocated card
ChangePackAction(Set& set, size_t pos, const PackTypeP& new_pack);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Add a newly allocated card
ChangePackAction(Set& set, size_t pos, const PackTypeP& new_pack);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
PackTypeP pack;
size_t pos;
PackTypeP pack;
size_t pos;
};
// ----------------------------------------------------------------------------- : EOF
+334 -334
View File
@@ -20,173 +20,173 @@ DECLARE_TYPEOF_COLLECTION(ControlPointP);
// ----------------------------------------------------------------------------- : Utility
String action_name_for(const set<SymbolPartP>& parts, const String& action) {
return format_string(action, parts.size() == 1 ? (*parts.begin())->name : _TYPE_("shapes"));
return format_string(action, parts.size() == 1 ? (*parts.begin())->name : _TYPE_("shapes"));
}
SymbolPartsAction::SymbolPartsAction(const set<SymbolPartP>& parts)
: parts(parts)
: parts(parts)
{}
// ----------------------------------------------------------------------------- : Moving symbol parts
SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta)
: SymbolPartsAction(parts)
, delta(delta), moved(-delta)
, constrain(false)
, snap(0)
: SymbolPartsAction(parts)
, delta(delta), moved(-delta)
, constrain(false)
, snap(0)
{
// Determine min/max_pos
FOR_EACH(p, parts) {
bounds.update(p->bounds);
}
// Determine min/max_pos
FOR_EACH(p, parts) {
bounds.update(p->bounds);
}
}
String SymbolPartMoveAction::getName(bool to_undo) const {
return action_name_for(parts, _ACTION_("move"));
return action_name_for(parts, _ACTION_("move"));
}
void SymbolPartMoveAction::perform(bool to_undo) {
// move the points back
FOR_EACH(p, parts) {
movePart(*p);
}
moved = -moved;
// move the points back
FOR_EACH(p, parts) {
movePart(*p);
}
moved = -moved;
}
void SymbolPartMoveAction::movePart(SymbolPart& part) {
part.bounds.min -= moved;
part.bounds.max -= moved;
if (SymbolShape* s = part.isSymbolShape()) {
FOR_EACH(pnt, s->points) {
pnt->pos -= moved;
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center -= moved;
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
movePart(*p);
}
}
part.bounds.min -= moved;
part.bounds.max -= moved;
if (SymbolShape* s = part.isSymbolShape()) {
FOR_EACH(pnt, s->points) {
pnt->pos -= moved;
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center -= moved;
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
movePart(*p);
}
}
}
void SymbolPartMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
// Determine actual delta, possibly constrained and snapped
Vector2D d = constrain_snap_vector_offset(bounds.min, bounds.max, delta, constrain, snap);
Vector2D dd = d - moved; // move this much more
// Move each point by d
moved = -dd;
perform(false); // (ab)use perform to move by +dd
moved = d;
delta += deltaDelta;
// Determine actual delta, possibly constrained and snapped
Vector2D d = constrain_snap_vector_offset(bounds.min, bounds.max, delta, constrain, snap);
Vector2D dd = d - moved; // move this much more
// Move each point by d
moved = -dd;
perform(false); // (ab)use perform to move by +dd
moved = d;
}
// ----------------------------------------------------------------------------- : Rotating symbol parts
SymbolPartMatrixAction::SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center)
: SymbolPartsAction(parts)
, center(center)
: SymbolPartsAction(parts)
, center(center)
{}
void SymbolPartMatrixAction::transform(const Matrix2D& m) {
// Transform each part
FOR_EACH(p, parts) {
transform(*p, m);
p->updateBounds();
}
// Transform each part
FOR_EACH(p, parts) {
transform(*p, m);
p->updateBounds();
}
}
void SymbolPartMatrixAction::transform(SymbolPart& part, const Matrix2D& m) {
if (SymbolShape* s = part.isSymbolShape()) {
FOR_EACH(pnt, s->points) {
pnt->pos = ((pnt->pos - center) * m) + center;
pnt->delta_before = pnt->delta_before * m;
pnt->delta_after = pnt->delta_after * m;
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center = (s->center - center) * m + center;
s->handle = s->handle * m;
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transform(*p, m);
}
}
if (SymbolShape* s = part.isSymbolShape()) {
FOR_EACH(pnt, s->points) {
pnt->pos = ((pnt->pos - center) * m) + center;
pnt->delta_before = pnt->delta_before * m;
pnt->delta_after = pnt->delta_after * m;
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center = (s->center - center) * m + center;
s->handle = s->handle * m;
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transform(*p, m);
}
}
}
SymbolPartRotateAction::SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center)
: SymbolPartMatrixAction(parts, center)
, angle(0)
, constrain(false)
: SymbolPartMatrixAction(parts, center)
, angle(0)
, constrain(false)
{}
String SymbolPartRotateAction::getName(bool to_undo) const {
return action_name_for(parts, _ACTION_("rotate"));
return action_name_for(parts, _ACTION_("rotate"));
}
void SymbolPartRotateAction::perform(bool to_undo) {
// move the points back
rotateBy(-angle);
angle = -angle;
// move the points back
rotateBy(-angle);
angle = -angle;
}
void SymbolPartRotateAction::rotateTo(Radians newAngle) {
Radians oldAngle = angle;
angle = newAngle;
// constrain?
if (constrain) {
// multiples of 2pi/24 i.e. 24 stops
double mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
}
if (oldAngle != angle) rotateBy(angle - oldAngle);
Radians oldAngle = angle;
angle = newAngle;
// constrain?
if (constrain) {
// multiples of 2pi/24 i.e. 24 stops
double mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
}
if (oldAngle != angle) rotateBy(angle - oldAngle);
}
void SymbolPartRotateAction::rotateBy(Radians deltaAngle) {
// Rotation 'matrix'
transform(
Matrix2D(cos(deltaAngle), -sin(deltaAngle)
,sin(deltaAngle), cos(deltaAngle))
);
// Rotation 'matrix'
transform(
Matrix2D(cos(deltaAngle), -sin(deltaAngle)
,sin(deltaAngle), cos(deltaAngle))
);
}
// ----------------------------------------------------------------------------- : Shearing symbol parts
SymbolPartShearAction::SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center)
: SymbolPartMatrixAction(parts, center)
// , constrain(false)
, snap(0)
: SymbolPartMatrixAction(parts, center)
// , constrain(false)
, snap(0)
{}
String SymbolPartShearAction::getName(bool to_undo) const {
return action_name_for(parts, _ACTION_("shear"));
return action_name_for(parts, _ACTION_("shear"));
}
void SymbolPartShearAction::perform(bool to_undo) {
// move the points back
// the vector shear = (x,y) is used as:
// <1 x>
// <y 1>
// inverse is:
// <1 -x> /
// <-y 1> / (1 - xy)
// we have: xy = 0 => (1 - xy) = 1
shearBy(-moved);
// move the points back
// the vector shear = (x,y) is used as:
// <1 x>
// <y 1>
// inverse is:
// <1 -x> /
// <-y 1> / (1 - xy)
// we have: xy = 0 => (1 - xy) = 1
shearBy(-moved);
}
void SymbolPartShearAction::move(const Vector2D& deltaShear) {
shear += deltaShear;
Vector2D d = snap_vector(shear - moved, snap);
shearBy(d);
moved += d;
shear += deltaShear;
Vector2D d = snap_vector(shear - moved, snap);
shearBy(d);
moved += d;
}
void SymbolPartShearAction::shearBy(const Vector2D& shear) {
// Shear 'matrix'
transform(
Matrix2D(1, shear.x
,shear.y, 1)
);
// Shear 'matrix'
transform(
Matrix2D(1, shear.x
,shear.y, 1)
);
}
@@ -194,366 +194,366 @@ void SymbolPartShearAction::shearBy(const Vector2D& shear) {
SymbolPartScaleAction::SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY)
: SymbolPartsAction(parts)
, scaleX(scaleX), scaleY(scaleY)
, constrain(false)
, snap(0)
: SymbolPartsAction(parts)
, scaleX(scaleX), scaleY(scaleY)
, constrain(false)
, snap(0)
{
// Find min and max coordinates
Bounds bounds;
FOR_EACH(p, parts) {
bounds.update(p->bounds);
}
// new == old
new_min = new_real_min = old_min = bounds.min;
new_size = new_real_size = old_size = bounds.max - bounds.min;
// Find min and max coordinates
Bounds bounds;
FOR_EACH(p, parts) {
bounds.update(p->bounds);
}
// new == old
new_min = new_real_min = old_min = bounds.min;
new_size = new_real_size = old_size = bounds.max - bounds.min;
}
String SymbolPartScaleAction::getName(bool to_undo) const {
return action_name_for(parts, _ACTION_("scale"));
return action_name_for(parts, _ACTION_("scale"));
}
void SymbolPartScaleAction::perform(bool to_undo) {
swap(old_min, new_min);
swap(old_size, new_size);
transformAll();
swap(old_min, new_min);
swap(old_size, new_size);
transformAll();
}
void SymbolPartScaleAction::move(const Vector2D& delta_min, const Vector2D& delta_max) {
new_real_min += delta_min;
new_real_size += delta_max - delta_min;
update();
new_real_min += delta_min;
new_real_size += delta_max - delta_min;
update();
}
void SymbolPartScaleAction::update() {
// Move each point so the range [old_min...old_max] maps to [new_min...new_max]
// we have already moved to the current [new_min...new_max]
Vector2D tmp_min = old_min, tmp_size = old_size; // the size before any scaling
old_min = new_min; old_size = new_size; // the size before this move
// the size after the move
new_min = new_real_min; new_size = new_real_size;
if (constrain && scaleX != 0 && scaleY != 0) {
Vector2D scale = new_size.div(tmp_size);
scale = constrain_vector(scale, true, true);
new_size = tmp_size.mul(scale);
new_min += (new_real_size - new_size).mul(Vector2D(scaleX == -1 ? 1 : 0, scaleY == -1 ? 1 : 0));
// TODO : snapping
} else if (snap >= 0) {
if (scaleX + scaleY < 0) {
new_min = snap_vector(new_min, snap);
new_size += new_real_min - new_min;
} else {
Vector2D new_max = snap_vector(new_min + new_size, snap);
new_size = new_max - new_min;
}
}
// now move all points
transformAll();
// restore old_min/size
old_min = tmp_min; old_size = tmp_size;
// Move each point so the range [old_min...old_max] maps to [new_min...new_max]
// we have already moved to the current [new_min...new_max]
Vector2D tmp_min = old_min, tmp_size = old_size; // the size before any scaling
old_min = new_min; old_size = new_size; // the size before this move
// the size after the move
new_min = new_real_min; new_size = new_real_size;
if (constrain && scaleX != 0 && scaleY != 0) {
Vector2D scale = new_size.div(tmp_size);
scale = constrain_vector(scale, true, true);
new_size = tmp_size.mul(scale);
new_min += (new_real_size - new_size).mul(Vector2D(scaleX == -1 ? 1 : 0, scaleY == -1 ? 1 : 0));
// TODO : snapping
} else if (snap >= 0) {
if (scaleX + scaleY < 0) {
new_min = snap_vector(new_min, snap);
new_size += new_real_min - new_min;
} else {
Vector2D new_max = snap_vector(new_min + new_size, snap);
new_size = new_max - new_min;
}
}
// now move all points
transformAll();
// restore old_min/size
old_min = tmp_min; old_size = tmp_size;
}
void SymbolPartScaleAction::transformAll() {
FOR_EACH(p, parts) {
transformPart(*p);
}
FOR_EACH(p, parts) {
transformPart(*p);
}
}
void SymbolPartScaleAction::transformPart(SymbolPart& part) {
// update bounds
part.bounds.min = transform(part.bounds.min);
part.bounds.max = transform(part.bounds.max);
// make sure that max >= min
if (part.bounds.min.x > part.bounds.max.x) swap(part.bounds.min.x, part.bounds.max.x);
if (part.bounds.min.y > part.bounds.max.y) swap(part.bounds.min.y, part.bounds.max.y);
if (SymbolShape* s = part.isSymbolShape()) {
// scale all points
Vector2D scale = new_size.div(old_size);
FOR_EACH(pnt, s->points) {
pnt->pos = transform(pnt->pos);
// also scale handles
pnt->delta_before = pnt->delta_before.mul(scale);
pnt->delta_after = pnt->delta_after .mul(scale);
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
transform(s->center);
s->handle.mul(new_size.div(old_size));
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transformPart(*p);
}
}
// update bounds
part.bounds.min = transform(part.bounds.min);
part.bounds.max = transform(part.bounds.max);
// make sure that max >= min
if (part.bounds.min.x > part.bounds.max.x) swap(part.bounds.min.x, part.bounds.max.x);
if (part.bounds.min.y > part.bounds.max.y) swap(part.bounds.min.y, part.bounds.max.y);
if (SymbolShape* s = part.isSymbolShape()) {
// scale all points
Vector2D scale = new_size.div(old_size);
FOR_EACH(pnt, s->points) {
pnt->pos = transform(pnt->pos);
// also scale handles
pnt->delta_before = pnt->delta_before.mul(scale);
pnt->delta_after = pnt->delta_after .mul(scale);
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
transform(s->center);
s->handle.mul(new_size.div(old_size));
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transformPart(*p);
}
}
}
Vector2D SymbolPartScaleAction::transform(const Vector2D& v) {
// TODO: prevent div by 0
return (v - old_min).div(old_size).mul(new_size) + new_min;
// TODO: prevent div by 0
return (v - old_min).div(old_size).mul(new_size) + new_min;
}
// ----------------------------------------------------------------------------- : Change combine mode
CombiningModeAction::CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode)
: SymbolPartsAction(parts)
: SymbolPartsAction(parts)
{
FOR_EACH(p, parts) {
add(p,mode);
}
FOR_EACH(p, parts) {
add(p,mode);
}
}
void CombiningModeAction::add(const SymbolPartP& part, SymbolShapeCombine mode) {
if (part->isSymbolShape()) {
this->parts.push_back(make_pair(static_pointer_cast<SymbolShape>(part),mode));
} else if (SymbolGroup* g = part->isSymbolGroup()) {
FOR_EACH(p, g->parts) add(p, mode);
}
if (part->isSymbolShape()) {
this->parts.push_back(make_pair(static_pointer_cast<SymbolShape>(part),mode));
} else if (SymbolGroup* g = part->isSymbolGroup()) {
FOR_EACH(p, g->parts) add(p, mode);
}
}
String CombiningModeAction::getName(bool to_undo) const {
return _ACTION_("change combine mode");
return _ACTION_("change combine mode");
}
void CombiningModeAction::perform(bool to_undo) {
FOR_EACH(pm, parts) {
swap(pm.first->combine, pm.second);
}
FOR_EACH(pm, parts) {
swap(pm.first->combine, pm.second);
}
}
// ----------------------------------------------------------------------------- : Change name
SymbolPartNameAction::SymbolPartNameAction(const SymbolPartP& part, const String& name, size_t old_cursor, size_t new_cursor)
: part(part), part_name(name)
, old_cursor(new_cursor), new_cursor(old_cursor) // will be swapped
: part(part), part_name(name)
, old_cursor(new_cursor), new_cursor(old_cursor) // will be swapped
{}
String SymbolPartNameAction::getName(bool to_undo) const {
return _ACTION_("change shape name");
return _ACTION_("change shape name");
}
bool SymbolPartNameAction::merge(const Action& action) {
TYPE_CASE(action, SymbolPartNameAction) {
if (action.part == part) {
// adjacent actions on the same part, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
TYPE_CASE(action, SymbolPartNameAction) {
if (action.part == part) {
// adjacent actions on the same part, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
}
void SymbolPartNameAction::perform(bool to_undo) {
swap(part->name, part_name);
swap(old_cursor, new_cursor);
swap(part->name, part_name);
swap(old_cursor, new_cursor);
}
// ----------------------------------------------------------------------------- : Add symbol part
AddSymbolPartAction::AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part)
: symbol(symbol), part(part)
: symbol(symbol), part(part)
{}
String AddSymbolPartAction::getName(bool to_undo) const {
return _ACTION_1_("add item", part->name);
return _ACTION_1_("add item", part->name);
}
void AddSymbolPartAction::perform(bool to_undo) {
if (to_undo) {
assert(!symbol.parts.empty());
symbol.parts.erase (symbol.parts.begin());
} else {
symbol.parts.insert(symbol.parts.begin(), part);
}
if (to_undo) {
assert(!symbol.parts.empty());
symbol.parts.erase (symbol.parts.begin());
} else {
symbol.parts.insert(symbol.parts.begin(), part);
}
}
// ----------------------------------------------------------------------------- : Remove symbol part
RemoveSymbolPartsAction::RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
: symbol(symbol)
: symbol(symbol)
{
check(symbol, parts);
check(symbol, parts);
}
void RemoveSymbolPartsAction::check(SymbolGroup& group, const set<SymbolPartP>& parts) {
size_t index = 0;
size_t removed = 0;
FOR_EACH(p, group.parts) {
if (parts.find(p) != parts.end()) {
removals.push_back(Removal(group, index, p)); // remove this part
++ removed;
} else if (SymbolGroup* g = p->isSymbolGroup()) {
check(*g, parts);
}
++index;
}
if (!group.isSymbolSymmetry() && &group != &symbol) {
// remove empty groups
// TODO
}
size_t index = 0;
size_t removed = 0;
FOR_EACH(p, group.parts) {
if (parts.find(p) != parts.end()) {
removals.push_back(Removal(group, index, p)); // remove this part
++ removed;
} else if (SymbolGroup* g = p->isSymbolGroup()) {
check(*g, parts);
}
++index;
}
if (!group.isSymbolSymmetry() && &group != &symbol) {
// remove empty groups
// TODO
}
}
String RemoveSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_1_("remove item", removals.size() == 1 ? _TYPE_("shape") : _TYPE_("shapes"));
return _ACTION_1_("remove item", removals.size() == 1 ? _TYPE_("shape") : _TYPE_("shapes"));
}
void RemoveSymbolPartsAction::perform(bool to_undo) {
if (to_undo) {
// reinsert the parts
// ascending order, this is the reverse of removal
FOR_EACH(r, removals) {
assert(r.pos <= r.parent->parts.size());
r.parent->parts.insert(r.parent->parts.begin() + r.pos, r.removed);
}
} else {
// remove the parts
// descending order, because earlier removals shift the rest of the vector
FOR_EACH_REVERSE(r, removals) {
assert(r.pos < r.parent->parts.size());
r.parent->parts.erase(r.parent->parts.begin() + r.pos);
}
}
if (to_undo) {
// reinsert the parts
// ascending order, this is the reverse of removal
FOR_EACH(r, removals) {
assert(r.pos <= r.parent->parts.size());
r.parent->parts.insert(r.parent->parts.begin() + r.pos, r.removed);
}
} else {
// remove the parts
// descending order, because earlier removals shift the rest of the vector
FOR_EACH_REVERSE(r, removals) {
assert(r.pos < r.parent->parts.size());
r.parent->parts.erase(r.parent->parts.begin() + r.pos);
}
}
}
// ----------------------------------------------------------------------------- : Duplicate symbol parts
DuplicateSymbolPartsAction::DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
: symbol(symbol)
: symbol(symbol)
{
UInt index = 0;
FOR_EACH(p, symbol.parts) {
index += 1;
if (parts.find(p) != parts.end()) {
// duplicate this part
duplications.push_back(make_pair(p->clone(), index));
index += 1; // the clone also takes up space on the vector
}
}
UInt index = 0;
FOR_EACH(p, symbol.parts) {
index += 1;
if (parts.find(p) != parts.end()) {
// duplicate this part
duplications.push_back(make_pair(p->clone(), index));
index += 1; // the clone also takes up space on the vector
}
}
}
String DuplicateSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_1_("duplicate", duplications.size() == 1 ? _TYPE_("shape") : _TYPE_("shapes"));
return _ACTION_1_("duplicate", duplications.size() == 1 ? _TYPE_("shape") : _TYPE_("shapes"));
}
void DuplicateSymbolPartsAction::perform(bool to_undo) {
if (to_undo) {
// remove the clones
// walk in reverse order, otherwise we will shift the vector
FOR_EACH_REVERSE(d, duplications) {
assert(d.second < symbol.parts.size());
symbol.parts.erase(symbol.parts.begin() + d.second);
}
} else {
// insert the clones
FOR_EACH(d, duplications) {
assert(d.second <= symbol.parts.size());
symbol.parts.insert(symbol.parts.begin() + d.second, d.first);
}
}
if (to_undo) {
// remove the clones
// walk in reverse order, otherwise we will shift the vector
FOR_EACH_REVERSE(d, duplications) {
assert(d.second < symbol.parts.size());
symbol.parts.erase(symbol.parts.begin() + d.second);
}
} else {
// insert the clones
FOR_EACH(d, duplications) {
assert(d.second <= symbol.parts.size());
symbol.parts.insert(symbol.parts.begin() + d.second, d.first);
}
}
}
void DuplicateSymbolPartsAction::getParts(set<SymbolPartP>& parts) {
parts.clear();
FOR_EACH(d, duplications) {
parts.insert(d.first);
}
parts.clear();
FOR_EACH(d, duplications) {
parts.insert(d.first);
}
}
// ----------------------------------------------------------------------------- : Reorder symbol parts
ReorderSymbolPartsAction::ReorderSymbolPartsAction(SymbolGroup& old_parent, size_t old_position, SymbolGroup& new_parent, size_t new_position)
: old_parent(&old_parent), new_parent(&new_parent)
, old_position(old_position), new_position(new_position)
: old_parent(&old_parent), new_parent(&new_parent)
, old_position(old_position), new_position(new_position)
{}
String ReorderSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("reorder parts");
return _ACTION_("reorder parts");
}
void ReorderSymbolPartsAction::perform(bool to_undo) {
// remove from old
assert(old_position < old_parent->parts.size());
SymbolPartP part = old_parent->parts.at(old_position);
old_parent->parts.erase( old_parent->parts.begin() + old_position);
// add to new
assert(new_position <= new_parent->parts.size());
new_parent->parts.insert(new_parent->parts.begin() + new_position, part);
// next time the other way around
swap(old_parent, new_parent);
swap(old_position, new_position);
// remove from old
assert(old_position < old_parent->parts.size());
SymbolPartP part = old_parent->parts.at(old_position);
old_parent->parts.erase( old_parent->parts.begin() + old_position);
// add to new
assert(new_position <= new_parent->parts.size());
new_parent->parts.insert(new_parent->parts.begin() + new_position, part);
// next time the other way around
swap(old_parent, new_parent);
swap(old_position, new_position);
}
UngroupReorderSymbolPartsAction::UngroupReorderSymbolPartsAction(SymbolGroup& group_parent, size_t group_pos, SymbolGroup& target_parent, size_t target_pos)
: group_parent(group_parent), group_pos(group_pos)
, target_parent(target_parent), target_pos(target_pos)
: group_parent(group_parent), group_pos(group_pos)
, target_parent(target_parent), target_pos(target_pos)
{
group = dynamic_pointer_cast<SymbolGroup>(group_parent.parts.at(group_pos));
assert(group);
group = dynamic_pointer_cast<SymbolGroup>(group_parent.parts.at(group_pos));
assert(group);
}
String UngroupReorderSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("reorder parts");
return _ACTION_("reorder parts");
}
void UngroupReorderSymbolPartsAction::perform(bool to_undo) {
if (!to_undo) {
group_parent.parts.erase(group_parent.parts.begin() + group_pos);
target_parent.parts.insert(target_parent.parts.begin() + target_pos, group->parts.begin(), group->parts.end());
} else {
target_parent.parts.erase(target_parent.parts.begin() + target_pos, target_parent.parts.begin() + target_pos + group->parts.size());
group_parent.parts.insert(group_parent.parts.begin() + group_pos, group);
}
if (!to_undo) {
group_parent.parts.erase(group_parent.parts.begin() + group_pos);
target_parent.parts.insert(target_parent.parts.begin() + target_pos, group->parts.begin(), group->parts.end());
} else {
target_parent.parts.erase(target_parent.parts.begin() + target_pos, target_parent.parts.begin() + target_pos + group->parts.size());
group_parent.parts.insert(group_parent.parts.begin() + group_pos, group);
}
}
// ----------------------------------------------------------------------------- : Group symbol parts
GroupSymbolPartsActionBase::GroupSymbolPartsActionBase(SymbolGroup& root)
: root(root)
: root(root)
{}
void GroupSymbolPartsActionBase::perform(bool to_undo) {
swap(root.parts, old_part_list);
swap(root.parts, old_part_list);
}
GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group)
: GroupSymbolPartsActionBase(root)
, group(group)
: GroupSymbolPartsActionBase(root)
, group(group)
{
// group parts in the old parts list
bool done = false;
FOR_EACH(p, root.parts) {
assert(p != group);
if (parts.find(p) != parts.end()) {
// add to group instead
group->parts.push_back(p);
if (!done) {
done = true;
old_part_list.push_back(group);
}
} else {
// not affected
old_part_list.push_back(p);
}
}
group->updateBounds();
// group parts in the old parts list
bool done = false;
FOR_EACH(p, root.parts) {
assert(p != group);
if (parts.find(p) != parts.end()) {
// add to group instead
group->parts.push_back(p);
if (!done) {
done = true;
old_part_list.push_back(group);
}
} else {
// not affected
old_part_list.push_back(p);
}
}
group->updateBounds();
}
String GroupSymbolPartsAction::getName(bool to_undo) const {
return group->isSymbolSymmetry() ? _ACTION_("add symmetry") : _ACTION_("group parts");
return group->isSymbolSymmetry() ? _ACTION_("add symmetry") : _ACTION_("group parts");
}
UngroupSymbolPartsAction::UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts)
: GroupSymbolPartsActionBase(root)
: GroupSymbolPartsActionBase(root)
{
// break up the parts in the old parts list
FOR_EACH(p, root.parts) {
if (parts.find(p) != parts.end() && p->isSymbolGroup()) {
// break up the group
SymbolGroup* g = p->isSymbolGroup();
FOR_EACH(p, g->parts) {
old_part_list.push_back(p);
}
} else {
// not affected
old_part_list.push_back(p);
}
}
// break up the parts in the old parts list
FOR_EACH(p, root.parts) {
if (parts.find(p) != parts.end() && p->isSymbolGroup()) {
// break up the group
SymbolGroup* g = p->isSymbolGroup();
FOR_EACH(p, g->parts) {
old_part_list.push_back(p);
}
} else {
// not affected
old_part_list.push_back(p);
}
}
}
String UngroupSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("ungroup parts");
return _ACTION_("ungroup parts");
}
+161 -161
View File
@@ -26,9 +26,9 @@ class SymbolPartAction : public Action {};
/// Anything that changes a set of parts
class SymbolPartsAction : public SymbolPartAction {
public:
SymbolPartsAction(const set<SymbolPartP>& parts);
const set<SymbolPartP> parts; ///< Affected parts
SymbolPartsAction(const set<SymbolPartP>& parts);
const set<SymbolPartP> parts; ///< Affected parts
};
/// Anything that changes a part as displayed in the part list
@@ -39,23 +39,23 @@ class SymbolPartListAction : public SymbolPartAction {};
/// Move some symbol parts
class SymbolPartMoveAction : public SymbolPartsAction {
public:
SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta = Vector2D());
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta = Vector2D());
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
Vector2D delta; ///< How much to move
Vector2D moved; ///< How much has been moved
Bounds bounds; ///< Bounding box of the thing we are moving
void movePart(SymbolPart& part); ///< Move a single part
Vector2D delta; ///< How much to move
Vector2D moved; ///< How much has been moved
Bounds bounds; ///< Bounding box of the thing we are moving
void movePart(SymbolPart& part); ///< Move a single part
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
// ----------------------------------------------------------------------------- : Rotating symbol parts
@@ -63,37 +63,37 @@ class SymbolPartMoveAction : public SymbolPartsAction {
/// Transforming symbol parts using a matrix
class SymbolPartMatrixAction : public SymbolPartsAction {
public:
SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center);
/// Update this action to move some more
void move(const Vector2D& delta);
SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center);
/// Update this action to move some more
void move(const Vector2D& delta);
protected:
/// Perform the transformation using the given matrix
void transform(const Matrix2D& m);
void transform(SymbolPart& part, const Matrix2D& m);
Vector2D center; ///< Center to transform around
/// Perform the transformation using the given matrix
void transform(const Matrix2D& m);
void transform(SymbolPart& part, const Matrix2D& m);
Vector2D center; ///< Center to transform around
};
/// Rotate some symbol parts
class SymbolPartRotateAction : public SymbolPartMatrixAction {
public:
SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to rotate to a different angle
void rotateTo(Radians newAngle);
/// Update this action to rotate by a deltaAngle
void rotateBy(Radians deltaAngle);
SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to rotate to a different angle
void rotateTo(Radians newAngle);
/// Update this action to rotate by a deltaAngle
void rotateBy(Radians deltaAngle);
private:
Radians angle; ///< How much to rotate?
Radians angle; ///< How much to rotate?
public:
bool constrain; ///< Constrain movement?
bool constrain; ///< Constrain movement?
};
@@ -102,21 +102,21 @@ class SymbolPartRotateAction : public SymbolPartMatrixAction {
/// Shear some symbol parts
class SymbolPartShearAction : public SymbolPartMatrixAction {
public:
SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Change shear by a given amount
void move(const Vector2D& deltaShear);
SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Change shear by a given amount
void move(const Vector2D& deltaShear);
private:
Vector2D shear; ///< Shearing, shear.x == 0 || shear.y == 0
Vector2D moved;
void shearBy(const Vector2D& shear);
Vector2D shear; ///< Shearing, shear.x == 0 || shear.y == 0
Vector2D moved;
void shearBy(const Vector2D& shear);
public:
// bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
// bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
@@ -125,29 +125,29 @@ class SymbolPartShearAction : public SymbolPartMatrixAction {
/// Scale some symbol parts
class SymbolPartScaleAction : public SymbolPartsAction {
public:
SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Change min and max coordinates
void move(const Vector2D& delta_min, const Vector2D& delta_max);
/// Update the action's effect
void update();
SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Change min and max coordinates
void move(const Vector2D& delta_min, const Vector2D& delta_max);
/// Update the action's effect
void update();
private:
Vector2D old_min, old_size; ///< the original pos/size
Vector2D new_real_min, new_real_size; ///< the target pos/sizevoid shearBy(const Vector2D& shear)
Vector2D new_min, new_size; ///< the target pos/size after applying constrains
int scaleX, scaleY; ///< to what corner are we attached?
/// Transform everything in the parts
void transformAll();
void transformPart(SymbolPart&);
/// Transform a single vector
inline Vector2D transform(const Vector2D& v);
Vector2D old_min, old_size; ///< the original pos/size
Vector2D new_real_min, new_real_size; ///< the target pos/sizevoid shearBy(const Vector2D& shear)
Vector2D new_min, new_size; ///< the target pos/size after applying constrains
int scaleX, scaleY; ///< to what corner are we attached?
/// Transform everything in the parts
void transformAll();
void transformPart(SymbolPart&);
/// Transform a single vector
inline Vector2D transform(const Vector2D& v);
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
// ----------------------------------------------------------------------------- : Change combine mode
@@ -155,15 +155,15 @@ class SymbolPartScaleAction : public SymbolPartsAction {
/// Change the name of a symbol part
class CombiningModeAction : public SymbolPartsAction {
public:
// All parts must be SymbolParts
CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
// All parts must be SymbolParts
CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
void add(const SymbolPartP&, SymbolShapeCombine mode);
vector<pair<SymbolShapeP,SymbolShapeCombine> > parts; ///< Affected parts with new combining modes
void add(const SymbolPartP&, SymbolShapeCombine mode);
vector<pair<SymbolShapeP,SymbolShapeCombine> > parts; ///< Affected parts with new combining modes
};
// ----------------------------------------------------------------------------- : Change name
@@ -171,17 +171,17 @@ class CombiningModeAction : public SymbolPartsAction {
/// Change the name of a symbol part
class SymbolPartNameAction : public SymbolPartAction {
public:
SymbolPartNameAction(const SymbolPartP& part, const String& name, size_t old_cursor, size_t new_cursor);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual bool merge(const Action& action);
SymbolPartNameAction(const SymbolPartP& part, const String& name, size_t old_cursor, size_t new_cursor);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual bool merge(const Action& action);
public:
SymbolPartP part; ///< Affected part
String part_name; ///< New name
size_t old_cursor; ///< Cursor position
size_t new_cursor; ///< Cursor position
SymbolPartP part; ///< Affected part
String part_name; ///< New name
size_t old_cursor; ///< Cursor position
size_t new_cursor; ///< Cursor position
};
// ----------------------------------------------------------------------------- : Add symbol part
@@ -189,14 +189,14 @@ class SymbolPartNameAction : public SymbolPartAction {
/// Adding a part to a symbol, added at the front of the list (drawn on top)
class AddSymbolPartAction : public SymbolPartListAction {
public:
AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
Symbol& symbol; ///< Symbol to add the part to
SymbolPartP part; ///< Part to add
Symbol& symbol; ///< Symbol to add the part to
SymbolPartP part; ///< Part to add
};
// ----------------------------------------------------------------------------- : Remove symbol part
@@ -204,28 +204,28 @@ class AddSymbolPartAction : public SymbolPartListAction {
/// Removing parts from a symbol
class RemoveSymbolPartsAction : public SymbolPartListAction {
public:
RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
Symbol& symbol;
/// Check for removals in a group
void check(SymbolGroup& group, const set<SymbolPartP>& parts);
Symbol& symbol;
/// Check for removals in a group
void check(SymbolGroup& group, const set<SymbolPartP>& parts);
public:
/// A removal step
struct Removal {
inline Removal(SymbolGroup& parent, size_t pos, const SymbolPartP& removed)
: parent(&parent), pos(pos), removed(removed)
{}
SymbolGroup* parent;
size_t pos;
SymbolPartP removed;
};
/// A removal step
struct Removal {
inline Removal(SymbolGroup& parent, size_t pos, const SymbolPartP& removed)
: parent(&parent), pos(pos), removed(removed)
{}
SymbolGroup* parent;
size_t pos;
SymbolPartP removed;
};
private:
/// Removed parts, sorted by ascending pos
vector<Removal> removals;
/// Removed parts, sorted by ascending pos
vector<Removal> removals;
};
// ----------------------------------------------------------------------------- : Duplicate symbol parts
@@ -233,18 +233,18 @@ class RemoveSymbolPartsAction : public SymbolPartListAction {
/// Duplicating parts in a symbol
class DuplicateSymbolPartsAction : public SymbolPartListAction {
public:
DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Fill a set with all the new parts
void getParts(set<SymbolPartP>& parts);
DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Fill a set with all the new parts
void getParts(set<SymbolPartP>& parts);
private:
Symbol& symbol;
/// Duplicates of parts and their positions, sorted by ascending pos
vector<pair<SymbolPartP, size_t> > duplications;
Symbol& symbol;
/// Duplicates of parts and their positions, sorted by ascending pos
vector<pair<SymbolPartP, size_t> > duplications;
};
@@ -253,32 +253,32 @@ class DuplicateSymbolPartsAction : public SymbolPartListAction {
/// Change the position of a part in a symbol, by moving a part.
class ReorderSymbolPartsAction : public SymbolPartListAction {
public:
ReorderSymbolPartsAction(SymbolGroup& old_parent, size_t old_position, SymbolGroup& new_parent, size_t new_position);
ReorderSymbolPartsAction(SymbolGroup& old_parent, size_t old_position, SymbolGroup& new_parent, size_t new_position);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
SymbolGroup* old_parent, *new_parent;///< Parents to move from and to
SymbolGroup* old_parent, *new_parent;///< Parents to move from and to
public:
size_t old_position, new_position; ///< Positions to move from and to
size_t old_position, new_position; ///< Positions to move from and to
};
/// Break up a single group, and put its contents at a specific position
class UngroupReorderSymbolPartsAction : public SymbolPartListAction {
public:
/// Remove all the given groups
UngroupReorderSymbolPartsAction(SymbolGroup& group_parent, size_t group_pos, SymbolGroup& target_parent, size_t target_pos);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Remove all the given groups
UngroupReorderSymbolPartsAction(SymbolGroup& group_parent, size_t group_pos, SymbolGroup& target_parent, size_t target_pos);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
SymbolGroup& group_parent;
size_t group_pos;
SymbolGroupP group; ///< Group to destroy
SymbolGroup& target_parent;
size_t target_pos;
SymbolGroup& group_parent;
size_t group_pos;
SymbolGroupP group; ///< Group to destroy
SymbolGroup& target_parent;
size_t target_pos;
};
// ----------------------------------------------------------------------------- : Group symbol parts
@@ -286,32 +286,32 @@ class UngroupReorderSymbolPartsAction : public SymbolPartListAction {
/// Group multiple symbol parts together
class GroupSymbolPartsActionBase : public SymbolPartListAction {
public:
GroupSymbolPartsActionBase(SymbolGroup& root);
virtual void perform(bool to_undo);
GroupSymbolPartsActionBase(SymbolGroup& root);
virtual void perform(bool to_undo);
protected:
SymbolGroup& root; ///< Symbol or group to group stuff in
vector<SymbolPartP> old_part_list; ///< Old part list of the symbol
SymbolGroup& root; ///< Symbol or group to group stuff in
vector<SymbolPartP> old_part_list; ///< Old part list of the symbol
};
/// Group multiple symbol parts together
class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
public:
GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group);
virtual String getName(bool to_undo) const;
GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group);
virtual String getName(bool to_undo) const;
private:
SymbolGroupP group;
SymbolGroupP group;
};
/// Break up one or more SymbolGroups
class UngroupSymbolPartsAction : public GroupSymbolPartsActionBase {
public:
/// Remove all the given groups
UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& groups);
virtual String getName(bool to_undo) const;
/// Remove all the given groups
UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& groups);
virtual String getName(bool to_undo) const;
};
// ----------------------------------------------------------------------------- : EOF
+311 -311
View File
@@ -18,270 +18,270 @@ DECLARE_TYPEOF_COLLECTION(ControlPointP);
inline double sgn(double v) { return v > 0 ? 1 : -1; }
Vector2D constrain_vector(const Vector2D& v, bool constrain, bool only_diagonal) {
if (!constrain) return v;
double ax = fabs(v.x), ay = fabs(v.y);
if (ax * 2 < ay && !only_diagonal) {
return Vector2D(0, v.y); // vertical
} else if(ay * 2 < ax && !only_diagonal) {
return Vector2D(v.x, 0); // horizontal
} else {
return Vector2D( // diagonal
sgn(v.x) * (ax + ay) / 2,
sgn(v.y) * (ax + ay) / 2
);
}
if (!constrain) return v;
double ax = fabs(v.x), ay = fabs(v.y);
if (ax * 2 < ay && !only_diagonal) {
return Vector2D(0, v.y); // vertical
} else if(ay * 2 < ax && !only_diagonal) {
return Vector2D(v.x, 0); // horizontal
} else {
return Vector2D( // diagonal
sgn(v.x) * (ax + ay) / 2,
sgn(v.y) * (ax + ay) / 2
);
}
}
inline double snap(double x, int steps) {
return steps <= 0 ? x : floor(x * steps + 0.5) / steps;
return steps <= 0 ? x : floor(x * steps + 0.5) / steps;
}
Vector2D snap_vector(const Vector2D& v, int steps) {
return Vector2D(snap(v.x, steps), snap(v.y, steps));
return Vector2D(snap(v.x, steps), snap(v.y, steps));
}
Vector2D constrain_snap_vector(const Vector2D& v, const Vector2D& d, bool constrain, int steps) {
if (!constrain) return snap_vector(v+d, steps);
double ax = fabs(d.x), ay = fabs(d.y);
if (ax * 2 < ay) {
return Vector2D(v.x, snap(d.y + v.y, steps)); // vertical
} else if(ay * 2 < ax) {
return Vector2D(snap(d.x + v.x, steps), v.y); // horizontal
} else {
double dc = (ax + ay) / 2; // delta in both directions
double dxs = snap(v.x + dc, steps) - v.x; // snapped to x
double dys = snap(v.y + dc, steps) - v.y; // snapped to y
if (fabs(dxs-dc) < fabs(dys-dc)) {
// take the one that is closest to the unsnaped delta
return Vector2D(v.x + sgn(d.x) * dxs, v.y + sgn(d.y) * dxs);
} else {
return Vector2D(v.x + sgn(d.x) * dys, v.y + sgn(d.y) * dys);
}
}
if (!constrain) return snap_vector(v+d, steps);
double ax = fabs(d.x), ay = fabs(d.y);
if (ax * 2 < ay) {
return Vector2D(v.x, snap(d.y + v.y, steps)); // vertical
} else if(ay * 2 < ax) {
return Vector2D(snap(d.x + v.x, steps), v.y); // horizontal
} else {
double dc = (ax + ay) / 2; // delta in both directions
double dxs = snap(v.x + dc, steps) - v.x; // snapped to x
double dys = snap(v.y + dc, steps) - v.y; // snapped to y
if (fabs(dxs-dc) < fabs(dys-dc)) {
// take the one that is closest to the unsnaped delta
return Vector2D(v.x + sgn(d.x) * dxs, v.y + sgn(d.y) * dxs);
} else {
return Vector2D(v.x + sgn(d.x) * dys, v.y + sgn(d.y) * dys);
}
}
}
Vector2D constrain_snap_vector_offset(const Vector2D& off1, const Vector2D& d, bool constrain, int steps) {
return constrain_snap_vector(off1, d, constrain, steps) - off1;
return constrain_snap_vector(off1, d, constrain, steps) - off1;
}
// calculate constrained delta for the given offset, store in output if it is better
void constrain_snap_vector_offset_(const Vector2D& off, const Vector2D& d, bool constrain, int steps, Vector2D& best, double& best_length) {
Vector2D d2 = constrain_snap_vector_offset(off, d, constrain, steps);
double l2 = d2.lengthSqr();
if (l2 < best_length) {
best_length = l2;
best = d2;
}
Vector2D d2 = constrain_snap_vector_offset(off, d, constrain, steps);
double l2 = d2.lengthSqr();
if (l2 < best_length) {
best_length = l2;
best = d2;
}
}
Vector2D constrain_snap_vector_offset(const Vector2D& off1, const Vector2D& off2, const Vector2D& d, bool constrain, int steps) {
Vector2D dd; double l = numeric_limits<double>::infinity();
constrain_snap_vector_offset_(off1, d, constrain, steps, dd, l);
constrain_snap_vector_offset_(off2, d, constrain, steps, dd, l);
constrain_snap_vector_offset_(Vector2D(off1.x,off2.y), d, constrain, steps, dd, l);
constrain_snap_vector_offset_(Vector2D(off2.x,off1.y), d, constrain, steps, dd, l);
return dd;
Vector2D dd; double l = numeric_limits<double>::infinity();
constrain_snap_vector_offset_(off1, d, constrain, steps, dd, l);
constrain_snap_vector_offset_(off2, d, constrain, steps, dd, l);
constrain_snap_vector_offset_(Vector2D(off1.x,off2.y), d, constrain, steps, dd, l);
constrain_snap_vector_offset_(Vector2D(off2.x,off1.y), d, constrain, steps, dd, l);
return dd;
}
String action_name_for(const set<ControlPointP>& points, const String& action) {
return format_string(action, points.size() == 1 ? _TYPE_("point") : _TYPE_("points"));
return format_string(action, points.size() == 1 ? _TYPE_("point") : _TYPE_("points"));
}
// ----------------------------------------------------------------------------- : Move control point
ControlPointMoveAction::ControlPointMoveAction(const set<ControlPointP>& points)
: points(points)
, constrain(false)
, snap(0)
: points(points)
, constrain(false)
, snap(0)
{
oldValues.reserve(points.size());
FOR_EACH(p, points) {
oldValues.push_back(p->pos);
}
oldValues.reserve(points.size());
FOR_EACH(p, points) {
oldValues.push_back(p->pos);
}
}
String ControlPointMoveAction::getName(bool to_undo) const {
return action_name_for(points, _ACTION_("move"));
return action_name_for(points, _ACTION_("move"));
}
void ControlPointMoveAction::perform(bool to_undo) {
FOR_EACH_2(p,points, op,oldValues) {
swap(p->pos, op);
}
FOR_EACH_2(p,points, op,oldValues) {
swap(p->pos, op);
}
}
void ControlPointMoveAction::move (const Vector2D& deltaDelta) {
delta += deltaDelta;
// Move each point by delta, possibly constrained
set<ControlPointP>::const_iterator it = points.begin();
vector<Vector2D> ::iterator it2 = oldValues.begin();
for( ; it != points.end() && it2 != oldValues.end() ; ++it, ++it2) {
(*it)->pos = constrain_snap_vector(*it2, delta, constrain, snap);
}
delta += deltaDelta;
// Move each point by delta, possibly constrained
set<ControlPointP>::const_iterator it = points.begin();
vector<Vector2D> ::iterator it2 = oldValues.begin();
for( ; it != points.end() && it2 != oldValues.end() ; ++it, ++it2) {
(*it)->pos = constrain_snap_vector(*it2, delta, constrain, snap);
}
}
// ----------------------------------------------------------------------------- : Move handle
HandleMoveAction::HandleMoveAction(const SelectedHandle& handle)
: handle(handle)
, old_handle(handle.getHandle())
, old_other (handle.getOther())
, constrain(false)
, snap(0)
: handle(handle)
, old_handle(handle.getHandle())
, old_other (handle.getOther())
, constrain(false)
, snap(0)
{}
String HandleMoveAction::getName(bool to_undo) const {
return _ACTION_("move handle");
return _ACTION_("move handle");
}
void HandleMoveAction::perform(bool to_undo) {
swap(old_handle, handle.getHandle());
swap(old_other, handle.getOther());
swap(old_handle, handle.getHandle());
swap(old_other, handle.getOther());
}
void HandleMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
handle.getHandle() = constrain_snap_vector_offset(handle.point->pos, old_handle + delta, constrain, snap);
handle.getOther() = old_other;
handle.onUpdateHandle();
delta += deltaDelta;
handle.getHandle() = constrain_snap_vector_offset(handle.point->pos, old_handle + delta, constrain, snap);
handle.getOther() = old_other;
handle.onUpdateHandle();
}
// ----------------------------------------------------------------------------- : Segment mode
ControlPointUpdate::ControlPointUpdate(const ControlPointP& pnt)
: other(*pnt)
, point(pnt)
: other(*pnt)
, point(pnt)
{}
void ControlPointUpdate::perform() {
swap(other, *point);
swap(other, *point);
}
SegmentModeAction::SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode)
: point1(p1), point2(p2)
: point1(p1), point2(p2)
{
if (p1->segment_after == mode) return;
point1.other.segment_after = point2.other.segment_before = mode;
if (mode == SEGMENT_LINE) {
point1.other.delta_after = Vector2D(0,0);
point2.other.delta_before = Vector2D(0,0);
point1.other.lock = LOCK_FREE;
point2.other.lock = LOCK_FREE;
} else if (mode == SEGMENT_CURVE) {
point1.other.delta_after = (p2->pos - p1->pos) / 3.0f;
point2.other.delta_before = (p1->pos - p2->pos) / 3.0f;
}
if (p1->segment_after == mode) return;
point1.other.segment_after = point2.other.segment_before = mode;
if (mode == SEGMENT_LINE) {
point1.other.delta_after = Vector2D(0,0);
point2.other.delta_before = Vector2D(0,0);
point1.other.lock = LOCK_FREE;
point2.other.lock = LOCK_FREE;
} else if (mode == SEGMENT_CURVE) {
point1.other.delta_after = (p2->pos - p1->pos) / 3.0f;
point2.other.delta_before = (p1->pos - p2->pos) / 3.0f;
}
}
String SegmentModeAction::getName(bool to_undo) const {
SegmentMode mode = to_undo ? point1.point->segment_after : point1.other.segment_after;
if (mode == SEGMENT_LINE) return _ACTION_("convert to line");
else return _ACTION_("convert to curve");
SegmentMode mode = to_undo ? point1.point->segment_after : point1.other.segment_after;
if (mode == SEGMENT_LINE) return _ACTION_("convert to line");
else return _ACTION_("convert to curve");
}
void SegmentModeAction::perform(bool to_undo) {
point1.perform();
point2.perform();
point1.perform();
point2.perform();
}
// ----------------------------------------------------------------------------- : Locking mode
LockModeAction::LockModeAction(const ControlPointP& p, LockMode lock)
: point(p)
: point(p)
{
point.other.lock = lock;
point.other.onUpdateLock();
point.other.lock = lock;
point.other.onUpdateLock();
}
String LockModeAction::getName(bool to_undo) const {
return _ACTION_("lock point");
return _ACTION_("lock point");
}
void LockModeAction::perform(bool to_undo) {
point.perform();
point.perform();
}
// ----------------------------------------------------------------------------- : Move curve
CurveDragAction::CurveDragAction(const ControlPointP& point1, const ControlPointP& point2)
: SegmentModeAction(point1, point2, SEGMENT_CURVE)
: SegmentModeAction(point1, point2, SEGMENT_CURVE)
{}
String CurveDragAction::getName(bool to_undo) const {
return _ACTION_("move curve");
return _ACTION_("move curve");
}
void CurveDragAction::perform(bool to_undo) {
SegmentModeAction::perform(to_undo);
SegmentModeAction::perform(to_undo);
}
void CurveDragAction::move(const Vector2D& delta, double t) {
// Logic:
// Assuming old point is p, new point is p'
// Point on old bezier curve is:
// p = a t^3 + 3b (1-t) t^2 + 3c (1-t)^2 t + d (1-t)^2
// Point on new bezier curve is:
// p_(' = a t^3 + 3b') (1-t) t^2 + 3c' (1-t)^2 t + d (1-t)^2
// We now want to change control points b and c, the closer we are to b (t close to 0)
// the more effect we have on b, so we substitute:
// b' = b + x t
// c' = c + x (1-t)
// Solving for x we get:
// x = (p'-p) / ( t (1-t) ( t^2 + (1-t)^2) )
// Naming:
// delta = p' - p
// pointDelta = x * t * (1-t)
Vector2D pointDelta = delta / (3 * (t * t + (1-t) * (1-t)));
point1.point->delta_after += pointDelta / t;
point2.point->delta_before += pointDelta / (1-t);
point1.point->onUpdateHandle(HANDLE_AFTER);
point2.point->onUpdateHandle(HANDLE_BEFORE);
// Logic:
// Assuming old point is p, new point is p'
// Point on old bezier curve is:
// p = a t^3 + 3b (1-t) t^2 + 3c (1-t)^2 t + d (1-t)^2
// Point on new bezier curve is:
// p_(' = a t^3 + 3b') (1-t) t^2 + 3c' (1-t)^2 t + d (1-t)^2
// We now want to change control points b and c, the closer we are to b (t close to 0)
// the more effect we have on b, so we substitute:
// b' = b + x t
// c' = c + x (1-t)
// Solving for x we get:
// x = (p'-p) / ( t (1-t) ( t^2 + (1-t)^2) )
// Naming:
// delta = p' - p
// pointDelta = x * t * (1-t)
Vector2D pointDelta = delta / (3 * (t * t + (1-t) * (1-t)));
point1.point->delta_after += pointDelta / t;
point2.point->delta_before += pointDelta / (1-t);
point1.point->onUpdateHandle(HANDLE_AFTER);
point2.point->onUpdateHandle(HANDLE_BEFORE);
}
// ----------------------------------------------------------------------------- : Add control point
ControlPointAddAction::ControlPointAddAction(const SymbolShapeP& shape, UInt insert_after, double t)
: shape(shape)
, new_point(new ControlPoint())
, insert_after(insert_after)
, point1(shape->getPoint(insert_after))
, point2(shape->getPoint(insert_after + 1))
: shape(shape)
, new_point(new ControlPoint())
, insert_after(insert_after)
, point1(shape->getPoint(insert_after))
, point2(shape->getPoint(insert_after + 1))
{
// calculate new point
if (point1.other.segment_after == SEGMENT_CURVE) {
// calculate new handles using de Casteljau's subdivision algorithm
deCasteljau(point1.other, point2.other, t, *new_point);
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
new_point->lock = LOCK_DIR;
new_point->segment_before = SEGMENT_CURVE;
new_point->segment_after = SEGMENT_CURVE;
} else {
new_point->pos = point1.other.pos * (1 - t) + point2.other.pos * t;
new_point->lock = LOCK_FREE;
new_point->segment_before = SEGMENT_LINE;
new_point->segment_after = SEGMENT_LINE;
}
// calculate new point
if (point1.other.segment_after == SEGMENT_CURVE) {
// calculate new handles using de Casteljau's subdivision algorithm
deCasteljau(point1.other, point2.other, t, *new_point);
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
new_point->lock = LOCK_DIR;
new_point->segment_before = SEGMENT_CURVE;
new_point->segment_after = SEGMENT_CURVE;
} else {
new_point->pos = point1.other.pos * (1 - t) + point2.other.pos * t;
new_point->lock = LOCK_FREE;
new_point->segment_before = SEGMENT_LINE;
new_point->segment_after = SEGMENT_LINE;
}
}
String ControlPointAddAction::getName(bool to_undo) const {
return _ACTION_("add control point");
return _ACTION_("add control point");
}
void ControlPointAddAction::perform(bool to_undo) {
if (to_undo) { // remove the point
shape->points.erase( shape->points.begin() + insert_after + 1);
} else {
shape->points.insert(shape->points.begin() + insert_after + 1, new_point);
}
// update points before/after
point1.perform();
point2.perform();
if (to_undo) { // remove the point
shape->points.erase( shape->points.begin() + insert_after + 1);
} else {
shape->points.insert(shape->points.begin() + insert_after + 1, new_point);
}
// update points before/after
point1.perform();
point2.perform();
}
// ----------------------------------------------------------------------------- : Remove control point
@@ -289,89 +289,89 @@ void ControlPointAddAction::perform(bool to_undo) {
/// Sqaure root that caries the sign over the root
/// or formally: ssqrt(x) = Re<sqrt(x)> - Im<sqrt(x)> = x / sqrt(|x|)
double ssqrt(double x) {
if (x > 0) return sqrt(x);
else return -sqrt(-x);
if (x > 0) return sqrt(x);
else return -sqrt(-x);
}
// Remove a single control point
class SinglePointRemoveAction : public Action, public IntrusivePtrBase<SinglePointRemoveAction> {
public:
SinglePointRemoveAction(const SymbolShapeP& shape, UInt position);
virtual String getName(bool to_undo) const { return _("Delete point"); }
virtual void perform(bool to_undo);
SinglePointRemoveAction(const SymbolShapeP& shape, UInt position);
virtual String getName(bool to_undo) const { return _("Delete point"); }
virtual void perform(bool to_undo);
private:
SymbolShapeP shape;
UInt position;
ControlPointP point; ///< Removed point
ControlPointUpdate point1, point2; ///< Points before/after
SymbolShapeP shape;
UInt position;
ControlPointP point; ///< Removed point
ControlPointUpdate point1, point2; ///< Points before/after
};
SinglePointRemoveAction::SinglePointRemoveAction(const SymbolShapeP& shape, UInt position)
: shape(shape)
, position(position)
, point (shape->getPoint(position))
, point1(shape->getPoint(position - 1))
, point2(shape->getPoint(position + 1))
: shape(shape)
, position(position)
, point (shape->getPoint(position))
, point1(shape->getPoint(position - 1))
, point2(shape->getPoint(position + 1))
{
if (point1.other.segment_after == SEGMENT_CURVE || point2.other.segment_before == SEGMENT_CURVE) {
// try to preserve curve
Vector2D before = point->delta_before;
Vector2D after = point->delta_after;
// convert both segments to curves first
if (point1.other.segment_after != SEGMENT_CURVE) {
before = (point1.other.pos - point->pos) / 3.0;
point1.other.delta_after = -before;
point1.other.segment_after = SEGMENT_CURVE;
}
if (point2.other.segment_before != SEGMENT_CURVE) {
after = (point2.other.pos - point->pos) / 3.0;
point2.other.delta_before = -after;
point2.other.segment_before = SEGMENT_CURVE;
}
// The inverse of adding a point, reconstruct the original handles
// before being subdivided using de Casteljau's algorithm
// length of handles
double bl = before.length() + 0.00000001; // prevent division by 0
double al = after .length() + 0.00000001;
double totl = bl + al;
// set new handle sizes
point1.other.delta_after *= totl / bl;
point2.other.delta_before *= totl / al;
// Also take in acount cases where the point does not correspond to a freshly added point.
// distance from the point to the curve as it would be in the above case can be used,
// in the case of a point just added this distance = 0
BezierCurve c(point1.other, point2.other);
double t = bl / totl;
Vector2D p = c.pointAt(t);
Vector2D distP = point->pos - p;
// adjust handle sizes
point1.other.delta_after *= ssqrt(dot(distP, point1.other.delta_after) /point1.other.delta_after.lengthSqr()) + 1;
point2.other.delta_before *= ssqrt(dot(distP, point2.other.delta_before)/point2.other.delta_before.lengthSqr()) + 1;
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
} else {
// just lines, keep it that way
}
if (point1.other.segment_after == SEGMENT_CURVE || point2.other.segment_before == SEGMENT_CURVE) {
// try to preserve curve
Vector2D before = point->delta_before;
Vector2D after = point->delta_after;
// convert both segments to curves first
if (point1.other.segment_after != SEGMENT_CURVE) {
before = (point1.other.pos - point->pos) / 3.0;
point1.other.delta_after = -before;
point1.other.segment_after = SEGMENT_CURVE;
}
if (point2.other.segment_before != SEGMENT_CURVE) {
after = (point2.other.pos - point->pos) / 3.0;
point2.other.delta_before = -after;
point2.other.segment_before = SEGMENT_CURVE;
}
// The inverse of adding a point, reconstruct the original handles
// before being subdivided using de Casteljau's algorithm
// length of handles
double bl = before.length() + 0.00000001; // prevent division by 0
double al = after .length() + 0.00000001;
double totl = bl + al;
// set new handle sizes
point1.other.delta_after *= totl / bl;
point2.other.delta_before *= totl / al;
// Also take in acount cases where the point does not correspond to a freshly added point.
// distance from the point to the curve as it would be in the above case can be used,
// in the case of a point just added this distance = 0
BezierCurve c(point1.other, point2.other);
double t = bl / totl;
Vector2D p = c.pointAt(t);
Vector2D distP = point->pos - p;
// adjust handle sizes
point1.other.delta_after *= ssqrt(dot(distP, point1.other.delta_after) /point1.other.delta_after.lengthSqr()) + 1;
point2.other.delta_before *= ssqrt(dot(distP, point2.other.delta_before)/point2.other.delta_before.lengthSqr()) + 1;
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
} else {
// just lines, keep it that way
}
}
void SinglePointRemoveAction::perform(bool to_undo) {
if (to_undo) {
// reinsert the point
shape->points.insert(shape->points.begin() + position, point);
} else {
// remove the point
shape->points.erase( shape->points.begin() + position);
}
// update points around removed point
point1.perform();
point2.perform();
if (to_undo) {
// reinsert the point
shape->points.insert(shape->points.begin() + position, point);
} else {
// remove the point
shape->points.erase( shape->points.begin() + position);
}
// update points around removed point
point1.perform();
point2.perform();
}
DECLARE_POINTER_TYPE(SinglePointRemoveAction);
@@ -383,50 +383,50 @@ DECLARE_TYPEOF_COLLECTION(SinglePointRemoveActionP);
// Not all points mat be removed, at least two points must remain.
class ControlPointRemoveAction : public Action {
public:
ControlPointRemoveAction(const SymbolShapeP& shape, const set<ControlPointP>& to_delete);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
ControlPointRemoveAction(const SymbolShapeP& shape, const set<ControlPointP>& to_delete);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
vector<SinglePointRemoveActionP> removals;
vector<SinglePointRemoveActionP> removals;
};
ControlPointRemoveAction::ControlPointRemoveAction(const SymbolShapeP& shape, const set<ControlPointP>& to_delete) {
int index = 0;
// find points to remove, in reverse order
FOR_EACH(point, shape->points) {
if (to_delete.find(point) != to_delete.end()) {
// remove this point
removals.push_back(intrusive(new SinglePointRemoveAction(shape, index)));
}
++index;
}
int index = 0;
// find points to remove, in reverse order
FOR_EACH(point, shape->points) {
if (to_delete.find(point) != to_delete.end()) {
// remove this point
removals.push_back(intrusive(new SinglePointRemoveAction(shape, index)));
}
++index;
}
}
String ControlPointRemoveAction::getName(bool to_undo) const {
return removals.size() == 1 ? _ACTION_("delete point") : _ACTION_("delete points");
return removals.size() == 1 ? _ACTION_("delete point") : _ACTION_("delete points");
}
void ControlPointRemoveAction::perform(bool to_undo) {
if (to_undo) {
FOR_EACH(r, removals) r->perform(to_undo);
} else {
// in reverse order, because positions of later points will
// change after removal of earlier points.
FOR_EACH_REVERSE(r, removals) r->perform(to_undo);
}
if (to_undo) {
FOR_EACH(r, removals) r->perform(to_undo);
} else {
// in reverse order, because positions of later points will
// change after removal of earlier points.
FOR_EACH_REVERSE(r, removals) r->perform(to_undo);
}
}
Action* control_point_remove_action(const SymbolShapeP& shape, const set<ControlPointP>& to_delete) {
if (shape->points.size() - to_delete.size() < 2) {
// TODO : remove part?
//intrusive(new ControlPointRemoveAllAction(part));
return 0; // no action
} else {
return new ControlPointRemoveAction(shape, to_delete);
}
if (shape->points.size() - to_delete.size() < 2) {
// TODO : remove part?
//intrusive(new ControlPointRemoveAllAction(part));
return 0; // no action
} else {
return new ControlPointRemoveAction(shape, to_delete);
}
}
@@ -434,95 +434,95 @@ Action* control_point_remove_action(const SymbolShapeP& shape, const set<Control
// ----------------------------------------------------------------------------- : Move symmetry center/handle
SymmetryMoveAction::SymmetryMoveAction(SymbolSymmetry& symmetry, bool is_handle)
: symmetry(symmetry)
, is_handle(is_handle)
, original(is_handle ? symmetry.handle : symmetry.center)
, constrain(false)
, snap(0)
: symmetry(symmetry)
, is_handle(is_handle)
, original(is_handle ? symmetry.handle : symmetry.center)
, constrain(false)
, snap(0)
{}
String SymmetryMoveAction::getName(bool to_undo) const {
return is_handle ? _ACTION_("move symmetry handle") : _ACTION_("move symmetry center");
return is_handle ? _ACTION_("move symmetry handle") : _ACTION_("move symmetry center");
}
void SymmetryMoveAction::perform(bool to_undo) {
if (is_handle) {
swap(symmetry.handle, original);
} else {
swap(symmetry.center, original);
}
if (is_handle) {
swap(symmetry.handle, original);
} else {
swap(symmetry.center, original);
}
}
void SymmetryMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
if (is_handle) {
symmetry.handle = snap_vector(symmetry.center + original + delta, snap) - symmetry.center;
if (constrain) {
// constrain to multiples of 2pi/24 i.e. 24 stops
Radians angle = atan2(symmetry.handle.y, symmetry.handle.x);
Radians mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
symmetry.handle = Vector2D(cos(angle), sin(angle)) * symmetry.handle.length();
}
} else {
// Determine actual delta, possibly constrained and snapped
symmetry.center = constrain_snap_vector(original, delta, constrain, snap);
}
delta += deltaDelta;
if (is_handle) {
symmetry.handle = snap_vector(symmetry.center + original + delta, snap) - symmetry.center;
if (constrain) {
// constrain to multiples of 2pi/24 i.e. 24 stops
Radians angle = atan2(symmetry.handle.y, symmetry.handle.x);
Radians mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
symmetry.handle = Vector2D(cos(angle), sin(angle)) * symmetry.handle.length();
}
} else {
// Determine actual delta, possibly constrained and snapped
symmetry.center = constrain_snap_vector(original, delta, constrain, snap);
}
}
// ----------------------------------------------------------------------------- : Change symmetry kind
SymmetryTypeAction::SymmetryTypeAction(SymbolSymmetry& symmetry, SymbolSymmetryType type)
: symmetry(symmetry), type(type)
, old_name(symmetry.name)
, copies(symmetry.copies)
: symmetry(symmetry), type(type)
, old_name(symmetry.name)
, copies(symmetry.copies)
{
if (type == SYMMETRY_REFLECTION && symmetry.copies % 2 == 1) {
// make sure it is a multiple of two
copies = copies / 2 * 2;
}
// update name?
if (old_name == symmetry.expectedName()) {
swap(symmetry.kind, type);
old_name = symmetry.expectedName();
swap(symmetry.kind, type);
}
if (type == SYMMETRY_REFLECTION && symmetry.copies % 2 == 1) {
// make sure it is a multiple of two
copies = copies / 2 * 2;
}
// update name?
if (old_name == symmetry.expectedName()) {
swap(symmetry.kind, type);
old_name = symmetry.expectedName();
swap(symmetry.kind, type);
}
}
String SymmetryTypeAction::getName(bool to_undo) const {
return _ACTION_("change symmetry type");
return _ACTION_("change symmetry type");
}
void SymmetryTypeAction::perform(bool to_undo) {
swap(symmetry.kind, type);
swap(symmetry.copies, copies);
swap(symmetry.name, old_name);
swap(symmetry.kind, type);
swap(symmetry.copies, copies);
swap(symmetry.name, old_name);
}
// ----------------------------------------------------------------------------- : Change symmetry copies
SymmetryCopiesAction::SymmetryCopiesAction(SymbolSymmetry& symmetry, int copies_)
: symmetry(symmetry), copies(copies_)
, old_name(symmetry.name)
: symmetry(symmetry), copies(copies_)
, old_name(symmetry.name)
{
if (symmetry.kind == SYMMETRY_REFLECTION && copies % 2 == 1) {
// make sure it is a multiple of two
if (copies > symmetry.copies) copies++;
else copies--;
}
// update name?
if (old_name == symmetry.expectedName()) {
swap(symmetry.copies, copies);
old_name = symmetry.expectedName();
swap(symmetry.copies, copies);
}
if (symmetry.kind == SYMMETRY_REFLECTION && copies % 2 == 1) {
// make sure it is a multiple of two
if (copies > symmetry.copies) copies++;
else copies--;
}
// update name?
if (old_name == symmetry.expectedName()) {
swap(symmetry.copies, copies);
old_name = symmetry.expectedName();
swap(symmetry.copies, copies);
}
}
String SymmetryCopiesAction::getName(bool to_undo) const {
return _ACTION_("change symmetry copies");
return _ACTION_("change symmetry copies");
}
void SymmetryCopiesAction::perform(bool to_undo) {
swap(symmetry.copies, copies);
swap(symmetry.name, old_name);
swap(symmetry.copies, copies);
swap(symmetry.name, old_name);
}
+97 -97
View File
@@ -45,21 +45,21 @@ Vector2D constrain_snap_vector_offset(const Vector2D& off1, const Vector2D& off2
/// Moving a control point in a symbol
class ControlPointMoveAction : public Action {
public:
ControlPointMoveAction(const set<ControlPointP>& points);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
ControlPointMoveAction(const set<ControlPointP>& points);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
set<ControlPointP> points; ///< Points to move
vector<Vector2D> oldValues; ///< Their old positions
Vector2D delta; ///< Amount we moved
set<ControlPointP> points; ///< Points to move
vector<Vector2D> oldValues; ///< Their old positions
Vector2D delta; ///< Amount we moved
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
// ----------------------------------------------------------------------------- : Move handle
@@ -67,22 +67,22 @@ class ControlPointMoveAction : public Action {
/// Moving a handle(before/after) of a control point in a symbol
class HandleMoveAction : public Action {
public:
HandleMoveAction(const SelectedHandle& handle);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
HandleMoveAction(const SelectedHandle& handle);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
SelectedHandle handle; ///< The handle to move
Vector2D old_handle; ///< Old value of this handle
Vector2D old_other; ///< Old value of other handle, needed for contraints
Vector2D delta; ///< Amount we moved
SelectedHandle handle; ///< The handle to move
Vector2D old_handle; ///< Old value of this handle
Vector2D old_other; ///< Old value of other handle, needed for contraints
Vector2D delta; ///< Amount we moved
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
// ----------------------------------------------------------------------------- : Segment mode
@@ -90,29 +90,29 @@ class HandleMoveAction : public Action {
/// Utility class to update a control point
class ControlPointUpdate {
public:
ControlPointUpdate(const ControlPointP& pnt);
/// Perform or undo an update on this control point
void perform();
/// Other value that is swapped with the current one.
/// Should be changed to make perform have an effect
ControlPoint other;
/// The point that is to be changed, should not be updated before perform()
ControlPointP point;
ControlPointUpdate(const ControlPointP& pnt);
/// Perform or undo an update on this control point
void perform();
/// Other value that is swapped with the current one.
/// Should be changed to make perform have an effect
ControlPoint other;
/// The point that is to be changed, should not be updated before perform()
ControlPointP point;
};
/// Changing a line to a curve and vice versa
class SegmentModeAction : public Action {
public:
SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
protected:
ControlPointUpdate point1, point2;
ControlPointUpdate point1, point2;
};
// ----------------------------------------------------------------------------- : Locking mode
@@ -120,13 +120,13 @@ class SegmentModeAction : public Action {
/// Locking a control point
class LockModeAction : public Action {
public:
LockModeAction(const ControlPointP& p, LockMode mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
LockModeAction(const ControlPointP& p, LockMode mode);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
ControlPointUpdate point; ///< The affected point
ControlPointUpdate point; ///< The affected point
};
// ----------------------------------------------------------------------------- : Move curve
@@ -136,13 +136,13 @@ class LockModeAction : public Action {
*/
class CurveDragAction : public SegmentModeAction {
public:
CurveDragAction(const ControlPointP& point1, const ControlPointP& point2);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
// Move the curve by this much, it is grabbed at time t
void move(const Vector2D& delta, double t);
CurveDragAction(const ControlPointP& point1, const ControlPointP& point2);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
// Move the curve by this much, it is grabbed at time t
void move(const Vector2D& delta, double t);
};
// ----------------------------------------------------------------------------- : Add control point
@@ -150,19 +150,19 @@ class CurveDragAction : public SegmentModeAction {
/// Insert a new point in a symbol shape
class ControlPointAddAction : public Action {
public:
/// Insert a new point in shape, after position insertAfter_, at the time t on the segment
ControlPointAddAction(const SymbolShapeP& shape, UInt insert_after, double t);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
inline ControlPointP getNewPoint() const { return new_point; }
/// Insert a new point in shape, after position insertAfter_, at the time t on the segment
ControlPointAddAction(const SymbolShapeP& shape, UInt insert_after, double t);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
inline ControlPointP getNewPoint() const { return new_point; }
private:
SymbolShapeP shape; ///< SymbolShape we are in
ControlPointP new_point; ///< The point to insert
UInt insert_after; ///< Insert after index .. in the array
ControlPointUpdate point1, point2; ///< Update the points around the new point
SymbolShapeP shape; ///< SymbolShape we are in
ControlPointP new_point; ///< The point to insert
UInt insert_after; ///< Insert after index .. in the array
ControlPointUpdate point1, point2; ///< Update the points around the new point
};
// ----------------------------------------------------------------------------- : Remove control point
@@ -179,22 +179,22 @@ Action* control_point_remove_action(const SymbolShapeP& shape, const set<Control
/// Moving the handle or the center of a symbol symmetry
class SymmetryMoveAction : public Action {
public:
SymmetryMoveAction(SymbolSymmetry& symmetry, bool is_handle);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
SymmetryMoveAction(SymbolSymmetry& symmetry, bool is_handle);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
SymbolSymmetry& symmetry; ///< Affected part
bool is_handle; ///< Move the handle or the center?
Vector2D delta; ///< Amount we moved
Vector2D original; ///< Original value
SymbolSymmetry& symmetry; ///< Affected part
bool is_handle; ///< Move the handle or the center?
Vector2D delta; ///< Amount we moved
Vector2D original; ///< Original value
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
// ----------------------------------------------------------------------------- : Change symmetry kind
@@ -202,15 +202,15 @@ class SymmetryMoveAction : public Action {
/// Change the type of symmetry
class SymmetryTypeAction : public Action {
public:
SymmetryTypeAction(SymbolSymmetry& symmetry, SymbolSymmetryType type);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
SymmetryTypeAction(SymbolSymmetry& symmetry, SymbolSymmetryType type);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
SymbolSymmetry& symmetry;
SymbolSymmetryType type;
String old_name;
int copies; /// may be changed to make it a multiple of two
SymbolSymmetry& symmetry;
SymbolSymmetryType type;
String old_name;
int copies; /// may be changed to make it a multiple of two
};
// ----------------------------------------------------------------------------- : Change symmetry copies
@@ -218,14 +218,14 @@ class SymmetryTypeAction : public Action {
/// Change the number of copies of a symmetry
class SymmetryCopiesAction : public Action {
public:
SymmetryCopiesAction(SymbolSymmetry& symmetry, int copies);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
SymmetryCopiesAction(SymbolSymmetry& symmetry, int copies);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
SymbolSymmetry& symmetry;
int copies;
String old_name;
SymbolSymmetry& symmetry;
int copies;
String old_name;
};
// ----------------------------------------------------------------------------- : EOF
+136 -136
View File
@@ -23,17 +23,17 @@
// ----------------------------------------------------------------------------- : ValueAction
String ValueAction::getName(bool to_undo) const {
return _ACTION_1_("change", valueP->fieldP->name);
return _ACTION_1_("change", valueP->fieldP->name);
}
void ValueAction::perform(bool to_undo) {
if (card) {
swap(const_cast<Card*>(card)->time_modified, old_time_modified);
}
if (card) {
swap(const_cast<Card*>(card)->time_modified, old_time_modified);
}
}
void ValueAction::isOnCard(Card* card) {
this->card = card;
this->card = card;
}
// ----------------------------------------------------------------------------- : Simple
@@ -46,38 +46,38 @@ inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b
inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); }
inline void swap_value(PackageChoiceValue& a, PackageChoiceValue ::ValueType& b) { swap(a.package_name, b); }
inline void swap_value(MultipleChoiceValue& a, MultipleChoiceValue::ValueType& b) {
swap(a.value, b.value);
swap(a.last_change, b.last_change);
swap(a.value, b.value);
swap(a.last_change, b.last_change);
}
/// A ValueAction that swaps between old and new values
template <typename T, bool ALLOW_MERGE>
class SimpleValueAction : public ValueAction {
public:
inline SimpleValueAction(const intrusive_ptr<T>& value, const typename T::ValueType& new_value)
: ValueAction(value), new_value(new_value)
{}
virtual void perform(bool to_undo) {
ValueAction::perform(to_undo);
swap_value(static_cast<T&>(*valueP), new_value);
valueP->onAction(*this, to_undo); // notify value
}
virtual bool merge(const Action& action) {
if (!ALLOW_MERGE) return false;
TYPE_CASE(action, SimpleValueAction) {
if (action.valueP == valueP) {
// adjacent actions on the same value, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
}
inline SimpleValueAction(const intrusive_ptr<T>& value, const typename T::ValueType& new_value)
: ValueAction(value), new_value(new_value)
{}
virtual void perform(bool to_undo) {
ValueAction::perform(to_undo);
swap_value(static_cast<T&>(*valueP), new_value);
valueP->onAction(*this, to_undo); // notify value
}
virtual bool merge(const Action& action) {
if (!ALLOW_MERGE) return false;
TYPE_CASE(action, SimpleValueAction) {
if (action.valueP == valueP) {
// adjacent actions on the same value, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
}
private:
typename T::ValueType new_value;
typename T::ValueType new_value;
};
ValueAction* value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) { return new SimpleValueAction<ChoiceValue, true> (value, new_value); }
@@ -86,171 +86,171 @@ ValueAction* value_action(const ImageValueP& value, const FileName&
ValueAction* value_action(const SymbolValueP& value, const FileName& new_value) { return new SimpleValueAction<SymbolValue, false>(value, new_value); }
ValueAction* value_action(const PackageChoiceValueP& value, const String& new_value) { return new SimpleValueAction<PackageChoiceValue, false>(value, new_value); }
ValueAction* value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change) {
MultipleChoiceValue::ValueType v = { new_value, last_change };
return new SimpleValueAction<MultipleChoiceValue, false>(value, v);
MultipleChoiceValue::ValueType v = { new_value, last_change };
return new SimpleValueAction<MultipleChoiceValue, false>(value, v);
}
// ----------------------------------------------------------------------------- : Text
TextValueAction::TextValueAction(const TextValueP& value, size_t start, size_t end, size_t new_end, const Defaultable<String>& new_value, const String& name)
: ValueAction(value)
, selection_start(start), selection_end(end), new_selection_end(new_end)
, new_value(new_value)
, name(name)
: ValueAction(value)
, selection_start(start), selection_end(end), new_selection_end(new_end)
, new_value(new_value)
, name(name)
{}
String TextValueAction::getName(bool to_undo) const { return name; }
void TextValueAction::perform(bool to_undo) {
ValueAction::perform(to_undo);
swap_value(value(), new_value);
swap(selection_end, new_selection_end);
valueP->onAction(*this, to_undo); // notify value
ValueAction::perform(to_undo);
swap_value(value(), new_value);
swap(selection_end, new_selection_end);
valueP->onAction(*this, to_undo); // notify value
}
bool TextValueAction::merge(const Action& action) {
TYPE_CASE(action, TextValueAction) {
if (&action.value() == &value() && action.name == name) {
if (action.selection_start == selection_end) {
// adjacent edits, keep old value of this, it is older
selection_end = action.selection_end;
return true;
} else if (action.new_selection_end == selection_start && name == _ACTION_("backspace")) {
// adjacent backspaces
selection_start = action.selection_start;
selection_end = action.selection_end;
return true;
}
}
}
return false;
TYPE_CASE(action, TextValueAction) {
if (&action.value() == &value() && action.name == name) {
if (action.selection_start == selection_end) {
// adjacent edits, keep old value of this, it is older
selection_end = action.selection_end;
return true;
} else if (action.new_selection_end == selection_start && name == _ACTION_("backspace")) {
// adjacent backspaces
selection_start = action.selection_start;
selection_end = action.selection_end;
return true;
}
}
}
return false;
}
TextValue& TextValueAction::value() const {
return static_cast<TextValue&>(*valueP);
return static_cast<TextValue&>(*valueP);
}
TextValueAction* toggle_format_action(const TextValueP& value, const String& tag, size_t start_i, size_t end_i, size_t start, size_t end, const String& action_name) {
if (start > end) {
swap(start, end);
swap(start_i, end_i);
}
String new_value;
const String& str = value->value();
// Are we inside the tag we are toggling?
if (!is_in_tag(str, _("<") + tag, start_i, end_i)) {
// we are not inside this tag, add it
new_value = str.substr(0, start_i);
new_value += _("<") + tag + _(">");
new_value += str.substr(start_i, end_i - start_i);
new_value += _("</") + tag + _(">");
new_value += str.substr(end_i);
} else {
// we are inside this tag, 'remove' it
new_value = str.substr(0, start_i);
new_value += _("</") + tag + _(">");
new_value += str.substr(start_i, end_i - start_i);
new_value += _("<") + tag + _(">");
new_value += str.substr(end_i);
}
// Build action
if (start != end) {
// don't simplify if start == end, this way we insert <b></b>, allowing the
// user to press Ctrl+B and start typing bold text
new_value = simplify_tagged(new_value);
}
if (value->value() == new_value) {
return nullptr; // no changes
} else {
return new TextValueAction(value, start, end, end, new_value, action_name);
}
if (start > end) {
swap(start, end);
swap(start_i, end_i);
}
String new_value;
const String& str = value->value();
// Are we inside the tag we are toggling?
if (!is_in_tag(str, _("<") + tag, start_i, end_i)) {
// we are not inside this tag, add it
new_value = str.substr(0, start_i);
new_value += _("<") + tag + _(">");
new_value += str.substr(start_i, end_i - start_i);
new_value += _("</") + tag + _(">");
new_value += str.substr(end_i);
} else {
// we are inside this tag, 'remove' it
new_value = str.substr(0, start_i);
new_value += _("</") + tag + _(">");
new_value += str.substr(start_i, end_i - start_i);
new_value += _("<") + tag + _(">");
new_value += str.substr(end_i);
}
// Build action
if (start != end) {
// don't simplify if start == end, this way we insert <b></b>, allowing the
// user to press Ctrl+B and start typing bold text
new_value = simplify_tagged(new_value);
}
if (value->value() == new_value) {
return nullptr; // no changes
} else {
return new TextValueAction(value, start, end, end, new_value, action_name);
}
}
TextValueAction* typing_action(const TextValueP& value, size_t start_i, size_t end_i, size_t start, size_t end, const String& replacement, const String& action_name) {
bool reverse = start > end;
if (reverse) {
swap(start, end);
swap(start_i, end_i);
}
String new_value = tagged_substr_replace(value->value(), start_i, end_i, replacement);
if (value->value() == new_value) {
// no change
return nullptr;
} else {
if (reverse) {
return new TextValueAction(value, end, start, start+untag(replacement).size(), new_value, action_name);
} else {
return new TextValueAction(value, start, end, start+untag(replacement).size(), new_value, action_name);
}
}
bool reverse = start > end;
if (reverse) {
swap(start, end);
swap(start_i, end_i);
}
String new_value = tagged_substr_replace(value->value(), start_i, end_i, replacement);
if (value->value() == new_value) {
// no change
return nullptr;
} else {
if (reverse) {
return new TextValueAction(value, end, start, start+untag(replacement).size(), new_value, action_name);
} else {
return new TextValueAction(value, start, end, start+untag(replacement).size(), new_value, action_name);
}
}
}
// ----------------------------------------------------------------------------- : Reminder text
TextToggleReminderAction::TextToggleReminderAction(const TextValueP& value, size_t pos_in)
: ValueAction(value)
: ValueAction(value)
{
pos = in_tag(value->value(), _("<kw-"), pos_in, pos_in);
if (pos == String::npos) {
throw InternalError(_("TextToggleReminderAction: not in <kw- tag"));
}
Char c = value->value().GetChar(pos + 4);
enable = !(c == _('1') || c == _('A')); // if it was not enabled, then enable it
old = enable ? _('1') : _('0');
pos = in_tag(value->value(), _("<kw-"), pos_in, pos_in);
if (pos == String::npos) {
throw InternalError(_("TextToggleReminderAction: not in <kw- tag"));
}
Char c = value->value().GetChar(pos + 4);
enable = !(c == _('1') || c == _('A')); // if it was not enabled, then enable it
old = enable ? _('1') : _('0');
}
String TextToggleReminderAction::getName(bool to_undo) const {
return enable ? _("Show reminder text") : _("Hide reminder text");
return enable ? _("Show reminder text") : _("Hide reminder text");
}
void TextToggleReminderAction::perform(bool to_undo) {
ValueAction::perform(to_undo);
TextValue& value = static_cast<TextValue&>(*valueP);
String& val = value.value.mutate();
assert(pos + 4 < val.size());
size_t end = match_close_tag(val, pos);
Char& c = val[pos + 4];
swap(c, old);
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
ValueAction::perform(to_undo);
TextValue& value = static_cast<TextValue&>(*valueP);
String& val = value.value.mutate();
assert(pos + 4 < val.size());
size_t end = match_close_tag(val, pos);
Char& c = val[pos + 4];
swap(c, old);
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
}
// ----------------------------------------------------------------------------- : Event
String ScriptValueEvent::getName(bool) const {
assert(false); // this action is just an event, getName shouldn't be called
throw InternalError(_("ScriptValueEvent::getName"));
assert(false); // this action is just an event, getName shouldn't be called
throw InternalError(_("ScriptValueEvent::getName"));
}
void ScriptValueEvent::perform(bool) {
assert(false); // this action is just an event, it should not be performed
assert(false); // this action is just an event, it should not be performed
}
String ScriptStyleEvent::getName(bool) const {
assert(false); // this action is just an event, getName shouldn't be called
throw InternalError(_("ScriptStyleEvent::getName"));
assert(false); // this action is just an event, getName shouldn't be called
throw InternalError(_("ScriptStyleEvent::getName"));
}
void ScriptStyleEvent::perform(bool) {
assert(false); // this action is just an event, it should not be performed
assert(false); // this action is just an event, it should not be performed
}
// ----------------------------------------------------------------------------- : Action performer
ValueActionPerformer::ValueActionPerformer(const ValueP& value, Card* card, const SetP& set)
: value(value), card(card), set(set)
: value(value), card(card), set(set)
{}
ValueActionPerformer::~ValueActionPerformer() {}
void ValueActionPerformer::addAction(ValueAction* action) {
action->isOnCard(card);
set->actions.addAction(action);
action->isOnCard(card);
set->actions.addAction(action);
}
Package& ValueActionPerformer::getLocalPackage() {
return *set;
return *set;
}
+72 -72
View File
@@ -36,20 +36,20 @@ DECLARE_POINTER_TYPE(PackageChoiceValue);
/// An Action the changes a Value
class ValueAction : public Action {
public:
inline ValueAction(const ValueP& value)
: valueP(value), card(nullptr), old_time_modified(wxDateTime::Now())
{}
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// We know that the value is on the given card, add that information
void isOnCard(Card* card);
const ValueP valueP; ///< The modified value
const Card* card; ///< The card the value is on, or null if it is not a card value
inline ValueAction(const ValueP& value)
: valueP(value), card(nullptr), old_time_modified(wxDateTime::Now())
{}
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// We know that the value is on the given card, add that information
void isOnCard(Card* card);
const ValueP valueP; ///< The modified value
const Card* card; ///< The card the value is on, or null if it is not a card value
private:
wxDateTime old_time_modified;
wxDateTime old_time_modified;
};
// ----------------------------------------------------------------------------- : Simple
@@ -67,22 +67,22 @@ ValueAction* value_action(const PackageChoiceValueP& value, const String&
/// An action that changes a TextValue
class TextValueAction : public ValueAction {
public:
TextValueAction(const TextValueP& value, size_t start, size_t end, size_t new_end, const Defaultable<String>& new_value, const String& name);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual bool merge(const Action& action);
inline const String& newValue() const { return new_value(); }
/// The modified selection
size_t selection_start, selection_end;
TextValueAction(const TextValueP& value, size_t start, size_t end, size_t new_end, const Defaultable<String>& new_value, const String& name);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual bool merge(const Action& action);
inline const String& newValue() const { return new_value(); }
/// The modified selection
size_t selection_start, selection_end;
private:
inline TextValue& value() const;
size_t new_selection_end;
Defaultable<String> new_value;
String name;
inline TextValue& value() const;
size_t new_selection_end;
Defaultable<String> new_value;
String name;
};
/// Action for toggling some formating tag on or off in some range
@@ -97,15 +97,15 @@ TextValueAction* typing_action(const TextValueP& value, size_t start_i, size_t e
/// Toggle reminder text for a keyword on or off
class TextToggleReminderAction : public ValueAction {
public:
TextToggleReminderAction(const TextValueP& value, size_t pos);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
size_t pos; ///< Position of "<kw-"
bool enable; ///< Should the reminder text be turned on or off?
Char old; ///< Old value of the <kw- tag
TextToggleReminderAction(const TextValueP& value, size_t pos);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
size_t pos; ///< Position of "<kw-"
bool enable; ///< Should the reminder text be turned on or off?
Char old; ///< Old value of the <kw- tag
};
// ----------------------------------------------------------------------------- : Replace all
@@ -113,22 +113,22 @@ class TextToggleReminderAction : public ValueAction {
/// A TextValueAction without the start and end stuff
class SimpleTextValueAction : public ValueAction {
public:
SimpleTextValueAction(const Card* card, const TextValueP& value, const Defaultable<String>& new_value);
virtual void perform(bool to_undo);
bool merge(const SimpleTextValueAction& action);
SimpleTextValueAction(const Card* card, const TextValueP& value, const Defaultable<String>& new_value);
virtual void perform(bool to_undo);
bool merge(const SimpleTextValueAction& action);
private:
Defaultable<String> new_value;
Defaultable<String> new_value;
};
/// An action from "Replace All"; just a bunch of value actions performed in sequence
class ReplaceAllAction : public Action {
public:
~ReplaceAllAction();
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
vector<SimpleTextValueAction> actions;
~ReplaceAllAction();
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
vector<SimpleTextValueAction> actions;
};
// ----------------------------------------------------------------------------- : Event
@@ -136,27 +136,27 @@ class ReplaceAllAction : public Action {
/// Notification that a script caused a value to change
class ScriptValueEvent : public Action {
public:
inline ScriptValueEvent(const Card* card, const Value* value) : card(card), value(value) {}
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const Card* card; ///< Card the value is on
const Value* value; ///< The modified value
inline ScriptValueEvent(const Card* card, const Value* value) : card(card), value(value) {}
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const Card* card; ///< Card the value is on
const Value* value; ///< The modified value
};
/// Notification that a script caused a style to change
class ScriptStyleEvent : public Action {
public:
inline ScriptStyleEvent(const StyleSheet* stylesheet, const Style* style)
: stylesheet(stylesheet), style(style)
{}
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const StyleSheet* stylesheet; ///< StyleSheet the style is for
const Style* style; ///< The modified style
inline ScriptStyleEvent(const StyleSheet* stylesheet, const Style* style)
: stylesheet(stylesheet), style(style)
{}
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
const StyleSheet* stylesheet; ///< StyleSheet the style is for
const Style* style; ///< The modified style
};
@@ -166,16 +166,16 @@ class ScriptStyleEvent : public Action {
/** Used to reduce coupling */
class ValueActionPerformer {
public:
ValueActionPerformer(const ValueP& value, Card* card, const SetP& set);
~ValueActionPerformer();
/// Perform an action. The performer takes ownerwhip of the action.
void addAction(ValueAction* action);
const ValueP value; ///< The value
Package& getLocalPackage();
ValueActionPerformer(const ValueP& value, Card* card, const SetP& set);
~ValueActionPerformer();
/// Perform an action. The performer takes ownerwhip of the action.
void addAction(ValueAction* action);
const ValueP value; ///< The value
Package& getLocalPackage();
private:
Card* card; ///< Card the value is on (if any)
SetP set; ///< Set for the actions
Card* card; ///< Card the value is on (if any)
SetP set; ///< Set for the actions
};
// ----------------------------------------------------------------------------- : EOF
+26 -26
View File
@@ -16,37 +16,37 @@
// ----------------------------------------------------------------------------- : AddCardsScript
IMPLEMENT_REFLECTION_NO_SCRIPT(AddCardsScript) {
REFLECT(name);
REFLECT(description);
REFLECT(enabled);
REFLECT(script);
REFLECT(name);
REFLECT(description);
REFLECT(enabled);
REFLECT(script);
}
void AddCardsScript::perform(Set& set, vector<CardP>& out) {
// Perform script
Context& ctx = set.getContext();
ScriptValueP result = script.invoke(ctx);
// Add cards to out
ScriptValueP it = result->makeIterator(result);
while (ScriptValueP item = it->next()) {
CardP card = from_script<CardP>(item);
// is this a new card?
if (contains(set.cards,card) || contains(out,card)) {
// make copy
card = intrusive(new Card(*card));
}
out.push_back(card);
}
// Perform script
Context& ctx = set.getContext();
ScriptValueP result = script.invoke(ctx);
// Add cards to out
ScriptValueP it = result->makeIterator(result);
while (ScriptValueP item = it->next()) {
CardP card = from_script<CardP>(item);
// is this a new card?
if (contains(set.cards,card) || contains(out,card)) {
// make copy
card = intrusive(new Card(*card));
}
out.push_back(card);
}
}
void AddCardsScript::perform(Set& set) {
// Perform script
vector<CardP> cards;
perform(set,cards);
// Add to set
if (!cards.empty()) {
// TODO: change the name of the action somehow
set.actions.addAction(new AddCardAction(ADD, set, cards));
}
// Perform script
vector<CardP> cards;
perform(set,cards);
// Add to set
if (!cards.empty()) {
// TODO: change the name of the action somehow
set.actions.addAction(new AddCardAction(ADD, set, cards));
}
}
+11 -11
View File
@@ -20,17 +20,17 @@ DECLARE_POINTER_TYPE(Card);
/// A script to add one or more cards to a set
class AddCardsScript : public IntrusivePtrBase<AddCardsScript> {
public:
String name;
String description;
Scriptable<bool> enabled;
OptionalScript script;
/// Perform the script; return the cards (if any)
void perform(Set& set, vector<CardP>& out);
/// Perform the script; add cards to the set
void perform(Set& set);
DECLARE_REFLECTION();
String name;
String description;
Scriptable<bool> enabled;
OptionalScript script;
/// Perform the script; return the cards (if any)
void perform(Set& set, vector<CardP>& out);
/// Perform the script; add cards to the set
void perform(Set& set);
DECLARE_REFLECTION();
};
+49 -49
View File
@@ -21,73 +21,73 @@ DECLARE_TYPEOF_NO_REV(IndexMap<FieldP COMMA ValueP>);
// ----------------------------------------------------------------------------- : Card
Card::Card()
// for files made before we saved these times, set the time to 'yesterday'
: time_created (wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, time_modified(wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, has_styling(false)
// for files made before we saved these times, set the time to 'yesterday'
: time_created (wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, time_modified(wxDateTime::Now().Subtract(wxDateSpan::Day()).ResetTime())
, has_styling(false)
{
if (!game_for_reading()) {
throw InternalError(_("game_for_reading not set"));
}
data.init(game_for_reading()->card_fields);
if (!game_for_reading()) {
throw InternalError(_("game_for_reading not set"));
}
data.init(game_for_reading()->card_fields);
}
Card::Card(const Game& game)
: time_created (wxDateTime::Now())
, time_modified(wxDateTime::Now())
, has_styling(false)
: time_created (wxDateTime::Now())
, time_modified(wxDateTime::Now())
, has_styling(false)
{
data.init(game.card_fields);
data.init(game.card_fields);
}
String Card::identification() const {
// an identifying field
FOR_EACH_CONST(v, data) {
if (v->fieldP->identifying) {
return v->toString();
}
}
// otherwise the first field
if (!data.empty()) {
return data.at(0)->toString();
} else {
return wxEmptyString;
}
// an identifying field
FOR_EACH_CONST(v, data) {
if (v->fieldP->identifying) {
return v->toString();
}
}
// otherwise the first field
if (!data.empty()) {
return data.at(0)->toString();
} else {
return wxEmptyString;
}
}
bool Card::contains(String const& query) const {
FOR_EACH_CONST(v, data) {
if (find_i(v->toString(),query) != String::npos) return true;
}
if (find_i(notes,query) != String::npos) return true;
return false;
FOR_EACH_CONST(v, data) {
if (find_i(v->toString(),query) != String::npos) return true;
}
if (find_i(notes,query) != String::npos) return true;
return false;
}
IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields);
return extra_data.get(stylesheet.name(), stylesheet.extra_card_fields);
}
void mark_dependency_member(const Card& card, const String& name, const Dependency& dep) {
mark_dependency_member(card.data, name, dep);
mark_dependency_member(card.data, name, dep);
}
IMPLEMENT_REFLECTION(Card) {
REFLECT(stylesheet);
REFLECT(has_styling);
if (has_styling) {
if (stylesheet) {
REFLECT_IF_READING styling_data.init(stylesheet->styling_fields);
REFLECT(styling_data);
} else if (stylesheet_for_reading()) {
REFLECT_IF_READING styling_data.init(stylesheet_for_reading()->styling_fields);
REFLECT(styling_data);
} else if (tag.reading()) {
has_styling = false; // We don't know the style, this can be because of copy/pasting
}
}
REFLECT(notes);
REFLECT(time_created);
REFLECT(time_modified);
REFLECT(extra_data); // don't allow scripts to depend on style specific data
REFLECT_NAMELESS(data);
REFLECT(stylesheet);
REFLECT(has_styling);
if (has_styling) {
if (stylesheet) {
REFLECT_IF_READING styling_data.init(stylesheet->styling_fields);
REFLECT(styling_data);
} else if (stylesheet_for_reading()) {
REFLECT_IF_READING styling_data.init(stylesheet_for_reading()->styling_fields);
REFLECT(styling_data);
} else if (tag.reading()) {
has_styling = false; // We don't know the style, this can be because of copy/pasting
}
}
REFLECT(notes);
REFLECT(time_created);
REFLECT(time_modified);
REFLECT(extra_data); // don't allow scripts to depend on style specific data
REFLECT_NAMELESS(data);
}
+64 -64
View File
@@ -27,75 +27,75 @@ DECLARE_POINTER_TYPE(StyleSheet);
/// A card from a card Set
class Card : public IntrusivePtrVirtualBase {
public:
/// Default constructor, uses game_for_new_cards to make the game
Card();
/// Creates a card using the given game
Card(const Game& game);
/// The values on the fields of the card.
/** The indices should correspond to the card_fields in the Game */
IndexMap<FieldP, ValueP> data;
/// Notes for this card
String notes;
/// Time the card was created/last modified
wxDateTime time_created, time_modified;
/// Alternative style to use for this card
/** Optional; if not set use the card style from the set */
StyleSheetP stylesheet;
/// Alternative options to use for this card, for this card's stylesheet
/** Optional; if not set use the styling data from the set.
* If stylesheet is set then contains data for the this->stylesheet, otherwise for set->stylesheet
*/
IndexMap<FieldP,ValueP> styling_data;
/// Is the styling_data set?
bool has_styling;
/// Extra values for specitic stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> extra_data;
/// Styling information for a particular stylesheet
IndexMap<FieldP, ValueP>& extraDataFor(const StyleSheet& stylesheet);
/// Keyword usage statistics
vector<pair<Value*,const Keyword*> > keyword_usage;
/// Get the identification of this card, an identification is something like a name, title, etc.
/** May return "" */
String identification() const;
/// Does any field contains the given query string?
bool contains(String const& query) const;
/// Does this card contain each of the words in the query string?
bool contains_words(String const& query) const;
/// Find a value in the data by name and type
template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) {
if ((*it)->fieldP->name == name) {
T* ret = dynamic_cast<T*>(it->get());
if (!ret) throw InternalError(_("Card field with name '")+name+_("' doesn't have the right type"));
return *ret;
}
}
throw InternalError(_("Expected a card field with name '")+name+_("'"));
}
template <typename T> const T& value(const String& name) const {
for(IndexMap<FieldP, ValueP>::const_iterator it = data.begin() ; it != data.end() ; ++it) {
if ((*it)->fieldP->name == name) {
const T* ret = dynamic_cast<const T*>(it->get());
if (!ret) throw InternalError(_("Card field with name '")+name+_("' doesn't have the right type"));
return *ret;
}
}
throw InternalError(_("Expected a card field with name '")+name+_("'"));
}
DECLARE_REFLECTION();
/// Default constructor, uses game_for_new_cards to make the game
Card();
/// Creates a card using the given game
Card(const Game& game);
/// The values on the fields of the card.
/** The indices should correspond to the card_fields in the Game */
IndexMap<FieldP, ValueP> data;
/// Notes for this card
String notes;
/// Time the card was created/last modified
wxDateTime time_created, time_modified;
/// Alternative style to use for this card
/** Optional; if not set use the card style from the set */
StyleSheetP stylesheet;
/// Alternative options to use for this card, for this card's stylesheet
/** Optional; if not set use the styling data from the set.
* If stylesheet is set then contains data for the this->stylesheet, otherwise for set->stylesheet
*/
IndexMap<FieldP,ValueP> styling_data;
/// Is the styling_data set?
bool has_styling;
/// Extra values for specitic stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> extra_data;
/// Styling information for a particular stylesheet
IndexMap<FieldP, ValueP>& extraDataFor(const StyleSheet& stylesheet);
/// Keyword usage statistics
vector<pair<Value*,const Keyword*> > keyword_usage;
/// Get the identification of this card, an identification is something like a name, title, etc.
/** May return "" */
String identification() const;
/// Does any field contains the given query string?
bool contains(String const& query) const;
/// Does this card contain each of the words in the query string?
bool contains_words(String const& query) const;
/// Find a value in the data by name and type
template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) {
if ((*it)->fieldP->name == name) {
T* ret = dynamic_cast<T*>(it->get());
if (!ret) throw InternalError(_("Card field with name '")+name+_("' doesn't have the right type"));
return *ret;
}
}
throw InternalError(_("Expected a card field with name '")+name+_("'"));
}
template <typename T> const T& value(const String& name) const {
for(IndexMap<FieldP, ValueP>::const_iterator it = data.begin() ; it != data.end() ; ++it) {
if ((*it)->fieldP->name == name) {
const T* ret = dynamic_cast<const T*>(it->get());
if (!ret) throw InternalError(_("Card field with name '")+name+_("' doesn't have the right type"));
return *ret;
}
}
throw InternalError(_("Expected a card field with name '")+name+_("'"));
}
DECLARE_REFLECTION();
};
inline String type_name(const Card&) {
return _TYPE_("card");
return _TYPE_("card");
}
inline String type_name(const vector<CardP>&) {
return _TYPE_("cards"); // not actually used, only for locale.pl script
return _TYPE_("cards"); // not actually used, only for locale.pl script
}
void mark_dependency_member(const Card& value, const String& name, const Dependency& dep);
+8 -8
View File
@@ -15,14 +15,14 @@
/// What should be drawn?
enum DrawWhat
{ DRAW_NOTHING = 0x00
, DRAW_NORMAL = 0x01 // draw normal things, like the text
, DRAW_BORDERS = 0x10 // draw editor stuff, such as borders/lines, can be disabled.
, DRAW_BOXES = 0x20 // draw editor stuff, such as borders/lines, can be disabled.
, DRAW_EDITING = 0x40 // draw other editor stuff, can be disabled.
, DRAW_ERRORS = 0x80 // draw error indicators, can't be disabled
, DRAW_ACTIVE = 0x100 // draw active editor stuff, such as hidden separators and atom highlights
, DRAW_NATIVELOOK = 0x200 // use a native look
{ DRAW_NOTHING = 0x00
, DRAW_NORMAL = 0x01 // draw normal things, like the text
, DRAW_BORDERS = 0x10 // draw editor stuff, such as borders/lines, can be disabled.
, DRAW_BOXES = 0x20 // draw editor stuff, such as borders/lines, can be disabled.
, DRAW_EDITING = 0x40 // draw other editor stuff, can be disabled.
, DRAW_ERRORS = 0x80 // draw error indicators, can't be disabled
, DRAW_ACTIVE = 0x100 // draw active editor stuff, such as hidden separators and atom highlights
, DRAW_NATIVELOOK = 0x200 // use a native look
};
// ----------------------------------------------------------------------------- : EOF
+15 -15
View File
@@ -15,8 +15,8 @@
// ----------------------------------------------------------------------------- : Export template, basics
ExportTemplate::ExportTemplate()
: file_type(_("HTML files (*.html)|*.html"))
, create_directory(false)
: file_type(_("HTML files (*.html)|*.html"))
, create_directory(false)
{}
String ExportTemplate::typeNameStatic() { return _("export-template"); }
@@ -24,23 +24,23 @@ String ExportTemplate::typeName() const { return _("export-template"); }
Version ExportTemplate::fileVersion() const { return file_version_export_template; }
void ExportTemplate::validate(Version) {
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("export template")));
}
// an export template depends on the game it is made for
requireDependency(game.get());
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("export template")));
}
// an export template depends on the game it is made for
requireDependency(game.get());
}
IMPLEMENT_REFLECTION(ExportTemplate) {
REFLECT_BASE(Packaged);
REFLECT(game);
REFLECT(file_type);
REFLECT(create_directory);
REFLECT(option_fields);
REFLECT_IF_READING option_style.init(option_fields);
REFLECT(option_style);
REFLECT(script);
REFLECT_BASE(Packaged);
REFLECT(game);
REFLECT(file_type);
REFLECT(create_directory);
REFLECT(option_fields);
REFLECT_IF_READING option_style.init(option_fields);
REFLECT(option_style);
REFLECT(script);
}
// ----------------------------------------------------------------------------- : ExportInfo
+24 -24
View File
@@ -25,37 +25,37 @@ DECLARE_POINTER_TYPE(Package);
/// A template for exporting sets to HTML or text format
class ExportTemplate : public Packaged {
public:
ExportTemplate();
GameP game; ///< Game this template is for
String file_type; ///< Type of the created file, in "name|*.ext" format
bool create_directory; ///< The export creates a directory for additional data files
vector<FieldP> option_fields; ///< Options for exporting
IndexMap<FieldP,StyleP> option_style; ///< Style of the options
OptionalScript script; ///< Export script, for multi file templates and initialization
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
virtual void validate(Version = app_version);
ExportTemplate();
GameP game; ///< Game this template is for
String file_type; ///< Type of the created file, in "name|*.ext" format
bool create_directory; ///< The export creates a directory for additional data files
vector<FieldP> option_fields; ///< Options for exporting
IndexMap<FieldP,StyleP> option_style; ///< Style of the options
OptionalScript script; ///< Export script, for multi file templates and initialization
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
virtual void validate(Version = app_version);
private:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : ExportInfo
/// Information that can be used by export functions
struct ExportInfo {
ExportInfo();
SetP set; ///< The set that is being exported
PackageP export_template; ///< The export template used
/// When using the CLI, this can be a fake package to allow reading from the cwd
String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory)
/// This is just the directory name
String directory_absolute; ///< The absolute path of the directory
map<String,wxSize> exported_images; ///< Images (from symbol font) already exported, and their size
bool allow_writes_outside; ///< Can files outside the directory be written to?
ExportInfo();
SetP set; ///< The set that is being exported
PackageP export_template; ///< The export template used
/// When using the CLI, this can be a fake package to allow reading from the cwd
String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory)
/// This is just the directory name
String directory_absolute; ///< The absolute path of the directory
map<String,wxSize> exported_images; ///< Images (from symbol font) already exported, and their size
bool allow_writes_outside; ///< Can files outside the directory be written to?
};
DECLARE_DYNAMIC_ARG(ExportInfo*, export_info);
+180 -180
View File
@@ -24,239 +24,239 @@ DECLARE_TYPEOF_COLLECTION(StyleListener*);
// ----------------------------------------------------------------------------- : Field
Field::Field()
: index (0) // sensible default?
, editable (true)
, save_value (true)
, show_statistics (true)
, position_hint (0)
, identifying (false)
, card_list_column (100)
, card_list_width (100)
, card_list_visible(false)
, card_list_allow (true)
, card_list_align (ALIGN_LEFT)
, tab_index (0)
: index (0) // sensible default?
, editable (true)
, save_value (true)
, show_statistics (true)
, position_hint (0)
, identifying (false)
, card_list_column (100)
, card_list_width (100)
, card_list_visible(false)
, card_list_allow (true)
, card_list_align (ALIGN_LEFT)
, tab_index (0)
{}
Field::~Field() {}
void Field::initDependencies(Context& ctx, const Dependency& dep) const {
sort_script.initDependencies(ctx, dep);
sort_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(Field) {
REFLECT_IF_NOT_READING {
String type = typeName();
REFLECT(type);
}
REFLECT(name);
REFLECT_IF_READING name = canonical_name_form(name);
REFLECT(description);
REFLECT_N("icon", icon_filename);
REFLECT(editable);
REFLECT(save_value);
REFLECT(show_statistics);
REFLECT(position_hint);
REFLECT(identifying);
REFLECT(card_list_column);
REFLECT(card_list_width);
REFLECT(card_list_visible);
REFLECT(card_list_allow);
REFLECT(card_list_name);
REFLECT(sort_script);
REFLECT_IF_READING if(card_list_name.empty()) card_list_name = name;
REFLECT_N("card_list_alignment", card_list_align);
REFLECT(tab_index);
REFLECT_IF_NOT_READING {
String type = typeName();
REFLECT(type);
}
REFLECT(name);
REFLECT_IF_READING name = canonical_name_form(name);
REFLECT(description);
REFLECT_N("icon", icon_filename);
REFLECT(editable);
REFLECT(save_value);
REFLECT(show_statistics);
REFLECT(position_hint);
REFLECT(identifying);
REFLECT(card_list_column);
REFLECT(card_list_width);
REFLECT(card_list_visible);
REFLECT(card_list_allow);
REFLECT(card_list_name);
REFLECT(sort_script);
REFLECT_IF_READING if(card_list_name.empty()) card_list_name = name;
REFLECT_N("card_list_alignment", card_list_align);
REFLECT(tab_index);
}
template <>
intrusive_ptr<Field> read_new<Field>(Reader& reader) {
// there must be a type specified
String type;
reader.handle(_("type"), type);
if (type == _("text")) return intrusive(new TextField());
else if (type == _("choice")) return intrusive(new ChoiceField());
else if (type == _("multiple choice")) return intrusive(new MultipleChoiceField());
else if (type == _("boolean")) return intrusive(new BooleanField());
else if (type == _("image")) return intrusive(new ImageField());
else if (type == _("symbol")) return intrusive(new SymbolField());
else if (type == _("color")) return intrusive(new ColorField());
else if (type == _("info")) return intrusive(new InfoField());
else if (type == _("package choice")) return intrusive(new PackageChoiceField());
else if (type.empty()) {
reader.warning(_ERROR_1_("expected key", _("type")));
throw ParseError(_ERROR_("aborting parsing"));
} else {
reader.warning(_ERROR_1_("unsupported field type", type));
throw ParseError(_ERROR_("aborting parsing"));
}
// there must be a type specified
String type;
reader.handle(_("type"), type);
if (type == _("text")) return intrusive(new TextField());
else if (type == _("choice")) return intrusive(new ChoiceField());
else if (type == _("multiple choice")) return intrusive(new MultipleChoiceField());
else if (type == _("boolean")) return intrusive(new BooleanField());
else if (type == _("image")) return intrusive(new ImageField());
else if (type == _("symbol")) return intrusive(new SymbolField());
else if (type == _("color")) return intrusive(new ColorField());
else if (type == _("info")) return intrusive(new InfoField());
else if (type == _("package choice")) return intrusive(new PackageChoiceField());
else if (type.empty()) {
reader.warning(_ERROR_1_("expected key", _("type")));
throw ParseError(_ERROR_("aborting parsing"));
} else {
reader.warning(_ERROR_1_("unsupported field type", type));
throw ParseError(_ERROR_("aborting parsing"));
}
}
// ----------------------------------------------------------------------------- : Style
Style::Style(const FieldP& field)
: fieldP(field)
, z_index(0)
, left (1000000), top (1000000)
, width(0), height(0)
, right(1000000), bottom(1000000)
, angle(0)
, visible(true)
, automatic_side(AUTO_UNKNOWN)
, content_dependent(false)
: fieldP(field)
, z_index(0)
, left (1000000), top (1000000)
, width(0), height(0)
, right(1000000), bottom(1000000)
, angle(0)
, visible(true)
, automatic_side(AUTO_UNKNOWN)
, content_dependent(false)
{}
Style::~Style() {}
IMPLEMENT_REFLECTION(Style) {
REFLECT(z_index);
REFLECT(left);
REFLECT(width);
REFLECT(right);
REFLECT(top);
REFLECT(height);
REFLECT(bottom);
REFLECT(angle);
REFLECT(visible);
REFLECT(mask);
REFLECT(z_index);
REFLECT(left);
REFLECT(width);
REFLECT(right);
REFLECT(top);
REFLECT(height);
REFLECT(bottom);
REFLECT(angle);
REFLECT(visible);
REFLECT(mask);
}
void init_object(const FieldP& field, StyleP& style) {
if (!style) style = field->newStyle(field);
if (!style) style = field->newStyle(field);
}
template <> StyleP read_new<Style>(Reader&) {
throw InternalError(_("IndexMap contains nullptr StyleP the application should have crashed already"));
throw InternalError(_("IndexMap contains nullptr StyleP the application should have crashed already"));
}
inline bool is_set(const Scriptable<double>& x) {
return x.isScripted() || x < 100000;
return x.isScripted() || x < 100000;
}
inline bool is_setw(const Scriptable<double>& x) {
return x.isScripted() || fabs(x()) > 0.001;
return x.isScripted() || fabs(x()) > 0.001;
}
int Style::update(Context& ctx) {
int changed =
( left .update(ctx)
| width .update(ctx)
| right .update(ctx)
| top .update(ctx)
| height .update(ctx)
| bottom .update(ctx)
| angle .update(ctx) ) * CHANGE_SIZE
| visible.update(ctx) * CHANGE_OTHER
| mask .update(ctx) * CHANGE_MASK;
// determine automatic_side and attachment of rotation point
if (automatic_side == AUTO_UNKNOWN) {
if (!is_set (right)) automatic_side = (AutomaticSide)(automatic_side | AUTO_RIGHT);
else if (!is_setw(width)) automatic_side = (AutomaticSide)(automatic_side | AUTO_WIDTH);
else if (!is_set (left)) automatic_side = (AutomaticSide)(automatic_side | AUTO_LEFT);
else automatic_side = (AutomaticSide)(automatic_side | AUTO_LR);
if (!is_set (bottom)) automatic_side = (AutomaticSide)(automatic_side | AUTO_BOTTOM);
else if (!is_setw(height)) automatic_side = (AutomaticSide)(automatic_side | AUTO_HEIGHT);
else if (!is_set (top)) automatic_side = (AutomaticSide)(automatic_side | AUTO_TOP);
else automatic_side = (AutomaticSide)(automatic_side | AUTO_TB);
changed |= CHANGE_SIZE;
}
if (!changed) return CHANGE_NONE;
// update the automatic_side
if (automatic_side & AUTO_LEFT) left = right - width;
else if (automatic_side & AUTO_WIDTH) width = right - left;
else if (automatic_side & AUTO_RIGHT) right = left + width;
else {int lr = int(left + right); left = (lr - width) / 2; right = (lr + width) / 2; }
if (automatic_side & AUTO_TOP) top = bottom - height;
else if (automatic_side & AUTO_HEIGHT) height = bottom - top;
else if (automatic_side & AUTO_BOTTOM) bottom = top + height;
else {int tb = int(top + bottom); top = (tb - height) / 2; bottom = (tb + height) / 2; }
// adjust rotation point
if (angle != 0 && (automatic_side & (AUTO_LEFT | AUTO_TOP))) {
double s = sin(deg_to_rad(angle)), c = cos(deg_to_rad(angle));
if (automatic_side & AUTO_LEFT) { // attach right corner instead of left
left = left + width * (1 - c);
top = top + width * s;
}
if (automatic_side & AUTO_TOP) { // attach botom corner instead of top
left = left - height * s;
top = top + height * (1 - c);
}
}
if (width < 0) width = -width;
if (height < 0) height = -height;
// done
return changed;
int changed =
( left .update(ctx)
| width .update(ctx)
| right .update(ctx)
| top .update(ctx)
| height .update(ctx)
| bottom .update(ctx)
| angle .update(ctx) ) * CHANGE_SIZE
| visible.update(ctx) * CHANGE_OTHER
| mask .update(ctx) * CHANGE_MASK;
// determine automatic_side and attachment of rotation point
if (automatic_side == AUTO_UNKNOWN) {
if (!is_set (right)) automatic_side = (AutomaticSide)(automatic_side | AUTO_RIGHT);
else if (!is_setw(width)) automatic_side = (AutomaticSide)(automatic_side | AUTO_WIDTH);
else if (!is_set (left)) automatic_side = (AutomaticSide)(automatic_side | AUTO_LEFT);
else automatic_side = (AutomaticSide)(automatic_side | AUTO_LR);
if (!is_set (bottom)) automatic_side = (AutomaticSide)(automatic_side | AUTO_BOTTOM);
else if (!is_setw(height)) automatic_side = (AutomaticSide)(automatic_side | AUTO_HEIGHT);
else if (!is_set (top)) automatic_side = (AutomaticSide)(automatic_side | AUTO_TOP);
else automatic_side = (AutomaticSide)(automatic_side | AUTO_TB);
changed |= CHANGE_SIZE;
}
if (!changed) return CHANGE_NONE;
// update the automatic_side
if (automatic_side & AUTO_LEFT) left = right - width;
else if (automatic_side & AUTO_WIDTH) width = right - left;
else if (automatic_side & AUTO_RIGHT) right = left + width;
else {int lr = int(left + right); left = (lr - width) / 2; right = (lr + width) / 2; }
if (automatic_side & AUTO_TOP) top = bottom - height;
else if (automatic_side & AUTO_HEIGHT) height = bottom - top;
else if (automatic_side & AUTO_BOTTOM) bottom = top + height;
else {int tb = int(top + bottom); top = (tb - height) / 2; bottom = (tb + height) / 2; }
// adjust rotation point
if (angle != 0 && (automatic_side & (AUTO_LEFT | AUTO_TOP))) {
double s = sin(deg_to_rad(angle)), c = cos(deg_to_rad(angle));
if (automatic_side & AUTO_LEFT) { // attach right corner instead of left
left = left + width * (1 - c);
top = top + width * s;
}
if (automatic_side & AUTO_TOP) { // attach botom corner instead of top
left = left - height * s;
top = top + height * (1 - c);
}
}
if (width < 0) width = -width;
if (height < 0) height = -height;
// done
return changed;
}
bool Style::isVisible() const {
return visible
&& (width()) > 0
&& fabs(left()) < 100000
&& fabs(right()) < 100000
&& (height()) > 0
&& fabs(top()) < 100000
&& fabs(bottom()) < 100000;
return visible
&& (width()) > 0
&& fabs(left()) < 100000
&& fabs(right()) < 100000
&& (height()) > 0
&& fabs(top()) < 100000
&& fabs(bottom()) < 100000;
}
bool Style::hasSize() const {
int h = is_setw(width) + is_set(left) + is_set(right);
int v = is_setw(height) + is_set(top) + is_set(bottom);
return h >= 2 && v >= 2;
int h = is_setw(width) + is_set(left) + is_set(right);
int v = is_setw(height) + is_set(top) + is_set(bottom);
return h >= 2 && v >= 2;
}
void Style::initDependencies(Context& ctx, const Dependency& dep) const {
// left .initDependencies(ctx,dep);
// top .initDependencies(ctx,dep);
// width .initDependencies(ctx,dep);
// height .initDependencies(ctx,dep);
// visible.initDependencies(ctx,dep);
// left .initDependencies(ctx,dep);
// top .initDependencies(ctx,dep);
// width .initDependencies(ctx,dep);
// height .initDependencies(ctx,dep);
// visible.initDependencies(ctx,dep);
}
void Style::checkContentDependencies(Context& ctx, const Dependency& dep) const {
left .initDependencies(ctx,dep);
top .initDependencies(ctx,dep);
width .initDependencies(ctx,dep);
height .initDependencies(ctx,dep);
right .initDependencies(ctx,dep);
bottom .initDependencies(ctx,dep);
visible.initDependencies(ctx,dep);
mask .initDependencies(ctx,dep);
left .initDependencies(ctx,dep);
top .initDependencies(ctx,dep);
width .initDependencies(ctx,dep);
height .initDependencies(ctx,dep);
right .initDependencies(ctx,dep);
bottom .initDependencies(ctx,dep);
visible.initDependencies(ctx,dep);
mask .initDependencies(ctx,dep);
}
void Style::markDependencyMember(const String& name, const Dependency& dep) const {
// mark dependencies on content
if (dep.type == DEP_DUMMY && dep.index == false && starts_with(name, _("content "))) {
// anything that starts with "content_" is a content property
const_cast<Dependency&>(dep).index = true;
}
// mark dependencies on content
if (dep.type == DEP_DUMMY && dep.index == false && starts_with(name, _("content "))) {
// anything that starts with "content_" is a content property
const_cast<Dependency&>(dep).index = true;
}
}
void mark_dependency_member(const Style& style, const String& name, const Dependency& dep) {
style.markDependencyMember(name,dep);
style.markDependencyMember(name,dep);
}
// ----------------------------------------------------------------------------- : StyleListener
void Style::addListener(StyleListener* listener) {
listeners.push_back(listener);
listeners.push_back(listener);
}
void Style::removeListener(StyleListener* listener) {
listeners.erase(
std::remove(
listeners.begin(),
listeners.end(),
listener
),
listeners.end()
);
listeners.erase(
std::remove(
listeners.begin(),
listeners.end(),
listener
),
listeners.end()
);
}
void Style::tellListeners(int changes) {
FOR_EACH(l, listeners) l->onStyleChange(changes);
FOR_EACH(l, listeners) l->onStyleChange(changes);
}
StyleListener::StyleListener(const StyleP& style)
: styleP(style)
: styleP(style)
{
style->addListener(this);
style->addListener(this);
}
StyleListener::~StyleListener() {
styleP->removeListener(this);
styleP->removeListener(this);
}
@@ -270,34 +270,34 @@ IMPLEMENT_REFLECTION_NAMELESS(Value) {
}
bool Value::equals(const Value* that) {
return this == that;
return this == that;
}
bool Value::update(Context& ctx) {
updateAge();
updateSortValue(ctx);
return false;
updateAge();
updateSortValue(ctx);
return false;
}
void Value::updateAge() {
last_script_update.update();
last_script_update.update();
}
void Value::updateSortValue(Context& ctx) {
sort_value = fieldP->sort_script.invoke(ctx)->toString();
sort_value = fieldP->sort_script.invoke(ctx)->toString();
}
void init_object(const FieldP& field, ValueP& value) {
if (!value)
value = field->newValue(field);
if (!value)
value = field->newValue(field);
}
template <> ValueP read_new<Value>(Reader&) {
throw InternalError(_("IndexMap contains nullptr ValueP the application should have crashed already"));
throw InternalError(_("IndexMap contains nullptr ValueP the application should have crashed already"));
}
void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String& name, const Dependency& dep) {
IndexMap<FieldP,ValueP>::const_iterator it = value.find(name);
if (it != value.end()) {
(*it)->fieldP->dependent_scripts.add(dep);
}
IndexMap<FieldP,ValueP>::const_iterator it = value.find(name);
if (it != value.end()) {
(*it)->fieldP->dependent_scripts.add(dep);
}
}
+196 -196
View File
@@ -39,52 +39,52 @@ DECLARE_DYNAMIC_ARG(Value*, value_being_updated);
/// Information on how to store a value
class Field : public IntrusivePtrVirtualBase {
public:
Field();
virtual ~Field();
size_t index; ///< Used by IndexMap
String name; ///< Name of the field, for refering to it from scripts and files
String description; ///< Description, used in status bar
String icon_filename; ///< Filename for an icon (for list of fields)
bool editable; ///< Can values of this field be edited?
bool save_value; ///< Should values of this field be written to files? Can be false for script generated fields.
bool show_statistics; ///< Should this field appear as a group by choice in the statistics panel?
int position_hint; ///< Position in the statistics list
bool identifying; ///< Does this field give Card::identification()?
int card_list_column; ///< What column to use in the card list?
UInt card_list_width; ///< Width of the card list column (pixels).
bool card_list_visible;///< Is this field shown in the card list?
bool card_list_allow; ///< Is this field allowed to appear in the card list?
String card_list_name; ///< Alternate name to use in card list.
Alignment card_list_align; ///< Alignment of the card list colummn.
OptionalScript sort_script; ///< The script to use when sorting this, if not the value.
int tab_index; ///< Tab index in editor
Dependencies dependent_scripts; ///< Scripts that depend on values of this field
/// Creates a new Value corresponding to this Field
/** thisP is a smart pointer to this */
virtual ValueP newValue(const FieldP& thisP) const = 0;
/// Creates a new Style corresponding to this Field
/** thisP is a smart pointer to this */
virtual StyleP newStyle(const FieldP& thisP) const = 0;
/// Type of this field
virtual String typeName() const = 0;
/// 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;
Field();
virtual ~Field();
size_t index; ///< Used by IndexMap
String name; ///< Name of the field, for refering to it from scripts and files
String description; ///< Description, used in status bar
String icon_filename; ///< Filename for an icon (for list of fields)
bool editable; ///< Can values of this field be edited?
bool save_value; ///< Should values of this field be written to files? Can be false for script generated fields.
bool show_statistics; ///< Should this field appear as a group by choice in the statistics panel?
int position_hint; ///< Position in the statistics list
bool identifying; ///< Does this field give Card::identification()?
int card_list_column; ///< What column to use in the card list?
UInt card_list_width; ///< Width of the card list column (pixels).
bool card_list_visible;///< Is this field shown in the card list?
bool card_list_allow; ///< Is this field allowed to appear in the card list?
String card_list_name; ///< Alternate name to use in card list.
Alignment card_list_align; ///< Alignment of the card list colummn.
OptionalScript sort_script; ///< The script to use when sorting this, if not the value.
int tab_index; ///< Tab index in editor
Dependencies dependent_scripts; ///< Scripts that depend on values of this field
/// Creates a new Value corresponding to this Field
/** thisP is a smart pointer to this */
virtual ValueP newValue(const FieldP& thisP) const = 0;
/// Creates a new Style corresponding to this Field
/** thisP is a smart pointer to this */
virtual StyleP newStyle(const FieldP& thisP) const = 0;
/// Type of this field
virtual String typeName() const = 0;
/// 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;
private:
DECLARE_REFLECTION_VIRTUAL();
DECLARE_REFLECTION_VIRTUAL();
};
template <>
intrusive_ptr<Field> read_new<Field>(Reader& reader);
inline void update_index(FieldP& f, size_t index) {
f->index = index;
f->index = index;
}
inline String type_name(const Field&) {
return _TYPE_("field");
return _TYPE_("field");
}
// ----------------------------------------------------------------------------- : Style
@@ -92,88 +92,88 @@ inline String type_name(const Field&) {
/// Style information needed to display a Value in a Field.
class Style : public IntrusivePtrVirtualBase {
public:
Style(const FieldP&);
virtual ~Style();
const FieldP fieldP; ///< Field this style is for, should have the right type!
int z_index; ///< Stacking of values of this field, higher = on top
Scriptable<double> left, top; ///< Position of this field
Scriptable<double> width, height; ///< Position of this field
Scriptable<double> right, bottom; ///< Position of this field
Scriptable<Degrees> angle; ///< Rotation of the box
Scriptable<bool> visible; ///< Is this field visible?
CachedScriptableMask mask; ///< Mask image
enum AutomaticSide {
AUTO_UNKNOWN = 0x00,
AUTO_LEFT = 0x01, AUTO_WIDTH = 0x02, AUTO_RIGHT = 0x04, AUTO_LR = 0x08,
AUTO_TOP = 0x10, AUTO_HEIGHT = 0x20, AUTO_BOTTOM = 0x40, AUTO_TB = 0x80,
ATTACH_LEFT = 0x04, ATTACH_CENTER = 0x02, ATTACH_RIGHT = 0x01,
ATTACH_TOP = 0x40, ATTACH_MIDDLE = 0x20, ATTACH_BOTTOM = 0x10,
} automatic_side : 8; ///< Which of (left, width, right) and (top, height, bottom) is determined automatically?
bool content_dependent; ///< Does this style depend on content properties?
inline RealPoint getPos() const { return RealPoint(left, top ); }
inline RealSize getSize() const { return RealSize ( width, height); }
inline RealRect getExternalRect() const { return RealRect (left, top, width, height); }
inline RealRect getInternalRect() const { return RealRect(0, 0, width, height); }
/// Does this style have a non-zero size (or is it scripted)?
bool hasSize() const;
/// Is this style visible, and does it have a sane size
bool isVisible() const;
/// Get a copy of this style
virtual StyleP clone() const = 0;
/// Make a viewer object for values using this style
/** thisP is a smart pointer to this */
virtual ValueViewerP makeViewer(DataViewer& parent, const StyleP& thisP) = 0;
/// Make an editor object for values using this style
/** thisP is a smart pointer to this */
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP) = 0;
/// Update scripted values of this style, return nonzero if anything has changed.
/** The caller should tellListeners()
* The result is a combination of StyleChange flags
*/
virtual int update(Context&);
/// Add the given dependency to the dependent_scripts list for the variables this style depends on
/** Only use for things that need invalidate() */
virtual void initDependencies(Context&, const Dependency&) const;
/// Check if the style depends on content properties
/** If there is such a dependency, set dep.index to true.
* This is done by mark_dependency_member for those properies */
virtual void checkContentDependencies(Context&, const Dependency&) const;
/// Dependencies on properies?
/** In particular, if dep == DEP_DUMMY and name is a content property, set dep.index=true */
virtual void markDependencyMember(const String& name, const Dependency&) const;
/// Invalidate scripted images for this style
virtual void invalidate() {}
/// Add a StyleListener
void addListener(StyleListener*);
/// Remove a StyleListener
void removeListener(StyleListener*);
/// Tell the StyleListeners that this style has changed
/** change_info is a subset of StyleChange flags */
void tellListeners(int changes);
Style(const FieldP&);
virtual ~Style();
const FieldP fieldP; ///< Field this style is for, should have the right type!
int z_index; ///< Stacking of values of this field, higher = on top
Scriptable<double> left, top; ///< Position of this field
Scriptable<double> width, height; ///< Position of this field
Scriptable<double> right, bottom; ///< Position of this field
Scriptable<Degrees> angle; ///< Rotation of the box
Scriptable<bool> visible; ///< Is this field visible?
CachedScriptableMask mask; ///< Mask image
enum AutomaticSide {
AUTO_UNKNOWN = 0x00,
AUTO_LEFT = 0x01, AUTO_WIDTH = 0x02, AUTO_RIGHT = 0x04, AUTO_LR = 0x08,
AUTO_TOP = 0x10, AUTO_HEIGHT = 0x20, AUTO_BOTTOM = 0x40, AUTO_TB = 0x80,
ATTACH_LEFT = 0x04, ATTACH_CENTER = 0x02, ATTACH_RIGHT = 0x01,
ATTACH_TOP = 0x40, ATTACH_MIDDLE = 0x20, ATTACH_BOTTOM = 0x10,
} automatic_side : 8; ///< Which of (left, width, right) and (top, height, bottom) is determined automatically?
bool content_dependent; ///< Does this style depend on content properties?
inline RealPoint getPos() const { return RealPoint(left, top ); }
inline RealSize getSize() const { return RealSize ( width, height); }
inline RealRect getExternalRect() const { return RealRect (left, top, width, height); }
inline RealRect getInternalRect() const { return RealRect(0, 0, width, height); }
/// Does this style have a non-zero size (or is it scripted)?
bool hasSize() const;
/// Is this style visible, and does it have a sane size
bool isVisible() const;
/// Get a copy of this style
virtual StyleP clone() const = 0;
/// Make a viewer object for values using this style
/** thisP is a smart pointer to this */
virtual ValueViewerP makeViewer(DataViewer& parent, const StyleP& thisP) = 0;
/// Make an editor object for values using this style
/** thisP is a smart pointer to this */
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP) = 0;
/// Update scripted values of this style, return nonzero if anything has changed.
/** The caller should tellListeners()
* The result is a combination of StyleChange flags
*/
virtual int update(Context&);
/// Add the given dependency to the dependent_scripts list for the variables this style depends on
/** Only use for things that need invalidate() */
virtual void initDependencies(Context&, const Dependency&) const;
/// Check if the style depends on content properties
/** If there is such a dependency, set dep.index to true.
* This is done by mark_dependency_member for those properies */
virtual void checkContentDependencies(Context&, const Dependency&) const;
/// Dependencies on properies?
/** In particular, if dep == DEP_DUMMY and name is a content property, set dep.index=true */
virtual void markDependencyMember(const String& name, const Dependency&) const;
/// Invalidate scripted images for this style
virtual void invalidate() {}
/// Add a StyleListener
void addListener(StyleListener*);
/// Remove a StyleListener
void removeListener(StyleListener*);
/// Tell the StyleListeners that this style has changed
/** change_info is a subset of StyleChange flags */
void tellListeners(int changes);
private:
DECLARE_REFLECTION_VIRTUAL();
/// Things that are listening to changes in this style
vector<StyleListener*> listeners;
DECLARE_REFLECTION_VIRTUAL();
/// Things that are listening to changes in this style
vector<StyleListener*> listeners;
};
/// What changed in a style update?
enum StyleChange
{ CHANGE_NONE = 0x00 // nothing changed
, CHANGE_OTHER = 0x01 // some other change (note: result of casting from bool)
, CHANGE_SIZE = 0x02 // size/angle changed
, CHANGE_DEFAULT = 0x04 // only the 'default' state is affected
, CHANGE_MASK = 0x08 // a mask image changed, must be reloaded
, CHANGE_ALREADY_PREPARED = 0x80 // hint that the change was the result of a content property change, viewers are already prepared
{ CHANGE_NONE = 0x00 // nothing changed
, CHANGE_OTHER = 0x01 // some other change (note: result of casting from bool)
, CHANGE_SIZE = 0x02 // size/angle changed
, CHANGE_DEFAULT = 0x04 // only the 'default' state is affected
, CHANGE_MASK = 0x08 // a mask image changed, must be reloaded
, CHANGE_ALREADY_PREPARED = 0x80 // hint that the change was the result of a content property change, viewers are already prepared
};
void init_object(const FieldP&, StyleP&);
@@ -182,7 +182,7 @@ inline const String& get_key_name(const StyleP& s) { return s->fieldP->name; }
template <> StyleP read_new<Style>(Reader&);
inline String type_name(const Style&) {
return _TYPE_("style");
return _TYPE_("style");
}
void mark_dependency_member(const Style& style, const String& name, const Dependency& dep);
@@ -192,14 +192,14 @@ void mark_dependency_member(const Style& style, const String& name, const Depend
/// An object that can respond when a style changes;
class StyleListener : public IntrusivePtrVirtualBase {
public:
StyleListener(const StyleP& style);
virtual ~StyleListener();
/// Called when a (scripted) property of the viewed style has changed
/** changes is a combination of StyleChange flags */
virtual void onStyleChange(int changes) {}
StyleListener(const StyleP& style);
virtual ~StyleListener();
/// Called when a (scripted) property of the viewed style has changed
/** changes is a combination of StyleChange flags */
virtual void onStyleChange(int changes) {}
protected:
const StyleP styleP; ///< The style we are listening to
const StyleP styleP; ///< The style we are listening to
};
// ----------------------------------------------------------------------------- : Value
@@ -207,43 +207,43 @@ class StyleListener : public IntrusivePtrVirtualBase {
/// A specific value 'in' a Field.
class Value : public IntrusivePtrVirtualBase {
public:
inline Value(const FieldP& field) : fieldP(field) {}
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)
String sort_value; ///< How this should be sorted.
/// Get a copy of this value
virtual ValueP clone() const = 0;
/// Convert this value to a string for use in tables
virtual String toString() const = 0;
/// Apply scripts to this value, return true if the value has changed
virtual bool update(Context& ctx);
/// This value has been updated by an action
/** Does nothing for most Values, only FakeValues can update underlying data */
virtual void onAction(Action& a, bool undone) {}
/// Is this value the same as some other value (for the same field&card)
/** Has behaviour other than == for FakeTextValue.
* In that case returns true if this and that editing the same undelying value.
* If so, this value is updated to reflect the (possibly changed) underlying value.
*/
virtual bool equals(const Value* that);
inline Value(const FieldP& field) : fieldP(field) {}
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)
String sort_value; ///< How this should be sorted.
/// Get a copy of this value
virtual ValueP clone() const = 0;
/// Convert this value to a string for use in tables
virtual String toString() const = 0;
/// Apply scripts to this value, return true if the value has changed
virtual bool update(Context& ctx);
/// This value has been updated by an action
/** Does nothing for most Values, only FakeValues can update underlying data */
virtual void onAction(Action& a, bool undone) {}
/// Is this value the same as some other value (for the same field&card)
/** Has behaviour other than == for FakeTextValue.
* In that case returns true if this and that editing the same undelying value.
* If so, this value is updated to reflect the (possibly changed) underlying value.
*/
virtual bool equals(const Value* that);
/// Get the key to use for sorting this value
inline String getSortKey() const {
return fieldP->sort_script ? sort_value : toString();
}
/// Get the key to use for sorting this value
inline String getSortKey() const {
return fieldP->sort_script ? sort_value : toString();
}
protected:
/// update() split into two functions;.
/** Derived classes should put their stuff in between if they need the age in scripts */
void updateAge();
void updateSortValue(Context& ctx);
/// update() split into two functions;.
/** Derived classes should put their stuff in between if they need the age in scripts */
void updateAge();
void updateSortValue(Context& ctx);
private:
DECLARE_REFLECTION_VIRTUAL();
DECLARE_REFLECTION_VIRTUAL();
};
void init_object(const FieldP&, ValueP&);
@@ -252,56 +252,56 @@ inline const String& get_key_name(const ValueP& v) { return v->fieldP->name; }
template <> ValueP read_new<Value>(Reader&);
inline String type_name(const Value&) {
return _TYPE_("value");
return _TYPE_("value");
}
// ----------------------------------------------------------------------------- : Utilities
#define DECLARE_FIELD_TYPE(Type) \
DECLARE_REFLECTION(); public: \
virtual ValueP newValue(const FieldP& thisP) const; \
virtual StyleP newStyle(const FieldP& thisP) const; \
virtual String typeName() const
#define DECLARE_FIELD_TYPE(Type) \
DECLARE_REFLECTION(); public: \
virtual ValueP newValue(const FieldP& thisP) const; \
virtual StyleP newStyle(const FieldP& thisP) const; \
virtual String typeName() const
// implement newStyle and newValue
#define IMPLEMENT_FIELD_TYPE(Type, NAME) \
StyleP Type ## Field::newStyle(const FieldP& thisP) const { \
assert(thisP.get() == this); \
return intrusive(new Type ## Style(static_pointer_cast<Type ## Field>(thisP))); \
} \
ValueP Type ## Field::newValue(const FieldP& thisP) const { \
assert(thisP.get() == this); \
return intrusive(new Type ## Value(static_pointer_cast<Type ## Field>(thisP))); \
} \
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); \
}
#define IMPLEMENT_FIELD_TYPE(Type, NAME) \
StyleP Type ## Field::newStyle(const FieldP& thisP) const { \
assert(thisP.get() == this); \
return intrusive(new Type ## Style(static_pointer_cast<Type ## Field>(thisP))); \
} \
ValueP Type ## Field::newValue(const FieldP& thisP) const { \
assert(thisP.get() == this); \
return intrusive(new Type ## Value(static_pointer_cast<Type ## Field>(thisP))); \
} \
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); \
}
#define DECLARE_STYLE_TYPE(Type) \
DECLARE_REFLECTION(); public: \
DECLARE_HAS_FIELD(Type) \
virtual StyleP clone() const; \
virtual ValueViewerP makeViewer(DataViewer& parent, const StyleP& thisP); \
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP)
#define DECLARE_STYLE_TYPE(Type) \
DECLARE_REFLECTION(); public: \
DECLARE_HAS_FIELD(Type) \
virtual StyleP clone() const; \
virtual ValueViewerP makeViewer(DataViewer& parent, const StyleP& thisP); \
virtual ValueViewerP makeEditor(DataEditor& parent, const StyleP& thisP)
#define DECLARE_VALUE_TYPE(Type,ValueType_) \
DECLARE_REFLECTION(); public: \
DECLARE_HAS_FIELD(Type) \
virtual ValueP clone() const; \
virtual String toString() const; \
typedef ValueType_ ValueType
#define DECLARE_VALUE_TYPE(Type,ValueType_) \
DECLARE_REFLECTION(); public: \
DECLARE_HAS_FIELD(Type) \
virtual ValueP clone() const; \
virtual String toString() const; \
typedef ValueType_ ValueType
// implement field() which returns a field with the right (derived) type
#define DECLARE_HAS_FIELD(Type) \
inline Type ## Field& field() const { \
return *static_cast<Type ## Field*>(fieldP.get()); \
}
#define DECLARE_HAS_FIELD(Type) \
inline Type ## Field& field() const { \
return *static_cast<Type ## Field*>(fieldP.get()); \
}
void mark_dependency_member(const IndexMap<FieldP,ValueP>& value, const String& name, const Dependency& dep);
+15 -15
View File
@@ -12,38 +12,38 @@
// ----------------------------------------------------------------------------- : BooleanField
BooleanField::BooleanField() {
choices->choices.push_back(intrusive(new Choice(_("yes"))));
choices->choices.push_back(intrusive(new Choice(_("no"))));
choices->initIds();
choices->choices.push_back(intrusive(new Choice(_("yes"))));
choices->choices.push_back(intrusive(new Choice(_("no"))));
choices->initIds();
}
IMPLEMENT_FIELD_TYPE(Boolean, "boolean");
IMPLEMENT_REFLECTION(BooleanField) {
REFLECT_BASE(Field); // NOTE: don't reflect as a ChoiceField
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(initial);
REFLECT_BASE(Field); // NOTE: don't reflect as a ChoiceField
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(initial);
}
// ----------------------------------------------------------------------------- : BooleanStyle
BooleanStyle::BooleanStyle(const ChoiceFieldP& field)
: ChoiceStyle(field)
: ChoiceStyle(field)
{
render_style = RENDER_BOTH;
//choice_images[_("yes")] = ScriptableImage(_("buildin_image(\"bool_yes\")"));
//choice_images[_("no")] = ScriptableImage(_("buildin_image(\"bool_no\")"));
choice_images[_("yes")] = ScriptableImage(intrusive(new BuiltInImage(_("bool_yes"))));
choice_images[_("no")] = ScriptableImage(intrusive(new BuiltInImage(_("bool_no"))));
render_style = RENDER_BOTH;
//choice_images[_("yes")] = ScriptableImage(_("buildin_image(\"bool_yes\")"));
//choice_images[_("no")] = ScriptableImage(_("buildin_image(\"bool_no\")"));
choice_images[_("yes")] = ScriptableImage(intrusive(new BuiltInImage(_("bool_yes"))));
choice_images[_("no")] = ScriptableImage(intrusive(new BuiltInImage(_("bool_no"))));
}
IMPLEMENT_REFLECTION(BooleanStyle) {
REFLECT_BASE(ChoiceStyle);
REFLECT_BASE(ChoiceStyle);
}
// ----------------------------------------------------------------------------- : BooleanValue
IMPLEMENT_REFLECTION_NAMELESS(BooleanValue) {
REFLECT_BASE(ChoiceValue);
REFLECT_BASE(ChoiceValue);
}
+18 -18
View File
@@ -21,10 +21,10 @@ DECLARE_POINTER_TYPE(BooleanValue);
/// A field whos value is either true or false
class BooleanField : public ChoiceField {
public:
BooleanField();
DECLARE_FIELD_TYPE(Boolean);
// no extra data
BooleanField();
DECLARE_FIELD_TYPE(Boolean);
// no extra data
};
// ----------------------------------------------------------------------------- : BooleanStyle
@@ -32,14 +32,14 @@ class BooleanField : public ChoiceField {
/// The Style for a BooleanField
class BooleanStyle : public ChoiceStyle {
public:
BooleanStyle(const ChoiceFieldP& field);
DECLARE_HAS_FIELD(Boolean); // not DECLARE_STYLE_TYPE, because we use a normal ChoiceValueViewer/Editor
virtual StyleP clone() const;
// no extra data
BooleanStyle(const ChoiceFieldP& field);
DECLARE_HAS_FIELD(Boolean); // not DECLARE_STYLE_TYPE, because we use a normal ChoiceValueViewer/Editor
virtual StyleP clone() const;
// no extra data
private:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : BooleanValue
@@ -47,14 +47,14 @@ class BooleanStyle : public ChoiceStyle {
/// 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
inline BooleanValue(const ChoiceFieldP& field) : ChoiceValue(field) {}
DECLARE_HAS_FIELD(Boolean);
virtual ValueP clone() const;
// no extra data
private:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF
+194 -194
View File
@@ -18,292 +18,292 @@ DECLARE_TYPEOF(map<String COMMA ScriptableImage>);
// ----------------------------------------------------------------------------- : ChoiceField
ChoiceField::ChoiceField()
: choices((Choice*)new Choice)
, default_name(_("Default"))
: choices((Choice*)new Choice)
, default_name(_("Default"))
{}
IMPLEMENT_FIELD_TYPE(Choice, "choice");
void ChoiceField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(ChoiceField) {
REFLECT_BASE(Field);
REFLECT_N("choices", choices->choices);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(initial);
REFLECT(default_name);
REFLECT_IF_READING {
choices->initIds();
}
REFLECT(choice_colors);
REFLECT(choice_colors_cardlist);
REFLECT_BASE(Field);
REFLECT_N("choices", choices->choices);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(initial);
REFLECT(default_name);
REFLECT_IF_READING {
choices->initIds();
}
REFLECT(choice_colors);
REFLECT(choice_colors_cardlist);
}
// ----------------------------------------------------------------------------- : ChoiceField::Choice
ChoiceField::Choice::Choice()
: line_below(false), enabled(true), type(CHOICE_TYPE_CHECK)
, first_id(0)
: line_below(false), enabled(true), type(CHOICE_TYPE_CHECK)
, first_id(0)
{}
ChoiceField::Choice::Choice(const String& name)
: name(name)
, line_below(false), enabled(true), type(CHOICE_TYPE_CHECK)
, first_id(0)
: name(name)
, line_below(false), enabled(true), type(CHOICE_TYPE_CHECK)
, first_id(0)
{}
bool ChoiceField::Choice::isGroup() const {
return !choices.empty();
return !choices.empty();
}
bool ChoiceField::Choice::hasDefault() const {
return !isGroup() || !default_name.empty();
return !isGroup() || !default_name.empty();
}
int ChoiceField::Choice::initIds() {
int id = first_id + (hasDefault() ? 1 : 0);
FOR_EACH(c, choices) {
c->first_id = id;
id = c->initIds();
}
return id;
int id = first_id + (hasDefault() ? 1 : 0);
FOR_EACH(c, choices) {
c->first_id = id;
id = c->initIds();
}
return id;
}
int ChoiceField::Choice::choiceCount() const {
return lastId() - first_id;
return lastId() - first_id;
}
int ChoiceField::Choice::lastId() const {
if (isGroup()) {
// last id of last choice
return choices.back()->lastId();
} else {
return first_id + 1;
}
if (isGroup()) {
// last id of last choice
return choices.back()->lastId();
} else {
return first_id + 1;
}
}
int ChoiceField::Choice::choiceId(const String& search_name) const {
if (hasDefault() && search_name == name) {
return first_id;
} else if (name.empty()) { // no name for this group, forward to all children
FOR_EACH_CONST(c, choices) {
int sub_id = c->choiceId(search_name);
if (sub_id != -1) return sub_id;
}
} else if (isGroup() && starts_with(search_name, name + _(" "))) {
String sub_name = search_name.substr(name.size() + 1);
FOR_EACH_CONST(c, choices) {
int sub_id = c->choiceId(sub_name);
if (sub_id != -1) return sub_id;
}
}
return -1;
if (hasDefault() && search_name == name) {
return first_id;
} else if (name.empty()) { // no name for this group, forward to all children
FOR_EACH_CONST(c, choices) {
int sub_id = c->choiceId(search_name);
if (sub_id != -1) return sub_id;
}
} else if (isGroup() && starts_with(search_name, name + _(" "))) {
String sub_name = search_name.substr(name.size() + 1);
FOR_EACH_CONST(c, choices) {
int sub_id = c->choiceId(sub_name);
if (sub_id != -1) return sub_id;
}
}
return -1;
}
String ChoiceField::Choice::choiceName(int id) const {
if (hasDefault() && id == first_id) {
return name;
} else {
FOR_EACH_CONST_REVERSE(c, choices) { // take the last one that still contains id
if (id >= c->first_id) {
if (name.empty()) {
return c->choiceName(id);
} else {
return name + _(" ") + c->choiceName(id);
}
}
}
}
return _("");
if (hasDefault() && id == first_id) {
return name;
} else {
FOR_EACH_CONST_REVERSE(c, choices) { // take the last one that still contains id
if (id >= c->first_id) {
if (name.empty()) {
return c->choiceName(id);
} else {
return name + _(" ") + c->choiceName(id);
}
}
}
}
return _("");
}
String ChoiceField::Choice::choiceNameNice(int id) const {
if (!isGroup() && id == first_id) {
return name;
} else if (hasDefault() && id == first_id) {
return default_name;
} else {
FOR_EACH_CONST_REVERSE(c, choices) {
if (id == c->first_id) {
return c->name; // we don't want "<group> default"
} else if (id > c->first_id) {
return c->choiceNameNice(id);
}
}
}
return _("");
if (!isGroup() && id == first_id) {
return name;
} else if (hasDefault() && id == first_id) {
return default_name;
} else {
FOR_EACH_CONST_REVERSE(c, choices) {
if (id == c->first_id) {
return c->name; // we don't want "<group> default"
} else if (id > c->first_id) {
return c->choiceNameNice(id);
}
}
}
return _("");
}
IMPLEMENT_REFLECTION_ENUM(ChoiceChoiceType) {
VALUE_N("check", CHOICE_TYPE_CHECK);
VALUE_N("radio", CHOICE_TYPE_RADIO);
VALUE_N("check", CHOICE_TYPE_CHECK);
VALUE_N("radio", CHOICE_TYPE_RADIO);
}
IMPLEMENT_REFLECTION(ChoiceField::Choice) {
if (isGroup() || line_below || enabled.isScripted() || tag.isComplex()) {
// complex values are groups
REFLECT(name);
REFLECT_N("group_choice", default_name);
REFLECT(choices);
REFLECT(line_below);
REFLECT(enabled);
REFLECT(type);
} else {
REFLECT_NAMELESS(name);
}
if (isGroup() || line_below || enabled.isScripted() || tag.isComplex()) {
// complex values are groups
REFLECT(name);
REFLECT_N("group_choice", default_name);
REFLECT(choices);
REFLECT(line_below);
REFLECT(enabled);
REFLECT(type);
} else {
REFLECT_NAMELESS(name);
}
}
// ----------------------------------------------------------------------------- : ChoiceStyle
ChoiceStyle::ChoiceStyle(const ChoiceFieldP& field)
: Style(field)
, popup_style(POPUP_DROPDOWN)
, render_style(RENDER_TEXT)
, choice_images_initialized(false)
, combine(COMBINE_NORMAL)
, alignment(ALIGN_STRETCH)
, thumbnails(nullptr)
, content_width(0.0), content_height(0.0)
: Style(field)
, popup_style(POPUP_DROPDOWN)
, render_style(RENDER_TEXT)
, choice_images_initialized(false)
, combine(COMBINE_NORMAL)
, alignment(ALIGN_STRETCH)
, thumbnails(nullptr)
, content_width(0.0), content_height(0.0)
{}
ChoiceStyle::~ChoiceStyle() {
delete thumbnails;
delete thumbnails;
}
void ChoiceStyle::initImage() {
if (image.isSet() || choice_images.empty()) return;
// for, for example:
// choice images:
// a: {uvw}
// b: {xyz}
// generate the script:
// [a: {uvw}, b: {xyz}][input]() or else nil
// or in bytecode
// PUSH_CONST [a: {uvw}, b: {xyz}]
// GET_VAR input
// MEMBER
// CALL 0
// PUSH_CONST nil
// OR_ELSE
ScriptCustomCollectionP lookup(new ScriptCustomCollection());
FOR_EACH(ci, choice_images) {
lookup->key_value[ci.first] = ci.second.getValidScriptP();
}
Script& script = image.getMutableScript();
script.addInstruction(I_PUSH_CONST, lookup);
script.addInstruction(I_GET_VAR, SCRIPT_VAR_input);
script.addInstruction(I_BINARY, I_MEMBER);
script.addInstruction(I_CALL, 0);
script.addInstruction(I_PUSH_CONST, script_nil);
script.addInstruction(I_BINARY, I_OR_ELSE);
if (image.isSet() || choice_images.empty()) return;
// for, for example:
// choice images:
// a: {uvw}
// b: {xyz}
// generate the script:
// [a: {uvw}, b: {xyz}][input]() or else nil
// or in bytecode
// PUSH_CONST [a: {uvw}, b: {xyz}]
// GET_VAR input
// MEMBER
// CALL 0
// PUSH_CONST nil
// OR_ELSE
ScriptCustomCollectionP lookup(new ScriptCustomCollection());
FOR_EACH(ci, choice_images) {
lookup->key_value[ci.first] = ci.second.getValidScriptP();
}
Script& script = image.getMutableScript();
script.addInstruction(I_PUSH_CONST, lookup);
script.addInstruction(I_GET_VAR, SCRIPT_VAR_input);
script.addInstruction(I_BINARY, I_MEMBER);
script.addInstruction(I_CALL, 0);
script.addInstruction(I_PUSH_CONST, script_nil);
script.addInstruction(I_BINARY, I_OR_ELSE);
}
int ChoiceStyle::update(Context& ctx) {
// Don't update the choice images, leave that to invalidate()
int change = Style::update(ctx)
| font .update(ctx) * CHANGE_OTHER;
if (!choice_images_initialized) {
// we only want to do this once because it is rather slow, other updates are handled by dependencies
choice_images_initialized = true;
FOR_EACH(ci, choice_images) {
if (ci.second.update(ctx)) {
change |= CHANGE_OTHER;
// TODO : remove this thumbnail
}
}
}
return change;
// Don't update the choice images, leave that to invalidate()
int change = Style::update(ctx)
| font .update(ctx) * CHANGE_OTHER;
if (!choice_images_initialized) {
// we only want to do this once because it is rather slow, other updates are handled by dependencies
choice_images_initialized = true;
FOR_EACH(ci, choice_images) {
if (ci.second.update(ctx)) {
change |= CHANGE_OTHER;
// TODO : remove this thumbnail
}
}
}
return change;
}
void ChoiceStyle::initDependencies(Context& ctx, const Dependency& dep) const {
Style::initDependencies(ctx, dep);
FOR_EACH_CONST(ci, choice_images) {
ci.second.initDependencies(ctx, dep);
}
Style::initDependencies(ctx, dep);
FOR_EACH_CONST(ci, choice_images) {
ci.second.initDependencies(ctx, dep);
}
}
void ChoiceStyle::checkContentDependencies(Context& ctx, const Dependency& dep) const {
Style::checkContentDependencies(ctx, dep);
image.initDependencies(ctx, dep);
FOR_EACH_CONST(ci, choice_images) {
ci.second.initDependencies(ctx, dep);
}
Style::checkContentDependencies(ctx, dep);
image.initDependencies(ctx, dep);
FOR_EACH_CONST(ci, choice_images) {
ci.second.initDependencies(ctx, dep);
}
}
void ChoiceStyle::invalidate() {
// TODO : this is also done in update(), once should be enough
// Update choice images and thumbnails
int end = field().choices->lastId();
thumbnails_status.resize(end, THUMB_NOT_MADE);
for (int i = 0 ; i < end ; ++i) {
if (thumbnails_status[i] == THUMB_OK) thumbnails_status[i] = THUMB_CHANGED;
}
tellListeners(CHANGE_OTHER);
// TODO : this is also done in update(), once should be enough
// Update choice images and thumbnails
int end = field().choices->lastId();
thumbnails_status.resize(end, THUMB_NOT_MADE);
for (int i = 0 ; i < end ; ++i) {
if (thumbnails_status[i] == THUMB_OK) thumbnails_status[i] = THUMB_CHANGED;
}
tellListeners(CHANGE_OTHER);
}
IMPLEMENT_REFLECTION_ENUM(ChoicePopupStyle) {
VALUE_N("dropdown", POPUP_DROPDOWN);
VALUE_N("menu", POPUP_MENU);
VALUE_N("in place", POPUP_DROPDOWN_IN_PLACE);
VALUE_N("dropdown", POPUP_DROPDOWN);
VALUE_N("menu", POPUP_MENU);
VALUE_N("in place", POPUP_DROPDOWN_IN_PLACE);
}
IMPLEMENT_REFLECTION_ENUM(ChoiceRenderStyle) {
VALUE_N("text", RENDER_TEXT);
VALUE_N("image", RENDER_IMAGE);
VALUE_N("both", RENDER_BOTH);
VALUE_N("hidden", RENDER_HIDDEN);
VALUE_N("image hidden", RENDER_HIDDEN_IMAGE);
VALUE_N("checklist", RENDER_TEXT_CHECKLIST);
VALUE_N("image checklist", RENDER_IMAGE_CHECKLIST);
VALUE_N("both checklist", RENDER_BOTH_CHECKLIST);
VALUE_N("text list", RENDER_TEXT_LIST);
VALUE_N("image list", RENDER_IMAGE_LIST);
VALUE_N("both list", RENDER_BOTH_LIST);
VALUE_N("text", RENDER_TEXT);
VALUE_N("image", RENDER_IMAGE);
VALUE_N("both", RENDER_BOTH);
VALUE_N("hidden", RENDER_HIDDEN);
VALUE_N("image hidden", RENDER_HIDDEN_IMAGE);
VALUE_N("checklist", RENDER_TEXT_CHECKLIST);
VALUE_N("image checklist", RENDER_IMAGE_CHECKLIST);
VALUE_N("both checklist", RENDER_BOTH_CHECKLIST);
VALUE_N("text list", RENDER_TEXT_LIST);
VALUE_N("image list", RENDER_IMAGE_LIST);
VALUE_N("both list", RENDER_BOTH_LIST);
}
template <typename T> void reflect_content(T& tag, const ChoiceStyle& cs) {}
template <> void reflect_content(GetMember& tag, const ChoiceStyle& cs) {
REFLECT_N("content_width", cs.content_width);
REFLECT_N("content_height", cs.content_height);
REFLECT_N("content_width", cs.content_width);
REFLECT_N("content_height", cs.content_height);
}
IMPLEMENT_REFLECTION(ChoiceStyle) {
REFLECT_ALIAS(300, "card list colors", "colors card list");
REFLECT_BASE(Style);
REFLECT(popup_style);
REFLECT(render_style);
REFLECT(combine);
REFLECT(alignment);
REFLECT(font);
REFLECT(image);
REFLECT(choice_images);
reflect_content(tag, *this);
REFLECT_ALIAS(300, "card list colors", "colors card list");
REFLECT_BASE(Style);
REFLECT(popup_style);
REFLECT(render_style);
REFLECT(combine);
REFLECT(alignment);
REFLECT(font);
REFLECT(image);
REFLECT(choice_images);
reflect_content(tag, *this);
}
// ----------------------------------------------------------------------------- : ChoiceValue
ChoiceValue::ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice)
: Value(field)
, value( !field->initial.empty() ? field->initial
: initial_first_choice ? field->choices->choiceName(0)
: _("")
, true)
: Value(field)
, value( !field->initial.empty() ? field->initial
: initial_first_choice ? field->choices->choiceName(0)
: _("")
, true)
{}
String ChoiceValue::toString() const {
return value();
return value();
}
bool ChoiceValue::update(Context& ctx) {
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
Value::update(ctx);
return change;
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
Value::update(ctx);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(ChoiceValue) {
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(value);
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(value);
}
INSTANTIATE_REFLECTION_NAMELESS(ChoiceValue)
+117 -117
View File
@@ -27,139 +27,139 @@ DECLARE_POINTER_TYPE(ChoiceValue);
/// A field that contains a list of choices
class ChoiceField : public Field {
public:
ChoiceField();
DECLARE_FIELD_TYPE(Choice);
class Choice;
typedef intrusive_ptr<Choice> ChoiceP;
ChoiceP choices; ///< A choice group of possible choices
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
String initial; ///< Initial choice of a new value, or ""
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
virtual void initDependencies(Context&, const Dependency&) const;
ChoiceField();
DECLARE_FIELD_TYPE(Choice);
class Choice;
typedef intrusive_ptr<Choice> ChoiceP;
ChoiceP choices; ///< A choice group of possible choices
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
String initial; ///< Initial choice of a new value, or ""
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
virtual void initDependencies(Context&, const Dependency&) const;
};
enum ChoiceChoiceType {
CHOICE_TYPE_CHECK,
CHOICE_TYPE_RADIO
CHOICE_TYPE_CHECK,
CHOICE_TYPE_RADIO
};
/// An item that can be chosen for this field
class ChoiceField::Choice : public IntrusivePtrBase<ChoiceField::Choice> {
public:
Choice();
Choice(const String& name);
String name; ///< Name/value of the item
String default_name; ///< A default item, if this is a group and default_name.empty() there is no default
vector<ChoiceP> choices; ///< Choices and sub groups in this group
bool line_below; ///< Show a line after this item?
Scriptable<bool> enabled; ///< Is this item enabled?
ChoiceChoiceType type; ///< How should this item be shown, only for multiple choice fields
/// First item-id in this group (can be the default item)
/** Item-ids are consecutive integers, a group uses all ids [first_id..lastId()).
* The top level group has first_id 0.
*/
int first_id;
/// Is this a group?
bool isGroup() const;
/// Can this Choice itself be chosen?
/** For a single choice this is always true, for a group only if it has a default choice */
bool hasDefault() const;
/// Initialize the first_id of children
/** @pre first_id is set
* Returns lastId()
*/
int initIds();
/// Number of choices in this group (and subgroups), 1 if it is not a group
/** The default choice also counts */
int choiceCount() const;
/// item-id just beyond the end of this group
int lastId() const;
/// item-id of a choice, given the internal name
/** If the id is not in this group, returns -1 */
int choiceId(const String& name) const;
/// Internal name of a choice
/** The internal name is formed by concatenating the names of all parents, separated by spaces.
* Returns "" if id is not in this group
*/
String choiceName(int id) const;
/// Formated name of a choice.
/** Intended for use in menu structures, so it doesn't include the group name for children.
* Returns "" if id is not in this group.
*/
String choiceNameNice(int id) const;
DECLARE_REFLECTION();
Choice();
Choice(const String& name);
String name; ///< Name/value of the item
String default_name; ///< A default item, if this is a group and default_name.empty() there is no default
vector<ChoiceP> choices; ///< Choices and sub groups in this group
bool line_below; ///< Show a line after this item?
Scriptable<bool> enabled; ///< Is this item enabled?
ChoiceChoiceType type; ///< How should this item be shown, only for multiple choice fields
/// First item-id in this group (can be the default item)
/** Item-ids are consecutive integers, a group uses all ids [first_id..lastId()).
* The top level group has first_id 0.
*/
int first_id;
/// Is this a group?
bool isGroup() const;
/// Can this Choice itself be chosen?
/** For a single choice this is always true, for a group only if it has a default choice */
bool hasDefault() const;
/// Initialize the first_id of children
/** @pre first_id is set
* Returns lastId()
*/
int initIds();
/// Number of choices in this group (and subgroups), 1 if it is not a group
/** The default choice also counts */
int choiceCount() const;
/// item-id just beyond the end of this group
int lastId() const;
/// item-id of a choice, given the internal name
/** If the id is not in this group, returns -1 */
int choiceId(const String& name) const;
/// Internal name of a choice
/** The internal name is formed by concatenating the names of all parents, separated by spaces.
* Returns "" if id is not in this group
*/
String choiceName(int id) const;
/// Formated name of a choice.
/** Intended for use in menu structures, so it doesn't include the group name for children.
* Returns "" if id is not in this group.
*/
String choiceNameNice(int id) const;
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : ChoiceStyle
// How should the menu for a choice look?
enum ChoicePopupStyle
{ POPUP_MENU
, POPUP_DROPDOWN
, POPUP_DROPDOWN_IN_PLACE
{ POPUP_MENU
, POPUP_DROPDOWN
, POPUP_DROPDOWN_IN_PLACE
};
// How should a choice value be rendered?
enum ChoiceRenderStyle
{ RENDER_TEXT = 0x01 // render the name as text
, RENDER_IMAGE = 0x10 // render an image
, RENDER_HIDDEN = 0x20 // don't render anything, only have a menu
, RENDER_CHECKLIST = 0x100 // render as a checklist, intended for multiple choice
, RENDER_LIST = 0x200 // render as a list of images/text, intended for multiple choice
, RENDER_BOTH = RENDER_TEXT | RENDER_IMAGE
, RENDER_HIDDEN_IMAGE = RENDER_HIDDEN | RENDER_IMAGE
, RENDER_TEXT_CHECKLIST = RENDER_CHECKLIST | RENDER_TEXT
, RENDER_IMAGE_CHECKLIST = RENDER_CHECKLIST | RENDER_IMAGE
, RENDER_BOTH_CHECKLIST = RENDER_CHECKLIST | RENDER_BOTH
, RENDER_TEXT_LIST = RENDER_LIST | RENDER_TEXT
, RENDER_IMAGE_LIST = RENDER_LIST | RENDER_IMAGE
, RENDER_BOTH_LIST = RENDER_LIST | RENDER_BOTH
{ RENDER_TEXT = 0x01 // render the name as text
, RENDER_IMAGE = 0x10 // render an image
, RENDER_HIDDEN = 0x20 // don't render anything, only have a menu
, RENDER_CHECKLIST = 0x100 // render as a checklist, intended for multiple choice
, RENDER_LIST = 0x200 // render as a list of images/text, intended for multiple choice
, RENDER_BOTH = RENDER_TEXT | RENDER_IMAGE
, RENDER_HIDDEN_IMAGE = RENDER_HIDDEN | RENDER_IMAGE
, RENDER_TEXT_CHECKLIST = RENDER_CHECKLIST | RENDER_TEXT
, RENDER_IMAGE_CHECKLIST = RENDER_CHECKLIST | RENDER_IMAGE
, RENDER_BOTH_CHECKLIST = RENDER_CHECKLIST | RENDER_BOTH
, RENDER_TEXT_LIST = RENDER_LIST | RENDER_TEXT
, RENDER_IMAGE_LIST = RENDER_LIST | RENDER_IMAGE
, RENDER_BOTH_LIST = RENDER_LIST | RENDER_BOTH
};
enum ThumbnailStatus
{ THUMB_NOT_MADE // there is no image
, THUMB_OK // image is ok
, THUMB_CHANGED // there is an image, but it may need to be updated
{ THUMB_NOT_MADE // there is no image
, THUMB_OK // image is ok
, THUMB_CHANGED // there is an image, but it may need to be updated
};
/// The Style for a ChoiceField
class ChoiceStyle : public Style {
public:
ChoiceStyle(const ChoiceFieldP& field);
DECLARE_STYLE_TYPE(Choice);
~ChoiceStyle();
ChoicePopupStyle popup_style; ///< Style of popups/menus
ChoiceRenderStyle render_style; ///< Style of rendering
Font font; ///< Font for drawing text (when RENDER_TEXT)
CachedScriptableImage image; ///< Image to draw (when RENDER_IMAGE)
map<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE)
bool choice_images_initialized;
ImageCombine combine; ///< Combining mode for drawing the images
Alignment alignment; ///< Alignment of images
wxImageList* thumbnails; ///< Thumbnails for the choices
vector<ThumbnailStatus> thumbnails_status; ///< Which thumbnails are up to date?
// information from image rendering
double content_width, content_height; ///< Size of the rendered image/text
/// Initialize image from choice_images
void initImage();
virtual int update(Context&);
virtual void initDependencies(Context&, const Dependency&) const;
virtual void checkContentDependencies(Context&, const Dependency&) const;
virtual void invalidate();
ChoiceStyle(const ChoiceFieldP& field);
DECLARE_STYLE_TYPE(Choice);
~ChoiceStyle();
ChoicePopupStyle popup_style; ///< Style of popups/menus
ChoiceRenderStyle render_style; ///< Style of rendering
Font font; ///< Font for drawing text (when RENDER_TEXT)
CachedScriptableImage image; ///< Image to draw (when RENDER_IMAGE)
map<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE)
bool choice_images_initialized;
ImageCombine combine; ///< Combining mode for drawing the images
Alignment alignment; ///< Alignment of images
wxImageList* thumbnails; ///< Thumbnails for the choices
vector<ThumbnailStatus> thumbnails_status; ///< Which thumbnails are up to date?
// information from image rendering
double content_width, content_height; ///< Size of the rendered image/text
/// Initialize image from choice_images
void initImage();
virtual int update(Context&);
virtual void initDependencies(Context&, const Dependency&) const;
virtual void checkContentDependencies(Context&, const Dependency&) const;
virtual void invalidate();
};
// ----------------------------------------------------------------------------- : ChoiceValue
@@ -167,16 +167,16 @@ class ChoiceStyle : public Style {
/// The Value in a ChoiceField
class ChoiceValue : public Value {
public:
/// Create a value for the given field
/** If initial_first_choice then the first choice should be used in the absence of
an explicit initial value
*/
ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice = true);
DECLARE_VALUE_TYPE(Choice, Defaultable<String>);
ValueType value; /// The name of the selected choice
virtual bool update(Context&);
/// Create a value for the given field
/** If initial_first_choice then the first choice should be used in the absence of
an explicit initial value
*/
ChoiceValue(const ChoiceFieldP& field, bool initial_first_choice = true);
DECLARE_VALUE_TYPE(Choice, Defaultable<String>);
ValueType value; /// The name of the selected choice
virtual bool update(Context&);
};
// ----------------------------------------------------------------------------- : EOF
+49 -49
View File
@@ -15,90 +15,90 @@ DECLARE_TYPEOF_COLLECTION(ColorField::ChoiceP);
// ----------------------------------------------------------------------------- : ColorField
ColorField::ColorField()
: allow_custom(true)
, default_name(_("Default"))
: allow_custom(true)
, default_name(_("Default"))
{}
IMPLEMENT_FIELD_TYPE(Color, "color");
void ColorField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(ColorField) {
REFLECT_BASE(Field);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(initial);
REFLECT(default_name);
REFLECT(allow_custom);
REFLECT(choices);
REFLECT_BASE(Field);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(initial);
REFLECT(default_name);
REFLECT(allow_custom);
REFLECT(choices);
}
// ----------------------------------------------------------------------------- : ColorField::Choice
IMPLEMENT_REFLECTION(ColorField::Choice) {
if (tag.reading() && !tag.isComplex()) {
REFLECT_NAMELESS(name);
color = parse_color(name);
} else {
REFLECT(name);
REFLECT(color);
}
if (tag.reading() && !tag.isComplex()) {
REFLECT_NAMELESS(name);
color = parse_color(name);
} else {
REFLECT(name);
REFLECT(color);
}
}
// ----------------------------------------------------------------------------- : ColorStyle
ColorStyle::ColorStyle(const ColorFieldP& field)
: Style(field)
, radius(0)
, left_width(100000), right_width (100000)
, top_width (100000), bottom_width(100000)
, combine(COMBINE_NORMAL)
: Style(field)
, radius(0)
, left_width(100000), right_width (100000)
, top_width (100000), bottom_width(100000)
, combine(COMBINE_NORMAL)
{}
IMPLEMENT_REFLECTION(ColorStyle) {
REFLECT_BASE(Style);
REFLECT(radius);
REFLECT(left_width);
REFLECT(right_width);
REFLECT(top_width);
REFLECT(bottom_width);
REFLECT(combine);
REFLECT_BASE(Style);
REFLECT(radius);
REFLECT(left_width);
REFLECT(right_width);
REFLECT(top_width);
REFLECT(bottom_width);
REFLECT(combine);
}
int ColorStyle::update(Context& ctx) {
return Style::update(ctx);
return Style::update(ctx);
}
// ----------------------------------------------------------------------------- : ColorValue
ColorValue::ColorValue(const ColorFieldP& field)
: Value(field)
, value( !field->initial.isDefault() ? field->initial()
: !field->choices.empty() ? field->choices[0]->color
: *wxBLACK
, true)
: Value(field)
, value( !field->initial.isDefault() ? field->initial()
: !field->choices.empty() ? field->choices[0]->color
: *wxBLACK
, true)
{}
String ColorValue::toString() const {
if (value.isDefault()) return field().default_name;
// is this a named color?
FOR_EACH(c, field().choices) {
if (value() == c->color) return c->name;
}
return _("<color>");
if (value.isDefault()) return field().default_name;
// is this a named color?
FOR_EACH(c, field().choices) {
if (value() == c->color) return c->name;
}
return _("<color>");
}
bool ColorValue::update(Context& ctx) {
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
Value::update(ctx);
return change;
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
Value::update(ctx);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(ColorValue) {
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(value);
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(value);
}
+35 -35
View File
@@ -24,29 +24,29 @@ DECLARE_POINTER_TYPE(ColorValue);
/// A field for color values, it contains a list of choices for colors
class ColorField : public Field {
public:
ColorField();
DECLARE_FIELD_TYPE(Color);
class Choice;
typedef intrusive_ptr<Choice> ChoiceP;
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
vector<ChoiceP> choices; ///< Color choices available
bool allow_custom; ///< Are colors not in the list of choices allowed?
Defaultable<Color> initial; ///< Initial choice of a new value, if not set the first choice is used
String default_name; ///< Name of "default" value
virtual void initDependencies(Context&, const Dependency&) const;
ColorField();
DECLARE_FIELD_TYPE(Color);
class Choice;
typedef intrusive_ptr<Choice> ChoiceP;
OptionalScript script; ///< Script to apply to all values
OptionalScript default_script; ///< Script that generates the default value
vector<ChoiceP> choices; ///< Color choices available
bool allow_custom; ///< Are colors not in the list of choices allowed?
Defaultable<Color> initial; ///< Initial choice of a new value, if not set the first choice is used
String default_name; ///< Name of "default" value
virtual void initDependencies(Context&, const Dependency&) const;
};
/// A color that can be chosen for this field
class ColorField::Choice : public IntrusivePtrBase<ColorField::Choice> {
public:
String name; ///< Name of the color
Color color; ///< The actual color
DECLARE_REFLECTION();
String name; ///< Name of the color
Color color; ///< The actual color
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : ColorStyle
@@ -54,17 +54,17 @@ class ColorField::Choice : public IntrusivePtrBase<ColorField::Choice> {
/// The Style for a ColorField
class ColorStyle : public Style {
public:
ColorStyle(const ColorFieldP& field);
DECLARE_STYLE_TYPE(Color);
double radius; ///< Radius of round corners
double left_width; ///< Width of the colored region on the left side
double right_width; ///< Width of the colored region on the right side
double top_width; ///< Width of the colored region on the top side
double bottom_width; ///< Width of the colored region on the bottom side
ImageCombine combine; ///< How to combine image with the background
virtual int update(Context&);
ColorStyle(const ColorFieldP& field);
DECLARE_STYLE_TYPE(Color);
double radius; ///< Radius of round corners
double left_width; ///< Width of the colored region on the left side
double right_width; ///< Width of the colored region on the right side
double top_width; ///< Width of the colored region on the top side
double bottom_width; ///< Width of the colored region on the bottom side
ImageCombine combine; ///< How to combine image with the background
virtual int update(Context&);
};
// ----------------------------------------------------------------------------- : ColorValue
@@ -72,12 +72,12 @@ class ColorStyle : public Style {
/// The Value in a ColorField
class ColorValue : public Value {
public:
ColorValue(const ColorFieldP& field);
DECLARE_VALUE_TYPE(Color, Defaultable<Color>);
ValueType value; ///< The value
virtual bool update(Context&);
ColorValue(const ColorFieldP& field);
DECLARE_VALUE_TYPE(Color, Defaultable<Color>);
ValueType value; ///< The value
virtual bool update(Context&);
};
+10 -10
View File
@@ -15,38 +15,38 @@
IMPLEMENT_FIELD_TYPE(Image, "image");
IMPLEMENT_REFLECTION(ImageField) {
REFLECT_BASE(Field);
REFLECT_BASE(Field);
}
// ----------------------------------------------------------------------------- : ImageStyle
IMPLEMENT_REFLECTION(ImageStyle) {
REFLECT_BASE(Style);
REFLECT_N("default", default_image);
REFLECT_BASE(Style);
REFLECT_N("default", default_image);
}
int ImageStyle::update(Context& ctx) {
return Style ::update(ctx)
| default_image.update(ctx) * CHANGE_DEFAULT;
return Style ::update(ctx)
| default_image.update(ctx) * CHANGE_DEFAULT;
}
// ----------------------------------------------------------------------------- : ImageValue
String ImageValue::toString() const {
return filename.empty() ? wxEmptyString : _("<image>");
return filename.empty() ? wxEmptyString : _("<image>");
}
// custom reflection: convert to ScriptImageP for scripting
void ImageValue::reflect(Reader& tag) {
tag.handle(filename);
tag.handle(filename);
}
void ImageValue::reflect(Writer& tag) {
if (fieldP->save_value) tag.handle(filename);
if (fieldP->save_value) tag.handle(filename);
}
void ImageValue::reflect(GetMember& tag) {}
void ImageValue::reflect(GetDefaultMember& tag) {
// convert to ScriptImageP for scripting
tag.handle( (ScriptValueP)intrusive(new ImageValueToImage(filename, last_update)) );
// convert to ScriptImageP for scripting
tag.handle( (ScriptValueP)intrusive(new ImageValueToImage(filename, last_update)) );
}
+13 -13
View File
@@ -23,8 +23,8 @@ DECLARE_POINTER_TYPE(ImageValue);
/// A field for image values
class ImageField : public Field {
public:
// no extra data
DECLARE_FIELD_TYPE(Image);
// no extra data
DECLARE_FIELD_TYPE(Image);
};
// ----------------------------------------------------------------------------- : ImageStyle
@@ -32,12 +32,12 @@ class ImageField : public Field {
/// The Style for a ImageField
class ImageStyle : public Style {
public:
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
DECLARE_STYLE_TYPE(Image);
ScriptableImage default_image; ///< Placeholder
virtual int update(Context&);
inline ImageStyle(const ImageFieldP& field) : Style(field) {}
DECLARE_STYLE_TYPE(Image);
ScriptableImage default_image; ///< Placeholder
virtual int update(Context&);
};
// ----------------------------------------------------------------------------- : ImageValue
@@ -45,11 +45,11 @@ class ImageStyle : public Style {
/// The Value in a ImageField, i.e. an image
class ImageValue : public Value {
public:
inline ImageValue(const ImageFieldP& field) : Value(field) {}
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?
inline ImageValue(const ImageFieldP& field) : Value(field) {}
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?
};
// ----------------------------------------------------------------------------- : EOF
+29 -29
View File
@@ -15,60 +15,60 @@
IMPLEMENT_FIELD_TYPE(Info, "info");
void InfoField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script. initDependencies(ctx, dep);
Field ::initDependencies(ctx, dep);
script. initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(InfoField) {
REFLECT_BASE(Field);
REFLECT(script);
REFLECT_BASE(Field);
REFLECT(script);
}
// ----------------------------------------------------------------------------- : InfoStyle
InfoStyle::InfoStyle(const InfoFieldP& field)
: Style(field)
, alignment(ALIGN_TOP_LEFT)
, padding_left (2)
, padding_right (2)
, padding_top (2)
, padding_bottom(2)
, background_color(255,255,255)
: Style(field)
, alignment(ALIGN_TOP_LEFT)
, padding_left (2)
, padding_right (2)
, padding_top (2)
, padding_bottom(2)
, background_color(255,255,255)
{}
int InfoStyle::update(Context& ctx) {
return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER;
return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER;
}
void InfoStyle::initDependencies(Context& ctx, const Dependency& dep) const {
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(InfoStyle) {
REFLECT_BASE(Style);
REFLECT(font);
REFLECT(alignment);
REFLECT(padding_left);
REFLECT(padding_right);
REFLECT(padding_top);
REFLECT(padding_bottom);
REFLECT(background_color);
REFLECT_BASE(Style);
REFLECT(font);
REFLECT(alignment);
REFLECT(padding_left);
REFLECT(padding_right);
REFLECT(padding_top);
REFLECT(padding_bottom);
REFLECT(background_color);
}
// ----------------------------------------------------------------------------- : InfoValue
String InfoValue::toString() const {
return value;
return value;
}
bool InfoValue::update(Context& ctx) {
if (value.empty()) value = field().name;
bool change = field().script.invokeOn(ctx, value);
Value::update(ctx);
return change;
if (value.empty()) value = field().name;
bool change = field().script.invokeOn(ctx, value);
Value::update(ctx);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(InfoValue) {
// never save
// never save
}
+23 -23
View File
@@ -26,12 +26,12 @@ DECLARE_POINTER_TYPE(InfoValue);
*/
class InfoField : public Field {
public:
InfoField() { editable = false; }
DECLARE_FIELD_TYPE(Text);
OptionalScript script; ///< Script to apply to all values
virtual void initDependencies(Context&, const Dependency&) const;
InfoField() { editable = false; }
DECLARE_FIELD_TYPE(Text);
OptionalScript script; ///< Script to apply to all values
virtual void initDependencies(Context&, const Dependency&) const;
};
// ----------------------------------------------------------------------------- : InfoStyle
@@ -39,17 +39,17 @@ class InfoField : public Field {
/// The Style for a InfoField
class InfoStyle : public Style {
public:
InfoStyle(const InfoFieldP&);
DECLARE_STYLE_TYPE(Info);
Font font; ///< Font to use for the text
Alignment alignment; ///< Alignment inside the box
double padding_left, padding_right; ///< Padding
double padding_top, padding_bottom;
Color background_color;
virtual int update(Context&);
virtual void initDependencies(Context&, const Dependency&) const;
InfoStyle(const InfoFieldP&);
DECLARE_STYLE_TYPE(Info);
Font font; ///< Font to use for the text
Alignment alignment; ///< Alignment inside the box
double padding_left, padding_right; ///< Padding
double padding_top, padding_bottom;
Color background_color;
virtual int update(Context&);
virtual void initDependencies(Context&, const Dependency&) const;
};
// ----------------------------------------------------------------------------- : InfoValue
@@ -57,12 +57,12 @@ class InfoStyle : public Style {
/// The Value in a InfoField
class InfoValue : public Value {
public:
inline InfoValue(const InfoFieldP& field) : Value(field) {}
DECLARE_VALUE_TYPE(Info, String);
ValueType value;
virtual bool update(Context&);
inline InfoValue(const InfoFieldP& field) : Value(field) {}
DECLARE_VALUE_TYPE(Info, String);
ValueType value;
virtual bool update(Context&);
};
// ----------------------------------------------------------------------------- : EOF
+69 -69
View File
@@ -12,103 +12,103 @@
// ----------------------------------------------------------------------------- : MultipleChoiceField
MultipleChoiceField::MultipleChoiceField()
: minimum_selection(0)
, maximum_selection(1000000)
: minimum_selection(0)
, maximum_selection(1000000)
{}
IMPLEMENT_FIELD_TYPE(MultipleChoice, "multiple choice");
IMPLEMENT_REFLECTION(MultipleChoiceField) {
REFLECT_BASE(ChoiceField);
REFLECT(minimum_selection);
REFLECT(maximum_selection);
REFLECT(empty_choice);
REFLECT_BASE(ChoiceField);
REFLECT(minimum_selection);
REFLECT(maximum_selection);
REFLECT(empty_choice);
}
// ----------------------------------------------------------------------------- : MultipleChoiceStyle
MultipleChoiceStyle::MultipleChoiceStyle(const MultipleChoiceFieldP& field)
: ChoiceStyle(field)
, direction(LEFT_TO_RIGHT)
, spacing(0)
: ChoiceStyle(field)
, direction(LEFT_TO_RIGHT)
, spacing(0)
{}
IMPLEMENT_REFLECTION(MultipleChoiceStyle) {
REFLECT_BASE(ChoiceStyle);
REFLECT(direction);
REFLECT(spacing);
REFLECT_BASE(ChoiceStyle);
REFLECT(direction);
REFLECT(spacing);
}
int MultipleChoiceStyle::update(Context& ctx) {
return ChoiceStyle::update(ctx)
| direction.update(ctx) * CHANGE_OTHER
| spacing.update(ctx) * CHANGE_OTHER;
return ChoiceStyle::update(ctx)
| direction.update(ctx) * CHANGE_OTHER
| spacing.update(ctx) * CHANGE_OTHER;
}
// ----------------------------------------------------------------------------- : MultipleChoiceValue
IMPLEMENT_REFLECTION_NAMELESS(MultipleChoiceValue) {
REFLECT_BASE(ChoiceValue);
REFLECT_BASE(ChoiceValue);
}
bool MultipleChoiceValue::update(Context& ctx) {
String old_value = value();
ctx.setVariable(_("last change"), to_script(last_change));
ChoiceValue::update(ctx);
normalForm();
return value() != old_value;
String old_value = value();
ctx.setVariable(_("last change"), to_script(last_change));
ChoiceValue::update(ctx);
normalForm();
return value() != old_value;
}
void MultipleChoiceValue::get(vector<String>& out) const {
// split the value
out.clear();
bool is_new = true;
FOR_EACH_CONST(c, value()) {
if (c == _(',')) {
is_new = true;
} else if (is_new) {
if (c != _(' ')) { // ignore whitespace after ,
is_new = false;
out.push_back(String(1, c));
}
} else {
assert(!out.empty());
out.back() += c;
}
}
// split the value
out.clear();
bool is_new = true;
FOR_EACH_CONST(c, value()) {
if (c == _(',')) {
is_new = true;
} else if (is_new) {
if (c != _(' ')) { // ignore whitespace after ,
is_new = false;
out.push_back(String(1, c));
}
} else {
assert(!out.empty());
out.back() += c;
}
}
}
void MultipleChoiceValue::normalForm() {
String& val = value.mutateDontChangeDefault();
// which choices are active?
vector<bool> seen(field().choices->lastId());
for (size_t pos = 0 ; pos < val.size() ; ) {
if (val.GetChar(pos) == _(' ')) {
++pos; // ingore whitespace
} else {
// does this choice match the one asked about?
size_t end = val.find_first_of(_(','), pos);
if (end == String::npos) end = val.size();
// find this choice
for (size_t i = 0 ; i < seen.size() ; ++i) {
if (is_substr(val, pos, field().choices->choiceName((int)i))) {
seen[i] = true;
break;
}
}
pos = end + 1;
}
}
// now put them back in the right order
val.clear();
for (size_t i = 0 ; i < seen.size() ; ++i) {
if (seen[i]) {
if (!val.empty()) val += _(", ");
val += field().choices->choiceName((int)i);
}
}
// empty choice name
if (val.empty()) {
val = field().empty_choice;
}
String& val = value.mutateDontChangeDefault();
// which choices are active?
vector<bool> seen(field().choices->lastId());
for (size_t pos = 0 ; pos < val.size() ; ) {
if (val.GetChar(pos) == _(' ')) {
++pos; // ingore whitespace
} else {
// does this choice match the one asked about?
size_t end = val.find_first_of(_(','), pos);
if (end == String::npos) end = val.size();
// find this choice
for (size_t i = 0 ; i < seen.size() ; ++i) {
if (is_substr(val, pos, field().choices->choiceName((int)i))) {
seen[i] = true;
break;
}
}
pos = end + 1;
}
}
// now put them back in the right order
val.clear();
for (size_t i = 0 ; i < seen.size() ; ++i) {
if (seen[i]) {
if (!val.empty()) val += _(", ");
val += field().choices->choiceName((int)i);
}
}
// empty choice name
if (val.empty()) {
val = field().empty_choice;
}
}
+33 -33
View File
@@ -21,11 +21,11 @@ DECLARE_POINTER_TYPE(MultipleChoiceValue);
/// A ChoiceField where multiple choices can be selected simultaniously
class MultipleChoiceField : public ChoiceField {
public:
MultipleChoiceField();
DECLARE_FIELD_TYPE(MultipleChoiceField);
UInt minimum_selection, maximum_selection; ///< How many choices can be selected simultaniously?
String empty_choice; ///< Name to use when nothing is selected
MultipleChoiceField();
DECLARE_FIELD_TYPE(MultipleChoiceField);
UInt minimum_selection, maximum_selection; ///< How many choices can be selected simultaniously?
String empty_choice; ///< Name to use when nothing is selected
};
// ----------------------------------------------------------------------------- : MultipleChoiceStyle
@@ -33,13 +33,13 @@ class MultipleChoiceField : public ChoiceField {
/// The Style for a MultipleChoiceField
class MultipleChoiceStyle : public ChoiceStyle {
public:
MultipleChoiceStyle(const MultipleChoiceFieldP& field);
DECLARE_STYLE_TYPE(MultipleChoice);
Scriptable<Direction> direction; ///< In what direction are choices layed out?
Scriptable<double> spacing; ///< Spacing between choices (images) in pixels
virtual int update(Context&);
MultipleChoiceStyle(const MultipleChoiceFieldP& field);
DECLARE_STYLE_TYPE(MultipleChoice);
Scriptable<Direction> direction; ///< In what direction are choices layed out?
Scriptable<double> spacing; ///< Spacing between choices (images) in pixels
virtual int update(Context&);
};
// ----------------------------------------------------------------------------- : MultipleChoiceValue
@@ -50,28 +50,28 @@ class MultipleChoiceStyle : public ChoiceStyle {
*/
class MultipleChoiceValue : public ChoiceValue {
public:
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, false) {}
DECLARE_HAS_FIELD(MultipleChoice);
virtual ValueP clone() const;
String last_change; ///< Which of the choices was selected/deselected last?
// for SimpleValueAction
struct ValueType {
ChoiceValue::ValueType value;
String last_change;
};
/// Splits the value, stores the selected choices in the out parameter
void get(vector<String>& out) const;
virtual bool update(Context&);
inline MultipleChoiceValue(const MultipleChoiceFieldP& field) : ChoiceValue(field, false) {}
DECLARE_HAS_FIELD(MultipleChoice);
virtual ValueP clone() const;
String last_change; ///< Which of the choices was selected/deselected last?
// for SimpleValueAction
struct ValueType {
ChoiceValue::ValueType value;
String last_change;
};
/// Splits the value, stores the selected choices in the out parameter
void get(vector<String>& out) const;
virtual bool update(Context&);
private:
DECLARE_REFLECTION();
/// Put the value in normal form (all choices ordered, empty_name
void normalForm();
DECLARE_REFLECTION();
/// Put the value in normal form (all choices ordered, empty_name
void normalForm();
};
// ----------------------------------------------------------------------------- : Utilities
+34 -34
View File
@@ -15,73 +15,73 @@
IMPLEMENT_FIELD_TYPE(PackageChoice, "package choice");
void PackageChoiceField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script. initDependencies(ctx, dep);
Field ::initDependencies(ctx, dep);
script. initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(PackageChoiceField) {
REFLECT_BASE(Field);
REFLECT(script);
REFLECT(match);
REFLECT(initial);
REFLECT(required);
REFLECT(empty_name);
REFLECT_BASE(Field);
REFLECT(script);
REFLECT(match);
REFLECT(initial);
REFLECT(required);
REFLECT(empty_name);
}
// ----------------------------------------------------------------------------- : PackageChoiceStyle
PackageChoiceStyle::PackageChoiceStyle(const PackageChoiceFieldP& field)
: Style(field)
: Style(field)
{}
int PackageChoiceStyle::update(Context& ctx) {
return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER;
return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER;
}
/*void PackageChoiceStyle::initDependencies(Context& ctx, const Dependency& dep) const {
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
}*/
IMPLEMENT_REFLECTION(PackageChoiceStyle) {
REFLECT_BASE(Style);
REFLECT(font);
REFLECT_BASE(Style);
REFLECT(font);
}
// ----------------------------------------------------------------------------- : PackageChoiceValue
String PackageChoiceValue::toString() const {
PackagedP pack = getPackage();
if (pack.get()) return pack->short_name;
else return _("");
PackagedP pack = getPackage();
if (pack.get()) return pack->short_name;
else return _("");
}
PackagedP PackageChoiceValue::getPackage() const {
if (package_name.empty()) return nullptr;
else return package_manager.openAny(package_name, true);
if (package_name.empty()) return nullptr;
else return package_manager.openAny(package_name, true);
}
bool PackageChoiceValue::update(Context& ctx) {
bool change = field().script.invokeOn(ctx, package_name);
Value::update(ctx);
return change;
bool change = field().script.invokeOn(ctx, package_name);
Value::update(ctx);
return change;
}
void PackageChoiceValue::reflect(Reader& tag) {
REFLECT_NAMELESS(package_name);
REFLECT_NAMELESS(package_name);
}
void PackageChoiceValue::reflect(Writer& tag) {
REFLECT_NAMELESS(package_name);
REFLECT_NAMELESS(package_name);
}
void PackageChoiceValue::reflect(GetDefaultMember& tag) {
if (package_name.empty()) {
REFLECT_NAMELESS(package_name);
} else if(package_name != field().initial) {
// add a space to the name, to indicate the dependency doesn't have to be marked
// see also PackageManager::openFileFromPackage and SymbolFontRef::loadFont
REFLECT_NAMELESS(_("/:NO-WARN-DEP:") + package_name);
} else {
REFLECT_NAMELESS(_("/") + package_name);
}
if (package_name.empty()) {
REFLECT_NAMELESS(package_name);
} else if(package_name != field().initial) {
// add a space to the name, to indicate the dependency doesn't have to be marked
// see also PackageManager::openFileFromPackage and SymbolFontRef::loadFont
REFLECT_NAMELESS(_("/:NO-WARN-DEP:") + package_name);
} else {
REFLECT_NAMELESS(_("/") + package_name);
}
}
void PackageChoiceValue::reflect(GetMember& tag) {}
+25 -25
View File
@@ -25,16 +25,16 @@ DECLARE_POINTER_TYPE(PackageChoiceValue);
/// A field for PackageChoice values, it contains a list of choices for PackageChoices
class PackageChoiceField : public Field {
public:
PackageChoiceField() : required(true), empty_name(_("none")) {}
DECLARE_FIELD_TYPE(PackageChoice);
OptionalScript script; ///< Script to apply to all values
String match; ///< Package filenames to match
String initial; ///< Initial value
bool required; ///< Is selecting a package required?
String empty_name; ///< Displayed name for the empty value (if !required)
virtual void initDependencies(Context&, const Dependency&) const;
PackageChoiceField() : required(true), empty_name(_("none")) {}
DECLARE_FIELD_TYPE(PackageChoice);
OptionalScript script; ///< Script to apply to all values
String match; ///< Package filenames to match
String initial; ///< Initial value
bool required; ///< Is selecting a package required?
String empty_name; ///< Displayed name for the empty value (if !required)
virtual void initDependencies(Context&, const Dependency&) const;
};
// ----------------------------------------------------------------------------- : PackageChoiceStyle
@@ -42,12 +42,12 @@ class PackageChoiceField : public Field {
/// The Style for a PackageChoiceField
class PackageChoiceStyle : public Style {
public:
PackageChoiceStyle(const PackageChoiceFieldP& field);
DECLARE_STYLE_TYPE(PackageChoice);
Font font; ///< Font to use for the text
virtual int update(Context&);
PackageChoiceStyle(const PackageChoiceFieldP& field);
DECLARE_STYLE_TYPE(PackageChoice);
Font font; ///< Font to use for the text
virtual int update(Context&);
};
// ----------------------------------------------------------------------------- : PackageChoiceValue
@@ -55,15 +55,15 @@ class PackageChoiceStyle : public Style {
/// The Value in a PackageChoiceField
class PackageChoiceValue : public Value {
public:
PackageChoiceValue(const PackageChoiceFieldP& field) : Value(field), package_name(field->initial) {}
DECLARE_VALUE_TYPE(PackageChoice, String);
ValueType package_name; ///< The selected package
/// Get the package (if it is set)
PackagedP getPackage() const;
virtual bool update(Context&);
PackageChoiceValue(const PackageChoiceFieldP& field) : Value(field), package_name(field->initial) {}
DECLARE_VALUE_TYPE(PackageChoice, String);
ValueType package_name; ///< The selected package
/// Get the package (if it is set)
PackagedP getPackage() const;
virtual bool update(Context&);
};
// ----------------------------------------------------------------------------- : EOF
+14 -14
View File
@@ -15,42 +15,42 @@
IMPLEMENT_FIELD_TYPE(Symbol, "symbol");
IMPLEMENT_REFLECTION(SymbolField) {
REFLECT_BASE(Field);
REFLECT_BASE(Field);
}
// ----------------------------------------------------------------------------- : SymbolStyle
IMPLEMENT_REFLECTION(SymbolStyle) {
REFLECT_BASE(Style);
REFLECT(min_aspect_ratio);
REFLECT(max_aspect_ratio);
REFLECT(variations);
REFLECT_BASE(Style);
REFLECT(min_aspect_ratio);
REFLECT(max_aspect_ratio);
REFLECT(variations);
}
SymbolVariation::SymbolVariation()
: border_radius(0.05)
: border_radius(0.05)
{}
SymbolVariation::~SymbolVariation() {}
bool SymbolVariation::operator == (const SymbolVariation& that) const {
return name == that.name
&& border_radius == that.border_radius
&& *filter == *that.filter;
return name == that.name
&& border_radius == that.border_radius
&& *filter == *that.filter;
}
IMPLEMENT_REFLECTION_NO_SCRIPT(SymbolVariation) {
REFLECT(name);
REFLECT(border_radius);
REFLECT_NAMELESS(filter);
REFLECT(name);
REFLECT(border_radius);
REFLECT_NAMELESS(filter);
}
// ----------------------------------------------------------------------------- : SymbolValue
String SymbolValue::toString() const {
return filename.empty() ? wxEmptyString : _("<symbol>");
return filename.empty() ? wxEmptyString : _("<symbol>");
}
IMPLEMENT_REFLECTION_NAMELESS(SymbolValue) {
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(filename);
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(filename);
}
+26 -26
View File
@@ -25,9 +25,9 @@ DECLARE_POINTER_TYPE(SymbolValue);
/// A field for image values
class SymbolField : public Field {
public:
DECLARE_FIELD_TYPE(Symbol);
// no extra data
DECLARE_FIELD_TYPE(Symbol);
// no extra data
};
// ----------------------------------------------------------------------------- : SymbolStyle
@@ -35,29 +35,29 @@ class SymbolField : public Field {
/// The Style for a SymbolField
class SymbolStyle : public Style {
public:
inline SymbolStyle(const SymbolFieldP& field)
: Style(field)
, min_aspect_ratio(1), max_aspect_ratio(1)
{}
DECLARE_STYLE_TYPE(Symbol);
double min_aspect_ratio;
double max_aspect_ratio; ///< Bounds for the symbol's aspect ratio
vector<SymbolVariationP> variations; ///< Different variantions of the same symbol
inline SymbolStyle(const SymbolFieldP& field)
: Style(field)
, min_aspect_ratio(1), max_aspect_ratio(1)
{}
DECLARE_STYLE_TYPE(Symbol);
double min_aspect_ratio;
double max_aspect_ratio; ///< Bounds for the symbol's aspect ratio
vector<SymbolVariationP> variations; ///< Different variantions of the same symbol
};
/// Styling for a symbol variation, defines color, border, etc.
class SymbolVariation : public IntrusivePtrBase<SymbolVariation> {
public:
SymbolVariation();
~SymbolVariation();
String name; ///< Name of this variation
SymbolFilterP filter; ///< Filter to color the symbol
double border_radius; ///< Border radius for the symbol
bool operator == (const SymbolVariation&) const;
DECLARE_REFLECTION();
SymbolVariation();
~SymbolVariation();
String name; ///< Name of this variation
SymbolFilterP filter; ///< Filter to color the symbol
double border_radius; ///< Border radius for the symbol
bool operator == (const SymbolVariation&) const;
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolValue
@@ -65,11 +65,11 @@ class SymbolVariation : public IntrusivePtrBase<SymbolVariation> {
/// The Value in a SymbolField, i.e. a symbol
class SymbolValue : public Value {
public:
inline SymbolValue(const SymbolFieldP& field) : Value(field) {}
DECLARE_VALUE_TYPE(Symbol, FileName);
ValueType filename; ///< Filename of the symbol (in the current package)
Age last_update; ///< When was the symbol last changed?
inline SymbolValue(const SymbolFieldP& field) : Value(field) {}
DECLARE_VALUE_TYPE(Symbol, FileName);
ValueType filename; ///< Filename of the symbol (in the current package)
Age last_update; ///< When was the symbol last changed?
};
// ----------------------------------------------------------------------------- : EOF
+115 -115
View File
@@ -14,176 +14,176 @@
// ----------------------------------------------------------------------------- : TextField
TextField::TextField()
: multi_line(false)
, default_name(_("Default"))
: multi_line(false)
, default_name(_("Default"))
{}
IMPLEMENT_FIELD_TYPE(Text, "text");
void TextField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(TextField) {
REFLECT_BASE(Field);
REFLECT(multi_line);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(default_name);
REFLECT_BASE(Field);
REFLECT(multi_line);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(default_name);
}
// ----------------------------------------------------------------------------- : TextStyle
TextStyle::TextStyle(const TextFieldP& field)
: Style(field)
, always_symbol(false), allow_formating(true)
, alignment(ALIGN_TOP_LEFT)
, padding_left (0), padding_left_min (10000)
, padding_right (0), padding_right_min (10000)
, padding_top (0), padding_top_min (10000)
, padding_bottom(0), padding_bottom_min(10000)
, line_height_soft(1.0)
, line_height_hard(1.0)
, line_height_line(1.0)
, line_height_soft_max(0.0)
, line_height_hard_max(0.0)
, line_height_line_max(0.0)
, paragraph_height(-1)
, direction(LEFT_TO_RIGHT)
, content_width(0), content_height(0), content_lines(0)
: Style(field)
, always_symbol(false), allow_formating(true)
, alignment(ALIGN_TOP_LEFT)
, padding_left (0), padding_left_min (10000)
, padding_right (0), padding_right_min (10000)
, padding_top (0), padding_top_min (10000)
, padding_bottom(0), padding_bottom_min(10000)
, line_height_soft(1.0)
, line_height_hard(1.0)
, line_height_line(1.0)
, line_height_soft_max(0.0)
, line_height_hard_max(0.0)
, line_height_line_max(0.0)
, paragraph_height(-1)
, direction(LEFT_TO_RIGHT)
, content_width(0), content_height(0), content_lines(0)
{}
double TextStyle::getStretch() const {
if (content_width > 0 && (alignment() & ALIGN_STRETCH)) {
double factor = (width - padding_left - padding_right) / content_width;
if (!(alignment() & ALIGN_IF_OVERFLOW) || factor < 1.0) {
return factor;
}
}
return 1.0;
if (content_width > 0 && (alignment() & ALIGN_STRETCH)) {
double factor = (width - padding_left - padding_right) / content_width;
if (!(alignment() & ALIGN_IF_OVERFLOW) || factor < 1.0) {
return factor;
}
}
return 1.0;
}
int TextStyle::update(Context& ctx) {
return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER
| symbol_font.update(ctx) * CHANGE_OTHER
| alignment .update(ctx) * CHANGE_OTHER
| ( padding_left .update(ctx)
| padding_left_min .update(ctx)
| padding_right .update(ctx)
| padding_right_min .update(ctx)
| padding_top .update(ctx)
| padding_top_min .update(ctx)
| padding_bottom .update(ctx)
| padding_bottom_min .update(ctx)
| line_height_soft .update(ctx)
| line_height_hard .update(ctx)
| line_height_line .update(ctx)
| line_height_soft_max.update(ctx)
| line_height_hard_max.update(ctx)
| line_height_line_max.update(ctx)
) * CHANGE_OTHER;
return Style ::update(ctx)
| font .update(ctx) * CHANGE_OTHER
| symbol_font.update(ctx) * CHANGE_OTHER
| alignment .update(ctx) * CHANGE_OTHER
| ( padding_left .update(ctx)
| padding_left_min .update(ctx)
| padding_right .update(ctx)
| padding_right_min .update(ctx)
| padding_top .update(ctx)
| padding_top_min .update(ctx)
| padding_bottom .update(ctx)
| padding_bottom_min .update(ctx)
| line_height_soft .update(ctx)
| line_height_hard .update(ctx)
| line_height_line .update(ctx)
| line_height_soft_max.update(ctx)
| line_height_hard_max.update(ctx)
| line_height_line_max.update(ctx)
) * CHANGE_OTHER;
}
void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const {
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
// symbol_font.initDependencies(ctx, dep);
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
// symbol_font.initDependencies(ctx, dep);
}
void TextStyle::checkContentDependencies(Context& ctx, const Dependency& dep) const {
Style ::checkContentDependencies(ctx, dep);
alignment.initDependencies(ctx, dep);
Style ::checkContentDependencies(ctx, dep);
alignment.initDependencies(ctx, dep);
}
template <typename T> void reflect_content(T& tag, const TextStyle& ts) {}
template <> void reflect_content(GetMember& tag, const TextStyle& ts) {
REFLECT_N("content_width", ts.content_width);
REFLECT_N("content_height", ts.content_height);
REFLECT_N("content_lines", ts.content_lines);
REFLECT_N("content_width", ts.content_width);
REFLECT_N("content_height", ts.content_height);
REFLECT_N("content_lines", ts.content_lines);
}
IMPLEMENT_REFLECTION(TextStyle) {
REFLECT_BASE(Style);
REFLECT(font);
REFLECT(symbol_font);
REFLECT(always_symbol);
REFLECT(allow_formating);
REFLECT(alignment);
REFLECT(padding_left);
REFLECT(padding_right);
REFLECT(padding_top);
REFLECT(padding_bottom);
REFLECT(padding_left_min);
REFLECT(padding_right_min);
REFLECT(padding_top_min);
REFLECT(padding_bottom_min);
REFLECT(line_height_soft);
REFLECT(line_height_hard);
REFLECT(line_height_line);
REFLECT(line_height_soft_max);
REFLECT(line_height_hard_max);
REFLECT(line_height_line_max);
REFLECT(paragraph_height);
REFLECT(direction);
reflect_content(tag, *this);
REFLECT_BASE(Style);
REFLECT(font);
REFLECT(symbol_font);
REFLECT(always_symbol);
REFLECT(allow_formating);
REFLECT(alignment);
REFLECT(padding_left);
REFLECT(padding_right);
REFLECT(padding_top);
REFLECT(padding_bottom);
REFLECT(padding_left_min);
REFLECT(padding_right_min);
REFLECT(padding_top_min);
REFLECT(padding_bottom_min);
REFLECT(line_height_soft);
REFLECT(line_height_hard);
REFLECT(line_height_line);
REFLECT(line_height_soft_max);
REFLECT(line_height_hard_max);
REFLECT(line_height_line_max);
REFLECT(paragraph_height);
REFLECT(direction);
reflect_content(tag, *this);
}
// ----------------------------------------------------------------------------- : TextValue
String TextValue::toString() const {
return untag_hide_sep(value());
return untag_hide_sep(value());
}
bool TextValue::update(Context& ctx) {
updateAge();
WITH_DYNAMIC_ARG(last_update_age, last_update.get());
WITH_DYNAMIC_ARG(value_being_updated, this);
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
if (change) last_update.update();
updateSortValue(ctx);
return change;
updateAge();
WITH_DYNAMIC_ARG(last_update_age, last_update.get());
WITH_DYNAMIC_ARG(value_being_updated, this);
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
if (change) last_update.update();
updateSortValue(ctx);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(TextValue) {
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(value);
if (fieldP->save_value || tag.scripting() || tag.reading()) REFLECT_NAMELESS(value);
}
// ----------------------------------------------------------------------------- : FakeTextValue
FakeTextValue::FakeTextValue(const TextFieldP& field, String* underlying, bool editable, bool untagged)
: TextValue(field), underlying(underlying)
, editable(editable), untagged(untagged)
: TextValue(field), underlying(underlying)
, editable(editable), untagged(untagged)
{}
void FakeTextValue::store() {
if (underlying) {
if (editable) {
*underlying = untagged ? untag(value) : value();
} else {
retrieve();
}
}
if (underlying) {
if (editable) {
*underlying = untagged ? untag(value) : value();
} else {
retrieve();
}
}
}
void FakeTextValue::retrieve() {
if (underlying) {
value.assign(untagged ? escape(*underlying) : *underlying);
} else {
value.assign(wxEmptyString);
}
if (underlying) {
value.assign(untagged ? escape(*underlying) : *underlying);
} else {
value.assign(wxEmptyString);
}
}
void FakeTextValue::onAction(Action& a, bool undone) {
store();
store();
}
bool FakeTextValue::equals(const Value* that) {
if (this == that) return true;
if (!underlying) return false;
const FakeTextValue* thatT = dynamic_cast<const FakeTextValue*>(that);
if (!thatT || underlying != thatT->underlying) return false;
// update the value
retrieve();
return true;
if (this == that) return true;
if (!underlying) return false;
const FakeTextValue* thatT = dynamic_cast<const FakeTextValue*>(that);
if (!thatT || underlying != thatT->underlying) return false;
// update the value
retrieve();
return true;
}
+68 -68
View File
@@ -30,17 +30,17 @@ DECLARE_POINTER_TYPE(TextBackground);
/// A field for values containing tagged text
class TextField : public Field {
public:
TextField();
DECLARE_FIELD_TYPE(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;
TextField();
DECLARE_FIELD_TYPE(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;
};
// ----------------------------------------------------------------------------- : TextStyle
@@ -48,37 +48,37 @@ class TextField : public Field {
/// The Style for a TextField
class TextStyle : public Style {
public:
TextStyle(const TextFieldP&);
DECLARE_STYLE_TYPE(Text);
Font font; ///< Font to use for the text
SymbolFontRef symbol_font; ///< Symbol font for symbols in the text
bool always_symbol; ///< Should everything be drawn as symbols?
bool allow_formating; ///< Is formating (bold/italic/..) allowed?
Scriptable<Alignment> alignment; ///< Alignment inside the box
Scriptable<double>
padding_left, padding_left_min, ///< Padding
padding_right, padding_right_min, ///< Padding
padding_top, padding_top_min, ///< Padding
padding_bottom, padding_bottom_min, ///< Padding
line_height_soft, ///< Line height for soft linebreaks
line_height_hard, ///< Line height for hard linebreaks
line_height_line, ///< Line height for <line> tags
line_height_soft_max, ///< Maximum line height
line_height_hard_max, ///< Maximum line height
line_height_line_max, ///< Maximum line height
paragraph_height; ///< Fixed height of paragraphs
Direction direction; ///< In what direction is text layed out?
// information from text rendering
double content_width, content_height; ///< Size of the rendered text
int content_lines; ///< Number of rendered lines
virtual int update(Context&);
virtual void initDependencies(Context&, const Dependency&) const;
virtual void checkContentDependencies(Context&, const Dependency&) const;
/// Stretch factor to use
double getStretch() const;
TextStyle(const TextFieldP&);
DECLARE_STYLE_TYPE(Text);
Font font; ///< Font to use for the text
SymbolFontRef symbol_font; ///< Symbol font for symbols in the text
bool always_symbol; ///< Should everything be drawn as symbols?
bool allow_formating; ///< Is formating (bold/italic/..) allowed?
Scriptable<Alignment> alignment; ///< Alignment inside the box
Scriptable<double>
padding_left, padding_left_min, ///< Padding
padding_right, padding_right_min, ///< Padding
padding_top, padding_top_min, ///< Padding
padding_bottom, padding_bottom_min, ///< Padding
line_height_soft, ///< Line height for soft linebreaks
line_height_hard, ///< Line height for hard linebreaks
line_height_line, ///< Line height for <line> tags
line_height_soft_max, ///< Maximum line height
line_height_hard_max, ///< Maximum line height
line_height_line_max, ///< Maximum line height
paragraph_height; ///< Fixed height of paragraphs
Direction direction; ///< In what direction is text layed out?
// information from text rendering
double content_width, content_height; ///< Size of the rendered text
int content_lines; ///< Number of rendered lines
virtual int update(Context&);
virtual void initDependencies(Context&, const Dependency&) const;
virtual void checkContentDependencies(Context&, const Dependency&) const;
/// Stretch factor to use
double getStretch() const;
};
// ----------------------------------------------------------------------------- : TextValue
@@ -86,13 +86,13 @@ class TextStyle : public Style {
/// The Value in a TextField
class TextValue : public Value {
public:
inline TextValue(const TextFieldP& field) : Value(field), last_update(1) {}
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&);
inline TextValue(const TextFieldP& field) : Value(field), last_update(1) {}
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&);
};
// ----------------------------------------------------------------------------- : TextValue
@@ -101,25 +101,25 @@ class TextValue : public Value {
/** Used by TextCtrl */
class FakeTextValue : public TextValue {
public:
/// Initialize the fake text value
/** underlying can be nullptr, in that case there is no underlying value */
FakeTextValue(const TextFieldP& field, String* underlying, bool editable, bool untagged);
String* const underlying; ///< The underlying actual value, can be null
bool const editable; ///< The underlying value can be edited
bool const untagged; ///< The underlying value is untagged
/// Store the value in the underlying value.
/** May be overloaded to do some transformation */
virtual void store();
/// Retrieve the value from the underlying value.
/** May be overloaded to do some transformation */
virtual void retrieve();
/// Update underlying data
virtual void onAction(Action& a, bool undone);
/// Editing the same underlying value?
virtual bool equals(const Value* that);
/// Initialize the fake text value
/** underlying can be nullptr, in that case there is no underlying value */
FakeTextValue(const TextFieldP& field, String* underlying, bool editable, bool untagged);
String* const underlying; ///< The underlying actual value, can be null
bool const editable; ///< The underlying value can be edited
bool const untagged; ///< The underlying value is untagged
/// Store the value in the underlying value.
/** May be overloaded to do some transformation */
virtual void store();
/// Retrieve the value from the underlying value.
/** May be overloaded to do some transformation */
virtual void retrieve();
/// Update underlying data
virtual void onAction(Action& a, bool undone);
/// Editing the same underlying value?
virtual bool equals(const Value* that);
};
// ----------------------------------------------------------------------------- : EOF
+51 -51
View File
@@ -17,21 +17,21 @@
template <typename T>
class Filter : public IntrusivePtrVirtualBase {
public:
typedef intrusive_ptr<T> TP;
virtual ~Filter() {}
/// Should an object be shown in the list?
virtual bool keep(T const& x) const {
return false;
}
/// Select objects from a list
virtual void getItems(vector<TP> const& in, vector<VoidP>& out) const {
for (typename vector<TP>::const_iterator it = in.begin() ; it != in.end() ; ++it) {
if (keep(**it)) {
out.push_back(*it);
}
}
}
typedef intrusive_ptr<T> TP;
virtual ~Filter() {}
/// Should an object be shown in the list?
virtual bool keep(T const& x) const {
return false;
}
/// Select objects from a list
virtual void getItems(vector<TP> const& in, vector<VoidP>& out) const {
for (typename vector<TP>::const_iterator it = in.begin() ; it != in.end() ; ++it) {
if (keep(**it)) {
out.push_back(*it);
}
}
}
};
// ----------------------------------------------------------------------------- : Quick search
@@ -39,49 +39,49 @@ class Filter : public IntrusivePtrVirtualBase {
/// Does the given object match the quick search query?
template <typename T>
bool match_quicksearch_query(String const& query, T const& object) {
bool need_match = true;
// iterate over the components of the query
for (size_t i = 0 ; i < query.size() ; ) {
if (query.GetChar(i) == _(' ')) {
// skip spaces
i++;
} else if (query.GetChar(i) == _('-')) {
// negate the next query, i.e. match only if it is not on the card
need_match = !need_match;
i++;
} else {
size_t end, next;
if (query.GetChar(i) == _('"')) {
// quoted string, match exactly
i++;
end =query.find_first_of(_('"'),i);
next = min(end,query.size()) + 1;
} else {
// single word
next = end = query.find_first_of(_(' '),i);
}
bool match = object.contains(query.substr(i,end-i));
if (match != need_match) {
return false;
}
need_match = true; // next word is no longer negated
i = next;
}
}
return true;
bool need_match = true;
// iterate over the components of the query
for (size_t i = 0 ; i < query.size() ; ) {
if (query.GetChar(i) == _(' ')) {
// skip spaces
i++;
} else if (query.GetChar(i) == _('-')) {
// negate the next query, i.e. match only if it is not on the card
need_match = !need_match;
i++;
} else {
size_t end, next;
if (query.GetChar(i) == _('"')) {
// quoted string, match exactly
i++;
end =query.find_first_of(_('"'),i);
next = min(end,query.size()) + 1;
} else {
// single word
next = end = query.find_first_of(_(' '),i);
}
bool match = object.contains(query.substr(i,end-i));
if (match != need_match) {
return false;
}
need_match = true; // next word is no longer negated
i = next;
}
}
return true;
}
/// A filter function that searches for objects containing a string
template <typename T>
class QuickFilter : public Filter<T> {
public:
using typename Filter<T>::TP;
QuickFilter(String const& query) : query(query) {}
virtual bool keep(T const& x) const {
return match_quicksearch_query(query, x);
}
using typename Filter<T>::TP;
QuickFilter(String const& query) : query(query) {}
virtual bool keep(T const& x) const {
return match_quicksearch_query(query, x);
}
private:
String query;
String query;
};
// ----------------------------------------------------------------------------- : EOF
+95 -95
View File
@@ -12,120 +12,120 @@
// ----------------------------------------------------------------------------- : Font
Font::Font()
: name()
, size(1)
, underline(false)
, scale_down_to(100000)
, max_stretch(1.0)
, color(AColor(0,0,0))
, shadow_displacement(0,0)
, shadow_blur(0)
, separator_color(AColor(0,0,0,128))
, flags(FONT_NORMAL)
: name()
, size(1)
, underline(false)
, scale_down_to(100000)
, max_stretch(1.0)
, color(AColor(0,0,0))
, shadow_displacement(0,0)
, shadow_blur(0)
, separator_color(AColor(0,0,0,128))
, flags(FONT_NORMAL)
{}
bool Font::update(Context& ctx) {
bool changes
= name .update(ctx)
| italic_name .update(ctx)
| size .update(ctx)
| weight .update(ctx)
| style .update(ctx)
| underline .update(ctx)
| color .update(ctx)
| shadow_color.update(ctx);
flags = (flags & ~FONT_BOLD & ~FONT_ITALIC)
| (weight() == _("bold") ? FONT_BOLD : FONT_NORMAL)
| (style() == _("italic") ? FONT_ITALIC : FONT_NORMAL);
return changes;
bool changes
= name .update(ctx)
| italic_name .update(ctx)
| size .update(ctx)
| weight .update(ctx)
| style .update(ctx)
| underline .update(ctx)
| color .update(ctx)
| shadow_color.update(ctx);
flags = (flags & ~FONT_BOLD & ~FONT_ITALIC)
| (weight() == _("bold") ? FONT_BOLD : FONT_NORMAL)
| (style() == _("italic") ? FONT_ITALIC : FONT_NORMAL);
return changes;
}
void Font::initDependencies(Context& ctx, const Dependency& dep) const {
name .initDependencies(ctx, dep);
italic_name .initDependencies(ctx, dep);
size .initDependencies(ctx, dep);
weight .initDependencies(ctx, dep);
style .initDependencies(ctx, dep);
underline .initDependencies(ctx, dep);
color .initDependencies(ctx, dep);
shadow_color.initDependencies(ctx, dep);
name .initDependencies(ctx, dep);
italic_name .initDependencies(ctx, dep);
size .initDependencies(ctx, dep);
weight .initDependencies(ctx, dep);
style .initDependencies(ctx, dep);
underline .initDependencies(ctx, dep);
color .initDependencies(ctx, dep);
shadow_color.initDependencies(ctx, dep);
}
FontP Font::make(int add_flags, AColor* other_color, double* other_size) const {
FontP f(new Font(*this));
f->flags |= add_flags;
if (add_flags & FONT_CODE_STRING) {
f->color = Color(0,0,100);
}
if (add_flags & FONT_CODE) {
f->color = Color(128,0,0);
}
if (add_flags & FONT_CODE_KW) {
f->color = Color(158,100,0);
f->flags |= FONT_BOLD;
}
if (add_flags & FONT_SOFT) {
f->color = f->separator_color;
f->shadow_displacement = RealSize(0,0); // no shadow
}
if (other_color) {
f->color = *other_color;
}
if (other_size) {
f->size = *other_size;
}
return f;
FontP f(new Font(*this));
f->flags |= add_flags;
if (add_flags & FONT_CODE_STRING) {
f->color = Color(0,0,100);
}
if (add_flags & FONT_CODE) {
f->color = Color(128,0,0);
}
if (add_flags & FONT_CODE_KW) {
f->color = Color(158,100,0);
f->flags |= FONT_BOLD;
}
if (add_flags & FONT_SOFT) {
f->color = f->separator_color;
f->shadow_displacement = RealSize(0,0); // no shadow
}
if (other_color) {
f->color = *other_color;
}
if (other_size) {
f->size = *other_size;
}
return f;
}
static const String BOLD_STRING = _(" Bold");
wxFont Font::toWxFont(double scale) const {
int size_i = to_int(scale * size);
int weight_i = flags & FONT_BOLD ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL;
int style_i = flags & FONT_ITALIC ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL;
// make font
wxFont font;
int size_i = to_int(scale * size);
int weight_i = flags & FONT_BOLD ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL;
int style_i = flags & FONT_ITALIC ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL;
// make font
wxFont font;
if (flags & FONT_CODE) {
if (size_i < 2) {
return wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, weight_i, underline(), _("Courier New"));
} else {
font = wxFont(size_i, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, weight_i, underline(), _("Courier New"));
}
} else if (name().empty()) {
font = *wxNORMAL_FONT;
font.SetPointSize(size > 1 ? size_i : int(scale * font.GetPointSize()));
return font;
} else if (flags & FONT_ITALIC && !italic_name().empty()) {
font = wxFont(size_i, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, weight_i, underline(), italic_name());
} else {
if (flags & FONT_CODE) {
if (size_i < 2) {
return wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, weight_i, underline(), _("Courier New"));
} else {
font = wxFont(size_i, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, weight_i, underline(), _("Courier New"));
}
} else if (name().empty()) {
font = *wxNORMAL_FONT;
font.SetPointSize(size > 1 ? size_i : int(scale * font.GetPointSize()));
return font;
} else if (flags & FONT_ITALIC && !italic_name().empty()) {
font = wxFont(size_i, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, weight_i, underline(), italic_name());
} else {
String familyName = name();
if(familyName.EndsWith(BOLD_STRING)) {
familyName = familyName.Left(familyName.length() - BOLD_STRING.length());
weight_i = wxFONTWEIGHT_BOLD;
}
font = wxFont(size_i, wxFONTFAMILY_DEFAULT, style_i, weight_i, underline(), familyName);
}
// fix size
#ifdef __WXMSW__
// make it independent of screen dpi, always use 96 dpi
// TODO: do something more sensible, and more portable
font.SetPixelSize(wxSize(0, -(int)(scale*size*96.0/72.0 + 0.5) ));
#endif
return font;
font = wxFont(size_i, wxFONTFAMILY_DEFAULT, style_i, weight_i, underline(), familyName);
}
// fix size
#ifdef __WXMSW__
// make it independent of screen dpi, always use 96 dpi
// TODO: do something more sensible, and more portable
font.SetPixelSize(wxSize(0, -(int)(scale*size*96.0/72.0 + 0.5) ));
#endif
return font;
}
IMPLEMENT_REFLECTION_NO_SCRIPT(Font) {
REFLECT(name);
REFLECT(size);
REFLECT(weight);
REFLECT(style);
REFLECT(underline);
REFLECT(italic_name);
REFLECT(color);
REFLECT(scale_down_to);
REFLECT(max_stretch);
REFLECT_N("shadow_displacement_x", shadow_displacement.width);
REFLECT_N("shadow_displacement_y", shadow_displacement.height);
REFLECT(shadow_color);
REFLECT(shadow_blur);
REFLECT(separator_color);
REFLECT(name);
REFLECT(size);
REFLECT(weight);
REFLECT(style);
REFLECT(underline);
REFLECT(italic_name);
REFLECT(color);
REFLECT(scale_down_to);
REFLECT(max_stretch);
REFLECT_N("shadow_displacement_x", shadow_displacement.width);
REFLECT_N("shadow_displacement_y", shadow_displacement.height);
REFLECT(shadow_color);
REFLECT(shadow_blur);
REFLECT(separator_color);
}
+42 -42
View File
@@ -19,55 +19,55 @@ DECLARE_POINTER_TYPE(Font);
// ----------------------------------------------------------------------------- : Font
enum FontFlags
{ FONT_NORMAL = 0
, FONT_BOLD = 0x01
, FONT_ITALIC = 0x02
, FONT_SOFT = 0x04
, FONT_CODE = 0x08
, FONT_CODE_KW = 0x10 // syntax highlighting
, FONT_CODE_STRING = 0x20 // syntax highlighting
, FONT_CODE_NUMBER = 0x40 // syntax highlighting
, FONT_CODE_OPER = 0x80 // syntax highlighting
{ FONT_NORMAL = 0
, FONT_BOLD = 0x01
, FONT_ITALIC = 0x02
, FONT_SOFT = 0x04
, FONT_CODE = 0x08
, FONT_CODE_KW = 0x10 // syntax highlighting
, FONT_CODE_STRING = 0x20 // syntax highlighting
, FONT_CODE_NUMBER = 0x40 // syntax highlighting
, FONT_CODE_OPER = 0x80 // syntax highlighting
};
/// A font for rendering text
/** Contains additional information about scaling, color and shadow */
class Font : public IntrusivePtrBase<Font> {
public:
Scriptable<String> name; ///< Name of the font
Scriptable<String> italic_name; ///< Font name for italic text (optional)
Scriptable<double> size; ///< Size of the font
Scriptable<String> weight, style; ///< Weight and style of the font (bold/italic)
Scriptable<bool> underline; ///< Underlined?
double scale_down_to; ///< Smallest size to scale down to
double max_stretch; ///< How much should the font be stretched before scaling down?
Scriptable<AColor> color; ///< Color to use
Scriptable<AColor> shadow_color; ///< Color for shadow
RealSize shadow_displacement; ///< Position of the shadow
double shadow_blur; ///< Blur radius of the shadow
AColor separator_color; ///< Color for <sep> text
int flags; ///< FontFlags for this font
Font();
/// Update the scritables, returns true if there is a change
bool update(Context& ctx);
/// Add the given dependency to the dependent_scripts list for the variables this font depends on
void initDependencies(Context&, const Dependency&) const;
/// Does this font have a shadow?
inline bool hasShadow() const {
return shadow_displacement.width != 0 || shadow_displacement.height != 0;
}
/// Add style to a font, and optionally change the color and size
FontP make(int add_flags, AColor* other_color, double* other_size) const;
/// Convert this font to a wxFont
wxFont toWxFont(double scale) const;
Scriptable<String> name; ///< Name of the font
Scriptable<String> italic_name; ///< Font name for italic text (optional)
Scriptable<double> size; ///< Size of the font
Scriptable<String> weight, style; ///< Weight and style of the font (bold/italic)
Scriptable<bool> underline; ///< Underlined?
double scale_down_to; ///< Smallest size to scale down to
double max_stretch; ///< How much should the font be stretched before scaling down?
Scriptable<AColor> color; ///< Color to use
Scriptable<AColor> shadow_color; ///< Color for shadow
RealSize shadow_displacement; ///< Position of the shadow
double shadow_blur; ///< Blur radius of the shadow
AColor separator_color; ///< Color for <sep> text
int flags; ///< FontFlags for this font
Font();
/// Update the scritables, returns true if there is a change
bool update(Context& ctx);
/// Add the given dependency to the dependent_scripts list for the variables this font depends on
void initDependencies(Context&, const Dependency&) const;
/// Does this font have a shadow?
inline bool hasShadow() const {
return shadow_displacement.width != 0 || shadow_displacement.height != 0;
}
/// Add style to a font, and optionally change the color and size
FontP make(int add_flags, AColor* other_color, double* other_size) const;
/// Convert this font to a wxFont
wxFont toWxFont(double scale) const;
private:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
File diff suppressed because it is too large Load Diff
+76 -76
View File
@@ -23,131 +23,131 @@
/// Serialize an object to a string, clipboard_package will be set to the given package.
template <typename T>
String serialize_for_clipboard(Package& package, T& object) {
shared_ptr<wxStringOutputStream> stream( new wxStringOutputStream );
Writer writer(stream, file_version_clipboard);
WITH_DYNAMIC_ARG(clipboard_package, &package);
writer.handle(object);
return stream->GetString();
shared_ptr<wxStringOutputStream> stream( new wxStringOutputStream );
Writer writer(stream, file_version_clipboard);
WITH_DYNAMIC_ARG(clipboard_package, &package);
writer.handle(object);
return stream->GetString();
}
template <typename T>
void deserialize_from_clipboard(T& object, Package& package, const String& data) {
shared_ptr<wxStringInputStream> stream( new wxStringInputStream(data) );
Reader reader(stream, nullptr, _("clipboard"));
WITH_DYNAMIC_ARG(clipboard_package, &package);
reader.handle_greedy(object);
shared_ptr<wxStringInputStream> stream( new wxStringInputStream(data) );
Reader reader(stream, nullptr, _("clipboard"));
WITH_DYNAMIC_ARG(clipboard_package, &package);
reader.handle_greedy(object);
}
// ----------------------------------------------------------------------------- : CardDataObject
/// A wrapped cards for storing on the clipboard
struct WrappedCards {
Game* expected_game;
String game_name;
vector<CardP> cards;
DECLARE_REFLECTION();
Game* expected_game;
String game_name;
vector<CardP> cards;
DECLARE_REFLECTION();
};
IMPLEMENT_REFLECTION(WrappedCards) {
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(cards);
}
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(cards);
}
}
wxDataFormat CardsDataObject::format = _("application/x-mse-cards");
CardsDataObject::CardsDataObject(const SetP& set, const vector<CardP>& cards) {
// set the stylesheet, so when deserializing we know whos style options we are reading
bool* has_styling = new bool[cards.size()];
for (size_t i = 0 ; i < cards.size() ; ++i) {
has_styling[i] = cards[i]->has_styling && !cards[i]->stylesheet;
if (has_styling[i]) {
cards[i]->stylesheet = set->stylesheet;
}
}
WrappedCards data = { set->game.get(), set->game->name(), cards };
SetText(serialize_for_clipboard(*set, data));
// restore cards
for (size_t i = 0 ; i < cards.size() ; ++i) {
if (has_styling[i]) {
cards[i]->stylesheet = StyleSheetP();
}
}
SetFormat(format);
delete [] has_styling;
// set the stylesheet, so when deserializing we know whos style options we are reading
bool* has_styling = new bool[cards.size()];
for (size_t i = 0 ; i < cards.size() ; ++i) {
has_styling[i] = cards[i]->has_styling && !cards[i]->stylesheet;
if (has_styling[i]) {
cards[i]->stylesheet = set->stylesheet;
}
}
WrappedCards data = { set->game.get(), set->game->name(), cards };
SetText(serialize_for_clipboard(*set, data));
// restore cards
for (size_t i = 0 ; i < cards.size() ; ++i) {
if (has_styling[i]) {
cards[i]->stylesheet = StyleSheetP();
}
}
SetFormat(format);
delete [] has_styling;
}
CardsDataObject::CardsDataObject() {
SetFormat(format);
SetFormat(format);
}
bool CardsDataObject::getCards(const SetP& set, vector<CardP>& out) {
WrappedCards data = { set->game.get(), set->game->name() };
deserialize_from_clipboard(data, *set, GetText());
if (data.cards.empty()) return false;
if (data.game_name == set->game->name()) {
// Cards are from the same game
out = data.cards;
return true;
} else {
return false;
}
WrappedCards data = { set->game.get(), set->game->name() };
deserialize_from_clipboard(data, *set, GetText());
if (data.cards.empty()) return false;
if (data.game_name == set->game->name()) {
// Cards are from the same game
out = data.cards;
return true;
} else {
return false;
}
}
// ----------------------------------------------------------------------------- : KeywordDataObject
/// A wrapped keyword for storing on the clipboard
struct WrappedKeyword {
Game* expected_game;
String game_name;
KeywordP keyword;
DECLARE_REFLECTION();
Game* expected_game;
String game_name;
KeywordP keyword;
DECLARE_REFLECTION();
};
IMPLEMENT_REFLECTION(WrappedKeyword) {
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(keyword);
}
REFLECT(game_name);
if (game_name == expected_game->name()) {
WITH_DYNAMIC_ARG(game_for_reading, expected_game);
REFLECT(keyword);
}
}
wxDataFormat KeywordDataObject::format = _("application/x-mse-keyword");
KeywordDataObject::KeywordDataObject(const SetP& set, const KeywordP& keyword) {
WrappedKeyword data = { set->game.get(), set->game->name(), keyword };
SetText(serialize_for_clipboard(*set, data));
SetFormat(format);
WrappedKeyword data = { set->game.get(), set->game->name(), keyword };
SetText(serialize_for_clipboard(*set, data));
SetFormat(format);
}
KeywordDataObject::KeywordDataObject() {
SetFormat(format);
SetFormat(format);
}
KeywordP KeywordDataObject::getKeyword(const SetP& set) {
KeywordP keyword(new Keyword());
WrappedKeyword data = { set->game.get(), set->game->name(), keyword};
deserialize_from_clipboard(data, *set, GetText());
if (data.game_name != set->game->name()) return KeywordP(); // Keyword is from a different game
else return keyword;
KeywordP keyword(new Keyword());
WrappedKeyword data = { set->game.get(), set->game->name(), keyword};
deserialize_from_clipboard(data, *set, GetText());
if (data.game_name != set->game->name()) return KeywordP(); // Keyword is from a different game
else return keyword;
}
// ----------------------------------------------------------------------------- : Card on clipboard
CardsOnClipboard::CardsOnClipboard(const SetP& set, const vector<CardP>& cards) {
// Conversion to text format
// TODO
//Add( new TextDataObject(_("card")))
// Conversion to bitmap format
if (cards.size() == 1) {
Add(new wxBitmapDataObject(export_bitmap(set, cards[0])));
}
// Conversion to serialized card format
Add(new CardsDataObject(set, cards), true);
// Conversion to text format
// TODO
//Add( new TextDataObject(_("card")))
// Conversion to bitmap format
if (cards.size() == 1) {
Add(new wxBitmapDataObject(export_bitmap(set, cards[0])));
}
// Conversion to serialized card format
Add(new CardsDataObject(set, cards), true);
}
+20 -20
View File
@@ -21,16 +21,16 @@ DECLARE_POINTER_TYPE(Keyword);
/// The data format for cards on the clipboard
class CardsDataObject : public wxTextDataObject {
public:
/// Name of the format of MSE cards
static wxDataFormat format;
CardsDataObject();
/// Store a card
CardsDataObject(const SetP& set, const vector<CardP>& cards);
/// Retrieve the cards, only if it is made with the same game as set
/** Return true if the cards are correctly retrieved, and there is at least one card */
bool getCards(const SetP& set, vector<CardP>& out);
/// Name of the format of MSE cards
static wxDataFormat format;
CardsDataObject();
/// Store a card
CardsDataObject(const SetP& set, const vector<CardP>& cards);
/// Retrieve the cards, only if it is made with the same game as set
/** Return true if the cards are correctly retrieved, and there is at least one card */
bool getCards(const SetP& set, vector<CardP>& out);
};
// ----------------------------------------------------------------------------- : KeywordDataObject
@@ -38,15 +38,15 @@ class CardsDataObject : public wxTextDataObject {
/// The data format for keywords on the clipboard
class KeywordDataObject : public wxTextDataObject {
public:
/// Name of the format of MSE keywords
static wxDataFormat format;
KeywordDataObject();
/// Store a keyword
KeywordDataObject(const SetP& set, const KeywordP& card);
/// Retrieve a keyword, only if it is made with the same game as set
KeywordP getKeyword(const SetP& set);
/// Name of the format of MSE keywords
static wxDataFormat format;
KeywordDataObject();
/// Store a keyword
KeywordDataObject(const SetP& set, const KeywordP& card);
/// Retrieve a keyword, only if it is made with the same game as set
KeywordP getKeyword(const SetP& set);
};
// ----------------------------------------------------------------------------- : Card on clipboard
@@ -54,7 +54,7 @@ class KeywordDataObject : public wxTextDataObject {
/// A DataObject for putting one or more cards on the clipboard, in multiple formats
class CardsOnClipboard : public wxDataObjectComposite {
public:
CardsOnClipboard(const SetP& set, const vector<CardP>& cards);
CardsOnClipboard(const SetP& set, const vector<CardP>& cards);
};
// ----------------------------------------------------------------------------- : EOF
+37 -37
View File
@@ -19,53 +19,53 @@ DECLARE_TYPEOF_COLLECTION(FileFormatP);
vector<FileFormatP> file_formats;
void init_file_formats() {
file_formats.push_back(mse2_file_format());
file_formats.push_back(mse1_file_format());
file_formats.push_back(mtg_editor_file_format());
file_formats.push_back(mse2_file_format());
file_formats.push_back(mse1_file_format());
file_formats.push_back(mtg_editor_file_format());
}
String import_formats() {
String all_extensions; // type1;type2
String type_strings; // |name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canImport()) {
if (!all_extensions.empty()) all_extensions += _(";");
all_extensions += f->matches();
type_strings += _("|") + f->name() + _("|") + f->matches();
}
}
return _("Set files|") + all_extensions + type_strings + _("|All files (*.*)|*");
String all_extensions; // type1;type2
String type_strings; // |name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canImport()) {
if (!all_extensions.empty()) all_extensions += _(";");
all_extensions += f->matches();
type_strings += _("|") + f->name() + _("|") + f->matches();
}
}
return _("Set files|") + all_extensions + type_strings + _("|All files (*.*)|*");
}
String export_formats(const Game& game) {
String type_strings; // name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canExport(game)) {
if (!type_strings.empty()) type_strings += _("|");
type_strings += f->name() + _("|") + f->matches();
}
}
return type_strings;
String type_strings; // name1|type1|name2|type2
FOR_EACH(f, file_formats) {
if (f->canExport(game)) {
if (!type_strings.empty()) type_strings += _("|");
type_strings += f->name() + _("|") + f->matches();
}
}
return type_strings;
}
void export_set(Set& set, const String& filename, size_t format_index, bool is_copy) {
FileFormatP format = file_formats.at(format_index);
if (!format->canExport(*set.game)) {
throw InternalError(_("File format doesn't apply to set"));
}
format->exportSet(set, filename, is_copy);
FileFormatP format = file_formats.at(format_index);
if (!format->canExport(*set.game)) {
throw InternalError(_("File format doesn't apply to set"));
}
format->exportSet(set, filename, is_copy);
}
SetP import_set(String name) {
size_t pos = name.find_last_of(_('.'));
String extension = pos==String::npos ? _("") : name.substr(pos + 1);
// determine format
FOR_EACH(f, file_formats) {
if (f->extension() == extension) {
return f->importSet(name);
}
}
// default: use first format = MSE2 format
assert(!file_formats.empty() && file_formats[0]->canImport());
return file_formats[0]->importSet(name);
size_t pos = name.find_last_of(_('.'));
String extension = pos==String::npos ? _("") : name.substr(pos + 1);
// determine format
FOR_EACH(f, file_formats) {
if (f->extension() == extension) {
return f->importSet(name);
}
}
// default: use first format = MSE2 format
assert(!file_formats.empty() && file_formats[0]->canImport());
return file_formats[0]->importSet(name);
}
+22 -22
View File
@@ -23,28 +23,28 @@ DECLARE_POINTER_TYPE(FileFormat);
/// A filter for a specific file format
class FileFormat : public IntrusivePtrVirtualBase {
public:
virtual ~FileFormat() {}
/// File extension used by this file format
virtual String extension() = 0;
/// What to match against
virtual String matches() {
return _("*.") + extension();
}
/// Name of the filter
virtual String name() = 0;
/// Can it be used for importing sets?
virtual bool canImport() = 0;
/// Can it be used for exporting sets for a particular game?
virtual bool canExport(const Game&) = 0;
/// Import using this filter
virtual SetP importSet(const String& filename) {
throw InternalError(_("Import not supported by this file format"));
}
/// Export using this filter
/** If is_copy, then the set should not be modified */
virtual void exportSet(Set& set, const String& filename, bool is_copy = false) {
throw InternalError(_("Export not supported by this file format"));
}
virtual ~FileFormat() {}
/// File extension used by this file format
virtual String extension() = 0;
/// What to match against
virtual String matches() {
return _("*.") + extension();
}
/// Name of the filter
virtual String name() = 0;
/// Can it be used for importing sets?
virtual bool canImport() = 0;
/// Can it be used for exporting sets for a particular game?
virtual bool canExport(const Game&) = 0;
/// Import using this filter
virtual SetP importSet(const String& filename) {
throw InternalError(_("Import not supported by this file format"));
}
/// Export using this filter
/** If is_copy, then the set should not be modified */
virtual void exportSet(Set& set, const String& filename, bool is_copy = false) {
throw InternalError(_("Export not supported by this file format"));
}
};
// ----------------------------------------------------------------------------- : Formats
+51 -51
View File
@@ -21,46 +21,46 @@ DECLARE_TYPEOF_COLLECTION(CardP);
// ----------------------------------------------------------------------------- : Single card export
void export_image(const SetP& set, const CardP& card, const String& filename) {
Image img = export_bitmap(set, card).ConvertToImage();
img.SaveFile(filename); // can't use Bitmap::saveFile, it wants to know the file type
// but image.saveFile determines it automagicly
Image img = export_bitmap(set, card).ConvertToImage();
img.SaveFile(filename); // can't use Bitmap::saveFile, it wants to know the file type
// but image.saveFile determines it automagicly
}
class UnzoomedDataViewer : public DataViewer {
public:
UnzoomedDataViewer(bool use_zoom_settings)
: use_zoom_settings(use_zoom_settings)
{}
virtual Rotation getRotation() const;
UnzoomedDataViewer(bool use_zoom_settings)
: use_zoom_settings(use_zoom_settings)
{}
virtual Rotation getRotation() const;
private:
bool use_zoom_settings;
bool use_zoom_settings;
};
Rotation UnzoomedDataViewer::getRotation() const {
if (use_zoom_settings) {
return DataViewer::getRotation();
} else {
if (!stylesheet) stylesheet = set->stylesheet;
return Rotation(0, stylesheet->getCardRect(), 1.0, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
if (use_zoom_settings) {
return DataViewer::getRotation();
} else {
if (!stylesheet) stylesheet = set->stylesheet;
return Rotation(0, stylesheet->getCardRect(), 1.0, 1.0, ROTATION_ATTACH_TOP_LEFT);
}
}
Bitmap export_bitmap(const SetP& set, const CardP& card) {
if (!set) throw Error(_("no set"));
// create viewer
UnzoomedDataViewer viewer(!settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export());
viewer.setSet(set);
viewer.setCard(card);
// size of cards
RealSize size = viewer.getRotation().getExternalSize();
// create bitmap & dc
Bitmap bitmap((int) size.width, (int) size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
if (!set) throw Error(_("no set"));
// create viewer
UnzoomedDataViewer viewer(!settings.stylesheetSettingsFor(set->stylesheetFor(card)).card_normal_export());
viewer.setSet(set);
viewer.setCard(card);
// size of cards
RealSize size = viewer.getRotation().getExternalSize();
// create bitmap & dc
Bitmap bitmap((int) size.width, (int) size.height);
if (!bitmap.Ok()) throw InternalError(_("Unable to create bitmap"));
wxMemoryDC dc;
dc.SelectObject(bitmap);
// draw
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bitmap;
}
// ----------------------------------------------------------------------------- : Multiple card export
@@ -69,25 +69,25 @@ Bitmap export_bitmap(const SetP& set, const CardP& card) {
void export_images(const SetP& set, const vector<CardP>& cards,
const String& path, const String& filename_template, FilenameConflicts conflicts)
{
wxBusyCursor busy;
// Script
ScriptP filename_script = parse(filename_template, nullptr, true);
// Path
wxFileName fn(path);
// Export
std::set<String> used; // for CONFLICT_NUMBER_OVERWRITE
FOR_EACH_CONST(card, cards) {
// filename for this card
Context& ctx = set->getContext(card);
String filename = clean_filename(untag(ctx.eval(*filename_script)->toString()));
if (!filename) continue; // no filename -> no saving
// full path
fn.SetFullName(filename);
// does the file exist?
if (!resolve_filename_conflicts(fn, conflicts, used)) continue;
// write image
filename = fn.GetFullPath();
used.insert(filename);
export_image(set, card, filename);
}
wxBusyCursor busy;
// Script
ScriptP filename_script = parse(filename_template, nullptr, true);
// Path
wxFileName fn(path);
// Export
std::set<String> used; // for CONFLICT_NUMBER_OVERWRITE
FOR_EACH_CONST(card, cards) {
// filename for this card
Context& ctx = set->getContext(card);
String filename = clean_filename(untag(ctx.eval(*filename_script)->toString()));
if (!filename) continue; // no filename -> no saving
// full path
fn.SetFullName(filename);
// does the file exist?
if (!resolve_filename_conflicts(fn, conflicts, used)) continue;
// write image
filename = fn.GetFullPath();
used.insert(filename);
export_image(set, card, filename);
}
}
+350 -350
View File
@@ -7,10 +7,10 @@
// ----------------------------------------------------------------------------- : Includes
#if defined(_MSC_VER) && _MSC_VER >= 1400
// VS 8 has the audacity to give a warning about fill_n
// That is both STUPID and WRONG, so disable that warning
// This has to be done before includes, because the warning is reported in standard headers!
#pragma warning(disable:4996)
// VS 8 has the audacity to give a warning about fill_n
// That is both STUPID and WRONG, so disable that warning
// This has to be done before includes, because the warning is reported in standard headers!
#pragma warning(disable:4996)
#endif
#include <util/prec.hpp>
@@ -25,9 +25,9 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : Image preprocessing
enum ImageMarker
{ EMPTY = 0 // This cell is empty
, FULL = 1 // This cell is full
, MARKED = 2 // This cell is full, but it has been used as a starting point for finding symbols
{ EMPTY = 0 // This cell is empty
, FULL = 1 // This cell is full
, MARKED = 2 // This cell is full, but it has been used as a starting point for finding symbols
};
@@ -37,13 +37,13 @@ enum ImageMarker
* is no longer an actual image.
*/
void greyscale(Image& img) {
UInt size = img.GetWidth() * img.GetHeight();
Byte* data = img.GetData();
Byte* out = data;
for (UInt i = 0 ; i < size ; ++i) {
*out++ = (data[0] + data[1] + data[2]) / 3;
data += 3;
}
UInt size = img.GetWidth() * img.GetHeight();
Byte* data = img.GetData();
Byte* out = data;
for (UInt i = 0 ; i < size ; ++i) {
*out++ = (data[0] + data[1] + data[2]) / 3;
data += 3;
}
}
/// Thresholds an image, giving a black & white result
@@ -52,192 +52,192 @@ void greyscale(Image& img) {
* EMPTY for the 'border' color, FULL for the interior
*/
void threshold(Byte* data, int w, int h) {
size_t size = w * h;
// make histogram of data
size_t hist[256];
fill_n(hist,256,0);
for (size_t i = 0 ; i < size ; ++i) {
hist[data[i]]++;
}
// find threshold
size_t threshold_pos = size / 2;
int threshold = 255;
size_t below = 0;
for (int i = 0 ; i < 255 ; ++i) {
if (below + hist[i]/2 > threshold_pos) {
threshold = i;
break;
}
below += hist[i];
if (below >= threshold_pos) {
threshold = i + 1;
break;
}
}
// threshold data
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] >= threshold ? FULL : EMPTY;
}
// should the colors be inverted?
int border_count = 0;
for (int x = 0 ; x < w ; ++x) {
border_count += data[x] + data[x+(h-1)*w];
}
for (int y = 0 ; y < h ; ++y) {
border_count += data[y*w] + data[w-1+y*w];
}
if (border_count > w + h) {
// more then half the border if FULL, invert
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] == FULL ? EMPTY : FULL;
}
}
size_t size = w * h;
// make histogram of data
size_t hist[256];
fill_n(hist,256,0);
for (size_t i = 0 ; i < size ; ++i) {
hist[data[i]]++;
}
// find threshold
size_t threshold_pos = size / 2;
int threshold = 255;
size_t below = 0;
for (int i = 0 ; i < 255 ; ++i) {
if (below + hist[i]/2 > threshold_pos) {
threshold = i;
break;
}
below += hist[i];
if (below >= threshold_pos) {
threshold = i + 1;
break;
}
}
// threshold data
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] >= threshold ? FULL : EMPTY;
}
// should the colors be inverted?
int border_count = 0;
for (int x = 0 ; x < w ; ++x) {
border_count += data[x] + data[x+(h-1)*w];
}
for (int y = 0 ; y < h ; ++y) {
border_count += data[y*w] + data[w-1+y*w];
}
if (border_count > w + h) {
// more then half the border if FULL, invert
for (size_t i = 0 ; i < size ; ++i) {
data[i] = data[i] == FULL ? EMPTY : FULL;
}
}
}
// ----------------------------------------------------------------------------- : Image to symbol
bool is_mse1_symbol(const Image& img) {
// mse1 symbols are 60x80
if (img.GetWidth() != 60 || img.GetHeight() != 80) return false;
// the right side is black & white
int delta = 0;
for (int y = 0 ; y < 80 ; ++y) {
Byte* d = img.GetData() + 3 * (y * 60 + 20);
for (int x = 20 ; x < 60 ; ++x) {
int r = *d++;
int g = *d++;
int b = *d++;
delta += abs(r - b) + abs(r - g) + abs(b - g);
}
}
if (delta > 5000) return false; // not black & white enough
// TODO : more checks?
return true;
// mse1 symbols are 60x80
if (img.GetWidth() != 60 || img.GetHeight() != 80) return false;
// the right side is black & white
int delta = 0;
for (int y = 0 ; y < 80 ; ++y) {
Byte* d = img.GetData() + 3 * (y * 60 + 20);
for (int x = 20 ; x < 60 ; ++x) {
int r = *d++;
int g = *d++;
int b = *d++;
delta += abs(r - b) + abs(r - g) + abs(b - g);
}
}
if (delta > 5000) return false; // not black & white enough
// TODO : more checks?
return true;
}
struct ImageData {
int width, height;
Byte* data;
mutable Byte dummy;
inline Byte& operator () (int x, int y) const {
if (x < 0 || x >= width || y < 0 || y >= height) {
return (dummy = EMPTY); // outside, return empty
} else {
return data[x + y*width];
}
}
int width, height;
Byte* data;
mutable Byte dummy;
inline Byte& operator () (int x, int y) const {
if (x < 0 || x >= width || y < 0 || y >= height) {
return (dummy = EMPTY); // outside, return empty
} else {
return data[x + y*width];
}
}
};
bool find_symbol_shape_start(const ImageData& data, int& x_out, int& y_out) {
for (int x = 0 ; x < data.width ; ++x) {
for (int y = 0 ; y < data.height ; ++y) {
if (data(x, y) == FULL && data(x, y-1) == EMPTY) {
// the point above must be clear, we don't want to start in the 'ground'
// also, we don't want to find things we found before
x_out = x;
y_out = y;
return true;
}
}
}
return false;
for (int x = 0 ; x < data.width ; ++x) {
for (int y = 0 ; y < data.height ; ++y) {
if (data(x, y) == FULL && data(x, y-1) == EMPTY) {
// the point above must be clear, we don't want to start in the 'ground'
// also, we don't want to find things we found before
x_out = x;
y_out = y;
return true;
}
}
}
return false;
}
SymbolShapeP read_symbol_shape(const ImageData& data) {
// find start point
int xs, ys;
if (!find_symbol_shape_start(data, xs, ys)) return SymbolShapeP();
data(xs, ys) |= MARKED;
SymbolShapeP shape(new SymbolShape);
// walk around, clockwise
xs += 1; // start right of the found point, otherwise last_move might think we came from above
int x = xs, y = ys;
int old_x = x, old_y = y;
int last_move = 1; // 1 = right or down, (as in x|y += 1)
do {
// the cursor (x,y) is between four pounts:
// a b
// .
// c d
bool a = data(x-1, y-1) & FULL;
bool b = data(x, y-1) & FULL;
bool c = data(x-1, y ) & FULL;
bool d = data(x, y ) & FULL;
UInt pack = (a << 12) + (b << 8) + (c << 4) + d; // 0xabcd
switch (pack) {
case 0x0001 : x += 1; break;
case 0x0010 : y += 1; break;
case 0x0011 : x += 1; break;
case 0x0100 : y -= 1; break;
case 0x0101 : y -= 1; break;
case 0x0110 : y -= last_move; break; // diagonal, we can come here from two sides, from left and right
case 0x0111 : y -= 1; break; // last_move indicates which of {b,c} we are 'attached' to
case 0x1000 : x -= 1; break;
case 0x1001 : x += last_move; break;
case 0x1010 : y += 1; break;
case 0x1011 : x += 1; break;
case 0x1100 : x -= 1; break;
case 0x1101 : x -= 1; break;
case 0x1110 : y += 1; break;
default:
throw InternalError(_("in the ground/air"));
}
// add to shape and place a mark
shape->points.push_back(intrusive(new ControlPoint(
double(x) / data.width,
double(y) / data.height
)));
if (x > old_x) data(old_x, y) |= MARKED; // mark when moving right -> only mark the top of the shape
last_move = (x + y) - (old_x + old_y);
old_x = x;
old_y = y;
} while (x != xs || y != ys); // we will end up in the start point
// are we on the inside or the outside?
if (data(x-2,y-1) & FULL) {
shape->combine = SYMBOL_COMBINE_SUBTRACT;
} else {
shape->combine = SYMBOL_COMBINE_MERGE;
}
return shape;
// find start point
int xs, ys;
if (!find_symbol_shape_start(data, xs, ys)) return SymbolShapeP();
data(xs, ys) |= MARKED;
SymbolShapeP shape(new SymbolShape);
// walk around, clockwise
xs += 1; // start right of the found point, otherwise last_move might think we came from above
int x = xs, y = ys;
int old_x = x, old_y = y;
int last_move = 1; // 1 = right or down, (as in x|y += 1)
do {
// the cursor (x,y) is between four pounts:
// a b
// .
// c d
bool a = data(x-1, y-1) & FULL;
bool b = data(x, y-1) & FULL;
bool c = data(x-1, y ) & FULL;
bool d = data(x, y ) & FULL;
UInt pack = (a << 12) + (b << 8) + (c << 4) + d; // 0xabcd
switch (pack) {
case 0x0001 : x += 1; break;
case 0x0010 : y += 1; break;
case 0x0011 : x += 1; break;
case 0x0100 : y -= 1; break;
case 0x0101 : y -= 1; break;
case 0x0110 : y -= last_move; break; // diagonal, we can come here from two sides, from left and right
case 0x0111 : y -= 1; break; // last_move indicates which of {b,c} we are 'attached' to
case 0x1000 : x -= 1; break;
case 0x1001 : x += last_move; break;
case 0x1010 : y += 1; break;
case 0x1011 : x += 1; break;
case 0x1100 : x -= 1; break;
case 0x1101 : x -= 1; break;
case 0x1110 : y += 1; break;
default:
throw InternalError(_("in the ground/air"));
}
// add to shape and place a mark
shape->points.push_back(intrusive(new ControlPoint(
double(x) / data.width,
double(y) / data.height
)));
if (x > old_x) data(old_x, y) |= MARKED; // mark when moving right -> only mark the top of the shape
last_move = (x + y) - (old_x + old_y);
old_x = x;
old_y = y;
} while (x != xs || y != ys); // we will end up in the start point
// are we on the inside or the outside?
if (data(x-2,y-1) & FULL) {
shape->combine = SYMBOL_COMBINE_SUBTRACT;
} else {
shape->combine = SYMBOL_COMBINE_MERGE;
}
return shape;
}
SymbolP image_to_symbol(Image& img) {
int w = img.GetWidth(), h = img.GetHeight();
// 1. threshold the image
greyscale(img);
threshold(img.GetData(), w, h);
// 2. read as many symbol shapes as we can
ImageData data = {w,h,img.GetData()};
SymbolP symbol(new Symbol);
while (true) {
SymbolShapeP shape = read_symbol_shape(data);
if (!shape) break;
symbol->parts.push_back(shape);
}
reverse(symbol->parts.begin(), symbol->parts.end());
return symbol;
int w = img.GetWidth(), h = img.GetHeight();
// 1. threshold the image
greyscale(img);
threshold(img.GetData(), w, h);
// 2. read as many symbol shapes as we can
ImageData data = {w,h,img.GetData()};
SymbolP symbol(new Symbol);
while (true) {
SymbolShapeP shape = read_symbol_shape(data);
if (!shape) break;
symbol->parts.push_back(shape);
}
reverse(symbol->parts.begin(), symbol->parts.end());
return symbol;
}
SymbolP import_symbol(Image& img) {
SymbolP symbol;
if (is_mse1_symbol(img)) {
Image img2 = img.GetSubImage(wxRect(20,0,40,40));
symbol = image_to_symbol(img2);
} else if (img.GetWidth() > 100 || img.GetHeight() > 100) {
// 100x100 ought to be enough, we trow out most afterwards data anyway
Image resampled = img.Rescale(100,100);
symbol = image_to_symbol(resampled);
} else {
symbol = image_to_symbol(img);
}
simplify_symbol(*symbol);
return symbol;
SymbolP symbol;
if (is_mse1_symbol(img)) {
Image img2 = img.GetSubImage(wxRect(20,0,40,40));
symbol = image_to_symbol(img2);
} else if (img.GetWidth() > 100 || img.GetHeight() > 100) {
// 100x100 ought to be enough, we trow out most afterwards data anyway
Image resampled = img.Rescale(100,100);
symbol = image_to_symbol(resampled);
} else {
symbol = image_to_symbol(img);
}
simplify_symbol(*symbol);
return symbol;
}
@@ -247,19 +247,19 @@ SymbolP import_symbol(Image& img) {
/** A corner is a point that has an angle between tangent greater then a treshold
*/
void mark_corners(SymbolShape& shape) {
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& current = *shape.getPoint(i);
Vector2D before = .6 * shape.getPoint(i-1)->pos + .2 * shape.getPoint(i-2)->pos + .1 * shape.getPoint(i-3)->pos + .1 * shape.getPoint(i-4)->pos;
Vector2D after = .6 * shape.getPoint(i+1)->pos + .2 * shape.getPoint(i+2)->pos + .1 * shape.getPoint(i+3)->pos + .1 * shape.getPoint(i+4)->pos;
before = (before - current.pos).normalized();
after = (after - current.pos).normalized();
if (dot(before,after) >= -0.25f) {
// corner
current.lock = LOCK_FREE;
} else {
current.lock = LOCK_DIR;
}
}
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& current = *shape.getPoint(i);
Vector2D before = .6 * shape.getPoint(i-1)->pos + .2 * shape.getPoint(i-2)->pos + .1 * shape.getPoint(i-3)->pos + .1 * shape.getPoint(i-4)->pos;
Vector2D after = .6 * shape.getPoint(i+1)->pos + .2 * shape.getPoint(i+2)->pos + .1 * shape.getPoint(i+3)->pos + .1 * shape.getPoint(i+4)->pos;
before = (before - current.pos).normalized();
after = (after - current.pos).normalized();
if (dot(before,after) >= -0.25f) {
// corner
current.lock = LOCK_FREE;
} else {
current.lock = LOCK_DIR;
}
}
}
/// Merge adjacent corners
@@ -280,116 +280,116 @@ void mark_corners(SymbolShape& shape) {
* is the merged corner. If it is too far away, don't merge
*/
void merge_corners(SymbolShape& shape) {
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i - 1);
if (prev.lock != LOCK_FREE || cur.lock != LOCK_FREE) continue;
// step 1. find tangent lines: try tangent lines to the first point, the second, etc.
// and take the one that has the largest angle with ab, i.e. the smallest dot,
// where ab is the line between the two corners
Vector2D ab = cur.pos - prev.pos;
double min_a_dot = 1e100, min_b_dot = 1e100;
Vector2D a, b;
for (int j = 0 ; j < 4 ; ++j) {
Vector2D a_ = (shape.getPoint(i-j-1)->pos - prev.pos).normalized();
Vector2D b_ = (shape.getPoint(i+j)->pos - cur.pos).normalized();
double a_dot = dot(a_, ab);
double b_dot = -dot(b_, ab);
if (a_dot < min_a_dot) {
min_a_dot = a_dot;
a = a_;
}
if (b_dot < min_b_dot) {
min_b_dot = b_dot;
b = b_;
}
}
// step 2. find intersection point, to solve:
// t a + ab = u b, solve for t,u
// Gives us:
// t = ab cross b / b cross a
double tden = max(0.00000001, cross(b,a));
double t = cross(ab,b) / tden;
// do these tangent lines intersect, and not too far away?
// if so, then the intersection point is the merged point
if (t >= 0 && t < 20.0) {
prev.pos += a * -t;
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i - 1);
if (prev.lock != LOCK_FREE || cur.lock != LOCK_FREE) continue;
// step 1. find tangent lines: try tangent lines to the first point, the second, etc.
// and take the one that has the largest angle with ab, i.e. the smallest dot,
// where ab is the line between the two corners
Vector2D ab = cur.pos - prev.pos;
double min_a_dot = 1e100, min_b_dot = 1e100;
Vector2D a, b;
for (int j = 0 ; j < 4 ; ++j) {
Vector2D a_ = (shape.getPoint(i-j-1)->pos - prev.pos).normalized();
Vector2D b_ = (shape.getPoint(i+j)->pos - cur.pos).normalized();
double a_dot = dot(a_, ab);
double b_dot = -dot(b_, ab);
if (a_dot < min_a_dot) {
min_a_dot = a_dot;
a = a_;
}
if (b_dot < min_b_dot) {
min_b_dot = b_dot;
b = b_;
}
}
// step 2. find intersection point, to solve:
// t a + ab = u b, solve for t,u
// Gives us:
// t = ab cross b / b cross a
double tden = max(0.00000001, cross(b,a));
double t = cross(ab,b) / tden;
// do these tangent lines intersect, and not too far away?
// if so, then the intersection point is the merged point
if (t >= 0 && t < 20.0) {
prev.pos += a * -t;
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
}
/// Avarage/'blur' a symbol shape
void avarage(SymbolShape& shape) {
// create a copy of the points
vector<Vector2D> old_points;
FOR_EACH(p, shape.points) {
old_points.push_back(p->pos);
}
// avarage points
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& p = *shape.getPoint(i);
if (p.lock == LOCK_DIR) {
p.pos = .25 * old_points[mod(i-1, old_points.size())]
+ .50 * p.pos
+ .25 * old_points[mod(i+1, old_points.size())];
}
}
// create a copy of the points
vector<Vector2D> old_points;
FOR_EACH(p, shape.points) {
old_points.push_back(p->pos);
}
// avarage points
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& p = *shape.getPoint(i);
if (p.lock == LOCK_DIR) {
p.pos = .25 * old_points[mod(i-1, old_points.size())]
+ .50 * p.pos
+ .25 * old_points[mod(i+1, old_points.size())];
}
}
}
/// Convert a symbol shape to curves
void convert_to_curves(SymbolShape& shape) {
// mark all segments as curves
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
cur.segment_after = SEGMENT_CURVE;
cur.segment_before = SEGMENT_CURVE;
cur.delta_after = (next.pos - cur.pos) / 3.0;
next.delta_before = (cur.pos - next.pos) / 3.0;
}
// make the curves smooth by enforcing direction constraints
FOR_EACH(p, shape.points) {
p->onUpdateLock();
}
// mark all segments as curves
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
cur.segment_after = SEGMENT_CURVE;
cur.segment_before = SEGMENT_CURVE;
cur.delta_after = (next.pos - cur.pos) / 3.0;
next.delta_before = (cur.pos - next.pos) / 3.0;
}
// make the curves smooth by enforcing direction constraints
FOR_EACH(p, shape.points) {
p->onUpdateLock();
}
}
/// Convert almost straight curves in a symbol shape to lines
void straighten(SymbolShape& shape) {
const double treshold = 0.2;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
Vector2D ab = (next.pos - cur.pos).normalized();
Vector2D aa = cur.delta_after.normalized();
Vector2D bb = next.delta_before.normalized();
// if the area beneath the polygon formed by the handles is small
// then it is a straight line
double cpDot = abs(cross(aa,ab)) + abs(cross(bb,ab));
if (cpDot < treshold) {
cur.segment_after = next.segment_before = SEGMENT_LINE;
cur.delta_after = next.delta_before = Vector2D();
cur.lock = next.lock = LOCK_FREE;
}
}
const double treshold = 0.2;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& next = *shape.getPoint(i + 1);
Vector2D ab = (next.pos - cur.pos).normalized();
Vector2D aa = cur.delta_after.normalized();
Vector2D bb = next.delta_before.normalized();
// if the area beneath the polygon formed by the handles is small
// then it is a straight line
double cpDot = abs(cross(aa,ab)) + abs(cross(bb,ab));
if (cpDot < treshold) {
cur.segment_after = next.segment_before = SEGMENT_LINE;
cur.delta_after = next.delta_before = Vector2D();
cur.lock = next.lock = LOCK_FREE;
}
}
}
/// Remove unneeded points between straight lines
void merge_lines(SymbolShape& shape) {
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
if (cur.segment_before != cur.segment_after) continue;
Vector2D a = shape.getPoint(i-1)->pos, b = cur.pos, c = shape.getPoint(i+1)->pos;
Vector2D ab = (a-b).normalized();
Vector2D bc = (b-c).normalized();
double angle_len = abs( atan2(ab.x,ab.y) - atan2(bc.x,bc.y)) * (a-c).lengthSqr();
bool keep = angle_len >= .0001;
if (!keep) {
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
ControlPoint& cur = *shape.getPoint(i);
if (cur.segment_before != cur.segment_after) continue;
Vector2D a = shape.getPoint(i-1)->pos, b = cur.pos, c = shape.getPoint(i+1)->pos;
Vector2D ab = (a-b).normalized();
Vector2D bc = (b-c).normalized();
double angle_len = abs( atan2(ab.x,ab.y) - atan2(bc.x,bc.y)) * (a-c).lengthSqr();
bool keep = angle_len >= .0001;
if (!keep) {
shape.points.erase(shape.points.begin() + i);
i -= 1;
}
}
}
double cost_of_point_removal(SymbolShape& shape, int i);
@@ -400,83 +400,83 @@ void remove_point(SymbolShape& shape, int i);
* stop when the cost becomes too high
*/
void remove_points(SymbolShape& shape) {
const double treshold = 0.0002; // maximum cost
while (true) {
// Find the point with the lowest cost of removal
int best = -1;
double best_cost = 1e100;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
double cost = cost_of_point_removal(shape, i);
if (cost < best_cost) {
best_cost = cost;
best = i;
}
}
if (best_cost > treshold) break;
// ... and remove it
remove_point(shape, best);
}
const double treshold = 0.0002; // maximum cost
while (true) {
// Find the point with the lowest cost of removal
int best = -1;
double best_cost = 1e100;
for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) {
double cost = cost_of_point_removal(shape, i);
if (cost < best_cost) {
best_cost = cost;
best = i;
}
}
if (best_cost > treshold) break;
// ... and remove it
remove_point(shape, best);
}
}
/// Cost of removing point i from a symbol shape
double cost_of_point_removal(SymbolShape& shape, int i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
if (cur.lock != LOCK_DIR) return 1e100; // don't remove corners
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
Vector2D ac = prev.pos - next.pos;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
Vector2D after0 = prev.delta_after * totl / bl;
Vector2D before2 = next.delta_before * totl / al;
// determine closest point on the merged curve
BezierCurve c(prev.pos, prev.pos + after0, next.pos + before2, next.pos);
double t = bl/totl;
Vector2D np = cur.pos - c.pointAt(t);
// cost is distance to new point * length of line ~= area added/removed from shape
return np.length() * ac.length();
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
if (cur.lock != LOCK_DIR) return 1e100; // don't remove corners
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
Vector2D ac = prev.pos - next.pos;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
Vector2D after0 = prev.delta_after * totl / bl;
Vector2D before2 = next.delta_before * totl / al;
// determine closest point on the merged curve
BezierCurve c(prev.pos, prev.pos + after0, next.pos + before2, next.pos);
double t = bl/totl;
Vector2D np = cur.pos - c.pointAt(t);
// cost is distance to new point * length of line ~= area added/removed from shape
return np.length() * ac.length();
}
/// Remove a point from a bezier curve
/** See SinglePointRemoveAction for algorithm */
void remove_point(SymbolShape& shape, int i) {
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
prev.delta_after *= totl / bl;
next.delta_before *= totl / al;
// remove
shape.points.erase(shape.points.begin() + i);
ControlPoint& cur = *shape.getPoint(i);
ControlPoint& prev = *shape.getPoint(i-1);
ControlPoint& next = *shape.getPoint(i+1);
Vector2D before = cur.delta_before;
Vector2D after = cur.delta_after;
// Based on SinglePointRemoveAction
double bl = before.length() + 0.00001; // prevent division by 0
double al = after.length() + 0.00001;
double totl = bl + al;
// set new handle sizes
prev.delta_after *= totl / bl;
next.delta_before *= totl / al;
// remove
shape.points.erase(shape.points.begin() + i);
}
void simplify_symbol_shape(SymbolShape& shape) {
mark_corners(shape);
merge_corners(shape);
for (int i = 0 ; i < 3 ; ++i) {
avarage(shape);
}
convert_to_curves(shape);
remove_points(shape);
straighten(shape);
merge_lines(shape);
mark_corners(shape);
merge_corners(shape);
for (int i = 0 ; i < 3 ; ++i) {
avarage(shape);
}
convert_to_curves(shape);
remove_points(shape);
straighten(shape);
merge_lines(shape);
}
void simplify_symbol(Symbol& symbol) {
FOR_EACH(pb, symbol.parts) {
if (SymbolShape* p = pb->isSymbolShape()) {
simplify_symbol_shape(*p);
}
}
FOR_EACH(pb, symbol.parts) {
if (SymbolShape* p = pb->isSymbolShape()) {
simplify_symbol_shape(*p);
}
}
}
+133 -133
View File
@@ -22,15 +22,15 @@
/// The file format of MSE1 files
class MSE1FileFormat : public FileFormat {
public:
virtual String extension() { return _("mse"); }
virtual String name() { return _("Magic Set Editor version 1 files (*.mse)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
virtual String extension() { return _("mse"); }
virtual String name() { return _("Magic Set Editor version 1 files (*.mse)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
};
FileFormatP mse1_file_format() {
return intrusive(new MSE1FileFormat());
return intrusive(new MSE1FileFormat());
}
// ----------------------------------------------------------------------------- : Importing
@@ -39,134 +39,134 @@ FileFormatP mse1_file_format() {
void read_mse1_card(Set& set, wxFileInputStream& f, wxTextInputStream& file);
SetP MSE1FileFormat::importSet(const String& filename) {
wxFileInputStream f(filename);
#ifdef UNICODE
wxTextInputStream file(f, _('\n'), wxConvLibc);
#else
wxTextInputStream file(f);
#endif
// create set
SetP set(new Set(Game::byName(_("magic"))));
// file version check
String format = file.ReadLine();
if (format.substr(0,8) != _("MTG Set8")) {
throw ParseError(_("Expected MSE format version 8\nTo convert files made with older versions of Magic Set Editor:\n 1. Download the latest version 1 from http:;//magicsetedtitor.sourceforge.net\n 2. Open the set, then save the set\n 3. Try to open them again in this program."));
}
// read general info
set->value<TextValue>(_("title")) .value = file.ReadLine();
set->value<TextValue>(_("artist")) .value = file.ReadLine();
set->value<TextValue>(_("copyright")).value = file.ReadLine();
file.ReadLine(); // border color, ignored
String stylesheet = file.ReadLine();
set->apprentice_code = file.ReadLine(); // apprentice prefix
file.ReadLine(); // 'formatN'?, not even used by MSE1 :S, ignored
file.ReadLine(); // 'formatS'?, same, ignored
file.ReadLine(); // symbol filename, ignored
file.ReadLine(); // use black symbol for all rarities, ignored
String desc, line;
while (!f.Eof()) {
line = file.ReadLine();
if (line == _("\xFF")) break;
desc += line;
}
set->value<TextValue>(_("description")).value = desc;
// load stylesheet
if (stylesheet.substr(0,3) == _("old")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
// read cards
CardP current_card;
while (!f.Eof()) {
read_mse1_card(*set, f, file);
}
// done
set->validate();
return set;
wxFileInputStream f(filename);
#ifdef UNICODE
wxTextInputStream file(f, _('\n'), wxConvLibc);
#else
wxTextInputStream file(f);
#endif
// create set
SetP set(new Set(Game::byName(_("magic"))));
// file version check
String format = file.ReadLine();
if (format.substr(0,8) != _("MTG Set8")) {
throw ParseError(_("Expected MSE format version 8\nTo convert files made with older versions of Magic Set Editor:\n 1. Download the latest version 1 from http:;//magicsetedtitor.sourceforge.net\n 2. Open the set, then save the set\n 3. Try to open them again in this program."));
}
// read general info
set->value<TextValue>(_("title")) .value = file.ReadLine();
set->value<TextValue>(_("artist")) .value = file.ReadLine();
set->value<TextValue>(_("copyright")).value = file.ReadLine();
file.ReadLine(); // border color, ignored
String stylesheet = file.ReadLine();
set->apprentice_code = file.ReadLine(); // apprentice prefix
file.ReadLine(); // 'formatN'?, not even used by MSE1 :S, ignored
file.ReadLine(); // 'formatS'?, same, ignored
file.ReadLine(); // symbol filename, ignored
file.ReadLine(); // use black symbol for all rarities, ignored
String desc, line;
while (!f.Eof()) {
line = file.ReadLine();
if (line == _("\xFF")) break;
desc += line;
}
set->value<TextValue>(_("description")).value = desc;
// load stylesheet
if (stylesheet.substr(0,3) == _("old")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
// read cards
CardP current_card;
while (!f.Eof()) {
read_mse1_card(*set, f, file);
}
// done
set->validate();
return set;
}
void read_mse1_card(Set& set, wxFileInputStream& f, wxTextInputStream& file) {
CardP card(new Card(*set.game));
while (!f.Eof()) {
// read a line
String line = file.ReadLine();
if (line.empty()) continue;
Char type = line.GetChar(0);
line = line.substr(1);
// interpret this line
switch (type) {
case 'A': { // done
set.cards.push_back(card);
return;
} case 'B': { // name
card->value<TextValue>(_("name")) .value.assign(line);
break;
} case 'C': case 'D': { // image filename
String image_file = set.newFileName(_("image"),_("")); // a new unique name in the package
if (wxCopyFile(line, set.nameOut(image_file), true)) {
card->value<ImageValue>(_("image")) .filename = image_file;
}
break;
} case 'E': { // super type
card->value<TextValue>(_("super type")) .value.assign(line);
break;
} case 'F': { // sub type
card->value<TextValue>(_("sub type")) .value.assign(line);
break;
} case 'G': { // casting cost
card->value<TextValue>(_("casting cost")).value.assign(line);
break;
} case 'H': { // rarity
String rarity;
if (line == _("(U)")) rarity = _("uncommon");
else if (line == _("(R)")) rarity = _("rare");
else rarity = _("common");
card->value<ChoiceValue>(_("rarity")) .value.assign(rarity);
break;
} case 'I': { // power/thoughness
size_t pos = line.find_first_of(_('/'));
if (pos != String::npos) {
card->value<TextValue>(_("power")) .value.assign(line.substr(0, pos));
card->value<TextValue>(_("toughness")) .value.assign(line.substr(pos+1));
}
break;
} case 'J': { // rule text or part of text
Defaultable<String>& text = card->value<TextValue>(_("rule text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'K': { // flavor text or part of text
Defaultable<String>& text = card->value<TextValue>(_("flavor text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'L': { // card color (if not default)
// decode color
String color;
if (line == _("1")) color = _("white");
else if (line == _("2")) color = _("blue");
else if (line == _("3")) color = _("black");
else if (line == _("4")) color = _("red");
else if (line == _("5")) color = _("green");
else if (line == _("6")) color = _("colorless");
else if (line == _("7")) color = _("land");
else if (line == _("9")) color = _("multicolor");
else color = _("colorless");
card->value<ChoiceValue>(_("card color")).value.assign(color);
break;
} default: {
throw ParseError(_("Not a valid MSE1 file"));
}
}
}
CardP card(new Card(*set.game));
while (!f.Eof()) {
// read a line
String line = file.ReadLine();
if (line.empty()) continue;
Char type = line.GetChar(0);
line = line.substr(1);
// interpret this line
switch (type) {
case 'A': { // done
set.cards.push_back(card);
return;
} case 'B': { // name
card->value<TextValue>(_("name")) .value.assign(line);
break;
} case 'C': case 'D': { // image filename
String image_file = set.newFileName(_("image"),_("")); // a new unique name in the package
if (wxCopyFile(line, set.nameOut(image_file), true)) {
card->value<ImageValue>(_("image")) .filename = image_file;
}
break;
} case 'E': { // super type
card->value<TextValue>(_("super type")) .value.assign(line);
break;
} case 'F': { // sub type
card->value<TextValue>(_("sub type")) .value.assign(line);
break;
} case 'G': { // casting cost
card->value<TextValue>(_("casting cost")).value.assign(line);
break;
} case 'H': { // rarity
String rarity;
if (line == _("(U)")) rarity = _("uncommon");
else if (line == _("(R)")) rarity = _("rare");
else rarity = _("common");
card->value<ChoiceValue>(_("rarity")) .value.assign(rarity);
break;
} case 'I': { // power/thoughness
size_t pos = line.find_first_of(_('/'));
if (pos != String::npos) {
card->value<TextValue>(_("power")) .value.assign(line.substr(0, pos));
card->value<TextValue>(_("toughness")) .value.assign(line.substr(pos+1));
}
break;
} case 'J': { // rule text or part of text
Defaultable<String>& text = card->value<TextValue>(_("rule text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'K': { // flavor text or part of text
Defaultable<String>& text = card->value<TextValue>(_("flavor text")).value;
if (!text().empty()) text.mutate() += _('\n');
text.mutate() += line;
break;
} case 'L': { // card color (if not default)
// decode color
String color;
if (line == _("1")) color = _("white");
else if (line == _("2")) color = _("blue");
else if (line == _("3")) color = _("black");
else if (line == _("4")) color = _("red");
else if (line == _("5")) color = _("green");
else if (line == _("6")) color = _("colorless");
else if (line == _("7")) color = _("land");
else if (line == _("9")) color = _("multicolor");
else color = _("colorless");
card->value<ChoiceValue>(_("card color")).value.assign(color);
break;
} default: {
throw ParseError(_("Not a valid MSE1 file"));
}
}
}
}
+27 -27
View File
@@ -16,34 +16,34 @@
/// The file format of MSE2 files
class MSE2FileFormat : public FileFormat {
public:
virtual String extension() { return _("mse-set"); }
virtual String matches() { return _("*.mse-set;set"); }
virtual String name() { return _("Magic Set Editor sets (*.mse-set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return true; }
virtual SetP importSet(const String& filename) {
wxString set_name = filename;
// Strip "/set" from the end, newer wx versions have a function for this:
// filename.EndsWith(_("/set"), &set_name);
if (filename.size() > 4 && filename.substr(filename.size()-4) == _("/set")) {
set_name = filename.substr(0, filename.size()-4);
}
SetP set(new Set);
set->open(set_name);
settings.addRecentFile(set_name);
return set;
}
virtual void exportSet(Set& set, const String& filename, bool is_copy) {
if (is_copy) {
set.saveCopy(filename);
} else {
set.saveAs(filename);
settings.addRecentFile(filename);
set.actions.setSavePoint();
}
}
virtual String extension() { return _("mse-set"); }
virtual String matches() { return _("*.mse-set;set"); }
virtual String name() { return _("Magic Set Editor sets (*.mse-set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return true; }
virtual SetP importSet(const String& filename) {
wxString set_name = filename;
// Strip "/set" from the end, newer wx versions have a function for this:
// filename.EndsWith(_("/set"), &set_name);
if (filename.size() > 4 && filename.substr(filename.size()-4) == _("/set")) {
set_name = filename.substr(0, filename.size()-4);
}
SetP set(new Set);
set->open(set_name);
settings.addRecentFile(set_name);
return set;
}
virtual void exportSet(Set& set, const String& filename, bool is_copy) {
if (is_copy) {
set.saveCopy(filename);
} else {
set.saveAs(filename);
settings.addRecentFile(filename);
set.actions.setSavePoint();
}
}
};
FileFormatP mse2_file_format() {
return intrusive(new MSE2FileFormat());
return intrusive(new MSE2FileFormat());
}
+225 -225
View File
@@ -25,253 +25,253 @@ DECLARE_TYPEOF_COLLECTION(CardP);
/// The file format of Mtg Editor files
class MtgEditorFileFormat : public FileFormat {
public:
virtual String extension() { return _("set"); }
virtual String name() { return _("Mtg Editor files (*.set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
virtual String extension() { return _("set"); }
virtual String name() { return _("Mtg Editor files (*.set)"); }
virtual bool canImport() { return true; }
virtual bool canExport(const Game&) { return false; }
virtual SetP importSet(const String& filename);
private:
// Filter: se filename -> image directory
// based on MtgEditor's: CardSet.getImageFolder
String filter1(const String& str);
// Filter: card name -> image name
// based on MtgEditor's: Tools::purgeSpecialChars
String filter2(const String& str);
// Remove mtg editor tags
void untag(const CardP& card, const String& field);
// Translate all tags, mana tags get converted to <sym>, other tags are removed
void translateTags(String& value);
// Filter: se filename -> image directory
// based on MtgEditor's: CardSet.getImageFolder
String filter1(const String& str);
// Filter: card name -> image name
// based on MtgEditor's: Tools::purgeSpecialChars
String filter2(const String& str);
// Remove mtg editor tags
void untag(const CardP& card, const String& field);
// Translate all tags, mana tags get converted to <sym>, other tags are removed
void translateTags(String& value);
};
FileFormatP mtg_editor_file_format() {
return intrusive(new MtgEditorFileFormat());
return intrusive(new MtgEditorFileFormat());
}
// ----------------------------------------------------------------------------- : Importing
SetP MtgEditorFileFormat::importSet(const String& filename) {
wxFileInputStream f(filename);
#ifdef UNICODE
wxTextInputStream file(f, _('\n'), wxConvLibc);
#else
wxTextInputStream file(f);
#endif
// create set
SetP set(new Set(Game::byName(_("magic"))));
// parsing state
CardP current_card;
Defaultable<String>* target = nullptr; // value we are currently reading
String layout = _("8e");
String set_date, card_date;
bool first = true;
// read file
while (!f.Eof()) {
// read a line
if (!current_card) current_card = intrusive(new Card(*set->game));
String line = file.ReadLine();
if (line == _("#SET###########")) { // set.title
target = &set->value<TextValue>(_("title")).value;
} else if (line == _("#SETDATE#######")) { // date
// remember date for generation of illustration filename
target = nullptr;
set_date = file.ReadLine();
} else if (line == _("#SHOWRARITY####") ||
line == _("#FONTS#########") || line == _("#COUNT#########")) { // ignore
target = nullptr;
file.ReadLine();
} else if (line == _("#LAYOUT########")) { // layout type
target = nullptr;
layout = file.ReadLine();
} else if (line == _("#CARD##########") || line == _("#EOF###########")) { // begin/end of card
if (!first) {
// First [CARD##] indicates only the start of a card, subsequent ones also the end of the previous
// card. We only care about the latter
// remove all tags from all text values
untag(current_card, _("name"));
untag(current_card, _("super type"));
untag(current_card, _("sub type"));
untag(current_card, _("casting cost"));
untag(current_card, _("flavor text"));
untag(current_card, _("illustrator"));
untag(current_card, _("copyright"));
untag(current_card, _("power"));
untag(current_card, _("toughness"));
// translate mtg editor tags to mse2 tags
translateTags(current_card->value<TextValue>(_("rule text")).value.mutate());
// add the card to the set
set->cards.push_back(current_card);
}
first = false;
current_card = intrusive(new Card(*set->game));
target = &current_card->value<TextValue>(_("name")).value;
} else if (line == _("#DATE##########")) { // date
// remember date for generation of illustration filename
target = nullptr;
card_date = file.ReadLine();
} else if (line == _("#TYPE##########")) { // super type
target = &current_card->value<TextValue>(_("super type")).value;
} else if (line == _("#SUBTYPE#######")) { // sub type
target = &current_card->value<TextValue>(_("sub type")).value;
} else if (line == _("#COST##########")) { // casting cost
target = &current_card->value<TextValue>(_("casting cost")).value;
} else if (line == _("#RARITY########") || line == _("#FREQUENCY#####")) { // rarity
target = 0;
line = file.ReadLine();
if (line == _("0")) current_card->value<ChoiceValue>(_("rarity")).value.assign(_("common"));
else if (line == _("1")) current_card->value<ChoiceValue>(_("rarity")).value.assign(_("uncommon"));
else current_card->value<ChoiceValue>(_("rarity")).value.assign(_("rare"));
} else if (line == _("#COLOR#########")) { // card color
target = 0;
line = file.ReadLine();
current_card->value<ChoiceValue>(_("card color")).value.assign(line);
} else if (line == _("#AUTOBG########")) { // card color.isDefault
target = 0;
line = file.ReadLine();
if (line == _("TRUE")) {
current_card->value<ChoiceValue>(_("card color")).value.makeDefault();
}
} else if (line == _("#RULETEXT######")) { // rule text
target = &current_card->value<TextValue>(_("rule text")).value;
} else if (line == _("#FLAVORTEXT####")) { // flavor text
target = &current_card->value<TextValue>(_("flavor text")).value;
} else if (line == _("#ARTIST########")) { // illustrator
target = &current_card->value<TextValue>(_("illustrator")).value;
} else if (line == _("#COPYRIGHT#####")) { // copyright
target = &current_card->value<TextValue>(_("copyright")).value;
} else if (line == _("#POWER#########")) { // power
target = &current_card->value<TextValue>(_("power")).value;
} else if (line == _("#TOUGHNESS#####")) { // toughness
target = &current_card->value<TextValue>(_("toughness")).value;
} else if (line == _("#ILLUSTRATION##") || line == _("#ILLUSTRATION8#")) { // image
target = 0;
line = file.ReadLine();
if (!wxFileExists(line)) {
// based on card name and date
line = filter1(filename) + set_date + _("/") +
filter2(current_card->value<TextValue>(_("name")).value) + card_date + _(".jpg");
}
// copy image into set
if (wxFileExists(line)) {
String image_file = set->newFileName(_("image"),_(""));
if (wxCopyFile(line, set->nameOut(image_file), true)) {
current_card->value<ImageValue>(_("image")).filename = image_file;
}
}
} else if (line == _("#TOMBSTONE#####")) { // tombstone
target = 0;
line = file.ReadLine();
current_card->value<ChoiceValue>(_("card symbol")).value.assign(
line==_("TRUE") ? _("tombstone") : _("none")
);
} else {
// normal text
if (target != 0) { // value of a text field
if (!target->isDefault()) target->mutate() += _("\n");
target->mutate() += line;
} else {
throw ParseError(_("Error in Mtg Editor file, unexpected text:\n") + line);
}
}
}
// set defaults for artist and copyright to that of the first card
if (!set->cards.empty()) {
String artist = set->cards[0]->value<TextValue>(_("illustrator")).value;
String copyright = set->cards[0]->value<TextValue>(_("copyright")) .value;
set->value<TextValue>(_("artist")) .value.assign(artist);
set->value<TextValue>(_("copyright")).value.assign(copyright);
// which cards have this value?
FOR_EACH(card, set->cards) {
Defaultable<String>& card_artist = card->value<TextValue>(_("illustrator")).value;
Defaultable<String>& card_copyright = card->value<TextValue>(_("copyright")) .value;
if (card_artist == artist) card_artist.makeDefault();
if (card_copyright == copyright) card_copyright.makeDefault();
}
}
// Load stylesheet
if (layout != _("8e")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
set->validate();
return set;
wxFileInputStream f(filename);
#ifdef UNICODE
wxTextInputStream file(f, _('\n'), wxConvLibc);
#else
wxTextInputStream file(f);
#endif
// create set
SetP set(new Set(Game::byName(_("magic"))));
// parsing state
CardP current_card;
Defaultable<String>* target = nullptr; // value we are currently reading
String layout = _("8e");
String set_date, card_date;
bool first = true;
// read file
while (!f.Eof()) {
// read a line
if (!current_card) current_card = intrusive(new Card(*set->game));
String line = file.ReadLine();
if (line == _("#SET###########")) { // set.title
target = &set->value<TextValue>(_("title")).value;
} else if (line == _("#SETDATE#######")) { // date
// remember date for generation of illustration filename
target = nullptr;
set_date = file.ReadLine();
} else if (line == _("#SHOWRARITY####") ||
line == _("#FONTS#########") || line == _("#COUNT#########")) { // ignore
target = nullptr;
file.ReadLine();
} else if (line == _("#LAYOUT########")) { // layout type
target = nullptr;
layout = file.ReadLine();
} else if (line == _("#CARD##########") || line == _("#EOF###########")) { // begin/end of card
if (!first) {
// First [CARD##] indicates only the start of a card, subsequent ones also the end of the previous
// card. We only care about the latter
// remove all tags from all text values
untag(current_card, _("name"));
untag(current_card, _("super type"));
untag(current_card, _("sub type"));
untag(current_card, _("casting cost"));
untag(current_card, _("flavor text"));
untag(current_card, _("illustrator"));
untag(current_card, _("copyright"));
untag(current_card, _("power"));
untag(current_card, _("toughness"));
// translate mtg editor tags to mse2 tags
translateTags(current_card->value<TextValue>(_("rule text")).value.mutate());
// add the card to the set
set->cards.push_back(current_card);
}
first = false;
current_card = intrusive(new Card(*set->game));
target = &current_card->value<TextValue>(_("name")).value;
} else if (line == _("#DATE##########")) { // date
// remember date for generation of illustration filename
target = nullptr;
card_date = file.ReadLine();
} else if (line == _("#TYPE##########")) { // super type
target = &current_card->value<TextValue>(_("super type")).value;
} else if (line == _("#SUBTYPE#######")) { // sub type
target = &current_card->value<TextValue>(_("sub type")).value;
} else if (line == _("#COST##########")) { // casting cost
target = &current_card->value<TextValue>(_("casting cost")).value;
} else if (line == _("#RARITY########") || line == _("#FREQUENCY#####")) { // rarity
target = 0;
line = file.ReadLine();
if (line == _("0")) current_card->value<ChoiceValue>(_("rarity")).value.assign(_("common"));
else if (line == _("1")) current_card->value<ChoiceValue>(_("rarity")).value.assign(_("uncommon"));
else current_card->value<ChoiceValue>(_("rarity")).value.assign(_("rare"));
} else if (line == _("#COLOR#########")) { // card color
target = 0;
line = file.ReadLine();
current_card->value<ChoiceValue>(_("card color")).value.assign(line);
} else if (line == _("#AUTOBG########")) { // card color.isDefault
target = 0;
line = file.ReadLine();
if (line == _("TRUE")) {
current_card->value<ChoiceValue>(_("card color")).value.makeDefault();
}
} else if (line == _("#RULETEXT######")) { // rule text
target = &current_card->value<TextValue>(_("rule text")).value;
} else if (line == _("#FLAVORTEXT####")) { // flavor text
target = &current_card->value<TextValue>(_("flavor text")).value;
} else if (line == _("#ARTIST########")) { // illustrator
target = &current_card->value<TextValue>(_("illustrator")).value;
} else if (line == _("#COPYRIGHT#####")) { // copyright
target = &current_card->value<TextValue>(_("copyright")).value;
} else if (line == _("#POWER#########")) { // power
target = &current_card->value<TextValue>(_("power")).value;
} else if (line == _("#TOUGHNESS#####")) { // toughness
target = &current_card->value<TextValue>(_("toughness")).value;
} else if (line == _("#ILLUSTRATION##") || line == _("#ILLUSTRATION8#")) { // image
target = 0;
line = file.ReadLine();
if (!wxFileExists(line)) {
// based on card name and date
line = filter1(filename) + set_date + _("/") +
filter2(current_card->value<TextValue>(_("name")).value) + card_date + _(".jpg");
}
// copy image into set
if (wxFileExists(line)) {
String image_file = set->newFileName(_("image"),_(""));
if (wxCopyFile(line, set->nameOut(image_file), true)) {
current_card->value<ImageValue>(_("image")).filename = image_file;
}
}
} else if (line == _("#TOMBSTONE#####")) { // tombstone
target = 0;
line = file.ReadLine();
current_card->value<ChoiceValue>(_("card symbol")).value.assign(
line==_("TRUE") ? _("tombstone") : _("none")
);
} else {
// normal text
if (target != 0) { // value of a text field
if (!target->isDefault()) target->mutate() += _("\n");
target->mutate() += line;
} else {
throw ParseError(_("Error in Mtg Editor file, unexpected text:\n") + line);
}
}
}
// set defaults for artist and copyright to that of the first card
if (!set->cards.empty()) {
String artist = set->cards[0]->value<TextValue>(_("illustrator")).value;
String copyright = set->cards[0]->value<TextValue>(_("copyright")) .value;
set->value<TextValue>(_("artist")) .value.assign(artist);
set->value<TextValue>(_("copyright")).value.assign(copyright);
// which cards have this value?
FOR_EACH(card, set->cards) {
Defaultable<String>& card_artist = card->value<TextValue>(_("illustrator")).value;
Defaultable<String>& card_copyright = card->value<TextValue>(_("copyright")) .value;
if (card_artist == artist) card_artist.makeDefault();
if (card_copyright == copyright) card_copyright.makeDefault();
}
}
// Load stylesheet
if (layout != _("8e")) {
try {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("old"));
} catch (const Error&) {
// If old style doesn't work try the new one
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
} else {
set->stylesheet = StyleSheet::byGameAndName(*set->game, _("new"));
}
set->validate();
return set;
}
// ----------------------------------------------------------------------------- : Filtering
String MtgEditorFileFormat::filter1(const String& str) {
String before, after, ret;
// split path name
size_t pos = str.find_last_of(_("/\\"));
if (pos == String::npos) {
before = _(""); after = str;
} else {
before = str.substr(0, pos + 1);
after = str.substr(pos + 1);
}
// filter
FOR_EACH(c, after) {
if (isAlnum(c)) ret += c;
else ret += _('_');
}
return before + ret;
String before, after, ret;
// split path name
size_t pos = str.find_last_of(_("/\\"));
if (pos == String::npos) {
before = _(""); after = str;
} else {
before = str.substr(0, pos + 1);
after = str.substr(pos + 1);
}
// filter
FOR_EACH(c, after) {
if (isAlnum(c)) ret += c;
else ret += _('_');
}
return before + ret;
}
String MtgEditorFileFormat::filter2(const String& str) {
String ret;
FOR_EACH_CONST(c, str) {
if (isAlnum(c)) ret += c;
else if (c==_(' ') || c==_('-')) ret += _('_');
}
return ret;
String ret;
FOR_EACH_CONST(c, str) {
if (isAlnum(c)) ret += c;
else if (c==_(' ') || c==_('-')) ret += _('_');
}
return ret;
}
void MtgEditorFileFormat::untag(const CardP& card, const String& field) {
Defaultable<String>& value = card->value<TextValue>(field).value;
value.assignDontChangeDefault(untag_no_escape(value));
Defaultable<String>& value = card->value<TextValue>(field).value;
value.assignDontChangeDefault(untag_no_escape(value));
}
void MtgEditorFileFormat::translateTags(String& value) {
// Translate tags
String ret;
size_t pos = 0;
while (pos < value.size()) {
Char c = value.GetChar(pos);
++pos;
if (c == _('<')) {
String tag;
while (pos < value.size()) {
c = value.GetChar(pos);
++pos;
if (c == _('>')) break;
else tag += c;
}
tag.MakeUpper();
unsigned long number;
if (tag==_("W") || tag==_("U") || tag==_("B") || tag==_("R") || tag==_("G") || tag==_("X") || tag==_("Y") || tag==_("Z")) {
ret += _("<sym>") + tag + _("</sym>");
} else if (tag.ToULong(&number)) {
ret += _("<sym>") + tag + _("</sym>");
} else if (tag==_("T") || tag==_("TAP")) {
ret += _("<sym>T</sym>");
} else if (tag==_("THIS")) {
ret += _("~");
}
} else {
ret += c;
}
}
// Join adjecent symbol sections
value = replace_all(ret, _("</sym><sym>"), _(""));
// Translate tags
String ret;
size_t pos = 0;
while (pos < value.size()) {
Char c = value.GetChar(pos);
++pos;
if (c == _('<')) {
String tag;
while (pos < value.size()) {
c = value.GetChar(pos);
++pos;
if (c == _('>')) break;
else tag += c;
}
tag.MakeUpper();
unsigned long number;
if (tag==_("W") || tag==_("U") || tag==_("B") || tag==_("R") || tag==_("G") || tag==_("X") || tag==_("Y") || tag==_("Z")) {
ret += _("<sym>") + tag + _("</sym>");
} else if (tag.ToULong(&number)) {
ret += _("<sym>") + tag + _("</sym>");
} else if (tag==_("T") || tag==_("TAP")) {
ret += _("<sym>T</sym>");
} else if (tag==_("THIS")) {
ret += _("~");
}
} else {
ret += c;
}
}
// Join adjecent symbol sections
value = replace_all(ret, _("</sym><sym>"), _(""));
}
+70 -70
View File
@@ -23,89 +23,89 @@ DECLARE_TYPEOF_COLLECTION(CardP);
/// Convert a tagged string to MWS format: \t\t before each line beyond the first
String untag_mws(const String& str) {
// TODO : em dashes?
return replace_all(untag(curly_quotes(str,false)), _("\n"), _("\n\t\t") );
// TODO : em dashes?
return replace_all(untag(curly_quotes(str,false)), _("\n"), _("\n\t\t") );
}
//String untag_mws(const Defaultable<String>& str) {
// str.
// str.
//}
/// Code for card color in MWS format
String card_color_mws(const String& col) {
if (col == _("white")) return _("W");
if (col == _("blue")) return _("U");
if (col == _("black")) return _("B");
if (col == _("red")) return _("R");
if (col == _("green")) return _("G");
if (col == _("artifact")) return _("Art");
if (col == _("colorless")) return _("Art");
if (col.find(_("land")) != String::npos) {
return _("Lnd"); // land
} else {
return _("Gld"); // multicolor
}
if (col == _("white")) return _("W");
if (col == _("blue")) return _("U");
if (col == _("black")) return _("B");
if (col == _("red")) return _("R");
if (col == _("green")) return _("G");
if (col == _("artifact")) return _("Art");
if (col == _("colorless")) return _("Art");
if (col.find(_("land")) != String::npos) {
return _("Lnd"); // land
} else {
return _("Gld"); // multicolor
}
}
/// Code for card rarity, used for MWS and Apprentice
String card_rarity_code(const String& rarity) {
if (rarity == _("rare")) return _("R");
if (rarity == _("uncommon")) return _("U");
else return _("C");
if (rarity == _("rare")) return _("R");
if (rarity == _("uncommon")) return _("U");
else return _("C");
}
// ----------------------------------------------------------------------------- : export_mws
void export_mws(Window* parent, const SetP& set) {
if (!set->game->isMagic()) {
throw Error(_("Can only export Magic sets to Magic Workstation"));
}
// Select filename
String name = wxFileSelector(_("Export to file"),settings.default_export_dir,_(""),_(""),
_("Text files (*.txt)|*.txt|All Files|*"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
if (name.empty()) return;
settings.default_export_dir = wxPathOnly(name);
wxBusyCursor busy;
// Open file
wxFileOutputStream f(name);
wxTextOutputStream file(f, wxEOL_DOS);
// Write header
file.WriteString(set->value<TextValue>(_("title")).value + _(" Spoiler List\n"));
file.WriteString(_("Set exported using Magic Set Editor 2, version ") + app_version.toString() + _("\n\n"));
wxDateTime now = wxDateTime::Now();
file.WriteString(_("Spoiler List created on ") + now.FormatISODate() + _(" ") + now.FormatISOTime());
file.WriteString(_("\n\n"));
// Write cards
FOR_EACH(card, set->cards) {
file.WriteString(_("Card Name:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("name")).value));
file.WriteString(_("\nCard Color:\t"));
file.WriteString(card_color_mws(card->value<ChoiceValue>(_("card color")).value));
file.WriteString(_("\nMana Cost:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("casting cost")).value));
file.WriteString(_("\nType & Class:\t"));
String sup_type = untag_mws(card->value<TextValue>(_("super type")).value);
String sub_type = untag_mws(card->value<TextValue>(_("sub type")).value);
if (sub_type.empty()) {
file.WriteString(sup_type);
} else {
file.WriteString(sup_type + _(" - ") + sub_type);
}
file.WriteString(_("\nPow/Tou:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("pt")).value));
file.WriteString(_("\nCard Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("rule text")).value));
file.WriteString(_("\nFlavor Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("flavor text")).value));
file.WriteString(_("\nArtist:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("illustrator")).value));
file.WriteString(_("\nRarity:\t\t"));
file.WriteString(card_rarity_code(card->value<ChoiceValue>(_("rarity")).value));
file.WriteString(_("\nCard #:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("card number")).value));
file.WriteString(_("\n\n"));
}
if (!set->game->isMagic()) {
throw Error(_("Can only export Magic sets to Magic Workstation"));
}
// Select filename
String name = wxFileSelector(_("Export to file"),settings.default_export_dir,_(""),_(""),
_("Text files (*.txt)|*.txt|All Files|*"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
if (name.empty()) return;
settings.default_export_dir = wxPathOnly(name);
wxBusyCursor busy;
// Open file
wxFileOutputStream f(name);
wxTextOutputStream file(f, wxEOL_DOS);
// Write header
file.WriteString(set->value<TextValue>(_("title")).value + _(" Spoiler List\n"));
file.WriteString(_("Set exported using Magic Set Editor 2, version ") + app_version.toString() + _("\n\n"));
wxDateTime now = wxDateTime::Now();
file.WriteString(_("Spoiler List created on ") + now.FormatISODate() + _(" ") + now.FormatISOTime());
file.WriteString(_("\n\n"));
// Write cards
FOR_EACH(card, set->cards) {
file.WriteString(_("Card Name:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("name")).value));
file.WriteString(_("\nCard Color:\t"));
file.WriteString(card_color_mws(card->value<ChoiceValue>(_("card color")).value));
file.WriteString(_("\nMana Cost:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("casting cost")).value));
file.WriteString(_("\nType & Class:\t"));
String sup_type = untag_mws(card->value<TextValue>(_("super type")).value);
String sub_type = untag_mws(card->value<TextValue>(_("sub type")).value);
if (sub_type.empty()) {
file.WriteString(sup_type);
} else {
file.WriteString(sup_type + _(" - ") + sub_type);
}
file.WriteString(_("\nPow/Tou:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("pt")).value));
file.WriteString(_("\nCard Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("rule text")).value));
file.WriteString(_("\nFlavor Text:\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("flavor text")).value));
file.WriteString(_("\nArtist:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("illustrator")).value));
file.WriteString(_("\nRarity:\t\t"));
file.WriteString(card_rarity_code(card->value<ChoiceValue>(_("rarity")).value));
file.WriteString(_("\nCard #:\t\t"));
file.WriteString(untag_mws(card->value<TextValue>(_("card number")).value));
file.WriteString(_("\n\n"));
}
}
+74 -74
View File
@@ -26,16 +26,16 @@ DECLARE_TYPEOF_COLLECTION(StatsDimensionP);
IMPLEMENT_DYNAMIC_ARG(Game*, game_for_reading, nullptr);
Game::Game()
: has_keywords(false)
, dependencies_initialized(false)
: has_keywords(false)
, dependencies_initialized(false)
{}
GameP Game::byName(const String& name) {
return package_manager.open<Game>(name + _(".mse-game"));
return package_manager.open<Game>(name + _(".mse-game"));
}
bool Game::isMagic() const {
return name() == _("magic");
return name() == _("magic");
}
String Game::typeNameStatic() { return _("game"); }
@@ -43,87 +43,87 @@ String Game::typeName() const { return _("game"); }
Version Game::fileVersion() const { return file_version_game; }
IMPLEMENT_REFLECTION(Game) {
REFLECT_BASE(Packaged);
REFLECT_NO_SCRIPT(init_script);
REFLECT_NO_SCRIPT(set_fields);
REFLECT_IF_READING {
default_set_style.init(set_fields);
}
REFLECT_NO_SCRIPT(default_set_style);
REFLECT_NO_SCRIPT(card_fields);
REFLECT_NO_SCRIPT(card_list_color_script);
REFLECT_NO_SCRIPT(statistics_dimensions);
REFLECT_NO_SCRIPT(statistics_categories);
REFLECT_ALIAS(308, "pack item", "pack type");
REFLECT_NO_SCRIPT(pack_types);
REFLECT_NO_SCRIPT(keyword_match_script);
REFLECT(has_keywords);
REFLECT(keyword_modes);
REFLECT(keyword_parameter_types);
REFLECT_NO_SCRIPT(keywords);
REFLECT_NO_SCRIPT(word_lists);
REFLECT_NO_SCRIPT(add_cards_scripts);
REFLECT_NO_SCRIPT(auto_replaces);
REFLECT_BASE(Packaged);
REFLECT_NO_SCRIPT(init_script);
REFLECT_NO_SCRIPT(set_fields);
REFLECT_IF_READING {
default_set_style.init(set_fields);
}
REFLECT_NO_SCRIPT(default_set_style);
REFLECT_NO_SCRIPT(card_fields);
REFLECT_NO_SCRIPT(card_list_color_script);
REFLECT_NO_SCRIPT(statistics_dimensions);
REFLECT_NO_SCRIPT(statistics_categories);
REFLECT_ALIAS(308, "pack item", "pack type");
REFLECT_NO_SCRIPT(pack_types);
REFLECT_NO_SCRIPT(keyword_match_script);
REFLECT(has_keywords);
REFLECT(keyword_modes);
REFLECT(keyword_parameter_types);
REFLECT_NO_SCRIPT(keywords);
REFLECT_NO_SCRIPT(word_lists);
REFLECT_NO_SCRIPT(add_cards_scripts);
REFLECT_NO_SCRIPT(auto_replaces);
}
void Game::validate(Version v) {
Packaged::validate(v);
// automatic statistics dimensions
{
vector<StatsDimensionP> dims;
FOR_EACH(f, card_fields) {
if (f->show_statistics) {
dims.push_back(intrusive(new StatsDimension(*f)));
}
}
statistics_dimensions.insert(statistics_dimensions.begin(), dims.begin(), dims.end()); // push front
}
// automatic statistics categories
{
vector<StatsCategoryP> cats;
FOR_EACH(dim, statistics_dimensions) {
cats.push_back(intrusive(new StatsCategory(dim)));
}
statistics_categories.insert(statistics_categories.begin(), cats.begin(), cats.end()); // push front
}
// automatic pack if there are none
if (pack_types.empty()) {
PackTypeP pack(new PackType);
pack->name = _("Any card");
pack->enabled = true;
pack->selectable = true;
pack->summary = true;
pack->filter = OptionalScript(_("true"));
pack->select = SELECT_NO_REPLACE;
pack_types.push_back(pack);
}
Packaged::validate(v);
// automatic statistics dimensions
{
vector<StatsDimensionP> dims;
FOR_EACH(f, card_fields) {
if (f->show_statistics) {
dims.push_back(intrusive(new StatsDimension(*f)));
}
}
statistics_dimensions.insert(statistics_dimensions.begin(), dims.begin(), dims.end()); // push front
}
// automatic statistics categories
{
vector<StatsCategoryP> cats;
FOR_EACH(dim, statistics_dimensions) {
cats.push_back(intrusive(new StatsCategory(dim)));
}
statistics_categories.insert(statistics_categories.begin(), cats.begin(), cats.end()); // push front
}
// automatic pack if there are none
if (pack_types.empty()) {
PackTypeP pack(new PackType);
pack->name = _("Any card");
pack->enabled = true;
pack->selectable = true;
pack->summary = true;
pack->filter = OptionalScript(_("true"));
pack->select = SELECT_NO_REPLACE;
pack_types.push_back(pack);
}
}
void Game::initCardListColorScript() {
if (card_list_color_script) return; // already done
// find a field with choice_colors_cardlist
FOR_EACH(s, card_fields) {
ChoiceFieldP cf = dynamic_pointer_cast<ChoiceField>(s);
if (cf && !cf->choice_colors_cardlist.empty()) {
// found the field to use
// initialize script: field.colors[card.field-name] or else rgb(0,0,0)
Script& s = card_list_color_script.getMutableScript();
s.addInstruction(I_PUSH_CONST, to_script(&cf->choice_colors_cardlist));
s.addInstruction(I_GET_VAR, SCRIPT_VAR_card);
s.addInstruction(I_MEMBER_C, cf->name);
s.addInstruction(I_BINARY, I_MEMBER);
s.addInstruction(I_PUSH_CONST, to_script(Color(0,0,0)));
s.addInstruction(I_BINARY, I_OR_ELSE);
return;
}
}
if (card_list_color_script) return; // already done
// find a field with choice_colors_cardlist
FOR_EACH(s, card_fields) {
ChoiceFieldP cf = dynamic_pointer_cast<ChoiceField>(s);
if (cf && !cf->choice_colors_cardlist.empty()) {
// found the field to use
// initialize script: field.colors[card.field-name] or else rgb(0,0,0)
Script& s = card_list_color_script.getMutableScript();
s.addInstruction(I_PUSH_CONST, to_script(&cf->choice_colors_cardlist));
s.addInstruction(I_GET_VAR, SCRIPT_VAR_card);
s.addInstruction(I_MEMBER_C, cf->name);
s.addInstruction(I_BINARY, I_MEMBER);
s.addInstruction(I_PUSH_CONST, to_script(Color(0,0,0)));
s.addInstruction(I_BINARY, I_OR_ELSE);
return;
}
}
}
// special behaviour of reading/writing GamePs: only read/write the name
void Reader::handle(GameP& game) {
game = Game::byName(getValue());
game = Game::byName(getValue());
}
void Writer::handle(const GameP& game) {
if (game) handle(game->name());
if (game) handle(game->name());
}
+42 -42
View File
@@ -36,52 +36,52 @@ DECLARE_DYNAMIC_ARG(Game*, game_for_reading);
/// A description of a card game
class Game : public Packaged {
public:
Game();
OptionalScript init_script; ///< Script of variables available to other scripts in this game
vector<FieldP> set_fields; ///< Fields for set information
IndexMap<FieldP,StyleP> default_set_style; ///< Default style for the set fields, because it is often the same
vector<FieldP> card_fields; ///< Fields on each card
OptionalScript card_list_color_script; ///< Script that determines the color of items in the card list
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
vector<AddCardsScriptP> add_cards_scripts; ///< Scripts for adding multiple cards to the set
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
bool has_keywords; ///< Does this game use keywords?
OptionalScript keyword_match_script; ///< For the keyword editor
vector<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
vector<KeywordModeP> keyword_modes; ///< Modes of keywords
vector<KeywordP> keywords; ///< Keywords for use in text
Dependencies dependent_scripts_cards; ///< scripts that depend on the card list
Dependencies dependent_scripts_keywords; ///< scripts that depend on the keywords
Dependencies dependent_scripts_stylesheet; ///< scripts that depend on the card's stylesheet
bool dependencies_initialized; ///< are the script dependencies comming from this game all initialized?
/// Loads the game with a particular name, for example "magic"
static GameP byName(const String& name);
/// Is this Magic the Gathering?
bool isMagic() const;
/// Initialize card_list_color_script
void initCardListColorScript();
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
Game();
OptionalScript init_script; ///< Script of variables available to other scripts in this game
vector<FieldP> set_fields; ///< Fields for set information
IndexMap<FieldP,StyleP> default_set_style; ///< Default style for the set fields, because it is often the same
vector<FieldP> card_fields; ///< Fields on each card
OptionalScript card_list_color_script; ///< Script that determines the color of items in the card list
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
vector<AddCardsScriptP> add_cards_scripts; ///< Scripts for adding multiple cards to the set
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
bool has_keywords; ///< Does this game use keywords?
OptionalScript keyword_match_script; ///< For the keyword editor
vector<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
vector<KeywordModeP> keyword_modes; ///< Modes of keywords
vector<KeywordP> keywords; ///< Keywords for use in text
Dependencies dependent_scripts_cards; ///< scripts that depend on the card list
Dependencies dependent_scripts_keywords; ///< scripts that depend on the keywords
Dependencies dependent_scripts_stylesheet; ///< scripts that depend on the card's stylesheet
bool dependencies_initialized; ///< are the script dependencies comming from this game all initialized?
/// Loads the game with a particular name, for example "magic"
static GameP byName(const String& name);
/// Is this Magic the Gathering?
bool isMagic() const;
/// Initialize card_list_color_script
void initCardListColorScript();
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
protected:
virtual void validate(Version);
DECLARE_REFLECTION();
virtual void validate(Version);
DECLARE_REFLECTION();
};
inline String type_name(const Game&) {
return _TYPE_("game");
return _TYPE_("game");
}
// ----------------------------------------------------------------------------- : EOF
+11 -11
View File
@@ -15,21 +15,21 @@
/// Types of graphs
enum GraphType
{ GRAPH_TYPE_PIE
, GRAPH_TYPE_BAR
, GRAPH_TYPE_STACK
, GRAPH_TYPE_SCATTER
, GRAPH_TYPE_SCATTER_PIE
{ GRAPH_TYPE_PIE
, GRAPH_TYPE_BAR
, GRAPH_TYPE_STACK
, GRAPH_TYPE_SCATTER
, GRAPH_TYPE_SCATTER_PIE
};
/// Dimensions for each graph type
inline size_t dimensionality(GraphType type) {
if (type == GRAPH_TYPE_PIE) return 1;
if (type == GRAPH_TYPE_BAR) return 1;
if (type == GRAPH_TYPE_STACK) return 2;
if (type == GRAPH_TYPE_SCATTER) return 2;
if (type == GRAPH_TYPE_SCATTER_PIE) return 3;
else return 0;
if (type == GRAPH_TYPE_PIE) return 1;
if (type == GRAPH_TYPE_BAR) return 1;
if (type == GRAPH_TYPE_STACK) return 2;
if (type == GRAPH_TYPE_SCATTER) return 2;
if (type == GRAPH_TYPE_SCATTER_PIE) return 3;
else return 0;
}
// ----------------------------------------------------------------------------- : EOF
+448 -448
View File
File diff suppressed because it is too large Load Diff
+94 -94
View File
@@ -32,22 +32,22 @@ DECLARE_POINTER_TYPE(InstallablePackage);
*/
class Installer : public Packaged {
public:
String prefered_filename; ///< What filename should be used (by default), when creating the installer
vector<PackageDescriptionP> packages; ///< Packages to install
/// Add a package to the installer (if it is not already added).
/** If the package is named *.mse-installer uses it as the filename instead */
void addPackage(const String& package);
/// Add a package to the installer (if it is not already added).
/** The first package gives the name of the installer.
*/
void addPackage(Packaged& package);
String prefered_filename; ///< What filename should be used (by default), when creating the installer
vector<PackageDescriptionP> packages; ///< Packages to install
/// Add a package to the installer (if it is not already added).
/** If the package is named *.mse-installer uses it as the filename instead */
void addPackage(const String& package);
/// Add a package to the installer (if it is not already added).
/** The first package gives the name of the installer.
*/
void addPackage(Packaged& package);
protected:
virtual String typeName() const;
virtual Version fileVersion() const;
virtual void validate(Version file_app_version);
DECLARE_REFLECTION();
virtual String typeName() const;
virtual Version fileVersion() const;
virtual void validate(Version file_app_version);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Installer descriptions
@@ -58,75 +58,75 @@ class Installer : public Packaged {
*/
class PackageDescription : public IntrusivePtrBase<PackageDescription> {
public:
PackageDescription();
PackageDescription(const Packaged& package);
String name; ///< Filename of the package
Version version; ///< Version number of this package
String short_name; ///< Short name of this package
String full_name; ///< Name of this package, for menus etc.
String icon_url; ///< Filename or URL of icon to use in package lists
Image icon; ///< Icon for the package
String installer_group; ///< Where to put this package in the installer
int position_hint; ///< A hint for the package list
String description; ///< Changelog/description
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
/// Merge two descriptions a package. This package takes precedence
/** Usually one of the descriptions will refer to the locally installed one, the other to the new one */
void merge(const PackageDescription& p2);
DECLARE_REFLECTION();
PackageDescription();
PackageDescription(const Packaged& package);
String name; ///< Filename of the package
Version version; ///< Version number of this package
String short_name; ///< Short name of this package
String full_name; ///< Name of this package, for menus etc.
String icon_url; ///< Filename or URL of icon to use in package lists
Image icon; ///< Icon for the package
String installer_group; ///< Where to put this package in the installer
int position_hint; ///< A hint for the package list
String description; ///< Changelog/description
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
/// Merge two descriptions a package. This package takes precedence
/** Usually one of the descriptions will refer to the locally installed one, the other to the new one */
void merge(const PackageDescription& p2);
DECLARE_REFLECTION();
};
/// A description of the contents of an installer
class InstallerDescription : public IntrusivePtrBase<InstallerDescription> {
public:
vector<PackageDescriptionP> packages;
DECLARE_REFLECTION();
vector<PackageDescriptionP> packages;
DECLARE_REFLECTION();
};
/// Information on an installer that can be downloaded
class DownloadableInstaller : public IntrusivePtrBase<DownloadableInstaller> {
public:
DownloadableInstaller() : downloadable(true) {}
DownloadableInstaller(const InstallerP& installer);
~DownloadableInstaller();
InstallerP installer; ///< The installer, if it is loaded
String installer_url; ///< The URL where the installer can be found
String installer_file; ///< The temp file where the installer can be found (after downloading)
bool downloadable; ///< Is the installer downloadable (in)directly from that url?
vector<PackageDescriptionP> packages; ///< Packages provided by this installer
DECLARE_REFLECTION();
DownloadableInstaller() : downloadable(true) {}
DownloadableInstaller(const InstallerP& installer);
~DownloadableInstaller();
InstallerP installer; ///< The installer, if it is loaded
String installer_url; ///< The URL where the installer can be found
String installer_file; ///< The temp file where the installer can be found (after downloading)
bool downloadable; ///< Is the installer downloadable (in)directly from that url?
vector<PackageDescriptionP> packages; ///< Packages provided by this installer
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Installable package
/// Installation status of a package
enum PackageStatus
{ PACKAGE_NOT_INSTALLED = 0x0000
, PACKAGE_INSTALLED = 0x0001
, PACKAGE_REMOVABLE = 0x0002
, PACKAGE_INSTALLER = 0x0010 ///< Package can be installed (there is an installer)
, PACKAGE_INSTALLABLE = 0x0110 ///< Package can be installed (and it makes sense to do so)
, PACKAGE_NOT_UP_TO_DATE= 0x0200 ///< The local installed version (if any) is not up to date
, PACKAGE_UPDATES = 0x0311 ///< Remote updates available
, PACKAGE_MODIFIED = 0x1001 ///< Local changes made
, PACKAGE_MISSING_DEP = 0x2000 ///< Missing a dependency for installation
, PACKAGE_CONFLICTS = PACKAGE_UPDATES | PACKAGE_MODIFIED
{ PACKAGE_NOT_INSTALLED = 0x0000
, PACKAGE_INSTALLED = 0x0001
, PACKAGE_REMOVABLE = 0x0002
, PACKAGE_INSTALLER = 0x0010 ///< Package can be installed (there is an installer)
, PACKAGE_INSTALLABLE = 0x0110 ///< Package can be installed (and it makes sense to do so)
, PACKAGE_NOT_UP_TO_DATE= 0x0200 ///< The local installed version (if any) is not up to date
, PACKAGE_UPDATES = 0x0311 ///< Remote updates available
, PACKAGE_MODIFIED = 0x1001 ///< Local changes made
, PACKAGE_MISSING_DEP = 0x2000 ///< Missing a dependency for installation
, PACKAGE_CONFLICTS = PACKAGE_UPDATES | PACKAGE_MODIFIED
};
/// (un)install a package?
enum PackageAction
{ PACKAGE_ACT_NOTHING = 0x001 ///< Don't change anything
, PACKAGE_ACT_INSTALL = 0x002 ///< Install or upgrade the package
, PACKAGE_ACT_REMOVE = 0x004 ///< Remove the package (if it was installed)
, PACKAGE_ACT_LOCAL = 0x010 ///< In the local package directory
, PACKAGE_ACT_GLOBAL = 0x020 ///< In the global package directory
, PACKAGE_ACT_WHERE = PACKAGE_ACT_LOCAL | PACKAGE_ACT_GLOBAL
{ PACKAGE_ACT_NOTHING = 0x001 ///< Don't change anything
, PACKAGE_ACT_INSTALL = 0x002 ///< Install or upgrade the package
, PACKAGE_ACT_REMOVE = 0x004 ///< Remove the package (if it was installed)
, PACKAGE_ACT_LOCAL = 0x010 ///< In the local package directory
, PACKAGE_ACT_GLOBAL = 0x020 ///< In the global package directory
, PACKAGE_ACT_WHERE = PACKAGE_ACT_LOCAL | PACKAGE_ACT_GLOBAL
};
// bit twidling
inline PackageAction operator | (PackageAction a, PackageAction b) { return (PackageAction)((int)a | (int) b); }
@@ -135,36 +135,36 @@ inline bool flag(int flags, int flag) { return (flags & flag) == flag; }
/// A package that can be installed, or is already installed
class InstallablePackage : public IntrusivePtrVirtualBase {
public:
/// A new package
InstallablePackage(const PackageDescriptionP&, const DownloadableInstallerP&);
/// An installed package
InstallablePackage(const PackageDescriptionP&, const PackageVersionP&);
PackageDescriptionP description; ///< The details of the package. Either from the installed package or from an installer
PackageVersionP installed; ///< The information of the installed package (if installed)
DownloadableInstallerP installer; ///< The installer to install from (if updates are available)
PackageStatus status; ///< Status of installation
PackageAction action; ///< What to do with this package?
int automatic; ///< Install/upgrade/remove automaticly to satisfy this many packages
PackageAction old_action;
int old_automatic;
void determineStatus();
/// After the action, will the package be installed?
bool willBeInstalled() const;
/// Is the action possible?
bool can(PackageAction act) const;
/// Is the action currently selected?
bool has(PackageAction act) const;
/// Does the package have the given status bits all set?
bool has(PackageStatus stat) const;
/// Merge two descriptions of installable packages
void merge(const InstallablePackage& p2);
/// A new package
InstallablePackage(const PackageDescriptionP&, const DownloadableInstallerP&);
/// An installed package
InstallablePackage(const PackageDescriptionP&, const PackageVersionP&);
PackageDescriptionP description; ///< The details of the package. Either from the installed package or from an installer
PackageVersionP installed; ///< The information of the installed package (if installed)
DownloadableInstallerP installer; ///< The installer to install from (if updates are available)
PackageStatus status; ///< Status of installation
PackageAction action; ///< What to do with this package?
int automatic; ///< Install/upgrade/remove automaticly to satisfy this many packages
PackageAction old_action;
int old_automatic;
void determineStatus();
/// After the action, will the package be installed?
bool willBeInstalled() const;
/// Is the action possible?
bool can(PackageAction act) const;
/// Is the action currently selected?
bool has(PackageAction act) const;
/// Does the package have the given status bits all set?
bool has(PackageStatus stat) const;
/// Merge two descriptions of installable packages
void merge(const InstallablePackage& p2);
};
+588 -588
View File
File diff suppressed because it is too large Load Diff
+129 -129
View File
@@ -25,48 +25,48 @@ class Value;
class ParamReferenceType : public IntrusivePtrBase<ParamReferenceType> {
public:
String name; ///< Name of the parameter reference type
String description; ///< Description (for status bar)
StringScript script; ///< Code to insert into the reminder text script, input is the actual parameter name
DECLARE_REFLECTION();
String name; ///< Name of the parameter reference type
String description; ///< Description (for status bar)
StringScript script; ///< Code to insert into the reminder text script, input is the actual parameter name
DECLARE_REFLECTION();
};
/// Parameter type of keywords
class KeywordParam : public IntrusivePtrBase<KeywordParam> {
public:
KeywordParam();
String name; ///< Name of the parameter type
String description; ///< Description of the parameter type
String placeholder; ///< Placholder for <atom-kwpph>, name is used if this is empty
bool optional; ///< Can this parameter be left out (a placeholder is then used)
String match; ///< Regular expression to match (including separators)
String separator_before_is; ///< Regular expression of separator before the param
Regex separator_before_re; ///< Regular expression of separator before the param, compiled
Regex separator_before_eat;///< Regular expression of separator before the param, if eat_separator
String separator_after_is; ///< Regular expression of separator after the param
Regex separator_after_re; ///< Regular expression of separator after the param, compiled
Regex separator_after_eat; ///< Regular expression of separator after the param, if eat_separator
bool eat_separator; ///< Remove the separator from the match string if it also appears there (prevent duplicates)
OptionalScript script; ///< Transformation of the value for showing as the parameter
OptionalScript reminder_script; ///< Transformation of the value for showing in the reminder text
OptionalScript separator_script; ///< Transformation of the separator
String example; ///< Example for the keyword editor
vector<ParamReferenceTypeP> refer_scripts;///< Way to refer to a parameter from the reminder text script
//% /// Make a string that can function as a separator before the parameter
//% /** This tries to decode the separator_before_is regex */
//% String make_separator_before() const;
/// Compile regexes for separators
void compile();
/// Remove separator_before from the end of the text
void eat_separator_before(String& text);
/// Advance i past separator_before if it is at position i in the text
void eat_separator_after(const String& text, size_t& i);
DECLARE_REFLECTION();
KeywordParam();
String name; ///< Name of the parameter type
String description; ///< Description of the parameter type
String placeholder; ///< Placholder for <atom-kwpph>, name is used if this is empty
bool optional; ///< Can this parameter be left out (a placeholder is then used)
String match; ///< Regular expression to match (including separators)
String separator_before_is; ///< Regular expression of separator before the param
Regex separator_before_re; ///< Regular expression of separator before the param, compiled
Regex separator_before_eat;///< Regular expression of separator before the param, if eat_separator
String separator_after_is; ///< Regular expression of separator after the param
Regex separator_after_re; ///< Regular expression of separator after the param, compiled
Regex separator_after_eat; ///< Regular expression of separator after the param, if eat_separator
bool eat_separator; ///< Remove the separator from the match string if it also appears there (prevent duplicates)
OptionalScript script; ///< Transformation of the value for showing as the parameter
OptionalScript reminder_script; ///< Transformation of the value for showing in the reminder text
OptionalScript separator_script; ///< Transformation of the separator
String example; ///< Example for the keyword editor
vector<ParamReferenceTypeP> refer_scripts;///< Way to refer to a parameter from the reminder text script
//% /// Make a string that can function as a separator before the parameter
//% /** This tries to decode the separator_before_is regex */
//% String make_separator_before() const;
/// Compile regexes for separators
void compile();
/// Remove separator_before from the end of the text
void eat_separator_before(String& text);
/// Advance i past separator_before if it is at position i in the text
void eat_separator_after(const String& text, size_t& i);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Keyword mode
@@ -74,13 +74,13 @@ class KeywordParam : public IntrusivePtrBase<KeywordParam> {
/// Information on when and how to use a keyword
class KeywordMode : public IntrusivePtrBase<KeywordMode> {
public:
KeywordMode() : is_default(false) {}
String name; ///< Name of the mode
String description; ///< Description of the type
bool is_default; ///< This is the default mode for new keywords
DECLARE_REFLECTION();
KeywordMode() : is_default(false) {}
String name; ///< Name of the mode
String description; ///< Description of the type
bool is_default; ///< This is the default mode for new keywords
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Keyword expansion
@@ -88,45 +88,45 @@ class KeywordMode : public IntrusivePtrBase<KeywordMode> {
/// A keyword for a set or a game
class Keyword : public IntrusivePtrVirtualBase {
public:
Keyword() : fixed(false), valid(false) {}
String keyword; ///< The keyword, only for human use
String rules; ///< Rules/explanation
String match; ///< String to match, <atom-param> tags are used for parameters
vector<KeywordParamP> parameters; ///< The types of parameters
StringScript reminder; ///< Reminder text of the keyword
String mode; ///< Mode of use, can be used by scripts (only gives the name)
/// Regular expression to match and split parameters, automatically generated.
/** The regex has exactly 2 * parameters.size() + 1 captures (excluding the entire match, caputure 0),
* captures 1,3,... capture the plain text of the match string
* captures 2,4,... capture the separators and parameters
*/
Regex match_re;
bool fixed; ///< Is this keyword uneditable? (true for game keywods, false for set keywords)
bool valid; ///< Is this keyword okay (reminder text compiles & runs; match does not match "")
/// Find the index of the mode in a list of possibilities.
/** Returns the default if not found and 0 if there is no default */
size_t findMode(const vector<KeywordModeP>& modes) const;
/// Prepare the expansion: (re)generate matchRe and the list of parameters.
/** Throws when there is an error in the input
* @param param_types A list of all parameter types.
* @param force Re-prepare even if the regex&parameters are okay
*/
void prepare(const vector<KeywordParamP>& param_types, bool force = false);
/// Does the keyword contain the given query word?
bool contains(String const& word) const;
DECLARE_REFLECTION();
Keyword() : fixed(false), valid(false) {}
String keyword; ///< The keyword, only for human use
String rules; ///< Rules/explanation
String match; ///< String to match, <atom-param> tags are used for parameters
vector<KeywordParamP> parameters; ///< The types of parameters
StringScript reminder; ///< Reminder text of the keyword
String mode; ///< Mode of use, can be used by scripts (only gives the name)
/// Regular expression to match and split parameters, automatically generated.
/** The regex has exactly 2 * parameters.size() + 1 captures (excluding the entire match, caputure 0),
* captures 1,3,... capture the plain text of the match string
* captures 2,4,... capture the separators and parameters
*/
Regex match_re;
bool fixed; ///< Is this keyword uneditable? (true for game keywods, false for set keywords)
bool valid; ///< Is this keyword okay (reminder text compiles & runs; match does not match "")
/// Find the index of the mode in a list of possibilities.
/** Returns the default if not found and 0 if there is no default */
size_t findMode(const vector<KeywordModeP>& modes) const;
/// Prepare the expansion: (re)generate matchRe and the list of parameters.
/** Throws when there is an error in the input
* @param param_types A list of all parameter types.
* @param force Re-prepare even if the regex&parameters are okay
*/
void prepare(const vector<KeywordParamP>& param_types, bool force = false);
/// Does the keyword contain the given query word?
bool contains(String const& word) const;
DECLARE_REFLECTION();
};
inline String type_name(const Keyword&) {
return _TYPE_("keyword");
return _TYPE_("keyword");
}
inline String type_name(const vector<KeywordP>&) {
return _TYPE_("keywords"); // not actually used, only for locale.pl script
return _TYPE_("keywords"); // not actually used, only for locale.pl script
}
// ----------------------------------------------------------------------------- : Using keywords
@@ -141,42 +141,42 @@ DECLARE_DYNAMIC_ARG(KeywordUsageStatistics*, keyword_usage_statistics);
*/
class KeywordDatabase {
public:
KeywordDatabase();
~KeywordDatabase();
/// Add a list of keywords to be matched
void add(const vector<KeywordP>&);
/// Add a keyword to be matched
void add(const Keyword&);
/// Prepare the parameters and match regex for a list of keywords
static void prepare_parameters(const vector<KeywordParamP>&, const vector<KeywordP>&);
/// Clear the database
void clear();
/// Is the database empty?
inline bool empty() const { return !root; }
/// Expand/update all keywords in the given string.
/** @param expand_default script function indicating whether reminder text should be shown by default
* @param combine_script script function to combine keyword and reminder text in some way
* @param case_sensitive case sensitive matching of keywords?
* @param ctx context for evaluation of scripts
*/
String expand(const String& text, const ScriptValueP& match_condition, const ScriptValueP& expand_default, const ScriptValueP& combine_script, Context& ctx) const;
KeywordDatabase();
~KeywordDatabase();
/// Add a list of keywords to be matched
void add(const vector<KeywordP>&);
/// Add a keyword to be matched
void add(const Keyword&);
/// Prepare the parameters and match regex for a list of keywords
static void prepare_parameters(const vector<KeywordParamP>&, const vector<KeywordP>&);
/// Clear the database
void clear();
/// Is the database empty?
inline bool empty() const { return !root; }
/// Expand/update all keywords in the given string.
/** @param expand_default script function indicating whether reminder text should be shown by default
* @param combine_script script function to combine keyword and reminder text in some way
* @param case_sensitive case sensitive matching of keywords?
* @param ctx context for evaluation of scripts
*/
String expand(const String& text, const ScriptValueP& match_condition, const ScriptValueP& expand_default, const ScriptValueP& combine_script, Context& ctx) const;
private:
KeywordTrie* root; ///< Data structure for finding keywords
/// (try to) expand a single keyword
/** If the keyword matches:
* - add the result to out
* - advance the tagged and untagged string by dropping a part from the front
* - return true
*/
bool tryExpand(const Keyword& kw, size_t pos, String& tagged, String& untagged, String& out, char expand_type,
const ScriptValueP& match_condition, const ScriptValueP& expand_default, const ScriptValueP& combine_script, Context& ctx,
KeywordUsageStatistics* stat, Value* stat_key) const;
KeywordTrie* root; ///< Data structure for finding keywords
/// (try to) expand a single keyword
/** If the keyword matches:
* - add the result to out
* - advance the tagged and untagged string by dropping a part from the front
* - return true
*/
bool tryExpand(const Keyword& kw, size_t pos, String& tagged, String& untagged, String& out, char expand_type,
const ScriptValueP& match_condition, const ScriptValueP& expand_default, const ScriptValueP& combine_script, Context& ctx,
KeywordUsageStatistics* stat, Value* stat_key) const;
};
// ----------------------------------------------------------------------------- : Processing parameters
@@ -184,22 +184,22 @@ class KeywordDatabase {
/// A script value containing the value of a keyword parameter
class KeywordParamValue : public ScriptValue {
public:
KeywordParamValue(const String& type, const String& separator_before, const String& separator_after, const String& value)
: type_name(type), separator_before(separator_before), separator_after(separator_after), value(value)
{}
String type_name;
String separator_before, separator_after;
String value;
virtual ScriptType type() const;
virtual String typeName() const;
virtual operator String() const;
virtual operator int() const;
virtual operator bool() const;
virtual operator double() const;
virtual operator AColor() const;
virtual int itemCount() const;
virtual ScriptValueP getMember(const String& name) const;
KeywordParamValue(const String& type, const String& separator_before, const String& separator_after, const String& value)
: type_name(type), separator_before(separator_before), separator_after(separator_after), value(value)
{}
String type_name;
String separator_before, separator_after;
String value;
virtual ScriptType type() const;
virtual String typeName() const;
virtual operator String() const;
virtual operator int() const;
virtual operator bool() const;
virtual operator double() const;
virtual operator AColor() const;
virtual int itemCount() const;
virtual ScriptValueP getMember(const String& name) const;
};
// ----------------------------------------------------------------------------- : EOF
+159 -159
View File
@@ -18,7 +18,7 @@
#include <wx/stdpaths.h>
#if defined(__WXMSW__)
#include <wx/mstream.h>
#include <wx/mstream.h>
#endif
DECLARE_TYPEOF(map<String COMMA SubLocaleP>);
@@ -31,9 +31,9 @@ typedef void (*ReaderPragmaHandler)(String&);
DECLARE_DYNAMIC_ARG(ReaderPragmaHandler,reader_pragma_handler);
void ignore_add_pragma(String& str) {
if (starts_with(str,_("#_ADD "))) str = str.substr(6);
else if (starts_with(str,_("#_ADD"))) str = str.substr(5);
else if (starts_with(str,_("#_DEL"))) str.clear();
if (starts_with(str,_("#_ADD "))) str = str.substr(6);
else if (starts_with(str,_("#_ADD"))) str = str.substr(5);
else if (starts_with(str,_("#_DEL"))) str.clear();
}
// ----------------------------------------------------------------------------- : Locale class
@@ -44,92 +44,92 @@ String Locale::typeName() const { return _("locale"); }
Version Locale::fileVersion() const { return file_version_locale; }
LocaleP Locale::byName(const String& name) {
return package_manager.open<Locale>(name + _(".mse-locale"));
return package_manager.open<Locale>(name + _(".mse-locale"));
}
IMPLEMENT_REFLECTION_NO_SCRIPT(Locale) {
REFLECT_BASE(Packaged);
WITH_DYNAMIC_ARG(reader_pragma_handler, ignore_add_pragma);
REFLECT_N("menu", translations[LOCALE_CAT_MENU]);
REFLECT_N("help", translations[LOCALE_CAT_HELP]);
REFLECT_N("tool", translations[LOCALE_CAT_TOOL]);
REFLECT_N("tooltip", translations[LOCALE_CAT_TOOLTIP]);
REFLECT_N("label", translations[LOCALE_CAT_LABEL]);
REFLECT_N("button", translations[LOCALE_CAT_BUTTON]);
REFLECT_N("title", translations[LOCALE_CAT_TITLE]);
REFLECT_N("action", translations[LOCALE_CAT_ACTION]);
REFLECT_N("error", translations[LOCALE_CAT_ERROR]);
REFLECT_N("type", translations[LOCALE_CAT_TYPE]);
REFLECT_N("package", package_translations);
REFLECT_BASE(Packaged);
WITH_DYNAMIC_ARG(reader_pragma_handler, ignore_add_pragma);
REFLECT_N("menu", translations[LOCALE_CAT_MENU]);
REFLECT_N("help", translations[LOCALE_CAT_HELP]);
REFLECT_N("tool", translations[LOCALE_CAT_TOOL]);
REFLECT_N("tooltip", translations[LOCALE_CAT_TOOLTIP]);
REFLECT_N("label", translations[LOCALE_CAT_LABEL]);
REFLECT_N("button", translations[LOCALE_CAT_BUTTON]);
REFLECT_N("title", translations[LOCALE_CAT_TITLE]);
REFLECT_N("action", translations[LOCALE_CAT_ACTION]);
REFLECT_N("error", translations[LOCALE_CAT_ERROR]);
REFLECT_N("type", translations[LOCALE_CAT_TYPE]);
REFLECT_N("package", package_translations);
}
IMPLEMENT_REFLECTION_NO_GET_MEMBER(SubLocale) {
REFLECT_NAMELESS(translations);
REFLECT_NAMELESS(translations);
}
// ----------------------------------------------------------------------------- : Wildcards
bool match_wildcard(const String& wildcard, const String& name) {
return Regex(replace_all(replace_all(wildcard, _("."), _("\\.")), _("*"), _(".*"))).matches(name);
return Regex(replace_all(replace_all(wildcard, _("."), _("\\.")), _("*"), _(".*"))).matches(name);
}
SubLocaleP find_wildcard(map<String,SubLocaleP>& items, const String& name) {
FOR_EACH_CONST(i, items) {
if (i.second && match_wildcard(i.first, name)) return i.second;
}
return intrusive(new SubLocale()); // so we don't search again
FOR_EACH_CONST(i, items) {
if (i.second && match_wildcard(i.first, name)) return i.second;
}
return intrusive(new SubLocale()); // so we don't search again
}
SubLocaleP find_wildcard_and_set(map<String,SubLocaleP>& items, const String& name) {
return items[name] = find_wildcard(items, name);
return items[name] = find_wildcard(items, name);
}
// ----------------------------------------------------------------------------- : Translation
String warn_and_identity(const String& key) {
queue_message(MESSAGE_WARNING, _("Missing key in locale: ") + key);
return key;
queue_message(MESSAGE_WARNING, _("Missing key in locale: ") + key);
return key;
}
String SubLocale::tr(const String& key, DefaultLocaleFun def) {
map<String,String>::const_iterator it = translations.find(key);
if (it == translations.end()) {
return def(key);
} else {
return it->second;
}
map<String,String>::const_iterator it = translations.find(key);
if (it == translations.end()) {
return def(key);
} else {
return it->second;
}
}
String SubLocale::tr(const String& subcat, const String& key, DefaultLocaleFun def) {
map<String,String>::const_iterator it = translations.find(subcat + _(" ") + key);
if (it == translations.end()) {
return def(key);
} else {
return it->second;
}
map<String,String>::const_iterator it = translations.find(subcat + _(" ") + key);
if (it == translations.end()) {
return def(key);
} else {
return it->second;
}
}
// from util/locale.hpp
String tr(LocaleCategory cat, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key); // no locale loaded (yet)
return the_locale->translations[cat].tr(key,def);
if (!the_locale) return def(key); // no locale loaded (yet)
return the_locale->translations[cat].tr(key,def);
}
String tr(const Package& pkg, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg.relativeFilename()];
if (!loc) {
loc = find_wildcard_and_set(the_locale->package_translations, pkg.relativeFilename());
}
return loc->tr(key, def);
if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg.relativeFilename()];
if (!loc) {
loc = find_wildcard_and_set(the_locale->package_translations, pkg.relativeFilename());
}
return loc->tr(key, def);
}
String tr(const Package& pkg, const String& subcat, const String& key, DefaultLocaleFun def) {
if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg.relativeFilename()];
if (!loc) {
loc = find_wildcard_and_set(the_locale->package_translations, pkg.relativeFilename());
}
return loc->tr(subcat, key, def);
if (!the_locale) return def(key);
SubLocaleP loc = the_locale->package_translations[pkg.relativeFilename()];
if (!loc) {
loc = find_wildcard_and_set(the_locale->package_translations, pkg.relativeFilename());
}
return loc->tr(subcat, key, def);
}
// ----------------------------------------------------------------------------- : Validation
@@ -138,61 +138,61 @@ DECLARE_POINTER_TYPE(SubLocaleValidator);
class KeyValidator {
public:
int args;
bool optional;
DECLARE_REFLECTION();
int args;
bool optional;
DECLARE_REFLECTION();
};
class SubLocaleValidator : public IntrusivePtrBase<SubLocaleValidator> {
public:
map<String,KeyValidator> keys; ///< Arg count for each key
DECLARE_REFLECTION();
map<String,KeyValidator> keys; ///< Arg count for each key
DECLARE_REFLECTION();
};
/// Validation information for locales
class LocaleValidator {
public:
map<String, SubLocaleValidatorP> sublocales;
DECLARE_REFLECTION();
map<String, SubLocaleValidatorP> sublocales;
DECLARE_REFLECTION();
};
template <> void Reader::handle(KeyValidator& k) {
String v = getValue();
if (starts_with(v, _("optional, "))) {
k.optional = true;
v = v.substr(10);
} else {
k.optional = false;
}
long l = 0;
v.ToLong(&l);
k.args = l;
String v = getValue();
if (starts_with(v, _("optional, "))) {
k.optional = true;
v = v.substr(10);
} else {
k.optional = false;
}
long l = 0;
v.ToLong(&l);
k.args = l;
}
template <> void Writer::handle(const KeyValidator& v) {
assert(false);
assert(false);
}
IMPLEMENT_REFLECTION_NO_SCRIPT(SubLocaleValidator) {
REFLECT_NAMELESS(keys);
REFLECT_NAMELESS(keys);
}
IMPLEMENT_REFLECTION_NO_SCRIPT(LocaleValidator) {
REFLECT_NAMELESS(sublocales);
REFLECT_NAMELESS(sublocales);
}
/// Count "%s" in str
int string_format_args(const String& str) {
int count = 0;
bool in_percent = false;
FOR_EACH_CONST(c, str) {
if (in_percent) {
if (c == _('s')) {
count++;
}
in_percent = false;
} else if (c == _('%')) {
in_percent = true;
}
}
return count;
int count = 0;
bool in_percent = false;
FOR_EACH_CONST(c, str) {
if (in_percent) {
if (c == _('s')) {
count++;
}
in_percent = false;
} else if (c == _('%')) {
in_percent = true;
}
}
return count;
}
/// Load a text file from a resource
@@ -200,21 +200,21 @@ int string_format_args(const String& str) {
*/
InputStreamP load_resource_text(const String& name);
InputStreamP load_resource_text(const String& name) {
#if defined(__WXMSW__) && !defined(__GNUC__)
HRSRC hResource = ::FindResource(wxGetInstance(), name, _("TEXT"));
if ( hResource == 0 ) throw InternalError(String::Format(_("Resource not found: %s"), name));
HGLOBAL hData = ::LoadResource(wxGetInstance(), hResource);
if ( hData == 0 ) throw InternalError(String::Format(_("Resource not text: %s"), name));
char* data = (char *)::LockResource(hData);
if ( !data ) throw InternalError(String::Format(_("Resource cannot be locked: %s"), name));
int len = ::SizeofResource(wxGetInstance(), hResource);
return shared(new wxMemoryInputStream(data, len));
#else
#if defined(__WXMSW__) && !defined(__GNUC__)
HRSRC hResource = ::FindResource(wxGetInstance(), name, _("TEXT"));
if ( hResource == 0 ) throw InternalError(String::Format(_("Resource not found: %s"), name));
HGLOBAL hData = ::LoadResource(wxGetInstance(), hResource);
if ( hData == 0 ) throw InternalError(String::Format(_("Resource not text: %s"), name));
char* data = (char *)::LockResource(hData);
if ( !data ) throw InternalError(String::Format(_("Resource cannot be locked: %s"), name));
int len = ::SizeofResource(wxGetInstance(), hResource);
return shared(new wxMemoryInputStream(data, len));
#else
static String path = wxStandardPaths::Get().GetDataDir() + _("/resource/");
static String local_path = wxStandardPaths::Get().GetUserDataDir() + _("/resource/");
if (wxFileExists(path + name)) return shared(new wxFileInputStream(path + name));
else return shared(new wxFileInputStream(local_path + name));
#endif
#endif
}
@@ -222,69 +222,69 @@ DECLARE_TYPEOF(map<String COMMA String>);
DECLARE_TYPEOF(map<String COMMA KeyValidator>);
void Locale::validate(Version ver) {
Packaged::validate(ver);
// load locale validator
LocaleValidator v;
Reader r(load_resource_text(_("expected_locale_keys")), nullptr, _("expected_locale_keys"));
r.handle_greedy(v);
// validate
String errors;
errors += translations[LOCALE_CAT_MENU ].validate(_("menu"), v.sublocales[_("menu") ]);
errors += translations[LOCALE_CAT_HELP ].validate(_("help"), v.sublocales[_("help") ]);
errors += translations[LOCALE_CAT_TOOL ].validate(_("tool"), v.sublocales[_("tool") ]);
errors += translations[LOCALE_CAT_TOOLTIP].validate(_("tooltip"), v.sublocales[_("tooltip")]);
errors += translations[LOCALE_CAT_LABEL ].validate(_("label"), v.sublocales[_("label") ]);
errors += translations[LOCALE_CAT_BUTTON ].validate(_("button"), v.sublocales[_("button") ]);
errors += translations[LOCALE_CAT_TITLE ].validate(_("title"), v.sublocales[_("title") ]);
errors += translations[LOCALE_CAT_ACTION ].validate(_("action"), v.sublocales[_("action") ]);
errors += translations[LOCALE_CAT_ERROR ].validate(_("error"), v.sublocales[_("error") ]);
errors += translations[LOCALE_CAT_TYPE ].validate(_("type"), v.sublocales[_("type") ]);
// errors?
if (!errors.empty()) {
if (ver != file_version_locale) {
errors = _("Errors in locale file ") + short_name + _(":") + errors;
} else {
errors = _("Errors in locale file ") + short_name +
_("\nThis is probably because the locale was made for a different version of MSE.") + errors;
}
} else if (ver != file_version_locale) {
errors = _("Errors in locale file ") + short_name + _(":")
+ _("\n Locale file out of date, expected: mse version: ") + file_version_locale.toString()
+ _("\n found: ") + ver.toString();
}
if (!errors.empty()) {
queue_message(MESSAGE_WARNING, errors);
}
Packaged::validate(ver);
// load locale validator
LocaleValidator v;
Reader r(load_resource_text(_("expected_locale_keys")), nullptr, _("expected_locale_keys"));
r.handle_greedy(v);
// validate
String errors;
errors += translations[LOCALE_CAT_MENU ].validate(_("menu"), v.sublocales[_("menu") ]);
errors += translations[LOCALE_CAT_HELP ].validate(_("help"), v.sublocales[_("help") ]);
errors += translations[LOCALE_CAT_TOOL ].validate(_("tool"), v.sublocales[_("tool") ]);
errors += translations[LOCALE_CAT_TOOLTIP].validate(_("tooltip"), v.sublocales[_("tooltip")]);
errors += translations[LOCALE_CAT_LABEL ].validate(_("label"), v.sublocales[_("label") ]);
errors += translations[LOCALE_CAT_BUTTON ].validate(_("button"), v.sublocales[_("button") ]);
errors += translations[LOCALE_CAT_TITLE ].validate(_("title"), v.sublocales[_("title") ]);
errors += translations[LOCALE_CAT_ACTION ].validate(_("action"), v.sublocales[_("action") ]);
errors += translations[LOCALE_CAT_ERROR ].validate(_("error"), v.sublocales[_("error") ]);
errors += translations[LOCALE_CAT_TYPE ].validate(_("type"), v.sublocales[_("type") ]);
// errors?
if (!errors.empty()) {
if (ver != file_version_locale) {
errors = _("Errors in locale file ") + short_name + _(":") + errors;
} else {
errors = _("Errors in locale file ") + short_name +
_("\nThis is probably because the locale was made for a different version of MSE.") + errors;
}
} else if (ver != file_version_locale) {
errors = _("Errors in locale file ") + short_name + _(":")
+ _("\n Locale file out of date, expected: mse version: ") + file_version_locale.toString()
+ _("\n found: ") + ver.toString();
}
if (!errors.empty()) {
queue_message(MESSAGE_WARNING, errors);
}
}
String SubLocale::validate(const String& name, const SubLocaleValidatorP& v) const {
if (!v) {
return _("\nInternal error validating local file: expected keys file missing for \"") + name + _("\" section.");
}
String errors;
// 1. keys in v but not in this, check arg count
FOR_EACH_CONST(kc, v->keys) {
map<String,String>::const_iterator it = translations.find(kc.first);
if (it == translations.end()) {
if (!kc.second.optional) {
errors += _("\n Missing key:\t\t\t") + name + _(": ") + kc.first;
}
} else if (string_format_args(it->second) != kc.second.args) {
errors += _("\n Incorrect number of arguments for:\t") + name + _(": ") + kc.first
+ String::Format(_("\t expected: %d, found %d"), kc.second.args, string_format_args(it->second));
}
}
// 2. keys in this but not in v
FOR_EACH_CONST(kv, translations) {
map<String,KeyValidator>::const_iterator it = v->keys.find(kv.first);
if (it == v->keys.end() && !kv.second.empty()) {
// allow extra keys with empty values as a kind of documentation
// for example in the help stirngs:
// help:
// file:
// new set: blah blah
errors += _("\n Unexpected key:\t\t\t") + name + _(": ") + kv.first;
}
}
return errors;
if (!v) {
return _("\nInternal error validating local file: expected keys file missing for \"") + name + _("\" section.");
}
String errors;
// 1. keys in v but not in this, check arg count
FOR_EACH_CONST(kc, v->keys) {
map<String,String>::const_iterator it = translations.find(kc.first);
if (it == translations.end()) {
if (!kc.second.optional) {
errors += _("\n Missing key:\t\t\t") + name + _(": ") + kc.first;
}
} else if (string_format_args(it->second) != kc.second.args) {
errors += _("\n Incorrect number of arguments for:\t") + name + _(": ") + kc.first
+ String::Format(_("\t expected: %d, found %d"), kc.second.args, string_format_args(it->second));
}
}
// 2. keys in this but not in v
FOR_EACH_CONST(kv, translations) {
map<String,KeyValidator>::const_iterator it = v->keys.find(kv.first);
if (it == v->keys.end() && !kv.second.empty()) {
// allow extra keys with empty values as a kind of documentation
// for example in the help stirngs:
// help:
// file:
// new set: blah blah
errors += _("\n Unexpected key:\t\t\t") + name + _(": ") + kv.first;
}
}
return errors;
}
+24 -24
View File
@@ -23,36 +23,36 @@ DECLARE_POINTER_TYPE(SubLocaleValidator);
/// Translations of the texts of a game/stylesheet/symbolfont
class SubLocale : public IntrusivePtrBase<SubLocale> {
public:
map<String,String> translations;
/// Translate a key, if not found, apply the default function to the key
String tr(const String& key, DefaultLocaleFun def);
String tr(const String& subcat, const String& key, DefaultLocaleFun def);
/// Is this a valid sublocale? Returns errors
String validate(const String& name, const SubLocaleValidatorP&) const;
DECLARE_REFLECTION();
map<String,String> translations;
/// Translate a key, if not found, apply the default function to the key
String tr(const String& key, DefaultLocaleFun def);
String tr(const String& subcat, const String& key, DefaultLocaleFun def);
/// Is this a valid sublocale? Returns errors
String validate(const String& name, const SubLocaleValidatorP&) const;
DECLARE_REFLECTION();
};
/// A collection of translations of messages
class Locale : public Packaged {
public:
/// Translations of UI strings in each category
SubLocale translations[LOCALE_CAT_MAX];
/// Translations of Package specific texts, by relativeFilename
map<String,SubLocaleP> package_translations;
/// Open a locale with the given name
static LocaleP byName(const String& name);
/// Validate that the locale is valid for this MSE version
virtual void validate(Version = app_version);
/// Translations of UI strings in each category
SubLocale translations[LOCALE_CAT_MAX];
/// Translations of Package specific texts, by relativeFilename
map<String,SubLocaleP> package_translations;
/// Open a locale with the given name
static LocaleP byName(const String& name);
/// Validate that the locale is valid for this MSE version
virtual void validate(Version = app_version);
protected:
String typeName() const;
Version fileVersion() const;
DECLARE_REFLECTION();
String typeName() const;
Version fileVersion() const;
DECLARE_REFLECTION();
};
/// The global locale object
+338 -338
View File
@@ -23,424 +23,424 @@ DECLARE_TYPEOF_CONST(map<String COMMA PackInstanceP>);
IMPLEMENT_REFLECTION_ENUM(PackSelectType) {
VALUE_N("auto", SELECT_AUTO);
VALUE_N("all", SELECT_ALL);
VALUE_N("no replace", SELECT_NO_REPLACE);
VALUE_N("replace", SELECT_REPLACE);
VALUE_N("proportional", SELECT_PROPORTIONAL);
VALUE_N("nonempty", SELECT_NONEMPTY);
VALUE_N("equal", SELECT_EQUAL);
VALUE_N("equal proportional", SELECT_EQUAL_PROPORTIONAL);
VALUE_N("equal nonempty", SELECT_NONEMPTY);
VALUE_N("first", SELECT_FIRST);
VALUE_N("auto", SELECT_AUTO);
VALUE_N("all", SELECT_ALL);
VALUE_N("no replace", SELECT_NO_REPLACE);
VALUE_N("replace", SELECT_REPLACE);
VALUE_N("proportional", SELECT_PROPORTIONAL);
VALUE_N("nonempty", SELECT_NONEMPTY);
VALUE_N("equal", SELECT_EQUAL);
VALUE_N("equal proportional", SELECT_EQUAL_PROPORTIONAL);
VALUE_N("equal nonempty", SELECT_NONEMPTY);
VALUE_N("first", SELECT_FIRST);
}
IMPLEMENT_REFLECTION(PackType) {
REFLECT(name);
REFLECT(enabled);
REFLECT(selectable);
REFLECT(summary);
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;
}
}
REFLECT(name);
REFLECT(enabled);
REFLECT(selectable);
REFLECT(summary);
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;
}
}
}
IMPLEMENT_REFLECTION(PackItem) {
if (!tag.isComplex()) {
REFLECT_NAMELESS(name);
} else {
REFLECT(name);
REFLECT(amount);
REFLECT(weight);
}
if (!tag.isComplex()) {
REFLECT_NAMELESS(name);
} else {
REFLECT(name);
REFLECT(amount);
REFLECT(weight);
}
}
PackType::PackType()
: enabled(true)
, selectable(indeterminate)
, summary(indeterminate)
, select(SELECT_AUTO)
: enabled(true)
, selectable(indeterminate)
, summary(indeterminate)
, select(SELECT_AUTO)
{}
PackItem::PackItem()
: amount(1)
, weight(1)
: amount(1)
, weight(1)
{}
PackItem::PackItem(const String& name, int amount)
: name(name)
, amount(amount)
, weight(1)
: name(name)
, amount(amount)
, weight(1)
{}
bool PackType::update(Context& ctx) {
bool change = enabled.update(ctx);
FOR_EACH(item, items) {
change |= item->update(ctx);
}
return change;
bool change = enabled.update(ctx);
FOR_EACH(item, items) {
change |= item->update(ctx);
}
return change;
}
bool PackItem::update(Context& ctx) {
return amount.update(ctx)
| weight.update(ctx);
return amount.update(ctx)
| weight.update(ctx);
}
// ----------------------------------------------------------------------------- : PackInstance
PackInstance::PackInstance(const PackType& pack_type, PackGenerator& parent)
: pack_type(pack_type)
, parent(parent)
, requested_copies(0)
, card_copies(0)
, expected_copies(0)
: pack_type(pack_type)
, parent(parent)
, requested_copies(0)
, card_copies(0)
, expected_copies(0)
{
// Filter cards
if (pack_type.filter) {
FOR_EACH(card, parent.set->cards) {
Context& ctx = parent.set->getContext(card);
bool keep = *pack_type.filter.invoke(ctx);
if (keep) {
cards.push_back(card);
}
}
}
// Sum of weights
if (pack_type.select == SELECT_FIRST) {
total_weight = cards.empty() ? 0 : 1;
} else {
total_weight = cards.size();
}
FOR_EACH_CONST(item, pack_type.items) {
if (pack_type.select == SELECT_PROPORTIONAL || pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
total_weight += item->weight * parent.get(item->name).total_weight;
} else if (pack_type.select == SELECT_NONEMPTY || pack_type.select == SELECT_EQUAL_NONEMPTY) {
if (parent.get(item->name).total_weight > 0) {
total_weight += item->weight;
}
} else if (pack_type.select == SELECT_FIRST) {
if (total_weight <= 0) {
total_weight = item->weight;
break;
}
} else {
total_weight += item->weight;
}
}
// Depth
depth = 0;
FOR_EACH_CONST(item, pack_type.items) {
depth = max(depth, 1 + parent.get(item->name).depth);
}
// Filter cards
if (pack_type.filter) {
FOR_EACH(card, parent.set->cards) {
Context& ctx = parent.set->getContext(card);
bool keep = *pack_type.filter.invoke(ctx);
if (keep) {
cards.push_back(card);
}
}
}
// Sum of weights
if (pack_type.select == SELECT_FIRST) {
total_weight = cards.empty() ? 0 : 1;
} else {
total_weight = cards.size();
}
FOR_EACH_CONST(item, pack_type.items) {
if (pack_type.select == SELECT_PROPORTIONAL || pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
total_weight += item->weight * parent.get(item->name).total_weight;
} else if (pack_type.select == SELECT_NONEMPTY || pack_type.select == SELECT_EQUAL_NONEMPTY) {
if (parent.get(item->name).total_weight > 0) {
total_weight += item->weight;
}
} else if (pack_type.select == SELECT_FIRST) {
if (total_weight <= 0) {
total_weight = item->weight;
break;
}
} else {
total_weight += item->weight;
}
}
// Depth
depth = 0;
FOR_EACH_CONST(item, pack_type.items) {
depth = max(depth, 1 + parent.get(item->name).depth);
}
}
void PackInstance::expect_copy(double copies) {
this->expected_copies += copies;
// propagate
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
if (pack_type.select == SELECT_ALL) {
i.expect_copy(copies * item->amount);
} else if (pack_type.select == SELECT_PROPORTIONAL || pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
i.expect_copy(copies * item->amount * item->weight * i.total_weight / total_weight);
} else if (pack_type.select == SELECT_NONEMPTY || pack_type.select == SELECT_EQUAL_NONEMPTY) {
if (i.total_weight > 0) {
i.expect_copy(copies * item->amount * item->weight / total_weight);
}
} else if (pack_type.select == SELECT_FIRST) {
if (i.total_weight > 0 && cards.empty()) {
i.expect_copy(copies * item->amount);
break;
}
} else {
i.expect_copy(copies * item->amount * item->weight / total_weight);
}
}
this->expected_copies += copies;
// propagate
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
if (pack_type.select == SELECT_ALL) {
i.expect_copy(copies * item->amount);
} else if (pack_type.select == SELECT_PROPORTIONAL || pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
i.expect_copy(copies * item->amount * item->weight * i.total_weight / total_weight);
} else if (pack_type.select == SELECT_NONEMPTY || pack_type.select == SELECT_EQUAL_NONEMPTY) {
if (i.total_weight > 0) {
i.expect_copy(copies * item->amount * item->weight / total_weight);
}
} else if (pack_type.select == SELECT_FIRST) {
if (i.total_weight > 0 && cards.empty()) {
i.expect_copy(copies * item->amount);
break;
}
} else {
i.expect_copy(copies * item->amount * item->weight / total_weight);
}
}
}
void PackInstance::request_copy(size_t copies) {
requested_copies += copies;
requested_copies += copies;
}
/// Random generator with random numbers in a range
template <typename Gen>
struct RandomRange {
RandomRange(Gen& gen) : gen(gen) {}
unsigned operator () (unsigned max) { return gen() % max; }
Gen& gen;
RandomRange(Gen& gen) : gen(gen) {}
unsigned operator () (unsigned max) { return gen() % max; }
Gen& gen;
};
struct WeightedItem {
double weight;
int count;
int tiebreaker;
double weight;
int count;
int tiebreaker;
};
struct CompareWeightedItems{
inline bool operator () (WeightedItem* a, WeightedItem* b) {
// compare (a->count+1)/a->weight <> (b->count+1)/b->weight
// prefer the one where this is lower, return true if b is prefered
double delta = b->weight * (a->count + 1) - a->weight * (b->count + 1);
if (delta < 0) return false;
if (delta > 0) return true;
return b->tiebreaker < a->tiebreaker;
}
inline bool operator () (WeightedItem* a, WeightedItem* b) {
// compare (a->count+1)/a->weight <> (b->count+1)/b->weight
// prefer the one where this is lower, return true if b is prefered
double delta = b->weight * (a->count + 1) - a->weight * (b->count + 1);
if (delta < 0) return false;
if (delta > 0) return true;
return b->tiebreaker < a->tiebreaker;
}
};
/// Distribute 'total' among the weighted items, higher weight items get chosen more often
void weighted_equal_divide(vector<WeightedItem>& items, int total) {
assert(!items.empty());
if (items.size() == 1) {
items.front().count = total;
} else {
priority_queue<WeightedItem*,vector<WeightedItem*>,CompareWeightedItems> pq;
for (size_t i = 0 ; i < items.size() ; ++i) {
pq.push(&items[i]);
}
while (total > 0) {
// repeatedly pick the item that minimizes, after incrementing count:
// max_wi wi->count/wi->weight
WeightedItem* wi = pq.top();pq.pop();
wi->count++;
total--;
pq.push(wi);
}
}
assert(!items.empty());
if (items.size() == 1) {
items.front().count = total;
} else {
priority_queue<WeightedItem*,vector<WeightedItem*>,CompareWeightedItems> pq;
for (size_t i = 0 ; i < items.size() ; ++i) {
pq.push(&items[i]);
}
while (total > 0) {
// repeatedly pick the item that minimizes, after incrementing count:
// max_wi wi->count/wi->weight
WeightedItem* wi = pq.top();pq.pop();
wi->count++;
total--;
pq.push(wi);
}
}
}
void PackInstance::generate(vector<CardP>* out) {
card_copies = 0;
if (requested_copies == 0) return;
if (pack_type.select == SELECT_ALL) {
// add all cards
generate_all(out, requested_copies);
card_copies = 0;
if (requested_copies == 0) return;
if (pack_type.select == SELECT_ALL) {
// add all cards
generate_all(out, requested_copies);
} else if (pack_type.select == SELECT_REPLACE
|| pack_type.select == SELECT_PROPORTIONAL
|| pack_type.select == SELECT_NONEMPTY) {
// multiple copies
for (size_t i = 0 ; i < requested_copies ; ++i) {
generate_one_random(out);
}
} else if (pack_type.select == SELECT_REPLACE
|| pack_type.select == SELECT_PROPORTIONAL
|| pack_type.select == SELECT_NONEMPTY) {
// multiple copies
for (size_t i = 0 ; i < requested_copies ; ++i) {
generate_one_random(out);
}
} else if (pack_type.select == SELECT_NO_REPLACE) {
if (!pack_type.items.empty()) {
throw Error(_("'select:no replace' is not yet supported in combination with 'items', only with 'filter'."));
}
card_copies += requested_copies;
// NOTE: there is no way to pick items without replacement
if (out && !cards.empty()) {
// to prevent us from being too predictable for small sets, periodically reshuffle
RandomRange<boost::mt19937> gen_range(parent.gen);
int max_per_batch = ((int)cards.size() + 1) / 2;
int rem = (int)requested_copies;
while (rem > 0) {
random_shuffle(cards.begin(), cards.end(), gen_range);
out->insert(out->end(), cards.begin(), cards.begin() + min(rem, max_per_batch));
rem -= max_per_batch;
}
}
} else if (pack_type.select == SELECT_NO_REPLACE) {
if (!pack_type.items.empty()) {
throw Error(_("'select:no replace' is not yet supported in combination with 'items', only with 'filter'."));
}
card_copies += requested_copies;
// NOTE: there is no way to pick items without replacement
if (out && !cards.empty()) {
// to prevent us from being too predictable for small sets, periodically reshuffle
RandomRange<boost::mt19937> gen_range(parent.gen);
int max_per_batch = ((int)cards.size() + 1) / 2;
int rem = (int)requested_copies;
while (rem > 0) {
random_shuffle(cards.begin(), cards.end(), gen_range);
out->insert(out->end(), cards.begin(), cards.begin() + min(rem, max_per_batch));
rem -= max_per_batch;
}
}
} else if (pack_type.select == SELECT_EQUAL
|| pack_type.select == SELECT_EQUAL_PROPORTIONAL
|| pack_type.select == SELECT_EQUAL_NONEMPTY) {
// equal selection instead of random
if (requested_copies == 1) {
// somewhat of a hack to keep things fair: just pick at random
// otherwise we would end up picking the lowest weight item
generate_one_random(out);
} else {
// 1. the weights of each item, and of the cards
vector<WeightedItem> weighted_items;
FOR_EACH_CONST(item, pack_type.items) {
WeightedItem wi = {0,0,parent.gen()};
if (pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
wi.weight = item->weight * parent.get(item->name).total_weight;
} else if (pack_type.select == SELECT_EQUAL_NONEMPTY) {
wi.weight = parent.get(item->name).total_weight > 0 ? static_cast<int>(item->weight) : 0;
} else {
wi.weight = item->weight;
}
weighted_items.push_back(wi);
}
WeightedItem wi = {cards.size(),0,parent.gen()};
weighted_items.push_back(wi);
// 2. divide the requested_copies among the cards and the items, taking the weights into account
weighted_equal_divide(weighted_items, (int)requested_copies);
// 3a. propagate to items
for (size_t j = 0 ; j < pack_type.items.size() ; ++j) {
const PackItem& item = *pack_type.items[j];
PackInstance& i = parent.get(item.name);
i.request_copy(item.amount * weighted_items[j].count);
}
// 3b. pick some cards
int new_card_copies = weighted_items.back().count;
card_copies += new_card_copies;
if (out && !cards.empty()) {
int div = new_card_copies / (int)cards.size();
int rem = new_card_copies % (int)cards.size();
// some copies of all cards
for (int i = 0 ; i < div ; ++i) {
out->insert(out->end(), cards.begin(), cards.end());
}
// pick the remainder at random
for (int i = 0 ; i < rem ; ++i) {
int nr = parent.gen() % cards.size();
out->push_back(cards.at(nr));
}
}
}
} else if (pack_type.select == SELECT_EQUAL
|| pack_type.select == SELECT_EQUAL_PROPORTIONAL
|| pack_type.select == SELECT_EQUAL_NONEMPTY) {
// equal selection instead of random
if (requested_copies == 1) {
// somewhat of a hack to keep things fair: just pick at random
// otherwise we would end up picking the lowest weight item
generate_one_random(out);
} else {
// 1. the weights of each item, and of the cards
vector<WeightedItem> weighted_items;
FOR_EACH_CONST(item, pack_type.items) {
WeightedItem wi = {0,0,parent.gen()};
if (pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
wi.weight = item->weight * parent.get(item->name).total_weight;
} else if (pack_type.select == SELECT_EQUAL_NONEMPTY) {
wi.weight = parent.get(item->name).total_weight > 0 ? static_cast<int>(item->weight) : 0;
} else {
wi.weight = item->weight;
}
weighted_items.push_back(wi);
}
WeightedItem wi = {cards.size(),0,parent.gen()};
weighted_items.push_back(wi);
// 2. divide the requested_copies among the cards and the items, taking the weights into account
weighted_equal_divide(weighted_items, (int)requested_copies);
// 3a. propagate to items
for (size_t j = 0 ; j < pack_type.items.size() ; ++j) {
const PackItem& item = *pack_type.items[j];
PackInstance& i = parent.get(item.name);
i.request_copy(item.amount * weighted_items[j].count);
}
// 3b. pick some cards
int new_card_copies = weighted_items.back().count;
card_copies += new_card_copies;
if (out && !cards.empty()) {
int div = new_card_copies / (int)cards.size();
int rem = new_card_copies % (int)cards.size();
// some copies of all cards
for (int i = 0 ; i < div ; ++i) {
out->insert(out->end(), cards.begin(), cards.end());
}
// pick the remainder at random
for (int i = 0 ; i < rem ; ++i) {
int nr = parent.gen() % cards.size();
out->push_back(cards.at(nr));
}
}
}
} else if (pack_type.select == SELECT_FIRST) {
if (!cards.empty()) {
// there is a card, pick it
card_copies += requested_copies;
if (out) out->insert(out->end(), requested_copies, cards.front());
} else {
// pick first nonempty item
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
if (i.total_weight > 0) {
i.request_copy(requested_copies * item->amount);
break;
}
}
}
}
requested_copies = 0;
} else if (pack_type.select == SELECT_FIRST) {
if (!cards.empty()) {
// there is a card, pick it
card_copies += requested_copies;
if (out) out->insert(out->end(), requested_copies, cards.front());
} else {
// pick first nonempty item
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
if (i.total_weight > 0) {
i.request_copy(requested_copies * item->amount);
break;
}
}
}
}
requested_copies = 0;
}
void PackInstance::generate_all(vector<CardP>* out, size_t copies) {
card_copies += copies * cards.size();
if (out) {
for (size_t i = 0 ; i < copies ; ++i) {
out->insert(out->end(), cards.begin(), cards.end());
}
}
// and all items
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
i.request_copy(copies * item->amount);
}
card_copies += copies * cards.size();
if (out) {
for (size_t i = 0 ; i < copies ; ++i) {
out->insert(out->end(), cards.begin(), cards.end());
}
}
// and all items
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
i.request_copy(copies * item->amount);
}
}
void PackInstance::generate_one_random(vector<CardP>* out) {
double r = parent.gen() * total_weight / parent.gen.max();
if (r < cards.size()) {
// pick a card
card_copies++;
if (out) {
int i = (int)r;
out->push_back(cards[i]);
}
} else {
// pick an item
r -= cards.size();
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
if (pack_type.select == SELECT_PROPORTIONAL || pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
r -= item->weight * i.total_weight;
} else if (pack_type.select == SELECT_NONEMPTY || pack_type.select == SELECT_EQUAL_NONEMPTY) {
if (i.total_weight > 0) r -= item->weight;
} else {
r -= item->weight;
}
// have we reached the item we were looking for?
if (r < 0) {
i.request_copy(item->amount);
break;
}
}
}
double r = parent.gen() * total_weight / parent.gen.max();
if (r < cards.size()) {
// pick a card
card_copies++;
if (out) {
int i = (int)r;
out->push_back(cards[i]);
}
} else {
// pick an item
r -= cards.size();
FOR_EACH_CONST(item, pack_type.items) {
PackInstance& i = parent.get(item->name);
if (pack_type.select == SELECT_PROPORTIONAL || pack_type.select == SELECT_EQUAL_PROPORTIONAL) {
r -= item->weight * i.total_weight;
} else if (pack_type.select == SELECT_NONEMPTY || pack_type.select == SELECT_EQUAL_NONEMPTY) {
if (i.total_weight > 0) r -= item->weight;
} else {
r -= item->weight;
}
// have we reached the item we were looking for?
if (r < 0) {
i.request_copy(item->amount);
break;
}
}
}
}
// ----------------------------------------------------------------------------- : PackGenerator
void PackGenerator::reset(const SetP& set, int seed) {
this->set = set;
gen.seed((unsigned)seed);
max_depth = 0;
instances.clear();
this->set = set;
gen.seed((unsigned)seed);
max_depth = 0;
instances.clear();
}
void PackGenerator::reset(int seed) {
gen.seed((unsigned)seed);
gen.seed((unsigned)seed);
}
PackInstance& PackGenerator::get(const String& name) {
assert(set);
PackInstanceP& instance = instances[name];
if (instance) {
return *instance;
} else {
FOR_EACH_CONST(type, set->pack_types) {
if (type->name == name) {
instance = PackInstanceP(new PackInstance(*type,*this));
max_depth = max(max_depth, instance->get_depth());
return *instance;
}
}
FOR_EACH_CONST(type, set->game->pack_types) {
if (type->name == name) {
instance = PackInstanceP(new PackInstance(*type,*this));
max_depth = max(max_depth, instance->get_depth());
return *instance;
}
}
throw Error(_ERROR_1_("pack type not found",name));
}
assert(set);
PackInstanceP& instance = instances[name];
if (instance) {
return *instance;
} else {
FOR_EACH_CONST(type, set->pack_types) {
if (type->name == name) {
instance = PackInstanceP(new PackInstance(*type,*this));
max_depth = max(max_depth, instance->get_depth());
return *instance;
}
}
FOR_EACH_CONST(type, set->game->pack_types) {
if (type->name == name) {
instance = PackInstanceP(new PackInstance(*type,*this));
max_depth = max(max_depth, instance->get_depth());
return *instance;
}
}
throw Error(_ERROR_1_("pack type not found",name));
}
}
PackInstance& PackGenerator::get(const PackTypeP& type) {
return get(type->name);
return get(type->name);
}
void PackGenerator::generate(vector<CardP>& out) {
if (!set) return;
// We generate from depth max_depth to 0
// instances can refer to other instances of lower depth, and generate
// can change the number of copies of those lower depth instances
for (int depth = max_depth ; depth >= 0 ; --depth) {
// in game file order
FOR_EACH_CONST(type, set->game->pack_types) {
PackInstance& i = get(type);
if (i.get_depth() == depth) {
i.generate(&out);
}
}
// ...and then set file order
FOR_EACH_CONST(type, set->pack_types) {
PackInstance& i = get(type);
if (i.get_depth() == depth) {
i.generate(&out);
}
}
}
if (!set) return;
// We generate from depth max_depth to 0
// instances can refer to other instances of lower depth, and generate
// can change the number of copies of those lower depth instances
for (int depth = max_depth ; depth >= 0 ; --depth) {
// in game file order
FOR_EACH_CONST(type, set->game->pack_types) {
PackInstance& i = get(type);
if (i.get_depth() == depth) {
i.generate(&out);
}
}
// ...and then set file order
FOR_EACH_CONST(type, set->pack_types) {
PackInstance& i = get(type);
if (i.get_depth() == depth) {
i.generate(&out);
}
}
}
}
void PackGenerator::update_card_counts() {
if (!set) return;
// update card_counts by using generate()
for (int depth = max_depth ; depth >= 0 ; --depth) {
FOR_EACH_CONST(i,instances) {
if (i.second->get_depth() == depth) {
i.second->generate(nullptr);
}
}
}
if (!set) return;
// update card_counts by using generate()
for (int depth = max_depth ; depth >= 0 ; --depth) {
FOR_EACH_CONST(i,instances) {
if (i.second->get_depth() == depth) {
i.second->generate(nullptr);
}
}
}
}
+88 -88
View File
@@ -26,57 +26,57 @@ class PackGenerator;
// ----------------------------------------------------------------------------- : PackType
enum PackSelectType
{ SELECT_AUTO
, SELECT_ALL
, SELECT_NO_REPLACE
, SELECT_REPLACE
, SELECT_PROPORTIONAL
, SELECT_NONEMPTY
, SELECT_EQUAL
, SELECT_EQUAL_PROPORTIONAL
, SELECT_EQUAL_NONEMPTY
, SELECT_FIRST
{ SELECT_AUTO
, SELECT_ALL
, SELECT_NO_REPLACE
, SELECT_REPLACE
, SELECT_PROPORTIONAL
, SELECT_NONEMPTY
, SELECT_EQUAL
, SELECT_EQUAL_PROPORTIONAL
, SELECT_EQUAL_NONEMPTY
, SELECT_FIRST
};
/// A card pack description for playtesting
class PackType : public IntrusivePtrBase<PackType> {
public:
PackType();
String name; ///< Name of this pack
Scriptable<bool> enabled; ///< Is this pack enabled?
tribool selectable; ///< Is this pack listed in the UI?
tribool summary; ///< Should the total be listed for this type?
PackSelectType select; ///< What cards/items to select
OptionalScript filter; ///< Filter to select this type of cards
vector<PackItemP> items; ///< Subpacks in this pack
/// Update scripts, returns true if there is a change
bool update(Context& ctx);
PackType();
String name; ///< Name of this pack
Scriptable<bool> enabled; ///< Is this pack enabled?
tribool selectable; ///< Is this pack listed in the UI?
tribool summary; ///< Should the total be listed for this type?
PackSelectType select; ///< What cards/items to select
OptionalScript filter; ///< Filter to select this type of cards
vector<PackItemP> items; ///< Subpacks in this pack
/// Update scripts, returns true if there is a change
bool update(Context& ctx);
private:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
/// An item in a PackType
class PackItem : public IntrusivePtrBase<PackItem> {
public:
PackItem();
PackItem(const String& name, int amount);
String name; ///< Name of the pack to select cards from
Scriptable<int> amount; ///< Number of cards of this type
Scriptable<double> weight; ///< Relative probability of picking this item
/// Update scripts, returns true if there is a change
bool update(Context& ctx);
PackItem();
PackItem(const String& name, int amount);
String name; ///< Name of the pack to select cards from
Scriptable<int> amount; ///< Number of cards of this type
Scriptable<double> weight; ///< Relative probability of picking this item
/// Update scripts, returns true if there is a change
bool update(Context& ctx);
private:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
inline String type_name(const PackType&) {
return _TYPE_("pack");
return _TYPE_("pack");
}
// ----------------------------------------------------------------------------- : Generating / counting
@@ -85,64 +85,64 @@ inline String type_name(const PackType&) {
// i.e. we now know the actual cards
class PackInstance : public IntrusivePtrBase<PackInstance> {
public:
PackInstance(const PackType& pack_type, PackGenerator& parent);
/// Expect to pick this many copies from this pack, updates expected_copies
void expect_copy(double copies = 1);
/// Request some copies of this pack
void request_copy(size_t copies = 1);
/// Generate cards if depth == at_depth
/** Some cards are (optionally) added to out and card_copies
* And also the copies of referenced items might be incremented
*
* Resets the count of this instance to 0 */
void generate(vector<CardP>* out);
inline int get_depth() const { return depth; }
inline bool has_cards() const { return !cards.empty(); }
inline size_t get_card_copies() const { return card_copies; }
inline double get_expected_copies() const { return expected_copies; }
PackInstance(const PackType& pack_type, PackGenerator& parent);
/// Expect to pick this many copies from this pack, updates expected_copies
void expect_copy(double copies = 1);
/// Request some copies of this pack
void request_copy(size_t copies = 1);
/// Generate cards if depth == at_depth
/** Some cards are (optionally) added to out and card_copies
* And also the copies of referenced items might be incremented
*
* Resets the count of this instance to 0 */
void generate(vector<CardP>* out);
inline int get_depth() const { return depth; }
inline bool has_cards() const { return !cards.empty(); }
inline size_t get_card_copies() const { return card_copies; }
inline double get_expected_copies() const { return expected_copies; }
private:
const PackType& pack_type;
PackGenerator& parent;
int depth; //< 0 = no items, otherwise 1+max depth of items refered to
vector<CardP> cards; //< All cards that pass the filter
double total_weight; //< Sum of item and card weights
size_t requested_copies; //< The requested number of copies of this pack
size_t card_copies; //< The number of cards that were chosen to come from this pack
double expected_copies;
/// Generate some copies of all cards and items
void generate_all(vector<CardP>* out, size_t copies);
/// Generate one card/item chosen at random (using the select type)
void generate_one_random(vector<CardP>* out);
const PackType& pack_type;
PackGenerator& parent;
int depth; //< 0 = no items, otherwise 1+max depth of items refered to
vector<CardP> cards; //< All cards that pass the filter
double total_weight; //< Sum of item and card weights
size_t requested_copies; //< The requested number of copies of this pack
size_t card_copies; //< The number of cards that were chosen to come from this pack
double expected_copies;
/// Generate some copies of all cards and items
void generate_all(vector<CardP>* out, size_t copies);
/// Generate one card/item chosen at random (using the select type)
void generate_one_random(vector<CardP>* out);
};
class PackGenerator {
public:
/// Reset the generator, possibly switching the set or reseeding
void reset(const SetP& set, int seed);
/// Reset the generator, but not the set
void reset(int seed);
/// Find the PackInstance for the PackType with the given name
PackInstance& get(const String& name);
PackInstance& get(const PackTypeP& type);
/// Generate all cards, resets copies
void generate(vector<CardP>& out);
/// Update all card_copies counters, resets copies
void update_card_counts();
// only for PackInstance
SetP set; ///< The set
boost::mt19937 gen; ///< Random generator
/// Reset the generator, possibly switching the set or reseeding
void reset(const SetP& set, int seed);
/// Reset the generator, but not the set
void reset(int seed);
/// Find the PackInstance for the PackType with the given name
PackInstance& get(const String& name);
PackInstance& get(const PackTypeP& type);
/// Generate all cards, resets copies
void generate(vector<CardP>& out);
/// Update all card_copies counters, resets copies
void update_card_counts();
// only for PackInstance
SetP set; ///< The set
boost::mt19937 gen; ///< Random generator
private:
/// Details for each PackType
map<String,PackInstanceP> instances;
int max_depth;
/// Details for each PackType
map<String,PackInstanceP> instances;
int max_depth;
};
// ----------------------------------------------------------------------------- : EOF
+194 -194
View File
@@ -29,98 +29,98 @@ DECLARE_TYPEOF_NO_REV(IndexMap<FieldP COMMA ValueP>);
// ----------------------------------------------------------------------------- : Set
Set::Set()
: vcs (intrusive(new VCS()))
, script_manager(new SetScriptManager(*this))
: vcs (intrusive(new VCS()))
, script_manager(new SetScriptManager(*this))
{}
Set::Set(const GameP& game)
: game(game)
, vcs (intrusive(new VCS()))
, script_manager(new SetScriptManager(*this))
: game(game)
, vcs (intrusive(new VCS()))
, script_manager(new SetScriptManager(*this))
{
data.init(game->set_fields);
data.init(game->set_fields);
}
Set::Set(const StyleSheetP& stylesheet)
: game(stylesheet->game)
, stylesheet(stylesheet)
, vcs (intrusive(new VCS()))
, script_manager(new SetScriptManager(*this))
: game(stylesheet->game)
, stylesheet(stylesheet)
, vcs (intrusive(new VCS()))
, script_manager(new SetScriptManager(*this))
{
data.init(game->set_fields);
data.init(game->set_fields);
}
Set::~Set() {}
Context& Set::getContext() {
assert(wxThread::IsMain());
return script_manager->getContext(stylesheet);
assert(wxThread::IsMain());
return script_manager->getContext(stylesheet);
}
Context& Set::getContext(const CardP& card) {
assert(wxThread::IsMain());
return script_manager->getContext(card);
assert(wxThread::IsMain());
return script_manager->getContext(card);
}
void Set::updateStyles(const CardP& card, bool only_content_dependent) {
script_manager->updateStyles(card, only_content_dependent);
script_manager->updateStyles(card, only_content_dependent);
}
void Set::updateDelayed() {
script_manager->updateDelayed();
script_manager->updateDelayed();
}
Context& Set::getContextForThumbnails() {
assert(!wxThread::IsMain());
if (!thumbnail_script_context) {
thumbnail_script_context.reset(new SetScriptContext(*this));
}
return thumbnail_script_context->getContext(stylesheet);
assert(!wxThread::IsMain());
if (!thumbnail_script_context) {
thumbnail_script_context.reset(new SetScriptContext(*this));
}
return thumbnail_script_context->getContext(stylesheet);
}
Context& Set::getContextForThumbnails(const CardP& card) {
assert(!wxThread::IsMain());
if (!thumbnail_script_context) {
thumbnail_script_context.reset(new SetScriptContext(*this));
}
return thumbnail_script_context->getContext(card);
assert(!wxThread::IsMain());
if (!thumbnail_script_context) {
thumbnail_script_context.reset(new SetScriptContext(*this));
}
return thumbnail_script_context->getContext(card);
}
Context& Set::getContextForThumbnails(const StyleSheetP& stylesheet) {
assert(!wxThread::IsMain());
if (!thumbnail_script_context) {
thumbnail_script_context.reset(new SetScriptContext(*this));
}
return thumbnail_script_context->getContext(stylesheet);
assert(!wxThread::IsMain());
if (!thumbnail_script_context) {
thumbnail_script_context.reset(new SetScriptContext(*this));
}
return thumbnail_script_context->getContext(stylesheet);
}
const StyleSheet& Set::stylesheetFor(const CardP& card) {
if (card && card->stylesheet) return *card->stylesheet;
else return *stylesheet;
if (card && card->stylesheet) return *card->stylesheet;
else return *stylesheet;
}
StyleSheetP Set::stylesheetForP(const CardP& card) {
if (card && card->stylesheet) return card->stylesheet;
else return stylesheet;
if (card && card->stylesheet) return card->stylesheet;
else return stylesheet;
}
IndexMap<FieldP, ValueP>& Set::stylingDataFor(const StyleSheet& stylesheet) {
return styling_data.get(stylesheet.name(), stylesheet.styling_fields);
return styling_data.get(stylesheet.name(), stylesheet.styling_fields);
}
IndexMap<FieldP, ValueP>& Set::stylingDataFor(const CardP& card) {
if (card && card->has_styling) return card->styling_data;
else return stylingDataFor(stylesheetFor(card));
if (card && card->has_styling) return card->styling_data;
else return stylingDataFor(stylesheetFor(card));
}
String Set::identification() const {
// an identifying field
FOR_EACH_CONST(v, data) {
if (v->fieldP->identifying) {
return v->toString();
}
}
// otherwise the first non-information field
FOR_EACH_CONST(v, data) {
if (!dynamic_pointer_cast<InfoValue>(v)) {
return v->toString();
}
}
return wxEmptyString;
// an identifying field
FOR_EACH_CONST(v, data) {
if (v->fieldP->identifying) {
return v->toString();
}
}
// otherwise the first non-information field
FOR_EACH_CONST(v, data) {
if (!dynamic_pointer_cast<InfoValue>(v)) {
return v->toString();
}
}
return wxEmptyString;
}
@@ -129,177 +129,177 @@ Version Set::fileVersion() const { return file_version_set; }
// fix values for versions < 0.2.7
void fix_value_207(const ValueP& value) {
if (TextValue* v = dynamic_cast<TextValue*>(value.get())) {
// text value -> fix it
v->value.assignDontChangeDefault( // don't change defaultness
fix_old_tags(v->value()) // remove tags
);
}
if (TextValue* v = dynamic_cast<TextValue*>(value.get())) {
// text value -> fix it
v->value.assignDontChangeDefault( // don't change defaultness
fix_old_tags(v->value()) // remove tags
);
}
}
void Set::validate(Version file_app_version) {
Packaged::validate(file_app_version);
// are the
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("set")));
}
if (!stylesheet) {
// TODO : Allow user to select a different style
throw Error(_ERROR_("no stylesheet specified for the set"));
}
if (stylesheet->game != game) {
throw Error(_ERROR_("stylesheet and set refer to different game"));
}
Packaged::validate(file_app_version);
// are the
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("set")));
}
if (!stylesheet) {
// TODO : Allow user to select a different style
throw Error(_ERROR_("no stylesheet specified for the set"));
}
if (stylesheet->game != game) {
throw Error(_ERROR_("stylesheet and set refer to different game"));
}
// This is our chance to fix version incompatabilities
if (file_app_version < 207) {
// Since 0.2.7 we use </tag> style close tags, in older versions it was </>
// Walk over all fields and fix...
FOR_EACH(c, cards) {
FOR_EACH(v, c->data) fix_value_207(v);
}
FOR_EACH(v, data) fix_value_207(v);
/* FOR_EACH(s, styleData) {
FOR_EACH(v, s.second->data) fix_value_207(v);
}
*/ }
// we want at least one card
if (cards.empty()) cards.push_back(intrusive(new Card(*game)));
// update scripts
script_manager->updateAll();
// This is our chance to fix version incompatabilities
if (file_app_version < 207) {
// Since 0.2.7 we use </tag> style close tags, in older versions it was </>
// Walk over all fields and fix...
FOR_EACH(c, cards) {
FOR_EACH(v, c->data) fix_value_207(v);
}
FOR_EACH(v, data) fix_value_207(v);
/* FOR_EACH(s, styleData) {
FOR_EACH(v, s.second->data) fix_value_207(v);
}
*/ }
// we want at least one card
if (cards.empty()) cards.push_back(intrusive(new Card(*game)));
// update scripts
script_manager->updateAll();
}
IMPLEMENT_REFLECTION(Set) {
REFLECT_ALIAS(300, "style", "stylesheet"); // < 0.3.0 used style instead of stylesheet
REFLECT_ALIAS(300, "extra set info", "styling");
REFLECT(game);
if (game) {
REFLECT_IF_READING {
data.init(game->set_fields);
}
WITH_DYNAMIC_ARG(game_for_reading, game.get());
REFLECT(stylesheet);
WITH_DYNAMIC_ARG(stylesheet_for_reading, stylesheet.get());
REFLECT_N("set_info", data);
if (stylesheet) {
REFLECT_N("styling", styling_data);
}
// Experimental: save each card to a different file
reflect_cards(tag);
REFLECT(keywords);
REFLECT(pack_types);
}
reflect_set_info_get_member(tag,data);
REFLECT_NO_SCRIPT_N("version control", vcs);
REFLECT(apprentice_code);
REFLECT_ALIAS(300, "style", "stylesheet"); // < 0.3.0 used style instead of stylesheet
REFLECT_ALIAS(300, "extra set info", "styling");
REFLECT(game);
if (game) {
REFLECT_IF_READING {
data.init(game->set_fields);
}
WITH_DYNAMIC_ARG(game_for_reading, game.get());
REFLECT(stylesheet);
WITH_DYNAMIC_ARG(stylesheet_for_reading, stylesheet.get());
REFLECT_N("set_info", data);
if (stylesheet) {
REFLECT_N("styling", styling_data);
}
// Experimental: save each card to a different file
reflect_cards(tag);
REFLECT(keywords);
REFLECT(pack_types);
}
reflect_set_info_get_member(tag,data);
REFLECT_NO_SCRIPT_N("version control", vcs);
REFLECT(apprentice_code);
}
// TODO: make this a more generic function to be used elsewhere
template <typename Tag>
void Set::reflect_cards (Tag& tag) {
REFLECT(cards);
REFLECT(cards);
}
template <>
void Set::reflect_cards<Writer> (Writer& tag) {
// When writing to a directory, we write each card in a separate file.
// We don't do this in zipfiles because it leads to bloat.
if (isZipfile()) {
REFLECT(cards);
} else {
set<String> used;
FOR_EACH(card, cards) {
// pick a unique filename for this card
// can't use Package::newFileName, because then we get conflicts with the previous save of the same card
String filename = _("card ") + normalize_internal_filename(clean_filename(card->identification()));
String full_name = filename;
int i = 0;
// When writing to a directory, we write each card in a separate file.
// We don't do this in zipfiles because it leads to bloat.
if (isZipfile()) {
REFLECT(cards);
} else {
set<String> used;
FOR_EACH(card, cards) {
// pick a unique filename for this card
// can't use Package::newFileName, because then we get conflicts with the previous save of the same card
String filename = _("card ") + normalize_internal_filename(clean_filename(card->identification()));
String full_name = filename;
int i = 0;
while (used.find(full_name) != used.end()) {
full_name = String(filename) << _(".") << ++i;
}
used.insert(full_name);
while (used.find(full_name) != used.end()) {
full_name = String(filename) << _(".") << ++i;
}
used.insert(full_name);
// writeFile won't quite work because we'd need
// include file: card: filename
// to do that
Writer writer(openOut(full_name), app_version);
writer.handle(_("card"), card);
referenceFile(full_name);
REFLECT_N("include file", full_name);
}
}
// writeFile won't quite work because we'd need
// include file: card: filename
// to do that
Writer writer(openOut(full_name), app_version);
writer.handle(_("card"), card);
referenceFile(full_name);
REFLECT_N("include file", full_name);
}
}
}
// ----------------------------------------------------------------------------- : Script utilities
ScriptValueP make_iterator(const Set& set) {
return intrusive(new ScriptCollectionIterator<vector<CardP> >(&set.cards));
return intrusive(new ScriptCollectionIterator<vector<CardP> >(&set.cards));
}
void mark_dependency_member(const Set& set, const String& name, const Dependency& dep) {
// is it the card list?
if (name == _("cards")) {
set.game->dependent_scripts_cards.add(dep);
return;
}
// is it the keywords?
if (name == _("keywords")) {
set.game->dependent_scripts_keywords.add(dep);
return;
}
// is it in the set data?
mark_dependency_member(set.data, name, dep);
// is it the card list?
if (name == _("cards")) {
set.game->dependent_scripts_cards.add(dep);
return;
}
// is it the keywords?
if (name == _("keywords")) {
set.game->dependent_scripts_keywords.add(dep);
return;
}
// is it in the set data?
mark_dependency_member(set.data, name, dep);
}
// in scripts, set.something is read from the set_info
template <typename Tag>
void reflect_set_info_get_member(Tag& tag, const IndexMap<FieldP, ValueP>& data) {}
void reflect_set_info_get_member(GetMember& tag, const IndexMap<FieldP, ValueP>& data) {
REFLECT_NAMELESS(data);
REFLECT_NAMELESS(data);
}
int Set::positionOfCard(const CardP& card, const ScriptValueP& order_by, const ScriptValueP& filter) {
// TODO : Lock the map?
assert(order_by);
OrderCacheP& order = order_cache[make_pair(order_by,filter)];
if (!order) {
// 1. make a list of the order value for each card
vector<String> values; values.reserve(cards.size());
vector<int> keep; if(filter) keep.reserve(cards.size());
FOR_EACH_CONST(c, cards) {
Context& ctx = getContext(c);
values.push_back(*order_by->eval(ctx));
if (filter) {
keep.push_back((bool)*filter->eval(ctx));
}
}
#if USE_SCRIPT_PROFILING
Timer t;
Profiler prof(t, order_by.get(), _("init order cache"));
#endif
// 3. initialize order cache
order = intrusive(new OrderCache<CardP>(cards, values, filter ? &keep : nullptr));
}
return order->find(card);
// TODO : Lock the map?
assert(order_by);
OrderCacheP& order = order_cache[make_pair(order_by,filter)];
if (!order) {
// 1. make a list of the order value for each card
vector<String> values; values.reserve(cards.size());
vector<int> keep; if(filter) keep.reserve(cards.size());
FOR_EACH_CONST(c, cards) {
Context& ctx = getContext(c);
values.push_back(*order_by->eval(ctx));
if (filter) {
keep.push_back((bool)*filter->eval(ctx));
}
}
#if USE_SCRIPT_PROFILING
Timer t;
Profiler prof(t, order_by.get(), _("init order cache"));
#endif
// 3. initialize order cache
order = intrusive(new OrderCache<CardP>(cards, values, filter ? &keep : nullptr));
}
return order->find(card);
}
int Set::numberOfCards(const ScriptValueP& filter) {
if (!filter) return (int)cards.size();
map<ScriptValueP,int>::const_iterator it = filter_cache.find(filter);
if (it !=filter_cache.end()) {
return it->second;
} else {
int n = 0;
FOR_EACH_CONST(c, cards) {
if (*filter->eval(getContext(c))) ++n;
}
filter_cache.insert(make_pair(filter,n));
return n;
}
if (!filter) return (int)cards.size();
map<ScriptValueP,int>::const_iterator it = filter_cache.find(filter);
if (it !=filter_cache.end()) {
return it->second;
} else {
int n = 0;
FOR_EACH_CONST(c, cards) {
if (*filter->eval(getContext(c))) ++n;
}
filter_cache.insert(make_pair(filter,n));
return n;
}
}
void Set::clearOrderCache() {
order_cache.clear();
filter_cache.clear();
order_cache.clear();
filter_cache.clear();
}
// ----------------------------------------------------------------------------- : SetView
@@ -307,17 +307,17 @@ void Set::clearOrderCache() {
SetView::SetView() {}
SetView::~SetView() {
if (set) set->actions.removeListener(this);
if (set) set->actions.removeListener(this);
}
void SetView::setSet(const SetP& newSet) {
// no longer listening to old set
if (set) {
onBeforeChangeSet();
set->actions.removeListener(this);
}
set = newSet;
// start listening to new set
if (set) set->actions.addListener(this);
onChangeSet();
// no longer listening to old set
if (set) {
onBeforeChangeSet();
set->actions.removeListener(this);
}
set = newSet;
// start listening to new set
if (set) set->actions.addListener(this);
onChangeSet();
}
+111 -111
View File
@@ -39,112 +39,112 @@ typedef intrusive_ptr<OrderCache<CardP> > OrderCacheP;
/// A set of cards
class Set : public Packaged {
public:
/// Create a set, the set should be open()ed later
Set();
/// Create a set using the given game
Set(const GameP& game);
/// Create a set using the given stylesheet, and its game
Set(const StyleSheetP& stylesheet);
~Set();
/// Create a set, the set should be open()ed later
Set();
/// Create a set using the given game
Set(const GameP& game);
/// Create a set using the given stylesheet, and its game
Set(const StyleSheetP& stylesheet);
~Set();
GameP game; ///< The game this set uses
StyleSheetP stylesheet; ///< The default stylesheet
/// The values on the fields of the set
/** The indices should correspond to the set_fields in the Game */
IndexMap<FieldP, ValueP> data;
/// Extra values for specitic stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> styling_data;
vector<CardP> cards; ///< The cards in the set
vector<KeywordP> keywords; ///< Additional keywords used in this set
vector<PackTypeP> pack_types; ///< Additional/replacement pack types
String apprentice_code; ///< Code to use for apprentice (Magic only)
GameP game; ///< The game this set uses
StyleSheetP stylesheet; ///< The default stylesheet
/// The values on the fields of the set
/** The indices should correspond to the set_fields in the Game */
IndexMap<FieldP, ValueP> data;
/// Extra values for specitic stylesheets, indexed by stylesheet name
DelayedIndexMaps<FieldP,ValueP> styling_data;
vector<CardP> cards; ///< The cards in the set
vector<KeywordP> keywords; ///< Additional keywords used in this set
vector<PackTypeP> pack_types; ///< Additional/replacement pack types
String apprentice_code; ///< Code to use for apprentice (Magic only)
ActionStack actions; ///< Actions performed on this set and the cards in it
KeywordDatabase keyword_db; ///< Database for matching keywords, must be cleared when keywords change
VCSP vcs; ///< The version control system to use
/// A context for performing scripts
/** Should only be used from the main thread! */
Context& getContext();
/// A context for performing scripts on a particular card
/** Should only be used from the main thread! */
Context& getContext(const CardP& card);
/// Update styles and extra_card_fields for a card
void updateStyles(const CardP& card, bool only_content_dependent);
/// Update scripts that were delayed
void updateDelayed();
/// A context for performing scripts
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails();
/// A context for performing scripts on a particular card
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails(const CardP& card);
/// A context for performing scripts on a particular stylesheet
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails(const StyleSheetP& stylesheet);
/// Stylesheet to use for a particular card
/** card may be null */
const StyleSheet& stylesheetFor (const CardP& card);
StyleSheetP stylesheetForP(const CardP& card);
/// Styling information for a particular stylesheet
IndexMap<FieldP, ValueP>& stylingDataFor(const StyleSheet&);
/// Styling information for a particular card
IndexMap<FieldP, ValueP>& stylingDataFor(const CardP& card);
/// Get the identification of this set, an identification is something like a name, title, etc.
/** May return "" */
String identification() const;
/// Find a value in the data by name and type
template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) {
if ((*it)->fieldP->name == name) {
T* ret = dynamic_cast<T*>(it->get());
if (!ret) throw InternalError(_("Set field with name '")+name+_("' doesn't have the right type"));
return *ret;
}
}
throw InternalError(_("Expected a set field with name '")+name+_("'"));
}
/// Find the position of a card in this set, when the card list is sorted using the given cirterium
int positionOfCard(const CardP& card, const ScriptValueP& order_by, const ScriptValueP& filter);
/// Find the number of cards that match the given filter
int numberOfCards(const ScriptValueP& filter);
/// Clear the order_cache used by positionOfCard
void clearOrderCache();
virtual String typeName() const;
Version fileVersion() const;
/// Validate that the set is correctly loaded
virtual void validate(Version = app_version);
ActionStack actions; ///< Actions performed on this set and the cards in it
KeywordDatabase keyword_db; ///< Database for matching keywords, must be cleared when keywords change
VCSP vcs; ///< The version control system to use
/// A context for performing scripts
/** Should only be used from the main thread! */
Context& getContext();
/// A context for performing scripts on a particular card
/** Should only be used from the main thread! */
Context& getContext(const CardP& card);
/// Update styles and extra_card_fields for a card
void updateStyles(const CardP& card, bool only_content_dependent);
/// Update scripts that were delayed
void updateDelayed();
/// A context for performing scripts
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails();
/// A context for performing scripts on a particular card
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails(const CardP& card);
/// A context for performing scripts on a particular stylesheet
/** Should only be used from the thumbnail thread! */
Context& getContextForThumbnails(const StyleSheetP& stylesheet);
/// Stylesheet to use for a particular card
/** card may be null */
const StyleSheet& stylesheetFor (const CardP& card);
StyleSheetP stylesheetForP(const CardP& card);
/// Styling information for a particular stylesheet
IndexMap<FieldP, ValueP>& stylingDataFor(const StyleSheet&);
/// Styling information for a particular card
IndexMap<FieldP, ValueP>& stylingDataFor(const CardP& card);
/// Get the identification of this set, an identification is something like a name, title, etc.
/** May return "" */
String identification() const;
/// Find a value in the data by name and type
template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) {
if ((*it)->fieldP->name == name) {
T* ret = dynamic_cast<T*>(it->get());
if (!ret) throw InternalError(_("Set field with name '")+name+_("' doesn't have the right type"));
return *ret;
}
}
throw InternalError(_("Expected a set field with name '")+name+_("'"));
}
/// Find the position of a card in this set, when the card list is sorted using the given cirterium
int positionOfCard(const CardP& card, const ScriptValueP& order_by, const ScriptValueP& filter);
/// Find the number of cards that match the given filter
int numberOfCards(const ScriptValueP& filter);
/// Clear the order_cache used by positionOfCard
void clearOrderCache();
virtual String typeName() const;
Version fileVersion() const;
/// Validate that the set is correctly loaded
virtual void validate(Version = app_version);
protected:
virtual VCSP getVCS() {
return vcs;
}
virtual VCSP getVCS() {
return vcs;
}
private:
DECLARE_REFLECTION();
template <typename Tag>
void reflect_cards (Tag& tag);
/// Object for managing and executing scripts
scoped_ptr<SetScriptManager> script_manager;
/// Object for executing scripts from the thumbnail thread
scoped_ptr<SetScriptContext> thumbnail_script_context;
/// Cache of cards ordered by some criterion
map<pair<ScriptValueP,ScriptValueP>,OrderCacheP> order_cache;
map<ScriptValueP,int> filter_cache;
DECLARE_REFLECTION();
template <typename Tag>
void reflect_cards (Tag& tag);
/// Object for managing and executing scripts
scoped_ptr<SetScriptManager> script_manager;
/// Object for executing scripts from the thumbnail thread
scoped_ptr<SetScriptContext> thumbnail_script_context;
/// Cache of cards ordered by some criterion
map<pair<ScriptValueP,ScriptValueP>,OrderCacheP> order_cache;
map<ScriptValueP,int> filter_cache;
};
inline String type_name(const Set&) {
return _TYPE_("set");
return _TYPE_("set");
}
inline int item_count(const Set& set) {
return (int)set.cards.size();
return (int)set.cards.size();
}
ScriptValueP make_iterator(const Set& set);
@@ -157,22 +157,22 @@ void mark_dependency_member(const Set& set, const String& name, const Dependency
*/
class SetView : public ActionListener {
public:
SetView();
~SetView();
/// Get the set that is currently being viewed
//inline SetP getSet() const { return set; }
/// Change the set that is being viewed
void setSet(const SetP& set);
SetView();
~SetView();
/// Get the set that is currently being viewed
//inline SetP getSet() const { return set; }
/// Change the set that is being viewed
void setSet(const SetP& set);
protected:
/// The set that is currently being viewed, should not be modified directly!
SetP set;
/// Called when another set is being viewed (using setSet)
virtual void onChangeSet() {}
/// Called when just before another set is being viewed (using setSet)
virtual void onBeforeChangeSet() {}
/// The set that is currently being viewed, should not be modified directly!
SetP set;
/// Called when another set is being viewed (using setSet)
virtual void onChangeSet() {}
/// Called when just before another set is being viewed (using setSet)
virtual void onBeforeChangeSet() {}
};
+194 -194
View File
@@ -28,135 +28,135 @@ DECLARE_TYPEOF_COLLECTION(AutoReplaceP);
// ----------------------------------------------------------------------------- : Extra types
IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
VALUE_N("if connected", CHECK_IF_CONNECTED); //default
VALUE_N("always", CHECK_ALWAYS);
VALUE_N("never", CHECK_NEVER);
VALUE_N("if connected", CHECK_IF_CONNECTED); //default
VALUE_N("always", CHECK_ALWAYS);
VALUE_N("never", CHECK_NEVER);
}
IMPLEMENT_REFLECTION_ENUM(InstallType) {
VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL);
VALUE_N("global", INSTALL_GLOBAL);
VALUE_N("default", INSTALL_DEFAULT); //default
VALUE_N("local", INSTALL_LOCAL);
VALUE_N("global", INSTALL_GLOBAL);
}
bool is_install_local(InstallType type) {
#ifdef __WXMSW__
#define DEFAULT_INSTALL_LOCAL false
#else
#define DEFAULT_INSTALL_LOCAL true
#endif
return type == INSTALL_DEFAULT ? DEFAULT_INSTALL_LOCAL : type == INSTALL_LOCAL;
#ifdef __WXMSW__
#define DEFAULT_INSTALL_LOCAL false
#else
#define DEFAULT_INSTALL_LOCAL true
#endif
return type == INSTALL_DEFAULT ? DEFAULT_INSTALL_LOCAL : type == INSTALL_LOCAL;
}
IMPLEMENT_REFLECTION_ENUM(FilenameConflicts) {
VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE);
VALUE_N("keep old", CONFLICT_KEEP_OLD);
VALUE_N("overwrite", CONFLICT_OVERWRITE);
VALUE_N("number", CONFLICT_NUMBER);
VALUE_N("number overwrite", CONFLICT_NUMBER_OVERWRITE);
}
const int COLUMN_NOT_INITIALIZED = -100000;
ColumnSettings::ColumnSettings()
: width(100), position(COLUMN_NOT_INITIALIZED), visible(false)
: width(100), position(COLUMN_NOT_INITIALIZED), visible(false)
{}
// dummy for ColumnSettings reflection
ScriptValueP to_script(const ColumnSettings&) { return script_nil; }
IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
REFLECT(width);
REFLECT(position);
REFLECT(visible);
REFLECT(width);
REFLECT(position);
REFLECT(visible);
}
GameSettings::GameSettings()
: sort_cards_ascending(true)
, images_export_filename(_("{card.name}.jpg"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace(true)
, pack_seed_random(true)
, pack_seed(123456)
, initialized(false)
: sort_cards_ascending(true)
, images_export_filename(_("{card.name}.jpg"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace(true)
, pack_seed_random(true)
, pack_seed(123456)
, initialized(false)
{}
void GameSettings::initDefaults(const Game& game) {
// Defer initialization until the game is fully loaded.
// This prevents data that needs to be initialized from
// being accessed from the new set window, but removes
// the need to load the entire file, which takes too long.
if (initialized || !game.isFullyLoaded()) return;
initialized = true;
// init auto_replaces, copy from game file
FOR_EACH_CONST(ar, game.auto_replaces) {
// do we have this one?
bool already_have = false;
FOR_EACH(ar2, auto_replaces) {
if (ar->match == ar2->match) {
ar2->custom = false;
already_have = true;
break;
}
}
if (!already_have) {
// TODO: when we start saving games, clone here
ar->custom = false;
auto_replaces.push_back(ar);
}
}
// Defer initialization until the game is fully loaded.
// This prevents data that needs to be initialized from
// being accessed from the new set window, but removes
// the need to load the entire file, which takes too long.
if (initialized || !game.isFullyLoaded()) return;
initialized = true;
// init auto_replaces, copy from game file
FOR_EACH_CONST(ar, game.auto_replaces) {
// do we have this one?
bool already_have = false;
FOR_EACH(ar2, auto_replaces) {
if (ar->match == ar2->match) {
ar2->custom = false;
already_have = true;
break;
}
}
if (!already_have) {
// TODO: when we start saving games, clone here
ar->custom = false;
auto_replaces.push_back(ar);
}
}
}
IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) {
REFLECT(default_stylesheet);
REFLECT(default_export);
REFLECT_N("cardlist columns", columns);
REFLECT(sort_cards_by);
REFLECT(sort_cards_ascending);
REFLECT(images_export_filename);
REFLECT(images_export_conflicts);
REFLECT(use_auto_replace);
REFLECT(auto_replaces);
REFLECT(pack_amounts);
REFLECT(pack_seed_random);
REFLECT(pack_seed);
REFLECT(default_stylesheet);
REFLECT(default_export);
REFLECT_N("cardlist columns", columns);
REFLECT(sort_cards_by);
REFLECT(sort_cards_ascending);
REFLECT(images_export_filename);
REFLECT(images_export_conflicts);
REFLECT(use_auto_replace);
REFLECT(auto_replaces);
REFLECT(pack_amounts);
REFLECT(pack_seed_random);
REFLECT(pack_seed);
}
StyleSheetSettings::StyleSheetSettings()
: card_zoom (1.0, true)
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_spellcheck_enabled(true, true)
: card_zoom (1.0, true)
, card_angle (0, true)
, card_anti_alias (true, true)
, card_borders (true, true)
, card_draw_editing (true, true)
, card_normal_export (true, true)
, card_spellcheck_enabled(true, true)
{}
void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_zoom .isDefault()) card_zoom .assignDefault(ss.card_zoom);
if (card_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias);
if (card_borders .isDefault()) card_borders .assignDefault(ss.card_borders);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing);
if (card_normal_export .isDefault()) card_normal_export .assignDefault(ss.card_normal_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
if (card_zoom .isDefault()) card_zoom .assignDefault(ss.card_zoom);
if (card_angle .isDefault()) card_angle .assignDefault(ss.card_angle);
if (card_anti_alias .isDefault()) card_anti_alias .assignDefault(ss.card_anti_alias);
if (card_borders .isDefault()) card_borders .assignDefault(ss.card_borders);
if (card_draw_editing .isDefault()) card_draw_editing .assignDefault(ss.card_draw_editing);
if (card_normal_export .isDefault()) card_normal_export .assignDefault(ss.card_normal_export);
if (card_spellcheck_enabled.isDefault()) card_spellcheck_enabled.assignDefault(ss.card_spellcheck_enabled);
}
IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
REFLECT(card_zoom);
REFLECT(card_angle);
REFLECT(card_anti_alias);
REFLECT(card_borders);
REFLECT(card_draw_editing);
REFLECT(card_normal_export);
REFLECT(card_spellcheck_enabled);
REFLECT(card_zoom);
REFLECT(card_angle);
REFLECT(card_anti_alias);
REFLECT(card_borders);
REFLECT(card_draw_editing);
REFLECT(card_normal_export);
REFLECT(card_spellcheck_enabled);
}
// ----------------------------------------------------------------------------- : Printing
IMPLEMENT_REFLECTION_ENUM(PageLayoutType) {
VALUE_N("no space", LAYOUT_NO_SPACE);
VALUE_N("equal space", LAYOUT_EQUAL_SPACE);
VALUE_N("no space", LAYOUT_NO_SPACE);
VALUE_N("equal space", LAYOUT_EQUAL_SPACE);
}
// ----------------------------------------------------------------------------- : Settings
@@ -164,145 +164,145 @@ IMPLEMENT_REFLECTION_ENUM(PageLayoutType) {
Settings settings;
Settings::Settings()
: locale (_("en"))
, set_window_maximized (false)
, set_window_width (790)
, set_window_height (300)
, card_notes_height (40)
, open_sets_in_new_window(true)
, symbol_grid_size (30)
, symbol_grid (true)
, symbol_grid_snap (false)
, print_layout (LAYOUT_NO_SPACE)
#if USE_OLD_STYLE_UPDATE_CHECKER
, updates_url (_("http://magicseteditor.sourceforge.net/updates"))
#endif
, package_versions_url (_("http://magicseteditor.sourceforge.net/packages"))
, installer_list_url (_("http://magicseteditor.sourceforge.net/installers"))
, check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true)
, website_url (_("http://magicseteditor.sourceforge.net/"))
, install_type (INSTALL_DEFAULT)
: locale (_("en"))
, set_window_maximized (false)
, set_window_width (790)
, set_window_height (300)
, card_notes_height (40)
, open_sets_in_new_window(true)
, symbol_grid_size (30)
, symbol_grid (true)
, symbol_grid_snap (false)
, print_layout (LAYOUT_NO_SPACE)
#if USE_OLD_STYLE_UPDATE_CHECKER
, updates_url (_("http://magicseteditor.sourceforge.net/updates"))
#endif
, package_versions_url (_("http://magicseteditor.sourceforge.net/packages"))
, installer_list_url (_("http://magicseteditor.sourceforge.net/installers"))
, check_updates (CHECK_IF_CONNECTED)
, check_updates_all (true)
, website_url (_("http://magicseteditor.sourceforge.net/"))
, install_type (INSTALL_DEFAULT)
{}
void Settings::addRecentFile(const String& filename) {
// get absolute path
wxFileName fn(filename);
fn.Normalize();
String filenameAbs = fn.GetFullPath();
// remove duplicates
recent_sets.erase(
remove(recent_sets.begin(), recent_sets.end(), filenameAbs),
recent_sets.end()
);
// add to front of list
recent_sets.insert(recent_sets.begin(), filenameAbs);
// enforce size limit
if (recent_sets.size() > max_recent_sets) recent_sets.resize(max_recent_sets);
// get absolute path
wxFileName fn(filename);
fn.Normalize();
String filenameAbs = fn.GetFullPath();
// remove duplicates
recent_sets.erase(
remove(recent_sets.begin(), recent_sets.end(), filenameAbs),
recent_sets.end()
);
// add to front of list
recent_sets.insert(recent_sets.begin(), filenameAbs);
// enforce size limit
if (recent_sets.size() > max_recent_sets) recent_sets.resize(max_recent_sets);
}
GameSettings& Settings::gameSettingsFor(const Game& game) {
GameSettingsP& gs = game_settings[game.name()];
if (!gs) gs = intrusive(new GameSettings);
gs->initDefaults(game);
return *gs;
GameSettingsP& gs = game_settings[game.name()];
if (!gs) gs = intrusive(new GameSettings);
gs->initDefaults(game);
return *gs;
}
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
// Get game info
GameSettings& gs = gameSettingsFor(game);
// Get column info
ColumnSettings& cs = gs.columns[field.name];
if (cs.position == COLUMN_NOT_INITIALIZED) {
// column info not set, initialize based on the game
cs.visible = field.card_list_visible;
cs.position = field.card_list_column;
cs.width = field.card_list_width;
}
return cs;
// Get game info
GameSettings& gs = gameSettingsFor(game);
// Get column info
ColumnSettings& cs = gs.columns[field.name];
if (cs.position == COLUMN_NOT_INITIALIZED) {
// column info not set, initialize based on the game
cs.visible = field.card_list_visible;
cs.position = field.card_list_column;
cs.width = field.card_list_width;
}
return cs;
}
StyleSheetSettings& Settings::stylesheetSettingsFor(const StyleSheet& stylesheet) {
StyleSheetSettingsP& ss = stylesheet_settings[stylesheet.name()];
if (!ss) ss = intrusive(new StyleSheetSettings);
ss->useDefault(default_stylesheet_settings); // update default settings
return *ss;
StyleSheetSettingsP& ss = stylesheet_settings[stylesheet.name()];
if (!ss) ss = intrusive(new StyleSheetSettings);
ss->useDefault(default_stylesheet_settings); // update default settings
return *ss;
}
IndexMap<FieldP,ValueP>& Settings::exportOptionsFor(const ExportTemplate& export_template) {
return export_options.get(export_template.name(), export_template.option_fields);
return export_options.get(export_template.name(), export_template.option_fields);
}
/// Retrieve the directory to use for settings and other data files
String user_settings_dir() {
String dir = wxStandardPaths::Get().GetUserDataDir();
if (!wxDirExists(dir)) wxMkdir(dir);
return dir + _("/");
String dir = wxStandardPaths::Get().GetUserDataDir();
if (!wxDirExists(dir)) wxMkdir(dir);
return dir + _("/");
}
String Settings::settingsFile() {
// return user_settings_dir() + _("mse.config");
return user_settings_dir() + _("mse8.config"); // use different file during development of C++ port
// return user_settings_dir() + _("mse.config");
return user_settings_dir() + _("mse8.config"); // use different file during development of C++ port
}
IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) {
REFLECT_ALIAS(300, "style settings", "stylesheet settings");
REFLECT_ALIAS(300, "default style settings", "default stylesheet settings");
REFLECT(locale);
REFLECT(recent_sets);
REFLECT(default_set_dir);
REFLECT(default_image_dir);
REFLECT(default_symbol_dir);
REFLECT(default_export_dir);
REFLECT(set_window_maximized);
REFLECT(set_window_width);
REFLECT(set_window_height);
REFLECT(card_notes_height);
REFLECT(open_sets_in_new_window);
REFLECT(symbol_grid_size);
REFLECT(symbol_grid);
REFLECT(symbol_grid_snap);
REFLECT(default_game);
REFLECT(print_layout);
REFLECT(apprentice_location);
#if USE_OLD_STYLE_UPDATE_CHECKER
REFLECT(updates_url);
#else
REFLECT_IGNORE(306,"updates url");
#endif
REFLECT(package_versions_url);
REFLECT(installer_list_url);
REFLECT(check_updates);
REFLECT(check_updates_all);
REFLECT(install_type);
REFLECT(website_url);
REFLECT(game_settings);
REFLECT(stylesheet_settings);
REFLECT(default_stylesheet_settings);
REFLECT(export_options);
REFLECT_ALIAS(300, "style settings", "stylesheet settings");
REFLECT_ALIAS(300, "default style settings", "default stylesheet settings");
REFLECT(locale);
REFLECT(recent_sets);
REFLECT(default_set_dir);
REFLECT(default_image_dir);
REFLECT(default_symbol_dir);
REFLECT(default_export_dir);
REFLECT(set_window_maximized);
REFLECT(set_window_width);
REFLECT(set_window_height);
REFLECT(card_notes_height);
REFLECT(open_sets_in_new_window);
REFLECT(symbol_grid_size);
REFLECT(symbol_grid);
REFLECT(symbol_grid_snap);
REFLECT(default_game);
REFLECT(print_layout);
REFLECT(apprentice_location);
#if USE_OLD_STYLE_UPDATE_CHECKER
REFLECT(updates_url);
#else
REFLECT_IGNORE(306,"updates url");
#endif
REFLECT(package_versions_url);
REFLECT(installer_list_url);
REFLECT(check_updates);
REFLECT(check_updates_all);
REFLECT(install_type);
REFLECT(website_url);
REFLECT(game_settings);
REFLECT(stylesheet_settings);
REFLECT(default_stylesheet_settings);
REFLECT(export_options);
}
void Settings::clear() {
recent_sets.clear();
game_settings.clear();
stylesheet_settings.clear();
default_stylesheet_settings = StyleSheetSettings();
export_options.clear();
recent_sets.clear();
game_settings.clear();
stylesheet_settings.clear();
default_stylesheet_settings = StyleSheetSettings();
export_options.clear();
}
void Settings::read() {
// clear current settings, otherwise we duplicate vector elements
clear();
// (re)load settings
String filename = settingsFile();
if (wxFileExists(filename)) {
// settings file not existing is not an error
shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
if (!file->Ok()) return; // failure is not an error
Reader reader(file, nullptr, filename);
reader.handle_greedy(*this);
}
// clear current settings, otherwise we duplicate vector elements
clear();
// (re)load settings
String filename = settingsFile();
if (wxFileExists(filename)) {
// settings file not existing is not an error
shared_ptr<wxFileInputStream> file = shared(new wxFileInputStream(filename));
if (!file->Ok()) return; // failure is not an error
Reader reader(file, nullptr, filename);
reader.handle_greedy(*this);
}
}
void Settings::write() {
Writer writer(shared(new wxFileOutputStream(settingsFile())), app_version);
writer.handle(*this);
Writer writer(shared(new wxFileOutputStream(settingsFile())), app_version);
writer.handle(*this);
}
+141 -141
View File
@@ -32,16 +32,16 @@ DECLARE_POINTER_TYPE(AutoReplace);
/// When to check for updates?
enum CheckUpdates
{ CHECK_ALWAYS
, CHECK_IF_CONNECTED
, CHECK_NEVER
{ CHECK_ALWAYS
, CHECK_IF_CONNECTED
, CHECK_NEVER
};
/// Where to install to?
enum InstallType
{ INSTALL_DEFAULT // the platform default.
, INSTALL_LOCAL // install to the user's files
, INSTALL_GLOBAL // install to the global files
{ INSTALL_DEFAULT // the platform default.
, INSTALL_LOCAL // install to the user's files
, INSTALL_GLOBAL // install to the global files
};
void parse_enum(const String&, InstallType&);
@@ -49,75 +49,75 @@ bool is_install_local(InstallType type);
/// How to handle filename conflicts
enum FilenameConflicts
{ CONFLICT_KEEP_OLD // always keep old file
, CONFLICT_OVERWRITE // always overwrite
, CONFLICT_NUMBER // always add numbers ("file.1.something")
, CONFLICT_NUMBER_OVERWRITE // only add numbers for conflicts inside a set, overwrite old stuff
{ CONFLICT_KEEP_OLD // always keep old file
, CONFLICT_OVERWRITE // always overwrite
, CONFLICT_NUMBER // always add numbers ("file.1.something")
, CONFLICT_NUMBER_OVERWRITE // only add numbers for conflicts inside a set, overwrite old stuff
};
/// Settings of a single column in the card list
class ColumnSettings {
public:
ColumnSettings();
UInt width;
int position;
bool visible;
DECLARE_REFLECTION();
ColumnSettings();
UInt width;
int position;
bool visible;
DECLARE_REFLECTION();
};
/// Settings for a Game
class GameSettings : public IntrusivePtrBase<GameSettings> {
public:
GameSettings();
/// Where the settings have defaults, initialize with the values from the game
void initDefaults(const Game& g);
String default_stylesheet;
String default_export;
map<String, ColumnSettings> columns;
String sort_cards_by;
bool sort_cards_ascending;
String images_export_filename;
FilenameConflicts images_export_conflicts;
bool use_auto_replace;
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
map<String, int> pack_amounts;
bool pack_seed_random;
int pack_seed;
DECLARE_REFLECTION();
GameSettings();
/// Where the settings have defaults, initialize with the values from the game
void initDefaults(const Game& g);
String default_stylesheet;
String default_export;
map<String, ColumnSettings> columns;
String sort_cards_by;
bool sort_cards_ascending;
String images_export_filename;
FilenameConflicts images_export_conflicts;
bool use_auto_replace;
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
map<String, int> pack_amounts;
bool pack_seed_random;
int pack_seed;
DECLARE_REFLECTION();
private:
bool initialized;
bool initialized;
};
/// Settings for a StyleSheet
class StyleSheetSettings : public IntrusivePtrBase<StyleSheetSettings> {
public:
StyleSheetSettings();
// Rendering/display settings
Defaultable<double> card_zoom;
Defaultable<Degrees> card_angle;
Defaultable<bool> card_anti_alias;
Defaultable<bool> card_borders;
Defaultable<bool> card_draw_editing;
Defaultable<bool> card_normal_export;
Defaultable<bool> card_spellcheck_enabled;
/// Where the settings are the default, use the value from ss
void useDefault(const StyleSheetSettings& ss);
DECLARE_REFLECTION();
StyleSheetSettings();
// Rendering/display settings
Defaultable<double> card_zoom;
Defaultable<Degrees> card_angle;
Defaultable<bool> card_anti_alias;
Defaultable<bool> card_borders;
Defaultable<bool> card_draw_editing;
Defaultable<bool> card_normal_export;
Defaultable<bool> card_spellcheck_enabled;
/// Where the settings are the default, use the value from ss
void useDefault(const StyleSheetSettings& ss);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Printing settings
enum PageLayoutType
{ LAYOUT_NO_SPACE
, LAYOUT_EQUAL_SPACE
//, LAYOUT_CUSTOM
{ LAYOUT_NO_SPACE
, LAYOUT_EQUAL_SPACE
//, LAYOUT_CUSTOM
};
// ----------------------------------------------------------------------------- : Settings
@@ -128,98 +128,98 @@ enum PageLayoutType
*/
class Settings {
public:
/// Default constructor initializes default settings
Settings();
// --------------------------------------------------- : Locale
String locale;
// --------------------------------------------------- : Recently opened sets
vector<String> recent_sets;
static const UInt max_recent_sets = 9; // store this many recent sets
/// Add a file to the list of recent files
void addRecentFile(const String& filename);
// --------------------------------------------------- : Files/directories
String default_set_dir; ///< Where to look for .mse-set files
String default_image_dir; ///< Where to look for images to import
String default_symbol_dir; ///< Where to look for .mse-symbol files
String default_export_dir; ///< Where to export to by default
// --------------------------------------------------- : Set window
bool set_window_maximized;
UInt set_window_width;
UInt set_window_height;
UInt card_notes_height;
bool open_sets_in_new_window;
// --------------------------------------------------- : Symbol editor
UInt symbol_grid_size;
bool symbol_grid;
bool symbol_grid_snap;
// --------------------------------------------------- : Default pacakge selections
String default_game;
// --------------------------------------------------- : Game/stylesheet specific
/// Get the settings object for a specific game
GameSettings& gameSettingsFor (const Game& game);
/// Get the settings for a column for a specific field in a game
ColumnSettings& columnSettingsFor (const Game& game, const Field& field);
/// Get the settings object for a specific stylesheet
StyleSheetSettings& stylesheetSettingsFor(const StyleSheet& stylesheet);
/// Default constructor initializes default settings
Settings();
// --------------------------------------------------- : Locale
String locale;
// --------------------------------------------------- : Recently opened sets
vector<String> recent_sets;
static const UInt max_recent_sets = 9; // store this many recent sets
/// Add a file to the list of recent files
void addRecentFile(const String& filename);
// --------------------------------------------------- : Files/directories
String default_set_dir; ///< Where to look for .mse-set files
String default_image_dir; ///< Where to look for images to import
String default_symbol_dir; ///< Where to look for .mse-symbol files
String default_export_dir; ///< Where to export to by default
// --------------------------------------------------- : Set window
bool set_window_maximized;
UInt set_window_width;
UInt set_window_height;
UInt card_notes_height;
bool open_sets_in_new_window;
// --------------------------------------------------- : Symbol editor
UInt symbol_grid_size;
bool symbol_grid;
bool symbol_grid_snap;
// --------------------------------------------------- : Default pacakge selections
String default_game;
// --------------------------------------------------- : Game/stylesheet specific
/// Get the settings object for a specific game
GameSettings& gameSettingsFor (const Game& game);
/// Get the settings for a column for a specific field in a game
ColumnSettings& columnSettingsFor (const Game& game, const Field& field);
/// Get the settings object for a specific stylesheet
StyleSheetSettings& stylesheetSettingsFor(const StyleSheet& stylesheet);
private:
map<String,GameSettingsP> game_settings;
map<String,StyleSheetSettingsP> stylesheet_settings;
map<String,GameSettingsP> game_settings;
map<String,StyleSheetSettingsP> stylesheet_settings;
public:
StyleSheetSettings default_stylesheet_settings; ///< The default settings for stylesheets
// --------------------------------------------------- : Exports
StyleSheetSettings default_stylesheet_settings; ///< The default settings for stylesheets
// --------------------------------------------------- : Exports
private:
DelayedIndexMaps<FieldP,ValueP> export_options;
DelayedIndexMaps<FieldP,ValueP> export_options;
public:
/// Get the options for an export template
IndexMap<FieldP,ValueP>& exportOptionsFor(const ExportTemplate& export_template);
// --------------------------------------------------- : Printing
PageLayoutType print_layout;
// --------------------------------------------------- : Special game stuff
String apprentice_location;
// --------------------------------------------------- : Update checking
#if USE_OLD_STYLE_UPDATE_CHECKER
String updates_url;
#endif
String package_versions_url; ///< latest package versions
String installer_list_url; ///< available installers
CheckUpdates check_updates;
bool check_updates_all; ///< Check updates of all packages, not just the program
String website_url;
// --------------------------------------------------- : Installation settings
InstallType install_type;
// --------------------------------------------------- : The io
/// Read the settings file from the standard location
void read();
/// Store the settings in the standard location
void write();
/// Get the options for an export template
IndexMap<FieldP,ValueP>& exportOptionsFor(const ExportTemplate& export_template);
// --------------------------------------------------- : Printing
PageLayoutType print_layout;
// --------------------------------------------------- : Special game stuff
String apprentice_location;
// --------------------------------------------------- : Update checking
#if USE_OLD_STYLE_UPDATE_CHECKER
String updates_url;
#endif
String package_versions_url; ///< latest package versions
String installer_list_url; ///< available installers
CheckUpdates check_updates;
bool check_updates_all; ///< Check updates of all packages, not just the program
String website_url;
// --------------------------------------------------- : Installation settings
InstallType install_type;
// --------------------------------------------------- : The io
/// Read the settings file from the standard location
void read();
/// Store the settings in the standard location
void write();
private:
/// Name of the settings file
String settingsFile();
/// Clear settings before reading them
void clear();
DECLARE_REFLECTION();
/// Name of the settings file
String settingsFile();
/// Clear settings before reading them
void clear();
DECLARE_REFLECTION();
};
/// The global settings object
+90 -90
View File
@@ -20,120 +20,120 @@ extern ScriptValueP script_primary_choice;
// ----------------------------------------------------------------------------- : Statistics dimension
StatsDimension::StatsDimension()
: automatic (false)
, position_hint(0)
, numeric (false)
, bin_size (0)
, show_empty (false)
, split_list (false)
: automatic (false)
, position_hint(0)
, numeric (false)
, bin_size (0)
, show_empty (false)
, split_list (false)
{}
StatsDimension::StatsDimension(const Field& field)
: automatic (true)
, name (field.name)
, description (field.description)
, position_hint(field.position_hint)
, icon_filename(field.icon_filename)
, numeric (false)
, show_empty (false)
, split_list (false)
: automatic (true)
, name (field.name)
, description (field.description)
, position_hint(field.position_hint)
, icon_filename(field.icon_filename)
, numeric (false)
, show_empty (false)
, split_list (false)
{
// choice field?
const ChoiceField* choice_field = dynamic_cast<const ChoiceField*>(&field);
if (choice_field) {
colors = choice_field->choice_colors;
/*int count = choice_field->choices->lastId();
for (int i = 0 ; i < count ; ++i) {
groups.push_back(choice_field->choices->choiceName(i));
}*/
// only top level choices
FOR_EACH_CONST(g, choice_field->choices->choices) {
groups.push_back(g->name);
}
// initialize script: primary_choice(card.{field_name})
Script& s = script.getMutableScript();
s.addInstruction(I_PUSH_CONST, script_primary_choice);
s.addInstruction(I_GET_VAR, SCRIPT_VAR_card);
s.addInstruction(I_MEMBER_C, field.name);
s.addInstruction(I_CALL, 1);
s.addInstruction(I_NOP, SCRIPT_VAR_input);
} else {
// initialize script, card.{field_name}
Script& s = script.getMutableScript();
s.addInstruction(I_GET_VAR, SCRIPT_VAR_card);
s.addInstruction(I_MEMBER_C, field.name);
}
// choice field?
const ChoiceField* choice_field = dynamic_cast<const ChoiceField*>(&field);
if (choice_field) {
colors = choice_field->choice_colors;
/*int count = choice_field->choices->lastId();
for (int i = 0 ; i < count ; ++i) {
groups.push_back(choice_field->choices->choiceName(i));
}*/
// only top level choices
FOR_EACH_CONST(g, choice_field->choices->choices) {
groups.push_back(g->name);
}
// initialize script: primary_choice(card.{field_name})
Script& s = script.getMutableScript();
s.addInstruction(I_PUSH_CONST, script_primary_choice);
s.addInstruction(I_GET_VAR, SCRIPT_VAR_card);
s.addInstruction(I_MEMBER_C, field.name);
s.addInstruction(I_CALL, 1);
s.addInstruction(I_NOP, SCRIPT_VAR_input);
} else {
// initialize script, card.{field_name}
Script& s = script.getMutableScript();
s.addInstruction(I_GET_VAR, SCRIPT_VAR_card);
s.addInstruction(I_MEMBER_C, field.name);
}
}
IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsDimension) {
if (!automatic) {
REFLECT(name);
REFLECT(description);
REFLECT(position_hint);
REFLECT_N("icon", icon_filename);
REFLECT(script);
REFLECT(numeric);
REFLECT(bin_size);
REFLECT(show_empty);
REFLECT(split_list);
REFLECT(colors);
REFLECT(groups);
}
if (!automatic) {
REFLECT(name);
REFLECT(description);
REFLECT(position_hint);
REFLECT_N("icon", icon_filename);
REFLECT(script);
REFLECT(numeric);
REFLECT(bin_size);
REFLECT(show_empty);
REFLECT(split_list);
REFLECT(colors);
REFLECT(groups);
}
}
// ----------------------------------------------------------------------------- : Statistics category
StatsCategory::StatsCategory()
: automatic(false)
, position_hint(0)
, type(GRAPH_TYPE_BAR)
: automatic(false)
, position_hint(0)
, type(GRAPH_TYPE_BAR)
{}
StatsCategory::StatsCategory(const StatsDimensionP& dim)
: automatic(true)
, name (dim->name)
, description (dim->description)
, position_hint(dim->position_hint)
, icon_filename(dim->icon_filename)
, dimensions(1, dim)
, type(GRAPH_TYPE_BAR)
: automatic(true)
, name (dim->name)
, description (dim->description)
, position_hint(dim->position_hint)
, icon_filename(dim->icon_filename)
, dimensions(1, dim)
, type(GRAPH_TYPE_BAR)
{}
IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsCategory) {
if (!automatic) {
REFLECT(name);
REFLECT(description);
REFLECT(position_hint);
REFLECT_N("icon", icon_filename);
REFLECT(type);
REFLECT_N("dimensions", dimension_names);
}
if (!automatic) {
REFLECT(name);
REFLECT(description);
REFLECT(position_hint);
REFLECT_N("icon", icon_filename);
REFLECT(type);
REFLECT_N("dimensions", dimension_names);
}
}
void StatsCategory::find_dimensions(const vector<StatsDimensionP>& available) {
if (!dimensions.empty()) return;
FOR_EACH_CONST(n, dimension_names) {
StatsDimensionP dim;
FOR_EACH_CONST(d, available) {
if (d->name == n) {
dim = d;
break;
}
}
if (!dim) {
handle_error(_ERROR_1_("dimension not found",n));
} else {
dimensions.push_back(dim);
}
}
if (!dimensions.empty()) return;
FOR_EACH_CONST(n, dimension_names) {
StatsDimensionP dim;
FOR_EACH_CONST(d, available) {
if (d->name == n) {
dim = d;
break;
}
}
if (!dim) {
handle_error(_ERROR_1_("dimension not found",n));
} else {
dimensions.push_back(dim);
}
}
}
// ----------------------------------------------------------------------------- : GraphType (from graph_type.hpp)
IMPLEMENT_REFLECTION_ENUM(GraphType) {
VALUE_N("bar", GRAPH_TYPE_BAR);
VALUE_N("pie", GRAPH_TYPE_PIE);
VALUE_N("stack", GRAPH_TYPE_STACK);
VALUE_N("scatter", GRAPH_TYPE_SCATTER);
VALUE_N("scatter pie", GRAPH_TYPE_SCATTER_PIE);
VALUE_N("bar", GRAPH_TYPE_BAR);
VALUE_N("pie", GRAPH_TYPE_PIE);
VALUE_N("stack", GRAPH_TYPE_STACK);
VALUE_N("scatter", GRAPH_TYPE_SCATTER);
VALUE_N("scatter pie", GRAPH_TYPE_SCATTER_PIE);
}
+35 -35
View File
@@ -24,24 +24,24 @@ DECLARE_POINTER_TYPE(StatsCategory);
/** Dimensions can be generated automatically based on card fields */
class StatsDimension : public IntrusivePtrBase<StatsDimension> {
public:
StatsDimension();
StatsDimension(const Field&);
const bool automatic; ///< Based on a card field?
String name; ///< Name of this dimension
String description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course)
OptionalScript script; ///< Script that determines the value(s)
bool numeric; ///< Are the values numeric? If so, they require special sorting
double bin_size; ///< Bin adjecent numbers?
bool show_empty; ///< Should "" be shown?
bool split_list; ///< Split values into multiple ones separated by commas
map<String,Color> colors; ///< Colors for the categories
vector<String> groups; ///< Order of the items
DECLARE_REFLECTION();
StatsDimension();
StatsDimension(const Field&);
const bool automatic; ///< Based on a card field?
String name; ///< Name of this dimension
String description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course)
OptionalScript script; ///< Script that determines the value(s)
bool numeric; ///< Are the values numeric? If so, they require special sorting
double bin_size; ///< Bin adjecent numbers?
bool show_empty; ///< Should "" be shown?
bool split_list; ///< Split values into multiple ones separated by commas
map<String,Color> colors; ///< Colors for the categories
vector<String> groups; ///< Order of the items
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Statistics category
@@ -50,23 +50,23 @@ class StatsDimension : public IntrusivePtrBase<StatsDimension> {
/** Can be generated automatically based on a dimension */
class StatsCategory : public IntrusivePtrBase<StatsCategory> {
public:
StatsCategory();
StatsCategory(const StatsDimensionP&);
const bool automatic; ///< Automatically generated?
String name; ///< Name/label
String description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course)
vector<String> dimension_names;///< Names of the dimensions to use
vector<StatsDimensionP> dimensions; ///< Actual dimensions
GraphType type; ///< Type of graph to use
/// Initialize dimensions from dimension_names
void find_dimensions(const vector<StatsDimensionP>& available);
DECLARE_REFLECTION();
StatsCategory();
StatsCategory(const StatsDimensionP&);
const bool automatic; ///< Automatically generated?
String name; ///< Name/label
String description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course)
vector<String> dimension_names;///< Names of the dimensions to use
vector<StatsDimensionP> dimensions; ///< Actual dimensions
GraphType type; ///< Type of graph to use
/// Initialize dimensions from dimension_names
void find_dimensions(const vector<StatsDimensionP>& available);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF
+95 -95
View File
@@ -21,44 +21,44 @@ DECLARE_TYPEOF_COLLECTION(FieldP);
IMPLEMENT_DYNAMIC_ARG(StyleSheet*, stylesheet_for_reading, nullptr);
StyleSheet::StyleSheet()
: card_width(100), card_height(100)
, card_dpi(96), card_background(*wxWHITE)
, dependencies_initialized(false)
: card_width(100), card_height(100)
, card_dpi(96), card_background(*wxWHITE)
, dependencies_initialized(false)
{}
StyleSheetP StyleSheet::byGameAndName(const Game& game, const String& name) {
/// Alternative stylesheets for game
static map<String, String> stylesheet_alternatives;
String full_name = game.name() + _("-") + name + _(".mse-style");
try {
map<String, String>::const_iterator it = stylesheet_alternatives.find(full_name);
if (it != stylesheet_alternatives.end()) {
return package_manager.open<StyleSheet>(it->second);
} else {
return package_manager.open<StyleSheet>(full_name);
}
} catch (PackageNotFoundError& e) {
if (stylesheet_for_reading()) {
// we already have a stylesheet higher up, so just return a null pointer
return StyleSheetP();
}
// load an alternative stylesheet
StyleSheetP ss = select_stylesheet(game, name);
if (ss) {
stylesheet_alternatives[full_name] = ss->relativeFilename();
return ss;
} else {
throw e;
}
}
/// Alternative stylesheets for game
static map<String, String> stylesheet_alternatives;
String full_name = game.name() + _("-") + name + _(".mse-style");
try {
map<String, String>::const_iterator it = stylesheet_alternatives.find(full_name);
if (it != stylesheet_alternatives.end()) {
return package_manager.open<StyleSheet>(it->second);
} else {
return package_manager.open<StyleSheet>(full_name);
}
} catch (PackageNotFoundError& e) {
if (stylesheet_for_reading()) {
// we already have a stylesheet higher up, so just return a null pointer
return StyleSheetP();
}
// load an alternative stylesheet
StyleSheetP ss = select_stylesheet(game, name);
if (ss) {
stylesheet_alternatives[full_name] = ss->relativeFilename();
return ss;
} else {
throw e;
}
}
}
String StyleSheet::stylesheetName() const {
String sn = name(), gn = game->name();
if (sn.size() + 1 > gn.size()) {
return sn.substr(gn.size() + 1); // remove "gamename-"
} else {
return sn;
}
String sn = name(), gn = game->name();
if (sn.size() + 1 > gn.size()) {
return sn.substr(gn.size() + 1); // remove "gamename-"
} else {
return sn;
}
}
String StyleSheet::typeNameStatic() { return _("style"); }
@@ -66,86 +66,86 @@ String StyleSheet::typeName() const { return _("style"); }
Version StyleSheet::fileVersion() const { return file_version_stylesheet; }
void StyleSheet::validate(Version ver) {
Packaged::validate(ver);
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("stylesheet")));
}
// a stylsheet depends on the game it is made for
requireDependency(game.get());
Packaged::validate(ver);
if (!game) {
throw Error(_ERROR_1_("no game specified",_TYPE_("stylesheet")));
}
// a stylsheet depends on the game it is made for
requireDependency(game.get());
}
StyleP StyleSheet::styleFor(const FieldP& field) {
if (card_style.containsKey(field)) {
return card_style[field];
} else if (set_info_style.containsKey(field)) {
return set_info_style[field];
} else if (styling_style.containsKey(field)) {
return styling_style[field];
} else {
throw InternalError(_("Can not find styling for field '")+field->name+_("'in stylesheet"));
}
if (card_style.containsKey(field)) {
return card_style[field];
} else if (set_info_style.containsKey(field)) {
return set_info_style[field];
} else if (styling_style.containsKey(field)) {
return styling_style[field];
} else {
throw InternalError(_("Can not find styling for field '")+field->name+_("'in stylesheet"));
}
}
void mark_dependency_value(const StyleSheet& stylesheet, const Dependency& dep) {
stylesheet.game->dependent_scripts_stylesheet.add(dep);
stylesheet.game->dependent_scripts_stylesheet.add(dep);
}
IMPLEMENT_REFLECTION(StyleSheet) {
// < 0.3.0 didn't use card_ prefix
REFLECT_ALIAS(300, "width", "card width");
REFLECT_ALIAS(300, "height", "card height");
REFLECT_ALIAS(300, "dpi", "card dpi");
REFLECT_ALIAS(300, "background", "card background");
REFLECT_ALIAS(300, "info style", "set info style");
REFLECT_ALIAS(300, "align", "alignment");
REFLECT_ALIAS(300, "extra field", "styling field");
REFLECT_ALIAS(300, "extra style", "styling style");
REFLECT(game);
REFLECT_BASE(Packaged);
REFLECT(card_width);
REFLECT(card_height);
REFLECT(card_dpi);
REFLECT(card_background);
REFLECT(init_script);
// styling
REFLECT(styling_fields);
REFLECT_IF_READING styling_style.init(styling_fields);
REFLECT(styling_style);
// style of game fields
if (game) {
REFLECT_IF_READING {
card_style.init(game->card_fields);
set_info_style.cloneFrom(game->default_set_style);
}
REFLECT(set_info_style);
REFLECT(card_style);
}
// extra card fields
REFLECT(extra_card_fields);
REFLECT_IF_READING {
if (extra_card_style.init(extra_card_fields)) {
// if a value is not editable, don't save it
FOR_EACH(f, extra_card_fields) {
if (!f->editable) f->save_value = false;
}
}
}
REFLECT(extra_card_style);
// < 0.3.0 didn't use card_ prefix
REFLECT_ALIAS(300, "width", "card width");
REFLECT_ALIAS(300, "height", "card height");
REFLECT_ALIAS(300, "dpi", "card dpi");
REFLECT_ALIAS(300, "background", "card background");
REFLECT_ALIAS(300, "info style", "set info style");
REFLECT_ALIAS(300, "align", "alignment");
REFLECT_ALIAS(300, "extra field", "styling field");
REFLECT_ALIAS(300, "extra style", "styling style");
REFLECT(game);
REFLECT_BASE(Packaged);
REFLECT(card_width);
REFLECT(card_height);
REFLECT(card_dpi);
REFLECT(card_background);
REFLECT(init_script);
// styling
REFLECT(styling_fields);
REFLECT_IF_READING styling_style.init(styling_fields);
REFLECT(styling_style);
// style of game fields
if (game) {
REFLECT_IF_READING {
card_style.init(game->card_fields);
set_info_style.cloneFrom(game->default_set_style);
}
REFLECT(set_info_style);
REFLECT(card_style);
}
// extra card fields
REFLECT(extra_card_fields);
REFLECT_IF_READING {
if (extra_card_style.init(extra_card_fields)) {
// if a value is not editable, don't save it
FOR_EACH(f, extra_card_fields) {
if (!f->editable) f->save_value = false;
}
}
}
REFLECT(extra_card_style);
}
// special behaviour of reading/writing StyleSheetPs: only read/write the name
void Reader::handle(StyleSheetP& stylesheet) {
if (!game_for_reading()) {
throw InternalError(_("game_for_reading not set"));
}
stylesheet = StyleSheet::byGameAndName(*game_for_reading(), getValue());
if (!game_for_reading()) {
throw InternalError(_("game_for_reading not set"));
}
stylesheet = StyleSheet::byGameAndName(*game_for_reading(), getValue());
}
void Writer::handle(const StyleSheetP& stylesheet) {
if (stylesheet) handle(stylesheet->stylesheetName());
if (stylesheet) handle(stylesheet->stylesheetName());
}
+44 -44
View File
@@ -27,54 +27,54 @@ DECLARE_DYNAMIC_ARG(StyleSheet*, stylesheet_for_reading);
/// A collection of style information for card and set fields
class StyleSheet : public Packaged {
public:
StyleSheet();
GameP game; ///< The game this stylesheet is made for
OptionalScript init_script; ///< Script of variables available to other scripts in this stylesheet
double card_width; ///< The width of a card in pixels
double card_height; ///< The height of a card in pixels
double card_dpi; ///< The resolution of a card in dots per inch
Color card_background; ///< The background color of cards
/// The styling for card fields
/** The indices should correspond to the card_fields in the Game */
IndexMap<FieldP, StyleP> card_style;
/// The styling for set info fields
/** The indices should correspond to the set_fields in the Game */
IndexMap<FieldP, StyleP> set_info_style;
/// Extra card fields for boxes and borders
vector<FieldP> extra_card_fields;
IndexMap<FieldP, StyleP> extra_card_style;
/// Extra fields for styling options
vector<FieldP> styling_fields;
/// The styling for the extra set fields
/** The indices should correspond to the styling_fields */
IndexMap<FieldP, StyleP> styling_style;
bool dependencies_initialized; ///< are the script dependencies comming from this stylesheet all initialized?
inline RealRect getCardRect() const { return RealRect(0, 0, card_width, card_height); }
/// Return the style for a given field, it is not specified what type of field this is.
StyleP styleFor(const FieldP& field);
/// Load a StyleSheet, given a Game and the name of the StyleSheet
static StyleSheetP byGameAndName(const Game& game, const String& name);
/// name of the package without the game name
String stylesheetName() const;
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
/// Validate the stylesheet
virtual void validate(Version = app_version);
StyleSheet();
GameP game; ///< The game this stylesheet is made for
OptionalScript init_script; ///< Script of variables available to other scripts in this stylesheet
double card_width; ///< The width of a card in pixels
double card_height; ///< The height of a card in pixels
double card_dpi; ///< The resolution of a card in dots per inch
Color card_background; ///< The background color of cards
/// The styling for card fields
/** The indices should correspond to the card_fields in the Game */
IndexMap<FieldP, StyleP> card_style;
/// The styling for set info fields
/** The indices should correspond to the set_fields in the Game */
IndexMap<FieldP, StyleP> set_info_style;
/// Extra card fields for boxes and borders
vector<FieldP> extra_card_fields;
IndexMap<FieldP, StyleP> extra_card_style;
/// Extra fields for styling options
vector<FieldP> styling_fields;
/// The styling for the extra set fields
/** The indices should correspond to the styling_fields */
IndexMap<FieldP, StyleP> styling_style;
bool dependencies_initialized; ///< are the script dependencies comming from this stylesheet all initialized?
inline RealRect getCardRect() const { return RealRect(0, 0, card_width, card_height); }
/// Return the style for a given field, it is not specified what type of field this is.
StyleP styleFor(const FieldP& field);
/// Load a StyleSheet, given a Game and the name of the StyleSheet
static StyleSheetP byGameAndName(const Game& game, const String& name);
/// name of the package without the game name
String stylesheetName() const;
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
/// Validate the stylesheet
virtual void validate(Version = app_version);
protected:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
inline String type_name(const StyleSheet&) {
return _TYPE_("stylesheet");
return _TYPE_("stylesheet");
}
void mark_dependency_value(const StyleSheet& value, const Dependency& dep);
+222 -222
View File
@@ -18,359 +18,359 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : ControlPoint
IMPLEMENT_REFLECTION_ENUM(LockMode) {
VALUE_N("free", LOCK_FREE);
VALUE_N("direction", LOCK_DIR);
VALUE_N("size", LOCK_SIZE);
VALUE_N("free", LOCK_FREE);
VALUE_N("direction", LOCK_DIR);
VALUE_N("size", LOCK_SIZE);
}
IMPLEMENT_REFLECTION_ENUM(SegmentMode) {
VALUE_N("line", SEGMENT_LINE);
VALUE_N("curve", SEGMENT_CURVE);
VALUE_N("line", SEGMENT_LINE);
VALUE_N("curve", SEGMENT_CURVE);
}
IMPLEMENT_REFLECTION(ControlPoint) {
REFLECT_N("position", pos);
REFLECT_N("lock", lock);
REFLECT_N("line_after", segment_after);
if (tag.reading() || segment_before == SEGMENT_CURVE) {
REFLECT_N("handle_before", delta_before);
}
if (tag.reading() || segment_after == SEGMENT_CURVE) {
REFLECT_N("handle_after", delta_after);
}
REFLECT_N("position", pos);
REFLECT_N("lock", lock);
REFLECT_N("line_after", segment_after);
if (tag.reading() || segment_before == SEGMENT_CURVE) {
REFLECT_N("handle_before", delta_before);
}
if (tag.reading() || segment_after == SEGMENT_CURVE) {
REFLECT_N("handle_after", delta_after);
}
}
ControlPoint::ControlPoint()
: segment_before(SEGMENT_LINE), segment_after(SEGMENT_LINE)
, lock(LOCK_FREE)
: segment_before(SEGMENT_LINE), segment_after(SEGMENT_LINE)
, lock(LOCK_FREE)
{}
ControlPoint::ControlPoint(double x, double y)
: pos(x,y)
, segment_before(SEGMENT_LINE), segment_after(SEGMENT_LINE)
, lock(LOCK_FREE)
: pos(x,y)
, segment_before(SEGMENT_LINE), segment_after(SEGMENT_LINE)
, lock(LOCK_FREE)
{}
ControlPoint::ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock)
: pos(x,y)
, delta_before(xb,yb)
, delta_after(xa,ya)
, segment_before(SEGMENT_CURVE), segment_after(SEGMENT_CURVE)
, lock(lock)
: pos(x,y)
, delta_before(xb,yb)
, delta_after(xa,ya)
, segment_before(SEGMENT_CURVE), segment_after(SEGMENT_CURVE)
, lock(lock)
{}
void ControlPoint::onUpdateHandle(WhichHandle wh) {
// One handle has changed, update only the other one
if (lock == LOCK_DIR) {
getOther(wh) = -getHandle(wh) * getOther(wh).length() / getHandle(wh).length();
} else if (lock == LOCK_SIZE) {
getOther(wh) = -getHandle(wh);
}
// One handle has changed, update only the other one
if (lock == LOCK_DIR) {
getOther(wh) = -getHandle(wh) * getOther(wh).length() / getHandle(wh).length();
} else if (lock == LOCK_SIZE) {
getOther(wh) = -getHandle(wh);
}
}
void ControlPoint::onUpdateLock() {
// The lock has changed, avarage the handle values
if (lock == LOCK_DIR) {
// delta_before = x * delta_after
Vector2D dir = (delta_before - delta_after).normalized();
delta_before = dir * delta_before.length();
delta_after = dir * -delta_after.length();
} else if (lock == LOCK_SIZE) {
// delta_before = -delta_after
delta_before = (delta_before - delta_after) * 0.5;
delta_after = -delta_before;
}
// The lock has changed, avarage the handle values
if (lock == LOCK_DIR) {
// delta_before = x * delta_after
Vector2D dir = (delta_before - delta_after).normalized();
delta_before = dir * delta_before.length();
delta_after = dir * -delta_after.length();
} else if (lock == LOCK_SIZE) {
// delta_before = -delta_after
delta_before = (delta_before - delta_after) * 0.5;
delta_after = -delta_before;
}
}
Vector2D& ControlPoint::getHandle(WhichHandle wh) {
if (wh == HANDLE_BEFORE) {
return delta_before;
} else {
assert(wh == HANDLE_AFTER);
return delta_after;
}
if (wh == HANDLE_BEFORE) {
return delta_before;
} else {
assert(wh == HANDLE_AFTER);
return delta_after;
}
}
Vector2D& ControlPoint::getOther(WhichHandle wh) {
if (wh == HANDLE_BEFORE) {
return delta_after;
} else {
assert(wh == HANDLE_AFTER);
return delta_before;
}
if (wh == HANDLE_BEFORE) {
return delta_after;
} else {
assert(wh == HANDLE_AFTER);
return delta_before;
}
}
// ----------------------------------------------------------------------------- : Bounds
void Bounds::update(const Vector2D& p) {
min = piecewise_min(min, p);
max = piecewise_max(max, p);
min = piecewise_min(min, p);
max = piecewise_max(max, p);
}
void Bounds::update(const Bounds& b) {
min = piecewise_min(min, b.min);
max = piecewise_max(max, b.max);
min = piecewise_min(min, b.min);
max = piecewise_max(max, b.max);
}
bool Bounds::contains(const Vector2D& p) const {
return p.x >= min.x && p.y >= min.y &&
p.x <= max.x && p.y <= max.y;
return p.x >= min.x && p.y >= min.y &&
p.x <= max.x && p.y <= max.y;
}
bool Bounds::contains(const Bounds& b) const {
return b.min.x >= min.x && b.min.y >= min.y &&
b.max.x <= max.x && b.max.y <= max.y;
return b.min.x >= min.x && b.min.y >= min.y &&
b.max.x <= max.x && b.max.y <= max.y;
}
Vector2D Bounds::corner(int dx, int dy) const {
return Vector2D(
0.5 * (min.x + max.x + dx * (max.x - min.x)),
0.5 * (min.y + max.y + dy * (max.y - min.y)));
return Vector2D(
0.5 * (min.x + max.x + dx * (max.x - min.x)),
0.5 * (min.y + max.y + dy * (max.y - min.y)));
}
// ----------------------------------------------------------------------------- : SymbolPart
void SymbolPart::updateBounds() {
calculateBounds(Vector2D(), Matrix2D(), true);
calculateBounds(Vector2D(), Matrix2D(), true);
}
IMPLEMENT_REFLECTION(SymbolPart) {
REFLECT_IF_NOT_READING {
String type = typeName();
REFLECT(type);
}
REFLECT(name);
REFLECT_IF_NOT_READING {
String type = typeName();
REFLECT(type);
}
REFLECT(name);
}
template <>
SymbolPartP read_new<SymbolPart>(Reader& reader) {
// there must be a type specified
String type;
reader.handle(_("type"), type);
if (type == _("shape") || type.empty()) return intrusive(new SymbolShape);
else if (type == _("symmetry")) return intrusive(new SymbolSymmetry);
else if (type == _("group")) return intrusive(new SymbolGroup);
else {
throw ParseError(_("Unsupported symbol part type: '") + type + _("'"));
}
// there must be a type specified
String type;
reader.handle(_("type"), type);
if (type == _("shape") || type.empty()) return intrusive(new SymbolShape);
else if (type == _("symmetry")) return intrusive(new SymbolSymmetry);
else if (type == _("group")) return intrusive(new SymbolGroup);
else {
throw ParseError(_("Unsupported symbol part type: '") + type + _("'"));
}
}
// ----------------------------------------------------------------------------- : SymbolShape
IMPLEMENT_REFLECTION_ENUM(SymbolShapeCombine) {
VALUE_N("overlap", SYMBOL_COMBINE_OVERLAP);
VALUE_N("merge", SYMBOL_COMBINE_MERGE);
VALUE_N("subtract", SYMBOL_COMBINE_SUBTRACT);
VALUE_N("intersection", SYMBOL_COMBINE_INTERSECTION);
VALUE_N("difference", SYMBOL_COMBINE_DIFFERENCE);
VALUE_N("border", SYMBOL_COMBINE_BORDER);
VALUE_N("overlap", SYMBOL_COMBINE_OVERLAP);
VALUE_N("merge", SYMBOL_COMBINE_MERGE);
VALUE_N("subtract", SYMBOL_COMBINE_SUBTRACT);
VALUE_N("intersection", SYMBOL_COMBINE_INTERSECTION);
VALUE_N("difference", SYMBOL_COMBINE_DIFFERENCE);
VALUE_N("border", SYMBOL_COMBINE_BORDER);
}
template<typename T> void fix(const T&,SymbolShape&) {}
void fix(const Reader& reader, SymbolShape& shape) {
if (reader.file_app_version != Version()) return;
shape.updateBounds();
if (shape.bounds.max.x < 100 || shape.bounds.max.y < 100) return;
// this is a <= 0.1.2 symbol, points range [0...500] instead of [0...1]
// adjust it
FOR_EACH(p, shape.points) {
p->pos /= 500.0;
p->delta_before /= 500.0;
p->delta_after /= 500.0;
}
if (shape.name.empty()) shape.name = _("Shape");
shape.updateBounds();
if (reader.file_app_version != Version()) return;
shape.updateBounds();
if (shape.bounds.max.x < 100 || shape.bounds.max.y < 100) return;
// this is a <= 0.1.2 symbol, points range [0...500] instead of [0...1]
// adjust it
FOR_EACH(p, shape.points) {
p->pos /= 500.0;
p->delta_before /= 500.0;
p->delta_after /= 500.0;
}
if (shape.name.empty()) shape.name = _("Shape");
shape.updateBounds();
}
IMPLEMENT_REFLECTION(SymbolShape) {
REFLECT_BASE(SymbolPart);
REFLECT(combine);
REFLECT(points);
// Fixes after reading
REFLECT_IF_READING {
// enforce constraints
enforceConstraints();
fix(tag,*this);
}
REFLECT_BASE(SymbolPart);
REFLECT(combine);
REFLECT(points);
// Fixes after reading
REFLECT_IF_READING {
// enforce constraints
enforceConstraints();
fix(tag,*this);
}
}
SymbolShape::SymbolShape()
: combine(SYMBOL_COMBINE_OVERLAP), rotation_center(.5, .5)
: combine(SYMBOL_COMBINE_OVERLAP), rotation_center(.5, .5)
{}
String SymbolShape::typeName() const {
return _("shape");
return _("shape");
}
SymbolPartP SymbolShape::clone() const {
SymbolShapeP part(new SymbolShape(*this));
// also clone the control points
FOR_EACH(p, part->points) {
p = intrusive(new ControlPoint(*p));
}
return part;
SymbolShapeP part(new SymbolShape(*this));
// also clone the control points
FOR_EACH(p, part->points) {
p = intrusive(new ControlPoint(*p));
}
return part;
}
void SymbolShape::enforceConstraints() {
for (int i = 0 ; i < (int)points.size() ; ++i) {
ControlPointP p1 = getPoint(i);
ControlPointP p2 = getPoint(i + 1);
p2->segment_before = p1->segment_after;
p1->onUpdateLock();
}
for (int i = 0 ; i < (int)points.size() ; ++i) {
ControlPointP p1 = getPoint(i);
ControlPointP p2 = getPoint(i + 1);
p2->segment_before = p1->segment_after;
p1->onUpdateLock();
}
}
Bounds SymbolShape::calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) {
Bounds bounds;
for (int i = 0 ; i < (int)points.size() ; ++i) {
bounds.update(segment_bounds(origin, m, *getPoint(i), *getPoint(i + 1)));
}
if (is_identity) this->bounds = bounds;
return bounds;
Bounds bounds;
for (int i = 0 ; i < (int)points.size() ; ++i) {
bounds.update(segment_bounds(origin, m, *getPoint(i), *getPoint(i + 1)));
}
if (is_identity) this->bounds = bounds;
return bounds;
}
// ----------------------------------------------------------------------------- : SymbolSymmetry
IMPLEMENT_REFLECTION_ENUM(SymbolSymmetryType) {
VALUE_N("rotation", SYMMETRY_ROTATION);
VALUE_N("reflection", SYMMETRY_REFLECTION);
VALUE_N("rotation", SYMMETRY_ROTATION);
VALUE_N("reflection", SYMMETRY_REFLECTION);
}
SymbolSymmetry::SymbolSymmetry()
: kind(SYMMETRY_ROTATION), copies(2)
: kind(SYMMETRY_ROTATION), copies(2)
{}
String SymbolSymmetry::typeName() const {
return _("symmetry");
return _("symmetry");
}
SymbolPartP SymbolSymmetry::clone() const {
SymbolSymmetryP part(new SymbolSymmetry(*this));
// also clone the parts inside
FOR_EACH(p, part->parts) {
p = p->clone();
}
return part;
SymbolSymmetryP part(new SymbolSymmetry(*this));
// also clone the parts inside
FOR_EACH(p, part->parts) {
p = p->clone();
}
return part;
}
String SymbolSymmetry::expectedName() const {
return capitalize(kind == SYMMETRY_ROTATION ? _TYPE_("rotation") : _TYPE_("reflection"))
+ String::Format(_(" (%d)"), copies);
return capitalize(kind == SYMMETRY_ROTATION ? _TYPE_("rotation") : _TYPE_("reflection"))
+ String::Format(_(" (%d)"), copies);
}
Bounds SymbolSymmetry::calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) {
Bounds bounds;
// See SymbolViewer::draw
Radians b = 2 * handle.angle();
int copies = kind == SYMMETRY_REFLECTION ? this->copies & ~1 : this->copies;
FOR_EACH_CONST(p, parts) {
for (int i = 0 ; i < copies ; ++i) {
double a = i * 2 * M_PI / copies;
if (kind == SYMMETRY_ROTATION || i % 2 == 0) {
Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
bounds.update(
p->calculateBounds(origin + (center - center*rot) * m, rot * m, is_identity && i == 0)
);
} else {
Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b));
bounds.update(
p->calculateBounds(origin + (center - center*rot) * m, rot * m, is_identity && i == 0)
);
}
}
}
// done
if (is_identity) this->bounds = bounds;
return bounds;
Bounds bounds;
// See SymbolViewer::draw
Radians b = 2 * handle.angle();
int copies = kind == SYMMETRY_REFLECTION ? this->copies & ~1 : this->copies;
FOR_EACH_CONST(p, parts) {
for (int i = 0 ; i < copies ; ++i) {
double a = i * 2 * M_PI / copies;
if (kind == SYMMETRY_ROTATION || i % 2 == 0) {
Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
bounds.update(
p->calculateBounds(origin + (center - center*rot) * m, rot * m, is_identity && i == 0)
);
} else {
Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b));
bounds.update(
p->calculateBounds(origin + (center - center*rot) * m, rot * m, is_identity && i == 0)
);
}
}
}
// done
if (is_identity) this->bounds = bounds;
return bounds;
}
IMPLEMENT_REFLECTION(SymbolSymmetry) {
REFLECT_BASE(SymbolPart);
REFLECT(kind);
REFLECT(copies);
REFLECT(center);
REFLECT(handle);
REFLECT(parts);
REFLECT_BASE(SymbolPart);
REFLECT(kind);
REFLECT(copies);
REFLECT(center);
REFLECT(handle);
REFLECT(parts);
}
// ----------------------------------------------------------------------------- : SymbolGroup
SymbolGroup::SymbolGroup() {
name = capitalize(_TYPE_("group"));
name = capitalize(_TYPE_("group"));
}
String SymbolGroup::typeName() const {
return _("group");
return _("group");
}
SymbolPartP SymbolGroup::clone() const {
SymbolGroupP part(new SymbolGroup(*this));
// also clone the parts inside
FOR_EACH(p, part->parts) {
p = p->clone();
}
return part;
SymbolGroupP part(new SymbolGroup(*this));
// also clone the parts inside
FOR_EACH(p, part->parts) {
p = p->clone();
}
return part;
}
bool SymbolGroup::isAncestor(const SymbolPart& that) const {
if (this == &that) return true;
FOR_EACH_CONST(p, parts) {
if (p->isAncestor(that)) return true;
}
return false;
if (this == &that) return true;
FOR_EACH_CONST(p, parts) {
if (p->isAncestor(that)) return true;
}
return false;
}
Bounds SymbolGroup::calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) {
Bounds bounds;
FOR_EACH(p, parts) {
bounds.update(p->calculateBounds(origin, m, is_identity));
}
if (is_identity) this->bounds = bounds;
return bounds;
Bounds bounds;
FOR_EACH(p, parts) {
bounds.update(p->calculateBounds(origin, m, is_identity));
}
if (is_identity) this->bounds = bounds;
return bounds;
}
IMPLEMENT_REFLECTION(SymbolGroup) {
REFLECT_BASE(SymbolPart);
REFLECT(parts);
REFLECT_BASE(SymbolPart);
REFLECT(parts);
}
// ----------------------------------------------------------------------------- : Symbol
IMPLEMENT_REFLECTION(Symbol) {
REFLECT(parts);
REFLECT_IF_READING updateBounds();
REFLECT(parts);
REFLECT_IF_READING updateBounds();
}
double Symbol::aspectRatio() const {
// Margin between the edges and the symbol.
// In each direction take the lowest one
// This is at most 0.5 (if the symbol is just a line in the middle)
// Multiply by 2 (below) to give something in the range [0...1] i.e. [touches the edge...only in the middle]
double margin_x = min(0.4999, max(0., min(bounds.min.x, 1-bounds.max.x)));
double margin_y = min(0.4999, max(0., min(bounds.min.y, 1-bounds.max.y)));
// The difference between these two,
// e.g. if the vertical margin is more then the horizontal one, the symbol is 'flat'
double delta = 2 * (margin_y - margin_x);
// The aspect ratio, i.e. width/height
if (delta > 0) {
return 1 / (1 - delta);
} else {
return 1 + delta;
}
// Margin between the edges and the symbol.
// In each direction take the lowest one
// This is at most 0.5 (if the symbol is just a line in the middle)
// Multiply by 2 (below) to give something in the range [0...1] i.e. [touches the edge...only in the middle]
double margin_x = min(0.4999, max(0., min(bounds.min.x, 1-bounds.max.x)));
double margin_y = min(0.4999, max(0., min(bounds.min.y, 1-bounds.max.y)));
// The difference between these two,
// e.g. if the vertical margin is more then the horizontal one, the symbol is 'flat'
double delta = 2 * (margin_y - margin_x);
// The aspect ratio, i.e. width/height
if (delta > 0) {
return 1 / (1 - delta);
} else {
return 1 + delta;
}
}
// ----------------------------------------------------------------------------- : Default symbol
// A default symbol part, a square, moved by d
SymbolShapeP default_symbol_part(double d) {
SymbolShapeP part = intrusive(new SymbolShape);
part->points.push_back(intrusive(new ControlPoint(d + .2, d + .2)));
part->points.push_back(intrusive(new ControlPoint(d + .2, d + .8)));
part->points.push_back(intrusive(new ControlPoint(d + .8, d + .8)));
part->points.push_back(intrusive(new ControlPoint(d + .8, d + .2)));
part->name = _("Square");
return part;
SymbolShapeP part = intrusive(new SymbolShape);
part->points.push_back(intrusive(new ControlPoint(d + .2, d + .2)));
part->points.push_back(intrusive(new ControlPoint(d + .2, d + .8)));
part->points.push_back(intrusive(new ControlPoint(d + .8, d + .8)));
part->points.push_back(intrusive(new ControlPoint(d + .8, d + .2)));
part->name = _("Square");
return part;
}
// A default symbol, a square
SymbolP default_symbol() {
SymbolP symbol = intrusive(new Symbol);
symbol->parts.push_back(default_symbol_part(0));
return symbol;
SymbolP symbol = intrusive(new Symbol);
symbol->parts.push_back(default_symbol_part(0));
return symbol;
}
// ----------------------------------------------------------------------------- : SymbolView
@@ -378,14 +378,14 @@ SymbolP default_symbol() {
SymbolView::SymbolView() {}
SymbolView::~SymbolView() {
if (symbol) symbol->actions.removeListener(this);
if (symbol) symbol->actions.removeListener(this);
}
void SymbolView::setSymbol(const SymbolP& newSymbol) {
// no longer listening to old symbol
if (symbol) symbol->actions.removeListener(this);
symbol = newSymbol;
// start listening to new symbol
if (symbol) symbol->actions.addListener(this);
onChangeSymbol();
// no longer listening to old symbol
if (symbol) symbol->actions.removeListener(this);
symbol = newSymbol;
// start listening to new symbol
if (symbol) symbol->actions.addListener(this);
onChangeSymbol();
}
+194 -194
View File
@@ -28,52 +28,52 @@ DECLARE_POINTER_TYPE(Symbol);
/** Specificly: the relation between delta_before and delta_after
*/
enum LockMode
{ LOCK_FREE ///< no locking
, LOCK_DIR ///< delta_before = x * delta_after
, LOCK_SIZE ///< delta_before = -delta_after
{ LOCK_FREE ///< no locking
, LOCK_DIR ///< delta_before = x * delta_after
, LOCK_SIZE ///< delta_before = -delta_after
};
/// Is the segment between two ControlPoints a line or a curve?
enum SegmentMode
{ SEGMENT_LINE
, SEGMENT_CURVE
{ SEGMENT_LINE
, SEGMENT_CURVE
};
/// To refer to a specific handle of a control point
enum WhichHandle
{ HANDLE_NONE = 0 ///< point is not selected
, HANDLE_MAIN
, HANDLE_BEFORE
, HANDLE_AFTER
{ HANDLE_NONE = 0 ///< point is not selected
, HANDLE_MAIN
, HANDLE_BEFORE
, HANDLE_AFTER
};
/// A control point (corner) of a SymbolShape (polygon/bezier-gon)
class ControlPoint : public IntrusivePtrBase<ControlPoint> {
public:
Vector2D pos; ///< position of the control point itself
Vector2D delta_before; ///< delta to bezier control point, for curve before point
Vector2D delta_after; ///< delta to bezier control point, for curve after point
SegmentMode segment_before, segment_after;
LockMode lock;
/// Default constructor
ControlPoint();
/// Constructor for straight lines, takes only the position
ControlPoint(double x, double y);
/// Constructor for curves lines, takes postions, delta_before, delta_after and lock mode
ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock = LOCK_FREE);
/// Must be called after delta_before/delta_after has changed, enforces lock constraints
void onUpdateHandle(WhichHandle wh);
/// Must be called after lock has changed, enforces lock constraints
void onUpdateLock();
Vector2D pos; ///< position of the control point itself
Vector2D delta_before; ///< delta to bezier control point, for curve before point
Vector2D delta_after; ///< delta to bezier control point, for curve after point
SegmentMode segment_before, segment_after;
LockMode lock;
/// Default constructor
ControlPoint();
/// Constructor for straight lines, takes only the position
ControlPoint(double x, double y);
/// Constructor for curves lines, takes postions, delta_before, delta_after and lock mode
ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock = LOCK_FREE);
/// Must be called after delta_before/delta_after has changed, enforces lock constraints
void onUpdateHandle(WhichHandle wh);
/// Must be called after lock has changed, enforces lock constraints
void onUpdateLock();
/// Get a handle of this control point
Vector2D& getHandle(WhichHandle wh);
/// Get a handle of this control point that is oposite wh
Vector2D& getOther(WhichHandle wh);
DECLARE_REFLECTION();
/// Get a handle of this control point
Vector2D& getHandle(WhichHandle wh);
/// Get a handle of this control point that is oposite wh
Vector2D& getOther(WhichHandle wh);
DECLARE_REFLECTION();
};
@@ -82,27 +82,27 @@ class ControlPoint : public IntrusivePtrBase<ControlPoint> {
/// A specific handle of a ControlPoint
class SelectedHandle {
public:
ControlPointP point; ///< the selected point
WhichHandle handle; ///< the selected handle of the point
// SelectedHandle
SelectedHandle() : handle(HANDLE_NONE) {}
SelectedHandle(const WhichHandle& handle) : handle(handle) { assert (handle == HANDLE_NONE); }
SelectedHandle(const ControlPointP& point, const WhichHandle& handle) : point(point), handle(handle) {}
inline Vector2D& getHandle() const { return point->getHandle(handle); }
inline Vector2D& getOther() const { return point->getOther (handle); }
inline void onUpdateHandle() const { return point->onUpdateHandle(handle); }
/*
bool operator == (const ControlPointP pnt) const;
bool SelectedHandle::operator == (const ControlPointP pnt) const { return point == pnt; }
bool operator == (const WhichHandle& wh) const;
bool SelectedHandle::operator == (const WhichHandle& wh) const { return handle == wh; }
bool operator ! () const;
bool SelectedHandle::operator ! () const { return handle == handleNone; }
*/
ControlPointP point; ///< the selected point
WhichHandle handle; ///< the selected handle of the point
// SelectedHandle
SelectedHandle() : handle(HANDLE_NONE) {}
SelectedHandle(const WhichHandle& handle) : handle(handle) { assert (handle == HANDLE_NONE); }
SelectedHandle(const ControlPointP& point, const WhichHandle& handle) : point(point), handle(handle) {}
inline Vector2D& getHandle() const { return point->getHandle(handle); }
inline Vector2D& getOther() const { return point->getOther (handle); }
inline void onUpdateHandle() const { return point->onUpdateHandle(handle); }
/*
bool operator == (const ControlPointP pnt) const;
bool SelectedHandle::operator == (const ControlPointP pnt) const { return point == pnt; }
bool operator == (const WhichHandle& wh) const;
bool SelectedHandle::operator == (const WhichHandle& wh) const { return handle == wh; }
bool operator ! () const;
bool SelectedHandle::operator ! () const { return handle == handleNone; }
*/
};
@@ -111,25 +111,25 @@ class SelectedHandle {
/// Bounding box of a symbol part
class Bounds {
public:
inline Bounds() : min(Vector2D::infinity()), max(-Vector2D::infinity()) {}
inline explicit Bounds(const Vector2D& p) : min(p), max(p) {}
inline Bounds(const Vector2D& min, const Vector2D& max) : min(min), max(max) {}
/// Combine with another bounding box
void update(const Bounds& b);
void update(const Vector2D& p);
/// Does this box contain the given point?
bool contains(const Vector2D& p) const;
/// Does this box contain the given rectangle?
bool contains(const Bounds& b) const;
/// Corner or center of this bounding box, dx,dy in <-1, 0, 1>
Vector2D corner(int dx, int dy) const;
Vector2D min, max;
inline operator RealRect () const { return RealRect(min, RealSize(max - min)); }
inline Bounds() : min(Vector2D::infinity()), max(-Vector2D::infinity()) {}
inline explicit Bounds(const Vector2D& p) : min(p), max(p) {}
inline Bounds(const Vector2D& min, const Vector2D& max) : min(min), max(max) {}
/// Combine with another bounding box
void update(const Bounds& b);
void update(const Vector2D& p);
/// Does this box contain the given point?
bool contains(const Vector2D& p) const;
/// Does this box contain the given rectangle?
bool contains(const Bounds& b) const;
/// Corner or center of this bounding box, dx,dy in <-1, 0, 1>
Vector2D corner(int dx, int dy) const;
Vector2D min, max;
inline operator RealRect () const { return RealRect(min, RealSize(max - min)); }
};
// ----------------------------------------------------------------------------- : SymbolPart
@@ -137,39 +137,39 @@ class Bounds {
/// A part of a symbol, not necesserly a shape
class SymbolPart : public IntrusivePtrVirtualBase {
public:
/// Name/label for this part
String name;
/// Position and size of the part.
/** this is the smallest axis aligned bounding box that fits around the part */
Bounds bounds;
/// Type of this part
virtual String typeName() const = 0;
/// Create a clone of this symbol part
virtual SymbolPartP clone() const = 0;
/// Icon for this part
virtual int icon() const = 0;
/// Convert to SymbolShape?
virtual SymbolShape* isSymbolShape() { return nullptr; }
virtual const SymbolShape* isSymbolShape() const { return nullptr; }
/// Convert to SymbolSymmetry?
virtual SymbolSymmetry* isSymbolSymmetry() { return nullptr; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return nullptr; }
/// Convert to SymbolGroup?
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
/// Does this part contain another?
/** also true if this==that*/
virtual bool isAncestor(const SymbolPart& that) const { return this == &that; }
/// Calculate the position and size of the part (bounds)
virtual void updateBounds();
/// Calculate the position and size of the part using the given rotation matrix
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) = 0;
DECLARE_REFLECTION_VIRTUAL();
/// Name/label for this part
String name;
/// Position and size of the part.
/** this is the smallest axis aligned bounding box that fits around the part */
Bounds bounds;
/// Type of this part
virtual String typeName() const = 0;
/// Create a clone of this symbol part
virtual SymbolPartP clone() const = 0;
/// Icon for this part
virtual int icon() const = 0;
/// Convert to SymbolShape?
virtual SymbolShape* isSymbolShape() { return nullptr; }
virtual const SymbolShape* isSymbolShape() const { return nullptr; }
/// Convert to SymbolSymmetry?
virtual SymbolSymmetry* isSymbolSymmetry() { return nullptr; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return nullptr; }
/// Convert to SymbolGroup?
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
/// Does this part contain another?
/** also true if this==that*/
virtual bool isAncestor(const SymbolPart& that) const { return this == &that; }
/// Calculate the position and size of the part (bounds)
virtual void updateBounds();
/// Calculate the position and size of the part using the given rotation matrix
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) = 0;
DECLARE_REFLECTION_VIRTUAL();
};
template <> SymbolPartP read_new<SymbolPart>(Reader& reader);
@@ -178,50 +178,50 @@ template <> SymbolPartP read_new<SymbolPart>(Reader& reader);
/// How are symbol parts combined with parts below it?
enum SymbolShapeCombine
{ SYMBOL_COMBINE_MERGE
, SYMBOL_COMBINE_SUBTRACT
, SYMBOL_COMBINE_INTERSECTION
, SYMBOL_COMBINE_DIFFERENCE
, SYMBOL_COMBINE_OVERLAP
, SYMBOL_COMBINE_BORDER
{ SYMBOL_COMBINE_MERGE
, SYMBOL_COMBINE_SUBTRACT
, SYMBOL_COMBINE_INTERSECTION
, SYMBOL_COMBINE_DIFFERENCE
, SYMBOL_COMBINE_OVERLAP
, SYMBOL_COMBINE_BORDER
};
/// A sane mod function, always returns a result in the range [0..size)
inline size_t mod(int a, size_t size) {
int m = a % (int)size;
return m >= 0 ? m : m + size;
int m = a % (int)size;
return m >= 0 ? m : m + size;
}
/// A single shape (polygon/bezier-gon) in a Symbol
class SymbolShape : public SymbolPart {
public:
/// The points of this polygon
vector<ControlPointP> points;
/// How is this part combined with parts below it?
SymbolShapeCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotation_center;
SymbolShape();
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return combine; }
virtual SymbolShape* isSymbolShape() { return this; }
virtual const SymbolShape* isSymbolShape() const { return this; }
/// Get a control point, wraps around
inline ControlPointP getPoint(int id) const {
return points[mod(id, points.size())];
}
/// Enforce lock constraints
void enforceConstraints();
/// Calculate the position and size of the part using the given rotation matrix
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity);
DECLARE_REFLECTION();
/// The points of this polygon
vector<ControlPointP> points;
/// How is this part combined with parts below it?
SymbolShapeCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotation_center;
SymbolShape();
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return combine; }
virtual SymbolShape* isSymbolShape() { return this; }
virtual const SymbolShape* isSymbolShape() const { return this; }
/// Get a control point, wraps around
inline ControlPointP getPoint(int id) const {
return points[mod(id, points.size())];
}
/// Enforce lock constraints
void enforceConstraints();
/// Calculate the position and size of the part using the given rotation matrix
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolGroup
@@ -229,52 +229,52 @@ class SymbolShape : public SymbolPart {
/// A group of symbol parts
class SymbolGroup : public SymbolPart {
public:
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
SymbolGroup();
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return SYMBOL_COMBINE_BORDER + 3; }
virtual SymbolGroup* isSymbolGroup() { return this; }
virtual const SymbolGroup* isSymbolGroup() const { return this; }
virtual bool isAncestor(const SymbolPart& that) const;
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity);
DECLARE_REFLECTION();
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
SymbolGroup();
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return SYMBOL_COMBINE_BORDER + 3; }
virtual SymbolGroup* isSymbolGroup() { return this; }
virtual const SymbolGroup* isSymbolGroup() const { return this; }
virtual bool isAncestor(const SymbolPart& that) const;
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolSymmetry
enum SymbolSymmetryType
{ SYMMETRY_ROTATION
, SYMMETRY_REFLECTION
{ SYMMETRY_ROTATION
, SYMMETRY_REFLECTION
};
/// A mirror, reflecting the part of the symbol in the group
/** Can handle rotation symmetry with any number of reflections */
class SymbolSymmetry : public SymbolGroup {
public:
SymbolSymmetryType kind; ///< What kind of symmetry
int copies; ///< How many times is the orignal reflected (including the original itself)
bool clip; ///< Clip the orignal so it doesn't intersect the mirror(s)
Vector2D center; ///< Center point of the mirror
Vector2D handle; ///< A handle pointing in the direction of the original, relative to the center
SymbolSymmetry();
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return kind + SYMBOL_COMBINE_BORDER + 1; }
virtual SymbolSymmetry* isSymbolSymmetry() { return this; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return this; }
String expectedName() const;
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity);
DECLARE_REFLECTION();
SymbolSymmetryType kind; ///< What kind of symmetry
int copies; ///< How many times is the orignal reflected (including the original itself)
bool clip; ///< Clip the orignal so it doesn't intersect the mirror(s)
Vector2D center; ///< Center point of the mirror
Vector2D handle; ///< A handle pointing in the direction of the original, relative to the center
SymbolSymmetry();
virtual String typeName() const;
virtual SymbolPartP clone() const;
virtual int icon() const { return kind + SYMBOL_COMBINE_BORDER + 1; }
virtual SymbolSymmetry* isSymbolSymmetry() { return this; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return this; }
String expectedName() const;
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Symbol
@@ -282,13 +282,13 @@ class SymbolSymmetry : public SymbolGroup {
/// An editable symbol, consists of any number of SymbolParts
class Symbol : public SymbolGroup {
public:
/// Actions performed on this symbol and the parts in it
ActionStack actions;
/// Determine the aspect ratio best suited for this symbol
double aspectRatio() const;
DECLARE_REFLECTION();
/// Actions performed on this symbol and the parts in it
ActionStack actions;
/// Determine the aspect ratio best suited for this symbol
double aspectRatio() const;
DECLARE_REFLECTION();
};
/// A default symbol: a square
@@ -301,20 +301,20 @@ SymbolP default_symbol();
*/
class SymbolView : public ActionListener {
public:
SymbolView();
~SymbolView();
SymbolView();
~SymbolView();
/// Get the symbol that is currently being viewed
inline SymbolP getSymbol() { return symbol; }
/// Change the symbol that is being viewed
void setSymbol(const SymbolP& symbol);
/// Get the symbol that is currently being viewed
inline SymbolP getSymbol() { return symbol; }
/// Change the symbol that is being viewed
void setSymbol(const SymbolP& symbol);
protected:
/// The symbol that is currently being viewed, should not be modified directly!
SymbolP symbol;
/// Called when another symbol is being viewn (using setSymbol)
virtual void onChangeSymbol() {}
/// The symbol that is currently being viewed, should not be modified directly!
SymbolP symbol;
/// Called when another symbol is being viewn (using setSymbol)
virtual void onChangeSymbol() {}
};
+438 -438
View File
File diff suppressed because it is too large Load Diff
+118 -118
View File
@@ -27,117 +27,117 @@ struct CharInfo;
/// A font that is drawn using images
class SymbolFont : public Packaged {
public:
SymbolFont();
~SymbolFont();
/// Loads the symbol font with a given name, for example "magic-mana-large"
static SymbolFontP byName(const String& name);
// Script update
void update(Context& ctx) const;
/// A symbol to be drawn
class DrawableSymbol {
public:
inline DrawableSymbol(const String& text, const String& draw_text, SymbolInFont& symbol)
: text(text), draw_text(draw_text), symbol(&symbol)
{}
String text; ///< Original text
String draw_text;///< Text to draw (extracted from the regex to avoid performance costs)
SymbolInFont* symbol; ///< Symbol to draw
};
typedef vector<DrawableSymbol> SplitSymbols;
/// Split a string into separate symbols for drawing and for determining their size
void split(const String& text, SplitSymbols& out) const;
/// How many consecutive characters of the text, starting at start can be rendered with this symbol font?
size_t recognizePrefix(const String& text, size_t start) const;
/// Draw a piece of text
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text);
/// Get information on characters in a string
void getCharInfo(RotatedDC& dc, Context& ctx, double font_size, const String& text, vector<CharInfo>& out);
/// Draw a piece of text prepared using split
void draw(RotatedDC& dc, RealRect rect, double font_size, const Alignment& align, const SplitSymbols& text);
/// Get information on characters in a string
void getCharInfo(RotatedDC& dc, double font_size, const SplitSymbols& text, vector<CharInfo>& out);
/// Get the image for a symbol
Image getImage(double font_size, const DrawableSymbol& symbol);
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
/// Generate a 'insert symbol' menu.
/** This class owns the menu!
* All ids used will be in the range ID_INSERT_SYMBOL_MENU_MIN...ID_INSERT_SYMBOL_MENU_MAX.
* If there is no insert symbol menu, returns nullptr.
*/
wxMenu* insertSymbolMenu(Context& ctx);
/// Process a choice from the insert symbol menu
/** Return the code representing the symbol */
String insertSymbolCode(int menu_id) const;
SymbolFont();
~SymbolFont();
/// Loads the symbol font with a given name, for example "magic-mana-large"
static SymbolFontP byName(const String& name);
// Script update
void update(Context& ctx) const;
/// A symbol to be drawn
class DrawableSymbol {
public:
inline DrawableSymbol(const String& text, const String& draw_text, SymbolInFont& symbol)
: text(text), draw_text(draw_text), symbol(&symbol)
{}
String text; ///< Original text
String draw_text;///< Text to draw (extracted from the regex to avoid performance costs)
SymbolInFont* symbol; ///< Symbol to draw
};
typedef vector<DrawableSymbol> SplitSymbols;
/// Split a string into separate symbols for drawing and for determining their size
void split(const String& text, SplitSymbols& out) const;
/// How many consecutive characters of the text, starting at start can be rendered with this symbol font?
size_t recognizePrefix(const String& text, size_t start) const;
/// Draw a piece of text
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text);
/// Get information on characters in a string
void getCharInfo(RotatedDC& dc, Context& ctx, double font_size, const String& text, vector<CharInfo>& out);
/// Draw a piece of text prepared using split
void draw(RotatedDC& dc, RealRect rect, double font_size, const Alignment& align, const SplitSymbols& text);
/// Get information on characters in a string
void getCharInfo(RotatedDC& dc, double font_size, const SplitSymbols& text, vector<CharInfo>& out);
/// Get the image for a symbol
Image getImage(double font_size, const DrawableSymbol& symbol);
static String typeNameStatic();
virtual String typeName() const;
Version fileVersion() const;
/// Generate a 'insert symbol' menu.
/** This class owns the menu!
* All ids used will be in the range ID_INSERT_SYMBOL_MENU_MIN...ID_INSERT_SYMBOL_MENU_MAX.
* If there is no insert symbol menu, returns nullptr.
*/
wxMenu* insertSymbolMenu(Context& ctx);
/// Process a choice from the insert symbol menu
/** Return the code representing the symbol */
String insertSymbolCode(int menu_id) const;
private:
double img_size; ///< Font size that the images use
RealSize spacing; ///< Spacing between sybmols (for the default font size)
// writing text
bool scale_text; ///< Should text be scaled down to fit in a symbol?
InsertSymbolMenuP insert_symbol_menu;
wxMenu* processed_insert_symbol_menu;
friend class SymbolInFont;
friend class InsertSymbolMenu;
vector<SymbolInFontP> symbols; ///< The individual symbols
/// Find the default symbol
/** may return nullptr */
SymbolInFont* defaultSymbol() const;
/// Draws a single symbol inside the given rectangle
void drawSymbol (RotatedDC& dc, RealRect sym_rect, double font_size, const Alignment& align, SymbolInFont& sym, const String& text);
/// Size of a single symbol, including spacing
RealSize symbolSize (double font_size, const DrawableSymbol& sym);
double img_size; ///< Font size that the images use
RealSize spacing; ///< Spacing between sybmols (for the default font size)
// writing text
bool scale_text; ///< Should text be scaled down to fit in a symbol?
InsertSymbolMenuP insert_symbol_menu;
wxMenu* processed_insert_symbol_menu;
friend class SymbolInFont;
friend class InsertSymbolMenu;
vector<SymbolInFontP> symbols; ///< The individual symbols
/// Find the default symbol
/** may return nullptr */
SymbolInFont* defaultSymbol() const;
/// Draws a single symbol inside the given rectangle
void drawSymbol (RotatedDC& dc, RealRect sym_rect, double font_size, const Alignment& align, SymbolInFont& sym, const String& text);
/// Size of a single symbol, including spacing
RealSize symbolSize (double font_size, const DrawableSymbol& sym);
public:
/// The default size of symbols, including spacing
RealSize defaultSymbolSize(double font_size);
DECLARE_REFLECTION();
/// The default size of symbols, including spacing
RealSize defaultSymbolSize(double font_size);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : InsertSymbolMenu
enum MenuItemType
{ ITEM_CODE ///< Name gives the code to insert
, ITEM_CUSTOM ///< Use a dialog box
, ITEM_LINE ///< A menu separator
, ITEM_SUBMENU ///< A submenu
{ ITEM_CODE ///< Name gives the code to insert
, ITEM_CUSTOM ///< Use a dialog box
, ITEM_LINE ///< A menu separator
, ITEM_SUBMENU ///< A submenu
};
/// Description of a menu to insert symbols from a symbol font into the text
class InsertSymbolMenu : public IntrusivePtrBase<InsertSymbolMenu> {
public:
InsertSymbolMenu();
MenuItemType type;
String name;
vector<InsertSymbolMenuP> items;
/// Number of ids used (recursive)
int size() const;
/// Get the code for an item, id relative to the start of this menu
String getCode(int id, const SymbolFont& font) const;
/// Make an actual menu
wxMenu* makeMenu(int first_id, SymbolFont& font) const;
/// Make an actual menu item
wxMenuItem* makeMenuItem(wxMenu* parent, int first_id, SymbolFont& font) const;
DECLARE_REFLECTION();
InsertSymbolMenu();
MenuItemType type;
String name;
vector<InsertSymbolMenuP> items;
/// Number of ids used (recursive)
int size() const;
/// Get the code for an item, id relative to the start of this menu
String getCode(int id, const SymbolFont& font) const;
/// Make an actual menu
wxMenu* makeMenu(int first_id, SymbolFont& font) const;
/// Make an actual menu item
wxMenuItem* makeMenuItem(wxMenu* parent, int first_id, SymbolFont& font) const;
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolFontRef
@@ -145,26 +145,26 @@ class InsertSymbolMenu : public IntrusivePtrBase<InsertSymbolMenu> {
/// A reference to an actual symbol font
class SymbolFontRef {
public:
SymbolFontRef();
// Script update
bool update(Context& ctx);
void initDependencies(Context&, const Dependency&) const;
/// Is a font loaded?
bool valid() const;
Scriptable<String> name; ///< Font package name, can be changed with script
Scriptable<double> size; ///< Size of the font
double scale_down_to; ///< Mimumum size of the font
Scriptable<Alignment> alignment; ///< Alignment of symbols in a line of text
SymbolFontP font; ///< The font, if it is loaded
SymbolFontRef();
// Script update
bool update(Context& ctx);
void initDependencies(Context&, const Dependency&) const;
/// Is a font loaded?
bool valid() const;
Scriptable<String> name; ///< Font package name, can be changed with script
Scriptable<double> size; ///< Size of the font
double scale_down_to; ///< Mimumum size of the font
Scriptable<Alignment> alignment; ///< Alignment of symbols in a line of text
SymbolFontP font; ///< The font, if it is loaded
private:
DECLARE_REFLECTION();
/// (re)load the symbol font based on name
void loadFont(Context& ctx);
DECLARE_REFLECTION();
/// (re)load the symbol font based on name
void loadFont(Context& ctx);
};
// ----------------------------------------------------------------------------- : EOF
+21 -21
View File
@@ -12,40 +12,40 @@
// ----------------------------------------------------------------------------- : WordList
WordListWord::WordListWord()
: line_below(false)
, is_prefix(false)
: line_below(false)
, is_prefix(false)
{}
IMPLEMENT_REFLECTION_NO_SCRIPT(WordListWord) {
if (line_below || is_prefix || isGroup() || script || (tag.reading() && tag.isComplex())) {
// complex value
REFLECT(name);
REFLECT(line_below);
REFLECT(is_prefix);
REFLECT(words);
REFLECT(script);
} else {
REFLECT_NAMELESS(name);
}
if (line_below || is_prefix || isGroup() || script || (tag.reading() && tag.isComplex())) {
// complex value
REFLECT(name);
REFLECT(line_below);
REFLECT(is_prefix);
REFLECT(words);
REFLECT(script);
} else {
REFLECT_NAMELESS(name);
}
}
IMPLEMENT_REFLECTION_NO_SCRIPT(WordList) {
REFLECT(name);
REFLECT(words);
REFLECT(name);
REFLECT(words);
}
// ----------------------------------------------------------------------------- : Auto replace words
AutoReplace::AutoReplace()
: enabled(true)
, whole_word(true)
, custom(true)
: enabled(true)
, whole_word(true)
, custom(true)
{}
IMPLEMENT_REFLECTION_NO_SCRIPT(AutoReplace) {
REFLECT(enabled);
REFLECT(whole_word);
REFLECT(match);
REFLECT(replace);
REFLECT(enabled);
REFLECT(whole_word);
REFLECT(match);
REFLECT(replace);
}
+17 -17
View File
@@ -22,23 +22,23 @@ DECLARE_POINTER_TYPE(AutoReplace);
/// A word in a WordList
class WordListWord : public IntrusivePtrBase<WordListWord> {
public:
WordListWord();
WordListWord();
String name; ///< Name of the list / the word
bool line_below; ///< Line below in the list?
bool is_prefix; ///< Is this a prefix before other words?
vector<WordListWordP> words; ///< Sublist
OptionalScript script; ///< Generate words using a script
String name; ///< Name of the list / the word
bool line_below; ///< Line below in the list?
bool is_prefix; ///< Is this a prefix before other words?
vector<WordListWordP> words; ///< Sublist
OptionalScript script; ///< Generate words using a script
inline bool isGroup() const { return !words.empty(); }
inline bool isGroup() const { return !words.empty(); }
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
/// A list of words for a drop down box
class WordList : public WordListWord {
public:
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Auto replace words
@@ -46,17 +46,17 @@ class WordList : public WordListWord {
/// Autoreplace specific shortcut words
class AutoReplace : public IntrusivePtrVirtualBase {
public:
AutoReplace();
AutoReplace();
bool enabled;
bool whole_word;
bool custom; ///< Is this a custom auto replace?
String match;
String replace;
bool enabled;
bool whole_word;
bool custom; ///< Is this a custom auto replace?
String match;
String replace;
inline AutoReplaceP clone() const { return intrusive(new AutoReplace(*this)); }
inline AutoReplaceP clone() const { return intrusive(new AutoReplace(*this)); }
DECLARE_REFLECTION();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF