diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 2f3c37e3c1..cd58e90e97 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -41,8 +41,6 @@ #include #include #include -#include -#include #include "libslic3r.h" #include "Utils.hpp" @@ -54,6 +52,78 @@ using boost::property_tree::ptree; namespace Slic3r { +namespace { + +struct ParsedName { + PresetOrigin::Kind kind { PresetOrigin::Kind::User }; + std::string bundle_id; + std::string bare; +}; + +// Canonical names are built in-memory with '/' separators, so a straight prefix+split match is enough. +static ParsedName parse_preset_name(const std::string &raw_name) +{ + ParsedName out; + + auto try_prefix = [&](const char *dir, PresetOrigin::Kind kind) { + const std::string prefix = std::string(dir) + "/"; + if (! boost::starts_with(raw_name, prefix)) + return false; + const size_t id_start = prefix.size(); + const size_t id_end = raw_name.find('/', id_start); + if (id_end == std::string::npos || id_end == id_start) + return false; + out.kind = kind; + out.bundle_id = raw_name.substr(id_start, id_end - id_start); + out.bare = raw_name.substr(id_end + 1); + return true; + }; + + if (! try_prefix(PRESET_LOCAL_DIR, PresetOrigin::Kind::LocalBundle) && + ! try_prefix(PRESET_SUBSCRIBED_DIR, PresetOrigin::Kind::SubscribedBundle)) + out.bare = raw_name; + + return out; +} + +} // namespace + +std::string get_preset_canonical_name(const std::string &preset_bare_name, const PresetOrigin &origin) +{ + switch (origin.kind) { + case PresetOrigin::Kind::LocalBundle: + return origin.bundle_id.empty() ? preset_bare_name : std::string(PRESET_LOCAL_DIR) + "/" + origin.bundle_id + "/" + preset_bare_name; + case PresetOrigin::Kind::SubscribedBundle: + return origin.bundle_id.empty() ? preset_bare_name : std::string(PRESET_SUBSCRIBED_DIR) + "/" + origin.bundle_id + "/" + preset_bare_name; + default: + return preset_bare_name; + } +} + +std::string get_preset_bare_name(const std::string &canonical_name) +{ + const auto pos = canonical_name.find_last_of('/'); + return pos == std::string::npos ? canonical_name : canonical_name.substr(pos + 1); +} + +PresetOrigin detect_origin_from_path(const boost::filesystem::path &path, const PresetOrigin &explicit_origin) +{ + if (explicit_origin.kind != PresetOrigin::Kind::Auto) + return explicit_origin; + + for (auto it = path.begin(); it != path.end(); ++ it) { + const auto next = std::next(it); + if (next == path.end()) + break; + const std::string segment = it->string(); + if (segment == PRESET_LOCAL_DIR) + return PresetOrigin(PresetOrigin::Kind::LocalBundle, next->string()); + if (segment == PRESET_SUBSCRIBED_DIR) + return PresetOrigin(PresetOrigin::Kind::SubscribedBundle, next->string()); + } + return PresetOrigin(PresetOrigin::Kind::User); +} + //BBS: add a function to load the version from xxx.json Semver get_version_from_json(std::string file_path) { @@ -511,20 +581,12 @@ void Preset::load_info(const std::string& file) catch (...) { return; } - - //TODO: workaround for current info file convert, will remove it later - if (this->updated_time == 0) { - this->updated_time = (long long)Slic3r::Utils::get_current_time_utc(); - //this->sync_info = "update"; - BOOST_LOG_TRIVIAL(info) << boost::format("old info file, updated time to %1%") % this->updated_time; - save_info(); - } } void Preset::save_info(std::string file) { //BBS: add project embedded preset logic - if (this->is_project_embedded) + if (this->is_project_embedded || this->is_from_bundle()) return; if (file.empty()) { fs::path idx_file(this->file); @@ -546,17 +608,26 @@ void Preset::save_info(std::string file) c.close(); } -void Preset::remove_files() +void Preset::remove_files(bool cloud_already_deleted) { //BBS: add project embedded preset logic - if (this->is_project_embedded) + if (this->is_project_embedded) { return; + } // Erase the preset file. boost::nowide::remove(this->file.c_str()); fs::path idx_path(this->file); idx_path.replace_extension(".info"); - if (fs::exists(idx_path)) - boost::nowide::remove(idx_path.string().c_str()); + if (fs::exists(idx_path)) { + if (!this->setting_id.empty() && !cloud_already_deleted) { + // Cloud-synced preset - mark for deletion and keep .info file until sync confirms + this->sync_info = "delete"; + this->save_info(idx_path.string()); + } else { + // Local-only preset or cloud already confirmed deletion - safe to delete .info immediately + boost::nowide::remove(idx_path.string().c_str()); + } + } } //BBS: add logic for only difference save @@ -572,12 +643,15 @@ void Preset::save(DynamicPrintConfig* parent_config) from_str = std::string("User"); else if (this->is_project_embedded) from_str = std::string("Project"); + else if (this->is_from_bundle()) + from_str = std::string("Bundle"); else if (this->is_system) from_str = std::string("System"); else from_str = std::string("Default"); boost::filesystem::create_directories(fs::path(this->file).parent_path()); + const std::string bare_name = get_preset_bare_name(this->name); //BBS: only save difference if it has parent if (parent_config) { @@ -617,24 +691,22 @@ void Preset::save(DynamicPrintConfig* parent_config) opt_dst->set(opt_src); } } - - if (!filament_id.empty()) { - temp_config.set_key_value(BBL_JSON_KEY_FILAMENT_ID, new ConfigOptionString(filament_id)); - } - - temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string()); + temp_config.save_to_json(this->file, bare_name, from_str, this->version.to_string()); } else if (!filament_id.empty() && inherits().empty()) { DynamicPrintConfig temp_config = config; temp_config.set_key_value(BBL_JSON_KEY_FILAMENT_ID, new ConfigOptionString(filament_id)); - temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string()); + temp_config.save_to_json(this->file, bare_name, from_str, this->version.to_string()); } else { - this->config.save_to_json(this->file, this->name, from_str, this->version.to_string()); + this->config.save_to_json(this->file, bare_name, from_str, this->version.to_string()); } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " save config for: " << this->name << " and filament_id: " << filament_id << " and base_id: " << this->base_id; - fs::path idx_file(this->file); - idx_file.replace_extension(".info"); - this->save_info(idx_file.string()); + // Bundle presets are synced via bundle_id and don't need individual .info files. + if (! this->is_from_bundle()) { + fs::path idx_file(this->file); + idx_file.replace_extension(".info"); + this->save_info(idx_file.string()); + } } void Preset::reload(Preset const &parent) @@ -710,7 +782,8 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre if (preset.vendor != nullptr && preset.preset.type == Preset::TYPE_FILAMENT) { const auto& excluded_printers = preset.preset.m_excluded_from; const auto excluded = preset.vendor->name == PresetBundle::ORCA_FILAMENT_LIBRARY && - excluded_printers.find(active_printer.preset.name) != excluded_printers.end(); + (excluded_printers.find(active_printer.preset.name) != excluded_printers.end() || + excluded_printers.find(active_printer.preset.inherits()) != excluded_printers.end()); if (excluded) return false; } @@ -894,74 +967,300 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle) return false; } -static std::vector s_Preset_print_options { - "layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode", - "top_shell_layers", "top_shell_thickness", "top_surface_density", "bottom_surface_density", "bottom_shell_layers", "bottom_shell_thickness", - "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction", - "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density","fill_multiline", "sparse_infill_pattern", "lateral_lattice_angle_1", "lateral_lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", - "infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density", - "align_infill_direction_to_model", "extra_solid_infills", - "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target", - "ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_angle_fixed", "ironing_inset", - "support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing", +static std::vector s_Preset_print_options{ + "layer_height", + "initial_layer_print_height", + "wall_loops", + "alternate_extra_wall", + "slice_closing_radius", + "spiral_mode", + "spiral_mode_smooth", + "spiral_mode_max_xy_smoothing", + "spiral_starting_flow_ratio", + "spiral_finishing_flow_ratio", + "slicing_mode", + "top_shell_layers", + "top_shell_thickness", + "top_surface_density", + "bottom_surface_density", + "bottom_shell_layers", + "bottom_shell_thickness", + "extra_perimeters_on_overhangs", + "ensure_vertical_shell_thickness", + "reduce_crossing_wall", + "detect_thin_wall", + "detect_overhang_wall", + "overhang_reverse", + "overhang_reverse_threshold", + "overhang_reverse_internal_only", + "wall_direction", + "seam_position", + "staggered_inner_seams", + "wall_sequence", + "is_infill_first", + "sparse_infill_density", + "fill_multiline", + "gyroid_optimized", + "sparse_infill_pattern", + "lateral_lattice_angle_1", + "lateral_lattice_angle_2", + "infill_overhang_angle", + "top_surface_pattern", + "bottom_surface_pattern", + "infill_direction", + "solid_infill_direction", + "counterbore_hole_bridging", + "infill_shift_step", + "sparse_infill_rotate_template", + "solid_infill_rotate_template", + "symmetric_infill_y_axis", + "skeleton_infill_density", + "infill_lock_depth", + "skin_infill_depth", + "skin_infill_density", + "align_infill_direction_to_model", + "extra_solid_infills", + "minimum_sparse_infill_area", + "reduce_infill_retraction", + "internal_solid_infill_pattern", + "gap_fill_target", + "ironing_type", + "ironing_pattern", + "ironing_flow", + "ironing_speed", + "ironing_spacing", + "ironing_angle", + "ironing_angle_fixed", + "ironing_inset", + "support_ironing", + "support_ironing_pattern", + "support_ironing_flow", + "support_ironing_spacing", "max_travel_detour_distance", - "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", + "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "fuzzy_skin_ripples_per_layer", "fuzzy_skin_ripple_offset", "fuzzy_skin_layers_between_ripple_offset", "max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only", "inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed", "top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed", "outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height","single_loop_draft_shield", "draft_shield", - "brim_width", "brim_object_gap", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", + "brim_width", "brim_object_gap", "brim_flow_ratio", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style", // BBS - "print_extruder_id", "print_extruder_variant", + "print_extruder_id", + "print_extruder_variant", "independent_support_layer_height", - "support_angle", "support_interface_top_layers", "support_interface_bottom_layers", - "support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern", - "support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang", - "filename_format", "wall_filament", "support_bottom_z_distance", - "sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body", - "ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width", - "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", - "skin_infill_line_width","skeleton_infill_line_width", - "top_surface_line_width", "support_line_width", "infill_wall_overlap","top_bottom_infill_wall_overlap", "bridge_flow", "internal_bridge_flow", - "elefant_foot_compensation", "elefant_foot_compensation_layers", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower", "prime_tower_enable_framework", - "prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_volume", + "support_angle", + "support_interface_top_layers", + "support_interface_bottom_layers", + "support_interface_pattern", + "support_interface_spacing", + "support_interface_loop_pattern", + "support_top_z_distance", + "support_on_build_plate_only", + "support_critical_regions_only", + "bridge_no_support", + "thick_bridges", + "thick_internal_bridges", + "dont_filter_internal_bridges", + "enable_extra_bridge_layer", + "max_bridge_length", + "print_sequence", + "print_order", + "support_remove_small_overhang", + "filename_format", + "wall_filament", + "support_bottom_z_distance", + "sparse_infill_filament", + "solid_infill_filament", + "support_filament", + "support_interface_filament", + "support_interface_not_for_body", + "ooze_prevention", + "standby_temperature_delta", + "preheat_time", + "preheat_steps", + "interface_shells", + "line_width", + "initial_layer_line_width", + "inner_wall_line_width", + "outer_wall_line_width", + "sparse_infill_line_width", + "internal_solid_infill_line_width", + "skin_infill_line_width", + "skeleton_infill_line_width", + "top_surface_line_width", + "support_line_width", + "infill_wall_overlap", + "top_bottom_infill_wall_overlap", + "bridge_flow", + "internal_bridge_flow", + "elefant_foot_compensation", + "elefant_foot_compensation_layers", + "elefant_foot_layers_density", + "xy_contour_compensation", + "xy_hole_compensation", + "resolution", + "enable_prime_tower", + "prime_tower_enable_framework", + "prime_tower_width", + "prime_tower_brim_width", + "prime_tower_skip_points", + "prime_volume", "prime_tower_infill_gap", "prime_tower_flat_ironing", "enable_tower_interface_features", "enable_tower_interface_cooldown_during_tower", - "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", - "flush_into_infill", "flush_into_objects", "flush_into_support", - "tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter", - "tree_support_branch_diameter", "tree_support_branch_diameter_angle", - "detect_narrow_internal_solid_infill", - "gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/ - "support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", - "initial_layer_infill_speed", "only_one_wall_top", - "timelapse_type", - "wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "min_length_factor", - "small_perimeter_speed", "small_perimeter_threshold","bridge_angle","internal_bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface", - "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk","default_junction_deviation", - "top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap", - "set_other_flow_ratios", "first_layer_flow_ratio", "outer_wall_flow_ratio", "inner_wall_flow_ratio", "overhang_flow_ratio", "sparse_infill_flow_ratio", "internal_solid_infill_flow_ratio", "gap_fill_flow_ratio", "support_flow_ratio", "support_interface_flow_ratio", - "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "wipe_before_external_loop", - "bridge_density","internal_bridge_density", "precise_outer_wall", "bridge_acceleration", - "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_auto_brim", - "tree_support_brim_width", "gcode_comments", "gcode_label_objects", - "initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width", - "make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes", - "wipe_tower_cone_angle", "wipe_tower_extra_spacing","wipe_tower_max_purge_speed", - "wipe_tower_wall_type", "wipe_tower_extra_rib_length", "wipe_tower_rib_width", "wipe_tower_fillet_wall", - "wipe_tower_filament", "wiping_volumes_extruders","wipe_tower_bridging", "wipe_tower_extra_flow","single_extruder_multi_material_priming", - "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", - "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", - "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", - "enable_wrapping_detection", - "seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "scarf_joint_speed", "scarf_joint_flow_ratio", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", "scarf_overhang_threshold", - "interlocking_beam", "interlocking_orientation", "interlocking_beam_layer_count", "interlocking_depth", "interlocking_boundary_avoidance", "interlocking_beam_width","calib_flowrate_topinfill_special_order", + "wipe_tower_no_sparse_layers", + "compatible_printers", + "compatible_printers_condition", + "inherits", + "flush_into_infill", + "flush_into_objects", + "flush_into_support", + "tree_support_branch_angle", + "tree_support_angle_slow", + "tree_support_wall_count", + "tree_support_top_rate", + "tree_support_branch_distance", + "tree_support_tip_diameter", + "tree_support_branch_diameter", + "tree_support_branch_diameter_angle", + "detect_narrow_internal_solid_infill", + "gcode_add_line_number", + "enable_arc_fitting", + "precise_z_height", + "infill_combination", + "infill_combination_max_layer_height", /*"adaptive_layer_height",*/ + "support_bottom_interface_spacing", + "enable_overhang_speed", + "slowdown_for_curled_perimeters", + "overhang_1_4_speed", + "overhang_2_4_speed", + "overhang_3_4_speed", + "overhang_4_4_speed", + "initial_layer_infill_speed", + "only_one_wall_top", + "timelapse_type", + "wall_generator", + "wall_transition_length", + "wall_transition_filter_deviation", + "wall_transition_angle", + "wall_distribution_count", + "min_feature_size", + "min_bead_width", + "post_process", + "process_change_extrusion_role_gcode", + "min_length_factor", + "wall_maximum_resolution", + "wall_maximum_deviation", + "small_perimeter_speed", + "small_perimeter_threshold", + "bridge_angle", + "internal_bridge_angle", + "filter_out_gap_fill", + "travel_acceleration", + "inner_wall_acceleration", + "min_width_top_surface", + "default_jerk", + "outer_wall_jerk", + "inner_wall_jerk", + "infill_jerk", + "top_surface_jerk", + "initial_layer_jerk", + "travel_jerk", + "default_junction_deviation", + "top_solid_infill_flow_ratio", + "bottom_solid_infill_flow_ratio", + "only_one_wall_first_layer", + "print_flow_ratio", + "seam_gap", + "set_other_flow_ratios", + "first_layer_flow_ratio", + "outer_wall_flow_ratio", + "inner_wall_flow_ratio", + "overhang_flow_ratio", + "sparse_infill_flow_ratio", + "internal_solid_infill_flow_ratio", + "gap_fill_flow_ratio", + "support_flow_ratio", + "support_interface_flow_ratio", + "role_based_wipe_speed", + "wipe_speed", + "accel_to_decel_enable", + "accel_to_decel_factor", + "wipe_on_loops", + "wipe_before_external_loop", + "bridge_density", + "internal_bridge_density", + "precise_outer_wall", + "bridge_acceleration", + "sparse_infill_acceleration", + "internal_solid_infill_acceleration", + "tree_support_auto_brim", + "tree_support_brim_width", + "gcode_comments", + "gcode_label_objects", + "initial_layer_travel_speed", + "initial_layer_travel_acceleration", + "initial_layer_travel_jerk", + "exclude_object", + "slow_down_layers", + "infill_anchor", + "infill_anchor_max", + "initial_layer_min_bead_width", + "make_overhang_printable", + "make_overhang_printable_angle", + "make_overhang_printable_hole_size", + "notes", + "wipe_tower_cone_angle", + "wipe_tower_extra_spacing", + "wipe_tower_max_purge_speed", + "wipe_tower_wall_type", + "wipe_tower_extra_rib_length", + "wipe_tower_rib_width", + "wipe_tower_fillet_wall", + "wipe_tower_filament", + "wiping_volumes_extruders", + "wipe_tower_bridging", + "wipe_tower_extra_flow", + "single_extruder_multi_material_priming", + "wipe_tower_rotation_angle", + "tree_support_branch_distance_organic", + "tree_support_branch_diameter_organic", + "tree_support_branch_angle_organic", + "hole_to_polyhole", + "hole_to_polyhole_threshold", + "hole_to_polyhole_twisted", + "mmu_segmented_region_max_width", + "mmu_segmented_region_interlocking_depth", + "small_area_infill_flow_compensation", + "small_area_infill_flow_compensation_model", + "enable_wrapping_detection", + "seam_slope_type", + "seam_slope_conditional", + "scarf_angle_threshold", + "scarf_joint_speed", + "scarf_joint_flow_ratio", + "seam_slope_start_height", + "seam_slope_entire_loop", + "seam_slope_min_length", + "seam_slope_steps", + "seam_slope_inner_walls", + "scarf_overhang_threshold", + "interlocking_beam", + "interlocking_orientation", + "interlocking_beam_layer_count", + "interlocking_depth", + "interlocking_boundary_avoidance", + "interlocking_beam_width", + "calib_flowrate_topinfill_special_order", + // Z Anti-Aliasing (ZAA) + "zaa_enabled", + "zaa_minimize_perimeter_height", + "zaa_dont_alternate_fill_direction", + "zaa_min_z", + "ironing_expansion", }; static std::vector s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "pellet_flow_coefficient", "volumetric_speed_coefficients", "filament_type", @@ -976,10 +1275,10 @@ static std::vector s_Preset_filament_options {/*"filament_colour", // "bed_type", //BBS:temperature_vitrification "temperature_vitrification", "reduce_fan_stop_start_freq","dont_slow_down_outer_wall", "slow_down_for_layer_cooling", "fan_min_speed", - "fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "full_fan_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed", - "filament_start_gcode", "filament_end_gcode", + "fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "close_additional_fan_first_x_layers", "first_x_layer_fan_speed", "full_fan_speed_layer", "additional_fan_full_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed", + "filament_start_gcode", "filament_end_gcode", "filament_change_extrusion_role_gcode", //exhaust fan control - "activate_air_filtration","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed", + "activate_air_filtration","activate_air_filtration_during_print","activate_air_filtration_on_completion","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed", // Retract overrides "filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_lift_enforce", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel", "filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe", @@ -1000,7 +1299,7 @@ static std::vector s_Preset_filament_options {/*"filament_colour", "filament_multitool_ramming", "filament_multitool_ramming_volume", "filament_multitool_ramming_flow", "activate_chamber_temp_control", "filament_long_retractions_when_cut","filament_retraction_distances_when_cut", "idle_temperature", //BBS filament change length while the extruder color - "filament_change_length","filament_flush_volumetric_speed","filament_flush_temp", + "filament_change_length","filament_flush_volumetric_speed","filament_flush_temp", "filament_cooling_before_tower", "long_retractions_when_ec", "retraction_distances_when_ec" }; @@ -1013,12 +1312,14 @@ static std::vector s_Preset_machine_limits_options { "machine_max_junction_deviation", //resonance avoidance ported from qidi slicer "resonance_avoidance", "min_resonance_avoidance_speed", "max_resonance_avoidance_speed", + // Orca: input shaping + "input_shaping_emit", "input_shaping_type", "input_shaping_freq_x", "input_shaping_freq_y", "input_shaping_damp_x", "input_shaping_damp_y", }; static std::vector s_Preset_printer_options { "printer_technology", "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", - "fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs", + "fan_kickstart", "part_cooling_fan_min_pwm", "fan_speedup_time", "fan_speedup_overhangs", "single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", @@ -1035,7 +1336,7 @@ static std::vector s_Preset_printer_options { "use_relative_e_distances", "extruder_type", "use_firmware_retraction", "printer_notes", "grab_length", "support_object_skip_flush", "physical_extruder_map", "cooling_tube_retraction", - "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming", + "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "wipe_tower_type", "purge_in_prime_tower", "enable_filament_ramming", "tool_change_on_wipe_tower", "z_offset", "disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "pellet_modded_printer", "support_multi_bed_types", "default_bed_type", "bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "enable_long_retraction_when_cut","long_retractions_when_cut","retraction_distances_when_cut", "bed_temperature_formula", "nozzle_flush_dataset" @@ -1211,19 +1512,34 @@ void PresetCollection::add_default_preset(const std::vector &keys, ++ m_num_default_presets; } +std::string PresetCollection::canonical_preset_name(const std::string &name, const PresetOrigin &load_origin) const +{ + const ParsedName parsed = parse_preset_name(name); + PresetOrigin origin = load_origin; + if (origin.kind == PresetOrigin::Kind::Auto) { + origin.kind = parsed.kind; + origin.bundle_id = parsed.bundle_id; + } else if (origin.is_bundle() && origin.bundle_id.empty()) { + origin.bundle_id = parsed.bundle_id; + } + return get_preset_canonical_name(parsed.bare, origin); +} + // Load all presets found in dir_path. // Throws an exception on error. void PresetCollection::load_presets( const std::string &dir_path, const std::string &subdir, - PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule) + PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule, + std::function preset_loaded_fn, const PresetOrigin &load_origin) { // Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points, // see https://github.com/prusa3d/PrusaSlicer/issues/732 boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred(); + const PresetOrigin resolved_origin = detect_origin_from_path(dir, load_origin); // Load custom roots first if (fs::exists(dir / "base")) { - load_presets(dir.string(), "base", substitutions, substitution_rule); + load_presets(dir.string(), "base", substitutions, substitution_rule, nullptr, resolved_origin); } //BBS: add config related logs @@ -1253,14 +1569,16 @@ void PresetCollection::load_presets( if (Slic3r::is_json_file(file_name)) { // Remove the .ini suffix. std::string name = file_name.erase(file_name.size() - 5); - if (this->find_preset(name, false)) { + std::string canonical_name = this->canonical_preset_name(name, resolved_origin); + if (this->find_preset(canonical_name, false)) { // This happens when there's is a preset (most likely legacy one) with the same name as a system preset // that's already been loaded from a bundle. - BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; + BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << canonical_name; continue; } try { - Preset preset(m_type, name, false); + Preset preset(m_type, canonical_name, false); + preset.bundle_id = resolved_origin.bundle_id; preset.file = dir_entry.path().string(); // Load the preset file, apply preset values on top of defaults. try { @@ -1311,7 +1629,6 @@ void PresetCollection::load_presets( std::string inherits_value = option_str->value; // Orca: try to find if the parent preset has been renamed inherit_preset = this->find_preset2(inherits_value); - } else { ; } @@ -1389,6 +1706,9 @@ void PresetCollection::load_presets( fs::remove(file_path); } + if (preset_loaded_fn != nullptr) + preset_loaded_fn(preset); + presets_loaded.emplace_back(preset); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " load config successful and preset name is:" << preset.name; } catch (const std::runtime_error &err) { @@ -1507,9 +1827,9 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map& proje inherits_value.replace(pos, 1, 1, '~'); option_str->value = inherits_value; }*/ - inherit_preset = this->find_preset(inherits_value, false, true); + inherit_preset = this->find_preset2(inherits_value, true); } const Preset& default_preset = this->default_preset_for(config); if (inherit_preset) { @@ -1661,9 +1981,10 @@ bool PresetCollection::reset_project_embedded_presets() void PresetCollection::set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time) { lock(); + const std::string canonical_name = this->canonical_preset_name(name); for (auto it = m_presets.begin(); it != m_presets.end(); it++) { Preset* preset = &m_presets[it - m_presets.begin()]; - if (preset->name == name) { + if (preset->name == canonical_name) { if (syncinfo.empty()) preset->sync_info.clear(); else @@ -1728,7 +2049,7 @@ void PresetCollection::update_user_presets_directory(const std::string& dir_path } //BBS: save user presets to local -void PresetCollection::save_user_presets(const std::string& dir_path, const std::string& type, std::vector& need_to_delete_list) +void PresetCollection::save_user_presets(const std::string& dir_path, const std::string& type, std::map& need_to_delete_list) { boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / type).make_preferred(); @@ -1782,31 +2103,37 @@ void PresetCollection::save_user_presets(const std::string& dir_path, const std: } //BBS: load one user preset from key-values -bool PresetCollection::load_user_preset(std::string name, std::map preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule) +bool PresetCollection::load_user_preset(std::string name, std::map preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, const PresetOrigin &load_origin) { std::string errors_cummulative; // Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken. // (see the "Preset already present, not loading" message). //std::deque presets_loaded; int count = 0; + const std::string canonical_name = this->canonical_preset_name(name, load_origin); + auto update_alias = [this](Preset &preset) { + if (! preset.alias.empty()) + return; + set_custom_preset_alias(preset); + }; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%name %preset_values.size(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%canonical_name %preset_values.size(); //if the version is not matching, skip it if (preset_values.find(BBL_JSON_KEY_VERSION) == preset_values.end()) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find version, not loading for user preset %1%")%name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find version, not loading for user preset %1%")%canonical_name; return false; } std::string version_str = preset_values[BBL_JSON_KEY_VERSION]; boost::optional cloud_version = Semver::parse(version_str); if (!cloud_version) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("invalid version %1%, not loading for user preset %2%")%version_str %name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("invalid version %1%, not loading for user preset %2%")%version_str %canonical_name; return false; } //setting_id if (preset_values.find(BBL_JSON_KEY_SETTING_ID) == preset_values.end()) { - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find setting_id, not loading for user preset %1%")%name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find setting_id, not loading for user preset %1%")%canonical_name; return false; } std::string cloud_setting_id = preset_values[BBL_JSON_KEY_SETTING_ID]; @@ -1819,17 +2146,17 @@ bool PresetCollection::load_user_preset(std::string name, std::mapname; - auto iter = this->find_preset_internal(name); + auto iter = this->find_preset_internal(canonical_name); bool need_update = false; - if ((iter != m_presets.end()) && (iter->name == name)) { - BOOST_LOG_TRIVIAL(info) << "Found the Preset locally: " << name; + if ((iter != m_presets.end()) && (iter->name == canonical_name)) { + BOOST_LOG_TRIVIAL(info) << "Found the Preset locally: " << canonical_name; //BBS: we should compare the time between cloud and local if ((cloud_update_time == 0) || (cloud_update_time <= iter->updated_time)) { if (cloud_update_time < iter->updated_time) @@ -1841,7 +2168,7 @@ bool PresetCollection::load_user_preset(std::string name, std::mapfile); idx_file.replace_extension(".info"); iter->save_info(idx_file.string()); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("preset %1%'s update_time is eqaul or newer, cloud update_time %2%, local update_time %3%")%name %cloud_update_time %iter->updated_time; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("preset %1%'s update_time is eqaul or newer, cloud update_time %2%, local update_time %3%")%canonical_name %cloud_update_time %iter->updated_time; unlock(); return false; } @@ -1851,26 +2178,36 @@ bool PresetCollection::load_user_preset(std::string name, std::mapsecond; + } else { + const auto inherits_iter = preset_values.find(BBL_JSON_KEY_INHERITS); + const bool preset_inherits_from_parent = inherits_iter != preset_values.end() && !inherits_iter->second.empty(); + if (preset_inherits_from_parent) { + // This indicates that there is inherits exists but there is no base_id + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ + << boost::format("can not find base_id, not loading for user preset %1%") % canonical_name; + unlock(); + return false; + } } - std::string cloud_base_id = preset_values[BBL_JSON_KEY_BASE_ID]; //filament_id std::string cloud_filament_id; if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) { cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID]; - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << name << " filament_id: " << cloud_filament_id << " base_id: " << cloud_base_id; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << canonical_name << " filament_id: " << cloud_filament_id << " base_id: " << based_id; } DynamicPrintConfig new_config, cloud_config; try { ConfigSubstitutions config_substitutions = cloud_config.load_string_map(preset_values, rule); if (! config_substitutions.empty()) - substitutions.push_back({ name, m_type, PresetConfigSubstitutions::Source::UserCloud, name, std::move(config_substitutions) }); + substitutions.push_back({ canonical_name, m_type, PresetConfigSubstitutions::Source::UserCloud, canonical_name, std::move(config_substitutions) }); //BBS: use inherit config as the base Preset* inherit_preset = nullptr; @@ -1878,12 +2215,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map (inherits_config); std::string inherits_value = option_str->value; - /*size_t pos = inherits_value.find_first_of('*'); - if (pos != std::string::npos) { - inherits_value.replace(pos, 1, 1, '~'); - option_str->value = inherits_value; - }*/ - inherit_preset = this->find_preset(inherits_value, false, true); + inherit_preset = this->find_preset2(inherits_value, true); } const Preset& default_preset = this->default_preset_for(cloud_config); if (inherit_preset) { @@ -1897,7 +2229,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map(inherits_config); if (inherits_config2 && !inherits_config2->value.empty()) { //we should skip this preset here - BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%name; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%canonical_name; unlock(); return false; } @@ -1923,13 +2255,17 @@ bool PresetCollection::load_user_preset(std::string name, std::mapname == m_edited_preset.name && iter->is_dirty) { // Keep modifies when update from remote new_config.apply_only(m_edited_preset.config, m_edited_preset.config.diff(iter->config)); + } else if (iter->name == m_edited_preset.name) { + // Preset is not dirty (no local unsaved changes) — also update the edited preset + // to prevent a false "dirty" indication (orange highlight) after a silent cloud sync + m_edited_preset.config = new_config; } iter->config = new_config; iter->updated_time = cloud_update_time; @@ -1937,25 +2273,28 @@ bool PresetCollection::load_user_preset(std::string name, std::mapversion = cloud_version.value(); iter->user_id = cloud_user_id; iter->setting_id = cloud_setting_id; - iter->base_id = cloud_base_id; + iter->base_id = based_id; iter->filament_id = cloud_filament_id; + update_alias(*iter); //presets_loaded.emplace_back(*it->second); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", update the user preset %1% from cloud, type %2%, setting_id %3%, base_id %4%, sync_info %5% inherits %6%, filament_id %7%") % iter->name %Preset::get_type_string(m_type) %iter->setting_id %iter->base_id %iter->sync_info %iter->inherits() % iter->filament_id; } else { //create a new one - Preset preset(m_type, name, false); + Preset preset(m_type, canonical_name, false); preset.is_system = false; preset.loaded = true; + preset.bundle_id = load_origin.bundle_id; preset.config = new_config; preset.updated_time = cloud_update_time; preset.sync_info = "save"; preset.version = cloud_version.value(); preset.user_id = cloud_user_id; preset.setting_id = cloud_setting_id; - preset.base_id = cloud_base_id; + preset.base_id = based_id; preset.filament_id = cloud_filament_id; + update_alias(preset); size_t cur_index = iter - m_presets.begin(); m_presets.insert(iter, preset); @@ -1977,7 +2316,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map::iterator it = this->find_preset_internal(preset_name); - bool found = (it != m_presets.end()) && (it->name == preset_name) && (it->is_system || it->is_default); + // Presets that came from system vendors, the built-in defaults, or any loaded bundle (local or + // subscribed) are trusted — their g-code isn't user-authored, so the 3MF importer should not + // warn about them. + auto is_trusted = [](const Preset &p) { return p.is_system || p.is_default || p.is_from_bundle(); }; + + const std::string canonical_name = this->canonical_preset_name(preset_name); + std::deque::iterator it = this->find_preset_internal(canonical_name); + bool found = (it != m_presets.end()) && (it->name == canonical_name) && is_trusted(*it); if (!found) { - it = this->find_preset_renamed(preset_name); - found = it != m_presets.end() && (it->is_system || it->is_default); + it = this->find_preset_renamed(canonical_name); + found = it != m_presets.end() && is_trusted(*it); } if (!found) { if (!inherit_name.empty()) { - it = this->find_preset_internal(inherit_name); - found = it != m_presets.end() && it->name == inherit_name && (it->is_system || it->is_default); + const std::string canonical_inherit_name = this->canonical_preset_name(inherit_name); + it = this->find_preset_internal(canonical_inherit_name); + found = it != m_presets.end() && it->name == canonical_inherit_name && is_trusted(*it); if (found) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": preset_name %1%, inherit_name %2%, found inherit in list")%preset_name %inherit_name; else @@ -2084,7 +2430,7 @@ std::pair PresetCollection::load_external_preset( cfg.apply_only(combined_config, keys, true); std::string &inherits = Preset::inherits(cfg); - //BBS: add different settings check logic, replace the old system preset's default value with new system preset's default values + //add different settings check logic, replace the old system preset's default value with new system preset's default values std::deque::iterator it = this->find_preset_internal(original_name); bool found = it != m_presets.end() && it->name == original_name; if (! found) { @@ -2424,7 +2770,7 @@ std::map> PresetCollection::get_filamen std::map> filament_presets; for (auto &preset : m_presets) { if (preset.is_user()) { - if (preset.inherits() == "" || !preset.filament_id.empty()) { filament_presets[preset.filament_id].push_back(&preset); } + if (preset.inherits() == "") { filament_presets[preset.filament_id].push_back(&preset); } continue; } if (get_preset_base(preset) == &preset) { filament_presets[preset.filament_id].push_back(&preset); } @@ -2446,7 +2792,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det // Preset with the same name found. Preset &preset = *it; //BBS: add project embedded preset logic - if (preset.is_default || preset.is_system) { + if (!preset.can_overwrite()) { //if (preset.is_default || preset.is_external || preset.is_system) // Cannot overwrite the default preset. //BBS: add lock logic for sync preset in background @@ -2505,6 +2851,9 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det preset.is_default = false; preset.is_system = false; preset.is_external = false; + + preset.bundle_id.clear(); + preset.file = this->path_for_preset(preset); // The newly saved preset will be activated -> make it visible. preset.is_visible = true; @@ -2518,18 +2867,8 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det preset.is_project_embedded = false; if (m_type == Preset::TYPE_PRINT) preset.config.option("print_settings_id", true)->value = new_name; - else if (m_type == Preset::TYPE_FILAMENT) { + else if (m_type == Preset::TYPE_FILAMENT) preset.config.option("filament_settings_id", true)->values[0] = new_name; - - boost::uuids::detail::md5 hash; - boost::uuids::detail::md5::digest_type digest; - hash.process_bytes(new_name.data(), new_name.size()); - hash.get_digest(digest); - const auto char_digest = reinterpret_cast(&digest); - std::string result; - boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(result)); - preset.filament_id = "P" + result.substr(0, 7); - } else if (m_type == Preset::TYPE_PRINTER) preset.config.option("printer_settings_id", true)->value = new_name; //BBS: add lock logic for sync preset in background @@ -2557,7 +2896,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det bool PresetCollection::delete_current_preset() { Preset &selected = this->get_selected_preset(); - if (selected.is_default) + if (!selected.can_overwrite()) return false; if (get_preset_base(selected) == &selected) { @@ -2588,18 +2927,24 @@ bool PresetCollection::delete_current_preset() return true; } -bool PresetCollection::delete_preset(const std::string& name) +bool PresetCollection::delete_preset(const std::string& name, bool force) { - auto it = this->find_preset_internal(name); + Preset *preset_ptr = this->find_preset(name, false, true); + if (preset_ptr == nullptr) + return false; + + auto it = this->find_preset_internal(preset_ptr->name); + if (it == m_presets.end() || it->name != preset_ptr->name) + return false; Preset& preset = *it; - if (preset.is_default) + // ORCA: if the preset can't be overridden then don't allow deletion + // force=true bypasses this for bundle preset cleanup from cloud sync + if (!force && !preset.can_overwrite()) return false; - //BBS: add project embedded preset logic and refine is_external - //if (!preset.is_external && !preset.is_system) { - if (! preset.is_system) { - preset.remove_files(); - } + + preset.remove_files(); + //BBS: add lock logic for sync preset in background lock(); set_printer_hold_alias(it->alias, *it, true); @@ -2609,6 +2954,30 @@ bool PresetCollection::delete_preset(const std::string& name) return true; } +void PresetCollection::check_and_fix_syncinfo(Preset& preset, const std::string& user_id) +{ + // user id can't be empty + if (user_id.empty()) + return; + // correct the sync info if preset.user_id is empty(the profile json file is copied to the user folder with missing .info file) or preset.user_id + // is not equal to the current user id or preset.setting_id is not in expected format(the .info is copied from the older format) + if (preset.user_id.empty() || preset.user_id != user_id || preset.setting_id.find('-') == std::string::npos) { + preset.user_id = user_id; + preset.setting_id = ""; + + if (preset.base_id.empty()) { + const std::string inherits = Preset::inherits(preset.config); + Preset* parent_preset = find_preset2(inherits, true); + if (parent_preset) + preset.base_id = parent_preset->setting_id; + } + // tell the sync logic to sync it as a new preset + preset.updated_time = 0; + preset.sync_info = "create"; + preset.save_info(); + } +} + const Preset* PresetCollection::get_selected_preset_parent() const { if (this->get_selected_idx() == size_t(-1)) @@ -2671,7 +3040,7 @@ const Preset *PresetCollection::get_preset_base(const Preset &child) const // Handle user preset if (child.inherits().empty()) return &child; // this is user root - auto inherits = find_preset2(child.inherits(),true); + auto inherits = find_preset2(child.inherits(), true); return inherits ? get_preset_base(*inherits) : nullptr; } @@ -2745,20 +3114,21 @@ const std::string& PresetCollection::get_suffix_modified() { // If a preset was not found by its name, null is returned. Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found, bool real, bool only_from_library) { - Preset key(m_type, name, false); - auto it = this->find_preset_internal(name, only_from_library); - // Ensure that a temporary copy is returned if the preset found is currently selected. - return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin(), real) : - first_visible_if_not_found ? &this->first_visible() : nullptr; + const ParsedName parsed = parse_preset_name(name); + const std::string canonical = get_preset_canonical_name(parsed.bare, PresetOrigin(parsed.kind, parsed.bundle_id)); + auto it = this->find_preset_internal(canonical, only_from_library); + if (it != m_presets.end() && it->name == canonical) + return &this->preset(it - m_presets.begin(), real); + return first_visible_if_not_found ? &this->first_visible() : nullptr; } Preset* PresetCollection::find_preset2(const std::string& name, bool auto_match/* = true */) { - auto preset = find_preset(name,false,true); + auto preset = find_preset(name, false, true); if (preset == nullptr) { auto _name = get_preset_name_renamed(name); if (_name != nullptr) - preset = find_preset(*_name,false,true); + preset = find_preset(*_name, false, true); if (auto_match && preset == nullptr) { //Orca: one more try, find the most likely preset in OrcaFilamentLibrary if (name.find("Generic") != std::string::npos) { @@ -2774,7 +3144,6 @@ Preset* PresetCollection::find_preset2(const std::string& name, bool auto_match/ } } } - return preset; } @@ -3126,10 +3495,11 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b //BBS: add config related logs BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1%, try to select by name %2%, force %3%")%Preset::get_type_string(m_type) %name_w_suffix %force; std::string name = Preset::remove_suffix_modified(name_w_suffix); + const std::string normalized_name = this->canonical_preset_name(name); // 1) Try to find the preset by its name. - auto it = this->find_preset_internal(name); + auto it = this->find_preset_internal(normalized_name); size_t idx = 0; - if (it != m_presets.end() && it->name == name && it->is_visible) + if (it != m_presets.end() && it->name == normalized_name && it->is_visible) // Preset found by its name and it is visible. idx = it - m_presets.begin(); else { @@ -3162,11 +3532,12 @@ bool PresetCollection::select_preset_by_name_strict(const std::string &name) { //BBS: add config related logs BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1%, try to select by name %2%")%Preset::get_type_string(m_type) %name; + const std::string canonical_name = this->canonical_preset_name(name); // 1) Try to find the preset by its name. - auto it = this->find_preset_internal(name); + auto it = this->find_preset_internal(canonical_name); size_t idx = (size_t)-1; - if (it != m_presets.end() && it->name == name && it->is_visible) + if (it != m_presets.end() && it->name == canonical_name && it->is_visible) // Preset found by its name. idx = it - m_presets.begin(); // 2) Select the new preset. @@ -3278,24 +3649,30 @@ void PresetCollection::update_map_system_profile_renamed() void PresetCollection::set_custom_preset_alias(Preset &preset) { - if (m_type == Preset::Type::TYPE_FILAMENT && preset.config.has(BBL_JSON_KEY_INHERITS) && preset.config.option(BBL_JSON_KEY_INHERITS)->value.empty()) { - std::string alias_name; - std::string preset_name = preset.name; - if (alias_name.empty()) { - size_t end_pos = preset_name.find_first_of("@"); - if (end_pos != std::string::npos) { - alias_name = preset_name.substr(0, end_pos); - boost::trim_right(alias_name); - } - } - if (alias_name.empty() || is_alias_exist(alias_name, &preset)) - preset.alias = ""; - else { - preset.alias = std::move(alias_name); - m_map_alias_to_profile_name[preset.alias].push_back(preset.name); - set_printer_hold_alias(preset.alias, preset); + // For filaments, remove the postfix + // For printers, there is nothing to remove + // For prints AKA processes, the postfix should be kept + // Alias should be set here, as the preset name may be augmented further later (i.e., prefixing relative path for bundles) + std::string bare_preset_name = get_preset_bare_name(preset.name); + std::string alias_name = bare_preset_name; + + const bool is_root_filament_preset = + m_type == Preset::Type::TYPE_FILAMENT && + preset.config.has(BBL_JSON_KEY_INHERITS) && + preset.config.option(BBL_JSON_KEY_INHERITS)->value.empty(); + if (is_root_filament_preset) { + const size_t suffix_separator_pos = bare_preset_name.find_first_of("@"); + if (suffix_separator_pos != std::string::npos) { + alias_name = bare_preset_name.substr(0, suffix_separator_pos); + boost::trim_right(alias_name); + if (alias_name.empty()) + alias_name = bare_preset_name; } } + + preset.alias = std::move(alias_name); + m_map_alias_to_profile_name[preset.alias].push_back(preset.name); + set_printer_hold_alias(preset.alias, preset); } void PresetCollection::set_printer_hold_alias(const std::string &alias, Preset &preset, bool remove) @@ -3396,7 +3773,7 @@ std::string PresetCollection::path_from_name(const std::string &new_name, bool d std::string PresetCollection::path_for_preset(const Preset &preset) const { - return path_from_name(preset.name, is_base_preset(preset)); + return path_from_name(get_preset_bare_name(preset.name), is_base_preset(preset)); } const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index f85b0103c7..5e0ab15849 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -3101,7 +3101,7 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info) continue; } auto iter = std::find_if(filaments.begin(), filaments.end(), - [this, &filament_id](auto &f) { return f.is_compatible && (filaments.get_preset_base(f) == &f || (f.is_user() && !f.filament_id.empty())) && f.filament_id == filament_id; }); + [this, &filament_id](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; }); if (iter == filaments.end()) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id; auto filament_type = ams.opt_string("filament_type", 0u); @@ -3203,7 +3203,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector