diff --git a/CHANGES.txt b/CHANGES.txt index 2b7ca71c..93511e88 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,7 +9,8 @@ Features: * In the quick search box you can specify which field to search in, for example `type:Wizard` searches for cards with Wizard in the type. - * Added "Select All" function (#19) + * Added "Select All" to menu (#19) + * Added "Save as Directory" to menu Bug fixes: * card variable in console panel now refers to the selected card diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index 5c7d246f..14e76418 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -1,7 +1,7 @@ -mse version: 2.0.0 +mse version: 2.0.2 installer group: translations/English full name: English -version: 2011-01-22 +version: 2029-05-09 icon: usgb.png ############################################################## Menu items @@ -11,6 +11,7 @@ menu: open set: &Open... Ctrl+O save set: &Save Ctrl+S save set as: Save &As... F12 + save set as directory: Save As Directory... export: &Export export html: &HTML... export image: Card &Image... @@ -124,6 +125,7 @@ help: last opened set: Open '%s' save set: Save the set save set as: Save the set with a new name + save set as directory: Save the set as a directory with separate files for each card export: Export the set... export html: Export the set to a web page export image: Export the selected card to an image file @@ -642,6 +644,7 @@ title: new set: New Set open set: Open Set save set: Save Set As + save set as directory: Save Set As Directory save image: Save Image updates available: Updates Available save changes: Save Changes? diff --git a/src/data/format/mse2.cpp b/src/data/format/mse2.cpp index fec91db6..92364a40 100644 --- a/src/data/format/mse2.cpp +++ b/src/data/format/mse2.cpp @@ -23,12 +23,13 @@ class MSE2FileFormat : public FileFormat { virtual bool canExport(const Game&) { return true; } virtual SetP importSet(const String& filename) { wxString set_name = filename; - // Strip "/set" from the end, newer wx versions have a function for this: - // filename.EndsWith(_("/set"), &set_name); - if (filename.size() > 4 && filename.substr(filename.size()-4) == _("/set")) { - set_name = filename.substr(0, filename.size()-4); + // Strip "/set" or "/set.mset-set" from the end, this allows opening directories as set files + if (filename.EndsWith(_(".mse-set/set")) || filename.EndsWith(_(".mse-set\\set"))) { + set_name = filename.substr(0, filename.size() - 4); + } else if (filename.EndsWith(_(".mse-set/set.mse-set")) || filename.EndsWith(_(".mse-set\\set.mse-set"))) { + set_name = filename.substr(0, filename.size() - 12); } - SetP set(new Set); + SetP set = make_intrusive(); set->open(set_name); settings.addRecentFile(set_name); return set; diff --git a/src/gui/set/window.cpp b/src/gui/set/window.cpp index 2f078430..b6fda933 100644 --- a/src/gui/set/window.cpp +++ b/src/gui/set/window.cpp @@ -56,6 +56,7 @@ SetWindow::SetWindow(Window* parent, const SetP& set) add_menu_item_tr(menuFile, ID_FILE_OPEN, "open", "open_set"); add_menu_item_tr(menuFile, ID_FILE_SAVE, "save", "save_set"); add_menu_item_tr(menuFile, ID_FILE_SAVE_AS, nullptr, "save_set_as"); + add_menu_item_tr(menuFile, ID_FILE_SAVE_AS_DIRECTORY, nullptr, "save_set_as_directory"); add_menu_item_tr(menuFile, wxID_ANY, "export", "export", wxITEM_NORMAL, makeExportMenu()); menuFile->AppendSeparator(); add_menu_item_tr(menuFile, ID_FILE_CHECK_UPDATES, nullptr, "check_updates"); @@ -446,7 +447,7 @@ bool SetWindow::askSaveAndContinue() { try { if (set->needSaveAs()) { // need save as - wxFileDialog dlg(this, _TITLE_("save set"), settings.default_set_dir, clean_filename(set->short_name), export_formats(*set->game), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wxFileDialog dlg(this, _TITLE_("save_set"), settings.default_set_dir, clean_filename(set->short_name), export_formats(*set->game), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg.ShowModal() == wxID_OK) { settings.default_set_dir = dlg.GetDirectory(); export_set(*set, dlg.GetPath(), dlg.GetFilterIndex()); @@ -561,7 +562,7 @@ void SetWindow::onFileNew(wxCommandEvent&) { void SetWindow::onFileOpen(wxCommandEvent&) { if (!settings.open_sets_in_new_window && isOnlyWithSet() && !askSaveAndContinue()) return; - wxFileDialog dlg(this, _TITLE_("open set"), settings.default_set_dir, _(""), import_formats(), wxFD_OPEN); + wxFileDialog dlg(this, _TITLE_("open_set"), settings.default_set_dir, _(""), import_formats(), wxFD_OPEN); if (dlg.ShowModal() == wxID_OK) { settings.default_set_dir = dlg.GetDirectory(); wxBusyCursor busy; @@ -582,7 +583,7 @@ void SetWindow::onFileSave(wxCommandEvent& ev) { } void SetWindow::onFileSaveAs(wxCommandEvent&) { - wxFileDialog dlg(this, _TITLE_("save set"), settings.default_set_dir, clean_filename(set->short_name), export_formats(*set->game), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + wxFileDialog dlg(this, _TITLE_("save_set"), settings.default_set_dir, clean_filename(set->short_name), export_formats(*set->game), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg.ShowModal() == wxID_OK) { settings.default_set_dir = dlg.GetDirectory(); export_set(*set, dlg.GetPath(), dlg.GetFilterIndex()); @@ -590,6 +591,18 @@ void SetWindow::onFileSaveAs(wxCommandEvent&) { } } +void SetWindow::onFileSaveAsDirectory(wxCommandEvent&) { + wxFileDialog dlg(this, _TITLE_("save_set_as_directory"), settings.default_set_dir, clean_filename(set->short_name), "Magic Set Editor sets (*.mse-set)|*.mse-set", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (dlg.ShowModal() == wxID_OK) { + String filename = dlg.GetPath(); + settings.default_set_dir = dlg.GetDirectory(); + set->saveAs(filename, true, true); + settings.addRecentFile(filename); + set->actions.setSavePoint(); + updateTitle(); // title may depend on filename + } +} + /* void SetWindow::onFileInspect(wxCommandEvent&) { var wnd = new TreeGridWindow(&this, set); @@ -835,6 +848,7 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame) EVT_MENU (ID_FILE_OPEN, SetWindow::onFileOpen) EVT_MENU (ID_FILE_SAVE, SetWindow::onFileSave) EVT_MENU (ID_FILE_SAVE_AS, SetWindow::onFileSaveAs) + EVT_MENU (ID_FILE_SAVE_AS_DIRECTORY, SetWindow::onFileSaveAsDirectory) EVT_MENU (ID_FILE_EXPORT, SetWindow::onFileExportMenu) EVT_MENU (ID_FILE_EXPORT_IMAGE, SetWindow::onFileExportImage) EVT_MENU (ID_FILE_EXPORT_IMAGES, SetWindow::onFileExportImages) diff --git a/src/gui/set/window.hpp b/src/gui/set/window.hpp index 59a65ad5..380d2bda 100644 --- a/src/gui/set/window.hpp +++ b/src/gui/set/window.hpp @@ -119,6 +119,7 @@ private: void onFileOpen (wxCommandEvent&); void onFileSave (wxCommandEvent&); void onFileSaveAs (wxCommandEvent&); + void onFileSaveAsDirectory (wxCommandEvent&); // void onFileInspect (wxCommandEvent&); void onFileExportMenu (wxCommandEvent&); void onFileExportImage (wxCommandEvent&); diff --git a/src/util/io/package.cpp b/src/util/io/package.cpp index 977214a7..b54f53cc 100644 --- a/src/util/io/package.cpp +++ b/src/util/io/package.cpp @@ -97,9 +97,9 @@ void Package::save(bool remove_unused) { saveAs(filename, remove_unused); } -void Package::saveAs(const String& name, bool remove_unused) { +void Package::saveAs(const String& name, bool remove_unused, bool as_directory) { // type of package - if (wxDirExists(name)) { + if (wxDirExists(name) || as_directory) { saveToDirectory(name, remove_unused, false); } else { saveToZipfile (name, remove_unused, false); @@ -355,6 +355,7 @@ Package::FileInfo::~FileInfo() { } void Package::loadZipStream() { + files.clear(); while (true) { wxZipEntry* entry = zipStream->GetNextEntry(); if (!entry) break; @@ -391,9 +392,7 @@ void Package::openSubdir(const String& name) { } void Package::openZipfile() { - // close old streams - zipStream.reset(); - // open streams + // open stream zipStream = make_unique(filename); if (!zipStream->IsOk()) throw PackageError(_ERROR_1_("package not found", filename)); // read zip entries @@ -401,30 +400,43 @@ void Package::openZipfile() { } void Package::saveToDirectory(const String& saveAs, bool remove_unused, bool is_copy) { + // create directory? + wxMkdir(saveAs); // write to a directory VCSP vcs = getVCS(); FOR_EACH(f, files) { + String f_out_path = saveAs + _("/") + f.first; if (!f.second.keep && remove_unused) { // remove files that are not to be kept // ignore failure (new file that is not kept) - vcs->removeFile(saveAs+_("/")+f.first); - } else if (f.second.wasWritten()) { + vcs->removeFile(f_out_path); + continue; + } + // TODO: create subdirectory? + // write file + if (f.second.wasWritten()) { // move files that were updated - remove_file(saveAs+_("/")+f.first); - if (!(is_copy ? wxCopyFile (f.second.tempName, saveAs+_("/")+f.first) - : wxRenameFile(f.second.tempName, saveAs+_("/")+f.first))) { + remove_file(f_out_path); + if (!(is_copy ? wxCopyFile (f.second.tempName, f_out_path) + : wxRenameFile(f.second.tempName, f_out_path))) { throw PackageError(_ERROR_("unable to store file")); } if (f.second.created) { - vcs->addFile(saveAs+_("/")+f.first); + vcs->addFile(f_out_path); f.second.created = false; } } else if (filename != saveAs) { // save as, copy old filess - if (!wxCopyFile(filename+_("/")+f.first, saveAs+_("/")+f.first)) { - throw PackageError(_ERROR_("unable to store file")); + if (isZipfile()) { + auto in_stream = openIn(f.first); + wxFileOutputStream out(f_out_path); + out.Write(*in_stream); + } else { + if (!wxCopyFile(filename+_("/")+f.first, f_out_path)) { + throw PackageError(_ERROR_("unable to store file")); + } } - vcs->addFile(saveAs+_("/")+f.first); + vcs->addFile(f_out_path); } else { // old file, just keep it } @@ -475,6 +487,8 @@ void Package::saveToZipfile(const String& saveAs, bool remove_unused, bool is_co wxRenameFile(saveAs, saveAs + _(".bak")); } wxRenameFile(tempFile, saveAs); + // re-open zip file + openZipfile(); } @@ -590,11 +604,11 @@ void Packaged::save() { referenceFile(typeName()); Package::save(); } -void Packaged::saveAs(const String& package, bool remove_unused) { +void Packaged::saveAs(const String& package, bool remove_unused, bool as_directory) { WITH_DYNAMIC_ARG(writing_package, this); writeFile(typeName(), *this, fileVersion()); referenceFile(typeName()); - Package::saveAs(package, remove_unused); + Package::saveAs(package, remove_unused, as_directory); } void Packaged::saveCopy(const String& package) { WITH_DYNAMIC_ARG(writing_package, this); diff --git a/src/util/io/package.hpp b/src/util/io/package.hpp index 2036af83..15711e22 100644 --- a/src/util/io/package.hpp +++ b/src/util/io/package.hpp @@ -124,7 +124,7 @@ class Package : public IntrusivePtrVirtualBase { void save(bool remove_unused = true); /// Saves the package under a different filename - void saveAs(const String& package, bool remove_unused = true); + void saveAs(const String& package, bool remove_unused = true, bool as_directory = false); /// Saves the package under a different filename, but keep the old one open void saveCopy(const String& package); @@ -279,7 +279,7 @@ class Packaged : public Package { /// Ensure the package is fully loaded. void loadFully(); void save(); - void saveAs(const String& package, bool remove_unused = true); + void saveAs(const String& package, bool remove_unused = true, bool as_directory = false); void saveCopy(const String& package); /// Check if this package lists a dependency on the given package diff --git a/src/util/window_id.hpp b/src/util/window_id.hpp index f987d6d8..af7c050b 100644 --- a/src/util/window_id.hpp +++ b/src/util/window_id.hpp @@ -23,6 +23,7 @@ enum MenuID { ID_FILE_OPEN = wxID_OPEN, ID_FILE_SAVE = wxID_SAVE, ID_FILE_SAVE_AS = wxID_SAVEAS, + ID_FILE_SAVE_AS_DIRECTORY = 12, ID_FILE_STORE = 1, ID_FILE_EXIT = wxID_EXIT, ID_FILE_EXPORT = 2, @@ -132,6 +133,7 @@ enum ChildMenuID { // SymbolSelectEditor toolbar/menu ID_SYMBOL_COMBINE = 7001, + ID_SYMBOL_COMBINE_MERGE = ID_SYMBOL_COMBINE + 0, //SYMBOL_COMBINE_MERGE ID_SYMBOL_COMBINE_SUBTRACT = ID_SYMBOL_COMBINE + 1, //SYMBOL_COMBINE_SUBTRACT ID_SYMBOL_COMBINE_INTERSECTION = ID_SYMBOL_COMBINE + 2, //SYMBOL_COMBINE_INTERSECTION