From 87fbc0e80e01c7f4e148fed2abcc41f0ab5d9cf8 Mon Sep 17 00:00:00 2001 From: GenevensiS <66968533+G-e-n-e-v-e-n-s-i-S@users.noreply.github.com> Date: Mon, 9 Jun 2025 04:53:33 +0200 Subject: [PATCH] Implement CSV / TSV import (#45) - add csv/tsv importer - add `make_map` script function - add `alt name` field property - add `construction script` field property - add `construction script` game property --- data/en.mse-locale/locale | 10 + doc/function/index.txt | 1 + doc/function/make_map.txt | 17 ++ doc/type/field.txt | 5 + doc/type/game.txt | 1 + src/data/field.cpp | 3 + src/data/field.hpp | 40 ++-- src/data/game.cpp | 25 ++- src/data/game.hpp | 22 +- src/data/stylesheet.cpp | 4 +- src/gui/add_csv_window.cpp | 189 ++++++++++++++++++ src/gui/add_csv_window.hpp | 50 +++++ src/gui/control/card_list.cpp | 12 +- src/gui/control/card_list.hpp | 3 +- src/gui/set/cards_panel.cpp | 4 + src/script/functions/basic.cpp | 27 ++- src/script/functions/construction.cpp | 153 +++++++++++--- src/util/string.cpp | 9 + src/util/string.hpp | 5 +- src/util/window_id.hpp | 3 + .../drupal/mse-drupal-modules/highlight.inc | 1 + 21 files changed, 521 insertions(+), 63 deletions(-) create mode 100644 doc/function/make_map.txt create mode 100644 src/gui/add_csv_window.cpp create mode 100644 src/gui/add_csv_window.hpp diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index e98ccb86..1964ccbb 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -52,6 +52,7 @@ menu: add card: &Add Card Ctrl+Enter add cards: Add &Multiple Cards... remove card: &Delete Selected Card + add card csv: Add Cards from CSV or TSV... orientation: &Orientation rotate 0: &Normal rotate 270: Rotated 90° &Clockwise @@ -536,6 +537,12 @@ label: html template: Template: html export options: Export options + # CSV import + add card csv sep: Separator: + add card csv tab: Tab + add card csv comma: Comma + add card csv file: CSV or TSV file path: + # Image slicer original: Original: result: Result: @@ -723,6 +730,9 @@ title: export cancelled: Export Cancelled export html: Export to HTML save html: Export to HTML + # import + add card csv: Add Cards from CSV or TSV file + add card csv file: Open CSV or TSV file # auto replace auto replaces: Auto Replace diff --git a/doc/function/index.txt b/doc/function/index.txt index 875b83c4..b4fa5497 100644 --- a/doc/function/index.txt +++ b/doc/function/index.txt @@ -11,6 +11,7 @@ These functions are built into the program, other [[type:function]]s can be defi | [[fun:to_color]] Convert any value to a [[type:color]] | [[fun:to_image]] Convert any value to an [[type:image]] | [[fun:to_date]] Convert any value to a [[type:date]] +| [[fun:make_map]] Create a [[type:map]] from two [[type:list]]s | [[fun:type_name]] Get the type of a value ! Numbers <<< diff --git a/doc/function/make_map.txt b/doc/function/make_map.txt new file mode 100644 index 00000000..57b6e624 --- /dev/null +++ b/doc/function/make_map.txt @@ -0,0 +1,17 @@ +Function: make_map + +--Usage-- +> make_map(keys: some_list, values: some_list) + +Creates a new map. Converts the elements in keys to strings, and uses them as keys for the elements in values. + +Produces a warning if keys and values are not of the same size. +nil keys will be ignored. + +--Parameters-- +! Parameter Type Description +| @keys@ [[type:list]] List of keys. +| @values@ [[type:list]] List of values. + +--Examples-- +> make_map(keys:["apple", "durian", 3], values:["good", "bad", "not edible"]) == ["apple":"good", "durian":"bad", "3":"not edible"] diff --git a/doc/type/field.txt b/doc/type/field.txt index eefa4a78..cb88089c 100644 --- a/doc/type/field.txt +++ b/doc/type/field.txt @@ -28,6 +28,7 @@ Fields are part of the [[file:style triangle]]: * @color@ * @info@ | @name@ [[type:string]] ''required'' Name of the field. +| @alt names@ [[type:list]] of [[type:string]] Possible alternate names for this field, mainly to look for it when importing CSV files. They are trimmed, made case insensitive, and don't distinguish between spaces and underscores. | @description@ [[type:localized string]] @""@ Description of the field, shown in the status bar when the mouse is over the field. | @icon@ [[type:filename]] Filename of an icon for this field, used for automatically generated [[type:statistics category]]s. | @editable@ [[type:boolean]] @true@ Can values of this field be edited? @@ -42,6 +43,10 @@ Fields are part of the [[file:style triangle]]: | @card list name@ [[type:localized string]] field name Alternate name to use for the card list, for example an abbreviation. | @card list alignment@ [[type:alignment]] @left@ Alignment of the card list column. | @sort script@ [[type:script]] Alternate way to sort the card list when using this column to sort the list. +| @construction script@ [[type:script]] Script applied to the value given when creating a card with the new_card function. + For example, the pt field should not be initialized directly, since it is a combination of the power field and toughness field. + So if a value is given for pt, it must be redirected to power and toughness like so: {split := split_text(value, match:"/"); [power:split[0], toughness:split[1]]}. + The script must return a map from field names to values. Use the make_map function to dynamically create maps. The @type@ determines what values of this field contain: ! Type Values contain Displayed as diff --git a/doc/type/game.txt b/doc/type/game.txt index ce2d5088..4aaf4bd4 100644 --- a/doc/type/game.txt +++ b/doc/type/game.txt @@ -33,6 +33,7 @@ Such a package contains a [[file:format|data file]] called game that ha | @default set style@ [[type:indexmap]] of [[type:style]]s Default style for the set fields, can be overridden by the stylesheet. | @card fields@ [[type:list]] of [[type:field]]s Fields for each card. | @card list color script@ [[type:script]] from fields Script that determines the color of an item in the card list.
If not set uses the @card list colors@ property of the first card field that has it. +| @construction script@ [[type:script]] Script that is applied as the last step in the creation of a card in the new_card function. Must return a map from field names to values. Use the make_map function to dynamically create maps. | @statistics dimensions@ [[type:list]] of [[type:statistics dimension]]s from fields Dimensions for statistics, a dimension is roughly the same as an axis.
By default all card fields with 'show statistics' set to true are used. | @statistics categories@ [[type:list]] of [[type:statistics category]]s from dimensions DOC_MSE_VERSION: not used since 0.3.6 Choices shown on the statistics panel.
By default all statistics dimensions are used. diff --git a/src/data/field.cpp b/src/data/field.cpp index 36536291..4b1430ab 100644 --- a/src/data/field.cpp +++ b/src/data/field.cpp @@ -40,6 +40,7 @@ Field::~Field() {} void Field::initDependencies(Context& ctx, const Dependency& dep) const { sort_script.initDependencies(ctx, dep); + construction_script.initDependencies(ctx, dep); } IMPLEMENT_REFLECTION(Field) { @@ -48,6 +49,7 @@ IMPLEMENT_REFLECTION(Field) { REFLECT(type); } REFLECT(name); + REFLECT(alt_names); REFLECT_LOCALIZED(caption); REFLECT_LOCALIZED(description); // FIXME: This field is both unused and uninitialized. REFLECT_N("icon", icon_filename); @@ -62,6 +64,7 @@ IMPLEMENT_REFLECTION(Field) { REFLECT(card_list_allow); REFLECT_LOCALIZED(card_list_name); REFLECT(sort_script); + REFLECT(construction_script); REFLECT_N("card_list_alignment", card_list_align); } diff --git a/src/data/field.hpp b/src/data/field.hpp index ba94022f..e11e1b1e 100644 --- a/src/data/field.hpp +++ b/src/data/field.hpp @@ -42,25 +42,27 @@ public: Field(); virtual ~Field(); - size_t index; ///< Used by IndexMap - String name; ///< Name of the field, for refering to it from scripts and files - LocalizedString caption; ///< Caption for NativeLookEditor - LocalizedString description;///< Description, used in status bar - String icon_filename; ///< Filename for an icon (for list of fields) - bool editable; ///< Can values of this field be edited? - bool save_value; ///< Should values of this field be written to files? Can be false for script generated fields. - bool show_statistics; ///< Should this field appear as a group by choice in the statistics panel? - int position_hint; ///< Position in the statistics list - bool identifying; ///< Does this field give Card::identification()? - int card_list_column; ///< What column to use in the card list? - UInt card_list_width; ///< Width of the card list column (pixels). - bool card_list_visible;///< Is this field shown in the card list? - bool card_list_allow; ///< Is this field allowed to appear in the card list? - LocalizedString card_list_name; ///< Name to use in card list. - Alignment card_list_align; ///< Alignment of the card list colummn. - OptionalScript sort_script; ///< The script to use when sorting this, if not the value. - Dependencies dependent_scripts; ///< Scripts that depend on values of this field - String package_relative_filename; + size_t index; ///< Used by IndexMap + String name; ///< Name of the field, for refering to it from scripts and files + vector alt_names; ///< Other names this field might go by, mainly in CSV files + LocalizedString caption; ///< Caption for NativeLookEditor + LocalizedString description; ///< Description, used in status bar + String icon_filename; ///< Filename for an icon (for list of fields) + bool editable; ///< Can values of this field be edited? + bool save_value; ///< Should values of this field be written to files? Can be false for script generated fields. + bool show_statistics; ///< Should this field appear as a group by choice in the statistics panel? + int position_hint; ///< Position in the statistics list + bool identifying; ///< Does this field give Card::identification()? + int card_list_column; ///< What column to use in the card list? + UInt card_list_width; ///< Width of the card list column (pixels). + bool card_list_visible; ///< Is this field shown in the card list? + bool card_list_allow; ///< Is this field allowed to appear in the card list? + LocalizedString card_list_name; ///< Name to use in card list. + Alignment card_list_align; ///< Alignment of the card list colummn. + OptionalScript sort_script; ///< The script to use when sorting this, if not the value. + OptionalScript construction_script; ///< The script to apply to the supplied value, when creating a new card. + Dependencies dependent_scripts; ///< Scripts that depend on values of this field + String package_relative_filename; /// Creates a new Value corresponding to this Field virtual ValueP newValue() = 0; diff --git a/src/data/game.cpp b/src/data/game.cpp index 18c7882b..09edff55 100644 --- a/src/data/game.cpp +++ b/src/data/game.cpp @@ -49,6 +49,7 @@ IMPLEMENT_REFLECTION(Game) { REFLECT_NO_SCRIPT(default_set_style); REFLECT_NO_SCRIPT(card_fields); REFLECT_NO_SCRIPT(card_list_color_script); + REFLECT_NO_SCRIPT(construction_script); REFLECT_NO_SCRIPT(statistics_dimensions); REFLECT_NO_SCRIPT(statistics_categories); REFLECT_COMPAT(<308, "pack_item", pack_types); @@ -93,6 +94,28 @@ void Game::validate(Version v) { pack->filter = OptionalScript(_("true")); pack->select = SELECT_NO_REPLACE; pack_types.push_back(pack); + } + // 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); + 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); + } + } } } @@ -114,7 +137,7 @@ void Game::initCardListColorScript() { return; } } -} +} // special behaviour of reading/writing GamePs: only read/write the name diff --git a/src/data/game.hpp b/src/data/game.hpp index c34c3ede..07b6dff7 100644 --- a/src/data/game.hpp +++ b/src/data/game.hpp @@ -12,7 +12,7 @@ #include #include