fix "referencing nonexistant file" bug

improve internal error message
This commit is contained in:
GenevensiS
2025-07-15 02:30:11 +02:00
parent 0e4c91b940
commit 70e44474a5
7 changed files with 97 additions and 62 deletions
+1 -50
View File
@@ -7,17 +7,10 @@
// ----------------------------------------------------------------------------- : Includes // ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp> #include <util/prec.hpp>
#include <util/tagged_string.hpp>
#include <data/action/value.hpp> #include <data/action/value.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <data/field/text.hpp>
#include <data/field/choice.hpp>
#include <data/field/multiple_choice.hpp>
#include <data/field/color.hpp>
#include <data/field/image.hpp>
#include <data/field/symbol.hpp>
#include <data/field/package_choice.hpp>
#include <data/card.hpp> #include <data/card.hpp>
#include <util/tagged_string.hpp>
#include <data/set.hpp> // for ValueActionPerformer #include <data/set.hpp> // for ValueActionPerformer
#include <wx/imaglist.h> #include <wx/imaglist.h>
@@ -44,48 +37,6 @@ void ValueAction::setCard(CardP const& card) {
// ----------------------------------------------------------------------------- : Simple // ----------------------------------------------------------------------------- : Simple
/// Swap the value in a Value object with a new one
inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); }
inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); }
inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); }
inline void swap_value(PackageChoiceValue& a, PackageChoiceValue ::ValueType& b) { swap(a.package_name, b); }
inline void swap_value(MultipleChoiceValue& a, MultipleChoiceValue::ValueType& b) {
swap(a.value, b.value);
swap(a.last_change, b.last_change);
}
/// A ValueAction that swaps between old and new values
template <typename T, bool ALLOW_MERGE>
class SimpleValueAction : public ValueAction {
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) {
if (action.valueP == valueP) {
// adjacent actions on the same value, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
}
private:
typename T::ValueType new_value;
};
unique_ptr<ValueAction> value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) { unique_ptr<ValueAction> value_action(const ChoiceValueP& value, const Defaultable<String>& new_value) {
return make_unique<SimpleValueAction<ChoiceValue, true>>(value, new_value); return make_unique<SimpleValueAction<ChoiceValue, true>>(value, new_value);
} }
+48
View File
@@ -16,6 +16,13 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <util/action_stack.hpp> #include <util/action_stack.hpp>
#include <util/defaultable.hpp> #include <util/defaultable.hpp>
#include <data/field/text.hpp>
#include <data/field/choice.hpp>
#include <data/field/multiple_choice.hpp>
#include <data/field/color.hpp>
#include <data/field/image.hpp>
#include <data/field/symbol.hpp>
#include <data/field/package_choice.hpp>
class StyleSheet; class StyleSheet;
class LocalFileName; class LocalFileName;
@@ -53,6 +60,47 @@ private:
// ----------------------------------------------------------------------------- : Simple // ----------------------------------------------------------------------------- : Simple
/// Swap the value in a Value object with a new one
inline void swap_value(ChoiceValue& a, ChoiceValue ::ValueType& b) { swap(a.value, b); }
inline void swap_value(ColorValue& a, ColorValue ::ValueType& b) { swap(a.value, b); }
inline void swap_value(ImageValue& a, ImageValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(SymbolValue& a, SymbolValue ::ValueType& b) { swap(a.filename, b); a.last_update.update(); }
inline void swap_value(TextValue& a, TextValue ::ValueType& b) { swap(a.value, b); a.last_update.update(); }
inline void swap_value(PackageChoiceValue& a, PackageChoiceValue ::ValueType& b) { swap(a.package_name, b); }
inline void swap_value(MultipleChoiceValue& a, MultipleChoiceValue::ValueType& b) {
swap(a.value, b.value);
swap(a.last_change, b.last_change);
}
/// A ValueAction that swaps between old and new values
template <typename T, bool ALLOW_MERGE>
class SimpleValueAction : public ValueAction {
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) {
if (action.valueP == valueP) {
// adjacent actions on the same value, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
}
typename T::ValueType new_value;
};
/// Action that updates a Value to a 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 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 MultipleChoiceValueP& value, const Defaultable<String>& new_value, const String& last_change);
+25
View File
@@ -16,6 +16,9 @@
#include <data/field.hpp> #include <data/field.hpp>
#include <data/field/text.hpp> // for 0.2.7 fix #include <data/field/text.hpp> // for 0.2.7 fix
#include <data/field/information.hpp> #include <data/field/information.hpp>
#include <data/field/image.hpp>
#include <data/field/symbol.hpp>
#include <data/action/value.hpp>
#include <util/tagged_string.hpp> // for 0.2.7 fix #include <util/tagged_string.hpp> // for 0.2.7 fix
#include <util/order_cache.hpp> #include <util/order_cache.hpp>
#include <util/delayed_index_maps.hpp> #include <util/delayed_index_maps.hpp>
@@ -104,6 +107,28 @@ IndexMap<FieldP, ValueP>& Set::stylingDataFor(const CardP& card) {
else return stylingDataFor(stylesheetFor(card)); else return stylingDataFor(stylesheetFor(card));
} }
void Set::referenceActionStackFiles() {
referenceActionStackFiles(true);
referenceActionStackFiles(false);
}
void Set::referenceActionStackFiles(bool undo) {
for (auto&& action : undo ? actions.undo_actions : actions.redo_actions) {
try {
SimpleValueAction<ImageValue, false>& v = dynamic_cast<SimpleValueAction<ImageValue, false>&>(*action);
if (ImageValue* v2 = dynamic_cast<ImageValue*>(v.valueP.get())) {
referenceFile(v.new_value.toStringForWriting());
referenceFile(v2->filename.toStringForWriting());
}
} catch (...) { try {
SimpleValueAction<SymbolValue, false>& v = dynamic_cast<SimpleValueAction<SymbolValue, false>&>(*action);
if (SymbolValue* v2 = dynamic_cast<SymbolValue*>(v.valueP.get())) {
referenceFile(v.new_value.toStringForWriting());
referenceFile(v2->filename.toStringForWriting());
}
} catch (...) {} }
}
}
String Set::identification() const { String Set::identification() const {
// an identifying field // an identifying field
FOR_EACH_CONST(v, data) { FOR_EACH_CONST(v, data) {
+7 -2
View File
@@ -89,8 +89,13 @@ public:
/// Styling information for a particular stylesheet /// Styling information for a particular stylesheet
IndexMap<FieldP, ValueP>& stylingDataFor(const StyleSheet&); IndexMap<FieldP, ValueP>& stylingDataFor(const StyleSheet&);
/// Styling information for a particular card /// Styling information for a particular card
IndexMap<FieldP, ValueP>& stylingDataFor(const CardP& card); IndexMap<FieldP, ValueP>& stylingDataFor(const CardP& card);
/// Make sure the image and symbol files from
/// the ActionStack are saved so we can undo
void referenceActionStackFiles();
void referenceActionStackFiles(bool undo);
/// Get the identification of this set, an identification is something like a name, title, etc. /// Get the identification of this set, an identification is something like a name, title, etc.
/** May return "" */ /** May return "" */
String identification() const; String identification() const;
+3 -2
View File
@@ -103,11 +103,12 @@ public:
/// Tell all listeners about an action /// Tell all listeners about an action
void tellListeners(const Action&, bool undone); void tellListeners(const Action&, bool undone);
private:
/// Actions to be undone. /// Actions to be undone.
vector<unique_ptr<Action>> undo_actions; vector<unique_ptr<Action>> undo_actions;
/// Actions to be redone /// Actions to be redone
vector<unique_ptr<Action>> redo_actions; vector<unique_ptr<Action>> redo_actions;
private:
/// Point at which the file was saved, corresponds to the top of the undo stack at that point /// Point at which the file was saved, corresponds to the top of the undo stack at that point
const Action* save_point; const Action* save_point;
/// Was the last thing the user did addAction? (as opposed to undo/redo) /// Was the last thing the user did addAction? (as opposed to undo/redo)
+6 -4
View File
@@ -110,10 +110,12 @@ String Error::what() const {
InternalError::InternalError(const String& str) InternalError::InternalError(const String& str)
: Error( : Error(
_("An internal error occured:\n\n") + _("An internal error occurred:\n\n") +
str + _("\n") str + _("\n\n")
_("Please save your work (use 'save as' to so you don't overwrite things)\n") _("Please save your work (use 'save as' so you don't overwrite things)\n")
_("and restart Magic Set Editor.\n\n") _("and restart Magic Set Editor.\n")
_("You can also find a backup of your set in the same folder as your set file\n")
_("called 'SETNAME.mse-set.bak'. Rename it to 'SETNAME-backup.mse-set' to open it.\n")
_("You should leave a bug report on https://github.com/twanvl/MagicSetEditor2/issues/\n") _("You should leave a bug report on https://github.com/twanvl/MagicSetEditor2/issues/\n")
_("Press Ctrl+C to copy this message to the clipboard.") _("Press Ctrl+C to copy this message to the clipboard.")
) )
+7 -4
View File
@@ -12,6 +12,7 @@
#include <util/error.hpp> #include <util/error.hpp>
#include <script/to_value.hpp> // for reflection #include <script/to_value.hpp> // for reflection
#include <script/profiler.hpp> // for PROFILER #include <script/profiler.hpp> // for PROFILER
#include <data/set.hpp>
#include <wx/wfstream.h> #include <wx/wfstream.h>
#include <wx/zipstrm.h> #include <wx/zipstrm.h>
#include <wx/dir.h> #include <wx/dir.h>
@@ -19,7 +20,7 @@
// ----------------------------------------------------------------------------- : Package : outside // ----------------------------------------------------------------------------- : Package : outside
IMPLEMENT_DYNAMIC_ARG(Package*, writing_package, nullptr); IMPLEMENT_DYNAMIC_ARG(Package*, writing_package, nullptr);
IMPLEMENT_DYNAMIC_ARG(Package*, clipboard_package, nullptr); IMPLEMENT_DYNAMIC_ARG(Package*, clipboard_package, nullptr);
Package::Package() Package::Package()
: zipStream (nullptr) : zipStream (nullptr)
@@ -97,7 +98,8 @@ void Package::save(bool remove_unused) {
saveAs(filename, remove_unused); saveAs(filename, remove_unused);
} }
void Package::saveAs(const String& name, bool remove_unused, bool as_directory) { void Package::saveAs(const String& name, bool remove_unused, bool as_directory) {
if (Set* s = dynamic_cast<Set*>(this)) s->referenceActionStackFiles();
// type of package // type of package
if (wxDirExists(name) || as_directory) { if (wxDirExists(name) || as_directory) {
saveToDirectory(name, remove_unused, false); saveToDirectory(name, remove_unused, false);
@@ -109,7 +111,8 @@ void Package::saveAs(const String& name, bool remove_unused, bool as_directory)
reopen(); reopen();
} }
void Package::saveCopy(const String& name) { void Package::saveCopy(const String& name) {
if (Set* s = dynamic_cast<Set*>(this)) s->referenceActionStackFiles();
saveToZipfile(name, true, true); saveToZipfile(name, true, true);
clearKeepFlag(); clearKeepFlag();
} }
@@ -298,7 +301,7 @@ LocalFileName Package::newFileName(const String& prefix, const String& suffix) {
void Package::referenceFile(const String& file) { void Package::referenceFile(const String& file) {
if (file.empty()) return; if (file.empty()) return;
FileInfos::iterator it = files.find(file); FileInfos::iterator it = files.find(file);
if (it == files.end()) throw InternalError(_("referencing a nonexistant file")); if (it == files.end()) throw InternalError(_("Referencing an inexistant file!"));
it->second.keep = true; it->second.keep = true;
} }