//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) Twan van Laarhoven and the other MSE developers | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ // ----------------------------------------------------------------------------- : Includes #include #include #include #include #include #include #include #include #include #include #include #include #include // for ValueActionPerformer #include // ----------------------------------------------------------------------------- : ValueAction String ValueAction::getName(bool to_undo) const { return _ACTION_1_("change", valueP->fieldP->name); } void ValueAction::perform(bool to_undo) { if (card) { swap(const_cast(card)->time_modified, old_time_modified); } } void ValueAction::isOnCard(Card* card) { this->card = card; } // ----------------------------------------------------------------------------- : Simple /// Swap the value in a Value object with a new one inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); } inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); } inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); } inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); } inline void swap_value(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); } /// A ValueAction that swaps between old and new values template class SimpleValueAction : public ValueAction { public: inline SimpleValueAction(const intrusive_ptr& value, const typename T::ValueType& new_value) : ValueAction(value), new_value(new_value) {} void perform(bool to_undo) override { ValueAction::perform(to_undo); swap_value(static_cast(*valueP), new_value); valueP->onAction(*this, to_undo); // notify value } bool merge(const Action& action) override { 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; }; unique_ptr value_action(const ChoiceValueP& value, const Defaultable& new_value) { return make_unique>(value, new_value); } unique_ptr value_action(const ColorValueP& value, const Defaultable& new_value) { return make_unique>(value, new_value); } unique_ptr value_action(const ImageValueP& value, const LocalFileName& new_value) { return make_unique>(value, new_value); } unique_ptr value_action(const SymbolValueP& value, const LocalFileName& new_value) { return make_unique>(value, new_value); } unique_ptr value_action(const PackageChoiceValueP& value, const String& new_value) { return make_unique>(value, new_value); } unique_ptr value_action(const MultipleChoiceValueP& value, const Defaultable& new_value, const String& last_change) { MultipleChoiceValue::ValueType v = { new_value, last_change }; return make_unique>(value, v); } // ----------------------------------------------------------------------------- : Text TextValueAction::TextValueAction(const TextValueP& value, size_t start, size_t end, size_t new_end, const Defaultable& new_value, const String& 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 } 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; } TextValue& TextValueAction::value() const { return static_cast(*valueP); } unique_ptr 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 += _(""); new_value += str.substr(end_i); } else { // we are inside this tag, 'remove' it new_value = str.substr(0, start_i); new_value += _(""); 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 , 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 make_unique(value, start, end, end, new_value, action_name); } } unique_ptr 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 make_unique(value, end, start, start+untag(replacement).size(), new_value, action_name); } else { return make_unique(value, start, end, start+untag(replacement).size(), new_value, action_name); } } } // ----------------------------------------------------------------------------- : Reminder text TextToggleReminderAction::TextToggleReminderAction(const TextValueP& value, size_t pos_in) : ValueAction(value) { pos = in_tag(value->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"); } void TextToggleReminderAction::perform(bool to_undo) { ValueAction::perform(to_undo); TextValue& value = static_cast(*valueP); String& val = value.value.mutate(); assert(pos + 4 < val.size()); size_t end = match_close_tag(val, pos); wxUniChar c = old; old = val[pos + 4]; val[pos + 4] = c; if (end != String::npos && end + 5 < val.size()) { val[end + 5] = 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")); } void ScriptValueEvent::perform(bool) { 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")); } void ScriptStyleEvent::perform(bool) { 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) {} ValueActionPerformer::~ValueActionPerformer() {} void ValueActionPerformer::addAction(unique_ptr&& action) { action->isOnCard(card); set->actions.addAction(move(action)); } Package& ValueActionPerformer::getLocalPackage() { return *set; }