1 #ifndef slic3r_Preset_hpp_
2 #define slic3r_Preset_hpp_
3 
4 #include <deque>
5 #include <set>
6 #include <unordered_map>
7 
8 #include <boost/filesystem/path.hpp>
9 #include <boost/property_tree/ptree_fwd.hpp>
10 
11 #include "PrintConfig.hpp"
12 #include "Semver.hpp"
13 
14 namespace Slic3r {
15 
16 class AppConfig;
17 class PresetBundle;
18 
19 enum ConfigFileType
20 {
21     CONFIG_FILE_TYPE_UNKNOWN,
22     CONFIG_FILE_TYPE_APP_CONFIG,
23     CONFIG_FILE_TYPE_CONFIG,
24     CONFIG_FILE_TYPE_CONFIG_BUNDLE,
25 };
26 
27 extern ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree);
28 
29 class VendorProfile
30 {
31 public:
32     std::string                     name;
33     std::string                     id;
34     Semver                          config_version;
35     std::string                     config_update_url;
36     std::string                     changelog_url;
37 
38     struct PrinterVariant {
PrinterVariantSlic3r::VendorProfile::PrinterVariant39         PrinterVariant() {}
PrinterVariantSlic3r::VendorProfile::PrinterVariant40         PrinterVariant(const std::string &name) : name(name) {}
41         std::string                 name;
42     };
43 
44     struct PrinterModel {
PrinterModelSlic3r::VendorProfile::PrinterModel45         PrinterModel() {}
46         std::string                 id;
47         std::string                 name;
48         PrinterTechnology           technology;
49         std::string                 family;
50         std::vector<PrinterVariant> variants;
51         std::vector<std::string>	default_materials;
52         // Vendor & Printer Model specific print bed model & texture.
53         std::string 			 	bed_model;
54         std::string 				bed_texture;
55 
variantSlic3r::VendorProfile::PrinterModel56         PrinterVariant*       variant(const std::string &name) {
57             for (auto &v : this->variants)
58                 if (v.name == name)
59                     return &v;
60             return nullptr;
61         }
62 
variantSlic3r::VendorProfile::PrinterModel63         const PrinterVariant* variant(const std::string &name) const { return const_cast<PrinterModel*>(this)->variant(name); }
64     };
65     std::vector<PrinterModel>          models;
66 
67     std::set<std::string>              default_filaments;
68     std::set<std::string>              default_sla_materials;
69 
VendorProfile()70     VendorProfile() {}
VendorProfile(std::string id)71     VendorProfile(std::string id) : id(std::move(id)) {}
72 
valid() const73     bool 		valid() const { return ! name.empty() && ! id.empty() && config_version.valid(); }
74 
75     // Load VendorProfile from an ini file.
76     // If `load_all` is false, only the header with basic info (name, version, URLs) is loaded.
77     static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true);
78     static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
79 
num_variants() const80     size_t      num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; }
81     std::vector<std::string> families() const;
82 
operator <(const VendorProfile & rhs) const83     bool        operator< (const VendorProfile &rhs) const { return this->id <  rhs.id; }
operator ==(const VendorProfile & rhs) const84     bool        operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }
85 };
86 
87 class Preset;
88 
89 // Helper to hold a profile with its vendor definition, where the vendor definition may have been extracted from a parent system preset.
90 // The parent preset is only accessible through PresetCollection, therefore to allow definition of the various is_compatible_with methods
91 // outside of the PresetCollection, this composite is returned by PresetCollection::get_preset_with_vendor_profile() when needed.
92 struct PresetWithVendorProfile {
PresetWithVendorProfileSlic3r::PresetWithVendorProfile93 	PresetWithVendorProfile(const Preset &preset, const VendorProfile *vendor) : preset(preset), vendor(vendor) {}
94 	const Preset 		&preset;
95 	const VendorProfile *vendor;
96 };
97 
98 // Note: it is imporant that map is used here rather than unordered_map,
99 // because we need iterators to not be invalidated,
100 // because Preset and the ConfigWizard hold pointers to VendorProfiles.
101 // XXX: maybe set is enough (cf. changes in Wizard)
102 typedef std::map<std::string, VendorProfile> VendorMap;
103 
104 class Preset
105 {
106 public:
107     enum Type
108     {
109         TYPE_INVALID,
110         TYPE_PRINT,
111         TYPE_SLA_PRINT,
112         TYPE_FILAMENT,
113         TYPE_SLA_MATERIAL,
114         TYPE_PRINTER,
115         // This type is here to support PresetConfigSubstitutions for physical printers, however it does not belong to the Preset class,
116         // PhysicalPrinter class is used instead.
117         TYPE_PHYSICAL_PRINTER,
118     };
119 
Preset(Type type,const std::string & name,bool is_default=false)120     Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
121 
122     Type                type        = TYPE_INVALID;
123 
124     // The preset represents a "default" set of properties,
125     // pulled from the default values of the PrintConfig (see PrintConfigDef for their definitions).
126     bool                is_default;
127     // External preset points to a configuration, which has been loaded but not imported
128     // into the Slic3r default configuration location.
129     bool                is_external = false;
130     // System preset is read-only.
131     bool                is_system   = false;
132     // Preset is visible, if it is associated with a printer model / variant that is enabled in the AppConfig
133     // or if it has no printer model / variant association.
134     // Also the "default" preset is only visible, if it is the only preset in the list.
135     bool                is_visible  = true;
136     // Has this preset been modified?
137     bool                is_dirty    = false;
138     // Is this preset compatible with the currently active printer?
139     bool                is_compatible = true;
140 
is_user() const141     bool                is_user() const { return ! this->is_default && ! this->is_system; }
142 
143     // Name of the preset, usually derived form the file name.
144     std::string         name;
145     // File name of the preset. This could be a Print / Filament / Printer preset,
146     // or a Configuration file bundling the Print + Filament + Printer presets (in that case is_external and possibly is_system will be true),
147     // or it could be a G-code (again, is_external will be true).
148     std::string         file;
149     // If this is a system profile, then there should be a vendor data available to display at the UI.
150     const VendorProfile *vendor      = nullptr;
151 
152     // Has this profile been loaded?
153     bool                loaded      = false;
154 
155     // Configuration data, loaded from a file, or set from the defaults.
156     DynamicPrintConfig  config;
157 
158     // Alias of the preset
159     std::string         alias;
160     // List of profile names, from which this profile was renamed at some point of time.
161     // This list is then used to match profiles by their names when loaded from .gcode, .3mf, .amf,
162     // and to match the "inherits" field of user profiles with updated system profiles.
163     std::vector<std::string> renamed_from;
164 
165     void                save();
166 
167     // Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
168     std::string         label() const;
169 
170     // Set the is_dirty flag if the provided config is different from the active one.
set_dirty(const DynamicPrintConfig & config)171     void                set_dirty(const DynamicPrintConfig &config) { this->is_dirty = ! this->config.diff(config).empty(); }
set_dirty(bool dirty=true)172     void                set_dirty(bool dirty = true) { this->is_dirty = dirty; }
reset_dirty()173     void                reset_dirty() { this->is_dirty = false; }
174 
175     // Returns the name of the preset, from which this preset inherits.
inherits(DynamicPrintConfig & cfg)176     static std::string& inherits(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("inherits", true)->value; }
inherits()177     std::string&        inherits() { return Preset::inherits(this->config); }
inherits() const178     const std::string&  inherits() const { return Preset::inherits(const_cast<Preset*>(this)->config); }
179 
180     // Returns the "compatible_prints_condition".
compatible_prints_condition(DynamicPrintConfig & cfg)181     static std::string& compatible_prints_condition(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("compatible_prints_condition", true)->value; }
compatible_prints_condition()182     std::string&        compatible_prints_condition() {
183 		assert(this->type == TYPE_FILAMENT || this->type == TYPE_SLA_MATERIAL);
184         return Preset::compatible_prints_condition(this->config);
185     }
compatible_prints_condition() const186     const std::string&  compatible_prints_condition() const { return const_cast<Preset*>(this)->compatible_prints_condition(); }
187 
188     // Returns the "compatible_printers_condition".
compatible_printers_condition(DynamicPrintConfig & cfg)189     static std::string& compatible_printers_condition(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("compatible_printers_condition", true)->value; }
compatible_printers_condition()190     std::string&        compatible_printers_condition() {
191 		assert(this->type == TYPE_PRINT || this->type == TYPE_SLA_PRINT || this->type == TYPE_FILAMENT || this->type == TYPE_SLA_MATERIAL);
192         return Preset::compatible_printers_condition(this->config);
193     }
compatible_printers_condition() const194     const std::string&  compatible_printers_condition() const { return const_cast<Preset*>(this)->compatible_printers_condition(); }
195 
196     // Return a printer technology, return ptFFF if the printer technology is not set.
printer_technology(const DynamicPrintConfig & cfg)197     static PrinterTechnology printer_technology(const DynamicPrintConfig &cfg) {
198         auto *opt = cfg.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
199         // The following assert may trigger when importing some legacy profile,
200         // but it is safer to keep it here to capture the cases where the "printer_technology" key is queried, where it should not.
201 //        assert(opt != nullptr);
202         return (opt == nullptr) ? ptFFF : opt->value;
203     }
printer_technology() const204     PrinterTechnology   printer_technology() const { return Preset::printer_technology(this->config); }
205     // This call returns a reference, it may add a new entry into the DynamicPrintConfig.
printer_technology_ref()206     PrinterTechnology&  printer_technology_ref() { return this->config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value; }
207 
208     // Set is_visible according to application config
209     void                set_visible_from_appconfig(const AppConfig &app_config);
210 
211     // Resize the extruder specific fields, initialize them with the content of the 1st extruder.
set_num_extruders(unsigned int n)212     void                set_num_extruders(unsigned int n) { this->config.set_num_extruders(n); }
213 
214     // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection.
operator <(const Preset & other) const215     bool                operator<(const Preset &other) const { return this->name < other.name; }
216 
217     static const std::vector<std::string>&  print_options();
218     static const std::vector<std::string>&  filament_options();
219     // Printer options contain the nozzle options.
220     static const std::vector<std::string>&  printer_options();
221     // Nozzle options of the printer options.
222     static const std::vector<std::string>&  nozzle_options();
223     // Printer machine limits, those are contained in printer_options().
224     static const std::vector<std::string>&  machine_limits_options();
225 
226     static const std::vector<std::string>&  sla_printer_options();
227     static const std::vector<std::string>&  sla_material_options();
228     static const std::vector<std::string>&  sla_print_options();
229 
230 	static void                             update_suffix_modified(const std::string& new_suffix_modified);
231     static const std::string&               suffix_modified();
232     static std::string                      remove_suffix_modified(const std::string& name);
233     static void                             normalize(DynamicPrintConfig &config);
234     // Report configuration fields, which are misplaced into a wrong group, remove them from the config.
235     static std::string                      remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config);
236 
237 protected:
238     friend class        PresetCollection;
239     friend class        PresetBundle;
240 };
241 
242 bool is_compatible_with_print  (const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print, const PresetWithVendorProfile &active_printer);
243 bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config);
244 bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer);
245 
246 enum class PresetSelectCompatibleType {
247 	// Never select a compatible preset if the newly selected profile is not compatible.
248 	Never,
249 	// Only select a compatible preset if the active profile used to be compatible, but it is no more.
250 	OnlyIfWasCompatible,
251 	// Always select a compatible preset if the active profile is no more compatible.
252 	Always
253 };
254 
255 // Substitutions having been performed during parsing a single configuration file.
256 struct PresetConfigSubstitutions {
257     // User readable preset name.
258     std::string                             preset_name;
259     // Type of the preset (Print / Filament / Printer ...)
260     Preset::Type                            preset_type;
261     enum class Source {
262         UserFile,
263         ConfigBundle,
264     };
265     Source                                  preset_source;
266     // Source of the preset. It may be empty in case of a ConfigBundle being loaded.
267     std::string                             preset_file;
268     // What config value has been substituted with what.
269     ConfigSubstitutions                     substitutions;
270 };
271 
272 // Substitutions having been performed during parsing a set of configuration files, for example when starting up
273 // PrusaSlicer and reading the user Print / Filament / Printer profiles.
274 using PresetsConfigSubstitutions = std::vector<PresetConfigSubstitutions>;
275 
276 // Collections of presets of the same type (one of the Print, Filament or Printer type).
277 class PresetCollection
278 {
279 public:
280     // Initialize the PresetCollection with the "- default -" preset.
281     PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -");
282     ~PresetCollection();
283 
284     typedef std::deque<Preset>::iterator Iterator;
285     typedef std::deque<Preset>::const_iterator ConstIterator;
begin()286     Iterator        begin() { return m_presets.begin() + m_num_default_presets; }
begin() const287     ConstIterator   begin() const { return m_presets.cbegin() + m_num_default_presets; }
cbegin() const288     ConstIterator   cbegin() const { return m_presets.cbegin() + m_num_default_presets; }
end()289     Iterator        end() { return m_presets.end(); }
end() const290     ConstIterator   end() const { return m_presets.cend(); }
cend() const291     ConstIterator   cend() const { return m_presets.cend(); }
292 
293     void            reset(bool delete_files);
294 
type() const295     Preset::Type    type() const { return m_type; }
296     // Name, to be used on the screen and in error messages. Not localized.
297     std::string     name() const;
298     // Name, to be used as a section name in config bundle, and as a folder name for presets.
299     std::string     section_name() const;
operator ()() const300     const std::deque<Preset>& operator()() const { return m_presets; }
301 
302     // Add default preset at the start of the collection, increment the m_default_preset counter.
303     void            add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name);
304 
305     // Load ini files of the particular type from the provided directory path.
306     void            load_presets(const std::string &dir_path, const std::string &subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
307 
308     // Load a preset from an already parsed config file, insert it into the sorted sequence of presets
309     // and select it, losing previous modifications.
310     Preset&         load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true);
311     Preset&         load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true);
312 
313     // Returns a loaded preset, returns true if an existing preset was selected AND modified from config.
314     // In that case the successive filament loaded for a multi material printer should not be modified, but
315     // an external preset should be created instead.
316     enum class LoadAndSelect {
317         // Never select
318         Never,
319         // Always select
320         Always,
321         // Select a profile only if it was modified.
322         OnlyIfModified,
323     };
324     std::pair<Preset*, bool> load_external_preset(
325         // Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
326         const std::string           &path,
327         // Name of the profile, derived from the source file name.
328         const std::string           &name,
329         // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
330         const std::string           &original_name,
331         // Config to initialize the preset from.
332         const DynamicPrintConfig    &config,
333         // Select the preset after loading?
334         LoadAndSelect                select = LoadAndSelect::Always);
335 
336     // Save the preset under a new name. If the name is different from the old one,
337     // a new preset is stored into the list of presets.
338     // All presets are marked as not modified and the new preset is activated.
339     void            save_current_preset(const std::string &new_name, bool detach = false);
340 
341     // Delete the current preset, activate the first visible preset.
342     // returns true if the preset was deleted successfully.
343     bool            delete_current_preset();
344     // Delete the current preset, activate the first visible preset.
345     // returns true if the preset was deleted successfully.
346     bool            delete_preset(const std::string& name);
347 
348     // Enable / disable the "- default -" preset.
349     void            set_default_suppressed(bool default_suppressed);
is_default_suppressed() const350     bool            is_default_suppressed() const { return m_default_suppressed; }
351 
352     // Select a preset. If an invalid index is provided, the first visible preset is selected.
353     Preset&         select_preset(size_t idx);
354     // Return the selected preset, without the user modifications applied.
get_selected_preset()355     Preset&         get_selected_preset()       { return m_presets[m_idx_selected]; }
get_selected_preset() const356     const Preset&   get_selected_preset() const { return m_presets[m_idx_selected]; }
get_selected_idx() const357     size_t          get_selected_idx()    const { return m_idx_selected; }
358     // Returns the name of the selected preset, or an empty string if no preset is selected.
get_selected_preset_name() const359     std::string     get_selected_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_preset().name; }
360     // For the current edited preset, return the parent preset if there is one.
361     // If there is no parent preset, nullptr is returned.
362     // The parent preset may be a system preset or a user preset, which will be
363     // reflected by the UI.
364     const Preset*   get_selected_preset_parent() const;
365 	// Get parent preset for a child preset, based on the "inherits" field of a child,
366 	// where the "inherits" profile name is searched for in both m_presets and m_map_system_profile_renamed.
367 	const Preset*	get_preset_parent(const Preset& child) const;
368 	// Return the selected preset including the user modifications.
get_edited_preset()369     Preset&         get_edited_preset()         { return m_edited_preset; }
get_edited_preset() const370     const Preset&   get_edited_preset() const   { return m_edited_preset; }
371 
372     // Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
373     PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
get_edited_preset_with_vendor_profile() const374     PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
375 
376     const std::string& 		get_preset_name_by_alias(const std::string& alias) const;
377 	const std::string*		get_preset_name_renamed(const std::string &old_name) const;
378 
379 	// used to update preset_choice from Tab
get_presets() const380 	const std::deque<Preset>&	get_presets() const	{ return m_presets; }
get_idx_selected()381     size_t                      get_idx_selected()	{ return m_idx_selected; }
382 	static const std::string&	get_suffix_modified();
383 
384     // Return a preset possibly with modifications.
default_preset(size_t idx=0)385 	Preset&			default_preset(size_t idx = 0)		 { assert(idx < m_num_default_presets); return m_presets[idx]; }
default_preset(size_t idx=0) const386 	const Preset&   default_preset(size_t idx = 0) const { assert(idx < m_num_default_presets); return m_presets[idx]; }
default_preset_for(const DynamicPrintConfig &) const387 	virtual const Preset& default_preset_for(const DynamicPrintConfig & /* config */) const { return this->default_preset(); }
388     // Return a preset by an index. If the preset is active, a temporary copy is returned.
preset(size_t idx)389     Preset&         preset(size_t idx)          { return (idx == m_idx_selected) ? m_edited_preset : m_presets[idx]; }
preset(size_t idx) const390     const Preset&   preset(size_t idx) const    { return const_cast<PresetCollection*>(this)->preset(idx); }
discard_current_changes()391     void            discard_current_changes()   { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; }
392 
393     // Return a preset by its name. If the preset is active, a temporary copy is returned.
394     // If a preset is not found by its name, null is returned.
395     Preset*         find_preset(const std::string &name, bool first_visible_if_not_found = false);
find_preset(const std::string & name,bool first_visible_if_not_found=false) const396     const Preset*   find_preset(const std::string &name, bool first_visible_if_not_found = false) const
397         { return const_cast<PresetCollection*>(this)->find_preset(name, first_visible_if_not_found); }
398 
399     size_t          first_visible_idx() const;
400     // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
401     // If one of the prefered_alternates is compatible, select it.
402     template<typename PreferedCondition>
first_compatible_idx(PreferedCondition prefered_condition) const403     size_t          first_compatible_idx(PreferedCondition prefered_condition) const
404     {
405         size_t i = m_default_suppressed ? m_num_default_presets : 0;
406         size_t n = this->m_presets.size();
407         size_t i_compatible = n;
408         int    match_quality = -1;
409         for (; i < n; ++ i)
410             // Since we use the filament selection from Wizard, it's needed to control the preset visibility too
411             if (m_presets[i].is_compatible && m_presets[i].is_visible) {
412                 int this_match_quality = prefered_condition(m_presets[i]);
413                 if (this_match_quality > match_quality) {
414                     if (match_quality == std::numeric_limits<int>::max())
415                         // Better match will not be found.
416                         return i;
417                     // Store the first compatible profile with highest match quality into i_compatible.
418                     i_compatible = i;
419                     match_quality = this_match_quality;
420                 }
421             }
422         return (i_compatible == n) ?
423             // No compatible preset found, return the default preset.
424             0 :
425             // Compatible preset found.
426             i_compatible;
427     }
428     // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
__anon9cc1ea150102(const Preset&) 429     size_t          first_compatible_idx() const { return this->first_compatible_idx([](const Preset&) -> int { return 0; }); }
430 
431     // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
432     // Return the first visible preset. Certainly at least the '- default -' preset shall be visible.
first_visible()433     Preset&         first_visible()             { return this->preset(this->first_visible_idx()); }
first_visible() const434     const Preset&   first_visible() const       { return this->preset(this->first_visible_idx()); }
first_compatible()435     Preset&         first_compatible()          { return this->preset(this->first_compatible_idx()); }
436     template<typename PreferedCondition>
first_compatible(PreferedCondition prefered_condition)437     Preset&         first_compatible(PreferedCondition prefered_condition) { return this->preset(this->first_compatible_idx(prefered_condition)); }
first_compatible() const438     const Preset&   first_compatible() const    { return this->preset(this->first_compatible_idx()); }
439 
440     // Return number of presets including the "- default -" preset.
size() const441     size_t          size() const                { return m_presets.size(); }
has_defaults_only() const442     bool            has_defaults_only() const   { return m_presets.size() <= m_num_default_presets; }
443 
444     // For Print / Filament presets, disable those, which are not compatible with the printer.
445     template<typename PreferedCondition>
update_compatible(const PresetWithVendorProfile & active_printer,const PresetWithVendorProfile * active_print,PresetSelectCompatibleType select_other_if_incompatible,PreferedCondition prefered_condition)446     void            update_compatible(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType select_other_if_incompatible, PreferedCondition prefered_condition)
447     {
448         if (this->update_compatible_internal(active_printer, active_print, select_other_if_incompatible) == (size_t)-1)
449             // Find some other compatible preset, or the "-- default --" preset.
450             this->select_preset(this->first_compatible_idx(prefered_condition));
451     }
update_compatible(const PresetWithVendorProfile & active_printer,const PresetWithVendorProfile * active_print,PresetSelectCompatibleType select_other_if_incompatible)452     void            update_compatible(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType select_other_if_incompatible)
453         { this->update_compatible(active_printer, active_print, select_other_if_incompatible, [](const Preset&) -> int { return 0; }); }
454 
num_visible() const455     size_t          num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
456 
457     // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
current_is_dirty() const458     bool                        current_is_dirty() const { return ! this->current_dirty_options().empty(); }
459     // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
current_dirty_options(const bool deep_compare=false) const460     std::vector<std::string>    current_dirty_options(const bool deep_compare = false) const
461         { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); }
462     // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
current_different_from_parent_options(const bool deep_compare=false) const463     std::vector<std::string>    current_different_from_parent_options(const bool deep_compare = false) const
464         { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
465 
466     // Return a sorted list of system preset names.
467     // Used for validating the "inherits" flag when importing user's config bundles.
468     // Returns names of all system presets including the former names of these presets.
469     std::vector<std::string>    system_preset_names() const;
470 
471     // Update a dirty flag of the current preset
472     // Return true if the dirty flag changed.
473     bool            update_dirty();
474 
475     // Select a profile by its name. Return true if the selection changed.
476     // Without force, the selection is only updated if the index changes.
477     // With force, the changes are reverted if the new index is the same as the old index.
478     bool            select_preset_by_name(const std::string &name, bool force);
479 
480     // Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
481     std::string     path_from_name(const std::string &new_name) const;
482 
num_default_presets()483     size_t num_default_presets() { return m_num_default_presets; }
484 
485 protected:
486     // Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
487     // This is a temporary state, which shall be fixed immediately by the following step.
488     bool            select_preset_by_name_strict(const std::string &name);
489 
490     // Merge one vendor's presets with the other vendor's presets, report duplicates.
491     std::vector<std::string> merge_presets(PresetCollection &&other, const VendorMap &new_vendors);
492 
493     // Update m_map_alias_to_profile_name from loaded system profiles.
494 	void 			update_map_alias_to_profile_name();
495 
496     // Update m_map_system_profile_renamed from loaded system profiles.
497     void 			update_map_system_profile_renamed();
498 
499 private:
500     PresetCollection();
501     PresetCollection(const PresetCollection &other);
502     PresetCollection& operator=(const PresetCollection &other);
503 
504     // Find a preset position in the sorted list of presets.
505     // The "-- default -- " preset is always the first, so it needs
506     // to be handled differently.
507     // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
find_preset_internal(const std::string & name)508     std::deque<Preset>::iterator find_preset_internal(const std::string &name)
509     {
510         auto it = Slic3r::lower_bound_by_predicate(m_presets.begin() + m_num_default_presets, m_presets.end(), [&name](const auto& l) { return l.name < name;  });
511         if (it == m_presets.end() || it->name != name) {
512             // Preset has not been not found in the sorted list of non-default presets. Try the defaults.
513             for (size_t i = 0; i < m_num_default_presets; ++ i)
514                 if (m_presets[i].name == name) {
515                     it = m_presets.begin() + i;
516                     break;
517                 }
518         }
519         return it;
520     }
find_preset_internal(const std::string & name) const521     std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
522         { return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
find_preset_renamed(const std::string & name)523     std::deque<Preset>::iterator 	   find_preset_renamed(const std::string &name) {
524     	auto it_renamed = m_map_system_profile_renamed.find(name);
525     	auto it = (it_renamed == m_map_system_profile_renamed.end()) ? m_presets.end() : this->find_preset_internal(it_renamed->second);
526     	assert((it_renamed == m_map_system_profile_renamed.end()) || (it != m_presets.end() && it->name == it_renamed->second));
527     	return it;
528     }
find_preset_renamed(const std::string & name) const529     std::deque<Preset>::const_iterator find_preset_renamed(const std::string &name) const
530         { return const_cast<PresetCollection*>(this)->find_preset_renamed(name); }
531 
532     size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible);
533 
534     static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
535 
536     // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
537     Preset::Type            m_type;
538     // List of presets, starting with the "- default -" preset.
539     // Use deque to force the container to allocate an object per each entry,
540     // so that the addresses of the presets don't change during resizing of the container.
541     std::deque<Preset>      m_presets;
542     // System profiles may have aliases. Map to the full profile name.
543     std::vector<std::pair<std::string, std::string>> m_map_alias_to_profile_name;
544     // Map from old system profile name to a current system profile name.
545     std::map<std::string, std::string> m_map_system_profile_renamed;
546     // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
547     Preset                  m_edited_preset;
548     // Selected preset.
549     size_t                  m_idx_selected;
550     // Is the "- default -" preset suppressed?
551     bool                    m_default_suppressed  = true;
552     size_t                  m_num_default_presets = 0;
553 
554     // Path to the directory to store the config files into.
555     std::string             m_dir_path;
556 
557     // to access select_preset_by_name_strict()
558     friend class PresetBundle;
559 };
560 
561 // Printer supports the FFF and SLA technologies, with different set of configuration values,
562 // therefore this PresetCollection needs to handle two defaults.
563 class PrinterPresetCollection : public PresetCollection
564 {
565 public:
PrinterPresetCollection(Preset::Type type,const std::vector<std::string> & keys,const Slic3r::StaticPrintConfig & defaults,const std::string & default_name="- default -")566     PrinterPresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -") :
567 		PresetCollection(type, keys, defaults, default_name) {}
568     const Preset&   default_preset_for(const DynamicPrintConfig &config) const override;
569 
570     const Preset*   find_by_model_id(const std::string &model_id) const;
571 };
572 
573 namespace PresetUtils {
574 	// PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile.
575 	const VendorProfile::PrinterModel* system_printer_model(const Preset &preset);
576     std::string system_printer_bed_model(const Preset& preset);
577     std::string system_printer_bed_texture(const Preset& preset);
578 } // namespace PresetUtils
579 
580 
581 //////////////////////////////////////////////////////////////////////
582 
583 class PhysicalPrinter
584 {
585 public:
586     PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config);
587     PhysicalPrinter(const std::string& name, const DynamicPrintConfig &default_config, const Preset& preset);
588     void set_name(const std::string &name);
589 
590     // Name of the Physical Printer, usually derived form the file name.
591     std::string         name;
592     // File name of the Physical Printer.
593     std::string         file;
594     // Configuration data, loaded from a file, or set from the defaults.
595     DynamicPrintConfig  config;
596     // set of presets used with this physical printer
597     std::set<std::string> preset_names;
598 
599     // Has this profile been loaded?
600     bool                loaded = false;
601 
602     static std::string  separator();
603     static const std::vector<std::string>&  printer_options();
604     static const std::vector<std::string>&  print_host_options();
605     static std::vector<std::string>         presets_with_print_host_information(const PrinterPresetCollection& printer_presets);
606     static bool has_print_host_information(const DynamicPrintConfig& config);
607 
608     const std::set<std::string>&            get_preset_names() const;
609 
610     bool                has_empty_config() const;
611     void                update_preset_names_in_config();
612 
save()613     void                save() { this->config.save(this->file); }
614     void                save(const std::string& file_name_from, const std::string& file_name_to);
615 
616     void                update_from_preset(const Preset& preset);
617     void                update_from_config(const DynamicPrintConfig &new_config);
618 
619     // add preset to the preset_names
620     // return false, if preset with this name is already exist in the set
621     bool                add_preset(const std::string& preset_name);
622     bool                delete_preset(const std::string& preset_name);
623     void                reset_presets();
624 
625     // Return a printer technology, return ptFFF if the printer technology is not set.
printer_technology(const DynamicPrintConfig & cfg)626     static PrinterTechnology printer_technology(const DynamicPrintConfig& cfg) {
627         auto* opt = cfg.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
628         // The following assert may trigger when importing some legacy profile,
629         // but it is safer to keep it here to capture the cases where the "printer_technology" key is queried, where it should not.
630         return (opt == nullptr) ? ptFFF : opt->value;
631     }
printer_technology() const632     PrinterTechnology   printer_technology() const { return printer_technology(this->config); }
633 
634     // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection.
operator <(const PhysicalPrinter & other) const635     bool                operator<(const PhysicalPrinter& other) const { return this->name < other.name; }
636 
637     // get full printer name included a name of the preset
638     std::string         get_full_name(std::string preset_name) const;
639 
640     // get printer name from the full name uncluded preset name
641     static std::string  get_short_name(std::string full_name);
642 
643     // get preset name from the full name uncluded printer name
644     static std::string  get_preset_name(std::string full_name);
645 
646 protected:
647     friend class        PhysicalPrinterCollection;
648 };
649 
650 
651 // ---------------------------------
652 // ***  PhysicalPrinterCollection  ***
653 // ---------------------------------
654 
655 // Collections of physical printers
656 class PhysicalPrinterCollection
657 {
658 public:
659     PhysicalPrinterCollection(const std::vector<std::string>& keys);
~PhysicalPrinterCollection()660     ~PhysicalPrinterCollection() {}
661 
662     typedef std::deque<PhysicalPrinter>::iterator Iterator;
663     typedef std::deque<PhysicalPrinter>::const_iterator ConstIterator;
begin()664     Iterator        begin() { return m_printers.begin(); }
begin() const665     ConstIterator   begin() const { return m_printers.cbegin(); }
cbegin() const666     ConstIterator   cbegin() const { return m_printers.cbegin(); }
end()667     Iterator        end() { return m_printers.end(); }
end() const668     ConstIterator   end() const { return m_printers.cend(); }
cend() const669     ConstIterator   cend() const { return m_printers.cend(); }
670 
empty() const671     bool            empty() const {return m_printers.empty(); }
672 
reset(bool delete_files)673     void            reset(bool delete_files) {};
674 
operator ()() const675     const std::deque<PhysicalPrinter>& operator()() const { return m_printers; }
676 
677     // Load ini files of the particular type from the provided directory path.
678     void            load_printers(const std::string& dir_path, const std::string& subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
679     void            load_printers_from_presets(PrinterPresetCollection &printer_presets);
680     // Load printer from the loaded configuration
681     void            load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select, bool save=false);
682 
683     // Save the printer under a new name. If the name is different from the old one,
684     // a new printer is stored into the list of printers.
685     // New printer is activated.
686     void            save_printer(PhysicalPrinter& printer, const std::string& renamed_from = "");
687 
688     // Delete the current preset, activate the first visible preset.
689     // returns true if the preset was deleted successfully.
690     bool            delete_printer(const std::string& name);
691     // Delete the selected preset
692     // returns true if the preset was deleted successfully.
693     bool            delete_selected_printer();
694     // Delete preset_name preset from all printers:
695     // If there is last preset for the printer and first_check== false, then delete this printer
696     // returns true if all presets were deleted successfully.
697     bool            delete_preset_from_printers(const std::string& preset_name);
698 
699     // Get list of printers which have more than one preset and "preset_name" preset is one of them
700     std::vector<std::string> get_printers_with_preset( const std::string &preset_name);
701     // Get list of printers which has only "preset_name" preset
702     std::vector<std::string> get_printers_with_only_preset( const std::string &preset_name);
703 
704     // Return the selected preset, without the user modifications applied.
get_selected_printer()705     PhysicalPrinter&        get_selected_printer() { return m_printers[m_idx_selected]; }
get_selected_printer() const706     const PhysicalPrinter&  get_selected_printer() const { return m_printers[m_idx_selected]; }
707 
get_selected_idx() const708     size_t                  get_selected_idx()    const { return m_idx_selected; }
709     // Returns the name of the selected preset, or an empty string if no preset is selected.
get_selected_printer_name() const710     std::string             get_selected_printer_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : this->get_selected_printer().name; }
711     // Returns the config of the selected printer, or nullptr if no printer is selected.
get_selected_printer_config()712     DynamicPrintConfig*     get_selected_printer_config() { return (m_idx_selected == size_t(-1)) ? nullptr : &(this->get_selected_printer().config); }
713     // Returns the config of the selected printer, or nullptr if no printer is selected.
get_selected_printer_technology()714     PrinterTechnology       get_selected_printer_technology() { return (m_idx_selected == size_t(-1)) ? PrinterTechnology::ptAny : this->get_selected_printer().printer_technology(); }
715 
716     // Each physical printer can have a several related preset,
717     // so, use the next functions to get an exact names of selections in the list:
718     // Returns the full name of the selected printer, or an empty string if no preset is selected.
719     std::string     get_selected_full_printer_name() const;
720     // Returns the printer model of the selected preset, or an empty string if no preset is selected.
get_selected_printer_preset_name() const721     std::string     get_selected_printer_preset_name() const { return (m_idx_selected == size_t(-1)) ? std::string() : m_selected_preset; }
722 
723     // Select printer by the full printer name, which contains name of printer, separator and name of selected preset
724     // If full_name doesn't contain name of selected preset, then select first preset in the list for this printer
725     void select_printer(const std::string& full_name);
726     void select_printer(const PhysicalPrinter& printer);
727     void select_printer(const std::string& printer_name, const std::string& preset_name);
728     bool has_selection() const;
729     void unselect_printer() ;
730     bool is_selected(ConstIterator it, const std::string &preset_name) const;
731 
732     // Return a printer by an index. If the printer is active, a temporary copy is returned.
printer(size_t idx)733     PhysicalPrinter& printer(size_t idx) { return m_printers[idx]; }
printer(size_t idx) const734     const PhysicalPrinter& printer(size_t idx) const { return const_cast<PhysicalPrinterCollection*>(this)->printer(idx); }
735 
736     // Return a preset by its name. If the preset is active, a temporary copy is returned.
737     // If a preset is not found by its name, null is returned.
738     // It is possible case (in)sensitive search
739     PhysicalPrinter* find_printer(const std::string& name, bool case_sensitive_search = true);
find_printer(const std::string & name,bool case_sensitive_search=true) const740     const PhysicalPrinter* find_printer(const std::string& name, bool case_sensitive_search = true) const
741     {
742         return const_cast<PhysicalPrinterCollection*>(this)->find_printer(name, case_sensitive_search);
743     }
744 
745     // Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
746     std::string     path_from_name(const std::string& new_name) const;
747 
default_config() const748     const DynamicPrintConfig& default_config() const { return m_default_config; }
749 
750 private:
751     PhysicalPrinterCollection& operator=(const PhysicalPrinterCollection& other);
752 
753     // Find a physical printer position in the sorted list of printers.
754     // The name of a printer should be unique and case insensitive
755     // Use this functions with case_sensitive_search = false, when you need case insensitive search
756     std::deque<PhysicalPrinter>::iterator find_printer_internal(const std::string& name, bool case_sensitive_search = true);
find_printer_internal(const std::string & name,bool case_sensitive_search=true) const757     std::deque<PhysicalPrinter>::const_iterator find_printer_internal(const std::string& name, bool case_sensitive_search = true) const
758     {
759         return const_cast<PhysicalPrinterCollection*>(this)->find_printer_internal(name);
760     }
761 
762     PhysicalPrinter* find_printer_with_same_config( const DynamicPrintConfig &config);
763 
764     // List of printers
765     // Use deque to force the container to allocate an object per each entry,
766     // so that the addresses of the presets don't change during resizing of the container.
767     std::deque<PhysicalPrinter> m_printers;
768 
769     // Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options().
770     DynamicPrintConfig          m_default_config;
771 
772     // Selected printer.
773     size_t                      m_idx_selected = size_t(-1);
774     // The name of the preset which is currently select for this printer
775     std::string                 m_selected_preset;
776 
777     // Path to the directory to store the config files into.
778     std::string                 m_dir_path;
779 };
780 
781 
782 } // namespace Slic3r
783 
784 #endif /* slic3r_Preset_hpp_ */
785