diff --git a/src/data/format/formats.hpp b/src/data/format/formats.hpp index 0c926e47..778f7757 100644 --- a/src/data/format/formats.hpp +++ b/src/data/format/formats.hpp @@ -11,6 +11,7 @@ #include #include +#include class Game; DECLARE_POINTER_TYPE(Set); @@ -85,6 +86,10 @@ FileFormatP mtg_editor_file_format(); /// Export images for each card in a set to a list of files void export_images(Window* parent, const SetP& set); +/// Export the image for each card in a list of cards +void export_images(const SetP& set, vector& cards, + const String& path, const String& filename_template, FilenameConflicts conflicts); + /// Export the image of a single card void export_image(const SetP& set, const CardP& card, const String& filename); diff --git a/src/data/format/image.cpp b/src/data/format/image.cpp index 8ca59aaf..264c60db 100644 --- a/src/data/format/image.cpp +++ b/src/data/format/image.cpp @@ -7,11 +7,16 @@ // ----------------------------------------------------------------------------- : Includes #include +#include #include #include +#include #include #include #include +#include + +DECLARE_TYPEOF_COLLECTION(CardP); // ----------------------------------------------------------------------------- : Single card export @@ -59,3 +64,30 @@ Bitmap export_bitmap(const SetP& set, const CardP& card) { } // ----------------------------------------------------------------------------- : Multiple card export + + +void export_images(const SetP& set, vector& cards, + const String& path, const String& filename_template, FilenameConflicts conflicts) +{ + wxBusyCursor busy; + // Script + ScriptP filename_script = parse(filename_template, nullptr, true); + // Path + wxFileName fn(path); + // Export + std::set used; // for CONFLICT_NUMBER_OVERWRITE + FOR_EACH_CONST(card, cards) { + // filename for this card + Context& ctx = set->getContext(card); + String filename = clean_filename(untag(ctx.eval(*filename_script)->toString())); + if (!filename) continue; // no filename -> no saving + // full path + fn.SetFullName(filename); + // does the file exist? + if (!resolve_filename_conflicts(fn, conflicts, used)) continue; + // write image + filename = fn.GetFullPath(); + used.insert(filename); + export_image(set, card, filename); + } +} diff --git a/src/gui/images_export_window.cpp b/src/gui/images_export_window.cpp index 0df25334..3626775d 100644 --- a/src/gui/images_export_window.cpp +++ b/src/gui/images_export_window.cpp @@ -57,50 +57,6 @@ ImagesExportWindow::ImagesExportWindow(Window* parent, const SetP& set) // ----------------------------------------------------------------------------- : Exporting the images -void ExportCardImages::exportImages(const SetP& set, wxFileName& fn, const String& filename_template, FilenameConflicts conflicts) { - // Script - ScriptP filename_script = parse(filename_template, nullptr, true); - // Export - std::set used; // for CONFLICT_NUMBER_OVERWRITE - FOR_EACH(card, set->cards) { - if (includeCard(card)) { - // filename for this card - Context& ctx = set->getContext(card); - String filename = untag(ctx.eval(*filename_script)->toString()); - if (!filename) continue; // no filename -> no saving - // sanitize filename - fn.SetFullName(clean_filename(filename)); - // does the file exist? - if (fn.FileExists()) { - // file exists, what to do? - switch (conflicts) { - case CONFLICT_KEEP_OLD: goto next_card; - case CONFLICT_OVERWRITE: break; - case CONFLICT_NUMBER: { - int i = 0; - String ext = fn.GetExt(); - do { - fn.SetExt(String() << ++i << _(".") << ext); - } while(fn.FileExists()); - } - case CONFLICT_NUMBER_OVERWRITE: { - int i = 0; - String ext = fn.GetExt(); - while(used.find(fn.GetFullPath()) != used.end()) { - fn.SetExt(String() << ++i << _(".") << ext); - } - } - } - } - // write image - filename = fn.GetFullPath(); - used.insert(filename); - export_image(set, card, filename); - } - next_card:; - } -} - void ImagesExportWindow::onOk(wxCommandEvent&) { // Update settings GameSettings& gs = settings.gameSettingsFor(*set->game); @@ -114,17 +70,17 @@ void ImagesExportWindow::onOk(wxCommandEvent&) { String name = wxFileSelector(_TITLE_("export images"),_(""), _LABEL_("filename is ignored"),_(""), _LABEL_("filename is ignored")+_("|*"), wxSAVE, this); if (name.empty()) return; - wxFileName fn(name); + // Cards to export + vector cards; + FOR_EACH(card, set->cards) { + if (isSelected(card)) cards.push_back(card); + } // Export - exportImages(set, fn, gs.images_export_filename, gs.images_export_conflicts); + export_images(set, cards, name, gs.images_export_filename, gs.images_export_conflicts); // Done EndModal(wxID_OK); } -bool ImagesExportWindow::includeCard(const CardP& card) const { - return isSelected(card); -} - BEGIN_EVENT_TABLE(ImagesExportWindow,CardSelectWindow) EVT_BUTTON (wxID_OK, ImagesExportWindow::onOk) diff --git a/src/gui/images_export_window.hpp b/src/gui/images_export_window.hpp index 2deebc92..b7d46373 100644 --- a/src/gui/images_export_window.hpp +++ b/src/gui/images_export_window.hpp @@ -12,24 +12,11 @@ #include #include #include -class wxFileName; - -// ----------------------------------------------------------------------------- : ImagesExportWindow - -/// Export the cards in a set -class ExportCardImages { - public: - void exportImages(const SetP& set, wxFileName& filename, const String& filename_template, FilenameConflicts conflicts); - virtual ~ExportCardImages() {} - protected: - /// Should the given card be exported? - virtual bool includeCard(const CardP& card) const { return true; } -}; // ----------------------------------------------------------------------------- : ImagesExportWindow /// A window for selecting a subset of the cards from a set to export to images -class ImagesExportWindow : public CardSelectWindow, private ExportCardImages { +class ImagesExportWindow : public CardSelectWindow { public: ImagesExportWindow(Window* parent, const SetP& set); @@ -38,8 +25,6 @@ class ImagesExportWindow : public CardSelectWindow, private ExportCardImages { void onOk(wxCommandEvent&); - virtual bool includeCard(const CardP& card) const; - wxTextCtrl* format; wxChoice* conflicts; }; diff --git a/src/main.cpp b/src/main.cpp index 18115315..61b66fc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -220,10 +219,8 @@ int MSE::OnRun() { path += _("/x"); out = out.substr(pos + 1); } - wxFileName fn(path); // export - ExportCardImages exporter; - exporter.exportImages(set, fn, out, CONFLICT_NUMBER_OVERWRITE); + export_images(set, set->cards, path, out, CONFLICT_NUMBER_OVERWRITE); return EXIT_SUCCESS; } else { handle_error(_("Invalid command line argument:\n") + String(argv[1])); diff --git a/src/util/file_utils.cpp b/src/util/file_utils.cpp index 3ac9dac5..ff74b49c 100644 --- a/src/util/file_utils.cpp +++ b/src/util/file_utils.cpp @@ -36,6 +36,51 @@ bool ignore_file(const String& name) { return name == _("Thumbs.db"); // winXP explorer thumbnails } +bool is_filename_char(Char c) { + return isAlnum(c) || c == _(' ') || c == _('_') || c == _('-') || c == _('.'); +} + +String clean_filename(const String& name) { + String clean; + FOR_EACH_CONST(c, name) { + if (is_filename_char(c)) { + clean += c; + } + } + if (clean.empty() || starts_with(clean, _("."))) { + clean = _("no-name") + clean; + } + return clean; +} + +bool resolve_filename_conflicts(wxFileName& fn, FilenameConflicts conflicts, set& used) { + switch (conflicts) { + case CONFLICT_KEEP_OLD: + return !fn.FileExists(); + case CONFLICT_OVERWRITE: + return true; + case CONFLICT_NUMBER: { + int i = 0; + String ext = fn.GetExt(); + while(fn.FileExists()) { + fn.SetExt(String() << ++i << _(".") << ext); + } + return true; + } + case CONFLICT_NUMBER_OVERWRITE: { + int i = 0; + String ext = fn.GetExt(); + while(used.find(fn.GetFullPath()) != used.end()) { + fn.SetExt(String() << ++i << _(".") << ext); + } + return true; + } + default: { + throw InternalError(_("resolve_filename_conflicts: default case")); + } + } +} + // ----------------------------------------------------------------------------- : Directories bool create_parent_dirs(const String& file) { diff --git a/src/util/file_utils.hpp b/src/util/file_utils.hpp index 43431612..718f12ba 100644 --- a/src/util/file_utils.hpp +++ b/src/util/file_utils.hpp @@ -10,6 +10,8 @@ // ----------------------------------------------------------------------------- : Includes #include +#include +class wxFileName; // ----------------------------------------------------------------------------- : File names @@ -19,9 +21,17 @@ String normalize_filename(const String& filename); /// Normalize a filename as much as possible, for files in packages String normalize_internal_filename(const String& filename); -/// Should a file with the given name be ignored? +/// Should a file with the given name be ignored in packages? +/** true for hidden OS and version control files */ bool ignore_file(const String& name); +/// Make sure a string is safe to use as a filename +String clean_filename(const String& name); + +/// Change the filename fn if it already exists, in the way described by conflicts. +/** Returns true if the filename should be used, false if failed. */ +bool resolve_filename_conflicts(wxFileName& fn, FilenameConflicts conflicts, set& used); + // ----------------------------------------------------------------------------- : Removing and renaming /// Ensure that the parent directories of the given filename exist diff --git a/src/util/string.cpp b/src/util/string.cpp index a85b559a..3e172b06 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -397,25 +397,6 @@ bool cannocial_name_compare(const String& as, const Char* b) { } } -// ----------------------------------------------------------------------------- : Filenames - -bool is_filename_char(Char c) { - return isAlnum(c) || c == _(' ') || c == _('_') || c == _('-') || c == _('.'); -} - -String clean_filename(const String& name) { - String clean; - FOR_EACH_CONST(c, name) { - if (is_filename_char(c)) { - clean += c; - } - } - if (clean.empty() || starts_with(clean, _("."))) { - clean = _("no-name") + clean; - } - return clean; -} - // ----------------------------------------------------------------------------- : Regular expressions /// Escape a single character for use in regular expressions diff --git a/src/util/string.hpp b/src/util/string.hpp index 426ce5ce..b022840a 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -196,11 +196,6 @@ bool is_substr_i(const String& str, size_t pos, const String& cmp); /// Compare two strings for equality, b may contain '_' where a contains ' ' bool cannocial_name_compare(const String& a, const Char* b); -// ----------------------------------------------------------------------------- : Filenames - -/// Make sure a string is safe to use as a filename -String clean_filename(const String& name); - // ----------------------------------------------------------------------------- : Regular expressions /// Escape a single character for use in regular expressions