linking refactor

This commit is contained in:
GenevensiS
2026-02-01 17:39:33 +01:00
parent 7fde6b2605
commit 11e506739a
18 changed files with 411 additions and 407 deletions
+89 -80
View File
@@ -22,66 +22,74 @@ AddCardAction::AddCardAction(Set& set)
, action(ADD, make_intrusive<Card>(*set.game), set.cards) , 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) AddCardAction::AddCardAction(AddingOrRemoving ar, Set& set, const vector<CardP>& cards)
: CardListAction(set) : CardListAction(set)
, action(ar, cards, set.cards) , 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 { String AddCardAction::getName(bool to_undo) const {
return action.getName(); return action.getName();
} }
void AddCardAction::perform(bool to_undo) { 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 // Add or remove cards
action.perform(set.cards, to_undo); 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 // ----------------------------------------------------------------------------- : Reorder cards
@@ -108,39 +116,40 @@ void ReorderCardsAction::perform(bool to_undo) {
// ----------------------------------------------------------------------------- : Link cards // ----------------------------------------------------------------------------- : Link cards
LinkCardsAction::LinkCardsAction(Set& set, const CardP& selected_card, vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation) OneWayLinkCardsAction::OneWayLinkCardsAction(Set& set, CardP& card, const String& uid, const String& relation, int index)
: CardListAction(set), selected_card(selected_card), linked_cards(linked_cards), selected_relation(selected_relation), linked_relation(linked_relation) : 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 { String OneWayLinkCardsAction::getName(bool to_undo) const {
return _("Link cards"); return _("Change link");
} }
void LinkCardsAction::perform(bool to_undo) { void OneWayLinkCardsAction::perform(bool to_undo) {
if (!to_undo) { swap(*linked_uid, uid);
selected_card->link(set, linked_cards, selected_relation, linked_relation); swap(*linked_relation, 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);
}
} }
// ----------------------------------------------------------------------------- : Change stylesheet // ----------------------------------------------------------------------------- : Change stylesheet
@@ -236,7 +245,7 @@ String ChangeCardUIDAction::getName(bool to_undo) const {
} }
void ChangeCardUIDAction::perform(bool to_undo) { void ChangeCardUIDAction::perform(bool to_undo) {
FOR_EACH(c, set.cards) { FOR_EACH(c, set.cards) {
c->updateLink(card->uid, uid); c->updateLinkedUID(card->uid, uid);
} }
swap(card->uid, uid); swap(card->uid, uid);
} }
+11 -24
View File
@@ -41,13 +41,13 @@ class AddCardAction : public CardListAction {
public: public:
/// Add a newly allocated card /// Add a newly allocated card
AddCardAction(Set& set); AddCardAction(Set& set);
AddCardAction(AddingOrRemoving, Set& set, const CardP& card);
AddCardAction(AddingOrRemoving, Set& set, const vector<CardP>& cards); AddCardAction(AddingOrRemoving, Set& set, const vector<CardP>& cards);
String getName(bool to_undo) const override; String getName(bool to_undo) const override;
void perform(bool to_undo) override; void perform(bool to_undo) override;
const GenericAddAction<CardP> action; const GenericAddAction<CardP> action;
vector<ActionP> card_link_actions;
}; };
// ----------------------------------------------------------------------------- : Reorder cards // ----------------------------------------------------------------------------- : Reorder cards
@@ -67,32 +67,19 @@ public:
// ----------------------------------------------------------------------------- : Link cards // ----------------------------------------------------------------------------- : Link cards
/// Add a link between two or more cards /// Add a link between two or more cards
class LinkCardsAction : public CardListAction { class OneWayLinkCardsAction : public CardListAction {
public: 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; String getName(bool to_undo) const override;
void perform(bool to_undo) override; void perform(bool to_undo) override;
//private: //private:
CardP selected_card; ///< The card currently selected in the cards tab CardP card; ///< The card we change the link of. We hold a reference so it doesn't get destroyed before this.
vector<CardP> linked_cards; ///< The cards that will be linked to the selected card String* linked_uid; ///< The uid slot we need to change
String selected_relation; ///< The nature of the relation of the selected card String* linked_relation; ///< The relation slot we need to change
String linked_relation; ///< The nature of the relation of the linked cards String uid; ///< The uid we have to change the link to
}; String relation; ///< The relation we have to change the link to
/// 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
}; };
// ----------------------------------------------------------------------------- : Change stylesheet // ----------------------------------------------------------------------------- : Change stylesheet
+3 -3
View File
@@ -212,12 +212,12 @@ void ScriptStyleEvent::perform(bool) {
// ----------------------------------------------------------------------------- : Bulk action // ----------------------------------------------------------------------------- : 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) : actions(actions), set(set), card_list_window(card_list_window)
{ {
if (actions.empty()) throw ScriptError(_("BulkAction created with no actions")); if (actions.empty()) throw ScriptError(_("BulkAction created with no actions"));
name_do = actions.front()->getName(false) + _(" ") + _ACTION_("bulk"); name_do = _ACTION_1_("bulk", actions.front()->getName(false));
name_undo = actions.front()->getName(true) + _(" ") + _ACTION_("bulk"); name_undo = _ACTION_1_("bulk", actions.front()->getName(true));
} }
BulkAction::~BulkAction() {} BulkAction::~BulkAction() {}
+2 -2
View File
@@ -206,7 +206,7 @@ public:
// An action that's just a list of other actions // An action that's just a list of other actions
class BulkAction : public Action { class BulkAction : public Action {
public: 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; ~BulkAction() override;
String getName(bool to_undo) const override; String getName(bool to_undo) const override;
@@ -216,7 +216,7 @@ public:
private: private:
String name_do; String name_do;
String name_undo; String name_undo;
vector<shared_ptr<Action>> actions; vector<ActionP> actions;
SetP set; SetP set;
CardListBase* card_list_window; CardListBase* card_list_window;
}; };
+139 -216
View File
@@ -63,205 +63,110 @@ bool Card::contains(QuickFilterPart const& query) const {
} }
if (query.match(_("notes"), notes)) return true; if (query.match(_("notes"), notes)) return true;
return false; return false;
} }
void Card::link(const Set& set, const vector<CardP>& linked_cards, const String& selected_relation, const String& linked_relation) vector<int> Card::findFreeLinks(vector<String>& linked_uids, const unordered_set<String>& all_existing_uids) {
{ vector<int> freeIndexes;
unlink(linked_cards); int count = min(4, (int)linked_uids.size());
LINK_PAIRS(linked_pairs, this);
unordered_set<String> all_existing_uids;
FOR_EACH(card, set.cards) { for (int i = 0; i < count; ++i) {
all_existing_uids.insert(card->uid); String& linked_uid = linked_uids[i];
} // Try to find an existing link
int free_link_count = 0; for (int j = 0; j < linked_pairs.size(); ++j) {
THIS_LINKED_PAIRS(this_linked_pairs); auto& linked_pair = linked_pairs[j];
FOR_EACH(this_linked_pair, this_linked_pairs) { if (linked_pair.first.get() == linked_uid &&
String& this_linked_uid = this_linked_pair.first.get(); std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end()
if ( ) {
this_linked_uid.empty() || // Not a reference freeIndexes.push_back(j);
all_existing_uids.find(this_linked_uid) == all_existing_uids.end() // Reference to nonexistent card goto continue_outer;
) free_link_count++; }
} }
if (free_link_count < linked_cards.size()) { // Try to find a free spot
queue_message(MESSAGE_WARNING, _ERROR_("not enough free links")); for (int j = 0; j < linked_pairs.size(); ++j) {
return; auto& linked_pair = linked_pairs[j];
} if (linked_pair.first.get().empty() &&
std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end()
vector<CardP> all_missed_cards; ) {
FOR_EACH(linked_card, linked_cards) { freeIndexes.push_back(j);
bool written = false; goto continue_outer;
// 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;
} }
} }
// Try to write to an erasable spot // Try to find an erasable spot
if (!written) { for (int j = 0; j < linked_pairs.size(); ++j) {
FOR_EACH(this_linked_pair, this_linked_pairs) { auto& linked_pair = linked_pairs[j];
String& this_linked_uid = this_linked_pair.first.get(); if (all_existing_uids.find(linked_pair.first.get()) == all_existing_uids.end() &&
String& this_linked_relation = this_linked_pair.second.get(); std::find(freeIndexes.begin(), freeIndexes.end(), j) != freeIndexes.end()
if (all_existing_uids.find(this_linked_uid) == all_existing_uids.end()) { ) {
this_linked_uid = linked_card->uid; freeIndexes.push_back(j);
this_linked_relation = linked_relation; goto continue_outer;
written = true;
break;
}
} }
} }
if (!written) { freeIndexes.push_back(-1);
// Should be impossible to end up here? 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); void Card::updateLinkedUID(const String& old_uid, const String& new_uid) {
written = false; int index = findUIDLink(old_uid);
// Try to write to a free spot if (index >= 0) getLinkedUID(index) = new_uid;
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::link(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation) vector<CardP> Card::getLinkedRelationCards(const vector<CardP>& cards, const String& linked_relation, bool erase_if_no_card) {
{ vector<CardP> other_cards;
vector<CardP> linked_cards { linked_card }; vector<int> indexes = findRelationLinks(linked_relation);
link(set, linked_cards, selected_relation, 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;
} }
vector<CardP> Card::getLinkedRelationCards(const Set& set, const String& linked_relation, bool erase_if_no_card) {
void Card::unlink(const vector<CardP>& unlinked_cards) return getLinkedRelationCards(set.cards, linked_relation, erase_if_no_card);
{
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<pair<CardP, String>> Card::getLinkedCards(const vector<CardP>& cards) { 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); return getLinkedCards(set.cards);
} }
CardP Card::getLinkedOtherFace(const vector<CardP>& cards) { CardP Card::getLinkedOtherFaceCard(const vector<CardP>& cards) {
unordered_set<String> faces; unordered_set<String> faces;
if (linked_relation_1 == _("Front Face") || linked_relation_1 == _("Back Face")) faces.emplace(linked_card_1); 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); 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; return nullptr;
} }
CardP Card::getLinkedOtherFace(const Set& set) { CardP Card::getLinkedOtherFaceCard(const Set& set) {
return getLinkedOtherFace(set.cards); return getLinkedOtherFaceCard(set.cards);
} }
vector<CardP> Card::getLinkedCardsFromLink(const vector<CardP>& cards, const String& link, bool erase_if_no_card) { void Card::addLink(const Set& set, CardP& linked_card, const String& selected_relation, const String& linked_relation) {
vector<CardP> other_cards; unordered_set<String> all_existing_uids;
THIS_LINKED_PAIRS(this_linked_pairs); FOR_EACH(card, set.cards) {
FOR_EACH(this_linked_pair, this_linked_pairs) { all_existing_uids.insert(card->uid);
String& this_linked_uid = this_linked_pair.first.get(); }
String& this_linked_relation = this_linked_pair.second.get();
if (this_linked_relation == link) { int index = findFreeLink(linked_card->uid, all_existing_uids);
CardP other_card = getCardFromUid(cards, this_linked_uid); if (index < 0) {
if (other_card) other_cards.push_back(other_card); queue_message(MESSAGE_ERROR, _ERROR_1_("not enough free links", identification()));
else if (erase_if_no_card) { return;
this_linked_relation = _(""); }
this_linked_uid = _(""); 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) { FOR_EACH(card, cards) {
if (card->uid == uid) return card; if (card->uid == uid) return card;
} }
return nullptr; return nullptr;
} }
CardP Card::getCardFromUid(const Set& set, const String& uid) { CardP Card::getUIDCard(const Set& set, const String& uid) {
return getCardFromUid(set.cards, uid); return getUIDCard(set.cards, uid);
} }
IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) { IndexMap<FieldP, ValueP>& Card::extraDataFor(const StyleSheet& stylesheet) {
+42 -23
View File
@@ -13,6 +13,7 @@
#include <util/error.hpp> #include <util/error.hpp>
#include <data/filter.hpp> #include <data/filter.hpp>
#include <data/field.hpp> // for Card::value #include <data/field.hpp> // for Card::value
#include <unordered_set>
class Game; class Game;
class Dependency; class Dependency;
@@ -23,8 +24,7 @@ DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(StyleSheet); 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 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)) }
#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)) }
// ----------------------------------------------------------------------------- : Card // ----------------------------------------------------------------------------- : Card
@@ -80,27 +80,6 @@ public:
/// Does any field contains the given query string? /// Does any field contains the given query string?
bool contains(QuickFilterPart const& query) const; 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 /// Find a value in the data by name and type
template <typename T> T& value(const String& name) { template <typename T> T& value(const String& name) {
for(IndexMap<FieldP, ValueP>::iterator it = data.begin() ; it != data.end() ; ++it) { 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+_("'")); 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(); DECLARE_REFLECTION();
}; };
+19 -17
View File
@@ -67,6 +67,19 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT_NO_SCRIPT(auto_replaces); 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) { void Game::validate(Version v) {
// check that we have at least one card field // check that we have at least one card field
if (card_fields.size() < 1) { if (card_fields.size() < 1) {
@@ -104,24 +117,13 @@ void Game::validate(Version v) {
} }
// alternate card field names map // alternate card field names map
for (auto it = card_fields.begin(); it != card_fields.end(); ++it) { for (auto it = card_fields.begin(); it != card_fields.end(); ++it) {
FieldP field = *it; FieldP field = *it;
String unified_name = unified_form(field->name); String& field_name = field->name;
if (card_fields_alt_names.count(unified_name)) { add_alt_name_or_warn(field_name, field_name);
queue_message(MESSAGE_WARNING, _("Duplicate alternate card field name: ") + unified_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);
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);
for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) { for (auto it2 = field->alt_names.begin(); it2 != field->alt_names.end(); ++it2) {
unified_name = unified_form(*it2); add_alt_name_or_warn(*it2, 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);
}
} }
} }
// front face/back face card link // front face/back face card link
+1
View File
@@ -78,6 +78,7 @@ public:
bool isMagic() const; bool isMagic() const;
protected: protected:
void add_alt_name_or_warn(const String& alt_name, const String& field_name);
void validate(Version) override; void validate(Version) override;
DECLARE_REFLECTION_OVERRIDE(); DECLARE_REFLECTION_OVERRIDE();
+11 -11
View File
@@ -204,7 +204,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
return; return;
} }
// get the new script values // get the new script values
vector<shared_ptr<Action>> actions; vector<ActionP> actions;
String field_name = field_type->GetString(field_type->GetSelection()); String field_name = field_type->GetString(field_type->GetSelection());
// stylesheet, notes or id change // stylesheet, notes or id change
if (field_name == _("stylesheet") || field_name == _("notes") || field_name == _("id")) { if (field_name == _("stylesheet") || field_name == _("notes") || field_name == _("id")) {
@@ -226,15 +226,15 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
if (field_name == _("stylesheet")) { if (field_name == _("stylesheet")) {
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
StyleSheetP stylesheet = StyleSheet::byGameAndName(*set->game, new_values[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")) { } else if (field_name == _("notes")) {
for (int i = 0; i < count; ++i) { 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")) { } else if (field_name == _("id")) {
for (int i = 0; i < count; ++i) { 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* value = dynamic_cast<TextValue*>(values[i]);
TextValue::ValueType new_value = new_values[i]->toString(); 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
@@ -280,7 +280,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
} }
MultipleChoiceValue* value = dynamic_cast<MultipleChoiceValue*>(values[i]); MultipleChoiceValue* value = dynamic_cast<MultipleChoiceValue*>(values[i]);
MultipleChoiceValue::ValueType new_value = { new_values[i]->toString(), _("") }; 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
@@ -295,7 +295,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
} }
ChoiceValue* value = dynamic_cast<ChoiceValue*>(values[i]); ChoiceValue* value = dynamic_cast<ChoiceValue*>(values[i]);
ChoiceValue::ValueType new_value = new_values[i]->toString(); 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
@@ -310,7 +310,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
} }
PackageChoiceValue* value = dynamic_cast<PackageChoiceValue*>(values[i]); PackageChoiceValue* value = dynamic_cast<PackageChoiceValue*>(values[i]);
PackageChoiceValue::ValueType new_value = new_values[i]->toString(); 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
@@ -321,7 +321,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
try { try {
ColorValue::ValueType new_value = new_values[i]->toColor(); ColorValue::ValueType new_value = new_values[i]->toColor();
ColorValue* value = dynamic_cast<ColorValue*>(values[i]); 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
@@ -338,7 +338,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
if (ExternalImage* img = dynamic_cast<ExternalImage*>(new_values[i].get())) { if (ExternalImage* img = dynamic_cast<ExternalImage*>(new_values[i].get())) {
ImageValue* value = dynamic_cast<ImageValue*>(values[i]); ImageValue* value = dynamic_cast<ImageValue*>(values[i]);
ImageValue::ValueType new_value = LocalFileName::fromReadString(img->toString(), ""); 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
@@ -355,7 +355,7 @@ void BulkModificationWindow::onOk(wxCommandEvent&) {
if (ExternalImage* img = dynamic_cast<ExternalImage*>(new_values[i].get())) { if (ExternalImage* img = dynamic_cast<ExternalImage*>(new_values[i].get())) {
SymbolValue* value = dynamic_cast<SymbolValue*>(values[i]); SymbolValue* value = dynamic_cast<SymbolValue*>(values[i]);
SymbolValue::ValueType new_value = LocalFileName::fromReadString(img->toString(), ""); 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]); action->setCard(cards[i]);
actions.push_back(action); actions.push_back(action);
} }
+1 -1
View File
@@ -27,10 +27,10 @@ protected:
wxStaticText* modification_description, *modification_errors, *predicate_description, *predicate_errors; wxStaticText* modification_description, *modification_errors, *predicate_description, *predicate_errors;
wxTextCtrl* modification, *predicate; wxTextCtrl* modification, *predicate;
bool modification_parsed, predicate_parsed; bool modification_parsed, predicate_parsed;
ScriptP modification_script, predicate_script;
wxButton* ok_button; wxButton* ok_button;
SetP set; SetP set;
Window* parent; Window* parent;
ScriptP modification_script, predicate_script;
void onSelectionChange(wxCommandEvent&); void onSelectionChange(wxCommandEvent&);
void changeSelection(); void changeSelection();
+61 -5
View File
@@ -9,17 +9,19 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <data/game.hpp> #include <data/game.hpp>
#include <data/card_link.hpp> #include <data/card_link.hpp>
#include <data/action/value.hpp>
#include <gui/card_link_window.hpp> #include <gui/card_link_window.hpp>
#include <gui/control/select_card_list.hpp> #include <gui/control/select_card_list.hpp>
#include <util/window_id.hpp> #include <util/window_id.hpp>
#include <data/action/set.hpp> #include <data/action/set.hpp>
#include <wx/statline.h> #include <wx/statline.h>
#include <unordered_set>
// ----------------------------------------------------------------------------- : ExportCardSelectionChoice // ----------------------------------------------------------------------------- : ExportCardSelectionChoice
CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& selected_card, bool sizer) 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) : wxDialog(parent, wxID_ANY, _TITLE_("link cards"), wxPoint(400,-1), wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, set(set), selected_card(selected_card) , set(set), parent(parent), selected_card(selected_card)
{ {
// init controls // init controls
selected_relation = new wxTextCtrl(this, wxID_ANY, _("")); selected_relation = new wxTextCtrl(this, wxID_ANY, _(""));
@@ -39,7 +41,7 @@ CardLinkWindow::CardLinkWindow(Window* parent, const SetP& set, const CardP& sel
// init sizers // init sizers
if (sizer) { if (sizer) {
wxSizer* s = new wxBoxSizer(wxVERTICAL); 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(relation_type, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(new wxStaticText(this, -1, _(" ") + _LABEL_("selected card")), 0, wxALL, 4); s->Add(new wxStaticText(this, -1, _(" ") + _LABEL_("selected card")), 0, wxALL, 4);
s->Add(selected_relation, 0, wxEXPAND | (wxALL & ~wxTOP), 8); s->Add(selected_relation, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
@@ -95,19 +97,73 @@ void CardLinkWindow::onRelationTypeChange(wxCommandEvent&) {
} }
void CardLinkWindow::onOk(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 // Perform the linking
// The selected_card is the one selected on the main cards tab // The selected_card is the one selected on the main cards tab
// The linked_cards are the ones selected in this dialogue window // The linked_cards are the ones selected in this dialogue window
vector<CardP> linked_cards; vector<CardP> linked_cards;
getSelection(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(); int index = relation_type->GetSelection();
if (index >= set->game->card_links.size()) { // Custom type 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 { else {
CardLinkP link = set->game->card_links[index]; 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 // Done
EndModal(wxID_OK); EndModal(wxID_OK);
} }
+1
View File
@@ -43,6 +43,7 @@ protected:
SetP set; SetP set;
CardP selected_card; CardP selected_card;
wxButton* sel_none; wxButton* sel_none;
Window* parent;
void onRelationTypeChange(wxCommandEvent&); void onRelationTypeChange(wxCommandEvent&);
+6 -2
View File
@@ -450,8 +450,12 @@ bool CardListBase::doLink() {
} }
return false; return false;
} }
bool CardListBase::doUnlink(CardP unlinked_card) { bool CardListBase::doUnlink(CardP linked_card) {
set->actions.addAction(make_unique<UnlinkCardsAction>(*set, getCard(), unlinked_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; return true;
} }
+1 -1
View File
@@ -105,7 +105,7 @@ public:
bool canLink() const; bool canLink() const;
bool doLink(); bool doLink();
bool doUnlink(CardP unlinked_card); bool doUnlink(CardP linked_card);
// --------------------------------------------------- : Set actions // --------------------------------------------------- : Set actions
+3 -3
View File
@@ -36,11 +36,11 @@ void PrintJob::measure_cards() {
FOR_EACH(card, cards) { FOR_EACH(card, cards) {
if (already_measured_cards.find(card) != already_measured_cards.end()) continue; if (already_measured_cards.find(card) != already_measured_cards.end()) continue;
already_measured_cards.emplace(card); already_measured_cards.emplace(card);
card_layouts.push_back(measure_card(card)); card_layouts.emplace_back(measure_card(card));
CardP other_face = card->getLinkedOtherFace(cards); CardP other_face = card->getLinkedOtherFaceCard(cards);
if (other_face && already_measured_cards.find(other_face) == already_measured_cards.end()) { if (other_face && already_measured_cards.find(other_face) == already_measured_cards.end()) {
already_measured_cards.emplace(other_face); 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; int index = card_layouts.size()-1;
card_layouts[index].other_face = index-1; card_layouts[index].other_face = index-1;
card_layouts[index-1].other_face = index; card_layouts[index-1].other_face = index;
+16 -16
View File
@@ -776,7 +776,7 @@ SCRIPT_FUNCTION(get_card_export_settings) {
SCRIPT_FUNCTION(get_card_from_uid) { SCRIPT_FUNCTION(get_card_from_uid) {
SCRIPT_PARAM_C(Set*, set); SCRIPT_PARAM_C(Set*, set);
SCRIPT_PARAM_C(String, input); SCRIPT_PARAM_C(String, input);
SCRIPT_RETURN(Card::getCardFromUid(*set, input)); SCRIPT_RETURN(Card::getUIDCard(*set, input));
} }
SCRIPT_FUNCTION(get_cards_from_link) { SCRIPT_FUNCTION(get_cards_from_link) {
@@ -787,7 +787,7 @@ SCRIPT_FUNCTION(get_cards_from_link) {
input_card = ic->getValue(); input_card = ic->getValue();
} }
else if (input->type() == SCRIPT_STRING) { else if (input->type() == SCRIPT_STRING) {
input_card = Card::getCardFromUid(*set, input->toString()); input_card = Card::getUIDCard(*set, input->toString());
} }
if (!input_card) { if (!input_card) {
queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
@@ -795,14 +795,14 @@ SCRIPT_FUNCTION(get_cards_from_link) {
} }
SCRIPT_PARAM(String, linked_relation); SCRIPT_PARAM(String, linked_relation);
ScriptCustomCollectionP ret(new ScriptCustomCollection()); 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) { if (other_cards.size() > 0) {
FOR_EACH(other_card, other_cards) { FOR_EACH(other_card, other_cards) {
ret->value.push_back(to_script(other_card)); 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()) { 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) { FOR_EACH(other_card, other_cards) {
ret->value.push_back(to_script(other_card)); ret->value.push_back(to_script(other_card));
} }
@@ -818,13 +818,13 @@ SCRIPT_FUNCTION(get_front_face) {
input_card = ic->getValue(); input_card = ic->getValue();
} }
else if (input->type() == SCRIPT_STRING) { else if (input->type() == SCRIPT_STRING) {
input_card = Card::getCardFromUid(*set, input->toString()); input_card = Card::getUIDCard(*set, input->toString());
} }
if (!input_card) { if (!input_card) {
queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
return script_nil; 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() == 0) return script_nil;
if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple front faces", input_card->identification())); if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple front faces", input_card->identification()));
SCRIPT_RETURN(other_cards[0]); SCRIPT_RETURN(other_cards[0]);
@@ -838,13 +838,13 @@ SCRIPT_FUNCTION(get_back_face) {
input_card = ic->getValue(); input_card = ic->getValue();
} }
else if (input->type() == SCRIPT_STRING) { else if (input->type() == SCRIPT_STRING) {
input_card = Card::getCardFromUid(*set, input->toString()); input_card = Card::getUIDCard(*set, input->toString());
} }
if (!input_card) { if (!input_card) {
queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
return script_nil; 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() == 0) return script_nil;
if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple back faces", input_card->identification())); if (other_cards.size() > 1) queue_message(MESSAGE_WARNING, _ERROR_1_("multiple back faces", input_card->identification()));
SCRIPT_RETURN(other_cards[0]); SCRIPT_RETURN(other_cards[0]);
@@ -858,7 +858,7 @@ SCRIPT_FUNCTION(add_link) {
input_card = ic->getValue(); input_card = ic->getValue();
} }
else if (input->type() == SCRIPT_STRING) { else if (input->type() == SCRIPT_STRING) {
input_card = Card::getCardFromUid(*set, input->toString()); input_card = Card::getUIDCard(*set, input->toString());
} }
if (!input_card) { if (!input_card) {
queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
@@ -870,7 +870,7 @@ SCRIPT_FUNCTION(add_link) {
other_card = c->getValue(); other_card = c->getValue();
} }
else if (linked_card->type() == SCRIPT_STRING) { 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) { if (!other_card) {
queue_message(MESSAGE_WARNING, _ERROR_("could not find linked")); 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, selected_relation);
SCRIPT_PARAM(String, linked_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); SCRIPT_RETURN(other_card);
} }
@@ -891,7 +891,7 @@ SCRIPT_FUNCTION(remove_links) {
input_card = ic->getValue(); input_card = ic->getValue();
} }
else if (input->type() == SCRIPT_STRING) { else if (input->type() == SCRIPT_STRING) {
input_card = Card::getCardFromUid(*set, input->toString()); input_card = Card::getUIDCard(*set, input->toString());
} }
if (!input_card) { if (!input_card) {
queue_message(MESSAGE_ERROR, _ERROR_("could not find input")); queue_message(MESSAGE_ERROR, _ERROR_("could not find input"));
@@ -905,7 +905,7 @@ SCRIPT_FUNCTION(remove_links) {
other_card = c->getValue(); other_card = c->getValue();
} }
else if (linked_card->type() == SCRIPT_STRING) { 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) { if (!other_card) {
queue_message(MESSAGE_WARNING, _ERROR_("could not find linked")); 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); SCRIPT_PARAM_DEFAULT(ScriptValueP, linked_relation, script_nil);
if (linked_relation != script_nil) { if (linked_relation != script_nil) {
if (linked_relation->type() == SCRIPT_STRING) { 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()); 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)); ret->value.push_back(to_script(other_card));
} }
return ret; return ret;
+2 -2
View File
@@ -145,8 +145,8 @@ SCRIPT_FUNCTION(add_card_to_set) {
Set& _set = *s->getValue(); Set& _set = *s->getValue();
ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get()); ScriptObject<CardP>* c = dynamic_cast<ScriptObject<CardP>*>(input.get());
if (c) { if (c) {
CardP _card = c->getValue(); vector<CardP> _cards { c->getValue() };
_set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _card)); _set.actions.addAction(make_unique<AddCardAction>(ADD, _set, _cards));
SCRIPT_RETURN(true); SCRIPT_RETURN(true);
} }
if (input->type() == SCRIPT_COLLECTION) { if (input->type() == SCRIPT_COLLECTION) {
+3 -1
View File
@@ -12,13 +12,15 @@
#include <util/string.hpp> #include <util/string.hpp>
#include <vector> #include <vector>
DECLARE_POINTER_TYPE(Action);
// ----------------------------------------------------------------------------- : Action // ----------------------------------------------------------------------------- : Action
/// Base class for actions that can be stored in an ActionStack. /// Base class for actions that can be stored in an ActionStack.
/** An action is something that can be done to modify an object. /** An action is something that can be done to modify an object.
* It must store the necessary information to also undo the action. * It must store the necessary information to also undo the action.
*/ */
class Action { class Action : public IntrusivePtrVirtualBase, public IntrusiveFromThis<Action> {
public: public:
virtual ~Action() {}; virtual ~Action() {};