mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
linking refactor
This commit is contained in:
+89
-80
@@ -22,66 +22,74 @@ AddCardAction::AddCardAction(Set& set)
|
||||
, action(ADD, make_intrusive<Card>(*set.game), set.cards)
|
||||
{}
|
||||
|
||||
AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const CardP& card)
|
||||
: CardListAction(set)
|
||||
, action(ar, card, set.cards)
|
||||
{}
|
||||
|
||||
AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector<CardP>& cards)
|
||||
: CardListAction(set)
|
||||
, action(ar, cards, set.cards)
|
||||
{}
|
||||
{
|
||||
// If we are adding cards, resolve any uid conflicts
|
||||
// We always assume uid conflicts occur because a card was copy-pasted into the same set,
|
||||
// and never because two different cards randomly got assigned the same uid
|
||||
if (!action.adding) return;
|
||||
// Tally existing unique ids
|
||||
unordered_map<String, CardP> all_existing_cards;
|
||||
unordered_set<String> all_existing_uids;
|
||||
FOR_EACH(card, set.cards) {
|
||||
all_existing_cards.insert({ card->uid, card });
|
||||
all_existing_uids.insert(card->uid);
|
||||
}
|
||||
// Tally added unique ids
|
||||
unordered_map<String, CardP> all_added_uids;
|
||||
for (size_t pos = 0; pos < action.steps.size(); ++pos) {
|
||||
CardP card = action.steps[pos].item;
|
||||
all_added_uids.insert({ card->uid, card });
|
||||
}
|
||||
// Cycle on the added cards
|
||||
unordered_map<String, CardP> all_added_uids_copy(all_added_uids);
|
||||
FOR_EACH(added_pair, all_added_uids_copy) {
|
||||
String old_uid = added_pair.first;
|
||||
CardP added_card = added_pair.second;
|
||||
// Assign new unique id if necessary
|
||||
if (all_existing_cards.find(old_uid) == all_existing_cards.end()) continue;
|
||||
String new_uid = generate_uid();
|
||||
added_card->uid = new_uid;
|
||||
all_added_uids.insert({ new_uid, added_card });
|
||||
// Update links on linked cards
|
||||
LINK_PAIRS(linked_pairs, added_card);
|
||||
FOR_EACH(linked_pair, linked_pairs) {
|
||||
String& linked_uid = linked_pair.first.get();
|
||||
String& linked_relation = linked_pair.second.get();
|
||||
if (linked_uid.empty()) continue;
|
||||
// If it's an added card, replace the link
|
||||
if (all_added_uids.find(linked_uid) != all_added_uids.end()) {
|
||||
all_added_uids.at(linked_uid)->updateLinkedUID(old_uid, new_uid);
|
||||
}
|
||||
// Otherwise, if it's an existing card, create an action to copy the link
|
||||
else if (all_existing_cards.find(linked_uid) != all_existing_cards.end()) {
|
||||
CardP linked_card = all_existing_cards.at(linked_uid);
|
||||
int linked_index = linked_card->findFreeLink(new_uid, all_existing_uids);
|
||||
if (linked_index < 0) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_1_("not enough free links", linked_card->identification()));
|
||||
}
|
||||
else {
|
||||
String relation(linked_card->getLinkedRelation(linked_index));
|
||||
card_link_actions.push_back(make_intrusive<OneWayLinkCardsAction>(set, linked_card, new_uid, relation, linked_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String AddCardAction::getName(bool to_undo) const {
|
||||
return action.getName();
|
||||
}
|
||||
|
||||
void AddCardAction::perform(bool to_undo) {
|
||||
// If we are adding cards, resolve any uid conflicts
|
||||
// (If we are re-adding cards, from a remove undo, there shouldn't be any uid conflicts)
|
||||
// We always assume uid conflicts occur because a card was copy-pasted into the same set,
|
||||
// and never because two different cards randomly got assigned the same uid
|
||||
if (action.adding && !to_undo) {
|
||||
// Tally existing unique ids
|
||||
unordered_map<String, CardP> all_existing_uids;
|
||||
FOR_EACH(card, set.cards) {
|
||||
all_existing_uids.insert({ card->uid, card });
|
||||
}
|
||||
// Tally added unique ids
|
||||
unordered_map<String, CardP> all_added_uids;
|
||||
for (size_t pos = 0; pos < action.steps.size(); ++pos) {
|
||||
CardP card = action.steps[pos].item;
|
||||
all_added_uids.insert({ card->uid, card });
|
||||
}
|
||||
FOR_EACH(added_pair, all_added_uids) {
|
||||
String old_uid = added_pair.first;
|
||||
CardP added_card = added_pair.second;
|
||||
// Assign new unique ids
|
||||
if (all_existing_uids.find(old_uid) != all_existing_uids.end()) {
|
||||
String new_uid = generate_uid();
|
||||
added_card->uid = new_uid;
|
||||
all_added_uids.insert({ new_uid, added_card });
|
||||
// Update links on linked cards
|
||||
OTHER_LINKED_PAIRS(linked_pairs, added_card);
|
||||
FOR_EACH(linked_pair, linked_pairs) {
|
||||
String& linked_uid = linked_pair.first.get();
|
||||
String& linked_relation = linked_pair.second.get();
|
||||
if (linked_uid.empty()) continue;
|
||||
// If it's an added card, replace the link
|
||||
if (all_added_uids.find(linked_uid) != all_added_uids.end()) {
|
||||
all_added_uids.at(linked_uid)->updateLink(old_uid, new_uid);
|
||||
}
|
||||
// Otherwise, if it's an existing card, copy the link
|
||||
else if (all_existing_uids.find(linked_uid) != all_existing_uids.end()) {
|
||||
all_existing_uids.at(linked_uid)->copyLink(set, old_uid, new_uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add or remove cards
|
||||
action.perform(set.cards, to_undo);
|
||||
// Update card links
|
||||
for (int i = 0; i < card_link_actions.size(); ++i) {
|
||||
card_link_actions[i]->perform(to_undo);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reorder cards
|
||||
@@ -108,39 +116,40 @@ void ReorderCardsAction::perform(bool to_undo) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Link cards
|
||||
|
||||
LinkCardsAction::LinkCardsAction(Set& set, const CardP& selected_card, vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation)
|
||||
: CardListAction(set), selected_card(selected_card), linked_cards(linked_cards), selected_relation(selected_relation), linked_relation(linked_relation)
|
||||
{}
|
||||
OneWayLinkCardsAction::OneWayLinkCardsAction(Set& set, CardP& card, const String& uid, const String& relation, int index)
|
||||
: CardListAction(set), card(card), uid(uid), relation(relation)
|
||||
{
|
||||
switch (index) {
|
||||
case 1: {
|
||||
linked_uid = &card->linked_card_1;
|
||||
linked_relation = &card->linked_relation_1;
|
||||
return;
|
||||
}
|
||||
case 2: {
|
||||
linked_uid = &card->linked_card_2;
|
||||
linked_relation = &card->linked_relation_2;
|
||||
return;
|
||||
}
|
||||
case 3: {
|
||||
linked_uid = &card->linked_card_3;
|
||||
linked_relation = &card->linked_relation_3;
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
linked_uid = &card->linked_card_4;
|
||||
linked_relation = &card->linked_relation_4;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String LinkCardsAction::getName(bool to_undo) const {
|
||||
return _("Link cards");
|
||||
String OneWayLinkCardsAction::getName(bool to_undo) const {
|
||||
return _("Change link");
|
||||
}
|
||||
|
||||
void LinkCardsAction::perform(bool to_undo) {
|
||||
if (!to_undo) {
|
||||
selected_card->link(set, linked_cards, selected_relation, linked_relation);
|
||||
} else {
|
||||
selected_card->unlink(linked_cards);
|
||||
}
|
||||
}
|
||||
|
||||
UnlinkCardsAction::UnlinkCardsAction(Set& set, const CardP& selected_card, CardP& unlinked_card)
|
||||
: CardListAction(set), selected_card(selected_card), unlinked_card(unlinked_card)
|
||||
{}
|
||||
|
||||
String UnlinkCardsAction::getName(bool to_undo) const {
|
||||
return _("Unlink card");
|
||||
}
|
||||
|
||||
void UnlinkCardsAction::perform(bool to_undo) {
|
||||
if (!to_undo) {
|
||||
pair<String, String> relations = selected_card->unlink(unlinked_card);
|
||||
selected_relation = relations.first;
|
||||
unlinked_relation = relations.second;
|
||||
}
|
||||
else {
|
||||
selected_card->link(set, unlinked_card, selected_relation, unlinked_relation);
|
||||
}
|
||||
void OneWayLinkCardsAction::perform(bool to_undo) {
|
||||
swap(*linked_uid, uid);
|
||||
swap(*linked_relation, relation);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Change stylesheet
|
||||
@@ -236,7 +245,7 @@ String ChangeCardUIDAction::getName(bool to_undo) const {
|
||||
}
|
||||
void ChangeCardUIDAction::perform(bool to_undo) {
|
||||
FOR_EACH(c, set.cards) {
|
||||
c->updateLink(card->uid, uid);
|
||||
c->updateLinkedUID(card->uid, uid);
|
||||
}
|
||||
swap(card->uid, uid);
|
||||
}
|
||||
|
||||
+11
-24
@@ -41,13 +41,13 @@ 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);
|
||||
|
||||
String getName(bool to_undo) const override;
|
||||
void perform(bool to_undo) override;
|
||||
|
||||
const GenericAddAction<CardP> action;
|
||||
const GenericAddAction<CardP> action;
|
||||
vector<ActionP> card_link_actions;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Reorder cards
|
||||
@@ -67,32 +67,19 @@ public:
|
||||
// ----------------------------------------------------------------------------- : Link cards
|
||||
|
||||
/// Add a link between two or more cards
|
||||
class LinkCardsAction : public CardListAction {
|
||||
class OneWayLinkCardsAction : public CardListAction {
|
||||
public:
|
||||
LinkCardsAction(Set& set, const CardP& selected_card, vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation);
|
||||
|
||||
OneWayLinkCardsAction(Set& set, CardP& card, const String& uid, const String& relation, int index);
|
||||
|
||||
String getName(bool to_undo) const override;
|
||||
void perform(bool to_undo) override;
|
||||
|
||||
|
||||
//private:
|
||||
CardP selected_card; ///< The card currently selected in the cards tab
|
||||
vector<CardP> linked_cards; ///< The cards that will be linked to the selected card
|
||||
String selected_relation; ///< The nature of the relation of the selected card
|
||||
String linked_relation; ///< The nature of the relation of the linked cards
|
||||
};
|
||||
/// Remove a link between two cards
|
||||
class UnlinkCardsAction : public CardListAction {
|
||||
public:
|
||||
UnlinkCardsAction(Set& set, const CardP& selected_card, CardP& unlinked_card);
|
||||
|
||||
String getName(bool to_undo) const override;
|
||||
void perform(bool to_undo) override;
|
||||
|
||||
//private:
|
||||
CardP selected_card; ///< The card currently selected in the cards tab
|
||||
CardP unlinked_card; ///< The card that will be unlinked from the selected card
|
||||
String selected_relation; ///< The nature of the relation of the selected card
|
||||
String unlinked_relation; ///< The nature of the relation of the unlinked card
|
||||
CardP card; ///< The card we change the link of. We hold a reference so it doesn't get destroyed before this.
|
||||
String* linked_uid; ///< The uid slot we need to change
|
||||
String* linked_relation; ///< The relation slot we need to change
|
||||
String uid; ///< The uid we have to change the link to
|
||||
String relation; ///< The relation we have to change the link to
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : Change stylesheet
|
||||
|
||||
@@ -212,12 +212,12 @@ void ScriptStyleEvent::perform(bool) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Bulk action
|
||||
|
||||
BulkAction::BulkAction(const vector<shared_ptr<Action>>& actions, const SetP& set, CardListBase* card_list_window)
|
||||
BulkAction::BulkAction(const vector<ActionP>& actions, const SetP& set, CardListBase* card_list_window)
|
||||
: actions(actions), set(set), card_list_window(card_list_window)
|
||||
{
|
||||
if (actions.empty()) throw ScriptError(_("BulkAction created with no actions"));
|
||||
name_do = actions.front()->getName(false) + _(" ") + _ACTION_("bulk");
|
||||
name_undo = actions.front()->getName(true) + _(" ") + _ACTION_("bulk");
|
||||
name_do = _ACTION_1_("bulk", actions.front()->getName(false));
|
||||
name_undo = _ACTION_1_("bulk", actions.front()->getName(true));
|
||||
}
|
||||
BulkAction::~BulkAction() {}
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ public:
|
||||
// An action that's just a list of other actions
|
||||
class BulkAction : public Action {
|
||||
public:
|
||||
BulkAction(const vector<shared_ptr<Action>>& actions, const SetP& set, CardListBase* card_list_window);
|
||||
BulkAction(const vector<ActionP>& actions, const SetP& set, CardListBase* card_list_window);
|
||||
~BulkAction() override;
|
||||
|
||||
String getName(bool to_undo) const override;
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
private:
|
||||
String name_do;
|
||||
String name_undo;
|
||||
vector<shared_ptr<Action>> actions;
|
||||
vector<ActionP> actions;
|
||||
SetP set;
|
||||
CardListBase* card_list_window;
|
||||
};
|
||||
|
||||
+139
-216
@@ -63,205 +63,110 @@ bool Card::contains(QuickFilterPart const& query) const {
|
||||
}
|
||||
if (query.match(_("notes"), notes)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Card::link(const Set& set, const vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation)
|
||||
{
|
||||
unlink(linked_cards);
|
||||
|
||||
unordered_set<String> all_existing_uids;
|
||||
FOR_EACH(card, set.cards) {
|
||||
all_existing_uids.insert(card->uid);
|
||||
}
|
||||
int free_link_count = 0;
|
||||
THIS_LINKED_PAIRS(this_linked_pairs);
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
if (
|
||||
this_linked_uid.empty() || // Not a reference
|
||||
all_existing_uids.find(this_linked_uid) == all_existing_uids.end() // Reference to nonexistent card
|
||||
) free_link_count++;
|
||||
}
|
||||
if (free_link_count < linked_cards.size()) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_("not enough free links"));
|
||||
return;
|
||||
}
|
||||
|
||||
vector<CardP> all_missed_cards;
|
||||
FOR_EACH(linked_card, linked_cards) {
|
||||
bool written = false;
|
||||
// Try to write to a free spot
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (this_linked_uid.empty()) {
|
||||
this_linked_uid = linked_card->uid;
|
||||
this_linked_relation = linked_relation;
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
|
||||
vector<int> Card::findFreeLinks(vector<String>& linked_uids, const unordered_set<String>& all_existing_uids) {
|
||||
vector<int> freeIndexes;
|
||||
int count = min(4, (int)linked_uids.size());
|
||||
LINK_PAIRS(linked_pairs, this);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
String& linked_uid = linked_uids[i];
|
||||
// Try to find an existing link
|
||||
for (int j = 0; j < linked_pairs.size(); ++j) {
|
||||
auto& linked_pair = linked_pairs[j];
|
||||
if (linked_pair.first.get() == linked_uid &&
|
||||
std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end()
|
||||
) {
|
||||
freeIndexes.push_back(j);
|
||||
goto continue_outer;
|
||||
}
|
||||
}
|
||||
// Try to find a free spot
|
||||
for (int j = 0; j < linked_pairs.size(); ++j) {
|
||||
auto& linked_pair = linked_pairs[j];
|
||||
if (linked_pair.first.get().empty() &&
|
||||
std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end()
|
||||
) {
|
||||
freeIndexes.push_back(j);
|
||||
goto continue_outer;
|
||||
}
|
||||
}
|
||||
// Try to write to an erasable spot
|
||||
if (!written) {
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) {
|
||||
this_linked_uid = linked_card->uid;
|
||||
this_linked_relation = linked_relation;
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
// Try to find an erasable spot
|
||||
for (int j = 0; j < linked_pairs.size(); ++j) {
|
||||
auto& linked_pair = linked_pairs[j];
|
||||
if (all_existing_uids.find(linked_pair.first.get()) == all_existing_uids.end() &&
|
||||
std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end()
|
||||
) {
|
||||
freeIndexes.push_back(j);
|
||||
goto continue_outer;
|
||||
}
|
||||
}
|
||||
if (!written) {
|
||||
// Should be impossible to end up here?
|
||||
}
|
||||
}
|
||||
freeIndexes.push_back(-1);
|
||||
continue_outer:;
|
||||
}
|
||||
return freeIndexes;
|
||||
}
|
||||
int Card::findFreeLink(const String& linked_uid, const unordered_set<String>& all_existing_uids) {
|
||||
vector<String> linked_uids { linked_uid };
|
||||
return findFreeLinks(linked_uids, all_existing_uids)[0];
|
||||
}
|
||||
|
||||
int Card::findUIDLink(const String& linked_uid) {
|
||||
if (linked_card_1 == linked_uid) return 0;
|
||||
if (linked_card_2 == linked_uid) return 1;
|
||||
if (linked_card_3 == linked_uid) return 2;
|
||||
if (linked_card_4 == linked_uid) return 3;
|
||||
return -1;
|
||||
}
|
||||
|
||||
vector<int> Card::findRelationLinks(const String& linked_relation) {
|
||||
vector<int> indexes;
|
||||
if (linked_relation_1 == linked_relation) indexes.push_back(0);
|
||||
if (linked_relation_2 == linked_relation) indexes.push_back(1);
|
||||
if (linked_relation_3 == linked_relation) indexes.push_back(2);
|
||||
if (linked_relation_4 == linked_relation) indexes.push_back(3);
|
||||
return indexes;
|
||||
}
|
||||
|
||||
String& Card::getLinkedUID(int index) {
|
||||
switch (index) {
|
||||
case 3: return linked_card_4;
|
||||
case 2: return linked_card_3;
|
||||
case 1: return linked_card_2;
|
||||
default: return linked_card_1;
|
||||
}
|
||||
}
|
||||
String& Card::getLinkedRelation(int index) {
|
||||
switch (index) {
|
||||
case 3: return linked_relation_4;
|
||||
case 2: return linked_relation_3;
|
||||
case 1: return linked_relation_2;
|
||||
default: return linked_relation_1;
|
||||
}
|
||||
}
|
||||
|
||||
OTHER_LINKED_PAIRS(linked_pairs, linked_card);
|
||||
written = false;
|
||||
// Try to write to a free spot
|
||||
FOR_EACH(linked_pair, linked_pairs) {
|
||||
String& linked_uid = linked_pair.first.get();
|
||||
String& linked_relation = linked_pair.second.get();
|
||||
if (linked_uid.empty()) {
|
||||
linked_uid = uid;
|
||||
linked_relation = selected_relation;
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Try to write to an erasable spot
|
||||
if (!written) {
|
||||
FOR_EACH(linked_pair, linked_pairs) {
|
||||
String& linked_uid = linked_pair.first.get();
|
||||
String& linked_relation = linked_pair.second.get();
|
||||
if (all_existing_uids.find(linked_uid) == all_existing_uids.end()) {
|
||||
linked_uid = uid;
|
||||
linked_relation = selected_relation;
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Notify we couldn't write
|
||||
if (!written) {
|
||||
all_missed_cards.push_back(linked_card);
|
||||
}
|
||||
}
|
||||
if (all_missed_cards.size() > 0) {
|
||||
std::stringstream ss;
|
||||
ss << _ERROR_("could not link");
|
||||
for (size_t pos = 0; pos < all_missed_cards.size(); ++pos) {
|
||||
ss << all_missed_cards[pos]->identification();
|
||||
if (pos < all_missed_cards.size() - 1) ss << ", ";
|
||||
};
|
||||
queue_message(MESSAGE_WARNING, wxString(ss.str().c_str()));
|
||||
}
|
||||
void Card::updateLinkedUID(const String& old_uid, const String& new_uid) {
|
||||
int index = findUIDLink(old_uid);
|
||||
if (index >= 0) getLinkedUID(index) = new_uid;
|
||||
}
|
||||
|
||||
void Card::link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation)
|
||||
{
|
||||
vector<CardP> linked_cards { linked_card };
|
||||
link(set, linked_cards, selected_relation, linked_relation);
|
||||
vector<CardP> Card::getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card) {
|
||||
vector<CardP> other_cards;
|
||||
vector<int> indexes = findRelationLinks(linked_relation);
|
||||
for (int i = 0; i < indexes.size(); ++i) {
|
||||
String& linked_uid = getLinkedUID(indexes[i]);
|
||||
CardP other_card = getUIDCard(cards, linked_uid);
|
||||
if (other_card) other_cards.push_back(other_card);
|
||||
else if (erase_if_no_card) {
|
||||
linked_uid = _("");
|
||||
getLinkedRelation(indexes[i]) = _("");
|
||||
}
|
||||
}
|
||||
return other_cards;
|
||||
}
|
||||
|
||||
void Card::unlink(const vector<CardP>& unlinked_cards)
|
||||
{
|
||||
for (size_t pos = 0; pos < unlinked_cards.size(); ++pos) {
|
||||
CardP unlinked_card = unlinked_cards[pos];
|
||||
unlink(unlinked_card);
|
||||
}
|
||||
}
|
||||
|
||||
pair<String, String> Card::unlink(CardP& unlinked_card)
|
||||
{
|
||||
String old_selected_relation = _("");
|
||||
THIS_LINKED_PAIRS(this_linked_pairs);
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (this_linked_uid == unlinked_card->uid) {
|
||||
old_selected_relation = this_linked_relation;
|
||||
this_linked_uid = _("");
|
||||
this_linked_relation = _("");
|
||||
}
|
||||
}
|
||||
String old_unlinked_relation = _("");
|
||||
OTHER_LINKED_PAIRS(unlinked_pairs, unlinked_card);
|
||||
FOR_EACH(unlinked_pair, unlinked_pairs) {
|
||||
String& unlinked_uid = unlinked_pair.first.get();
|
||||
String& unlinked_relation = unlinked_pair.second.get();
|
||||
if (unlinked_uid == uid) {
|
||||
old_unlinked_relation = unlinked_relation;
|
||||
unlinked_uid = _("");
|
||||
unlinked_relation = _("");
|
||||
}
|
||||
}
|
||||
return make_pair(old_selected_relation, old_unlinked_relation);
|
||||
}
|
||||
|
||||
void Card::copyLink(const Set& set, String old_uid, String new_uid) {
|
||||
// Find what relation we need to copy
|
||||
String relation_copy = _("");
|
||||
THIS_LINKED_PAIRS(this_linked_pairs);
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (this_linked_uid == old_uid) {
|
||||
relation_copy = this_linked_relation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Nothing to copy
|
||||
if (relation_copy.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to copy to a free spot
|
||||
bool written = false;
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (this_linked_uid.empty()) {
|
||||
this_linked_uid = new_uid;
|
||||
this_linked_relation = relation_copy;
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Try to copy to an erasable spot
|
||||
if (!written) {
|
||||
unordered_set<String> all_existing_uids;
|
||||
FOR_EACH(card, set.cards) {
|
||||
all_existing_uids.insert(card->uid);
|
||||
}
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) {
|
||||
this_linked_uid = new_uid;
|
||||
this_linked_relation = relation_copy;
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Notify we couldn't copy
|
||||
if (!written) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_("not enough free links for copy"));
|
||||
}
|
||||
}
|
||||
|
||||
void Card::updateLink(String old_uid, String new_uid) {
|
||||
THIS_LINKED_PAIRS(this_linked_pairs);
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
if (this_linked_uid == old_uid) {
|
||||
this_linked_uid = new_uid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
vector<CardP> Card::getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card) {
|
||||
return getLinkedRelationCards(set.cards, linked_relation, erase_if_no_card);
|
||||
}
|
||||
|
||||
vector<pair<CardP, String>> Card::getLinkedCards(const vector<CardP>& cards) {
|
||||
@@ -283,7 +188,7 @@ vector<pair<CardP, String>> Card::getLinkedCards(const Set& set) {
|
||||
return getLinkedCards(set.cards);
|
||||
}
|
||||
|
||||
CardP Card::getLinkedOtherFace(const vector<CardP>& cards) {
|
||||
CardP Card::getLinkedOtherFaceCard(const vector<CardP>& cards) {
|
||||
unordered_set<String> faces;
|
||||
if (linked_relation_1 == _("Front Face") || linked_relation_1 == _("Back Face")) faces.emplace(linked_card_1);
|
||||
if (linked_relation_2 == _("Front Face") || linked_relation_2 == _("Back Face")) faces.emplace(linked_card_2);
|
||||
@@ -294,39 +199,57 @@ CardP Card::getLinkedOtherFace(const vector<CardP>& cards) {
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
CardP Card::getLinkedOtherFace(const Set& set) {
|
||||
return getLinkedOtherFace(set.cards);
|
||||
CardP Card::getLinkedOtherFaceCard(const Set& set) {
|
||||
return getLinkedOtherFaceCard(set.cards);
|
||||
}
|
||||
|
||||
vector<CardP> Card::getLinkedCardsFromLink(const vector<CardP>& cards, const String& link, bool erase_if_no_card) {
|
||||
vector<CardP> other_cards;
|
||||
THIS_LINKED_PAIRS(this_linked_pairs);
|
||||
FOR_EACH(this_linked_pair, this_linked_pairs) {
|
||||
String& this_linked_uid = this_linked_pair.first.get();
|
||||
String& this_linked_relation = this_linked_pair.second.get();
|
||||
if (this_linked_relation == link) {
|
||||
CardP other_card = getCardFromUid(cards, this_linked_uid);
|
||||
if (other_card) other_cards.push_back(other_card);
|
||||
else if (erase_if_no_card) {
|
||||
this_linked_relation = _("");
|
||||
this_linked_uid = _("");
|
||||
}
|
||||
}
|
||||
void Card::addLink(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation) {
|
||||
unordered_set<String> all_existing_uids;
|
||||
FOR_EACH(card, set.cards) {
|
||||
all_existing_uids.insert(card->uid);
|
||||
}
|
||||
|
||||
int index = findFreeLink(linked_card->uid, all_existing_uids);
|
||||
if (index < 0) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", identification()));
|
||||
return;
|
||||
}
|
||||
getLinkedUID(index) = linked_card->uid;
|
||||
getLinkedRelation(index) = selected_relation;
|
||||
|
||||
index = linked_card->findFreeLink(uid, all_existing_uids);
|
||||
if (index < 0) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", linked_card->identification()));
|
||||
}
|
||||
else {
|
||||
linked_card->getLinkedUID(index) = uid;
|
||||
linked_card->getLinkedRelation(index) = linked_relation;
|
||||
}
|
||||
}
|
||||
|
||||
void Card::removeLink(const CardP& linked_card)
|
||||
{
|
||||
int index = findUIDLink(linked_card->uid);
|
||||
if (index >= 0) {
|
||||
getLinkedUID(index) = _("");
|
||||
getLinkedRelation(index) = _("");
|
||||
}
|
||||
|
||||
index = linked_card->findUIDLink(uid);
|
||||
if (index >= 0) {
|
||||
linked_card->getLinkedUID(index) = _("");
|
||||
linked_card->getLinkedRelation(index) = _("");
|
||||
}
|
||||
return other_cards;
|
||||
}
|
||||
vector<CardP> Card::getLinkedCardsFromLink(const Set& set, const String& link, bool erase_if_no_card) {
|
||||
return getLinkedCardsFromLink(set.cards, link, erase_if_no_card);
|
||||
}
|
||||
|
||||
CardP Card::getCardFromUid(const vector<CardP>& cards, const String& uid) {
|
||||
CardP Card::getUIDCard(const vector<CardP>& cards, const String& uid) {
|
||||
FOR_EACH(card, cards) {
|
||||
if (card->uid == uid) return card;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
CardP Card::getCardFromUid(const Set& set, const String& uid) {
|
||||
return getCardFromUid(set.cards, uid);
|
||||
CardP Card::getUIDCard(const Set& set, const String& uid) {
|
||||
return getUIDCard(set.cards, uid);
|
||||
}
|
||||
|
||||
IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
|
||||
|
||||
+42
-23
@@ -13,6 +13,7 @@
|
||||
#include <util/error.hpp>
|
||||
#include <data/filter.hpp>
|
||||
#include <data/field.hpp> // for Card::value
|
||||
#include <unordered_set>
|
||||
|
||||
class Game;
|
||||
class Dependency;
|
||||
@@ -23,8 +24,7 @@ DECLARE_POINTER_TYPE(Field);
|
||||
DECLARE_POINTER_TYPE(Value);
|
||||
DECLARE_POINTER_TYPE(StyleSheet);
|
||||
|
||||
#define THIS_LINKED_PAIRS(var) vector<pair<reference_wrapper<String>, reference_wrapper<String>>> var { make_pair(ref(linked_card_1), ref(linked_relation_1)), make_pair(ref(linked_card_2), ref(linked_relation_2)), make_pair(ref(linked_card_3), ref(linked_relation_3)), make_pair(ref(linked_card_4), ref(linked_relation_4)) }
|
||||
#define OTHER_LINKED_PAIRS(var, other_card) vector<pair<reference_wrapper<String>, reference_wrapper<String>>> var { make_pair(ref(other_card->linked_card_1), ref(other_card->linked_relation_1)), make_pair(ref(other_card->linked_card_2), ref(other_card->linked_relation_2)), make_pair(ref(other_card->linked_card_3), ref(other_card->linked_relation_3)), make_pair(ref(other_card->linked_card_4), ref(other_card->linked_relation_4)) }
|
||||
#define LINK_PAIRS(var, card) vector<pair<reference_wrapper<String>, reference_wrapper<String>>> var { make_pair(ref(card->linked_card_1), ref(card->linked_relation_1)), make_pair(ref(card->linked_card_2), ref(card->linked_relation_2)), make_pair(ref(card->linked_card_3), ref(card->linked_relation_3)), make_pair(ref(card->linked_card_4), ref(card->linked_relation_4)) }
|
||||
|
||||
// ----------------------------------------------------------------------------- : Card
|
||||
|
||||
@@ -80,27 +80,6 @@ public:
|
||||
/// Does any field contains the given query string?
|
||||
bool contains(QuickFilterPart const& query) const;
|
||||
|
||||
/// Link or unlink other cards to this card
|
||||
void link(const Set& set, const vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation);
|
||||
void link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation);
|
||||
void unlink(const vector<CardP>& linked_cards);
|
||||
pair<String, String> unlink(CardP& unlinked_card); // Returns the relations that were deleted, so we can undo
|
||||
|
||||
void copyLink(const Set& set, String old_uid, String new_uid);
|
||||
void updateLink(String old_uid, String new_uid);
|
||||
|
||||
vector<pair<CardP, String>> getLinkedCards(const vector<CardP>& cards);
|
||||
vector<pair<CardP, String>> getLinkedCards(const Set& set);
|
||||
|
||||
vector<CardP> getLinkedCardsFromLink(const vector<CardP>& cards, const String& link, bool erase_if_no_card);
|
||||
vector<CardP> getLinkedCardsFromLink(const Set& set, const String& link, bool erase_if_no_card);
|
||||
|
||||
CardP getLinkedOtherFace(const vector<CardP>& cards);
|
||||
CardP getLinkedOtherFace(const Set& set);
|
||||
|
||||
static CardP getCardFromUid(const vector<CardP>& cards, const String& uid);
|
||||
static CardP getCardFromUid(const Set& set, const String& uid);
|
||||
|
||||
/// 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) {
|
||||
@@ -122,6 +101,46 @@ public:
|
||||
}
|
||||
throw InternalError(_("Expected a card field with name '")+name+_("'"));
|
||||
}
|
||||
|
||||
/// Find the index of a free link slot to write to. Returns -1 if not found.
|
||||
int findFreeLink (const String& linked_uid, const unordered_set<String>& all_existing_uids);
|
||||
vector<int> findFreeLinks(vector<String>& linked_uids, const unordered_set<String>& all_existing_uids);
|
||||
|
||||
/// Find the index of a link slot that references the linked_uid. Returns -1 if not found.
|
||||
int findUIDLink(const String& linked_uid);
|
||||
/// Find all indexes of link slots that references the linked_relation.
|
||||
vector<int> findRelationLinks(const String& linked_relation);
|
||||
|
||||
/// Get a reference to the linked uid slot.
|
||||
String& getLinkedUID (int index);
|
||||
/// Get a reference to the linked relation slot.
|
||||
String& getLinkedRelation(int index);
|
||||
|
||||
/// Make all links that point to old_uid point to new_uid instead.
|
||||
void updateLinkedUID(const String& old_uid, const String& new_uid);
|
||||
/// Make all links that have old_relation have new_relation instead. Not needed apparently.
|
||||
//void updateLinkedRelation(const String& old_relation, const String& new_relation);
|
||||
|
||||
/// Get the card with the given uid.
|
||||
static CardP getUIDCard(const vector<CardP>& cards, const String& uid);
|
||||
static CardP getUIDCard(const Set& set, const String& uid);
|
||||
/// Get all the cards linked to this card with the given relation.
|
||||
vector<CardP> getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card = true);
|
||||
vector<CardP> getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card = true);
|
||||
|
||||
/// Get all the cards linked to this card.
|
||||
vector<pair<CardP, String>> getLinkedCards(const vector<CardP>& cards);
|
||||
vector<pair<CardP, String>> getLinkedCards(const Set& set);
|
||||
|
||||
/// Get the back face or front face of this card.
|
||||
CardP getLinkedOtherFaceCard(const vector<CardP>& cards);
|
||||
CardP getLinkedOtherFaceCard(const Set& set);
|
||||
|
||||
/// Link a card to this card.
|
||||
void addLink(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation);
|
||||
|
||||
/// Unlink a card from this card.
|
||||
void removeLink(const CardP& linked_card);
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
+19
-17
@@ -67,6 +67,19 @@ IMPLEMENT_REFLECTION(Game) {
|
||||
REFLECT_NO_SCRIPT(auto_replaces);
|
||||
}
|
||||
|
||||
void Game::add_alt_name_or_warn(const String& alt_name, const String& field_name)
|
||||
{
|
||||
String unified_name = unified_form(alt_name);
|
||||
if (card_fields_alt_names.count(unified_name)) {
|
||||
if (card_fields_alt_names[unified_name] != field_name) {
|
||||
queue_message(MESSAGE_WARNING, _("Duplicate alt card field name ' ") + unified_name + _(" ' is both an alt for: ' ") + field_name + _(" ' and ' ") + card_fields_alt_names[unified_name] + _(" '."));
|
||||
}
|
||||
}
|
||||
else {
|
||||
card_fields_alt_names.emplace(unified_name, field_name);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::validate(Version v) {
|
||||
// check that we have at least one card field
|
||||
if (card_fields.size() < 1) {
|
||||
@@ -104,24 +117,13 @@ void Game::validate(Version v) {
|
||||
}
|
||||
// alternate card field names map
|
||||
for (auto it = card_fields.begin(); it != card_fields.end(); ++it) {
|
||||
FieldP field = *it;
|
||||
String unified_name = unified_form(field->name);
|
||||
if (card_fields_alt_names.count(unified_name)) {
|
||||
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
|
||||
}
|
||||
else {
|
||||
card_fields_alt_names.emplace(unified_name, field->name);
|
||||
}
|
||||
//String column_name = field->card_list_name.get();
|
||||
//card_fields_alt_names.emplace(unified_form(column_name), field->name);
|
||||
FieldP field = *it;
|
||||
String& field_name = field->name;
|
||||
add_alt_name_or_warn(field_name, field_name);
|
||||
add_alt_name_or_warn(field->card_list_name.default_, field_name);
|
||||
add_alt_name_or_warn(field->card_list_name.get(), field_name);
|
||||
for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) {
|
||||
unified_name = unified_form(*it2);
|
||||
if (card_fields_alt_names.count(unified_name)) {
|
||||
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_name);
|
||||
}
|
||||
else {
|
||||
card_fields_alt_names.emplace(unified_name, field->name);
|
||||
}
|
||||
add_alt_name_or_warn(*it2, field_name);
|
||||
}
|
||||
}
|
||||
// front face/back face card link
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
bool isMagic() const;
|
||||
|
||||
protected:
|
||||
void add_alt_name_or_warn(const String& alt_name, const String& field_name);
|
||||
void validate(Version) override;
|
||||
|
||||
DECLARE_REFLECTION_OVERRIDE();
|
||||
|
||||
@@ -204,7 +204,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
return;
|
||||
}
|
||||
// get the new script values
|
||||
vector<shared_ptr<Action>> actions;
|
||||
vector<ActionP> actions;
|
||||
String field_name = field_type->GetString(field_type->GetSelection());
|
||||
// stylesheet, notes or id change
|
||||
if (field_name == _("stylesheet") || field_name == _("notes") || field_name == _("id")) {
|
||||
@@ -226,15 +226,15 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
if (field_name == _("stylesheet")) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, new_values[i]);
|
||||
actions.push_back(make_shared<ChangeCardStyleAction>(cards[i], stylesheet));
|
||||
actions.push_back(make_intrusive<ChangeCardStyleAction>(cards[i], stylesheet));
|
||||
}
|
||||
} else if (field_name == _("notes")) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
actions.push_back(make_shared<ChangeCardNotesAction>(cards[i], new_values[i]));
|
||||
actions.push_back(make_intrusive<ChangeCardNotesAction>(cards[i], new_values[i]));
|
||||
}
|
||||
} else if (field_name == _("id")) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
actions.push_back(make_shared<ChangeCardUIDAction>(*set, cards[i], new_values[i]));
|
||||
actions.push_back(make_intrusive<ChangeCardUIDAction>(*set, cards[i], new_values[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
}
|
||||
TextValue* value = dynamic_cast<TextValue*>(values[i]);
|
||||
TextValue::ValueType new_value = new_values[i]->toString();
|
||||
shared_ptr<SimpleValueAction<TextValue, false>> action = make_shared<SimpleValueAction<TextValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<TextValue, false>> action = make_intrusive<SimpleValueAction<TextValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
@@ -280,7 +280,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
}
|
||||
MultipleChoiceValue* value = dynamic_cast<MultipleChoiceValue*>(values[i]);
|
||||
MultipleChoiceValue::ValueType new_value = { new_values[i]->toString(), _("") };
|
||||
shared_ptr<SimpleValueAction<MultipleChoiceValue, false>> action = make_shared<SimpleValueAction<MultipleChoiceValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<MultipleChoiceValue, false>> action = make_intrusive<SimpleValueAction<MultipleChoiceValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
@@ -295,7 +295,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
}
|
||||
ChoiceValue* value = dynamic_cast<ChoiceValue*>(values[i]);
|
||||
ChoiceValue::ValueType new_value = new_values[i]->toString();
|
||||
shared_ptr<SimpleValueAction<ChoiceValue, false>> action = make_shared<SimpleValueAction<ChoiceValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<ChoiceValue, false>> action = make_intrusive<SimpleValueAction<ChoiceValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
@@ -310,7 +310,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
}
|
||||
PackageChoiceValue* value = dynamic_cast<PackageChoiceValue*>(values[i]);
|
||||
PackageChoiceValue::ValueType new_value = new_values[i]->toString();
|
||||
shared_ptr<SimpleValueAction<PackageChoiceValue, false>> action = make_shared<SimpleValueAction<PackageChoiceValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<PackageChoiceValue, false>> action = make_intrusive<SimpleValueAction<PackageChoiceValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
@@ -321,7 +321,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
try {
|
||||
ColorValue::ValueType new_value = new_values[i]->toColor();
|
||||
ColorValue* value = dynamic_cast<ColorValue*>(values[i]);
|
||||
shared_ptr<SimpleValueAction<ColorValue, false>> action = make_shared<SimpleValueAction<ColorValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<ColorValue, false>> action = make_intrusive<SimpleValueAction<ColorValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
@@ -338,7 +338,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
if (ExternalImage* img = dynamic_cast<ExternalImage*>(new_values[i].get())) {
|
||||
ImageValue* value = dynamic_cast<ImageValue*>(values[i]);
|
||||
ImageValue::ValueType new_value = LocalFileName::fromReadString(img->toString(), "");
|
||||
shared_ptr<SimpleValueAction<ImageValue, false>> action = make_shared<SimpleValueAction<ImageValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<ImageValue, false>> action = make_intrusive<SimpleValueAction<ImageValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
@@ -355,7 +355,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
|
||||
if (ExternalImage* img = dynamic_cast<ExternalImage*>(new_values[i].get())) {
|
||||
SymbolValue* value = dynamic_cast<SymbolValue*>(values[i]);
|
||||
SymbolValue::ValueType new_value = LocalFileName::fromReadString(img->toString(), "");
|
||||
shared_ptr<SimpleValueAction<SymbolValue, false>> action = make_shared<SimpleValueAction<SymbolValue, false>>(value, new_value);
|
||||
intrusive_ptr<SimpleValueAction<SymbolValue, false>> action = make_intrusive<SimpleValueAction<SymbolValue, false>>(value, new_value);
|
||||
action->setCard(cards[i]);
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@ protected:
|
||||
wxStaticText* modification_description, *modification_errors, *predicate_description, *predicate_errors;
|
||||
wxTextCtrl* modification, *predicate;
|
||||
bool modification_parsed, predicate_parsed;
|
||||
ScriptP modification_script, predicate_script;
|
||||
wxButton* ok_button;
|
||||
SetP set;
|
||||
Window* parent;
|
||||
ScriptP modification_script, predicate_script;
|
||||
|
||||
void onSelectionChange(wxCommandEvent&);
|
||||
void changeSelection();
|
||||
|
||||
@@ -9,17 +9,19 @@
|
||||
#include <util/prec.hpp>
|
||||
#include <data/game.hpp>
|
||||
#include <data/card_link.hpp>
|
||||
#include <data/action/value.hpp>
|
||||
#include <gui/card_link_window.hpp>
|
||||
#include <gui/control/select_card_list.hpp>
|
||||
#include <util/window_id.hpp>
|
||||
#include <data/action/set.hpp>
|
||||
#include <wx/statline.h>
|
||||
#include <unordered_set>
|
||||
|
||||
// ----------------------------------------------------------------------------- : ExportCardSelectionChoice
|
||||
|
||||
CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& selected_card, bool sizer)
|
||||
: wxDialog(parent, wxID_ANY, _TITLE_("link cards"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, set(set), selected_card(selected_card)
|
||||
: wxDialog(parent, wxID_ANY, _TITLE_("link cards"), wxPoint(400,-1), wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, set(set), parent(parent), selected_card(selected_card)
|
||||
{
|
||||
// init controls
|
||||
selected_relation = new wxTextCtrl(this, wxID_ANY, _(""));
|
||||
@@ -39,7 +41,7 @@ CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& sel
|
||||
// init sizers
|
||||
if (sizer) {
|
||||
wxSizer* s = new wxBoxSizer(wxVERTICAL);
|
||||
s->Add(new wxStaticText(this, -1, _LABEL_("linked cards relation")), 0, wxALL, 8);
|
||||
s->Add(new wxStaticText(this, -1, _LABEL_1_("linked cards relation", selected_card->identification())), 0, wxALL, 8);
|
||||
s->Add(relation_type, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
|
||||
s->Add(new wxStaticText(this, -1, _(" ") + _LABEL_("selected card")), 0, wxALL, 4);
|
||||
s->Add(selected_relation, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
|
||||
@@ -95,19 +97,73 @@ void CardLinkWindow::onRelationTypeChange(wxCommandEvent&) {
|
||||
}
|
||||
|
||||
void CardLinkWindow::onOk(wxCommandEvent&) {
|
||||
wxBusyCursor wait;
|
||||
// get the context
|
||||
CardListBase* card_list_window = dynamic_cast<CardListBase*>(parent);
|
||||
if (!card_list_window) {
|
||||
queue_message(MESSAGE_ERROR, _("Bulk modification must be called from a card list window!"));
|
||||
EndModal(wxID_ABORT);
|
||||
return;
|
||||
}
|
||||
// Perform the linking
|
||||
// The selected_card is the one selected on the main cards tab
|
||||
// The linked_cards are the ones selected in this dialogue window
|
||||
vector<CardP> linked_cards;
|
||||
getSelection(linked_cards);
|
||||
// Check that we are not linking to self
|
||||
if (std::find(linked_cards.begin(), linked_cards.end(), selected_card) != linked_cards.end()) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_("cant link to self"));
|
||||
linked_cards.erase(std::remove(linked_cards.begin(), linked_cards.end(), selected_card), linked_cards.end());
|
||||
}
|
||||
vector<String> linked_uids;
|
||||
for (int i = 0; i < linked_cards.size(); ++i) {
|
||||
linked_uids.push_back(linked_cards[i]->uid);
|
||||
}
|
||||
// Find free links
|
||||
unordered_set<String> all_existing_uids;
|
||||
FOR_EACH(card, set->cards) {
|
||||
all_existing_uids.insert(card->uid);
|
||||
}
|
||||
vector<int> free_link_indexes = selected_card->findFreeLinks(linked_uids, all_existing_uids);
|
||||
int free_link_count = 0;
|
||||
for (int i = 0; i < free_link_indexes.size(); ++i) {
|
||||
if (free_link_indexes[i] >= 0) free_link_count++;
|
||||
}
|
||||
if (free_link_count < linked_cards.size()) {
|
||||
wxMessageDialog dial = wxMessageDialog(this, _ERROR_1_("missing free links", wxString::Format(wxT("%i"), free_link_count)));
|
||||
dial.ShowModal();
|
||||
return;
|
||||
}
|
||||
// Get the relations
|
||||
String selected_relation_string;
|
||||
String linked_relation_string;
|
||||
int index = relation_type->GetSelection();
|
||||
if (index >= set->game->card_links.size()) { // Custom type
|
||||
set->actions.addAction(make_unique<LinkCardsAction>(*set, selected_card, linked_cards, selected_relation->GetValue(), linked_relation->GetValue()));
|
||||
selected_relation_string = selected_relation->GetValue();
|
||||
linked_relation_string = linked_relation->GetValue();
|
||||
}
|
||||
else {
|
||||
CardLinkP link = set->game->card_links[index];
|
||||
set->actions.addAction(make_unique<LinkCardsAction>(*set, selected_card, linked_cards, link->selected.default_, link->linked.default_));
|
||||
selected_relation_string = link->selected.default_;
|
||||
linked_relation_string = link->linked.default_;
|
||||
}
|
||||
// Make the actions
|
||||
vector<ActionP> actions;
|
||||
for (int i = 0; i < free_link_indexes.size(); ++i) {
|
||||
if (free_link_indexes[i] >= 0) {
|
||||
actions.push_back(make_intrusive<OneWayLinkCardsAction>(*set, selected_card, linked_uids[i], selected_relation_string, free_link_indexes[i]));
|
||||
}
|
||||
}
|
||||
// Find reciprocal free slots and make actions
|
||||
String& selected_uid = selected_card->uid;
|
||||
for (int i = 0; i < linked_cards.size(); ++i) {
|
||||
int free_link_index = linked_cards[i]->findFreeLink(selected_uid, all_existing_uids);
|
||||
if (free_link_index >= 0) {
|
||||
actions.push_back(make_intrusive<OneWayLinkCardsAction>(*set, linked_cards[i], selected_uid, linked_relation_string, free_link_index));
|
||||
}
|
||||
}
|
||||
// Add action to set
|
||||
set->actions.addAction(make_unique<BulkAction>(actions, set, card_list_window), false);
|
||||
// Done
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ protected:
|
||||
SetP set;
|
||||
CardP selected_card;
|
||||
wxButton* sel_none;
|
||||
Window* parent;
|
||||
|
||||
void onRelationTypeChange(wxCommandEvent&);
|
||||
|
||||
|
||||
@@ -450,8 +450,12 @@ bool CardListBase::doLink() {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool CardListBase::doUnlink(CardP unlinked_card) {
|
||||
set->actions.addAction(make_unique<UnlinkCardsAction>(*set, getCard(), unlinked_card));
|
||||
bool CardListBase::doUnlink(CardP linked_card) {
|
||||
CardP selected_card = getCard();
|
||||
vector<ActionP> actions;
|
||||
actions.emplace_back(make_intrusive<OneWayLinkCardsAction>(*set, selected_card, _(""), _(""), selected_card->findUIDLink(linked_card->uid)));
|
||||
actions.emplace_back(make_intrusive<OneWayLinkCardsAction>(*set, linked_card, _(""), _(""), linked_card->findUIDLink(selected_card->uid)));
|
||||
set->actions.addAction(make_unique<BulkAction>(actions, set, this), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
|
||||
bool canLink() const;
|
||||
bool doLink();
|
||||
bool doUnlink(CardP unlinked_card);
|
||||
bool doUnlink(CardP linked_card);
|
||||
|
||||
// --------------------------------------------------- : Set actions
|
||||
|
||||
|
||||
@@ -36,11 +36,11 @@ void PrintJob::measure_cards() {
|
||||
FOR_EACH(card, cards) {
|
||||
if (already_measured_cards.find(card) != already_measured_cards.end()) continue;
|
||||
already_measured_cards.emplace(card);
|
||||
card_layouts.push_back(measure_card(card));
|
||||
CardP other_face = card->getLinkedOtherFace(cards);
|
||||
card_layouts.emplace_back(measure_card(card));
|
||||
CardP other_face = card->getLinkedOtherFaceCard(cards);
|
||||
if (other_face && already_measured_cards.find(other_face) == already_measured_cards.end()) {
|
||||
already_measured_cards.emplace(other_face);
|
||||
card_layouts.push_back(measure_card(other_face));
|
||||
card_layouts.emplace_back(measure_card(other_face));
|
||||
int index = card_layouts.size()-1;
|
||||
card_layouts[index].other_face = index-1;
|
||||
card_layouts[index-1].other_face = index;
|
||||
|
||||
@@ -776,7 +776,7 @@ SCRIPT_FUNCTION(get_card_export_settings) {
|
||||
SCRIPT_FUNCTION(get_card_from_uid) {
|
||||
SCRIPT_PARAM_C(Set*, set);
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
SCRIPT_RETURN(Card::getCardFromUid(*set, input));
|
||||
SCRIPT_RETURN(Card::getUIDCard(*set, input));
|
||||
}
|
||||
|
||||
SCRIPT_FUNCTION(get_cards_from_link) {
|
||||
@@ -787,7 +787,7 @@ SCRIPT_FUNCTION(get_cards_from_link) {
|
||||
input_card = ic->getValue();
|
||||
}
|
||||
else if (input->type() == SCRIPT_STRING) {
|
||||
input_card = Card::getCardFromUid(*set, input->toString());
|
||||
input_card = Card::getUIDCard(*set, input->toString());
|
||||
}
|
||||
if (!input_card) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
|
||||
@@ -795,14 +795,14 @@ SCRIPT_FUNCTION(get_cards_from_link) {
|
||||
}
|
||||
SCRIPT_PARAM(String, linked_relation);
|
||||
ScriptCustomCollectionP ret(new ScriptCustomCollection());
|
||||
vector<CardP> other_cards = input_card->getLinkedCardsFromLink(*set, linked_relation, true);
|
||||
vector<CardP> other_cards = input_card->getLinkedRelationCards(*set, linked_relation);
|
||||
if (other_cards.size() > 0) {
|
||||
FOR_EACH(other_card, other_cards) {
|
||||
ret->value.push_back(to_script(other_card));
|
||||
}
|
||||
}
|
||||
else if (set->game->card_links_alt_names.find(linked_relation) != set->game->card_links_alt_names.end()) {
|
||||
other_cards = input_card->getLinkedCardsFromLink(*set, set->game->card_links_alt_names[linked_relation], true);
|
||||
other_cards = input_card->getLinkedRelationCards(*set, set->game->card_links_alt_names[linked_relation]);
|
||||
FOR_EACH(other_card, other_cards) {
|
||||
ret->value.push_back(to_script(other_card));
|
||||
}
|
||||
@@ -818,13 +818,13 @@ SCRIPT_FUNCTION(get_front_face) {
|
||||
input_card = ic->getValue();
|
||||
}
|
||||
else if (input->type() == SCRIPT_STRING) {
|
||||
input_card = Card::getCardFromUid(*set, input->toString());
|
||||
input_card = Card::getUIDCard(*set, input->toString());
|
||||
}
|
||||
if (!input_card) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
|
||||
return script_nil;
|
||||
}
|
||||
vector<CardP> other_cards = input_card->getLinkedCardsFromLink(*set, "Front Face", true);
|
||||
vector<CardP> other_cards = input_card->getLinkedRelationCards(*set, "Front Face");
|
||||
if (other_cards.size() == 0) return script_nil;
|
||||
if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple front faces", input_card->identification()));
|
||||
SCRIPT_RETURN(other_cards[0]);
|
||||
@@ -838,13 +838,13 @@ SCRIPT_FUNCTION(get_back_face) {
|
||||
input_card = ic->getValue();
|
||||
}
|
||||
else if (input->type() == SCRIPT_STRING) {
|
||||
input_card = Card::getCardFromUid(*set, input->toString());
|
||||
input_card = Card::getUIDCard(*set, input->toString());
|
||||
}
|
||||
if (!input_card) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
|
||||
return script_nil;
|
||||
}
|
||||
vector<CardP> other_cards = input_card->getLinkedCardsFromLink(*set, "Back Face", true);
|
||||
vector<CardP> other_cards = input_card->getLinkedRelationCards(*set, "Back Face");
|
||||
if (other_cards.size() == 0) return script_nil;
|
||||
if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple back faces", input_card->identification()));
|
||||
SCRIPT_RETURN(other_cards[0]);
|
||||
@@ -858,7 +858,7 @@ SCRIPT_FUNCTION(add_link) {
|
||||
input_card = ic->getValue();
|
||||
}
|
||||
else if (input->type() == SCRIPT_STRING) {
|
||||
input_card = Card::getCardFromUid(*set, input->toString());
|
||||
input_card = Card::getUIDCard(*set, input->toString());
|
||||
}
|
||||
if (!input_card) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
|
||||
@@ -870,7 +870,7 @@ SCRIPT_FUNCTION(add_link) {
|
||||
other_card = c->getValue();
|
||||
}
|
||||
else if (linked_card->type() == SCRIPT_STRING) {
|
||||
other_card = Card::getCardFromUid(*set, linked_card->toString());
|
||||
other_card = Card::getUIDCard(*set, linked_card->toString());
|
||||
}
|
||||
if (!other_card) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_("could not find linked"));
|
||||
@@ -878,7 +878,7 @@ SCRIPT_FUNCTION(add_link) {
|
||||
}
|
||||
SCRIPT_PARAM(String, selected_relation);
|
||||
SCRIPT_PARAM(String, linked_relation);
|
||||
input_card->link(*set, other_card, selected_relation, linked_relation);
|
||||
input_card->addLink(*set, other_card, selected_relation, linked_relation);
|
||||
SCRIPT_RETURN(other_card);
|
||||
}
|
||||
|
||||
@@ -891,7 +891,7 @@ SCRIPT_FUNCTION(remove_links) {
|
||||
input_card = ic->getValue();
|
||||
}
|
||||
else if (input->type() == SCRIPT_STRING) {
|
||||
input_card = Card::getCardFromUid(*set, input->toString());
|
||||
input_card = Card::getUIDCard(*set, input->toString());
|
||||
}
|
||||
if (!input_card) {
|
||||
queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
|
||||
@@ -905,7 +905,7 @@ SCRIPT_FUNCTION(remove_links) {
|
||||
other_card = c->getValue();
|
||||
}
|
||||
else if (linked_card->type() == SCRIPT_STRING) {
|
||||
other_card = Card::getCardFromUid(*set, linked_card->toString());
|
||||
other_card = Card::getUIDCard(*set, linked_card->toString());
|
||||
}
|
||||
if (!other_card) {
|
||||
queue_message(MESSAGE_WARNING, _ERROR_("could not find linked"));
|
||||
@@ -915,12 +915,12 @@ SCRIPT_FUNCTION(remove_links) {
|
||||
SCRIPT_PARAM_DEFAULT(ScriptValueP, linked_relation, script_nil);
|
||||
if (linked_relation != script_nil) {
|
||||
if (linked_relation->type() == SCRIPT_STRING) {
|
||||
vector<CardP> other_other_cards = input_card->getLinkedCardsFromLink(*set, linked_relation->toString(), true);
|
||||
vector<CardP> other_other_cards = input_card->getLinkedRelationCards(*set, linked_relation->toString());
|
||||
other_cards.insert(other_cards.end(), other_other_cards.begin(), other_other_cards.end());
|
||||
}
|
||||
}
|
||||
input_card->unlink(other_cards);
|
||||
FOR_EACH(other_card, other_cards) {
|
||||
FOR_EACH(other_card, other_cards) {
|
||||
input_card->removeLink(other_card);
|
||||
ret->value.push_back(to_script(other_card));
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -145,8 +145,8 @@ SCRIPT_FUNCTION(add_card_to_set) {
|
||||
Set& _set = *s->getValue();
|
||||
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get());
|
||||
if (c) {
|
||||
CardP _card = c->getValue();
|
||||
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _card));
|
||||
vector<CardP> _cards { c->getValue() };
|
||||
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _cards));
|
||||
SCRIPT_RETURN(true);
|
||||
}
|
||||
if (input->type() == SCRIPT_COLLECTION) {
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
#include <util/string.hpp>
|
||||
#include <vector>
|
||||
|
||||
DECLARE_POINTER_TYPE(Action);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Action
|
||||
|
||||
/// Base class for actions that can be stored in an ActionStack.
|
||||
/** An action is something that can be done to modify an object.
|
||||
* It must store the necessary information to also undo the action.
|
||||
*/
|
||||
class Action {
|
||||
class Action : public IntrusivePtrVirtualBase, public IntrusiveFromThis<Action> {
|
||||
public:
|
||||
virtual ~Action() {};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user