mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-12 10:21:17 +00:00
Ask Before Migrating User Filament Presets to Their Legacy Printers
Instead of giving user presets their parent's 2.4.2 printers in memory, detect the user presets that inherit their parent's compatible_printers and are missing printers from the legacy map, and offer to migrate them at startup or from Help > Migrate Filament Presets. Migrating first backs up the user folder to filament_preset_migration_<datetime>_backup.zip in the configuration folder, and migrates nothing if that fails. It then writes the printers into each preset's compatible_printers, patching only that key so the preset keeps the parent name it was saved with and older releases can still resolve it. Cloud-synced presets are marked for upload. No asks again at the next launch; "Don't remind me again" stops the startup prompt.
This commit is contained in:
@@ -309,6 +309,7 @@ src/slic3r/GUI/BBLStatusBarPrint.cpp
|
||||
src/slic3r/GUI/BBLStatusBarSend.cpp
|
||||
src/slic3r/GUI/BaseTransparentDPIFrame.cpp
|
||||
src/slic3r/GUI/CloneDialog.cpp
|
||||
src/slic3r/GUI/LegacyPrinterMigrationDialog.cpp
|
||||
src/slic3r/GUI/ColorDecomposeDialog.cpp
|
||||
src/slic3r/GUI/ColorDecomposeSupport.cpp
|
||||
src/slic3r/GUI/ConfigWizard.cpp
|
||||
|
||||
@@ -11,7 +11,7 @@ commands:
|
||||
normalize rewrite profile files into their canonical shape
|
||||
trim delete profile files no <vendor>.json list references
|
||||
update-index regenerate the *_list sections of <vendor>.json
|
||||
legacy-compat write the printers user presets keep from an older release
|
||||
legacy-compat write the printers user presets can be migrated back to
|
||||
|
||||
options shared by several commands:
|
||||
--vendor VENDOR act on one vendor bundle only; repeatable, empty means all
|
||||
@@ -27,7 +27,7 @@ normalize supplies missing types; update-index registers presets before id
|
||||
generation.
|
||||
After renaming filament presets or narrowing their compatible_printers, also
|
||||
rerun legacy-compat --base <2.4.2 profiles> so user presets made against 2.4.2
|
||||
keep their printers.
|
||||
can be migrated back to their printers.
|
||||
Use trim only for deliberate cleanup, previewed with --dry-run: it judges against
|
||||
the current index and can delete newly added, unindexed presets.
|
||||
|
||||
@@ -3136,12 +3136,12 @@ def legacy_compatible_printers(base_dir, profiles_dir=PROFILES_DIR):
|
||||
User presets store only their diff, so they inherit their parent's
|
||||
compatible_printers; when a profile change narrows that list, a user preset
|
||||
made against the base release disappears from printers it was used on. The
|
||||
loader adds these printers back to such user presets. Requiring a same-id
|
||||
app offers to write these printers into such user presets. Requiring a same-id
|
||||
successor leaves out printers a filament was deliberately taken off, and
|
||||
requiring a shared extruder variant keeps a printer from picking up another
|
||||
variant's values.
|
||||
|
||||
Keyed by the current preset name: the loader resolves a renamed parent
|
||||
Keyed by the current preset name: the app resolves a renamed parent
|
||||
before the map is consulted. Printer names are translated through the
|
||||
printers' renamed_from. Returns {preset name: sorted printer names}.
|
||||
"""
|
||||
@@ -3232,7 +3232,7 @@ normalize supplies missing types; update-index registers presets before id
|
||||
generation.
|
||||
After renaming filament presets or narrowing their compatible_printers, also
|
||||
rerun legacy-compat --base <2.4.2 profiles> so user presets made against 2.4.2
|
||||
keep their printers.
|
||||
can be migrated back to their printers.
|
||||
Use trim only for deliberate cleanup, previewed with --dry-run: it judges against
|
||||
the current index and can delete newly added, unindexed presets.
|
||||
"""
|
||||
|
||||
+64
-20
@@ -81,6 +81,7 @@
|
||||
#include "LifecycleEvents.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "InstanceLock.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include "PlaceholderParser.hpp"
|
||||
@@ -932,14 +933,10 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const std::vector<std::string> &legacy = preset.preset.legacy_compatible_printers;
|
||||
auto is_legacy_printer = [&legacy](const std::string &name) { return std::find(legacy.begin(), legacy.end(), name) != legacy.end(); };
|
||||
return preset.preset.is_default || active_printer.preset.name.empty() || !has_compatible_printers ||
|
||||
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.preset.name) !=
|
||||
compatible_printers->values.end() ||
|
||||
(!active_printer.preset.is_system && is_compatible_with_parent_printer(preset, active_printer)) ||
|
||||
is_legacy_printer(active_printer.preset.name) ||
|
||||
(!active_printer.preset.is_system && is_legacy_printer(active_printer.preset.inherits()));
|
||||
(!active_printer.preset.is_system && is_compatible_with_parent_printer(preset, active_printer));
|
||||
}
|
||||
|
||||
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer)
|
||||
@@ -2745,27 +2742,80 @@ const Preset *PresetCollection::system_parent_with_inherited_printers(const Pres
|
||||
return own != nullptr && inherited != nullptr && own->values == inherited->values ? parent : nullptr;
|
||||
}
|
||||
|
||||
void PresetCollection::set_legacy_compatible_printers(const std::map<std::string, std::vector<std::string>> &printers_by_parent)
|
||||
std::vector<LegacyPrinterMigration> PresetCollection::legacy_printer_migrations(const std::map<std::string, std::vector<std::string>> &printers_by_parent) const
|
||||
{
|
||||
lock();
|
||||
for (Preset &preset : m_presets) {
|
||||
preset.legacy_compatible_printers.clear();
|
||||
std::vector<LegacyPrinterMigration> migrations;
|
||||
for (const Preset &preset : m_presets) {
|
||||
const Preset *parent = system_parent_with_inherited_printers(preset);
|
||||
if (parent == nullptr)
|
||||
continue;
|
||||
// An empty list already covers every printer.
|
||||
const std::vector<std::string> &own = preset.config.option<ConfigOptionStrings>("compatible_printers")->values;
|
||||
if (own.empty())
|
||||
continue;
|
||||
// The map names the parent as it was when the map was written; a later rename keeps that name in renamed_from.
|
||||
auto it = printers_by_parent.find(parent->name);
|
||||
for (auto old_name = parent->renamed_from.begin(); it == printers_by_parent.end() && old_name != parent->renamed_from.end(); ++old_name)
|
||||
it = printers_by_parent.find(*old_name);
|
||||
if (it == printers_by_parent.end())
|
||||
continue;
|
||||
preset.legacy_compatible_printers = it->second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": user preset " << preset.name << " keeps " << it->second.size()
|
||||
<< " printer(s) its parent " << parent->name << " listed in an older release";
|
||||
LegacyPrinterMigration migration{preset.name, parent->name, {}};
|
||||
for (const std::string &printer : it->second)
|
||||
if (std::find(own.begin(), own.end(), printer) == own.end())
|
||||
migration.printers.push_back(printer);
|
||||
if (!migration.printers.empty())
|
||||
migrations.push_back(std::move(migration));
|
||||
}
|
||||
if (m_idx_selected < m_presets.size() && m_edited_preset.name == m_presets[m_idx_selected].name)
|
||||
m_edited_preset.legacy_compatible_printers = m_presets[m_idx_selected].legacy_compatible_printers;
|
||||
return migrations;
|
||||
}
|
||||
|
||||
bool PresetCollection::apply_legacy_printer_migration(const LegacyPrinterMigration &migration)
|
||||
{
|
||||
lock();
|
||||
auto it = this->find_preset_internal(migration.preset);
|
||||
if (it == m_presets.end() || it->name != migration.preset || system_parent_with_inherited_printers(*it) == nullptr) {
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
Preset &preset = *it;
|
||||
std::vector<std::string> printers = preset.config.option<ConfigOptionStrings>("compatible_printers")->values;
|
||||
for (const std::string &printer : migration.printers)
|
||||
if (std::find(printers.begin(), printers.end(), printer) == printers.end())
|
||||
printers.push_back(printer);
|
||||
const bool selected = m_idx_selected == static_cast<size_t>(it - m_presets.begin());
|
||||
unlock();
|
||||
|
||||
// Only compatible_printers changes in the file. Saving the whole preset would also rewrite an inherits that
|
||||
// names a renamed parent by its old name, the only name older releases know it by.
|
||||
{
|
||||
InstanceLock instance_lock(user_presets_lock_path());
|
||||
try {
|
||||
boost::nowide::ifstream in(preset.file);
|
||||
json j = json::parse(in);
|
||||
j["compatible_printers"] = printers;
|
||||
if (const std::error_code ec = write_file_atomically(preset.file, j.dump(1, '\t'))) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to save " << preset.file << ": " << ec.message();
|
||||
return false;
|
||||
}
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": can not migrate " << preset.file << ": " << err.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lock();
|
||||
preset.config.option<ConfigOptionStrings>("compatible_printers")->values = printers;
|
||||
if (selected)
|
||||
m_edited_preset.config.option<ConfigOptionStrings>("compatible_printers", true)->values = printers;
|
||||
unlock();
|
||||
// Push the change to the cloud copy, as an edit in the settings tab would.
|
||||
if (!preset.setting_id.empty()) {
|
||||
preset.sync_info = "update";
|
||||
preset.save_info();
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": user preset " << preset.name << " gets " << migration.printers.size()
|
||||
<< " printer(s) its parent listed in an older release";
|
||||
return true;
|
||||
}
|
||||
|
||||
//BBS: validate_preset
|
||||
@@ -3121,7 +3171,6 @@ bool PresetCollection::clone_presets(std::vector<Preset const *> const &presets,
|
||||
preset.vendor = nullptr;
|
||||
preset.renamed_from.clear();
|
||||
preset.m_excluded_from.clear();
|
||||
preset.legacy_compatible_printers.clear();
|
||||
preset.setting_id.clear();
|
||||
preset.inherits().clear();
|
||||
preset.is_default = false;
|
||||
@@ -3259,9 +3308,6 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
|
||||
preset.m_excluded_from.clear();
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": save preset %1% , with detach")%new_name;
|
||||
}
|
||||
// Legacy printers belong only to a preset that still inherits its system parent's printer list.
|
||||
if (system_parent_with_inherited_printers(preset) == nullptr)
|
||||
preset.legacy_compatible_printers.clear();
|
||||
//BBS: add lock logic for sync preset in background
|
||||
|
||||
if (m_type == Preset::TYPE_PRINT)
|
||||
@@ -3314,8 +3360,6 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
|
||||
}
|
||||
else
|
||||
preset.is_project_embedded = false;
|
||||
if (system_parent_with_inherited_printers(preset) == nullptr)
|
||||
preset.legacy_compatible_printers.clear();
|
||||
if (m_type == Preset::TYPE_PRINT)
|
||||
preset.config.option<ConfigOptionString>("print_settings_id", true)->value = new_name;
|
||||
else if (m_type == Preset::TYPE_FILAMENT)
|
||||
|
||||
@@ -311,10 +311,6 @@ public:
|
||||
// certain printer models defined in the vendor profile as well, in this case we want to hide this generic preset for these printer models.
|
||||
std::set<std::string> m_excluded_from;
|
||||
|
||||
// Printers a user filament preset is also compatible with because its system parent listed them in
|
||||
// an older release; see PresetCollection::set_legacy_compatible_printers().
|
||||
std::vector<std::string> legacy_compatible_printers;
|
||||
|
||||
// Orca: flag to indicate if this preset is from Orca Filament Library
|
||||
bool m_from_orca_filament_lib = false;
|
||||
|
||||
@@ -555,6 +551,14 @@ struct PresetConfigSubstitutions {
|
||||
// PrusaSlicer and reading the user Print / Filament / Printer profiles.
|
||||
using PresetsConfigSubstitutions = std::vector<PresetConfigSubstitutions>;
|
||||
|
||||
// A user filament preset that no longer shows on printers its system parent listed in an older release,
|
||||
// and those printers. Migrating it writes them into the preset's own compatible_printers.
|
||||
struct LegacyPrinterMigration {
|
||||
std::string preset;
|
||||
std::string parent;
|
||||
std::vector<std::string> printers;
|
||||
};
|
||||
|
||||
// Collections of presets of the same type (one of the Print, Filament or Printer type).
|
||||
class PresetCollection
|
||||
{
|
||||
@@ -606,10 +610,13 @@ public:
|
||||
bool load_user_preset(std::string name, std::map<std::string, std::string> preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, const PresetOrigin &load_origin = PresetOrigin(PresetOrigin::Kind::User));
|
||||
void update_after_user_presets_loaded();
|
||||
// User presets store only their diff, so a user preset inherits its system parent's compatible_printers
|
||||
// and disappears from a printer once a profile update narrows that list. Give each user preset that
|
||||
// inherits the list the printers its parent covered in an older release (printers_by_parent, keyed by the
|
||||
// parent's current name), so it keeps showing on the printers it was made for.
|
||||
void set_legacy_compatible_printers(const std::map<std::string, std::vector<std::string>> &printers_by_parent);
|
||||
// and disappears from a printer once a profile update narrows that list. Lists the user presets that
|
||||
// inherit the list and the printers their parent covered in an older release (printers_by_parent, keyed
|
||||
// by the parent's name then) but no longer lists.
|
||||
std::vector<LegacyPrinterMigration> legacy_printer_migrations(const std::map<std::string, std::vector<std::string>> &printers_by_parent) const;
|
||||
// Adds the migration's printers to the preset's own compatible_printers and saves the preset to disk.
|
||||
// False if the preset no longer inherits its parent's list or could not be saved.
|
||||
bool apply_legacy_printer_migration(const LegacyPrinterMigration &migration);
|
||||
//BBS: get user presets
|
||||
int get_user_presets(PresetBundle *preset_bundle, std::vector<Preset> &result_presets);
|
||||
void set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time, const std::string& user_id);
|
||||
@@ -903,7 +910,7 @@ public:
|
||||
|
||||
protected:
|
||||
// The system parent of a user preset whose compatible_printers is the parent's, or nullptr: the presets
|
||||
// set_legacy_compatible_printers() gives legacy printers to.
|
||||
// legacy_printer_migrations() considers.
|
||||
const Preset* system_parent_with_inherited_printers(const Preset &preset) const;
|
||||
|
||||
PresetCollection() = default;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/filesystem/directory.hpp>
|
||||
#include <boost/filesystem/file_status.hpp>
|
||||
#include <boost/uuid/uuid.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
@@ -39,6 +40,8 @@
|
||||
#include "ParallelResolve.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "PresetCacheFormat.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "Zipper.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "PublishSettings.hpp"
|
||||
#include "FilamentMixer.hpp"
|
||||
@@ -918,7 +921,6 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
||||
// raw vendor-JSON references instead of the silently-repaired ones.
|
||||
if (!validation_mode)
|
||||
this->normalize_compatible_presets();
|
||||
filaments.set_legacy_compatible_printers(legacy_compatible_printers());
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
@@ -1075,6 +1077,55 @@ bool PresetBundle::backup_user_folder() const
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetBundle::backup_user_folder_to_zip(const std::string &zip_path) const
|
||||
{
|
||||
const fs::path user_dir = fs::path(data_dir()) / PRESET_USER_DIR;
|
||||
BOOST_LOG_TRIVIAL(info) << "Backing up user folder to: " << zip_path;
|
||||
try {
|
||||
Zipper zipper(zip_path);
|
||||
// Directories get their own entries, which extract_archive_confined() needs to recreate them.
|
||||
zipper.add_entry(std::string(PRESET_USER_DIR) + "/", nullptr, 0);
|
||||
for (const fs::directory_entry &entry : fs::recursive_directory_iterator(user_dir)) {
|
||||
const std::string name = (fs::path(PRESET_USER_DIR) / entry.path().lexically_relative(user_dir)).generic_string();
|
||||
if (fs::is_directory(entry.status())) {
|
||||
zipper.add_entry(name + "/", nullptr, 0);
|
||||
continue;
|
||||
}
|
||||
if (!fs::is_regular_file(entry.status()))
|
||||
continue;
|
||||
boost::nowide::ifstream in(entry.path().string(), std::ios::binary);
|
||||
const std::string data((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
if (!in.good() && !in.eof())
|
||||
throw Slic3r::RuntimeError("can not read " + entry.path().string());
|
||||
zipper.add_entry(name, data.data(), data.size());
|
||||
}
|
||||
zipper.finalize();
|
||||
return true;
|
||||
} catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Exception during user folder backup: " << ex.what();
|
||||
boost::system::error_code ec;
|
||||
fs::remove(zip_path, ec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LegacyPrinterMigration> PresetBundle::legacy_printer_migrations() const
|
||||
{
|
||||
return filaments.legacy_printer_migrations(legacy_compatible_printers());
|
||||
}
|
||||
|
||||
std::string PresetBundle::migrate_legacy_printers(const std::vector<LegacyPrinterMigration> &migrations, std::vector<std::string> &failed)
|
||||
{
|
||||
const std::string zip_path = data_dir() + "/filament_preset_migration_" +
|
||||
Utils::time2str(Utils::TimeZone::utc, Utils::TimeFormat::iso8601Z) + "_backup.zip";
|
||||
if (!backup_user_folder_to_zip(zip_path))
|
||||
return {};
|
||||
for (const LegacyPrinterMigration &migration : migrations)
|
||||
if (!filaments.apply_legacy_printer_migration(migration))
|
||||
failed.push_back(migration.preset);
|
||||
return zip_path;
|
||||
}
|
||||
|
||||
std::optional<FilamentBaseInfo> PresetBundle::get_filament_by_filament_id(const std::string& filament_id, const std::string& printer_name) const
|
||||
{
|
||||
if (filament_id.empty())
|
||||
@@ -1547,7 +1598,6 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &
|
||||
|
||||
// Rewrite renamed compatible references in synced user presets before compatibility is evaluated.
|
||||
this->normalize_compatible_presets();
|
||||
filaments.set_legacy_compatible_printers(legacy_compatible_printers());
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
@@ -2554,7 +2604,6 @@ PresetsConfigSubstitutions PresetBundle::update_subscribed_presets(
|
||||
|
||||
// Rewrite renamed compatible references in synced user presets before compatibility is evaluated.
|
||||
this->normalize_compatible_presets();
|
||||
filaments.set_legacy_compatible_printers(legacy_compatible_printers());
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
|
||||
@@ -376,6 +376,16 @@ public:
|
||||
bool use_bbl_device_tab();
|
||||
|
||||
bool backup_user_folder() const;
|
||||
// Writes the user preset folder into a zip archive at zip_path, its entries under "user/".
|
||||
bool backup_user_folder_to_zip(const std::string &zip_path) const;
|
||||
|
||||
// User filament presets that no longer show on printers their parent listed in an older release
|
||||
// (resources/info/legacy_compatible_printers.json).
|
||||
std::vector<LegacyPrinterMigration> legacy_printer_migrations() const;
|
||||
// Backs up the user preset folder into a zip in data_dir(), then writes each migration's printers into its
|
||||
// preset file. Returns the zip's path, or an empty string when the backup failed and nothing was migrated.
|
||||
// The presets that could not be migrated are added to failed.
|
||||
std::string migrate_legacy_printers(const std::vector<LegacyPrinterMigration> &migrations, std::vector<std::string> &failed);
|
||||
|
||||
//BBS: project embedded preset logic
|
||||
PresetsConfigSubstitutions load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule);
|
||||
|
||||
@@ -362,6 +362,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Lazy.hpp
|
||||
GUI/LazyPage.cpp
|
||||
GUI/LazyPage.hpp
|
||||
GUI/LegacyPrinterMigrationDialog.cpp
|
||||
GUI/LegacyPrinterMigrationDialog.hpp
|
||||
GUI/LibVGCode/LibVGCodeWrapper.hpp
|
||||
GUI/LibVGCode/LibVGCodeWrapper.cpp
|
||||
GUI/LinuxDisplayBackend.cpp
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "DeviceManager.hpp"
|
||||
#include "HMS.hpp"
|
||||
#include "PresetBundleDialog.hpp"
|
||||
#include "LegacyPrinterMigrationDialog.hpp"
|
||||
#include "WebUserLoginDialog.hpp"
|
||||
#include "WebViewDialog.hpp"
|
||||
#include "slic3r/Utils/BBLCloudServiceAgent.hpp"
|
||||
@@ -1109,6 +1110,8 @@ void GUI_App::post_init()
|
||||
}
|
||||
|
||||
this->check_new_version_sf();
|
||||
if (!cw_showed)
|
||||
this->check_legacy_printer_migration(false);
|
||||
const auto cloud_provider = get_printer_cloud_provider();
|
||||
if (is_user_login(cloud_provider) && !app_config->get_stealth_mode()) {
|
||||
// this->check_privacy_version(0);
|
||||
@@ -6822,6 +6825,50 @@ void GUI_App::reload_settings()
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_App::check_legacy_printer_migration(bool by_user)
|
||||
{
|
||||
const std::vector<LegacyPrinterMigration> migrations = preset_bundle->legacy_printer_migrations();
|
||||
if (migrations.empty()) {
|
||||
if (by_user)
|
||||
MessageDialog(mainframe, _L("All your filament presets are available on the printers they were made for."),
|
||||
_L("Migrate Filament Presets"), wxOK | wxICON_INFORMATION).ShowModal();
|
||||
return;
|
||||
}
|
||||
if (!by_user && app_config->get_bool("legacy_printer_migration_dont_remind"))
|
||||
return;
|
||||
|
||||
LegacyPrinterMigrationDialog dlg(mainframe, migrations, !by_user);
|
||||
const bool migrate = dlg.ShowModal() == wxID_YES;
|
||||
if (dlg.dont_remind())
|
||||
app_config->set_bool("legacy_printer_migration_dont_remind", true);
|
||||
if (!migrate)
|
||||
return;
|
||||
|
||||
std::vector<std::string> failed;
|
||||
const std::string backup = preset_bundle->migrate_legacy_printers(migrations, failed);
|
||||
if (backup.empty()) {
|
||||
show_error(mainframe, _L("Your presets could not be backed up, so they were not migrated."));
|
||||
return;
|
||||
}
|
||||
preset_bundle->update_compatible(PresetSelectCompatibleType::Never);
|
||||
mainframe->update_side_preset_ui();
|
||||
for (auto tab : tabs_list) {
|
||||
tab->reload_config();
|
||||
tab->update_changed_ui();
|
||||
}
|
||||
if (plater_)
|
||||
plater_->sidebar().update_all_preset_comboboxes();
|
||||
|
||||
wxString message = format_wxstr(_L("Your filament presets were migrated. Select them again in the filament list where you used them.\n\n"
|
||||
"A backup of your presets was saved to:\n%1%"), backup);
|
||||
if (!failed.empty()) {
|
||||
message += "\n\n" + _L("These presets could not be migrated:");
|
||||
for (const std::string &name : failed)
|
||||
message += "\n" + from_u8(name);
|
||||
}
|
||||
MessageDialog(mainframe, message, _L("Migrate Filament Presets"), wxOK | (failed.empty() ? wxICON_INFORMATION : wxICON_WARNING)).ShowModal();
|
||||
}
|
||||
|
||||
//BBS reload when logout
|
||||
void GUI_App::remove_user_presets()
|
||||
{
|
||||
|
||||
@@ -568,6 +568,10 @@ public:
|
||||
void check_update(bool show_tips, int by_user);
|
||||
void check_new_version(bool show_tips = false, int by_user = 0);
|
||||
void check_new_version_sf(bool show_tips = false, int by_user = 0);
|
||||
// Offers to migrate user filament presets that lost printers their parent listed in an older release.
|
||||
// At startup it asks once per release unless the user chose not to be reminded; by_user also reports
|
||||
// when there is nothing to migrate.
|
||||
void check_legacy_printer_migration(bool by_user);
|
||||
bool process_network_msg(std::string dev_id, std::string msg);
|
||||
void request_new_version(int by_user);
|
||||
void enter_force_upgrade();
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "LegacyPrinterMigrationDialog.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/DialogButtons.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/toplevel.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
LegacyPrinterMigrationDialog::LegacyPrinterMigrationDialog(wxWindow *parent, const std::vector<LegacyPrinterMigration> &migrations,
|
||||
bool offer_dont_remind)
|
||||
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Migrate Filament Presets"),
|
||||
wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetFont(Label::Body_14);
|
||||
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto *message = new Label(this,
|
||||
_L("Since OrcaSlicer 2.5.0, filament presets have been restructured to support more features. Some of your "
|
||||
"presets need to be migrated to the new structure, or they will no longer be available for some of your "
|
||||
"printers.\n\n"
|
||||
"Would you like to migrate them now? Your presets will be backed up first, and this only needs to be done "
|
||||
"once. You can also do it later from Help > Migrate Filament Presets."),
|
||||
0);
|
||||
message->Wrap(FromDIP(520));
|
||||
sizer->Add(message, 0, wxEXPAND | wxALL, FromDIP(15));
|
||||
|
||||
sizer->Add(new Label(this, _L("Affected presets:")), 0, wxLEFT | wxRIGHT, FromDIP(15));
|
||||
sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
wxString details;
|
||||
for (const LegacyPrinterMigration &migration : migrations) {
|
||||
details += format_wxstr(_L("%1% (based on %2%)"), migration.preset, migration.parent) + "\n";
|
||||
for (const std::string &printer : migration.printers)
|
||||
details += " " + from_u8(printer) + "\n";
|
||||
}
|
||||
auto *list = new wxTextCtrl(this, wxID_ANY, details, wxDefaultPosition, wxSize(-1, FromDIP(220)),
|
||||
wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP);
|
||||
sizer->Add(list, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(15));
|
||||
|
||||
if (offer_dont_remind) {
|
||||
auto *dont_remind_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_dont_remind = new ::CheckBox(this);
|
||||
auto *dont_remind_label = new Label(this, _L("Don't remind me again"));
|
||||
dont_remind_label->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
|
||||
m_dont_remind->SetValue(!m_dont_remind->GetValue());
|
||||
e.Skip();
|
||||
});
|
||||
dont_remind_sizer->Add(m_dont_remind, 0, wxALIGN_CENTER_VERTICAL);
|
||||
dont_remind_sizer->Add(dont_remind_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6));
|
||||
sizer->Add(dont_remind_sizer, 0, wxLEFT | wxRIGHT | wxTOP, FromDIP(15));
|
||||
}
|
||||
|
||||
auto *buttons = new DialogButtons(this, {"Yes", "No"}, "Yes");
|
||||
buttons->GetYES()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { EndModal(wxID_YES); });
|
||||
buttons->GetNO()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { EndModal(wxID_NO); });
|
||||
sizer->Add(buttons, 0, wxEXPAND);
|
||||
|
||||
SetSizerAndFit(sizer);
|
||||
CenterOnParent();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
bool LegacyPrinterMigrationDialog::dont_remind() const
|
||||
{
|
||||
return m_dont_remind != nullptr && m_dont_remind->GetValue();
|
||||
}
|
||||
|
||||
void LegacyPrinterMigrationDialog::on_dpi_changed(const wxRect &)
|
||||
{
|
||||
if (m_dont_remind != nullptr)
|
||||
m_dont_remind->Rescale();
|
||||
GetSizer()->SetSizeHints(this);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CheckBox;
|
||||
class wxWindow;
|
||||
class wxRect;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct LegacyPrinterMigration;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
// Lists the user filament presets that no longer show on printers their parent listed in an older
|
||||
// release, and asks whether to migrate them. ShowModal() returns wxID_YES to migrate. With
|
||||
// offer_dont_remind it also offers to stop the startup reminder for good.
|
||||
class LegacyPrinterMigrationDialog : public DPIDialog
|
||||
{
|
||||
public:
|
||||
LegacyPrinterMigrationDialog(wxWindow *parent, const std::vector<LegacyPrinterMigration> &migrations, bool offer_dont_remind);
|
||||
|
||||
bool dont_remind() const;
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
private:
|
||||
::CheckBox *m_dont_remind{nullptr};
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -2827,6 +2827,10 @@ wxMenu* MainFrame::generate_help_menu()
|
||||
append_menu_item(helpMenu, wxID_ANY, _L("Show Configuration Folder"), _L("Show Configuration Folder"),
|
||||
[](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
|
||||
|
||||
append_menu_item(helpMenu, wxID_ANY, _L("Migrate Filament Presets"),
|
||||
_L("Bring back filament presets that a profile update removed from printers they were made for"),
|
||||
[](wxCommandEvent&) { wxGetApp().check_legacy_printer_migration(true); });
|
||||
|
||||
helpMenu->AppendSeparator();
|
||||
|
||||
// Troubleshoot center
|
||||
|
||||
@@ -589,6 +589,10 @@ std::vector<NativeCommand> build_command_catalog()
|
||||
Slic3r::GUI::desktop_open_datadir_folder();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_migrate_filament_presets", L("Migrate Filament Presets"), L("Help"), [](const std::string&) {
|
||||
wxGetApp().check_legacy_printer_migration(true);
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
});
|
||||
add("help_troubleshoot", L("Troubleshoot Center"), L("Help"), [](const std::string&) {
|
||||
wxGetApp().troubleshoot();
|
||||
return AppActionRunResult{AppActionRunResult::Level::Success};
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <utility>
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PublishSettings.hpp"
|
||||
#include <set>
|
||||
@@ -7162,7 +7163,7 @@ TEST_CASE("A user filament whose derived compatible printer cannot be written ba
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("A user filament keeps the printers its narrowed system parent listed in an older release", "[Preset][Bundle]")
|
||||
TEST_CASE("User filaments that lost printers their narrowed system parent listed in an older release are found", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
PresetCollection &filaments = bundle.filaments;
|
||||
@@ -7179,25 +7180,14 @@ TEST_CASE("A user filament keeps the printers its narrowed system parent listed
|
||||
set_printers("My PETG", { "Creality K1C 0.6 nozzle" });
|
||||
set_printers("My PETG own list", { "Creality K1C 0.6 nozzle", "Creality K1C 0.8 nozzle" });
|
||||
|
||||
filaments.set_legacy_compatible_printers({ { parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
|
||||
auto compatible = [&filaments](const std::string &filament, const Preset &printer) {
|
||||
return is_compatible_with_printer(PresetWithVendorProfile(*filaments.find_preset(filament), nullptr),
|
||||
PresetWithVendorProfile(printer, nullptr));
|
||||
};
|
||||
Preset k1c_04(Preset::TYPE_PRINTER, "Creality K1C 0.4 nozzle");
|
||||
k1c_04.is_system = true;
|
||||
Preset k1_max(Preset::TYPE_PRINTER, "Creality K1 Max 0.4 nozzle");
|
||||
k1_max.is_system = true;
|
||||
Preset my_k1c(Preset::TYPE_PRINTER, "My K1C");
|
||||
my_k1c.config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS, true)->value = "Creality K1C 0.4 nozzle";
|
||||
|
||||
CHECK(compatible("My PETG", k1c_04));
|
||||
CHECK(compatible("My PETG", my_k1c));
|
||||
CHECK_FALSE(compatible("My PETG", k1_max));
|
||||
// The system preset itself and a user preset with its own list keep exactly their lists.
|
||||
CHECK_FALSE(compatible(parent, k1c_04));
|
||||
CHECK_FALSE(compatible("My PETG own list", k1c_04));
|
||||
// A printer the parent still lists is not reported again. The system preset and a user preset
|
||||
// with its own list are left alone.
|
||||
std::vector<LegacyPrinterMigration> migrations =
|
||||
filaments.legacy_printer_migrations({ { parent, { "Creality K1C 0.4 nozzle", "Creality K1C 0.6 nozzle" } } });
|
||||
REQUIRE(migrations.size() == 1);
|
||||
CHECK(migrations[0].preset == "My PETG");
|
||||
CHECK(migrations[0].parent == parent);
|
||||
CHECK(migrations[0].printers == std::vector<std::string>{ "Creality K1C 0.4 nozzle" });
|
||||
|
||||
// A parent renamed after the map was written is still found under its old name.
|
||||
add_inmemory_preset(filaments, "Generic PETG @Creality K1 renamed").is_system = true;
|
||||
@@ -7205,45 +7195,93 @@ TEST_CASE("A user filament keeps the printers its narrowed system parent listed
|
||||
set_renamed_from(filaments, "Generic PETG @Creality K1 renamed", { parent });
|
||||
set_printers("Generic PETG @Creality K1 renamed", { "Creality K1C 0.6 nozzle" });
|
||||
set_printers("My renamed PETG", { "Creality K1C 0.6 nozzle" });
|
||||
filaments.set_legacy_compatible_printers({ { parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
CHECK(compatible("My renamed PETG", k1c_04));
|
||||
migrations = filaments.legacy_printer_migrations({ { parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(migrations.size() == 2);
|
||||
CHECK(migrations[1].preset == "My renamed PETG");
|
||||
CHECK(migrations[1].parent == "Generic PETG @Creality K1 renamed");
|
||||
}
|
||||
|
||||
TEST_CASE("A saved user filament keeps its legacy printers only while it inherits its parent's printer list", "[Preset][Bundle]")
|
||||
TEST_CASE("Migrating a user filament backs up the user folder to a zip and writes the legacy printers to its file", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
PresetBundle bundle;
|
||||
PresetCollection &filaments = bundle.filaments;
|
||||
ScopedDataDir data(temp_dir.path());
|
||||
const std::string parent = "Generic PETG @Creality K1-all";
|
||||
auto with_parent = [&parent](PresetBundle &bundle) {
|
||||
add_inmemory_preset(bundle.filaments, parent).is_system = true;
|
||||
bundle.filaments.find_preset(parent)->config.option<ConfigOptionStrings>("compatible_printers", true)->values = { "Creality K1C 0.6 nozzle" };
|
||||
};
|
||||
// Like a real user preset, the file states only its diff, so the printer list comes from the parent.
|
||||
const fs::path user_dir = temp_dir.path() / PRESET_USER_DIR / "default";
|
||||
const fs::path file = user_dir / PRESET_FILAMENT_NAME / "My PETG.json";
|
||||
fs::create_directories(file.parent_path());
|
||||
const std::string original = std::string(R"({"type":"filament","name":"My PETG","from":"User","version":"1.0.0","inherits":")") + parent + R"("})";
|
||||
std::ofstream(file.string()) << original;
|
||||
auto load = [&user_dir](PresetCollection &filaments) {
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
filaments.load_presets(user_dir.string(), PRESET_FILAMENT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
};
|
||||
PresetBundle bundle;
|
||||
PresetCollection &filaments = bundle.filaments;
|
||||
with_parent(bundle);
|
||||
load(filaments);
|
||||
const std::map<std::string, std::vector<std::string>> map{ { parent, { "Creality K1C 0.4 nozzle" } } };
|
||||
const std::vector<LegacyPrinterMigration> migrations = filaments.legacy_printer_migrations(map);
|
||||
REQUIRE(migrations.size() == 1);
|
||||
|
||||
std::vector<std::string> failed;
|
||||
const std::string backup = bundle.migrate_legacy_printers(migrations, failed);
|
||||
REQUIRE_FALSE(backup.empty());
|
||||
CHECK(std::regex_match(fs::path(backup).filename().string(), std::regex("filament_preset_migration_\\d{8}T\\d{6}Z_backup\\.zip")));
|
||||
CHECK(failed.empty());
|
||||
const fs::path extracted = temp_dir.path() / "extracted";
|
||||
REQUIRE(extract_archive_confined(backup, extracted.string()));
|
||||
CHECK(read_file(extracted / PRESET_USER_DIR / "default" / PRESET_FILAMENT_NAME / "My PETG.json") == original);
|
||||
|
||||
// Reloaded from disk, the preset carries both printers itself and needs no further migration.
|
||||
PresetBundle reloaded;
|
||||
with_parent(reloaded);
|
||||
load(reloaded.filaments);
|
||||
const std::vector<std::string> expected{ "Creality K1C 0.6 nozzle", "Creality K1C 0.4 nozzle" };
|
||||
CHECK(reloaded.filaments.find_preset("My PETG")->config.option<ConfigOptionStrings>("compatible_printers")->values == expected);
|
||||
CHECK(reloaded.filaments.legacy_printer_migrations(map).empty());
|
||||
CHECK_FALSE(reloaded.filaments.apply_legacy_printer_migration(migrations[0]));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A filament collection that exposes the rename map update, which PresetBundle runs after loading system presets.
|
||||
struct RenameTestFilaments : public PresetCollection
|
||||
{
|
||||
RenameTestFilaments()
|
||||
: PresetCollection(Preset::TYPE_FILAMENT, Preset::filament_options(),
|
||||
static_cast<const PrintRegionConfig &>(FullPrintConfig::defaults()))
|
||||
{}
|
||||
using PresetCollection::update_map_system_profile_renamed;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Migrating a user filament keeps the old name of its renamed parent", "[Preset][Bundle]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
RenameTestFilaments filaments;
|
||||
const std::string parent = "Generic PETG @Creality K1-all";
|
||||
const std::string old_parent = "Creality Generic PETG @K1-all";
|
||||
add_inmemory_preset(filaments, parent).is_system = true;
|
||||
filaments.find_preset(parent)->config.option<ConfigOptionStrings>("compatible_printers", true)->values = { "Creality K1C 0.6 nozzle" };
|
||||
// Like a real user preset, the file states only its diff, so the printer list comes from the parent.
|
||||
fs::create_directories(temp_dir.path() / PRESET_FILAMENT_NAME);
|
||||
std::ofstream((temp_dir.path() / PRESET_FILAMENT_NAME / "My PETG.json").string())
|
||||
<< R"({"type":"filament","name":"My PETG","from":"User","version":"1.0.0","inherits":")" << parent << R"("})";
|
||||
set_renamed_from(filaments, parent, { old_parent });
|
||||
filaments.update_map_system_profile_renamed();
|
||||
const fs::path file = temp_dir.path() / PRESET_FILAMENT_NAME / "My PETG.json";
|
||||
fs::create_directories(file.parent_path());
|
||||
std::ofstream(file.string()) << R"({"type":"filament","name":"My PETG","from":"User","version":"1.0.0","inherits":")" << old_parent << R"("})";
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
filaments.load_presets(temp_dir.path().string(), PRESET_FILAMENT_NAME, substitutions,
|
||||
ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
filaments.set_legacy_compatible_printers({ { parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
const std::vector<std::string> legacy { "Creality K1C 0.4 nozzle" };
|
||||
REQUIRE(filaments.find_preset("My PETG")->legacy_compatible_printers == legacy);
|
||||
filaments.load_presets(temp_dir.path().string(), PRESET_FILAMENT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
|
||||
auto save_from_my_petg = [&filaments](const std::string &name, bool detach) {
|
||||
filaments.select_preset_by_name("My PETG", true);
|
||||
filaments.save_current_preset(name, detach);
|
||||
return filaments.find_preset(name)->legacy_compatible_printers;
|
||||
};
|
||||
CHECK(save_from_my_petg("My PETG copy", false) == legacy);
|
||||
CHECK(save_from_my_petg("My PETG detached", true).empty());
|
||||
|
||||
// A clone is detached from the parent, as "Create filament" makes for another printer.
|
||||
std::vector<std::string> failures;
|
||||
REQUIRE(filaments.clone_presets({ filaments.find_preset("My PETG") }, failures,
|
||||
[](Preset &preset, Preset::Type &) { preset.name = "My PETG clone"; }));
|
||||
CHECK(filaments.find_preset("My PETG clone")->legacy_compatible_printers.empty());
|
||||
|
||||
filaments.select_preset_by_name("My PETG", true);
|
||||
filaments.get_edited_preset().config.option<ConfigOptionStrings>("compatible_printers")->values = { "Creality K1C 0.8 nozzle" };
|
||||
filaments.save_current_preset("My PETG", false);
|
||||
CHECK(filaments.find_preset("My PETG")->legacy_compatible_printers.empty());
|
||||
const std::vector<LegacyPrinterMigration> migrations = filaments.legacy_printer_migrations({ { parent, { "Creality K1C 0.4 nozzle" } } });
|
||||
REQUIRE(migrations.size() == 1);
|
||||
REQUIRE(filaments.apply_legacy_printer_migration(migrations[0]));
|
||||
// An older release knows the parent only by its old name.
|
||||
const std::string saved = read_file(file);
|
||||
CHECK(saved.find("\"inherits\": \"" + old_parent + "\"") != std::string::npos);
|
||||
CHECK(saved.find("Creality K1C 0.4 nozzle") != std::string::npos);
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ TEST_CASE("Native command catalog includes the Help and developer-mode commands"
|
||||
|
||||
const Slic3r::GUI::NativeCommand* first = find("help_keyboard_shortcuts");
|
||||
REQUIRE(first != nullptr);
|
||||
for (const char* key : {"help_setup_wizard", "help_open_config_folder", "help_troubleshoot", "help_network_test",
|
||||
for (const char* key : {"help_setup_wizard", "help_open_config_folder", "help_migrate_filament_presets", "help_troubleshoot", "help_network_test",
|
||||
"help_benchmark_3d_scene", "help_tip_of_the_day", "help_check_updates", "help_about", "open_wiki",
|
||||
"open_youtube"}) {
|
||||
const Slic3r::GUI::NativeCommand* c = find(key);
|
||||
|
||||
Reference in New Issue
Block a user