Bulk card modification

This commit is contained in:
GenevensiS
2025-12-04 15:44:04 +01:00
committed by GitHub
parent f22046d77b
commit 2932d0007d
12 changed files with 527 additions and 24 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

+1
View File
@@ -55,6 +55,7 @@ tool/no_auto IMAGE "tool/no_auto.png"
tool/card_add IMAGE "tool/card_add.png"
tool/card_add_multiple IMAGE "tool/card_add_multiple.png"
tool/card_modify_multiple IMAGE "tool/card_modify_multiple.png"
tool/card_del IMAGE "tool/card_del.png"
tool/card_link IMAGE "tool/card_link.png"
tool/card_copy IMAGE "tool/card_copy.png"
+27
View File
@@ -212,7 +212,34 @@ String ChangeCardHasStylingAction::getName(bool to_undo) const {
void ChangeCardHasStylingAction::perform(bool to_undo) {
card->has_styling = !card->has_styling;
swap(card->styling_data, styling_data);
}
// ----------------------------------------------------------------------------- : Change notes
ChangeCardNotesAction::ChangeCardNotesAction(const CardP& card, const String& notes)
: card(card), notes(notes)
{}
String ChangeCardNotesAction::getName(bool to_undo) const {
return _("Change notes");
}
void ChangeCardNotesAction::perform(bool to_undo) {
swap(card->notes, notes);
}
// ----------------------------------------------------------------------------- : Change uid
ChangeCardUIDAction::ChangeCardUIDAction(Set& set, const CardP& card, const String& uid)
: CardListAction(set), card(card), uid(uid)
{}
String ChangeCardUIDAction::getName(bool to_undo) const {
return _("Change ID");
}
void ChangeCardUIDAction::perform(bool to_undo) {
FOR_EACH(c, set.cards) {
c->updateLink(card->uid, uid);
}
swap(card->uid, uid);
}
// ----------------------------------------------------------------------------- : Pack types
+30
View File
@@ -147,8 +147,38 @@ public:
Set& set; ///< The set to copy styling from
CardP card; ///< The affected card
IndexMap<FieldP,ValueP> styling_data; ///< The old styling of the card
};
// ----------------------------------------------------------------------------- : Change notes
/// Changing the notes of a card
class ChangeCardNotesAction : public Action {
public:
ChangeCardNotesAction(const CardP& card, const String& notes);
String getName(bool to_undo) const override;
void perform(bool to_undo) override;
//private:
CardP card; ///< The affected card
String notes; ///< Its old notes
};
// ----------------------------------------------------------------------------- : Change uid
/// Changing the uid of a card
class ChangeCardUIDAction : public CardListAction {
public:
ChangeCardUIDAction(Set& set, const CardP& card, const String& id);
String getName(bool to_undo) const override;
void perform(bool to_undo) override;
//private:
CardP card; ///< The affected card
String uid; ///< Its old uid
};
// ----------------------------------------------------------------------------- : Pack types
/// An Action the changes the pack types of a set
+31 -1
View File
@@ -36,7 +36,10 @@ void ValueAction::setCard(CardP const& card) {
}
// ----------------------------------------------------------------------------- : Simple
unique_ptr<ValueAction> value_action(const TextValueP& value, const Defaultable<String>& new_value) {
return make_unique<SimpleValueAction<TextValue, false>>(value, new_value);
}
unique_ptr<ValueAction> value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) {
return make_unique<SimpleValueAction<ChoiceValue, true>>(value, new_value);
}
@@ -205,7 +208,34 @@ String ScriptStyleEvent::getName(bool) const {
}
void ScriptStyleEvent::perform(bool) {
assert(false); // this action is just an event, it should not be performed
}
// ----------------------------------------------------------------------------- : Bulk action
BulkAction::BulkAction(const vector<shared_ptr<Action>>& actions, const SetP& set, CardListBase* card_list_window)
: actions(actions), set(set), card_list_window(card_list_window)
{
if (actions.empty()) throw InternalError(_("BulkAction created with no actions"));
name_do = actions.front()->getName(false) + _(" ") + _ACTION_("bulk");
name_undo = actions.front()->getName(true) + _(" ") + _ACTION_("bulk");
}
BulkAction::~BulkAction() {}
String BulkAction::getName(bool to_undo) const {
return to_undo ? name_undo : name_do;
}
void BulkAction::perform(bool to_undo) {
FOR_EACH(action, actions) {
action->perform(to_undo);
set->actions.tellListeners(*action, to_undo);
}
card_list_window->Refresh();
}
bool BulkAction::merge(const Action& action) {
return false;
}
// ----------------------------------------------------------------------------- : Action performer
+33 -19
View File
@@ -23,6 +23,7 @@
#include <data/field/image.hpp>
#include <data/field/symbol.hpp>
#include <data/field/package_choice.hpp>
#include <gui/control/card_list.hpp>
class StyleSheet;
class LocalFileName;
@@ -30,15 +31,8 @@ DECLARE_POINTER_TYPE(Card);
DECLARE_POINTER_TYPE(Set);
DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(Style);
DECLARE_POINTER_TYPE(TextValue);
DECLARE_POINTER_TYPE(ChoiceValue);
DECLARE_POINTER_TYPE(MultipleChoiceValue);
DECLARE_POINTER_TYPE(ColorValue);
DECLARE_POINTER_TYPE(ImageValue);
DECLARE_POINTER_TYPE(SymbolValue);
DECLARE_POINTER_TYPE(PackageChoiceValue);
// ----------------------------------------------------------------------------- : ValueAction (based class)
// ----------------------------------------------------------------------------- : ValueAction (base class)
/// An Action the changes a Value
class ValueAction : public Action {
@@ -72,6 +66,15 @@ inline void swap_value(MultipleChoiceValue& a, MultipleChoiceValue::ValueType& b
swap(a.last_change, b.last_change);
}
/// Action that updates a Value to a new value
unique_ptr<ValueAction> value_action(const TextValueP& value, const Defaultable<String>& new_value);
unique_ptr<ValueAction> value_action(const ChoiceValueP& value, const Defaultable<String>& new_value);
unique_ptr<ValueAction> value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change);
unique_ptr<ValueAction> value_action(const ColorValueP& value, const Defaultable<Color>& new_value);
unique_ptr<ValueAction> value_action(const ImageValueP& value, const LocalFileName& new_value);
unique_ptr<ValueAction> value_action(const SymbolValueP& value, const LocalFileName& new_value);
unique_ptr<ValueAction> value_action(const PackageChoiceValueP& value, const String& new_value);
/// A ValueAction that swaps between old and new values
template <typename T, bool ALLOW_MERGE>
class SimpleValueAction : public ValueAction {
@@ -79,13 +82,13 @@ public:
inline SimpleValueAction(const intrusive_ptr<T>& value, const typename T::ValueType& new_value)
: ValueAction(value), new_value(new_value)
{}
void perform(bool to_undo) override {
ValueAction::perform(to_undo);
swap_value(static_cast<T&>(*valueP), new_value);
valueP->onAction(*this, to_undo); // notify value
}
bool merge(const Action& action) override {
if (!ALLOW_MERGE) return false;
TYPE_CASE(action, SimpleValueAction) {
@@ -97,18 +100,10 @@ public:
}
return false;
}
typename T::ValueType new_value;
};
/// Action that updates a Value to a new value
unique_ptr<ValueAction> value_action(const ChoiceValueP& value, const Defaultable<String>& new_value);
unique_ptr<ValueAction> value_action(const MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change);
unique_ptr<ValueAction> value_action(const ColorValueP& value, const Defaultable<Color>& new_value);
unique_ptr<ValueAction> value_action(const ImageValueP& value, const LocalFileName& new_value);
unique_ptr<ValueAction> value_action(const SymbolValueP& value, const LocalFileName& new_value);
unique_ptr<ValueAction> value_action(const PackageChoiceValueP& value, const String& new_value);
// ----------------------------------------------------------------------------- : Text
/// An action that changes a TextValue
@@ -206,6 +201,25 @@ public:
const Style* style; ///< The modified style
};
// ----------------------------------------------------------------------------- : Bulk action
// 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() override;
String getName(bool to_undo) const override;
void perform(bool to_undo) override;
bool merge(const Action& action) override;
private:
String name_do;
String name_undo;
vector<shared_ptr<Action>> actions;
SetP set;
CardListBase* card_list_window;
};
// ----------------------------------------------------------------------------- : Action performer
+330
View File
@@ -0,0 +1,330 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <fstream>
#include <sstream>
#include <regex>
#include <util/prec.hpp>
#include <data/game.hpp>
#include <data/set.hpp>
#include <data/card.hpp>
#include <data/stylesheet.hpp>
#include <data/field/multiple_choice.hpp>
#include <data/field/symbol.hpp>
#include <data/action/set.hpp>
#include <data/action/value.hpp>
#include <script/functions/construction_helper.hpp>
#include <gui/bulk_modification_window.hpp>
#include <gui/control/card_list.hpp>
#include <util/window_id.hpp>
#include <wx/statline.h>
// ----------------------------------------------------------------------------- : AddCSV
BulkModificationWindow::BulkModificationWindow(Window* parent, const SetP& set, bool sizer)
: wxDialog(parent, wxID_ANY, _TITLE_("bulk modify"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, set(set), parent(parent)
{
// init controls
ok_button = new wxButton(this, wxID_OK);
predicate_description = new wxStaticText(this, -1, _LABEL_("bulk modify predicate description"));
predicate = new wxTextCtrl(this, ID_CARD_BULK_PREDICATE, wxEmptyString, wxDefaultPosition, wxSize(400, 70), wxTE_MULTILINE);
predicate->SetHint(_LABEL_("bulk modify predicate example") + _("\ncard.cmc <= 3 and contains(card.type, match:\"Creature\")"));
predicate_errors = new wxStaticText(this, wxID_ANY, _(""));
predicate_errors->SetForegroundColour(*wxRED);
modification_description = new wxStaticText(this, -1, _LABEL_("bulk modify mod description"));
modification = new wxTextCtrl(this, ID_CARD_BULK_MODIFICATION, wxEmptyString, wxDefaultPosition, wxSize(400, 70), wxTE_MULTILINE);
modification_errors = new wxStaticText(this, wxID_ANY, _(""));
modification_errors->SetForegroundColour(*wxRED);
modification_parsed = true;
modification_selection = new wxChoice(this, ID_CARD_BULK_TYPE, wxDefaultPosition, wxDefaultSize, 0, nullptr);
modification_selection->Clear();
modification_selection->Append(_LABEL_("bulk modify all"));
modification_selection->Append(_LABEL_("bulk modify filtered"));
modification_selection->Append(_LABEL_("bulk modify selected"));
modification_selection->Append(_LABEL_("bulk modify predicate"));
modification_selection->SetSelection(0);
changeSelection();
parseModification();
field_type = new wxChoice(this, ID_CARD_BULK_FIELD, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_SORT);
field_type->Clear();
String default_selection = _("");
field_type->Append(_("id"));
field_type->Append(_("stylesheet"));
field_type->Append(_("notes"));
FOR_EACH(field, set->game->card_fields) {
field_type->Append(field->name);
if (field->identifying) default_selection = field->name;
}
int default_index = field_type->FindString(default_selection);
if (default_index == wxNOT_FOUND) default_index = 0;
field_type->SetSelection(default_index);
// init sizers
if (sizer) {
wxSizer* s = new wxBoxSizer(wxVERTICAL);
s->Add(new wxStaticText(this, -1, _LABEL_("bulk modify selection")), 0, wxALL, 8);
s->Add(modification_selection, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(predicate_description, 0, wxEXPAND | wxALL, 8);
s->Add(predicate, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(predicate_errors, 0, wxALL & ~wxTOP, 8);
s->AddSpacer(20);
s->Add(new wxStaticText(this, -1, _LABEL_("bulk modify field")), 0, wxALL, 8);
s->Add(field_type, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(modification_description, 0, wxEXPAND | wxALL, 8);
s->Add(modification, 0, wxEXPAND | (wxALL & ~wxTOP), 8);
s->Add(modification_errors, 0, wxALL & ~wxTOP, 8);
s->AddStretchSpacer(1);
wxStdDialogButtonSizer* s1 = new wxStdDialogButtonSizer();
s1->AddStretchSpacer(1);
s1->AddButton(ok_button);
s1->AddButton(new wxButton(this, wxID_CANCEL));
s1->Realize();
s->Add(s1, 1, wxEXPAND | wxALL, 12);
s->SetSizeHints(this);
SetSizer(s);
SetSize(600, 400);
Layout();
}
}
void BulkModificationWindow::updateOkButton() {
if (predicate_parsed && modification_parsed) {
ok_button->Enable(true);
}
else {
ok_button->Enable(false);
}
}
void BulkModificationWindow::changeSelection() {
if (modification_selection->GetSelection() <= 2) {
predicate_description->Hide();
predicate->Hide();
predicate_errors->Hide();
predicate_parsed = true;
SetSize(600, 400);
} else {
predicate_description->Show();
predicate->Show();
predicate_errors->Show();
parsePredicate();
SetSize(600, 560);
}
updateOkButton();
Layout();
}
void BulkModificationWindow::onSelectionChange(wxCommandEvent&) {
changeSelection();
}
void BulkModificationWindow::parsePredicate() {
vector<ScriptParseError> parse_errors;
String value = predicate->GetValue();
if (value.StartsWith("{") && value.EndsWith("}")) value = value.substr(1, value.length()-2);
predicate_script = parse(value, nullptr, false, parse_errors);
if (parse_errors.empty()) {
predicate_errors->SetLabel(_(""));
predicate_parsed = true;
}
else {
predicate_errors->SetLabel(ScriptParseErrors(parse_errors).what());
predicate_parsed = false;
}
updateOkButton();
}
void BulkModificationWindow::onPredicateChange(wxCommandEvent&) {
parsePredicate();
}
void BulkModificationWindow::parseModification() {
vector<ScriptParseError> parse_errors;
modification_script = parse(modification->GetValue(), nullptr, true, parse_errors);
if (parse_errors.empty()) {
modification_errors->SetLabel(_(""));
modification_parsed = true;
}
else {
modification_errors->SetLabel(ScriptParseErrors(parse_errors).what());
modification_parsed = false;
}
updateOkButton();
}
void BulkModificationWindow::onModificationChange(wxCommandEvent&) {
parseModification();
}
void BulkModificationWindow::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;
}
// get the cards
vector<CardP> cards;
int selection_type = modification_selection->GetSelection();
if (selection_type == 0) { // all
for (int i = 0; i < set->cards.size(); ++i) {
cards.push_back(set->cards[i]);
}
} else if (selection_type == 1) { // currently filtered
for (int i = 0; i < card_list_window->GetItemCount(); ++i) {
cards.push_back(card_list_window->getCard(i));
}
} else if (selection_type == 2) { // currently selected
card_list_window->getSelection(cards);
} else { // predicate
FOR_EACH(card, set->cards) {
Context& ctx = set->getContext(card);
ScriptValueP result = predicate_script->eval(ctx, false);
if (result->type() != SCRIPT_BOOL) {
queue_message(MESSAGE_ERROR, _ERROR_("bulk modify predicate is not bool"));
EndModal(wxID_ABORT);
return;
}
if (result->toBool()) {
cards.push_back(card);
}
}
}
int count = cards.size();
if (count == 0) {
queue_message(MESSAGE_ERROR, _ERROR_("bulk modify no cards"));
EndModal(wxID_ABORT);
return;
}
// get the new script values
vector<shared_ptr<Action>> 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")) {
vector<String> new_values;
FOR_EACH(card, cards) {
Context& ctx = set->getContext(card);
ScriptValueP new_value = modification_script->eval(ctx, false);
if (new_value->type() != SCRIPT_STRING) {
queue_message(MESSAGE_ERROR, _ERROR_("bulk modify mod is not string"));
EndModal(wxID_ABORT);
return;
}
new_values.push_back(new_value->toString());
}
assert(count == new_values.size());
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));
}
} else if (field_name == _("notes")) {
for (int i = 0; i < count; ++i) {
actions.push_back(make_shared<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]));
}
}
}
// card field value change
else {
vector<Value*> values;
vector<ScriptValueP> new_values;
FOR_EACH(card, cards) {
Value* value = get_card_field_container(*set->game, card->data, field_name, false);
values.push_back(value);
Context& ctx = set->getContext(card);
ScriptValueP new_value = modification_script->eval(ctx, false);
new_values.push_back(new_value);
}
assert(count == values.size());
assert(count == new_values.size());
// make the modifications (I have lost my battle with c++ templates)
if (dynamic_cast<TextValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
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);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else if (dynamic_cast<MultipleChoiceValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
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);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else if (dynamic_cast<ChoiceValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
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);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else if (dynamic_cast<PackageChoiceValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
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);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else if (dynamic_cast<ColorValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
ColorValue* value = dynamic_cast<ColorValue*>(values[i]);
ColorValue::ValueType new_value = new_values[i]->toColor();
shared_ptr<SimpleValueAction<ColorValue, false>> action = make_shared<SimpleValueAction<ColorValue, false>>(value, new_value);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else if (dynamic_cast<ImageValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
ImageValue* value = dynamic_cast<ImageValue*>(values[i]);
wxFileName fname(static_cast<ExternalImage*>(new_values[i].get())->toString());
ImageValue::ValueType new_value = LocalFileName::fromReadString(fname.GetName(), "");
shared_ptr<SimpleValueAction<ImageValue, false>> action = make_shared<SimpleValueAction<ImageValue, false>>(value, new_value);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else if (dynamic_cast<SymbolValue*>(values.front())) {
for (int i = 0; i < count; ++i) {
SymbolValue* value = dynamic_cast<SymbolValue*>(values[i]);
wxFileName fname(static_cast<ExternalImage*>(new_values[i].get())->toString());
SymbolValue::ValueType new_value = LocalFileName::fromReadString(fname.GetName(), "");
shared_ptr<SimpleValueAction<SymbolValue, false>> action = make_shared<SimpleValueAction<SymbolValue, false>>(value, new_value);
action->setCard(cards[i]);
actions.push_back(action);
}
}
else {
queue_message(MESSAGE_ERROR, _ERROR_("bulk modify script type unknown"));
}
}
set->actions.addAction(make_unique<BulkAction>(actions, set, card_list_window), false);
EndModal(wxID_OK);
}
BEGIN_EVENT_TABLE(BulkModificationWindow, wxDialog)
EVT_BUTTON (wxID_OK, BulkModificationWindow::onOk)
EVT_CHOICE (ID_CARD_BULK_TYPE, BulkModificationWindow::onSelectionChange)
EVT_TEXT (ID_CARD_BULK_PREDICATE, BulkModificationWindow::onPredicateChange)
EVT_TEXT (ID_CARD_BULK_MODIFICATION, BulkModificationWindow::onModificationChange)
END_EVENT_TABLE()
+48
View File
@@ -0,0 +1,48 @@
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#pragma once
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
DECLARE_POINTER_TYPE(Game);
DECLARE_POINTER_TYPE(Set);
// ----------------------------------------------------------------------------- : BulkModificationWindow
/// A window for modifying multiple cards at once.
class BulkModificationWindow : public wxDialog {
public:
BulkModificationWindow(Window* parent, const SetP& set, bool sizer);
protected:
DECLARE_EVENT_TABLE();
wxChoice* modification_selection, *field_type;
wxStaticText* modification_description, *modification_errors, *predicate_description, *predicate_errors;
wxTextCtrl* modification, *predicate;
bool modification_parsed, predicate_parsed;
wxButton* ok_button;
SetP set;
Window* parent;
ScriptP modification_script, predicate_script;
void onSelectionChange(wxCommandEvent&);
void changeSelection();
void onPredicateChange(wxCommandEvent&);
void parsePredicate();
void onModificationChange(wxCommandEvent&);
void parseModification();
void updateOkButton();
void onOk(wxCommandEvent&);
};
+11 -1
View File
@@ -16,6 +16,7 @@
#include <gui/util.hpp>
#include <gui/add_csv_window.hpp>
#include <gui/add_json_window.hpp>
#include <gui/bulk_modification_window.hpp>
#include <data/game.hpp>
#include <data/field.hpp>
#include <data/field/choice.hpp>
@@ -230,7 +231,16 @@ bool CardListBase::doAddJSON() {
return true;
}
return false;
}
}
bool CardListBase::doBulkModification() {
BulkModificationWindow wnd(this, set, true);
if (wnd.ShowModal() == wxID_OK) {
// The actual modifying is done in this window's onOk function
return true;
}
return false;
}
bool CardListBase::parseUrl(String& url, vector<CardP>& out) {
size_t j = out.size();
+6 -2
View File
@@ -81,9 +81,13 @@ public:
bool doCopy() override;
bool doCopyCardAndLinkedCards();
bool doPaste() override;
bool doDelete() override;
bool doDelete() override;
// Try to perform a bulk operation, return success
bool doAddCSV();
bool doAddJSON();
bool doAddJSON();
bool doBulkModification();
// Look for cards inside some given data
bool parseData(bool ignore_cards_from_own_card_list);
+4
View File
@@ -138,6 +138,7 @@ CardsPanel::CardsPanel(Window* parent, int id)
add_menu_item(menuCard, ID_CARD_REMOVE, "card_del", _MENU_("remove card")+_(" "), _HELP_("remove card"));
add_menu_item(menuCard, ID_CARD_LINK, "card_link", _MENU_("link card") + _(" "), _HELP_("link card"));
add_menu_item(menuCard, ID_CARD_AND_LINK_COPY, "card_copy", _MENU_("copy card and links") + _(" "), _HELP_("copy card and links"));
add_menu_item(menuCard, ID_CARD_BULK, "card_modify_multiple", _MENU_("bulk modify") + _(" "), _HELP_("bulk modify"));
menuCard->AppendSeparator();
auto menuRotate = new wxMenu();
add_menu_item_tr(menuRotate, ID_CARD_ROTATE_0, "card_rotate_0", "rotate_0", wxITEM_CHECK);
@@ -451,6 +452,9 @@ void CardsPanel::onCommand(int id) {
}
case ID_CARD_AND_LINK_COPY:
card_list->doCopyCardAndLinkedCards();
break;
case ID_CARD_BULK:
card_list->doBulkModification();
break;
case ID_CARD_ROTATE:
case ID_CARD_ROTATE_0: case ID_CARD_ROTATE_90: case ID_CARD_ROTATE_180: case ID_CARD_ROTATE_270: {
+6 -1
View File
@@ -115,7 +115,12 @@ enum ChildMenuID {
ID_CARD_ADD_CSV_BROWSE,
ID_CARD_ADD_JSON,
ID_CARD_ADD_JSON_ARRAY,
ID_CARD_ADD_JSON_BROWSE,
ID_CARD_ADD_JSON_BROWSE,
ID_CARD_BULK,
ID_CARD_BULK_TYPE,
ID_CARD_BULK_FIELD,
ID_CARD_BULK_MODIFICATION,
ID_CARD_BULK_PREDICATE,
// Keyword menu
ID_KEYWORD_ADD = 6101,