mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-10 07:15:08 -06:00
commit
3deeda0f73
105 changed files with 8069 additions and 1964 deletions
|
@ -170,10 +170,14 @@ add_library(libslic3r STATIC
|
|||
)
|
||||
|
||||
add_library(libslic3r_gui STATIC
|
||||
${LIBDIR}/slic3r/GUI/AboutDialog.cpp
|
||||
${LIBDIR}/slic3r/GUI/AboutDialog.hpp
|
||||
${LIBDIR}/slic3r/GUI/AppConfig.cpp
|
||||
${LIBDIR}/slic3r/GUI/AppConfig.hpp
|
||||
${LIBDIR}/slic3r/GUI/BitmapCache.cpp
|
||||
${LIBDIR}/slic3r/GUI/BitmapCache.hpp
|
||||
${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.cpp
|
||||
${LIBDIR}/slic3r/GUI/ConfigSnapshotDialog.hpp
|
||||
${LIBDIR}/slic3r/GUI/3DScene.cpp
|
||||
${LIBDIR}/slic3r/GUI/3DScene.hpp
|
||||
${LIBDIR}/slic3r/GUI/GLShader.cpp
|
||||
|
@ -208,18 +212,30 @@ add_library(libslic3r_gui STATIC
|
|||
${LIBDIR}/slic3r/GUI/RammingChart.hpp
|
||||
${LIBDIR}/slic3r/GUI/BonjourDialog.cpp
|
||||
${LIBDIR}/slic3r/GUI/BonjourDialog.hpp
|
||||
${LIBDIR}/slic3r/GUI/ButtonsDescription.cpp
|
||||
${LIBDIR}/slic3r/GUI/ButtonsDescription.hpp
|
||||
${LIBDIR}/slic3r/Config/Snapshot.cpp
|
||||
${LIBDIR}/slic3r/Config/Snapshot.hpp
|
||||
${LIBDIR}/slic3r/Config/Version.cpp
|
||||
${LIBDIR}/slic3r/Config/Version.hpp
|
||||
${LIBDIR}/slic3r/Utils/ASCIIFolding.cpp
|
||||
${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp
|
||||
${LIBDIR}/slic3r/GUI/ConfigWizard.cpp
|
||||
${LIBDIR}/slic3r/GUI/ConfigWizard.hpp
|
||||
${LIBDIR}/slic3r/GUI/MsgDialog.cpp
|
||||
${LIBDIR}/slic3r/GUI/MsgDialog.hpp
|
||||
${LIBDIR}/slic3r/GUI/UpdateDialogs.cpp
|
||||
${LIBDIR}/slic3r/GUI/UpdateDialogs.hpp
|
||||
${LIBDIR}/slic3r/Utils/Http.cpp
|
||||
${LIBDIR}/slic3r/Utils/Http.hpp
|
||||
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
|
||||
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
|
||||
${LIBDIR}/slic3r/Utils/Bonjour.cpp
|
||||
${LIBDIR}/slic3r/Utils/Bonjour.hpp
|
||||
${LIBDIR}/slic3r/Utils/PresetUpdater.cpp
|
||||
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
|
||||
${LIBDIR}/slic3r/Utils/Time.cpp
|
||||
${LIBDIR}/slic3r/Utils/Time.hpp
|
||||
)
|
||||
|
||||
add_library(admesh STATIC
|
||||
|
@ -365,6 +381,7 @@ set(XS_XSP_FILES
|
|||
${XSP_DIR}/SurfaceCollection.xsp
|
||||
${XSP_DIR}/TriangleMesh.xsp
|
||||
${XSP_DIR}/Utils_OctoPrint.xsp
|
||||
${XSP_DIR}/Utils_PresetUpdater.xsp
|
||||
${XSP_DIR}/XS.xsp
|
||||
)
|
||||
foreach (file ${XS_XSP_FILES})
|
||||
|
@ -410,7 +427,7 @@ if(APPLE)
|
|||
# Ignore undefined symbols of the perl interpreter, they will be found in the caller image.
|
||||
target_link_libraries(XS "-undefined dynamic_lookup")
|
||||
endif()
|
||||
target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri)
|
||||
target_link_libraries(XS libslic3r libslic3r_gui admesh miniz clipper nowide polypartition poly2tri semver)
|
||||
if(SLIC3R_PROFILE)
|
||||
target_link_libraries(XS Shiny)
|
||||
endif()
|
||||
|
@ -537,13 +554,13 @@ if (SLIC3R_PRUSACONTROL)
|
|||
set(wxWidgets_UseAlienWx 1)
|
||||
if (wxWidgets_UseAlienWx)
|
||||
set(AlienWx_DEBUG 1)
|
||||
find_package(AlienWx REQUIRED COMPONENTS base core adv)
|
||||
find_package(AlienWx REQUIRED COMPONENTS base core adv html)
|
||||
include_directories(${AlienWx_INCLUDE_DIRS})
|
||||
#add_compile_options(${AlienWx_CXX_FLAGS})
|
||||
add_definitions(${AlienWx_DEFINITIONS})
|
||||
set(wxWidgets_LIBRARIES ${AlienWx_LIBRARIES})
|
||||
else ()
|
||||
find_package(wxWidgets REQUIRED COMPONENTS base core adv)
|
||||
find_package(wxWidgets REQUIRED COMPONENTS base core adv html)
|
||||
include(${wxWidgets_USE_FILE})
|
||||
endif ()
|
||||
add_definitions(-DSLIC3R_GUI -DSLIC3R_PRUS)
|
||||
|
@ -623,6 +640,7 @@ add_custom_command(
|
|||
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:XS>" "${PERL_LOCAL_LIB_DIR}/auto/Slic3r/XS/"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${PERL_LOCAL_LIB_DIR}/Slic3r/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/xs/lib/Slic3r/XS.pm" "${PERL_LOCAL_LIB_DIR}/Slic3r/"
|
||||
COMMENT "Installing XS.pm and XS.{so,dll,bundle} into the local-lib directory ..."
|
||||
)
|
||||
if(APPLE)
|
||||
add_custom_command(
|
||||
|
|
|
@ -12,7 +12,7 @@ our $VERSION = '0.01';
|
|||
BEGIN {
|
||||
if ($^O eq 'MSWin32') {
|
||||
eval "use Wx";
|
||||
# eval "use Wx::Html";
|
||||
eval "use Wx::Html";
|
||||
eval "use Wx::Print"; # because of some Wx bug, thread creation fails if we don't have this (looks like Wx::Printout is hard-coded in some thread cleanup code)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,6 +206,44 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
|
|||
return diff;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase *this_c)
|
||||
{
|
||||
const T* opt_init = static_cast<const T*>(other.option(opt_key));
|
||||
const T* opt_cur = static_cast<const T*>(this_c->option(opt_key));
|
||||
int opt_init_max_id = opt_init->values.size() - 1;
|
||||
for (int i = 0; i < opt_cur->values.size(); i++)
|
||||
{
|
||||
int init_id = i <= opt_init_max_id ? i : 0;
|
||||
if (opt_cur->values[i] != opt_init->values[init_id])
|
||||
vec.emplace_back(opt_key + "#" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
t_config_option_keys ConfigBase::deep_diff(const ConfigBase &other) const
|
||||
{
|
||||
t_config_option_keys diff;
|
||||
for (const t_config_option_key &opt_key : this->keys()) {
|
||||
const ConfigOption *this_opt = this->option(opt_key);
|
||||
const ConfigOption *other_opt = other.option(opt_key);
|
||||
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
|
||||
{
|
||||
if (opt_key == "bed_shape"){ diff.emplace_back(opt_key); continue; }
|
||||
switch (other_opt->type())
|
||||
{
|
||||
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, other, this); break;
|
||||
case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, other, this); break;
|
||||
case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, other, this); break;
|
||||
case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, other, this); break;
|
||||
case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, other, this); break;
|
||||
case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, other, this); break;
|
||||
default: diff.emplace_back(opt_key); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
|
||||
{
|
||||
t_config_option_keys equal;
|
||||
|
|
|
@ -659,6 +659,7 @@ public:
|
|||
ConfigOptionPoints() : ConfigOptionVector<Pointf>() {}
|
||||
explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector<Pointf>(n, value) {}
|
||||
explicit ConfigOptionPoints(std::initializer_list<Pointf> il) : ConfigOptionVector<Pointf>(std::move(il)) {}
|
||||
explicit ConfigOptionPoints(const std::vector<Pointf> &values) : ConfigOptionVector<Pointf>(values) {}
|
||||
|
||||
static ConfigOptionType static_type() { return coPoints; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
|
@ -1046,6 +1047,9 @@ public:
|
|||
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
|
||||
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
|
||||
t_config_option_keys diff(const ConfigBase &other) const;
|
||||
// Use deep_diff to correct return of changed options,
|
||||
// considering individual options for each extruder
|
||||
t_config_option_keys deep_diff(const ConfigBase &other) const;
|
||||
t_config_option_keys equal(const ConfigBase &other) const;
|
||||
std::string serialize(const t_config_option_key &opt_key) const;
|
||||
// Set a configuration value from a string, it will call an overridable handle_legacy()
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "libslic3r.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -15,6 +16,9 @@ public:
|
|||
file_parser_error(const std::string &msg, const std::string &file, unsigned long line = 0) :
|
||||
std::runtime_error(format_what(msg, file, line)),
|
||||
m_message(msg), m_filename(file), m_line(line) {}
|
||||
file_parser_error(const std::string &msg, const boost::filesystem::path &file, unsigned long line = 0) :
|
||||
std::runtime_error(format_what(msg, file.string(), line)),
|
||||
m_message(msg), m_filename(file.string()), m_line(line) {}
|
||||
// gcc 3.4.2 complains about lack of throw specifier on compiler
|
||||
// generated dtor
|
||||
~file_parser_error() throw() {}
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
#include <Eigen/Dense>
|
||||
#include <miniz/miniz_zip.h>
|
||||
|
||||
// VERSION NUMBERS
|
||||
// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
|
||||
// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
|
||||
const unsigned int VERSION_3MF = 1;
|
||||
const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
|
||||
|
||||
const std::string MODEL_FOLDER = "3D/";
|
||||
const std::string MODEL_EXTENSION = ".model";
|
||||
const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
|
||||
|
@ -23,6 +29,7 @@ const std::string CONTENT_TYPES_FILE = "[Content_Types].xml";
|
|||
const std::string RELATIONSHIPS_FILE = "_rels/.rels";
|
||||
const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
|
||||
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
|
||||
const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
|
||||
|
||||
const char* MODEL_TAG = "model";
|
||||
const char* RESOURCES_TAG = "resources";
|
||||
|
@ -36,9 +43,9 @@ const char* COMPONENTS_TAG = "components";
|
|||
const char* COMPONENT_TAG = "component";
|
||||
const char* BUILD_TAG = "build";
|
||||
const char* ITEM_TAG = "item";
|
||||
const char* METADATA_TAG = "metadata";
|
||||
|
||||
const char* CONFIG_TAG = "config";
|
||||
const char* METADATA_TAG = "metadata";
|
||||
const char* VOLUME_TAG = "volume";
|
||||
|
||||
const char* UNIT_ATTR = "unit";
|
||||
|
@ -315,6 +322,10 @@ namespace Slic3r {
|
|||
typedef std::vector<Instance> InstancesList;
|
||||
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
|
||||
typedef std::map<int, Geometry> IdToGeometryMap;
|
||||
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
|
||||
|
||||
// Version of the 3mf file
|
||||
unsigned int m_version;
|
||||
|
||||
XML_Parser m_xml_parser;
|
||||
Model* m_model;
|
||||
|
@ -326,6 +337,9 @@ namespace Slic3r {
|
|||
IdToGeometryMap m_geometries;
|
||||
CurrentConfig m_curr_config;
|
||||
IdToMetadataMap m_objects_metadata;
|
||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||
std::string m_curr_metadata_name;
|
||||
std::string m_curr_characters;
|
||||
|
||||
public:
|
||||
_3MF_Importer();
|
||||
|
@ -339,12 +353,14 @@ namespace Slic3r {
|
|||
|
||||
bool _load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle);
|
||||
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
bool _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename);
|
||||
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename);
|
||||
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
|
||||
|
||||
// handlers to parse the .model file
|
||||
void _handle_start_model_xml_element(const char* name, const char** attributes);
|
||||
void _handle_end_model_xml_element(const char* name);
|
||||
void _handle_model_xml_characters(const XML_Char* s, int len);
|
||||
|
||||
// handlers to parse the MODEL_CONFIG_FILE file
|
||||
void _handle_start_config_xml_element(const char* name, const char** attributes);
|
||||
|
@ -386,6 +402,9 @@ namespace Slic3r {
|
|||
bool _handle_start_item(const char** attributes, unsigned int num_attributes);
|
||||
bool _handle_end_item();
|
||||
|
||||
bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
|
||||
bool _handle_end_metadata();
|
||||
|
||||
bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter);
|
||||
|
||||
void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix);
|
||||
|
@ -407,6 +426,7 @@ namespace Slic3r {
|
|||
// callbacks to parse the .model file
|
||||
static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
|
||||
static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name);
|
||||
static void XMLCALL _handle_model_xml_characters(void* userData, const XML_Char* s, int len);
|
||||
|
||||
// callbacks to parse the MODEL_CONFIG_FILE file
|
||||
static void XMLCALL _handle_start_config_xml_element(void* userData, const char* name, const char** attributes);
|
||||
|
@ -414,9 +434,12 @@ namespace Slic3r {
|
|||
};
|
||||
|
||||
_3MF_Importer::_3MF_Importer()
|
||||
: m_xml_parser(nullptr)
|
||||
: m_version(0)
|
||||
, m_xml_parser(nullptr)
|
||||
, m_model(nullptr)
|
||||
, m_unit_factor(1.0f)
|
||||
, m_curr_metadata_name("")
|
||||
, m_curr_characters("")
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -427,6 +450,7 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle)
|
||||
{
|
||||
m_version = 0;
|
||||
m_model = &model;
|
||||
m_unit_factor = 1.0f;
|
||||
m_curr_object.reset();
|
||||
|
@ -437,6 +461,9 @@ namespace Slic3r {
|
|||
m_curr_config.object_id = -1;
|
||||
m_curr_config.volume_id = -1;
|
||||
m_objects_metadata.clear();
|
||||
m_layer_heights_profiles.clear();
|
||||
m_curr_metadata_name.clear();
|
||||
m_curr_characters.clear();
|
||||
clear_errors();
|
||||
|
||||
return _load_model_from_file(filename, model, bundle);
|
||||
|
@ -472,6 +499,8 @@ namespace Slic3r {
|
|||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
|
||||
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
||||
|
@ -489,15 +518,26 @@ namespace Slic3r {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we then loop again the entries to read other files stored in the archive
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
||||
{
|
||||
std::string name(stat.m_filename);
|
||||
std::replace(name.begin(), name.end(), '\\', '/');
|
||||
|
||||
if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE))
|
||||
{
|
||||
// extract slic3r lazer heights profile file
|
||||
_extract_layer_heights_profile_config_from_archive(archive, stat);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
|
||||
{
|
||||
// extract slic3r print config file
|
||||
if (!_extract_print_config_from_archive(archive, stat, bundle, filename))
|
||||
{
|
||||
mz_zip_reader_end(&archive);
|
||||
add_error("Archive does not contain a valid print config");
|
||||
return false;
|
||||
}
|
||||
_extract_print_config_from_archive(archive, stat, bundle, filename);
|
||||
}
|
||||
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE))
|
||||
{
|
||||
|
@ -526,6 +566,13 @@ namespace Slic3r {
|
|||
return false;
|
||||
}
|
||||
|
||||
IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
|
||||
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
|
||||
{
|
||||
object.second->layer_height_profile = obj_layer_heights_profile->second;
|
||||
object.second->layer_height_profile_valid = true;
|
||||
}
|
||||
|
||||
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
|
||||
if (obj_metadata != m_objects_metadata.end())
|
||||
{
|
||||
|
@ -583,6 +630,7 @@ namespace Slic3r {
|
|||
|
||||
XML_SetUserData(m_xml_parser, (void*)this);
|
||||
XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_model_xml_element, _3MF_Importer::_handle_end_model_xml_element);
|
||||
XML_SetCharacterDataHandler(m_xml_parser, _3MF_Importer::_handle_model_xml_characters);
|
||||
|
||||
void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
|
||||
if (parser_buffer == nullptr)
|
||||
|
@ -609,23 +657,90 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename)
|
||||
void _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename)
|
||||
{
|
||||
if (stat.m_uncomp_size > 0)
|
||||
{
|
||||
std::vector<char> buffer((size_t)stat.m_uncomp_size + 1, 0);
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
add_error("Error while reading config data to buffer");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.back() = '\0';
|
||||
bundle.load_config_string(buffer.data(), archive_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
void _3MF_Importer::_extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
||||
{
|
||||
if (stat.m_uncomp_size > 0)
|
||||
{
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
add_error("Error while reading layer heights profile data to buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.back() == '\n')
|
||||
buffer.pop_back();
|
||||
|
||||
std::vector<std::string> objects;
|
||||
boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
|
||||
|
||||
for (const std::string& object : objects)
|
||||
{
|
||||
std::vector<std::string> object_data;
|
||||
boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
|
||||
if (object_data.size() != 2)
|
||||
{
|
||||
add_error("Error while reading object data");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> object_data_id;
|
||||
boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
|
||||
if (object_data_id.size() != 2)
|
||||
{
|
||||
add_error("Error while reading object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
int object_id = std::atoi(object_data_id[1].c_str());
|
||||
if (object_id == 0)
|
||||
{
|
||||
add_error("Found invalid object id");
|
||||
continue;
|
||||
}
|
||||
|
||||
IdToLayerHeightsProfileMap::iterator object_item = m_layer_heights_profiles.find(object_id);
|
||||
if (object_item != m_layer_heights_profiles.end())
|
||||
{
|
||||
add_error("Found duplicated layer heights profile");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> object_data_profile;
|
||||
boost::split(object_data_profile, object_data[1], boost::is_any_of(";"), boost::token_compress_off);
|
||||
if ((object_data_profile.size() <= 4) || (object_data_profile.size() % 2 != 0))
|
||||
{
|
||||
add_error("Found invalid layer heights profile");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<coordf_t> profile;
|
||||
profile.reserve(object_data_profile.size());
|
||||
|
||||
for (const std::string& value : object_data_profile)
|
||||
{
|
||||
profile.push_back((coordf_t)std::atof(value.c_str()));
|
||||
}
|
||||
|
||||
m_layer_heights_profiles.insert(IdToLayerHeightsProfileMap::value_type(object_id, profile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
|
||||
|
@ -705,6 +820,8 @@ namespace Slic3r {
|
|||
res = _handle_start_build(attributes, num_attributes);
|
||||
else if (::strcmp(ITEM_TAG, name) == 0)
|
||||
res = _handle_start_item(attributes, num_attributes);
|
||||
else if (::strcmp(METADATA_TAG, name) == 0)
|
||||
res = _handle_start_metadata(attributes, num_attributes);
|
||||
|
||||
if (!res)
|
||||
_stop_xml_parser();
|
||||
|
@ -741,11 +858,18 @@ namespace Slic3r {
|
|||
res = _handle_end_build();
|
||||
else if (::strcmp(ITEM_TAG, name) == 0)
|
||||
res = _handle_end_item();
|
||||
else if (::strcmp(METADATA_TAG, name) == 0)
|
||||
res = _handle_end_metadata();
|
||||
|
||||
if (!res)
|
||||
_stop_xml_parser();
|
||||
}
|
||||
|
||||
void _3MF_Importer::_handle_model_xml_characters(const XML_Char* s, int len)
|
||||
{
|
||||
m_curr_characters.append(s, len);
|
||||
}
|
||||
|
||||
void _3MF_Importer::_handle_start_config_xml_element(const char* name, const char** attributes)
|
||||
{
|
||||
if (m_xml_parser == nullptr)
|
||||
|
@ -1052,6 +1176,25 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_handle_start_metadata(const char** attributes, unsigned int num_attributes)
|
||||
{
|
||||
m_curr_characters.clear();
|
||||
|
||||
std::string name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
|
||||
if (!name.empty())
|
||||
m_curr_metadata_name = name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_handle_end_metadata()
|
||||
{
|
||||
if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION)
|
||||
m_version = (unsigned int)atoi(m_curr_characters.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter)
|
||||
{
|
||||
static const unsigned int MAX_RECURSIONS = 10;
|
||||
|
@ -1358,6 +1501,13 @@ namespace Slic3r {
|
|||
importer->_handle_end_model_xml_element(name);
|
||||
}
|
||||
|
||||
void XMLCALL _3MF_Importer::_handle_model_xml_characters(void* userData, const XML_Char* s, int len)
|
||||
{
|
||||
_3MF_Importer* importer = (_3MF_Importer*)userData;
|
||||
if (importer != nullptr)
|
||||
importer->_handle_model_xml_characters(s, len);
|
||||
}
|
||||
|
||||
void XMLCALL _3MF_Importer::_handle_start_config_xml_element(void* userData, const char* name, const char** attributes)
|
||||
{
|
||||
_3MF_Importer* importer = (_3MF_Importer*)userData;
|
||||
|
@ -1429,6 +1579,7 @@ namespace Slic3r {
|
|||
bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
|
||||
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
|
||||
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
|
||||
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
|
||||
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print);
|
||||
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model);
|
||||
};
|
||||
|
@ -1477,6 +1628,14 @@ namespace Slic3r {
|
|||
return false;
|
||||
}
|
||||
|
||||
// adds layer height profile file
|
||||
if (!_add_layer_height_profile_file_to_archive(archive, model))
|
||||
{
|
||||
mz_zip_writer_end(&archive);
|
||||
boost::filesystem::remove(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// adds slic3r print config file
|
||||
if (export_print_config)
|
||||
{
|
||||
|
@ -1552,7 +1711,8 @@ namespace Slic3r {
|
|||
{
|
||||
std::stringstream stream;
|
||||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << RESOURCES_TAG << ">\n";
|
||||
|
||||
BuildItemsList build_items;
|
||||
|
@ -1736,6 +1896,44 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
std::string out = "";
|
||||
char buffer[1024];
|
||||
|
||||
unsigned int count = 0;
|
||||
for (const ModelObject* object : model.objects)
|
||||
{
|
||||
++count;
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
|
||||
{
|
||||
sprintf(buffer, "object_id=%d|", count);
|
||||
out += buffer;
|
||||
|
||||
// Store the layer height profile as a single semicolon separated list.
|
||||
for (size_t i = 0; i < layer_height_profile.size(); ++i)
|
||||
{
|
||||
sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
|
||||
out += buffer;
|
||||
}
|
||||
|
||||
out += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!out.empty())
|
||||
{
|
||||
if (!mz_zip_writer_add_mem(&archive, LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
|
||||
{
|
||||
add_error("Unable to add layer heights profile file to archive");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
@ -1744,10 +1942,13 @@ namespace Slic3r {
|
|||
|
||||
GCode::append_full_config(print, out);
|
||||
|
||||
if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
|
||||
if (!out.empty())
|
||||
{
|
||||
add_error("Unable to add print config file to archive");
|
||||
return false;
|
||||
if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
|
||||
{
|
||||
add_error("Unable to add print config file to archive");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1832,10 +2033,7 @@ namespace Slic3r {
|
|||
|
||||
_3MF_Importer importer;
|
||||
bool res = importer.load_model_from_file(path, *model, *bundle);
|
||||
|
||||
if (!res)
|
||||
importer.log_errors();
|
||||
|
||||
importer.log_errors();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,12 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
// VERSION NUMBERS
|
||||
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
|
||||
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
|
||||
const unsigned int VERSION_AMF = 1;
|
||||
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
|
||||
|
||||
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
|
||||
|
||||
namespace Slic3r
|
||||
|
@ -32,6 +38,7 @@ namespace Slic3r
|
|||
struct AMFParserContext
|
||||
{
|
||||
AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) :
|
||||
m_version(0),
|
||||
m_parser(parser),
|
||||
m_model(*model),
|
||||
m_object(nullptr),
|
||||
|
@ -137,6 +144,8 @@ struct AMFParserContext
|
|||
std::vector<Instance> instances;
|
||||
};
|
||||
|
||||
// Version of the amf file
|
||||
unsigned int m_version;
|
||||
// Current Expat XML parser instance.
|
||||
XML_Parser m_parser;
|
||||
// Model to receive objects extracted from an AMF file.
|
||||
|
@ -360,9 +369,9 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
case NODE_TYPE_VERTEX:
|
||||
assert(m_object);
|
||||
// Parse the vertex data
|
||||
m_object_vertices.emplace_back(atof(m_value[0].c_str()));
|
||||
m_object_vertices.emplace_back(atof(m_value[1].c_str()));
|
||||
m_object_vertices.emplace_back(atof(m_value[2].c_str()));
|
||||
m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
|
||||
m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
|
||||
m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
m_value[2].clear();
|
||||
|
@ -462,6 +471,10 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
if (m_volume && m_value[0] == "name")
|
||||
m_volume->name = std::move(m_value[1]);
|
||||
}
|
||||
else if (strncmp(m_value[0].c_str(), SLIC3RPE_AMF_VERSION, strlen(SLIC3RPE_AMF_VERSION)) == 0) {
|
||||
m_version = (unsigned int)atoi(m_value[1].c_str());
|
||||
}
|
||||
|
||||
m_value[0].clear();
|
||||
m_value[1].clear();
|
||||
break;
|
||||
|
@ -543,46 +556,8 @@ bool load_amf_file(const char *path, PresetBundle* bundle, Model *model)
|
|||
return result;
|
||||
}
|
||||
|
||||
// Load an AMF archive into a provided model.
|
||||
bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
|
||||
bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, const char* path, PresetBundle* bundle, Model* model, unsigned int& version)
|
||||
{
|
||||
if ((path == nullptr) || (model == nullptr))
|
||||
return false;
|
||||
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Unable to init zip reader\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
if (num_entries != 1)
|
||||
{
|
||||
printf("Found invalid number of entries\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
res = mz_zip_reader_file_stat(&archive, 0, &stat);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Unable to extract entry statistics\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!boost::iends_with(stat.m_filename, ".amf"))
|
||||
{
|
||||
printf("Found invalid internal filename\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat.m_uncomp_size == 0)
|
||||
{
|
||||
printf("Found invalid size\n");
|
||||
|
@ -610,7 +585,7 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
|
|||
return false;
|
||||
}
|
||||
|
||||
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Error while reading model data to buffer\n");
|
||||
|
@ -627,6 +602,62 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
|
|||
|
||||
ctx.endDocument();
|
||||
|
||||
version = ctx.m_version;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load an AMF archive into a provided model.
|
||||
bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
|
||||
{
|
||||
if ((path == nullptr) || (model == nullptr))
|
||||
return false;
|
||||
|
||||
unsigned int version = 0;
|
||||
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Unable to init zip reader\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
// we first loop the entries to read from the archive the .amf file only, in order to extract the version from it
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
||||
{
|
||||
if (boost::iends_with(stat.m_filename, ".amf"))
|
||||
{
|
||||
if (!extract_model_from_archive(archive, stat, path, bundle, model, version))
|
||||
{
|
||||
mz_zip_reader_end(&archive);
|
||||
printf("Archive does not contain a valid model");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // forward compatibility
|
||||
// we then loop again the entries to read other files stored in the archive
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
||||
{
|
||||
// add code to extract the file
|
||||
}
|
||||
}
|
||||
#endif // forward compatibility
|
||||
|
||||
mz_zip_reader_end(&archive);
|
||||
return true;
|
||||
}
|
||||
|
@ -664,6 +695,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
|||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<amf unit=\"millimeter\">\n";
|
||||
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
|
||||
stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n";
|
||||
|
||||
if (export_print_config)
|
||||
{
|
||||
|
|
|
@ -718,10 +718,10 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
Helper::store_polyline(polyline, data, z, preview_data);
|
||||
|
||||
// updates preview ranges data
|
||||
preview_data.ranges.height.set_from(height_range);
|
||||
preview_data.ranges.width.set_from(width_range);
|
||||
preview_data.ranges.feedrate.set_from(feedrate_range);
|
||||
preview_data.ranges.volumetric_rate.set_from(volumetric_rate_range);
|
||||
preview_data.ranges.height.update_from(height_range);
|
||||
preview_data.ranges.width.update_from(width_range);
|
||||
preview_data.ranges.feedrate.update_from(feedrate_range);
|
||||
preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
|
||||
|
@ -790,9 +790,9 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
|
|||
Helper::store_polyline(polyline, type, direction, feedrate, extruder_id, preview_data);
|
||||
|
||||
// updates preview ranges data
|
||||
preview_data.ranges.height.set_from(height_range);
|
||||
preview_data.ranges.width.set_from(width_range);
|
||||
preview_data.ranges.feedrate.set_from(feedrate_range);
|
||||
preview_data.ranges.height.update_from(height_range);
|
||||
preview_data.ranges.width.update_from(width_range);
|
||||
preview_data.ranges.feedrate.update_from(feedrate_range);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,13 +9,17 @@ namespace Slic3r {
|
|||
|
||||
class GCode;
|
||||
class Layer;
|
||||
class PerExtruderAdjustments;
|
||||
|
||||
/*
|
||||
A standalone G-code filter, to control cooling of the print.
|
||||
The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
|
||||
and the print is modified to stretch over a minimum layer time.
|
||||
*/
|
||||
|
||||
// A standalone G-code filter, to control cooling of the print.
|
||||
// The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
|
||||
// and the print is modified to stretch over a minimum layer time.
|
||||
//
|
||||
// The simple it sounds, the actual implementation is significantly more complex.
|
||||
// Namely, for a multi-extruder print, each material may require a different cooling logic.
|
||||
// For example, some materials may not like to print too slowly, while with some materials
|
||||
// we may slow down significantly.
|
||||
//
|
||||
class CoolingBuffer {
|
||||
public:
|
||||
CoolingBuffer(GCode &gcodegen);
|
||||
|
@ -25,7 +29,12 @@ public:
|
|||
GCode* gcodegen() { return &m_gcodegen; }
|
||||
|
||||
private:
|
||||
CoolingBuffer& operator=(const CoolingBuffer&);
|
||||
CoolingBuffer& operator=(const CoolingBuffer&) = delete;
|
||||
std::vector<PerExtruderAdjustments> parse_layer_gcode(const std::string &gcode, std::vector<float> ¤t_pos) const;
|
||||
float calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
|
||||
// Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed.
|
||||
// Returns the adjusted G-code.
|
||||
std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
|
||||
|
||||
GCode& m_gcodegen;
|
||||
std::string m_gcode;
|
||||
|
@ -34,6 +43,9 @@ private:
|
|||
std::vector<char> m_axis;
|
||||
std::vector<float> m_current_pos;
|
||||
unsigned int m_current_extruder;
|
||||
|
||||
// Old logic: proportional.
|
||||
bool m_cooling_logic_proportional = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -99,17 +99,31 @@ void GCodePreviewData::Range::set_from(const Range& other)
|
|||
|
||||
float GCodePreviewData::Range::step_size() const
|
||||
{
|
||||
return (max - min) / (float)Colors_Count;
|
||||
return (max - min) / (float)(Colors_Count - 1);
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at_max() const
|
||||
GCodePreviewData::Color GCodePreviewData::Range::get_color_at(float value) const
|
||||
{
|
||||
return colors[Colors_Count - 1];
|
||||
}
|
||||
if (empty())
|
||||
return Color::Dummy;
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at(float value) const
|
||||
{
|
||||
return empty() ? get_color_at_max() : colors[clamp((unsigned int)0, Colors_Count - 1, (unsigned int)((value - min) / step_size()))];
|
||||
float global_t = (value - min) / step_size();
|
||||
|
||||
unsigned int low = (unsigned int)global_t;
|
||||
unsigned int high = clamp((unsigned int)0, Colors_Count - 1, low + 1);
|
||||
|
||||
Color color_low = colors[low];
|
||||
Color color_high = colors[high];
|
||||
|
||||
float local_t = global_t - (float)low;
|
||||
|
||||
// interpolate in RGB space
|
||||
Color ret;
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
{
|
||||
ret.rgba[i] = lerp(color_low.rgba[i], color_high.rgba[i], local_t);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
|
||||
|
@ -266,22 +280,22 @@ const GCodePreviewData::Color& GCodePreviewData::get_extrusion_role_color(Extrus
|
|||
return extrusion.role_colors[role];
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_height_color(float height) const
|
||||
GCodePreviewData::Color GCodePreviewData::get_height_color(float height) const
|
||||
{
|
||||
return ranges.height.get_color_at(height);
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_width_color(float width) const
|
||||
GCodePreviewData::Color GCodePreviewData::get_width_color(float width) const
|
||||
{
|
||||
return ranges.width.get_color_at(width);
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_feedrate_color(float feedrate) const
|
||||
GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) const
|
||||
{
|
||||
return ranges.feedrate.get_color_at(feedrate);
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_volumetric_rate_color(float rate) const
|
||||
GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
|
||||
{
|
||||
return ranges.volumetric_rate.get_color_at(rate);
|
||||
}
|
||||
|
@ -370,10 +384,10 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
|||
list.reserve(Range::Colors_Count);
|
||||
|
||||
float step = range.step_size();
|
||||
for (unsigned int i = 0; i < Range::Colors_Count; ++i)
|
||||
for (int i = Range::Colors_Count - 1; i >= 0; --i)
|
||||
{
|
||||
char buf[1024];
|
||||
sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step));
|
||||
sprintf(buf, "%.*f", decimals, scale_factor * (range.min + (float)i * step));
|
||||
list.emplace_back(buf, range.colors[i]);
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +422,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
|||
}
|
||||
case Extrusion::Feedrate:
|
||||
{
|
||||
Helper::FillListFromRange(items, ranges.feedrate, 0, 1.0f);
|
||||
Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f);
|
||||
break;
|
||||
}
|
||||
case Extrusion::VolumetricRate:
|
||||
|
|
|
@ -41,8 +41,7 @@ public:
|
|||
void set_from(const Range& other);
|
||||
float step_size() const;
|
||||
|
||||
const Color& get_color_at(float value) const;
|
||||
const Color& get_color_at_max() const;
|
||||
Color get_color_at(float value) const;
|
||||
};
|
||||
|
||||
struct Ranges
|
||||
|
@ -189,10 +188,10 @@ public:
|
|||
bool empty() const;
|
||||
|
||||
const Color& get_extrusion_role_color(ExtrusionRole role) const;
|
||||
const Color& get_height_color(float height) const;
|
||||
const Color& get_width_color(float width) const;
|
||||
const Color& get_feedrate_color(float feedrate) const;
|
||||
const Color& get_volumetric_rate_color(float rate) const;
|
||||
Color get_height_color(float height) const;
|
||||
Color get_width_color(float width) const;
|
||||
Color get_feedrate_color(float feedrate) const;
|
||||
Color get_volumetric_rate_color(float rate) const;
|
||||
|
||||
void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
|
||||
void set_extrusion_paths_colors(const std::vector<std::string>& colors);
|
||||
|
|
|
@ -598,10 +598,10 @@ std::string Print::validate() const
|
|||
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
|
||||
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
|
||||
|
||||
const PrintObject* most_layered_object = this->objects.front(); // object with highest layer_height_profile.size() encountered so far
|
||||
const PrintObject* tallest_object = this->objects.front(); // let's find the tallest object
|
||||
for (const auto* object : objects)
|
||||
if (object->layer_height_profile.size() > most_layered_object->layer_height_profile.size())
|
||||
most_layered_object = object;
|
||||
if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
|
||||
tallest_object = object;
|
||||
|
||||
for (PrintObject *object : this->objects) {
|
||||
SlicingParameters slicing_params = object->slicing_parameters();
|
||||
|
@ -618,17 +618,26 @@ std::string Print::validate() const
|
|||
object->update_layer_height_profile();
|
||||
object->layer_height_profile_valid = was_layer_height_profile_valid;
|
||||
|
||||
if ( this->config.variable_layer_height ) {
|
||||
int i = 0;
|
||||
while ( i < object->layer_height_profile.size() ) {
|
||||
if (std::abs(most_layered_object->layer_height_profile[i] - object->layer_height_profile[i]) > EPSILON)
|
||||
return "The Wipe tower is only supported if all objects have the same layer height profile";
|
||||
++i;
|
||||
if (i == object->layer_height_profile.size()-2) // this element contains the objects max z, if the other object is taller,
|
||||
// it does not have to match - we will step over it
|
||||
if (most_layered_object->layer_height_profile[i] > object->layer_height_profile[i])
|
||||
++i;
|
||||
if ( this->config.variable_layer_height ) { // comparing layer height profiles
|
||||
bool failed = false;
|
||||
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
|
||||
int i = 0;
|
||||
while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
||||
if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
|
||||
if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
|
||||
++i;
|
||||
}
|
||||
}
|
||||
else
|
||||
failed = true;
|
||||
|
||||
if (failed)
|
||||
return "The Wipe tower is only supported if all objects have the same layer height profile";
|
||||
}
|
||||
|
||||
/*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
|
||||
|
|
|
@ -1679,7 +1679,7 @@ PrintConfigDef::PrintConfigDef()
|
|||
"temperature control commands in the output.");
|
||||
def->cli = "temperature=i@";
|
||||
def->full_label = L("Temperature");
|
||||
def->max = 0;
|
||||
def->min = 0;
|
||||
def->max = max_temp;
|
||||
def->default_value = new ConfigOptionInts { 200 };
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <locale>
|
||||
|
||||
#include "libslic3r.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
extern void set_logging_level(unsigned int level);
|
||||
|
@ -60,6 +62,9 @@ extern std::string timestamp_str();
|
|||
// to be placed at the top of Slic3r generated files.
|
||||
inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
|
||||
|
||||
// getpid platform wrapper
|
||||
extern unsigned get_current_pid();
|
||||
|
||||
// Compute the next highest power of 2 of 32-bit v
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html
|
||||
template<typename T>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.39.0"
|
||||
#define SLIC3R_VERSION "1.40.0"
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef int32_t coord_t;
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#include <locale>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
@ -271,4 +277,13 @@ std::string timestamp_str()
|
|||
return buf;
|
||||
}
|
||||
|
||||
unsigned get_current_pid()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return GetCurrentProcessId();
|
||||
#else
|
||||
return ::getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -62,8 +62,8 @@ REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection");
|
|||
REGISTER_CLASS(Preset, "GUI::Preset");
|
||||
REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
|
||||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
||||
REGISTER_CLASS(TabIface, "GUI::Tab");
|
||||
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
|
||||
REGISTER_CLASS(OctoPrint, "OctoPrint");
|
||||
|
||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
||||
|
|
|
@ -175,6 +175,9 @@ semver_parse_version (const char *str, semver_t *ver) {
|
|||
slice = (char *) str;
|
||||
index = 0;
|
||||
|
||||
// non mandatory
|
||||
ver->patch = 0;
|
||||
|
||||
while (slice != NULL && index++ < 4) {
|
||||
next = strchr(slice, DELIMITER[0]);
|
||||
if (next == NULL)
|
||||
|
@ -200,7 +203,8 @@ semver_parse_version (const char *str, semver_t *ver) {
|
|||
slice = next + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// Major and minor versions are mandatory, patch version is not mandatory.
|
||||
return (index == 2 || index == 3) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -615,3 +619,22 @@ semver_numeric (semver_t *x) {
|
|||
|
||||
return num;
|
||||
}
|
||||
|
||||
char *semver_strdup(const char *src) {
|
||||
if (src == NULL) return NULL;
|
||||
size_t len = strlen(src) + 1;
|
||||
char *res = malloc(len);
|
||||
return res != NULL ? (char *) memcpy(res, src, len) : NULL;
|
||||
}
|
||||
|
||||
semver_t
|
||||
semver_copy(const semver_t *ver) {
|
||||
semver_t res = *ver;
|
||||
if (ver->metadata != NULL) {
|
||||
res.metadata = strdup(ver->metadata);
|
||||
}
|
||||
if (ver->prerelease != NULL) {
|
||||
res.prerelease = strdup(ver->prerelease);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -98,6 +98,12 @@ semver_is_valid (const char *s);
|
|||
int
|
||||
semver_clean (char *s);
|
||||
|
||||
char *
|
||||
semver_strdup(const char *src);
|
||||
|
||||
semver_t
|
||||
semver_copy(const semver_t *ver);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include "Snapshot.hpp"
|
||||
#include "../GUI/AppConfig.hpp"
|
||||
#include "../GUI/PresetBundle.hpp"
|
||||
#include "../Utils/Time.hpp"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
@ -56,6 +58,7 @@ void Snapshot::load_ini(const std::string &path)
|
|||
// Parse snapshot.ini
|
||||
std::string group_name_vendor = "Vendor:";
|
||||
std::string key_filament = "filament";
|
||||
std::string key_prefix_model = "model_";
|
||||
for (auto §ion : tree) {
|
||||
if (section.first == "snapshot") {
|
||||
// Parse the common section.
|
||||
|
@ -79,6 +82,8 @@ void Snapshot::load_ini(const std::string &path)
|
|||
this->reason = SNAPSHOT_UPGRADE;
|
||||
else if (rsn == "downgrade")
|
||||
this->reason = SNAPSHOT_DOWNGRADE;
|
||||
else if (rsn == "before_rollback")
|
||||
this->reason = SNAPSHOT_BEFORE_ROLLBACK;
|
||||
else if (rsn == "user")
|
||||
this->reason = SNAPSHOT_USER;
|
||||
else
|
||||
|
@ -106,24 +111,49 @@ void Snapshot::load_ini(const std::string &path)
|
|||
VendorConfig vc;
|
||||
vc.name = section.first.substr(group_name_vendor.size());
|
||||
for (auto &kvp : section.second) {
|
||||
if (boost::starts_with(kvp.first, "model_")) {
|
||||
//model:MK2S = 0.4;xxx
|
||||
//model:MK3 = 0.4;xxx
|
||||
} else if (kvp.first == "version" || kvp.first == "min_slic3r_version" || kvp.first == "max_slic3r_version") {
|
||||
if (kvp.first == "version" || kvp.first == "min_slic3r_version" || kvp.first == "max_slic3r_version") {
|
||||
// Version of the vendor specific config bundle bundled with this snapshot.
|
||||
auto semver = Semver::parse(kvp.second.data());
|
||||
if (! semver)
|
||||
throw_on_parse_error("invalid " + kvp.first + " format for " + section.first);
|
||||
if (kvp.first == "version")
|
||||
vc.version = *semver;
|
||||
vc.version.config_version = *semver;
|
||||
else if (kvp.first == "min_slic3r_version")
|
||||
vc.min_slic3r_version = *semver;
|
||||
vc.version.min_slic3r_version = *semver;
|
||||
else
|
||||
vc.max_slic3r_version = *semver;
|
||||
}
|
||||
vc.version.max_slic3r_version = *semver;
|
||||
} else if (boost::starts_with(kvp.first, key_prefix_model) && kvp.first.size() > key_prefix_model.size()) {
|
||||
// Parse the printer variants installed for the current model.
|
||||
auto &set_variants = vc.models_variants_installed[kvp.first.substr(key_prefix_model.size())];
|
||||
std::vector<std::string> variants;
|
||||
if (unescape_strings_cstyle(kvp.second.data(), variants))
|
||||
for (auto &variant : variants)
|
||||
set_variants.insert(std::move(variant));
|
||||
}
|
||||
}
|
||||
this->vendor_configs.emplace_back(std::move(vc));
|
||||
}
|
||||
}
|
||||
// Sort the vendors lexicographically.
|
||||
std::sort(this->vendor_configs.begin(), this->vendor_configs.begin(),
|
||||
[](const VendorConfig &cfg1, const VendorConfig &cfg2) { return cfg1.name < cfg2.name; });
|
||||
}
|
||||
|
||||
static std::string reason_string(const Snapshot::Reason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case Snapshot::SNAPSHOT_UPGRADE:
|
||||
return "upgrade";
|
||||
case Snapshot::SNAPSHOT_DOWNGRADE:
|
||||
return "downgrade";
|
||||
case Snapshot::SNAPSHOT_BEFORE_ROLLBACK:
|
||||
return "before_rollback";
|
||||
case Snapshot::SNAPSHOT_USER:
|
||||
return "user";
|
||||
case Snapshot::SNAPSHOT_UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void Snapshot::save_ini(const std::string &path)
|
||||
|
@ -138,7 +168,7 @@ void Snapshot::save_ini(const std::string &path)
|
|||
c << "time_captured = " << Slic3r::Utils::format_time_ISO8601Z(this->time_captured) << std::endl;
|
||||
c << "slic3r_version_captured = " << this->slic3r_version_captured.to_string() << std::endl;
|
||||
c << "comment = " << this->comment << std::endl;
|
||||
c << "reason = " << this->reason << std::endl;
|
||||
c << "reason = " << reason_string(this->reason) << std::endl;
|
||||
|
||||
// Export the active presets at the time of the snapshot.
|
||||
c << std::endl << "[presets]" << std::endl;
|
||||
|
@ -151,9 +181,17 @@ void Snapshot::save_ini(const std::string &path)
|
|||
// Export the vendor configs.
|
||||
for (const VendorConfig &vc : this->vendor_configs) {
|
||||
c << std::endl << "[Vendor:" << vc.name << "]" << std::endl;
|
||||
c << "version = " << vc.version.to_string() << std::endl;
|
||||
c << "min_slic3r_version = " << vc.min_slic3r_version.to_string() << std::endl;
|
||||
c << "max_slic3r_version = " << vc.max_slic3r_version.to_string() << std::endl;
|
||||
c << "version = " << vc.version.config_version.to_string() << std::endl;
|
||||
c << "min_slic3r_version = " << vc.version.min_slic3r_version.to_string() << std::endl;
|
||||
c << "max_slic3r_version = " << vc.version.max_slic3r_version.to_string() << std::endl;
|
||||
// Export installed printer models and their variants.
|
||||
for (const auto &model : vc.models_variants_installed) {
|
||||
if (model.second.size() == 0)
|
||||
continue;
|
||||
const std::vector<std::string> variants(model.second.begin(), model.second.end());
|
||||
const auto escaped = escape_strings_cstyle(variants);
|
||||
c << "model_" << model.first << " = " << escaped << std::endl;
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
|
@ -172,6 +210,82 @@ void Snapshot::export_selections(AppConfig &config) const
|
|||
config.set("presets", "printer", printer);
|
||||
}
|
||||
|
||||
void Snapshot::export_vendor_configs(AppConfig &config) const
|
||||
{
|
||||
std::map<std::string, std::map<std::string, std::set<std::string>>> vendors;
|
||||
for (const VendorConfig &vc : vendor_configs)
|
||||
vendors[vc.name] = vc.models_variants_installed;
|
||||
config.set_vendors(std::move(vendors));
|
||||
}
|
||||
|
||||
// Perform a deep compare of the active print / filament / printer / vendor directories.
|
||||
// Return true if the content of the current print / filament / printer / vendor directories
|
||||
// matches the state stored in this snapshot.
|
||||
bool Snapshot::equal_to_active(const AppConfig &app_config) const
|
||||
{
|
||||
// 1) Check, whether this snapshot contains the same set of active vendors, printer models and variants
|
||||
// as app_config.
|
||||
{
|
||||
std::set<std::string> matched;
|
||||
for (const VendorConfig &vc : this->vendor_configs) {
|
||||
auto it_vendor_models_variants = app_config.vendors().find(vc.name);
|
||||
if (it_vendor_models_variants == app_config.vendors().end() ||
|
||||
it_vendor_models_variants->second != vc.models_variants_installed)
|
||||
// There are more vendors enabled in the snapshot than currently installed.
|
||||
return false;
|
||||
matched.insert(vc.name);
|
||||
}
|
||||
for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &v : app_config.vendors())
|
||||
if (matched.find(v.first) == matched.end() && ! v.second.empty())
|
||||
// There are more vendors currently installed than enabled in the snapshot.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) Check, whether this snapshot references the same set of ini files as the current state.
|
||||
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
||||
boost::filesystem::path snapshot_dir = boost::filesystem::path(Slic3r::data_dir()) / SLIC3R_SNAPSHOTS_DIR / this->id;
|
||||
for (const char *subdir : { "print", "filament", "printer", "vendor" }) {
|
||||
boost::filesystem::path path1 = data_dir / subdir;
|
||||
boost::filesystem::path path2 = snapshot_dir / subdir;
|
||||
std::vector<std::string> files1, files2;
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path1))
|
||||
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini"))
|
||||
files1.emplace_back(dir_entry.path().filename().string());
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path2))
|
||||
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini"))
|
||||
files2.emplace_back(dir_entry.path().filename().string());
|
||||
std::sort(files1.begin(), files1.end());
|
||||
std::sort(files2.begin(), files2.end());
|
||||
if (files1 != files2)
|
||||
return false;
|
||||
for (const std::string &filename : files1) {
|
||||
FILE *f1 = boost::nowide::fopen((path1 / filename).string().c_str(), "rb");
|
||||
FILE *f2 = boost::nowide::fopen((path2 / filename).string().c_str(), "rb");
|
||||
bool same = true;
|
||||
if (f1 && f2) {
|
||||
char buf1[4096];
|
||||
char buf2[4096];
|
||||
do {
|
||||
size_t r1 = fread(buf1, 1, 4096, f1);
|
||||
size_t r2 = fread(buf2, 1, 4096, f2);
|
||||
if (r1 != r2 || memcmp(buf1, buf2, r1)) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
} while (! feof(f1) || ! feof(f2));
|
||||
} else
|
||||
same = false;
|
||||
if (f1)
|
||||
fclose(f1);
|
||||
if (f2)
|
||||
fclose(f2);
|
||||
if (! same)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t SnapshotDB::load_db()
|
||||
{
|
||||
boost::filesystem::path snapshots_dir = SnapshotDB::create_db_dir();
|
||||
|
@ -199,12 +313,29 @@ size_t SnapshotDB::load_db()
|
|||
}
|
||||
m_snapshots.emplace_back(std::move(snapshot));
|
||||
}
|
||||
|
||||
// Sort the snapshots by their date/time.
|
||||
std::sort(m_snapshots.begin(), m_snapshots.end(), [](const Snapshot &s1, const Snapshot &s2) { return s1.time_captured < s2.time_captured; });
|
||||
if (! errors_cummulative.empty())
|
||||
throw std::runtime_error(errors_cummulative);
|
||||
return m_snapshots.size();
|
||||
}
|
||||
|
||||
void SnapshotDB::update_slic3r_versions(std::vector<Index> &index_db)
|
||||
{
|
||||
for (Snapshot &snapshot : m_snapshots) {
|
||||
for (Snapshot::VendorConfig &vendor_config : snapshot.vendor_configs) {
|
||||
auto it = std::find_if(index_db.begin(), index_db.end(), [&vendor_config](const Index &idx) { return idx.vendor() == vendor_config.name; });
|
||||
if (it != index_db.end()) {
|
||||
Index::const_iterator it_version = it->find(vendor_config.version.config_version);
|
||||
if (it_version != it->end()) {
|
||||
vendor_config.version.min_slic3r_version = it_version->min_slic3r_version;
|
||||
vendor_config.version.max_slic3r_version = it_version->max_slic3r_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_config_dir_single_level(const boost::filesystem::path &path_src, const boost::filesystem::path &path_dst)
|
||||
{
|
||||
if (! boost::filesystem::is_directory(path_dst) &&
|
||||
|
@ -225,7 +356,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path)
|
|||
boost::filesystem::remove(dir_entry.path());
|
||||
}
|
||||
|
||||
const Snapshot& SnapshotDB::make_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment)
|
||||
const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment)
|
||||
{
|
||||
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
||||
boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir();
|
||||
|
@ -235,7 +366,7 @@ const Snapshot& SnapshotDB::make_snapshot(const AppConfig &app_config, Snapshot:
|
|||
// Snapshot header.
|
||||
snapshot.time_captured = Slic3r::Utils::get_current_time_utc();
|
||||
snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured);
|
||||
snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION);
|
||||
snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION); // XXX: have Semver Slic3r version
|
||||
snapshot.comment = comment;
|
||||
snapshot.reason = reason;
|
||||
// Active presets at the time of the snapshot.
|
||||
|
@ -250,22 +381,54 @@ const Snapshot& SnapshotDB::make_snapshot(const AppConfig &app_config, Snapshot:
|
|||
snapshot.filaments.emplace_back(app_config.get("presets", name));
|
||||
}
|
||||
// Vendor specific config bundles and installed printers.
|
||||
for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &vendor : app_config.vendors()) {
|
||||
Snapshot::VendorConfig cfg;
|
||||
cfg.name = vendor.first;
|
||||
cfg.models_variants_installed = vendor.second;
|
||||
for (auto it = cfg.models_variants_installed.begin(); it != cfg.models_variants_installed.end();)
|
||||
if (it->second.empty())
|
||||
cfg.models_variants_installed.erase(it ++);
|
||||
else
|
||||
++ it;
|
||||
// Read the active config bundle, parse the config version.
|
||||
PresetBundle bundle;
|
||||
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY);
|
||||
for (const VendorProfile &vp : bundle.vendors)
|
||||
if (vp.id == cfg.name)
|
||||
cfg.version.config_version = vp.config_version;
|
||||
// Fill-in the min/max slic3r version from the config index, if possible.
|
||||
try {
|
||||
// Load the config index for the vendor.
|
||||
Index index;
|
||||
index.load(data_dir / "vendor" / (cfg.name + ".idx"));
|
||||
auto it = index.find(cfg.version.config_version);
|
||||
if (it != index.end()) {
|
||||
cfg.version.min_slic3r_version = it->min_slic3r_version;
|
||||
cfg.version.max_slic3r_version = it->max_slic3r_version;
|
||||
}
|
||||
} catch (const std::runtime_error &err) {
|
||||
}
|
||||
snapshot.vendor_configs.emplace_back(std::move(cfg));
|
||||
}
|
||||
|
||||
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
||||
boost::filesystem::create_directory(snapshot_dir);
|
||||
|
||||
// Backup the presets.
|
||||
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
||||
for (const char *subdir : { "print", "filament", "printer", "vendor" })
|
||||
copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
|
||||
snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
|
||||
assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
|
||||
m_snapshots.emplace_back(std::move(snapshot));
|
||||
return m_snapshots.back();
|
||||
}
|
||||
|
||||
void SnapshotDB::restore_snapshot(const std::string &id, AppConfig &app_config)
|
||||
const Snapshot& SnapshotDB::restore_snapshot(const std::string &id, AppConfig &app_config)
|
||||
{
|
||||
for (const Snapshot &snapshot : m_snapshots)
|
||||
if (snapshot.id == id) {
|
||||
this->restore_snapshot(snapshot, app_config);
|
||||
return;
|
||||
return snapshot;
|
||||
}
|
||||
throw std::runtime_error(std::string("Snapshot with id " + id + " was not found."));
|
||||
}
|
||||
|
@ -275,18 +438,59 @@ void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_confi
|
|||
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
||||
boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir();
|
||||
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
||||
|
||||
// Remove existing ini files and restore the ini files from the snapshot.
|
||||
for (const char *subdir : { "print", "filament", "printer", "vendor" }) {
|
||||
delete_existing_ini_files(data_dir / subdir);
|
||||
copy_config_dir_single_level(snapshot_dir / subdir, data_dir / subdir);
|
||||
}
|
||||
|
||||
// Update app_config from the snapshot.
|
||||
// Update AppConfig with the selections of the print / filament / printer profiles
|
||||
// and about the installed printer types and variants.
|
||||
snapshot.export_selections(app_config);
|
||||
snapshot.export_vendor_configs(app_config);
|
||||
}
|
||||
|
||||
// Store information about the snapshot.
|
||||
bool SnapshotDB::is_on_snapshot(AppConfig &app_config) const
|
||||
{
|
||||
// Is the "on_snapshot" configuration value set?
|
||||
std::string on_snapshot = app_config.get("on_snapshot");
|
||||
if (on_snapshot.empty())
|
||||
// No, we are not on a snapshot.
|
||||
return false;
|
||||
// Is the "on_snapshot" equal to the current configuration state?
|
||||
auto it_snapshot = this->snapshot(on_snapshot);
|
||||
if (it_snapshot != this->end() && it_snapshot->equal_to_active(app_config))
|
||||
// Yes, we are on the snapshot.
|
||||
return true;
|
||||
// No, we are no more on a snapshot. Reset the state.
|
||||
app_config.set("on_snapshot", "");
|
||||
return false;
|
||||
}
|
||||
|
||||
SnapshotDB::const_iterator SnapshotDB::snapshot_with_vendor_preset(const std::string &vendor_name, const Semver &config_version)
|
||||
{
|
||||
auto it_found = m_snapshots.end();
|
||||
Snapshot::VendorConfig key;
|
||||
key.name = vendor_name;
|
||||
for (auto it = m_snapshots.begin(); it != m_snapshots.end(); ++ it) {
|
||||
const Snapshot &snapshot = *it;
|
||||
auto it_vendor_config = std::lower_bound(snapshot.vendor_configs.begin(), snapshot.vendor_configs.end(),
|
||||
key, [](const Snapshot::VendorConfig &cfg1, const Snapshot::VendorConfig &cfg2) { return cfg1.name < cfg2.name; });
|
||||
if (it_vendor_config != snapshot.vendor_configs.end() && it_vendor_config->name == vendor_name &&
|
||||
config_version == it_vendor_config->version.config_version) {
|
||||
// Vendor config found with the correct version.
|
||||
// Save it, but continue searching, as we want the newest snapshot.
|
||||
it_found = it;
|
||||
}
|
||||
}
|
||||
return it_found;
|
||||
}
|
||||
|
||||
SnapshotDB::const_iterator SnapshotDB::snapshot(const std::string &id) const
|
||||
{
|
||||
for (const_iterator it = m_snapshots.begin(); it != m_snapshots.end(); ++ it)
|
||||
if (it->id == id)
|
||||
return it;
|
||||
return m_snapshots.end();
|
||||
}
|
||||
|
||||
boost::filesystem::path SnapshotDB::create_db_dir()
|
||||
|
@ -303,6 +507,26 @@ boost::filesystem::path SnapshotDB::create_db_dir()
|
|||
return snapshots_dir;
|
||||
}
|
||||
|
||||
SnapshotDB& SnapshotDB::singleton()
|
||||
{
|
||||
static SnapshotDB instance;
|
||||
static bool loaded = false;
|
||||
if (! loaded) {
|
||||
try {
|
||||
loaded = true;
|
||||
// Load the snapshot database.
|
||||
instance.load_db();
|
||||
// Load the vendor specific configuration indices.
|
||||
std::vector<Index> index_db = Index::load_db();
|
||||
// Update the min / max slic3r versions compatible with the configurations stored inside the snapshots
|
||||
// based on the min / max slic3r versions defined by the vendor specific config indices.
|
||||
instance.update_slic3r_versions(index_db);
|
||||
} catch (std::exception &ex) {
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#ifndef slic3r_GUI_Snapshot_
|
||||
#define slic3r_GUI_Snapshot_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "Version.hpp"
|
||||
#include "../Utils/Semver.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -15,6 +18,8 @@ class AppConfig;
|
|||
namespace GUI {
|
||||
namespace Config {
|
||||
|
||||
class Index;
|
||||
|
||||
// A snapshot contains:
|
||||
// Slic3r.ini
|
||||
// vendor/
|
||||
|
@ -28,6 +33,7 @@ public:
|
|||
SNAPSHOT_UNKNOWN,
|
||||
SNAPSHOT_UPGRADE,
|
||||
SNAPSHOT_DOWNGRADE,
|
||||
SNAPSHOT_BEFORE_ROLLBACK,
|
||||
SNAPSHOT_USER,
|
||||
};
|
||||
|
||||
|
@ -39,6 +45,12 @@ public:
|
|||
|
||||
// Export the print / filament / printer selections to be activated into the AppConfig.
|
||||
void export_selections(AppConfig &config) const;
|
||||
void export_vendor_configs(AppConfig &config) const;
|
||||
|
||||
// Perform a deep compare of the active print / filament / printer / vendor directories.
|
||||
// Return true if the content of the current print / filament / printer / vendor directories
|
||||
// matches the state stored in this snapshot.
|
||||
bool equal_to_active(const AppConfig &app_config) const;
|
||||
|
||||
// ID of a snapshot should equal to the name of the snapshot directory.
|
||||
// The ID contains the date/time, reason and comment to be human readable.
|
||||
|
@ -50,6 +62,8 @@ public:
|
|||
std::string comment;
|
||||
Reason reason;
|
||||
|
||||
std::string format_reason() const;
|
||||
|
||||
// Active presets at the time of the snapshot.
|
||||
std::string print;
|
||||
std::vector<std::string> filaments;
|
||||
|
@ -61,41 +75,50 @@ public:
|
|||
struct VendorConfig {
|
||||
// Name of the vendor contained in this snapshot.
|
||||
std::string name;
|
||||
// Version of the vendor config contained in this snapshot.
|
||||
Semver version = Semver::invalid();
|
||||
// Minimum Slic3r version compatible with this vendor configuration.
|
||||
Semver min_slic3r_version = Semver::zero();
|
||||
// Maximum Slic3r version compatible with this vendor configuration, or empty.
|
||||
Semver max_slic3r_version = Semver::inf();
|
||||
// Version of the vendor config contained in this snapshot, along with compatibility data.
|
||||
Version version;
|
||||
// Which printer models of this vendor were installed, and which variants of the models?
|
||||
std::map<std::string, std::set<std::string>> models_variants_installed;
|
||||
};
|
||||
// List of vendor configs contained in this snapshot.
|
||||
// List of vendor configs contained in this snapshot, sorted lexicographically.
|
||||
std::vector<VendorConfig> vendor_configs;
|
||||
};
|
||||
|
||||
class SnapshotDB
|
||||
{
|
||||
public:
|
||||
// Initialize the SnapshotDB singleton instance. Load the database if it has not been loaded yet.
|
||||
static SnapshotDB& singleton();
|
||||
|
||||
typedef std::vector<Snapshot>::const_iterator const_iterator;
|
||||
|
||||
// Load the snapshot database from the snapshots directory.
|
||||
// If the snapshot directory or its parent does not exist yet, it will be created.
|
||||
// Returns a number of snapshots loaded.
|
||||
size_t load_db();
|
||||
void update_slic3r_versions(std::vector<Index> &index_db);
|
||||
|
||||
// Create a snapshot directory, copy the vendor config bundles, user print/filament/printer profiles,
|
||||
// create an index.
|
||||
const Snapshot& make_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment);
|
||||
void restore_snapshot(const std::string &id, AppConfig &app_config);
|
||||
const Snapshot& take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment = "");
|
||||
const Snapshot& restore_snapshot(const std::string &id, AppConfig &app_config);
|
||||
void restore_snapshot(const Snapshot &snapshot, AppConfig &app_config);
|
||||
// Test whether the AppConfig's on_snapshot variable points to an existing snapshot, and the existing snapshot
|
||||
// matches the current state. If it does not match the current state, the AppConfig's "on_snapshot" ID is reset.
|
||||
bool is_on_snapshot(AppConfig &app_config) const;
|
||||
// Finds the newest snapshot, which contains a config bundle for vendor_name with config_version.
|
||||
const_iterator snapshot_with_vendor_preset(const std::string &vendor_name, const Semver &config_version);
|
||||
|
||||
const_iterator begin() const { return m_snapshots.begin(); }
|
||||
const_iterator end() const { return m_snapshots.end(); }
|
||||
const_iterator snapshot(const std::string &id) const;
|
||||
const std::vector<Snapshot>& snapshots() const { return m_snapshots; }
|
||||
|
||||
private:
|
||||
// Create the snapshots directory if it does not exist yet.
|
||||
static boost::filesystem::path create_db_dir();
|
||||
|
||||
// Snapshots are sorted by their date/time, oldest first.
|
||||
std::vector<Snapshot> m_snapshots;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,18 +6,140 @@
|
|||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
#include "../../libslic3r/Config.hpp"
|
||||
#include "../../libslic3r/FileParserError.hpp"
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
namespace Config {
|
||||
|
||||
static boost::optional<Semver> s_current_slic3r_semver = Semver::parse(SLIC3R_VERSION);
|
||||
static const Semver s_current_slic3r_semver(SLIC3R_VERSION);
|
||||
|
||||
// Optimized lexicographic compare of two pre-release versions, ignoring the numeric suffix.
|
||||
static int compare_prerelease(const char *p1, const char *p2)
|
||||
{
|
||||
for (;;) {
|
||||
char c1 = *p1 ++;
|
||||
char c2 = *p2 ++;
|
||||
bool a1 = std::isalpha(c1) && c1 != 0;
|
||||
bool a2 = std::isalpha(c2) && c2 != 0;
|
||||
if (a1) {
|
||||
if (a2) {
|
||||
if (c1 != c2)
|
||||
return (c1 < c2) ? -1 : 1;
|
||||
} else
|
||||
return 1;
|
||||
} else {
|
||||
if (a2)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// This shall never happen.
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Version::is_slic3r_supported(const Semver &slic3r_version) const
|
||||
{
|
||||
if (! slic3r_version.in_range(min_slic3r_version, max_slic3r_version))
|
||||
return false;
|
||||
// Now verify, whether the configuration pre-release status is compatible with the Slic3r's pre-release status.
|
||||
// Alpha Slic3r will happily load any configuration, while beta Slic3r will ignore alpha configurations etc.
|
||||
const char *prerelease_slic3r = slic3r_version.prerelease();
|
||||
const char *prerelease_config = this->config_version.prerelease();
|
||||
if (prerelease_config == nullptr)
|
||||
// Released config is always supported.
|
||||
return true;
|
||||
else if (prerelease_slic3r == nullptr)
|
||||
// Released slic3r only supports released configs.
|
||||
return false;
|
||||
// Compare the pre-release status of Slic3r against the config.
|
||||
// If the prerelease status of slic3r is lexicographically lower or equal
|
||||
// to the prerelease status of the config, accept it.
|
||||
return compare_prerelease(prerelease_slic3r, prerelease_config) != 1;
|
||||
}
|
||||
|
||||
bool Version::is_current_slic3r_supported() const
|
||||
{
|
||||
return this->is_slic3r_supported(*s_current_slic3r_semver);
|
||||
return this->is_slic3r_supported(s_current_slic3r_semver);
|
||||
}
|
||||
|
||||
#if 0
|
||||
//TODO: This test should be moved to a unit test, once we have C++ unit tests in place.
|
||||
static int version_test()
|
||||
{
|
||||
Version v;
|
||||
v.config_version = *Semver::parse("1.1.2");
|
||||
v.min_slic3r_version = *Semver::parse("1.38.0");
|
||||
v.max_slic3r_version = Semver::inf();
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.38.0")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
|
||||
// Test the prerelease status.
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0")));
|
||||
v.config_version = *Semver::parse("1.1.2-alpha");
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
|
||||
v.config_version = *Semver::parse("1.1.2-alpha1");
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
|
||||
v.config_version = *Semver::parse("1.1.2-beta");
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
|
||||
v.config_version = *Semver::parse("1.1.2-rc");
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
|
||||
v.config_version = *Semver::parse("1.1.2-rc2");
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-alpha1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-beta1")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc")));
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.39.0-rc2")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.39.0")));
|
||||
// Test the upper boundary.
|
||||
v.config_version = *Semver::parse("1.1.2");
|
||||
v.max_slic3r_version = *Semver::parse("1.39.3-beta1");
|
||||
assert(v.is_slic3r_supported(*Semver::parse("1.38.0")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.38.0-alpha1")));
|
||||
assert(! v.is_slic3r_supported(*Semver::parse("1.37.0-alpha")));
|
||||
return 0;
|
||||
}
|
||||
static int version_test_run = version_test();
|
||||
#endif
|
||||
|
||||
inline char* left_trim(char *c)
|
||||
{
|
||||
for (; *c == ' ' || *c == '\t'; ++ c);
|
||||
|
@ -38,12 +160,13 @@ inline std::string unquote_value(char *value, char *end, const std::string &path
|
|||
if (value == end) {
|
||||
// Empty string is a valid string.
|
||||
} else if (*value == '"') {
|
||||
if (++ value < -- end || *end != '"')
|
||||
if (++ value > -- end || *end != '"')
|
||||
throw file_parser_error("String not enquoted correctly", path, idx_line);
|
||||
*end = 0;
|
||||
if (! unescape_string_cstyle(value, svalue))
|
||||
throw file_parser_error("Invalid escape sequence inside a quoted string", path, idx_line);
|
||||
}
|
||||
} else
|
||||
svalue.assign(value, end);
|
||||
return svalue;
|
||||
}
|
||||
|
||||
|
@ -53,20 +176,22 @@ inline std::string unquote_version_comment(char *value, char *end, const std::st
|
|||
if (value == end) {
|
||||
// Empty string is a valid string.
|
||||
} else if (*value == '"') {
|
||||
if (++ value < -- end || *end != '"')
|
||||
if (++ value > -- end || *end != '"')
|
||||
throw file_parser_error("Version comment not enquoted correctly", path, idx_line);
|
||||
*end = 0;
|
||||
if (! unescape_string_cstyle(value, svalue))
|
||||
throw file_parser_error("Invalid escape sequence inside a quoted version comment", path, idx_line);
|
||||
}
|
||||
} else
|
||||
svalue.assign(value, end);
|
||||
return svalue;
|
||||
}
|
||||
|
||||
size_t Index::load(const std::string &path)
|
||||
size_t Index::load(const boost::filesystem::path &path)
|
||||
{
|
||||
m_configs.clear();
|
||||
m_vendor = path.stem().string();
|
||||
|
||||
boost::nowide::ifstream ifs(path);
|
||||
boost::nowide::ifstream ifs(path.string());
|
||||
std::string line;
|
||||
size_t idx_line = 0;
|
||||
Version ver;
|
||||
|
@ -74,63 +199,121 @@ size_t Index::load(const std::string &path)
|
|||
++ idx_line;
|
||||
// Skip the initial white spaces.
|
||||
char *key = left_trim(const_cast<char*>(line.data()));
|
||||
if (*key == '#')
|
||||
// Skip a comment line.
|
||||
continue;
|
||||
// Right trim the line.
|
||||
char *end = right_trim(key);
|
||||
if (key == end)
|
||||
// Skip an empty line.
|
||||
continue;
|
||||
// Keyword may only contain alphanumeric characters. Semantic version may in addition contain "+.-".
|
||||
char *key_end = key;
|
||||
bool maybe_semver = false;
|
||||
for (;; ++ key) {
|
||||
if (strchr("+.-", *key) != nullptr)
|
||||
maybe_semver = true;
|
||||
else if (! std::isalnum(*key))
|
||||
break;
|
||||
bool maybe_semver = true;
|
||||
for (; *key_end != 0; ++ key_end) {
|
||||
if (std::isalnum(*key_end) || strchr("+.-", *key_end) != nullptr) {
|
||||
// It may be a semver.
|
||||
} else if (*key_end == '_') {
|
||||
// Cannot be a semver, but it may be a key.
|
||||
maybe_semver = false;
|
||||
} else
|
||||
// End of semver or keyword.
|
||||
break;
|
||||
}
|
||||
if (*key != 0 && *key != ' ' && *key != '\t' && *key != '=')
|
||||
if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
|
||||
throw file_parser_error("Invalid keyword or semantic version", path, idx_line);
|
||||
*key_end = 0;
|
||||
char *value = left_trim(key_end);
|
||||
bool key_value_pair = *value == '=';
|
||||
if (key_value_pair)
|
||||
value = left_trim(value + 1);
|
||||
*key_end = 0;
|
||||
boost::optional<Semver> semver;
|
||||
if (maybe_semver)
|
||||
semver = Semver::parse(key);
|
||||
char *value = left_trim(key_end);
|
||||
if (*value == '=') {
|
||||
if (key_value_pair) {
|
||||
if (semver)
|
||||
throw file_parser_error("Key cannot be a semantic version", path, idx_line);
|
||||
throw file_parser_error("Key cannot be a semantic version", path, idx_line);\
|
||||
// Verify validity of the key / value pair.
|
||||
std::string svalue = unquote_value(left_trim(++ value), end, path, idx_line);
|
||||
if (key == "min_sic3r_version" || key == "max_slic3r_version") {
|
||||
std::string svalue = unquote_value(value, end, path.string(), idx_line);
|
||||
if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) {
|
||||
if (! svalue.empty())
|
||||
semver = Semver::parse(key);
|
||||
semver = Semver::parse(svalue);
|
||||
if (! semver)
|
||||
throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line);
|
||||
if (key == "min_sic3r_version")
|
||||
if (strcmp(key, "min_slic3r_version") == 0)
|
||||
ver.min_slic3r_version = *semver;
|
||||
else
|
||||
ver.max_slic3r_version = *semver;
|
||||
} else {
|
||||
// Ignore unknown keys, as there may come new keys in the future.
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (! semver)
|
||||
throw file_parser_error("Invalid semantic version", path, idx_line);
|
||||
ver.config_version = *semver;
|
||||
ver.comment = (end <= key_end) ? "" : unquote_version_comment(value, end, path, idx_line);
|
||||
ver.comment = (end <= key_end) ? "" : unquote_version_comment(value, end, path.string(), idx_line);
|
||||
m_configs.emplace_back(ver);
|
||||
}
|
||||
|
||||
// Sort the configs by their version.
|
||||
std::sort(m_configs.begin(), m_configs.end(), [](const Version &v1, const Version &v2) { return v1.config_version < v2.config_version; });
|
||||
return m_configs.size();
|
||||
}
|
||||
|
||||
Semver Index::version() const
|
||||
{
|
||||
Semver ver = Semver::zero();
|
||||
for (const Version &cv : m_configs)
|
||||
if (cv.config_version >= ver)
|
||||
ver = cv.config_version;
|
||||
return ver;
|
||||
}
|
||||
|
||||
Index::const_iterator Index::find(const Semver &ver) const
|
||||
{
|
||||
Version key;
|
||||
key.config_version = ver;
|
||||
auto it = std::lower_bound(m_configs.begin(), m_configs.end(), key,
|
||||
[](const Version &v1, const Version &v2) { return v1.config_version < v2.config_version; });
|
||||
return (it == m_configs.end() || it->config_version == ver) ? it : m_configs.end();
|
||||
}
|
||||
|
||||
Index::const_iterator Index::recommended() const
|
||||
{
|
||||
int idx = -1;
|
||||
const_iterator highest = m_configs.end();
|
||||
const_iterator highest = this->end();
|
||||
for (const_iterator it = this->begin(); it != this->end(); ++ it)
|
||||
if (it->is_current_slic3r_supported() &&
|
||||
(highest == this->end() || highest->max_slic3r_version < it->max_slic3r_version))
|
||||
(highest == this->end() || highest->config_version < it->config_version))
|
||||
highest = it;
|
||||
return highest;
|
||||
}
|
||||
|
||||
std::vector<Index> Index::load_db()
|
||||
{
|
||||
boost::filesystem::path cache_dir = boost::filesystem::path(Slic3r::data_dir()) / "cache";
|
||||
|
||||
std::vector<Index> index_db;
|
||||
std::string errors_cummulative;
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(cache_dir))
|
||||
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".idx")) {
|
||||
Index idx;
|
||||
try {
|
||||
idx.load(dir_entry.path());
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
errors_cummulative += "\n";
|
||||
continue;
|
||||
}
|
||||
index_db.emplace_back(std::move(idx));
|
||||
}
|
||||
|
||||
if (! errors_cummulative.empty())
|
||||
throw std::runtime_error(errors_cummulative);
|
||||
return index_db;
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "../../libslic3r/FileParserError.hpp"
|
||||
#include "../Utils/Semver.hpp"
|
||||
|
||||
|
@ -25,7 +27,7 @@ struct Version
|
|||
// Single comment line.
|
||||
std::string comment;
|
||||
|
||||
bool is_slic3r_supported(const Semver &slicer_version) const { return slicer_version.in_range(min_slic3r_version, max_slic3r_version); }
|
||||
bool is_slic3r_supported(const Semver &slicer_version) const;
|
||||
bool is_current_slic3r_supported() const;
|
||||
};
|
||||
|
||||
|
@ -54,17 +56,28 @@ public:
|
|||
typedef std::vector<Version>::const_iterator const_iterator;
|
||||
// Read a config index file in the simple format described in the Index class comment.
|
||||
// Throws Slic3r::file_parser_error and the standard std file access exceptions.
|
||||
size_t load(const std::string &path);
|
||||
size_t load(const boost::filesystem::path &path);
|
||||
|
||||
const std::string& vendor() const { return m_vendor; }
|
||||
// Returns version of the index as the highest version of all the configs.
|
||||
// If there is no config, Semver::zero() is returned.
|
||||
Semver version() const;
|
||||
|
||||
const_iterator begin() const { return m_configs.begin(); }
|
||||
const_iterator end() const { return m_configs.end(); }
|
||||
const_iterator find(const Semver &ver) const;
|
||||
const std::vector<Version>& configs() const { return m_configs; }
|
||||
// Finds a recommended config to be installed for the current Slic3r version.
|
||||
// Returns configs().end() if such version does not exist in the index. This shall never happen
|
||||
// if the index is valid.
|
||||
const_iterator recommended() const;
|
||||
|
||||
// Load all vendor specific indices.
|
||||
// Throws Slic3r::file_parser_error and the standard std file access exceptions.
|
||||
static std::vector<Index> load_db();
|
||||
|
||||
private:
|
||||
std::string m_vendor;
|
||||
std::vector<Version> m_configs;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "2DBed.hpp";
|
||||
#include "2DBed.hpp"
|
||||
|
||||
#include <wx/dcbuffer.h>
|
||||
#include "BoundingBox.hpp"
|
||||
|
@ -66,7 +66,7 @@ void Bed_2D::repaint()
|
|||
shift.y - (cbb.max.y - GetSize().GetHeight()));
|
||||
|
||||
// draw bed fill
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(255, 255, 255), wxSOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxSOLID));
|
||||
wxPointList pt_list;
|
||||
for (auto pt: m_bed_shape)
|
||||
{
|
||||
|
@ -87,7 +87,7 @@ void Bed_2D::repaint()
|
|||
}
|
||||
polylines = intersection_pl(polylines, bed_polygon);
|
||||
|
||||
dc.SetPen(*new wxPen(*new wxColour(230, 230, 230), 1, wxSOLID));
|
||||
dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxSOLID));
|
||||
for (auto pl : polylines)
|
||||
{
|
||||
for (size_t i = 0; i < pl.points.size()-1; i++){
|
||||
|
@ -98,8 +98,8 @@ void Bed_2D::repaint()
|
|||
}
|
||||
|
||||
// draw bed contour
|
||||
dc.SetPen(*new wxPen(*new wxColour(0, 0, 0), 1, wxSOLID));
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(0, 0, 0), wxTRANSPARENT));
|
||||
dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxTRANSPARENT));
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
auto origin_px = to_pixels(Pointf(0, 0));
|
||||
|
@ -108,7 +108,7 @@ void Bed_2D::repaint()
|
|||
auto axes_len = 50;
|
||||
auto arrow_len = 6;
|
||||
auto arrow_angle = Geometry::deg2rad(45.0);
|
||||
dc.SetPen(*new wxPen(*new wxColour(255, 0, 0), 2, wxSOLID)); // red
|
||||
dc.SetPen(wxPen(wxColour(255, 0, 0), 2, wxSOLID)); // red
|
||||
auto x_end = Pointf(origin_px.x + axes_len, origin_px.y);
|
||||
dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(x_end.x, x_end.y));
|
||||
for (auto angle : { -arrow_angle, arrow_angle }){
|
||||
|
@ -118,7 +118,7 @@ void Bed_2D::repaint()
|
|||
dc.DrawLine(wxPoint(x_end.x, x_end.y), wxPoint(end.x, end.y));
|
||||
}
|
||||
|
||||
dc.SetPen(*new wxPen(*new wxColour(0, 255, 0), 2, wxSOLID)); // green
|
||||
dc.SetPen(wxPen(wxColour(0, 255, 0), 2, wxSOLID)); // green
|
||||
auto y_end = Pointf(origin_px.x, origin_px.y - axes_len);
|
||||
dc.DrawLine(wxPoint(origin_px.x, origin_px.y), wxPoint(y_end.x, y_end.y));
|
||||
for (auto angle : { -arrow_angle, arrow_angle }) {
|
||||
|
@ -129,19 +129,23 @@ void Bed_2D::repaint()
|
|||
}
|
||||
|
||||
// draw origin
|
||||
dc.SetPen(*new wxPen(*new wxColour(0, 0, 0), 1, wxSOLID));
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(0, 0, 0), wxSOLID));
|
||||
dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxSOLID));
|
||||
dc.DrawCircle(origin_px.x, origin_px.y, 3);
|
||||
|
||||
dc.SetTextForeground(*new wxColour(0, 0, 0));
|
||||
dc.SetFont(*new wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||
dc.DrawText("(0,0)", origin_px.x + 1, origin_px.y + 2);
|
||||
static const auto origin_label = wxString("(0,0)");
|
||||
dc.SetTextForeground(wxColour(0, 0, 0));
|
||||
dc.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||
auto extent = dc.GetTextExtent(origin_label);
|
||||
const auto origin_label_x = origin_px.x <= cw / 2 ? origin_px.x + 1 : origin_px.x - 1 - extent.GetWidth();
|
||||
const auto origin_label_y = origin_px.y <= ch / 2 ? origin_px.y + 1 : origin_px.y - 1 - extent.GetHeight();
|
||||
dc.DrawText(origin_label, origin_label_x, origin_label_y);
|
||||
|
||||
// draw current position
|
||||
if (m_pos!= Pointf(0, 0)) {
|
||||
auto pos_px = to_pixels(m_pos);
|
||||
dc.SetPen(*new wxPen(*new wxColour(200, 0, 0), 2, wxSOLID));
|
||||
dc.SetBrush(*new wxBrush(*new wxColour(200, 0, 0), wxTRANSPARENT));
|
||||
dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxSOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxTRANSPARENT));
|
||||
dc.DrawCircle(pos_px.x, pos_px.y, 5);
|
||||
|
||||
dc.DrawLine(pos_px.x - 15, pos_px.y, pos_px.x + 15, pos_px.y);
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef slic3r_2DBed_hpp_
|
||||
#define slic3r_2DBed_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "Config.hpp"
|
||||
|
||||
|
@ -45,3 +48,5 @@ public:
|
|||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_2DBed_hpp_ */
|
||||
|
|
|
@ -751,7 +751,10 @@ std::vector<double> GLVolumeCollection::get_current_print_zs() const
|
|||
// Collect layer top positions of all volumes.
|
||||
std::vector<double> print_zs;
|
||||
for (GLVolume *vol : this->volumes)
|
||||
append(print_zs, vol->print_zs);
|
||||
{
|
||||
if (vol->is_active)
|
||||
append(print_zs, vol->print_zs);
|
||||
}
|
||||
std::sort(print_zs.begin(), print_zs.end());
|
||||
|
||||
// Replace intervals of layers with similar top positions with their average value.
|
||||
|
@ -1757,6 +1760,11 @@ void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* pr
|
|||
{
|
||||
_generate_legend_texture(*preview_data, tool_colors);
|
||||
_load_shells(*print, *volumes, use_VBOs);
|
||||
|
||||
// removes empty volumes
|
||||
volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(),
|
||||
[](const GLVolume *volume) { return volume->print_zs.empty(); }),
|
||||
volumes->volumes.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
126
xs/src/slic3r/GUI/AboutDialog.cpp
Normal file
126
xs/src/slic3r/GUI/AboutDialog.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include "AboutDialog.hpp"
|
||||
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
AboutDialogLogo::AboutDialogLogo(wxWindow* parent)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
this->logo = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
|
||||
this->SetMinSize(this->logo.GetSize());
|
||||
|
||||
this->Bind(wxEVT_PAINT, &AboutDialogLogo::onRepaint, this);
|
||||
}
|
||||
|
||||
void AboutDialogLogo::onRepaint(wxEvent &event)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
dc.SetBackgroundMode(wxTRANSPARENT);
|
||||
|
||||
wxSize size = this->GetSize();
|
||||
int logo_w = this->logo.GetWidth();
|
||||
int logo_h = this->logo.GetHeight();
|
||||
dc.DrawBitmap(this->logo, (size.GetWidth() - logo_w)/2, (size.GetHeight() - logo_h)/2, true);
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
AboutDialog::AboutDialog()
|
||||
: wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxSize(600, 340), wxCAPTION)
|
||||
{
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)/**wxWHITE*/);
|
||||
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
this->SetSizer(hsizer);
|
||||
|
||||
// logo
|
||||
// AboutDialogLogo* logo = new AboutDialogLogo(this);
|
||||
wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
|
||||
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp));
|
||||
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
hsizer->Add(vsizer, 1, wxEXPAND, 0);
|
||||
|
||||
// title
|
||||
{
|
||||
wxStaticText* title = new wxStaticText(this, wxID_ANY, "Slic3r Prusa Edition", wxDefaultPosition, wxDefaultSize);
|
||||
wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
title_font.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
title_font.SetFamily(wxFONTFAMILY_ROMAN);
|
||||
title_font.SetPointSize(24);
|
||||
title->SetFont(title_font);
|
||||
vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 30);
|
||||
}
|
||||
|
||||
// version
|
||||
{
|
||||
auto version_string = _(L("Version ")) + std::string(SLIC3R_VERSION);
|
||||
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
|
||||
wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __WXMSW__
|
||||
version_font.SetPointSize(9);
|
||||
#else
|
||||
version_font.SetPointSize(11);
|
||||
#endif
|
||||
version->SetFont(version_font);
|
||||
vsizer->Add(version, 0, wxALIGN_LEFT | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
// text
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER);
|
||||
{
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __WXMSW__
|
||||
int size[] = {8,8,8,8,8,8,8};
|
||||
#else
|
||||
int size[] = {11,11,11,11,11,11,11};
|
||||
#endif
|
||||
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
html->SetHTMLBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
html->SetBorders(2);
|
||||
const char* text =
|
||||
"<html>"
|
||||
"<body bgcolor=\"#ffffff\" link=\"#808080\">"
|
||||
"<font color=\"#808080\">"
|
||||
"Copyright © 2016-2018 Prusa Research. <br />"
|
||||
"Copyright © 2011-2017 Alessandro Ranellucci. <br />"
|
||||
"<a href=\"http://slic3r.org/\">Slic3r</a> is licensed under the "
|
||||
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">GNU Affero General Public License, version 3</a>."
|
||||
"<br /><br /><br />"
|
||||
"Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. "
|
||||
"Manual by Gary Hodgson. Inspired by the RepRap community. <br />"
|
||||
"Slic3r logo designed by Corey Daniels, <a href=\"http://www.famfamfam.com/lab/icons/silk/\">Silk Icon Set</a> designed by Mark James. "
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
html->SetPage(text);
|
||||
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20);
|
||||
html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this);
|
||||
}
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->Bind(wxEVT_BUTTON, &AboutDialog::onCloseDialog, this, wxID_CLOSE);
|
||||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
}
|
||||
|
||||
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
||||
{
|
||||
wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
|
||||
event.Skip(false);
|
||||
}
|
||||
|
||||
void AboutDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
36
xs/src/slic3r/GUI/AboutDialog.hpp
Normal file
36
xs/src/slic3r/GUI/AboutDialog.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef slic3r_GUI_AboutDialog_hpp_
|
||||
#define slic3r_GUI_AboutDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class AboutDialogLogo : public wxPanel
|
||||
{
|
||||
public:
|
||||
AboutDialogLogo(wxWindow* parent);
|
||||
|
||||
private:
|
||||
wxBitmap logo;
|
||||
void onRepaint(wxEvent &event);
|
||||
};
|
||||
|
||||
class AboutDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
AboutDialog();
|
||||
|
||||
private:
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
|
@ -1,5 +1,3 @@
|
|||
#include <GL/glew.h>
|
||||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
|
@ -9,15 +7,22 @@
|
|||
#include <string.h>
|
||||
#include <utility>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/cenv.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static const std::string VENDOR_PREFIX = "vendor:";
|
||||
static const std::string MODEL_PREFIX = "model:";
|
||||
static const std::string VERSION_CHECK_URL = "https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/Slic3rPE.version";
|
||||
|
||||
void AppConfig::reset()
|
||||
{
|
||||
m_storage.clear();
|
||||
|
@ -42,9 +47,12 @@ void AppConfig::set_defaults()
|
|||
set("no_defaults", "1");
|
||||
if (get("show_incompatible_presets").empty())
|
||||
set("show_incompatible_presets", "0");
|
||||
// Version check is enabled by default in the config, but it is not implemented yet.
|
||||
|
||||
if (get("version_check").empty())
|
||||
set("version_check", "1");
|
||||
if (get("preset_update").empty())
|
||||
set("preset_update", "1");
|
||||
|
||||
// Use OpenGL 1.1 even if OpenGL 2.0 is available. This is mainly to support some buggy Intel HD Graphics drivers.
|
||||
// https://github.com/prusa3d/Slic3r/issues/233
|
||||
if (get("use_legacy_opengl").empty())
|
||||
|
@ -67,6 +75,19 @@ void AppConfig::load()
|
|||
if (! data.empty())
|
||||
// If there is a non-empty data, then it must be a top-level (without a section) config entry.
|
||||
m_storage[""][section.first] = data;
|
||||
} else if (boost::starts_with(section.first, VENDOR_PREFIX)) {
|
||||
// This is a vendor section listing enabled model / variants
|
||||
const auto vendor_name = section.first.substr(VENDOR_PREFIX.size());
|
||||
auto &vendor = m_vendors[vendor_name];
|
||||
for (const auto &kvp : section.second) {
|
||||
if (! boost::starts_with(kvp.first, MODEL_PREFIX)) { continue; }
|
||||
const auto model_name = kvp.first.substr(MODEL_PREFIX.size());
|
||||
std::vector<std::string> variants;
|
||||
if (! unescape_strings_cstyle(kvp.second.data(), variants)) { continue; }
|
||||
for (const auto &variant : variants) {
|
||||
vendor[model_name].insert(variant);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This must be a section name. Read the entries of a section.
|
||||
std::map<std::string, std::string> &storage = m_storage[section.first];
|
||||
|
@ -75,6 +96,16 @@ void AppConfig::load()
|
|||
}
|
||||
}
|
||||
|
||||
// Figure out if datadir has legacy presets
|
||||
auto ini_ver = Semver::parse(get("version"));
|
||||
m_legacy_datadir = false;
|
||||
if (ini_ver) {
|
||||
// Make 1.40.0 alphas compare well
|
||||
ini_ver->set_metadata(boost::none);
|
||||
ini_ver->set_prerelease(boost::none);
|
||||
m_legacy_datadir = ini_ver < Semver(1, 40, 0);
|
||||
}
|
||||
|
||||
// Override missing or keys with their defaults.
|
||||
this->set_defaults();
|
||||
m_dirty = false;
|
||||
|
@ -96,10 +127,57 @@ void AppConfig::save()
|
|||
for (const std::pair<std::string, std::string> &kvp : category.second)
|
||||
c << kvp.first << " = " << kvp.second << std::endl;
|
||||
}
|
||||
// Write vendor sections
|
||||
for (const auto &vendor : m_vendors) {
|
||||
size_t size_sum = 0;
|
||||
for (const auto &model : vendor.second) { size_sum += model.second.size(); }
|
||||
if (size_sum == 0) { continue; }
|
||||
|
||||
c << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl;
|
||||
|
||||
for (const auto &model : vendor.second) {
|
||||
if (model.second.size() == 0) { continue; }
|
||||
const std::vector<std::string> variants(model.second.begin(), model.second.end());
|
||||
const auto escaped = escape_strings_cstyle(variants);
|
||||
c << MODEL_PREFIX << model.first << " = " << escaped << std::endl;
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
bool AppConfig::get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const
|
||||
{
|
||||
const auto it_v = m_vendors.find(vendor);
|
||||
if (it_v == m_vendors.end()) { return false; }
|
||||
const auto it_m = it_v->second.find(model);
|
||||
return it_m == it_v->second.end() ? false : it_m->second.find(variant) != it_m->second.end();
|
||||
}
|
||||
|
||||
void AppConfig::set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
if (get_variant(vendor, model, variant)) { return; }
|
||||
m_vendors[vendor][model].insert(variant);
|
||||
} else {
|
||||
auto it_v = m_vendors.find(vendor);
|
||||
if (it_v == m_vendors.end()) { return; }
|
||||
auto it_m = it_v->second.find(model);
|
||||
if (it_m == it_v->second.end()) { return; }
|
||||
auto it_var = it_m->second.find(variant);
|
||||
if (it_var == it_m->second.end()) { return; }
|
||||
it_m->second.erase(it_var);
|
||||
}
|
||||
// If we got here, there was an update
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void AppConfig::set_vendors(const AppConfig &from)
|
||||
{
|
||||
m_vendors = from.m_vendors;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
std::string AppConfig::get_last_dir() const
|
||||
{
|
||||
const auto it = m_storage.find("recent");
|
||||
|
@ -161,6 +239,12 @@ std::string AppConfig::config_path()
|
|||
return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string();
|
||||
}
|
||||
|
||||
std::string AppConfig::version_check_url() const
|
||||
{
|
||||
auto from_settings = get("version_check_url");
|
||||
return from_settings.empty() ? VERSION_CHECK_URL : from_settings;
|
||||
}
|
||||
|
||||
bool AppConfig::exists()
|
||||
{
|
||||
return boost::filesystem::exists(AppConfig::config_path());
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
#ifndef slic3r_AppConfig_hpp_
|
||||
#define slic3r_AppConfig_hpp_
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig
|
||||
{
|
||||
public:
|
||||
AppConfig() : m_dirty(false) { this->reset(); }
|
||||
AppConfig() : m_dirty(false), m_legacy_datadir(false) { this->reset(); }
|
||||
|
||||
// Clear and reset to defaults.
|
||||
void reset();
|
||||
|
@ -65,6 +69,14 @@ public:
|
|||
void clear_section(const std::string §ion)
|
||||
{ m_storage[section].clear(); }
|
||||
|
||||
typedef std::map<std::string, std::map<std::string, std::set<std::string>>> VendorMap;
|
||||
bool get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const;
|
||||
void set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable);
|
||||
void set_vendors(const AppConfig &from);
|
||||
void set_vendors(const VendorMap &vendors) { m_vendors = vendors; m_dirty = true; }
|
||||
void set_vendors(VendorMap &&vendors) { m_vendors = std::move(vendors); m_dirty = true; }
|
||||
const VendorMap& vendors() const { return m_vendors; }
|
||||
|
||||
// return recent/skein_directory or recent/config_directory or empty string.
|
||||
std::string get_last_dir() const;
|
||||
void update_config_dir(const std::string &dir);
|
||||
|
@ -81,14 +93,25 @@ public:
|
|||
// Get the default config path from Slic3r::data_dir().
|
||||
static std::string config_path();
|
||||
|
||||
// Returns true if the user's data directory comes from before Slic3r 1.40.0 (no updating)
|
||||
bool legacy_datadir() const { return m_legacy_datadir; }
|
||||
|
||||
// Get the Slic3r version check url.
|
||||
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
|
||||
std::string version_check_url() const;
|
||||
|
||||
// Does the config file exist?
|
||||
static bool exists();
|
||||
|
||||
private:
|
||||
// Map of section, name -> value
|
||||
std::map<std::string, std::map<std::string, std::string>> m_storage;
|
||||
// Map of enabled vendors / models / variants
|
||||
VendorMap m_vendors;
|
||||
// Has any value been modified since the config.ini has been last saved or loaded?
|
||||
bool m_dirty;
|
||||
// Whether the existing version is before system profiles & configuration updating
|
||||
bool m_legacy_datadir;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#ifndef slic3r_BedShapeDialog_hpp_
|
||||
#define slic3r_BedShapeDialog_hpp_
|
||||
// The bed shape dialog.
|
||||
// The dialog opens from Print Settins tab->Bed Shape : Set...
|
||||
|
||||
|
@ -49,3 +51,6 @@ public:
|
|||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
|
||||
#endif /* slic3r_BedShapeDialog_hpp_ */
|
||||
|
|
84
xs/src/slic3r/GUI/ButtonsDescription.cpp
Normal file
84
xs/src/slic3r/GUI/ButtonsDescription.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include "ButtonsDescription.hpp"
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/clrpicker.h>
|
||||
|
||||
#include "GUI.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions) :
|
||||
wxDialog(parent, wxID_ANY, "Buttons And Text Colors Description", wxDefaultPosition, wxDefaultSize),
|
||||
m_icon_descriptions(icon_descriptions)
|
||||
{
|
||||
auto grid_sizer = new wxFlexGridSizer(3, 20, 20);
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(grid_sizer, 0, wxEXPAND | wxALL, 20);
|
||||
|
||||
// Icon description
|
||||
for (auto pair : *m_icon_descriptions)
|
||||
{
|
||||
auto icon = new wxStaticBitmap(this, wxID_ANY, *pair.first);
|
||||
grid_sizer->Add(icon, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
|
||||
std::istringstream f(pair.second);
|
||||
std::string s;
|
||||
getline(f, s, ';');
|
||||
auto description = new wxStaticText(this, wxID_ANY, _(s));
|
||||
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
getline(f, s, ';');
|
||||
description = new wxStaticText(this, wxID_ANY, _(s));
|
||||
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
|
||||
}
|
||||
|
||||
// Text color description
|
||||
auto sys_label = new wxStaticText(this, wxID_ANY, _(L("Value is the same as the system value")));
|
||||
sys_label->SetForegroundColour(get_label_clr_sys());
|
||||
auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_sys());
|
||||
sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([sys_colour, sys_label](wxCommandEvent e)
|
||||
{
|
||||
sys_label->SetForegroundColour(sys_colour->GetColour());
|
||||
sys_label->Refresh();
|
||||
}));
|
||||
size_t t= 0;
|
||||
while (t < 3){
|
||||
grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
|
||||
++t;
|
||||
}
|
||||
grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(sys_colour, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
|
||||
|
||||
auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset")));
|
||||
mod_label->SetForegroundColour(get_label_clr_modified());
|
||||
auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_modified());
|
||||
mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([mod_colour, mod_label](wxCommandEvent e)
|
||||
{
|
||||
mod_label->SetForegroundColour(mod_colour->GetColour());
|
||||
mod_label->Refresh();
|
||||
}));
|
||||
grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(mod_colour, -1, wxALIGN_CENTRE_VERTICAL);
|
||||
grid_sizer->Add(mod_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
|
||||
|
||||
|
||||
auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL);
|
||||
main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
|
||||
|
||||
wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
|
||||
btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) {
|
||||
set_label_clr_sys(sys_colour->GetColour());
|
||||
set_label_clr_modified(mod_colour->GetColour());
|
||||
EndModal(wxID_OK);
|
||||
});
|
||||
|
||||
SetSizer(main_sizer);
|
||||
main_sizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
27
xs/src/slic3r/GUI/ButtonsDescription.hpp
Normal file
27
xs/src/slic3r/GUI/ButtonsDescription.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef slic3r_ButtonsDescription_hpp
|
||||
#define slic3r_ButtonsDescription_hpp
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
using t_icon_descriptions = std::vector<std::pair<wxBitmap*, std::string>>;
|
||||
|
||||
class ButtonsDescription : public wxDialog
|
||||
{
|
||||
t_icon_descriptions* m_icon_descriptions;
|
||||
public:
|
||||
ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions);
|
||||
~ButtonsDescription(){}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
|
||||
#endif
|
||||
|
139
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
Normal file
139
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
#include "ConfigSnapshotDialog.hpp"
|
||||
|
||||
#include "../Config/Snapshot.hpp"
|
||||
#include "../Utils/Time.hpp"
|
||||
|
||||
#include "../../libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static std::string format_reason(const Config::Snapshot::Reason reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case Config::Snapshot::SNAPSHOT_UPGRADE:
|
||||
return std::string(_(L("Upgrade")));
|
||||
case Config::Snapshot::SNAPSHOT_DOWNGRADE:
|
||||
return std::string(_(L("Downgrade")));
|
||||
case Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK:
|
||||
return std::string(_(L("Before roll back")));
|
||||
case Config::Snapshot::SNAPSHOT_USER:
|
||||
return std::string(_(L("User")));
|
||||
case Config::Snapshot::SNAPSHOT_UNKNOWN:
|
||||
default:
|
||||
return std::string(_(L("Unknown")));
|
||||
}
|
||||
}
|
||||
|
||||
static std::string generate_html_row(const Config::Snapshot &snapshot, bool row_even, bool snapshot_active)
|
||||
{
|
||||
// Start by declaring a row with an alternating background color.
|
||||
std::string text = "<tr bgcolor=\"";
|
||||
text += snapshot_active ? "#B3FFCB" : (row_even ? "#FFFFFF" : "#D5D5D5");
|
||||
text += "\">";
|
||||
text += "<td>";
|
||||
// Format the row header.
|
||||
text += std::string("<font size=\"5\"><b>") + (snapshot_active ? _(L("Active: ")) : "") +
|
||||
Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason);
|
||||
if (! snapshot.comment.empty())
|
||||
text += " (" + snapshot.comment + ")";
|
||||
text += "</b></font><br>";
|
||||
// End of row header.
|
||||
text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
||||
text += _(L("print")) + ": " + snapshot.print + "<br>";
|
||||
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
|
||||
text += _(L("printer")) + ": " + snapshot.printer + "<br>";
|
||||
|
||||
bool compatible = true;
|
||||
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
|
||||
text += _(L("vendor")) + ": " + vc.name + ", ver: " + vc.version.config_version.to_string() + ", min slic3r ver: " + vc.version.min_slic3r_version.to_string();
|
||||
if (vc.version.max_slic3r_version != Semver::inf())
|
||||
text += ", max slic3r ver: " + vc.version.max_slic3r_version.to_string();
|
||||
text += "<br>";
|
||||
for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) {
|
||||
text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": ";
|
||||
for (const std::string &variant : model.second) {
|
||||
if (&variant != &*model.second.begin())
|
||||
text += ", ";
|
||||
text += variant;
|
||||
}
|
||||
text += "<br>";
|
||||
}
|
||||
if (! vc.version.is_current_slic3r_supported()) { compatible = false; }
|
||||
}
|
||||
|
||||
if (! compatible) {
|
||||
text += "<p align=\"right\">" + _(L("Incompatible with this Slic3r")) + "</p>";
|
||||
}
|
||||
else if (! snapshot_active)
|
||||
text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>";
|
||||
text += "</td>";
|
||||
text += "</tr>";
|
||||
return text;
|
||||
}
|
||||
|
||||
static std::string generate_html_page(const Config::SnapshotDB &snapshot_db, const std::string &on_snapshot)
|
||||
{
|
||||
std::string text =
|
||||
"<html>"
|
||||
"<body bgcolor=\"#ffffff\" cellspacing=\"2\" cellpadding=\"0\" border=\"0\" link=\"#800000\">"
|
||||
"<font color=\"#000000\">";
|
||||
text += "<table style=\"width:100%\">";
|
||||
for (size_t i_row = 0; i_row < snapshot_db.snapshots().size(); ++ i_row) {
|
||||
const Config::Snapshot &snapshot = snapshot_db.snapshots()[snapshot_db.snapshots().size() - i_row - 1];
|
||||
text += generate_html_row(snapshot, i_row & 1, snapshot.id == on_snapshot);
|
||||
}
|
||||
text +=
|
||||
"</table>"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
return text;
|
||||
}
|
||||
|
||||
ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const std::string &on_snapshot)
|
||||
: wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
this->SetSizer(vsizer);
|
||||
|
||||
// text
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
||||
{
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
#ifdef __WXMSW__
|
||||
int size[] = {8,8,8,8,11,11,11};
|
||||
#else
|
||||
int size[] = {11,11,11,11,14,14,14};
|
||||
#endif
|
||||
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
html->SetBorders(2);
|
||||
std::string text = generate_html_page(snapshot_db, on_snapshot);
|
||||
html->SetPage(text.c_str());
|
||||
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0);
|
||||
html->Bind(wxEVT_HTML_LINK_CLICKED, &ConfigSnapshotDialog::onLinkClicked, this);
|
||||
}
|
||||
|
||||
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
this->SetEscapeId(wxID_CLOSE);
|
||||
this->Bind(wxEVT_BUTTON, &ConfigSnapshotDialog::onCloseDialog, this, wxID_CLOSE);
|
||||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
}
|
||||
|
||||
void ConfigSnapshotDialog::onLinkClicked(wxHtmlLinkEvent &event)
|
||||
{
|
||||
m_snapshot_to_activate = event.GetLinkInfo().GetHref();
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void ConfigSnapshotDialog::onCloseDialog(wxEvent &)
|
||||
{
|
||||
this->EndModal(wxID_CLOSE);
|
||||
this->Close();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
34
xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp
Normal file
34
xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef slic3r_GUI_ConfigSnapshotDialog_hpp_
|
||||
#define slic3r_GUI_ConfigSnapshotDialog_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
namespace Config {
|
||||
class SnapshotDB;
|
||||
}
|
||||
|
||||
class ConfigSnapshotDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const std::string &id);
|
||||
const std::string& snapshot_to_activate() const { return m_snapshot_to_activate; }
|
||||
|
||||
private:
|
||||
void onLinkClicked(wxHtmlLinkEvent &event);
|
||||
void onCloseDialog(wxEvent &);
|
||||
|
||||
// If set, it contains a snapshot ID to be restored after the dialog closes.
|
||||
std::string m_snapshot_to_activate;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GUI_ConfigSnapshotDialog_hpp_ */
|
860
xs/src/slic3r/GUI/ConfigWizard.cpp
Normal file
860
xs/src/slic3r/GUI/ConfigWizard.cpp
Normal file
|
@ -0,0 +1,860 @@
|
|||
#include "ConfigWizard_private.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/settings.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/dcclient.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
// Printer model picker GUI control
|
||||
|
||||
struct PrinterPickerEvent : public wxEvent
|
||||
{
|
||||
std::string vendor_id;
|
||||
std::string model_id;
|
||||
std::string variant_name;
|
||||
bool enable;
|
||||
|
||||
PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) :
|
||||
wxEvent(winid, eventType),
|
||||
vendor_id(std::move(vendor_id)),
|
||||
model_id(std::move(model_id)),
|
||||
variant_name(std::move(variant_name)),
|
||||
enable(enable)
|
||||
{}
|
||||
|
||||
virtual wxEvent *Clone() const
|
||||
{
|
||||
return new PrinterPickerEvent(*this);
|
||||
}
|
||||
};
|
||||
|
||||
wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent);
|
||||
|
||||
PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors) :
|
||||
wxPanel(parent),
|
||||
vendor_id(vendor.id),
|
||||
variants_checked(0)
|
||||
{
|
||||
const auto &models = vendor.models;
|
||||
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20);
|
||||
printer_grid->SetFlexibleDirection(wxVERTICAL);
|
||||
sizer->Add(printer_grid);
|
||||
|
||||
auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
namefont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
for (const auto &model : models) {
|
||||
auto *panel = new wxPanel(this);
|
||||
auto *col_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
panel->SetSizer(col_sizer);
|
||||
|
||||
auto *title = new wxStaticText(panel, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
title->SetFont(namefont);
|
||||
col_sizer->Add(title, 0, wxBOTTOM, 3);
|
||||
|
||||
auto bitmap_file = wxString::Format("printers/%s_%s.png", vendor.id, model.id);
|
||||
wxBitmap bitmap(GUI::from_u8(Slic3r::var(bitmap_file.ToStdString())), wxBITMAP_TYPE_PNG);
|
||||
auto *bitmap_widget = new wxStaticBitmap(panel, wxID_ANY, bitmap);
|
||||
col_sizer->Add(bitmap_widget, 0, wxBOTTOM, 3);
|
||||
|
||||
col_sizer->AddSpacer(20);
|
||||
|
||||
const auto model_id = model.id;
|
||||
|
||||
for (const auto &variant : model.variants) {
|
||||
const auto label = wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle")));
|
||||
auto *cbox = new Checkbox(panel, label, model_id, variant.name);
|
||||
const size_t idx = cboxes.size();
|
||||
cboxes.push_back(cbox);
|
||||
bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name);
|
||||
variants_checked += enabled;
|
||||
cbox->SetValue(enabled);
|
||||
col_sizer->Add(cbox, 0, wxBOTTOM, 3);
|
||||
cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) {
|
||||
if (idx >= this->cboxes.size()) { return; }
|
||||
this->on_checkbox(this->cboxes[idx], event.IsChecked());
|
||||
});
|
||||
}
|
||||
|
||||
printer_grid->Add(panel);
|
||||
}
|
||||
|
||||
auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all")));
|
||||
auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none")));
|
||||
sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); });
|
||||
sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); });
|
||||
all_none_sizer->AddStretchSpacer();
|
||||
all_none_sizer->Add(sel_all);
|
||||
all_none_sizer->Add(sel_none);
|
||||
sizer->AddStretchSpacer();
|
||||
sizer->Add(all_none_sizer, 0, wxEXPAND);
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
void PrinterPicker::select_all(bool select)
|
||||
{
|
||||
for (const auto &cb : cboxes) {
|
||||
if (cb->GetValue() != select) {
|
||||
cb->SetValue(select);
|
||||
on_checkbox(cb, select);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked)
|
||||
{
|
||||
variants_checked += checked ? 1 : -1;
|
||||
PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
|
||||
AddPendingEvent(evt);
|
||||
}
|
||||
|
||||
|
||||
// Wizard page base
|
||||
|
||||
ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) :
|
||||
wxPanel(parent),
|
||||
parent(parent),
|
||||
shortname(std::move(shortname)),
|
||||
p_prev(nullptr),
|
||||
p_next(nullptr)
|
||||
{
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
auto font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
font.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
font.SetPointSize(14);
|
||||
text->SetFont(font);
|
||||
sizer->Add(text, 0, wxALIGN_LEFT, 0);
|
||||
sizer->AddSpacer(10);
|
||||
|
||||
content = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(content, 1);
|
||||
|
||||
SetSizer(sizer);
|
||||
|
||||
this->Hide();
|
||||
|
||||
Bind(wxEVT_SIZE, [this](wxSizeEvent &event) {
|
||||
this->Layout();
|
||||
event.Skip();
|
||||
});
|
||||
}
|
||||
|
||||
ConfigWizardPage::~ConfigWizardPage() {}
|
||||
|
||||
ConfigWizardPage* ConfigWizardPage::chain(ConfigWizardPage *page)
|
||||
{
|
||||
if (p_next != nullptr) { p_next->p_prev = nullptr; }
|
||||
p_next = page;
|
||||
if (page != nullptr) {
|
||||
if (page->p_prev != nullptr) { page->p_prev->p_next = nullptr; }
|
||||
page->p_prev = this;
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void ConfigWizardPage::append_text(wxString text)
|
||||
{
|
||||
auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
widget->Wrap(CONTENT_WIDTH);
|
||||
widget->SetMinSize(wxSize(CONTENT_WIDTH, -1));
|
||||
append(widget);
|
||||
}
|
||||
|
||||
void ConfigWizardPage::append_spacer(int space)
|
||||
{
|
||||
content->AddSpacer(space);
|
||||
}
|
||||
|
||||
bool ConfigWizardPage::Show(bool show)
|
||||
{
|
||||
if (extra_buttons() != nullptr) { extra_buttons()->Show(show); }
|
||||
return wxPanel::Show(show);
|
||||
}
|
||||
|
||||
void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable); }
|
||||
|
||||
|
||||
// Wizard pages
|
||||
|
||||
PageWelcome::PageWelcome(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))),
|
||||
printer_picker(nullptr),
|
||||
others_buttons(new wxPanel(parent)),
|
||||
cbox_reset(nullptr)
|
||||
{
|
||||
if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) {
|
||||
wxString::Format(_(L("Run %s")), ConfigWizard::name());
|
||||
append_text(wxString::Format(
|
||||
_(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")),
|
||||
ConfigWizard::name())
|
||||
);
|
||||
} else {
|
||||
cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)")));
|
||||
append(cbox_reset);
|
||||
}
|
||||
|
||||
const auto &vendors = wizard_p()->vendors;
|
||||
const auto vendor_prusa = vendors.find("PrusaResearch");
|
||||
|
||||
if (vendor_prusa != vendors.cend()) {
|
||||
AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
|
||||
|
||||
printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors);
|
||||
printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
|
||||
appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
|
||||
this->on_variant_checked();
|
||||
});
|
||||
|
||||
append(printer_picker);
|
||||
}
|
||||
|
||||
const size_t num_other_vendors = vendors.size() - (vendor_prusa != vendors.cend());
|
||||
auto *sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *other_vendors = new wxButton(others_buttons, wxID_ANY, _(L("Other vendors")));
|
||||
other_vendors->Enable(num_other_vendors > 0);
|
||||
auto *custom_setup = new wxButton(others_buttons, wxID_ANY, _(L("Custom setup")));
|
||||
|
||||
sizer->Add(other_vendors);
|
||||
sizer->AddSpacer(BTN_SPACING);
|
||||
sizer->Add(custom_setup);
|
||||
|
||||
other_vendors->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_other_vendors(); });
|
||||
custom_setup->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_custom_setup(); });
|
||||
|
||||
others_buttons->SetSizer(sizer);
|
||||
}
|
||||
|
||||
void PageWelcome::on_page_set()
|
||||
{
|
||||
chain(wizard_p()->page_update);
|
||||
on_variant_checked();
|
||||
}
|
||||
|
||||
void PageWelcome::on_variant_checked()
|
||||
{
|
||||
enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false);
|
||||
}
|
||||
|
||||
PageUpdate::PageUpdate(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))),
|
||||
version_check(true),
|
||||
preset_update(true)
|
||||
{
|
||||
const AppConfig *app_config = GUI::get_app_config();
|
||||
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates")));
|
||||
box_slic3r->SetValue(app_config->get("version_check") == "1");
|
||||
append(box_slic3r);
|
||||
append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.")));
|
||||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically")));
|
||||
box_presets->SetValue(app_config->get("preset_update") == "1");
|
||||
append(box_presets);
|
||||
append_text(_(L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.")));
|
||||
const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings."));
|
||||
auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold);
|
||||
label_bold->SetFont(boldfont);
|
||||
label_bold->Wrap(CONTENT_WIDTH);
|
||||
append(label_bold);
|
||||
append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied.")));
|
||||
|
||||
box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); });
|
||||
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
|
||||
}
|
||||
|
||||
PageVendors::PageVendors(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors")))
|
||||
{
|
||||
append_text(_(L("Pick another vendor supported by Slic3r PE:")));
|
||||
|
||||
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors;
|
||||
wxArrayString choices_vendors;
|
||||
|
||||
for (const auto vendor_pair : wizard_p()->vendors) {
|
||||
const auto &vendor = vendor_pair.second;
|
||||
if (vendor.id == "PrusaResearch") { continue; }
|
||||
|
||||
auto *picker = new PrinterPicker(this, vendor, appconfig_vendors);
|
||||
picker->Hide();
|
||||
pickers.push_back(picker);
|
||||
choices_vendors.Add(vendor.name);
|
||||
|
||||
picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) {
|
||||
appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable);
|
||||
this->on_variant_checked();
|
||||
});
|
||||
}
|
||||
|
||||
auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors);
|
||||
if (choices_vendors.GetCount() > 0) {
|
||||
vendor_picker->SetSelection(0);
|
||||
on_vendor_pick(0);
|
||||
}
|
||||
|
||||
vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) {
|
||||
this->on_vendor_pick(evt.GetInt());
|
||||
});
|
||||
|
||||
append(vendor_picker);
|
||||
for (PrinterPicker *picker : pickers) { this->append(picker); }
|
||||
}
|
||||
|
||||
void PageVendors::on_page_set()
|
||||
{
|
||||
on_variant_checked();
|
||||
}
|
||||
|
||||
void PageVendors::on_vendor_pick(size_t i)
|
||||
{
|
||||
for (PrinterPicker *picker : pickers) { picker->Hide(); }
|
||||
if (i < pickers.size()) {
|
||||
pickers[i]->Show();
|
||||
wizard_p()->layout_fit();
|
||||
}
|
||||
}
|
||||
|
||||
void PageVendors::on_variant_checked()
|
||||
{
|
||||
size_t variants_checked = 0;
|
||||
for (const PrinterPicker *picker : pickers) { variants_checked += picker->variants_checked; }
|
||||
enable_next(variants_checked > 0);
|
||||
}
|
||||
|
||||
PageFirmware::PageFirmware(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))),
|
||||
gcode_opt(print_config_def.options["gcode_flavor"]),
|
||||
gcode_picker(nullptr)
|
||||
{
|
||||
append_text(_(L("Choose the type of firmware used by your printer.")));
|
||||
append_text(gcode_opt.tooltip);
|
||||
|
||||
wxArrayString choices;
|
||||
choices.Alloc(gcode_opt.enum_labels.size());
|
||||
for (const auto &label : gcode_opt.enum_labels) {
|
||||
choices.Add(label);
|
||||
}
|
||||
|
||||
gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
|
||||
const auto &enum_values = gcode_opt.enum_values;
|
||||
auto needle = enum_values.cend();
|
||||
if (gcode_opt.default_value != nullptr) {
|
||||
needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize());
|
||||
}
|
||||
if (needle != enum_values.cend()) {
|
||||
gcode_picker->SetSelection(needle - enum_values.cbegin());
|
||||
} else {
|
||||
gcode_picker->SetSelection(0);
|
||||
}
|
||||
|
||||
append(gcode_picker);
|
||||
}
|
||||
|
||||
void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
|
||||
{
|
||||
ConfigOptionEnum<GCodeFlavor> opt;
|
||||
|
||||
auto sel = gcode_picker->GetSelection();
|
||||
if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) {
|
||||
config.set_key_value("gcode_flavor", &opt);
|
||||
}
|
||||
}
|
||||
|
||||
PageBedShape::PageBedShape(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape"))),
|
||||
shape_panel(new BedShapePanel(this))
|
||||
{
|
||||
append_text(_(L("Set the shape of your printer's bed.")));
|
||||
|
||||
shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
append(shape_panel);
|
||||
}
|
||||
|
||||
void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
|
||||
{
|
||||
const auto points(shape_panel->GetValue());
|
||||
auto *opt = new ConfigOptionPoints(points);
|
||||
config.set_key_value("bed_shape", opt);
|
||||
}
|
||||
|
||||
PageDiameters::PageDiameters(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters"))),
|
||||
spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)),
|
||||
spin_filam(new wxSpinCtrlDouble(this, wxID_ANY))
|
||||
{
|
||||
spin_nozzle->SetDigits(2);
|
||||
spin_nozzle->SetIncrement(0.1);
|
||||
const auto &def_nozzle = print_config_def.options["nozzle_diameter"];
|
||||
auto *default_nozzle = dynamic_cast<const ConfigOptionFloats*>(def_nozzle.default_value);
|
||||
spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5);
|
||||
|
||||
spin_filam->SetDigits(2);
|
||||
spin_filam->SetIncrement(0.25);
|
||||
const auto &def_filam = print_config_def.options["filament_diameter"];
|
||||
auto *default_filam = dynamic_cast<const ConfigOptionFloats*>(def_filam.default_value);
|
||||
spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0);
|
||||
|
||||
append_text(_(L("Enter the diameter of your printer's hot end nozzle.")));
|
||||
|
||||
auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:")));
|
||||
auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm")));
|
||||
sizer_nozzle->AddGrowableCol(0, 1);
|
||||
sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_nozzle->Add(spin_nozzle);
|
||||
sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
append(sizer_nozzle);
|
||||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
append_text(_(L("Enter the diameter of your filament.")));
|
||||
append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.")));
|
||||
|
||||
auto *sizer_filam = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:")));
|
||||
auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm")));
|
||||
sizer_filam->AddGrowableCol(0, 1);
|
||||
sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_filam->Add(spin_filam);
|
||||
sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
append(sizer_filam);
|
||||
}
|
||||
|
||||
void PageDiameters::apply_custom_config(DynamicPrintConfig &config)
|
||||
{
|
||||
auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue());
|
||||
config.set_key_value("nozzle_diameter", opt_nozzle);
|
||||
auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue());
|
||||
config.set_key_value("filament_diameter", opt_filam);
|
||||
}
|
||||
|
||||
PageTemperatures::PageTemperatures(ConfigWizard *parent) :
|
||||
ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures"))),
|
||||
spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)),
|
||||
spin_bed(new wxSpinCtrlDouble(this, wxID_ANY))
|
||||
{
|
||||
spin_extr->SetIncrement(5.0);
|
||||
const auto &def_extr = print_config_def.options["temperature"];
|
||||
spin_extr->SetRange(def_extr.min, def_extr.max);
|
||||
auto *default_extr = dynamic_cast<const ConfigOptionInts*>(def_extr.default_value);
|
||||
spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200);
|
||||
|
||||
spin_bed->SetIncrement(5.0);
|
||||
const auto &def_bed = print_config_def.options["bed_temperature"];
|
||||
spin_bed->SetRange(def_bed.min, def_bed.max);
|
||||
auto *default_bed = dynamic_cast<const ConfigOptionInts*>(def_bed.default_value);
|
||||
spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
|
||||
|
||||
append_text(_(L("Enter the temperature needed for extruding your filament.")));
|
||||
append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.")));
|
||||
|
||||
auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:")));
|
||||
auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C")));
|
||||
sizer_extr->AddGrowableCol(0, 1);
|
||||
sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_extr->Add(spin_extr);
|
||||
sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
append(sizer_extr);
|
||||
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed.")));
|
||||
append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.")));
|
||||
|
||||
auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:")));
|
||||
auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C")));
|
||||
sizer_bed->AddGrowableCol(0, 1);
|
||||
sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
sizer_bed->Add(spin_bed);
|
||||
sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL);
|
||||
append(sizer_bed);
|
||||
}
|
||||
|
||||
void PageTemperatures::apply_custom_config(DynamicPrintConfig &config)
|
||||
{
|
||||
auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue());
|
||||
config.set_key_value("temperature", opt_extr);
|
||||
auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue());
|
||||
config.set_key_value("first_layer_temperature", opt_extr1st);
|
||||
auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue());
|
||||
config.set_key_value("bed_temperature", opt_bed);
|
||||
auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue());
|
||||
config.set_key_value("first_layer_bed_temperature", opt_bed1st);
|
||||
}
|
||||
|
||||
|
||||
// Index
|
||||
|
||||
ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) :
|
||||
wxPanel(parent),
|
||||
bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG),
|
||||
bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG),
|
||||
bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG),
|
||||
bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG)
|
||||
{
|
||||
SetMinSize(bg.GetSize());
|
||||
|
||||
wxClientDC dc(this);
|
||||
text_height = dc.GetCharHeight();
|
||||
|
||||
// Add logo bitmap.
|
||||
// This could be done in on_paint() along with the index labels, but I've found it tricky
|
||||
// to get the bitmap rendered well on all platforms with transparent background.
|
||||
// In some cases it didn't work at all. And so wxStaticBitmap is used here instead,
|
||||
// because it has all the platform quirks figured out.
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto *logo = new wxStaticBitmap(this, wxID_ANY, bg);
|
||||
sizer->AddStretchSpacer();
|
||||
sizer->Add(logo);
|
||||
SetSizer(sizer);
|
||||
|
||||
Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this);
|
||||
}
|
||||
|
||||
void ConfigWizardIndex::load_items(ConfigWizardPage *firstpage)
|
||||
{
|
||||
items.clear();
|
||||
item_active = items.cend();
|
||||
|
||||
for (auto *page = firstpage; page != nullptr; page = page->page_next()) {
|
||||
items.emplace_back(page->shortname);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ConfigWizardIndex::set_active(ConfigWizardPage *page)
|
||||
{
|
||||
item_active = std::find(items.cbegin(), items.cend(), page->shortname);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
|
||||
{
|
||||
enum {
|
||||
MARGIN = 10,
|
||||
SPACING = 5,
|
||||
};
|
||||
|
||||
const auto size = GetClientSize();
|
||||
if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; }
|
||||
|
||||
wxPaintDC dc(this);
|
||||
|
||||
const auto bullet_w = bullet_black.GetSize().GetWidth();
|
||||
const auto bullet_h = bullet_black.GetSize().GetHeight();
|
||||
const int yoff_icon = bullet_h < text_height ? (text_height - bullet_h) / 2 : 0;
|
||||
const int yoff_text = bullet_h > text_height ? (bullet_h - text_height) / 2 : 0;
|
||||
const int yinc = std::max(bullet_h, text_height) + SPACING;
|
||||
|
||||
unsigned y = 0;
|
||||
for (auto it = items.cbegin(); it != items.cend(); ++it) {
|
||||
if (it < item_active) { dc.DrawBitmap(bullet_black, MARGIN, y + yoff_icon, false); }
|
||||
if (it == item_active) { dc.DrawBitmap(bullet_blue, MARGIN, y + yoff_icon, false); }
|
||||
if (it > item_active) { dc.DrawBitmap(bullet_white, MARGIN, y + yoff_icon, false); }
|
||||
dc.DrawText(*it, MARGIN + bullet_w + SPACING, y + yoff_text);
|
||||
y += yinc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// priv
|
||||
|
||||
static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{
|
||||
{ "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") },
|
||||
{ "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2S", "0.4") },
|
||||
{ "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
|
||||
{ "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2S", "0.4") },
|
||||
{ "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
|
||||
{ "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") },
|
||||
{ "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") },
|
||||
{ "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") },
|
||||
}};
|
||||
|
||||
void ConfigWizard::priv::load_vendors()
|
||||
{
|
||||
const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor";
|
||||
const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles";
|
||||
|
||||
// Load vendors from the "vendors" directory in datadir
|
||||
for (fs::directory_iterator it(vendor_dir); it != fs::directory_iterator(); ++it) {
|
||||
if (it->path().extension() == ".ini") {
|
||||
auto vp = VendorProfile::from_ini(it->path());
|
||||
vendors[vp.id] = std::move(vp);
|
||||
}
|
||||
}
|
||||
|
||||
// Additionally load up vendors from the application resources directory, but only those not seen in the datadir
|
||||
for (fs::directory_iterator it(rsrc_vendor_dir); it != fs::directory_iterator(); ++it) {
|
||||
if (it->path().extension() == ".ini") {
|
||||
const auto id = it->path().stem().string();
|
||||
if (vendors.find(id) == vendors.end()) {
|
||||
auto vp = VendorProfile::from_ini(it->path());
|
||||
vendors_rsrc[vp.id] = it->path().filename().string();
|
||||
vendors[vp.id] = std::move(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load up the set of vendors / models / variants the user has had enabled up till now
|
||||
const AppConfig *app_config = GUI::get_app_config();
|
||||
if (! app_config->legacy_datadir()) {
|
||||
appconfig_vendors.set_vendors(*app_config);
|
||||
} else {
|
||||
// In case of legacy datadir, try to guess the preference based on the printer preset files that are present
|
||||
const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer";
|
||||
for (fs::directory_iterator it(printer_dir); it != fs::directory_iterator(); ++it) {
|
||||
auto needle = legacy_preset_map.find(it->path().filename().string());
|
||||
if (needle == legacy_preset_map.end()) { continue; }
|
||||
|
||||
const auto &model = needle->second.first;
|
||||
const auto &variant = needle->second.second;
|
||||
appconfig_vendors.set_variant("PrusaResearch", model, variant, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::index_refresh()
|
||||
{
|
||||
index->load_items(page_welcome);
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::add_page(ConfigWizardPage *page)
|
||||
{
|
||||
topsizer->Add(page, 0, wxEXPAND);
|
||||
|
||||
auto *extra_buttons = page->extra_buttons();
|
||||
if (extra_buttons != nullptr) {
|
||||
btnsizer->Prepend(extra_buttons, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::set_page(ConfigWizardPage *page)
|
||||
{
|
||||
if (page == nullptr) { return; }
|
||||
if (page_current != nullptr) { page_current->Hide(); }
|
||||
page_current = page;
|
||||
enable_next(true);
|
||||
|
||||
page->on_page_set();
|
||||
index->load_items(page_welcome);
|
||||
index->set_active(page);
|
||||
page->Show();
|
||||
|
||||
btn_prev->Enable(page->page_prev() != nullptr);
|
||||
btn_next->Show(page->page_next() != nullptr);
|
||||
btn_finish->Show(page->page_next() == nullptr);
|
||||
|
||||
layout_fit();
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::layout_fit()
|
||||
{
|
||||
q->Layout();
|
||||
q->Fit();
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::enable_next(bool enable)
|
||||
{
|
||||
btn_next->Enable(enable);
|
||||
btn_finish->Enable(enable);
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::on_other_vendors()
|
||||
{
|
||||
page_welcome
|
||||
->chain(page_vendors)
|
||||
->chain(page_update);
|
||||
set_page(page_vendors);
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::on_custom_setup()
|
||||
{
|
||||
page_welcome->chain(page_firmware);
|
||||
page_temps->chain(page_update);
|
||||
set_page(page_firmware);
|
||||
}
|
||||
|
||||
void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
|
||||
{
|
||||
const bool is_custom_setup = page_welcome->page_next() == page_firmware;
|
||||
|
||||
if (! is_custom_setup) {
|
||||
const auto enabled_vendors = appconfig_vendors.vendors();
|
||||
|
||||
// Install bundles from resources if needed:
|
||||
std::vector<std::string> install_bundles;
|
||||
for (const auto &vendor_rsrc : vendors_rsrc) {
|
||||
const auto vendor = enabled_vendors.find(vendor_rsrc.first);
|
||||
if (vendor == enabled_vendors.end()) { continue; }
|
||||
|
||||
size_t size_sum = 0;
|
||||
for (const auto &model : vendor->second) { size_sum += model.second.size(); }
|
||||
if (size_sum == 0) { continue; }
|
||||
|
||||
// This vendor needs to be installed
|
||||
install_bundles.emplace_back(vendor_rsrc.second);
|
||||
}
|
||||
|
||||
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
||||
bool snapshot = true;
|
||||
switch (run_reason) {
|
||||
case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break;
|
||||
case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break;
|
||||
case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason
|
||||
case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break;
|
||||
}
|
||||
if (install_bundles.size() > 0) {
|
||||
// Install bundles from resources.
|
||||
updater->install_bundles_rsrc(std::move(install_bundles), snapshot);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
|
||||
}
|
||||
|
||||
if (page_welcome->reset_user_profile()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Resetting user profiles...";
|
||||
preset_bundle->reset(true);
|
||||
}
|
||||
|
||||
app_config->set_vendors(appconfig_vendors);
|
||||
app_config->set("version_check", page_update->version_check ? "1" : "0");
|
||||
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
|
||||
app_config->reset_selections();
|
||||
// ^ TODO: replace with appropriate printer selection
|
||||
preset_bundle->load_presets(*app_config);
|
||||
} else {
|
||||
for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) {
|
||||
page->apply_custom_config(*custom_config);
|
||||
}
|
||||
preset_bundle->load_config("My Settings", *custom_config);
|
||||
}
|
||||
// Update the selections from the compatibilty.
|
||||
preset_bundle->export_selections(*app_config);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
|
||||
wxDialog(parent, wxID_ANY, name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
p(new priv(this))
|
||||
{
|
||||
p->run_reason = reason;
|
||||
|
||||
p->load_vendors();
|
||||
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
|
||||
"gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
|
||||
}));
|
||||
|
||||
p->index = new ConfigWizardIndex(this);
|
||||
|
||||
auto *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
p->topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *hline = new wxStaticLine(this);
|
||||
p->btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
p->topsizer->Add(p->index, 0, wxEXPAND);
|
||||
p->topsizer->AddSpacer(INDEX_MARGIN);
|
||||
|
||||
p->btn_prev = new wxButton(this, wxID_BACKWARD);
|
||||
p->btn_next = new wxButton(this, wxID_FORWARD);
|
||||
p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
|
||||
p->btn_cancel = new wxButton(this, wxID_CANCEL);
|
||||
p->btnsizer->AddStretchSpacer();
|
||||
p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
|
||||
p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);
|
||||
p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING);
|
||||
p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING);
|
||||
|
||||
p->add_page(p->page_welcome = new PageWelcome(this));
|
||||
p->add_page(p->page_update = new PageUpdate(this));
|
||||
p->add_page(p->page_vendors = new PageVendors(this));
|
||||
p->add_page(p->page_firmware = new PageFirmware(this));
|
||||
p->add_page(p->page_bed = new PageBedShape(this));
|
||||
p->add_page(p->page_diams = new PageDiameters(this));
|
||||
p->add_page(p->page_temps = new PageTemperatures(this));
|
||||
p->index_refresh();
|
||||
|
||||
p->page_welcome->chain(p->page_update);
|
||||
p->page_firmware
|
||||
->chain(p->page_bed)
|
||||
->chain(p->page_diams)
|
||||
->chain(p->page_temps);
|
||||
|
||||
vsizer->Add(p->topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
vsizer->Add(hline, 0, wxEXPAND);
|
||||
vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
|
||||
p->set_page(p->page_welcome);
|
||||
SetSizerAndFit(vsizer);
|
||||
SetMinSize(GetSize());
|
||||
|
||||
p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
|
||||
p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_next(); });
|
||||
p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->EndModal(wxID_OK); });
|
||||
}
|
||||
|
||||
ConfigWizard::~ConfigWizard() {}
|
||||
|
||||
bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason;
|
||||
if (ShowModal() == wxID_OK) {
|
||||
p->apply_config(GUI::get_app_config(), preset_bundle, updater);
|
||||
BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied";
|
||||
return true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const wxString& ConfigWizard::name()
|
||||
{
|
||||
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
|
||||
#if WIN32
|
||||
static const wxString config_wizard_name = _(L("Configuration Wizard"));
|
||||
#else
|
||||
static const wxString config_wizard_name = _(L("Configuration Assistant"));
|
||||
#endif
|
||||
return config_wizard_name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
50
xs/src/slic3r/GUI/ConfigWizard.hpp
Normal file
50
xs/src/slic3r/GUI/ConfigWizard.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef slic3r_ConfigWizard_hpp_
|
||||
#define slic3r_ConfigWizard_hpp_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PresetBundle;
|
||||
class PresetUpdater;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
class ConfigWizard: public wxDialog
|
||||
{
|
||||
public:
|
||||
// Why is the Wizard run
|
||||
enum RunReason {
|
||||
RR_DATA_EMPTY, // No or empty datadir
|
||||
RR_DATA_LEGACY, // Pre-updating datadir
|
||||
RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation
|
||||
RR_USER, // User requested the Wizard from the menus
|
||||
};
|
||||
|
||||
ConfigWizard(wxWindow *parent, RunReason run_reason);
|
||||
ConfigWizard(ConfigWizard &&) = delete;
|
||||
ConfigWizard(const ConfigWizard &) = delete;
|
||||
ConfigWizard &operator=(ConfigWizard &&) = delete;
|
||||
ConfigWizard &operator=(const ConfigWizard &) = delete;
|
||||
~ConfigWizard();
|
||||
|
||||
// Run the Wizard. Return whether it was completed.
|
||||
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
|
||||
static const wxString& name();
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
||||
friend class ConfigWizardPage;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
238
xs/src/slic3r/GUI/ConfigWizard_private.hpp
Normal file
238
xs/src/slic3r/GUI/ConfigWizard_private.hpp
Normal file
|
@ -0,0 +1,238 @@
|
|||
#ifndef slic3r_ConfigWizard_private_hpp_
|
||||
#define slic3r_ConfigWizard_private_hpp_
|
||||
|
||||
#include "ConfigWizard.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/spinctrl.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "BedShapeDialog.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
enum {
|
||||
CONTENT_WIDTH = 500,
|
||||
|
||||
DIALOG_MARGIN = 15,
|
||||
INDEX_MARGIN = 40,
|
||||
BTN_SPACING = 10,
|
||||
INDENT_SPACING = 30,
|
||||
VERTICAL_SPACING = 10,
|
||||
};
|
||||
|
||||
struct PrinterPicker: wxPanel
|
||||
{
|
||||
struct Checkbox : wxCheckBox
|
||||
{
|
||||
Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) :
|
||||
wxCheckBox(parent, wxID_ANY, label),
|
||||
model(model),
|
||||
variant(variant)
|
||||
{}
|
||||
|
||||
std::string model;
|
||||
std::string variant;
|
||||
};
|
||||
|
||||
const std::string vendor_id;
|
||||
std::vector<Checkbox*> cboxes;
|
||||
unsigned variants_checked;
|
||||
|
||||
PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors);
|
||||
|
||||
void select_all(bool select);
|
||||
void on_checkbox(const Checkbox *cbox, bool checked);
|
||||
};
|
||||
|
||||
struct ConfigWizardPage: wxPanel
|
||||
{
|
||||
ConfigWizard *parent;
|
||||
const wxString shortname;
|
||||
wxBoxSizer *content;
|
||||
|
||||
ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname);
|
||||
|
||||
virtual ~ConfigWizardPage();
|
||||
|
||||
ConfigWizardPage* page_prev() const { return p_prev; }
|
||||
ConfigWizardPage* page_next() const { return p_next; }
|
||||
ConfigWizardPage* chain(ConfigWizardPage *page);
|
||||
|
||||
template<class T>
|
||||
void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10)
|
||||
{
|
||||
content->Add(thing, proportion, flag, border);
|
||||
}
|
||||
|
||||
void append_text(wxString text);
|
||||
void append_spacer(int space);
|
||||
|
||||
ConfigWizard::priv *wizard_p() const { return parent->p.get(); }
|
||||
|
||||
virtual bool Show(bool show = true);
|
||||
virtual bool Hide() { return Show(false); }
|
||||
virtual wxPanel* extra_buttons() { return nullptr; }
|
||||
virtual void on_page_set() {}
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config) {}
|
||||
|
||||
void enable_next(bool enable);
|
||||
private:
|
||||
ConfigWizardPage *p_prev;
|
||||
ConfigWizardPage *p_next;
|
||||
};
|
||||
|
||||
struct PageWelcome: ConfigWizardPage
|
||||
{
|
||||
PrinterPicker *printer_picker;
|
||||
wxPanel *others_buttons;
|
||||
wxCheckBox *cbox_reset;
|
||||
|
||||
PageWelcome(ConfigWizard *parent);
|
||||
|
||||
virtual wxPanel* extra_buttons() { return others_buttons; }
|
||||
virtual void on_page_set();
|
||||
|
||||
bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; }
|
||||
void on_variant_checked();
|
||||
};
|
||||
|
||||
struct PageUpdate: ConfigWizardPage
|
||||
{
|
||||
bool version_check;
|
||||
bool preset_update;
|
||||
|
||||
PageUpdate(ConfigWizard *parent);
|
||||
};
|
||||
|
||||
struct PageVendors: ConfigWizardPage
|
||||
{
|
||||
std::vector<PrinterPicker*> pickers;
|
||||
|
||||
PageVendors(ConfigWizard *parent);
|
||||
|
||||
virtual void on_page_set();
|
||||
|
||||
void on_vendor_pick(size_t i);
|
||||
void on_variant_checked();
|
||||
};
|
||||
|
||||
struct PageFirmware: ConfigWizardPage
|
||||
{
|
||||
const ConfigOptionDef &gcode_opt;
|
||||
wxChoice *gcode_picker;
|
||||
|
||||
PageFirmware(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageBedShape: ConfigWizardPage
|
||||
{
|
||||
BedShapePanel *shape_panel;
|
||||
|
||||
PageBedShape(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageDiameters: ConfigWizardPage
|
||||
{
|
||||
wxSpinCtrlDouble *spin_nozzle;
|
||||
wxSpinCtrlDouble *spin_filam;
|
||||
|
||||
PageDiameters(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
struct PageTemperatures: ConfigWizardPage
|
||||
{
|
||||
wxSpinCtrlDouble *spin_extr;
|
||||
wxSpinCtrlDouble *spin_bed;
|
||||
|
||||
PageTemperatures(ConfigWizard *parent);
|
||||
virtual void apply_custom_config(DynamicPrintConfig &config);
|
||||
};
|
||||
|
||||
|
||||
class ConfigWizardIndex: public wxPanel
|
||||
{
|
||||
public:
|
||||
ConfigWizardIndex(wxWindow *parent);
|
||||
|
||||
void load_items(ConfigWizardPage *firstpage);
|
||||
void set_active(ConfigWizardPage *page);
|
||||
private:
|
||||
const wxBitmap bg;
|
||||
const wxBitmap bullet_black;
|
||||
const wxBitmap bullet_blue;
|
||||
const wxBitmap bullet_white;
|
||||
int text_height;
|
||||
|
||||
std::vector<wxString> items;
|
||||
std::vector<wxString>::const_iterator item_active;
|
||||
|
||||
void on_paint(wxPaintEvent &evt);
|
||||
};
|
||||
|
||||
struct ConfigWizard::priv
|
||||
{
|
||||
ConfigWizard *q;
|
||||
ConfigWizard::RunReason run_reason;
|
||||
AppConfig appconfig_vendors;
|
||||
std::unordered_map<std::string, VendorProfile> vendors;
|
||||
std::unordered_map<std::string, std::string> vendors_rsrc;
|
||||
std::unique_ptr<DynamicPrintConfig> custom_config;
|
||||
|
||||
wxBoxSizer *topsizer = nullptr;
|
||||
wxBoxSizer *btnsizer = nullptr;
|
||||
ConfigWizardPage *page_current = nullptr;
|
||||
ConfigWizardIndex *index = nullptr;
|
||||
wxButton *btn_prev = nullptr;
|
||||
wxButton *btn_next = nullptr;
|
||||
wxButton *btn_finish = nullptr;
|
||||
wxButton *btn_cancel = nullptr;
|
||||
|
||||
PageWelcome *page_welcome = nullptr;
|
||||
PageUpdate *page_update = nullptr;
|
||||
PageVendors *page_vendors = nullptr;
|
||||
PageFirmware *page_firmware = nullptr;
|
||||
PageBedShape *page_bed = nullptr;
|
||||
PageDiameters *page_diams = nullptr;
|
||||
PageTemperatures *page_temps = nullptr;
|
||||
|
||||
priv(ConfigWizard *q) : q(q) {}
|
||||
|
||||
void load_vendors();
|
||||
void add_page(ConfigWizardPage *page);
|
||||
void index_refresh();
|
||||
void set_page(ConfigWizardPage *page);
|
||||
void layout_fit();
|
||||
void go_prev() { if (page_current != nullptr) { set_page(page_current->page_prev()); } }
|
||||
void go_next() { if (page_current != nullptr) { set_page(page_current->page_next()); } }
|
||||
void enable_next(bool enable);
|
||||
|
||||
void on_other_vendors();
|
||||
void on_custom_setup();
|
||||
|
||||
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -20,21 +20,22 @@ namespace Slic3r { namespace GUI {
|
|||
|
||||
void Field::PostInitialize(){
|
||||
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
m_Undo_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
m_Undo_to_sys_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
m_Undo_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
m_Undo_to_sys_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
if (wxMSW) {
|
||||
m_Undo_btn->SetBackgroundColour(color);
|
||||
m_Undo_to_sys_btn->SetBackgroundColour(color);
|
||||
}
|
||||
m_Undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG));
|
||||
m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); }));
|
||||
m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); }));
|
||||
|
||||
BUILD();
|
||||
}
|
||||
//set default bitmap
|
||||
wxBitmap bmp;
|
||||
bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG);
|
||||
set_undo_bitmap(&bmp);
|
||||
set_undo_to_sys_bitmap(&bmp);
|
||||
|
||||
void Field::set_nonsys_btn_icon(const std::string& icon){
|
||||
m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG));
|
||||
BUILD();
|
||||
}
|
||||
|
||||
void Field::on_kill_focus(wxEvent& event) {
|
||||
|
@ -81,12 +82,13 @@ namespace Slic3r { namespace GUI {
|
|||
return std::regex_match(string, regex_pattern);
|
||||
}
|
||||
|
||||
boost::any Field::get_value_by_opt_type(wxString& str)
|
||||
// boost::any Field::get_value_by_opt_type(wxString& str)
|
||||
void Field::get_value_by_opt_type(wxString& str)
|
||||
{
|
||||
boost::any ret_val;
|
||||
// boost::any m_value;
|
||||
switch (m_opt.type){
|
||||
case coInt:
|
||||
ret_val = wxAtoi(str);
|
||||
m_value = wxAtoi(str);
|
||||
break;
|
||||
case coPercent:
|
||||
case coPercents:
|
||||
|
@ -96,18 +98,18 @@ namespace Slic3r { namespace GUI {
|
|||
str.RemoveLast();
|
||||
double val;
|
||||
str.ToCDouble(&val);
|
||||
ret_val = val;
|
||||
m_value = val;
|
||||
break; }
|
||||
case coString:
|
||||
case coStrings:
|
||||
case coFloatOrPercent:
|
||||
ret_val = str.ToStdString();
|
||||
m_value = str.ToStdString();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
// return m_value;
|
||||
}
|
||||
|
||||
void TextCtrl::BUILD() {
|
||||
|
@ -183,12 +185,12 @@ namespace Slic3r { namespace GUI {
|
|||
window = dynamic_cast<wxWindow*>(temp);
|
||||
}
|
||||
|
||||
boost::any TextCtrl::get_value()
|
||||
boost::any& TextCtrl::get_value()
|
||||
{
|
||||
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
||||
boost::any ret_val = get_value_by_opt_type(ret_str);
|
||||
/*boost::any ret_val*/get_value_by_opt_type(ret_str);
|
||||
|
||||
return ret_val;
|
||||
return m_value;//ret_val;
|
||||
}
|
||||
|
||||
void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); }
|
||||
|
@ -216,15 +218,15 @@ void CheckBox::BUILD() {
|
|||
window = dynamic_cast<wxWindow*>(temp);
|
||||
}
|
||||
|
||||
boost::any CheckBox::get_value()
|
||||
boost::any& CheckBox::get_value()
|
||||
{
|
||||
boost::any ret_val;
|
||||
// boost::any m_value;
|
||||
bool value = dynamic_cast<wxCheckBox*>(window)->GetValue();
|
||||
if (m_opt.type == coBool)
|
||||
ret_val = static_cast<bool>(value);
|
||||
m_value = static_cast<bool>(value);
|
||||
else
|
||||
ret_val = static_cast<unsigned char>(value);
|
||||
return ret_val;
|
||||
m_value = static_cast<unsigned char>(value);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
int undef_spin_val = -9999; //! Probably, It's not necessary
|
||||
|
@ -424,7 +426,33 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||
break;
|
||||
}
|
||||
case coEnum:{
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(boost::any_cast<int>(value));
|
||||
int val = boost::any_cast<int>(value);
|
||||
if (m_opt_id.compare("external_fill_pattern") == 0)
|
||||
{
|
||||
if (!m_opt.enum_values.empty()){
|
||||
std::string key;
|
||||
t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
for (auto it : map_names) {
|
||||
if (val == it.second) {
|
||||
key = it.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
for (auto el : m_opt.enum_values)
|
||||
{
|
||||
if (el.compare(key) == 0)
|
||||
break;
|
||||
++idx;
|
||||
}
|
||||
|
||||
val = idx == m_opt.enum_values.size() ? 0 : idx;
|
||||
}
|
||||
else
|
||||
val = 0;
|
||||
}
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -454,16 +482,16 @@ void Choice::set_values(const std::vector<std::string>& values)
|
|||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any Choice::get_value()
|
||||
boost::any& Choice::get_value()
|
||||
{
|
||||
boost::any ret_val;
|
||||
// boost::any m_value;
|
||||
wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();
|
||||
|
||||
if (m_opt_id == "support")
|
||||
return ret_str;
|
||||
return m_value = boost::any(ret_str);//ret_str;
|
||||
|
||||
if (m_opt.type != coEnum)
|
||||
ret_val = get_value_by_opt_type(ret_str);
|
||||
/*m_value = */get_value_by_opt_type(ret_str);
|
||||
else
|
||||
{
|
||||
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
||||
|
@ -474,22 +502,22 @@ boost::any Choice::get_value()
|
|||
t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
int value = map_names.at(key);
|
||||
|
||||
ret_val = static_cast<InfillPattern>(value);
|
||||
m_value = static_cast<InfillPattern>(value);
|
||||
}
|
||||
else
|
||||
ret_val = static_cast<InfillPattern>(0);
|
||||
m_value = static_cast<InfillPattern>(0);
|
||||
}
|
||||
if (m_opt_id.compare("fill_pattern") == 0)
|
||||
ret_val = static_cast<InfillPattern>(ret_enum);
|
||||
m_value = static_cast<InfillPattern>(ret_enum);
|
||||
else if (m_opt_id.compare("gcode_flavor") == 0)
|
||||
ret_val = static_cast<GCodeFlavor>(ret_enum);
|
||||
m_value = static_cast<GCodeFlavor>(ret_enum);
|
||||
else if (m_opt_id.compare("support_material_pattern") == 0)
|
||||
ret_val = static_cast<SupportMaterialPattern>(ret_enum);
|
||||
m_value = static_cast<SupportMaterialPattern>(ret_enum);
|
||||
else if (m_opt_id.compare("seam_position") == 0)
|
||||
ret_val = static_cast<SeamPosition>(ret_enum);
|
||||
m_value = static_cast<SeamPosition>(ret_enum);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void ColourPicker::BUILD()
|
||||
|
@ -509,14 +537,14 @@ void ColourPicker::BUILD()
|
|||
temp->SetToolTip(get_tooltip_text(clr));
|
||||
}
|
||||
|
||||
boost::any ColourPicker::get_value(){
|
||||
boost::any ret_val;
|
||||
boost::any& ColourPicker::get_value(){
|
||||
// boost::any m_value;
|
||||
|
||||
auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour();
|
||||
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
|
||||
ret_val = clr_str.ToStdString();
|
||||
m_value = clr_str.ToStdString();
|
||||
|
||||
return ret_val;
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void PointCtrl::BUILD()
|
||||
|
@ -580,7 +608,7 @@ void PointCtrl::set_value(const boost::any& value, bool change_event)
|
|||
set_value(pt, change_event);
|
||||
}
|
||||
|
||||
boost::any PointCtrl::get_value()
|
||||
boost::any& PointCtrl::get_value()
|
||||
{
|
||||
Pointf ret_point;
|
||||
double val;
|
||||
|
@ -588,7 +616,7 @@ boost::any PointCtrl::get_value()
|
|||
ret_point.x = val;
|
||||
y_textctrl->GetValue().ToDouble(&val);
|
||||
ret_point.y = val;
|
||||
return ret_point;
|
||||
return m_value = ret_point;
|
||||
}
|
||||
|
||||
} // GUI
|
||||
|
|
|
@ -36,6 +36,24 @@ using t_back_to_init = std::function<void(const std::string&)>;
|
|||
|
||||
wxString double_to_string(double const value);
|
||||
|
||||
class MyButton : public wxButton
|
||||
{
|
||||
public:
|
||||
MyButton() {}
|
||||
MyButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize, long style = 0,
|
||||
const wxValidator& validator = wxDefaultValidator,
|
||||
const wxString& name = wxTextCtrlNameStr)
|
||||
{
|
||||
this->Create(parent, id, label, pos, size, style, validator, name);
|
||||
}
|
||||
|
||||
// overridden from wxWindow base class
|
||||
virtual bool
|
||||
AcceptsFocusFromKeyboard() const { return false; }
|
||||
};
|
||||
|
||||
class Field {
|
||||
protected:
|
||||
// factory function to defer and enforce creation of derived type.
|
||||
|
@ -85,22 +103,18 @@ public:
|
|||
|
||||
/// Gets a boost::any representing this control.
|
||||
/// subclasses should overload with a specific version
|
||||
virtual boost::any get_value() = 0;
|
||||
virtual boost::any& get_value() = 0;
|
||||
|
||||
virtual void enable() = 0;
|
||||
virtual void disable() = 0;
|
||||
|
||||
wxStaticText* m_Label = nullptr;
|
||||
wxButton* m_Undo_btn = nullptr;
|
||||
wxButton* m_Undo_to_sys_btn = nullptr;
|
||||
|
||||
/// Fires the enable or disable function, based on the input.
|
||||
/// Fires the enable or disable function, based on the input.
|
||||
inline void toggle(bool en) { en ? enable() : disable(); }
|
||||
|
||||
virtual wxString get_tooltip_text(const wxString& default_string);
|
||||
|
||||
// set icon to "UndoToSystemValue" button according to an inheritance of preset
|
||||
void set_nonsys_btn_icon(const std::string& icon);
|
||||
// void set_nonsys_btn_icon(const wxBitmap& icon);
|
||||
|
||||
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {};
|
||||
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {};
|
||||
|
@ -110,7 +124,8 @@ public:
|
|||
virtual wxWindow* getWindow() { return nullptr; }
|
||||
|
||||
bool is_matched(const std::string& string, const std::string& pattern);
|
||||
boost::any get_value_by_opt_type(wxString& str);
|
||||
// boost::any get_value_by_opt_type(wxString& str);
|
||||
void get_value_by_opt_type(wxString& str);
|
||||
|
||||
/// Factory method for generating new derived classes.
|
||||
template<class T>
|
||||
|
@ -120,6 +135,78 @@ public:
|
|||
p->PostInitialize();
|
||||
return std::move(p); //!p;
|
||||
}
|
||||
|
||||
bool set_undo_bitmap(const wxBitmap *bmp) {
|
||||
if (m_undo_bitmap != bmp) {
|
||||
m_undo_bitmap = bmp;
|
||||
m_Undo_btn->SetBitmap(*bmp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_undo_to_sys_bitmap(const wxBitmap *bmp) {
|
||||
if (m_undo_to_sys_bitmap != bmp) {
|
||||
m_undo_to_sys_bitmap = bmp;
|
||||
m_Undo_to_sys_btn->SetBitmap(*bmp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_label_colour(const wxColour *clr) {
|
||||
if (m_Label == nullptr) return false;
|
||||
if (m_label_color != clr) {
|
||||
m_label_color = clr;
|
||||
m_Label->SetForegroundColour(*clr);
|
||||
m_Label->Refresh(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_label_colour_force(const wxColour *clr) {
|
||||
if (m_Label == nullptr) return false;
|
||||
m_Label->SetForegroundColour(*clr);
|
||||
m_Label->Refresh(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_undo_tooltip(const wxString *tip) {
|
||||
if (m_undo_tooltip != tip) {
|
||||
m_undo_tooltip = tip;
|
||||
m_Undo_btn->SetToolTip(*tip);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set_undo_to_sys_tooltip(const wxString *tip) {
|
||||
if (m_undo_to_sys_tooltip != tip) {
|
||||
m_undo_to_sys_tooltip = tip;
|
||||
m_Undo_to_sys_btn->SetToolTip(*tip);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
MyButton* m_Undo_btn = nullptr;
|
||||
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
|
||||
const wxBitmap* m_undo_bitmap = nullptr;
|
||||
const wxString* m_undo_tooltip = nullptr;
|
||||
MyButton* m_Undo_to_sys_btn = nullptr;
|
||||
// Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
|
||||
const wxBitmap* m_undo_to_sys_bitmap = nullptr;
|
||||
const wxString* m_undo_to_sys_tooltip = nullptr;
|
||||
|
||||
wxStaticText* m_Label = nullptr;
|
||||
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
|
||||
const wxColour* m_label_color = nullptr;
|
||||
|
||||
// current value
|
||||
boost::any m_value;
|
||||
|
||||
friend class OptionsGroup;
|
||||
};
|
||||
|
||||
/// Convenience function, accepts a const reference to t_field and checks to see whether
|
||||
|
@ -153,7 +240,7 @@ public:
|
|||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any get_value() override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
virtual void enable();
|
||||
virtual void disable();
|
||||
|
@ -180,7 +267,7 @@ public:
|
|||
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
boost::any get_value() override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void enable() override { dynamic_cast<wxCheckBox*>(window)->Enable(); }
|
||||
void disable() override { dynamic_cast<wxCheckBox*>(window)->Disable(); }
|
||||
|
@ -210,8 +297,9 @@ public:
|
|||
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
boost::any get_value() override {
|
||||
return boost::any(tmp_value);
|
||||
boost::any& get_value() override {
|
||||
// return boost::any(tmp_value);
|
||||
return m_value = tmp_value;
|
||||
}
|
||||
|
||||
void enable() override { dynamic_cast<wxSpinCtrl*>(window)->Enable(); }
|
||||
|
@ -233,7 +321,7 @@ public:
|
|||
void set_value(const std::string& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
void set_values(const std::vector<std::string> &values);
|
||||
boost::any get_value() override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); };
|
||||
void disable() override{ dynamic_cast<wxComboBox*>(window)->Disable(); };
|
||||
|
@ -261,7 +349,7 @@ public:
|
|||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any get_value() override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
|
||||
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
|
||||
|
@ -283,7 +371,7 @@ public:
|
|||
|
||||
void set_value(const Pointf& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
boost::any get_value() override;
|
||||
boost::any& get_value() override;
|
||||
|
||||
void enable() override {
|
||||
x_textctrl->Enable();
|
||||
|
|
|
@ -39,16 +39,25 @@
|
|||
#include <wx/sizer.h>
|
||||
#include <wx/combo.h>
|
||||
#include <wx/window.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/settings.h>
|
||||
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include "Tab.hpp"
|
||||
#include "TabIface.hpp"
|
||||
#include "AboutDialog.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "ConfigSnapshotDialog.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "Preferences.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "UpdateDialogs.hpp"
|
||||
|
||||
#include "../Utils/PresetUpdater.hpp"
|
||||
#include "../Config/Snapshot.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
|
@ -177,8 +186,10 @@ wxFrame *g_wxMainFrame = nullptr;
|
|||
wxNotebook *g_wxTabPanel = nullptr;
|
||||
AppConfig *g_AppConfig = nullptr;
|
||||
PresetBundle *g_PresetBundle= nullptr;
|
||||
PresetUpdater *g_PresetUpdater = nullptr;
|
||||
wxColour g_color_label_modified;
|
||||
wxColour g_color_label_sys;
|
||||
wxColour g_color_label_default;
|
||||
|
||||
std::vector<Tab *> g_tabs_list;
|
||||
|
||||
|
@ -198,6 +209,22 @@ static void init_label_colours()
|
|||
g_color_label_modified = wxColour(253, 111, 40);
|
||||
g_color_label_sys = wxColour(115, 220, 103);
|
||||
}
|
||||
g_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
}
|
||||
|
||||
void update_label_colours_from_appconfig()
|
||||
{
|
||||
if (g_AppConfig->has("label_clr_sys")){
|
||||
auto str = g_AppConfig->get("label_clr_sys");
|
||||
if (str != "")
|
||||
g_color_label_sys = wxColour(str);
|
||||
}
|
||||
|
||||
if (g_AppConfig->has("label_clr_modified")){
|
||||
auto str = g_AppConfig->get("label_clr_modified");
|
||||
if (str != "")
|
||||
g_color_label_modified = wxColour(str);
|
||||
}
|
||||
}
|
||||
|
||||
void set_wxapp(wxApp *app)
|
||||
|
@ -226,6 +253,11 @@ void set_preset_bundle(PresetBundle *preset_bundle)
|
|||
g_PresetBundle = preset_bundle;
|
||||
}
|
||||
|
||||
void set_preset_updater(PresetUpdater *updater)
|
||||
{
|
||||
g_PresetUpdater = updater;
|
||||
}
|
||||
|
||||
std::vector<Tab *>& get_tabs_list()
|
||||
{
|
||||
return g_tabs_list;
|
||||
|
@ -349,26 +381,143 @@ void get_installed_languages(wxArrayString & names,
|
|||
}
|
||||
}
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu, int event_language_change)
|
||||
enum ConfigMenuIDs {
|
||||
ConfigMenuWizard,
|
||||
ConfigMenuSnapshots,
|
||||
ConfigMenuTakeSnapshot,
|
||||
ConfigMenuUpdate,
|
||||
ConfigMenuPreferences,
|
||||
ConfigMenuLanguage,
|
||||
ConfigMenuCnt,
|
||||
};
|
||||
|
||||
void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change)
|
||||
{
|
||||
//#if 0
|
||||
auto local_menu = new wxMenu();
|
||||
local_menu->Append(wxWindow::NewControlId(1), _(L("Change Application Language")));
|
||||
local_menu->Bind(wxEVT_MENU, [event_language_change](wxEvent&){
|
||||
wxArrayString names;
|
||||
wxArrayLong identifiers;
|
||||
get_installed_languages(names, identifiers);
|
||||
if (select_language(names, identifiers)){
|
||||
save_language();
|
||||
show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
|
||||
if (event_language_change > 0) {
|
||||
wxCommandEvent event(event_language_change);
|
||||
g_wxApp->ProcessEvent(event);
|
||||
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
|
||||
|
||||
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), ConfigWizard::name());
|
||||
// Cmd+, is standard on OS X - what about other operating systems?
|
||||
local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + "\u2026", config_wizard_tooltip);
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots\u2026")), _(L("Inspect / activate configuration snapshots")));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot")));
|
||||
local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences\u2026\tCtrl+,")), _(L("Application preferences")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
|
||||
local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){
|
||||
switch (event.GetId() - config_id_base) {
|
||||
case ConfigMenuWizard:
|
||||
config_wizard(ConfigWizard::RR_USER);
|
||||
break;
|
||||
case ConfigMenuTakeSnapshot:
|
||||
// Take a configuration snapshot.
|
||||
if (check_unsaved_changes()) {
|
||||
wxTextEntryDialog dlg(nullptr, _(L("Taking configuration snapshot")), _(L("Snapshot name")));
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
g_AppConfig->set("on_snapshot",
|
||||
Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot(
|
||||
*g_AppConfig, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()).id);
|
||||
}
|
||||
break;
|
||||
case ConfigMenuSnapshots:
|
||||
if (check_unsaved_changes()) {
|
||||
std::string on_snapshot;
|
||||
if (Config::SnapshotDB::singleton().is_on_snapshot(*g_AppConfig))
|
||||
on_snapshot = g_AppConfig->get("on_snapshot");
|
||||
ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot);
|
||||
dlg.ShowModal();
|
||||
if (! dlg.snapshot_to_activate().empty()) {
|
||||
if (! Config::SnapshotDB::singleton().is_on_snapshot(*g_AppConfig))
|
||||
Config::SnapshotDB::singleton().take_snapshot(*g_AppConfig, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK);
|
||||
g_AppConfig->set("on_snapshot",
|
||||
Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *g_AppConfig).id);
|
||||
g_PresetBundle->load_presets(*g_AppConfig);
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
for (Tab *tab : g_tabs_list)
|
||||
tab->load_current_preset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ConfigMenuPreferences:
|
||||
{
|
||||
PreferencesDialog dlg(g_wxMainFrame, event_preferences_changed);
|
||||
dlg.ShowModal();
|
||||
break;
|
||||
}
|
||||
case ConfigMenuLanguage:
|
||||
{
|
||||
wxArrayString names;
|
||||
wxArrayLong identifiers;
|
||||
get_installed_languages(names, identifiers);
|
||||
if (select_language(names, identifiers)) {
|
||||
save_language();
|
||||
show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
|
||||
if (event_language_change > 0) {
|
||||
wxCommandEvent event(event_language_change);
|
||||
g_wxApp->ProcessEvent(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
menu->Append(local_menu, _(L("&Localization")));
|
||||
//#endif
|
||||
menu->Append(local_menu, _(L("&Configuration")));
|
||||
}
|
||||
|
||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
// to notify the user whether he is aware that some preset changes will be lost.
|
||||
bool check_unsaved_changes()
|
||||
{
|
||||
std::string dirty;
|
||||
for (Tab *tab : g_tabs_list)
|
||||
if (tab->current_preset_is_dirty())
|
||||
if (dirty.empty())
|
||||
dirty = tab->name();
|
||||
else
|
||||
dirty += std::string(", ") + tab->name();
|
||||
if (dirty.empty())
|
||||
// No changes, the application may close or reload presets.
|
||||
return true;
|
||||
// Ask the user.
|
||||
auto dialog = new wxMessageDialog(g_wxMainFrame,
|
||||
_(L("You have unsaved changes ")) + dirty + _(L(". Discard changes and continue anyway?")),
|
||||
_(L("Unsaved Presets")),
|
||||
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
||||
return dialog->ShowModal() == wxID_YES;
|
||||
}
|
||||
|
||||
bool config_wizard_startup(bool app_config_exists)
|
||||
{
|
||||
if (! app_config_exists || g_PresetBundle->has_defauls_only()) {
|
||||
config_wizard(ConfigWizard::RR_DATA_EMPTY);
|
||||
return true;
|
||||
} else if (g_AppConfig->legacy_datadir()) {
|
||||
// Looks like user has legacy pre-vendorbundle data directory,
|
||||
// explain what this is and run the wizard
|
||||
|
||||
MsgDataLegacy dlg;
|
||||
dlg.ShowModal();
|
||||
|
||||
config_wizard(ConfigWizard::RR_DATA_LEGACY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void config_wizard(int reason)
|
||||
{
|
||||
// Exit wizard if there are unsaved changes and the user cancels the action.
|
||||
if (! check_unsaved_changes())
|
||||
return;
|
||||
|
||||
ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
|
||||
wizard.run(g_PresetBundle, g_PresetUpdater);
|
||||
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
for (Tab *tab : g_tabs_list)
|
||||
tab->load_current_preset();
|
||||
}
|
||||
|
||||
void open_preferences_dialog(int event_preferences)
|
||||
|
@ -379,6 +528,7 @@ void open_preferences_dialog(int event_preferences)
|
|||
|
||||
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
|
||||
{
|
||||
update_label_colours_from_appconfig();
|
||||
add_created_tab(new TabPrint (g_wxTabPanel, no_controller));
|
||||
add_created_tab(new TabFilament (g_wxTabPanel, no_controller));
|
||||
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller));
|
||||
|
@ -519,35 +669,65 @@ void add_created_tab(Tab* panel)
|
|||
g_wxTabPanel->AddPage(panel, panel->title());
|
||||
}
|
||||
|
||||
void show_error(wxWindow* parent, const wxString& message){
|
||||
auto msg_wingow = new wxMessageDialog(parent, message, _(L("Error")), wxOK | wxICON_ERROR);
|
||||
msg_wingow->ShowModal();
|
||||
void show_error(wxWindow* parent, const wxString& message) {
|
||||
ErrorDialog msg(parent, message);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
||||
void show_error_id(int id, const std::string& message) {
|
||||
auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr;
|
||||
show_error(parent, message);
|
||||
}
|
||||
|
||||
void show_info(wxWindow* parent, const wxString& message, const wxString& title){
|
||||
auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
|
||||
msg_wingow->ShowModal();
|
||||
wxMessageDialog msg_wingow(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
|
||||
msg_wingow.ShowModal();
|
||||
}
|
||||
|
||||
void warning_catcher(wxWindow* parent, const wxString& message){
|
||||
if (message == _(L("GLUquadricObjPtr | Attempt to free unreferenced scalar")) )
|
||||
return;
|
||||
auto msg = new wxMessageDialog(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
|
||||
msg->ShowModal();
|
||||
wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
||||
wxApp* get_app(){
|
||||
return g_wxApp;
|
||||
}
|
||||
|
||||
const wxColour& get_modified_label_clr() {
|
||||
PresetBundle* get_preset_bundle()
|
||||
{
|
||||
return g_PresetBundle;
|
||||
}
|
||||
|
||||
const wxColour& get_label_clr_modified() {
|
||||
return g_color_label_modified;
|
||||
}
|
||||
|
||||
const wxColour& get_sys_label_clr() {
|
||||
const wxColour& get_label_clr_sys() {
|
||||
return g_color_label_sys;
|
||||
}
|
||||
|
||||
void set_label_clr_modified(const wxColour& clr) {
|
||||
g_color_label_modified = clr;
|
||||
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue());
|
||||
std::string str = clr_str.ToStdString();
|
||||
g_AppConfig->set("label_clr_modified", str);
|
||||
g_AppConfig->save();
|
||||
}
|
||||
|
||||
void set_label_clr_sys(const wxColour& clr) {
|
||||
g_color_label_sys = clr;
|
||||
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue());
|
||||
std::string str = clr_str.ToStdString();
|
||||
g_AppConfig->set("label_clr_sys", str);
|
||||
g_AppConfig->save();
|
||||
}
|
||||
|
||||
const wxColour& get_label_clr_default() {
|
||||
return g_color_label_default;
|
||||
}
|
||||
|
||||
unsigned get_colour_approx_luma(const wxColour &colour)
|
||||
{
|
||||
double r = colour.Red();
|
||||
|
@ -634,8 +814,8 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
|
|||
{
|
||||
DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config;
|
||||
m_optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
|
||||
const wxArrayInt& ar = preset_sizer->GetColWidths();
|
||||
m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front();
|
||||
// const wxArrayInt& ar = preset_sizer->GetColWidths();
|
||||
// m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front(); // doesn't work
|
||||
m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
|
||||
TabPrint* tab_print = nullptr;
|
||||
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
|
||||
|
@ -689,10 +869,9 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
|
|||
tab_print->update_dirty();
|
||||
};
|
||||
|
||||
const int width = 250;
|
||||
Option option = m_optgroup->get_option("fill_density");
|
||||
option.opt.sidetext = "";
|
||||
option.opt.width = width;
|
||||
option.opt.full_width = true;
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
ConfigOptionDef def;
|
||||
|
@ -711,7 +890,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
|
|||
"Everywhere";
|
||||
def.default_value = new ConfigOptionStrings { selection };
|
||||
option = Option(def, "support");
|
||||
option.opt.width = width;
|
||||
option.opt.full_width = true;
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
m_brim_width = config->opt_float("brim_width");
|
||||
|
@ -750,7 +929,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
|
|||
|
||||
|
||||
|
||||
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxBottom, 1);
|
||||
sizer->Add(m_optgroup->sizer, 1, wxEXPAND | wxBOTTOM, 2);
|
||||
}
|
||||
|
||||
ConfigOptionsGroup* get_optgroup()
|
||||
|
@ -769,6 +948,7 @@ wxWindow* export_option_creator(wxWindow* parent)
|
|||
wxPanel* panel = new wxPanel(parent, -1);
|
||||
wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config"));
|
||||
cbox->SetValue(true);
|
||||
sizer->AddSpacer(5);
|
||||
sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
panel->SetSizer(sizer);
|
||||
|
@ -810,6 +990,11 @@ int get_export_option(wxFileDialog* dlg)
|
|||
|
||||
}
|
||||
|
||||
void about()
|
||||
{
|
||||
AboutDialog dlg;
|
||||
dlg.ShowModal();
|
||||
dlg.Destroy();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
} }
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Slic3r {
|
|||
class PresetBundle;
|
||||
class PresetCollection;
|
||||
class AppConfig;
|
||||
class PresetUpdater;
|
||||
class DynamicPrintConfig;
|
||||
class TabIface;
|
||||
|
||||
|
@ -77,18 +78,35 @@ void set_main_frame(wxFrame *main_frame);
|
|||
void set_tab_panel(wxNotebook *tab_panel);
|
||||
void set_app_config(AppConfig *app_config);
|
||||
void set_preset_bundle(PresetBundle *preset_bundle);
|
||||
void set_preset_updater(PresetUpdater *updater);
|
||||
|
||||
AppConfig* get_app_config();
|
||||
wxApp* get_app();
|
||||
PresetBundle* get_preset_bundle();
|
||||
|
||||
const wxColour& get_modified_label_clr();
|
||||
const wxColour& get_sys_label_clr();
|
||||
const wxColour& get_label_clr_modified();
|
||||
const wxColour& get_label_clr_sys();
|
||||
const wxColour& get_label_clr_default();
|
||||
unsigned get_colour_approx_luma(const wxColour &colour);
|
||||
void set_label_clr_modified(const wxColour& clr);
|
||||
void set_label_clr_sys(const wxColour& clr);
|
||||
|
||||
void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
||||
extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
|
||||
|
||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
// to notify the user whether he is aware that some preset changes will be lost.
|
||||
extern bool check_unsaved_changes();
|
||||
|
||||
// Checks if configuration wizard needs to run, calls config_wizard if so.
|
||||
// Returns whether the Wizard ran.
|
||||
extern bool config_wizard_startup(bool app_config_exists);
|
||||
|
||||
// Opens the configuration wizard, returns true if wizard is finished & accepted.
|
||||
// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl.
|
||||
extern void config_wizard(int run_reason);
|
||||
|
||||
// Create "Preferences" dialog after selecting menu "Preferences" in Perl part
|
||||
void open_preferences_dialog(int event_preferences);
|
||||
extern void open_preferences_dialog(int event_preferences);
|
||||
|
||||
// Create a new preset tab (print, filament and printer),
|
||||
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed);
|
||||
|
@ -100,6 +118,7 @@ void add_created_tab(Tab* panel);
|
|||
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
|
||||
|
||||
void show_error(wxWindow* parent, const wxString& message);
|
||||
void show_error_id(int id, const std::string& message); // For Perl
|
||||
void show_info(wxWindow* parent, const wxString& message, const wxString& title);
|
||||
void warning_catcher(wxWindow* parent, const wxString& message);
|
||||
|
||||
|
@ -138,7 +157,11 @@ wxButton* get_wiping_dialog_button();
|
|||
|
||||
void add_export_option(wxFileDialog* dlg, const std::string& format);
|
||||
int get_export_option(wxFileDialog* dlg);
|
||||
}
|
||||
}
|
||||
|
||||
// Display an About dialog
|
||||
void about();
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
87
xs/src/slic3r/GUI/MsgDialog.cpp
Normal file
87
xs/src/slic3r/GUI/MsgDialog.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "MsgDialog.hpp"
|
||||
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/scrolwin.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) :
|
||||
MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id)
|
||||
{}
|
||||
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) :
|
||||
wxDialog(parent, wxID_ANY, title),
|
||||
boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)),
|
||||
content_sizer(new wxBoxSizer(wxVERTICAL)),
|
||||
btn_sizer(new wxBoxSizer(wxHORIZONTAL))
|
||||
{
|
||||
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
|
||||
|
||||
auto *topsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto *rightsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto *headtext = new wxStaticText(this, wxID_ANY, headline);
|
||||
headtext->SetFont(boldfont);
|
||||
headtext->Wrap(CONTENT_WIDTH);
|
||||
rightsizer->Add(headtext);
|
||||
rightsizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
rightsizer->Add(content_sizer, 1, wxEXPAND);
|
||||
|
||||
if (button_id != wxID_NONE) {
|
||||
auto *button = new wxButton(this, button_id);
|
||||
button->SetFocus();
|
||||
btn_sizer->Add(button);
|
||||
}
|
||||
|
||||
rightsizer->Add(btn_sizer, 0, wxALIGN_CENTRE_HORIZONTAL);
|
||||
|
||||
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(bitmap));
|
||||
|
||||
topsizer->Add(logo, 0, wxALL, BORDER);
|
||||
topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER);
|
||||
|
||||
SetSizerAndFit(topsizer);
|
||||
}
|
||||
|
||||
MsgDialog::~MsgDialog() {}
|
||||
|
||||
|
||||
// ErrorDialog
|
||||
|
||||
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
|
||||
MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG))
|
||||
{
|
||||
auto *panel = new wxScrolledWindow(this);
|
||||
auto *p_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
panel->SetSizer(p_sizer);
|
||||
|
||||
auto *text = new wxStaticText(panel, wxID_ANY, msg);
|
||||
text->Wrap(CONTENT_WIDTH);
|
||||
p_sizer->Add(text, 1, wxEXPAND);
|
||||
|
||||
panel->SetMinSize(wxSize(CONTENT_WIDTH, CONTENT_HEIGHT));
|
||||
panel->SetScrollRate(0, 5);
|
||||
|
||||
content_sizer->Add(panel, 1, wxEXPAND);
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
ErrorDialog::~ErrorDialog() {}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
65
xs/src/slic3r/GUI/MsgDialog.hpp
Normal file
65
xs/src/slic3r/GUI/MsgDialog.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef slic3r_MsgDialog_hpp_
|
||||
#define slic3r_MsgDialog_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/bitmap.h>
|
||||
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxCheckBox;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
// A message / query dialog with a bitmap on the left and any content on the right
|
||||
// with buttons underneath.
|
||||
struct MsgDialog : wxDialog
|
||||
{
|
||||
MsgDialog(MsgDialog &&) = delete;
|
||||
MsgDialog(const MsgDialog &) = delete;
|
||||
MsgDialog &operator=(MsgDialog &&) = delete;
|
||||
MsgDialog &operator=(const MsgDialog &) = delete;
|
||||
virtual ~MsgDialog();
|
||||
|
||||
protected:
|
||||
enum {
|
||||
CONTENT_WIDTH = 500,
|
||||
CONTENT_HEIGHT = 300,
|
||||
BORDER = 30,
|
||||
VERT_SPACING = 15,
|
||||
HORIZ_SPACING = 5,
|
||||
};
|
||||
|
||||
// button_id is an id of a button that can be added by default, use wxID_NONE to disable
|
||||
MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id = wxID_OK);
|
||||
MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id = wxID_OK);
|
||||
|
||||
wxFont boldfont;
|
||||
wxBoxSizer *content_sizer;
|
||||
wxBoxSizer *btn_sizer;
|
||||
};
|
||||
|
||||
|
||||
// Generic error dialog, used for displaying exceptions
|
||||
struct ErrorDialog : MsgDialog
|
||||
{
|
||||
ErrorDialog(wxWindow *parent, const wxString &msg);
|
||||
ErrorDialog(ErrorDialog &&) = delete;
|
||||
ErrorDialog(const ErrorDialog &) = delete;
|
||||
ErrorDialog &operator=(ErrorDialog &&) = delete;
|
||||
ErrorDialog &operator=(const ErrorDialog &) = delete;
|
||||
virtual ~ErrorDialog();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -22,13 +22,13 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
|
|||
// is the normal type.
|
||||
if (opt.gui_type.compare("select") == 0) {
|
||||
} else if (opt.gui_type.compare("select_open") == 0) {
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
|
||||
} else if (opt.gui_type.compare("color") == 0) {
|
||||
m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(parent(), opt, id)));
|
||||
} else if (opt.gui_type.compare("f_enum_open") == 0 ||
|
||||
opt.gui_type.compare("i_enum_open") == 0 ||
|
||||
opt.gui_type.compare("i_enum_closed") == 0) {
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
|
||||
} else if (opt.gui_type.compare("slider") == 0) {
|
||||
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
|
||||
} else {
|
||||
|
@ -40,21 +40,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
|
|||
case coPercents:
|
||||
case coString:
|
||||
case coStrings:
|
||||
m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
|
||||
break;
|
||||
case coBool:
|
||||
case coBools:
|
||||
m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(parent(), opt, id)));
|
||||
break;
|
||||
case coInt:
|
||||
case coInts:
|
||||
m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(parent(), opt, id)));
|
||||
break;
|
||||
case coEnum:
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
|
||||
break;
|
||||
case coPoints:
|
||||
m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(m_parent, opt, id)));
|
||||
m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(parent(), opt, id)));
|
||||
break;
|
||||
case coNone: break;
|
||||
default:
|
||||
|
@ -90,8 +90,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
|
|||
field->m_Undo_btn->Hide();
|
||||
field->m_Undo_to_sys_btn->Hide();
|
||||
}
|
||||
if (nonsys_btn_icon != nullptr)
|
||||
field->set_nonsys_btn_icon(nonsys_btn_icon());
|
||||
// if (nonsys_btn_icon != nullptr)
|
||||
// field->set_nonsys_btn_icon(*nonsys_btn_icon);
|
||||
|
||||
// assign function objects for callbacks, etc.
|
||||
return field;
|
||||
|
@ -137,11 +137,11 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
|
|||
// Build a label if we have it
|
||||
wxStaticText* label=nullptr;
|
||||
if (label_width != 0) {
|
||||
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"),
|
||||
wxDefaultPosition, wxSize(label_width, -1));
|
||||
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "),
|
||||
wxDefaultPosition, staticbox ? wxSize(label_width, -1) : wxDefaultSize);
|
||||
label->SetFont(label_font);
|
||||
label->Wrap(label_width); // avoid a Linux/GTK bug
|
||||
grid_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL,0);
|
||||
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT) | wxALIGN_CENTER_VERTICAL, 0);
|
||||
if (line.label_tooltip.compare("") != 0)
|
||||
label->SetToolTip(line.label_tooltip);
|
||||
}
|
||||
|
@ -149,7 +149,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
|
|||
// If there's a widget, build it and add the result to the sizer.
|
||||
if (line.widget != nullptr) {
|
||||
auto wgt = line.widget(parent());
|
||||
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
|
||||
// If widget doesn't have label, don't use border
|
||||
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5);
|
||||
if (colored_Label != nullptr) *colored_Label = label;
|
||||
return;
|
||||
}
|
||||
|
@ -165,7 +166,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
|
|||
sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
if (is_window_field(field))
|
||||
sizer->Add(field->getWindow(), 0, (option.opt.full_width ? wxEXPAND : 0) |
|
||||
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) |
|
||||
wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, wxOSX ? 0 : 2);
|
||||
if (is_sizer_field(field))
|
||||
sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
@ -467,10 +468,10 @@ Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int op
|
|||
return opt_id.empty() ? nullptr : get_field(opt_id);
|
||||
}
|
||||
|
||||
void ogStaticText::SetText(const wxString& value)
|
||||
void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
|
||||
{
|
||||
SetLabel(value);
|
||||
Wrap(400);
|
||||
if (wrap) Wrap(400);
|
||||
GetParent()->Layout();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef slic3r_OptionsGroup_hpp_
|
||||
#define slic3r_OptionsGroup_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/settings.h>
|
||||
|
@ -86,12 +89,18 @@ public:
|
|||
wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
|
||||
wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) };
|
||||
|
||||
std::function<std::string()> nonsys_btn_icon{ nullptr };
|
||||
// std::function<const wxBitmap&()> nonsys_btn_icon{ nullptr };
|
||||
|
||||
/// Returns a copy of the pointer of the parent wxWindow.
|
||||
/// Accessor function is because users are not allowed to change the parent
|
||||
/// but defining it as const means a lot of const_casts to deal with wx functions.
|
||||
inline wxWindow* parent() const { return m_parent; }
|
||||
inline wxWindow* parent() const {
|
||||
#ifdef __WXGTK__
|
||||
return m_panel;
|
||||
#else
|
||||
return m_parent;
|
||||
#endif /* __WXGTK__ */
|
||||
}
|
||||
|
||||
void append_line(const Line& line, wxStaticText** colored_Label = nullptr);
|
||||
Line create_single_option_line(const Option& option) const;
|
||||
|
@ -127,8 +136,15 @@ public:
|
|||
m_grid_sizer = new wxFlexGridSizer(0, num_columns, 0,0);
|
||||
static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxHORIZONTAL);
|
||||
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0);
|
||||
|
||||
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0: 5);
|
||||
#ifdef __WXGTK__
|
||||
m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
m_panel->SetSizer(m_grid_sizer);
|
||||
m_panel->Layout();
|
||||
sizer->Fit(m_panel);
|
||||
sizer->Add(m_panel, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
|
||||
#else
|
||||
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
|
||||
#endif /* __WXGTK__ */
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -144,6 +160,13 @@ protected:
|
|||
// "true" if option is created in preset tabs
|
||||
bool m_is_tab_opt{ false };
|
||||
|
||||
// This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox
|
||||
// Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel
|
||||
// inside it before you insert the other controls.
|
||||
#ifdef __WXGTK__
|
||||
wxPanel* m_panel {nullptr};
|
||||
#endif /* __WXGTK__ */
|
||||
|
||||
/// Generate a wxSizer or wxWindow from a configuration option
|
||||
/// Precondition: opt resolves to a known ConfigOption
|
||||
/// Postcondition: fields contains a wx gui object.
|
||||
|
@ -200,7 +223,9 @@ public:
|
|||
ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){}
|
||||
~ogStaticText(){}
|
||||
|
||||
void SetText(const wxString& value);
|
||||
void SetText(const wxString& value, bool wrap = true);
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* slic3r_OptionsGroup_hpp_ */
|
||||
|
|
|
@ -9,11 +9,12 @@ void PreferencesDialog::build()
|
|||
{
|
||||
auto app_config = get_app_config();
|
||||
m_optgroup = std::make_shared<ConfigOptionsGroup>(this, _(L("General")));
|
||||
m_optgroup->label_width = 200;
|
||||
m_optgroup->label_width = 400;
|
||||
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||
};
|
||||
|
||||
// TODO
|
||||
// $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
// opt_id = > 'version_check',
|
||||
// type = > 'bool',
|
||||
|
@ -48,6 +49,22 @@ void PreferencesDialog::build()
|
|||
option = Option (def,"background_processing");
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
// Please keep in sync with ConfigWizard
|
||||
def.label = L("Check for application updates");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, Slic3r checks for new versions of Slic3r PE online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.");
|
||||
def.default_value = new ConfigOptionBool(app_config->get("version_check") == "1");
|
||||
option = Option (def, "version_check");
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
// Please keep in sync with ConfigWizard
|
||||
def.label = L("Update built-in Presets automatically");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.");
|
||||
def.default_value = new ConfigOptionBool(app_config->get("preset_update") == "1");
|
||||
option = Option (def, "preset_update");
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
def.label = L("Disable USB/serial connection");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("Disable communication with the printer over a serial / USB cable. "
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef slic3r_Preferences_hpp_
|
||||
#define slic3r_Preferences_hpp_
|
||||
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include <wx/dialog.h>
|
||||
|
@ -25,3 +28,5 @@ public:
|
|||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
|
||||
#endif /* slic3r_Preferences_hpp_ */
|
||||
|
|
|
@ -2,9 +2,14 @@
|
|||
#include <cassert>
|
||||
|
||||
#include "Preset.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <boost/nowide/cenv.hpp>
|
||||
|
@ -13,6 +18,7 @@
|
|||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/image.h>
|
||||
#include <wx/choice.h>
|
||||
|
@ -23,14 +29,16 @@
|
|||
#include "../../libslic3r/Utils.hpp"
|
||||
#include "../../libslic3r/PlaceholderParser.hpp"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree)
|
||||
ConfigFileType guess_config_file_type(const ptree &tree)
|
||||
{
|
||||
size_t app_config = 0;
|
||||
size_t bundle = 0;
|
||||
size_t config = 0;
|
||||
for (const boost::property_tree::ptree::value_type &v : tree) {
|
||||
for (const ptree::value_type &v : tree) {
|
||||
if (v.second.empty()) {
|
||||
if (v.first == "background_processing" ||
|
||||
v.first == "last_output_path" ||
|
||||
|
@ -58,6 +66,80 @@ ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree)
|
|||
(bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG;
|
||||
}
|
||||
|
||||
|
||||
VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool load_all)
|
||||
{
|
||||
ptree tree;
|
||||
boost::filesystem::ifstream ifs(path);
|
||||
boost::property_tree::read_ini(ifs, tree);
|
||||
return VendorProfile::from_ini(tree, path, load_all);
|
||||
}
|
||||
|
||||
VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all)
|
||||
{
|
||||
static const std::string printer_model_key = "printer_model:";
|
||||
const std::string id = path.stem().string();
|
||||
|
||||
if (! boost::filesystem::exists(path)) {
|
||||
throw std::runtime_error((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str());
|
||||
}
|
||||
|
||||
VendorProfile res(id);
|
||||
|
||||
auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator
|
||||
{
|
||||
auto res = tree.find(key);
|
||||
if (res == tree.not_found()) {
|
||||
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const auto &vendor_section = get_or_throw(tree, "vendor")->second;
|
||||
res.name = get_or_throw(vendor_section, "name")->second.data();
|
||||
|
||||
auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data();
|
||||
auto config_version = Semver::parse(config_version_str);
|
||||
if (! config_version) {
|
||||
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
|
||||
} else {
|
||||
res.config_version = std::move(*config_version);
|
||||
}
|
||||
|
||||
auto config_update_url = vendor_section.find("config_update_url");
|
||||
if (config_update_url != vendor_section.not_found()) {
|
||||
res.config_update_url = config_update_url->second.data();
|
||||
}
|
||||
|
||||
if (! load_all) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (auto §ion : tree) {
|
||||
if (boost::starts_with(section.first, printer_model_key)) {
|
||||
VendorProfile::PrinterModel model;
|
||||
model.id = section.first.substr(printer_model_key.size());
|
||||
model.name = section.second.get<std::string>("name", model.id);
|
||||
section.second.get<std::string>("variants", "");
|
||||
const auto variants_field = section.second.get<std::string>("variants", "");
|
||||
std::vector<std::string> variants;
|
||||
if (Slic3r::unescape_strings_cstyle(variants_field, variants)) {
|
||||
for (const std::string &variant_name : variants) {
|
||||
if (model.variant(variant_name) == nullptr)
|
||||
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field;
|
||||
}
|
||||
if (! model.id.empty() && ! model.variants.empty())
|
||||
res.models.push_back(std::move(model));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Suffix to be added to a modified preset name in the combo box.
|
||||
static std::string g_suffix_modified = " (modified)";
|
||||
const std::string& Preset::suffix_modified()
|
||||
|
@ -175,6 +257,15 @@ bool Preset::update_compatible_with_printer(const Preset &active_printer, const
|
|||
return this->is_compatible = is_compatible_with_printer(active_printer, extra_config);
|
||||
}
|
||||
|
||||
void Preset::set_visible_from_appconfig(const AppConfig &app_config)
|
||||
{
|
||||
if (vendor == nullptr) { return; }
|
||||
const std::string &model = config.opt_string("printer_model");
|
||||
const std::string &variant = config.opt_string("printer_variant");
|
||||
if (model.empty() || variant.empty()) { return; }
|
||||
is_visible = app_config.get_variant(vendor->id, model, variant);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& Preset::print_options()
|
||||
{
|
||||
static std::vector<std::string> s_opts {
|
||||
|
@ -252,7 +343,8 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
|
|||
m_type(type),
|
||||
m_edited_preset(type, "", false),
|
||||
m_idx_selected(0),
|
||||
m_bitmap_main_frame(new wxBitmap)
|
||||
m_bitmap_main_frame(new wxBitmap),
|
||||
m_bitmap_cache(new GUI::BitmapCache)
|
||||
{
|
||||
// Insert just the default preset.
|
||||
m_presets.emplace_back(Preset(type, "- default -", true));
|
||||
|
@ -264,6 +356,8 @@ PresetCollection::~PresetCollection()
|
|||
{
|
||||
delete m_bitmap_main_frame;
|
||||
m_bitmap_main_frame = nullptr;
|
||||
delete m_bitmap_cache;
|
||||
m_bitmap_cache = nullptr;
|
||||
}
|
||||
|
||||
void PresetCollection::reset(bool delete_files)
|
||||
|
@ -295,7 +389,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
|
|||
// Remove the .ini suffix.
|
||||
name.erase(name.size() - 4);
|
||||
if (this->find_preset(name, false)) {
|
||||
errors_cummulative += "The user preset \"" + name + "\" cannot be loaded. A system preset of the same name has already been loaded.";
|
||||
// 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;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
|
@ -453,18 +549,6 @@ size_t PresetCollection::first_visible_idx() const
|
|||
return idx;
|
||||
}
|
||||
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
size_t PresetCollection::first_compatible_idx() const
|
||||
{
|
||||
size_t idx = m_default_suppressed ? 1 : 0;
|
||||
for (; idx < this->m_presets.size(); ++ idx)
|
||||
if (m_presets[idx].is_compatible)
|
||||
break;
|
||||
if (idx == this->m_presets.size())
|
||||
idx = 0;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void PresetCollection::set_default_suppressed(bool default_suppressed)
|
||||
{
|
||||
if (m_default_suppressed != default_suppressed) {
|
||||
|
@ -473,7 +557,7 @@ void PresetCollection::set_default_suppressed(bool default_suppressed)
|
|||
}
|
||||
}
|
||||
|
||||
void PresetCollection::update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible)
|
||||
size_t PresetCollection::update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible)
|
||||
{
|
||||
DynamicPrintConfig config;
|
||||
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
|
||||
|
@ -484,14 +568,12 @@ void PresetCollection::update_compatible_with_printer(const Preset &active_print
|
|||
Preset &preset_selected = m_presets[idx_preset];
|
||||
Preset &preset_edited = selected ? m_edited_preset : preset_selected;
|
||||
if (! preset_edited.update_compatible_with_printer(active_printer, &config) &&
|
||||
selected && select_other_if_incompatible)
|
||||
selected && unselect_if_incompatible)
|
||||
m_idx_selected = (size_t)-1;
|
||||
if (selected)
|
||||
preset_selected.is_compatible = preset_edited.is_compatible;
|
||||
}
|
||||
if (m_idx_selected == (size_t)-1)
|
||||
// Find some other compatible preset, or the "-- default --" preset.
|
||||
this->select_preset(first_compatible_idx());
|
||||
return m_idx_selected;
|
||||
}
|
||||
|
||||
// Save the preset under a new name. If the name is different from the old one,
|
||||
|
@ -511,17 +593,41 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
|
|||
// Otherwise fill in the list from scratch.
|
||||
ui->Freeze();
|
||||
ui->Clear();
|
||||
std::map<wxString, bool> nonsys_presets;
|
||||
|
||||
const Preset &selected_preset = this->get_selected_preset();
|
||||
// Show wide icons if the currently selected preset is not compatible with the current printer,
|
||||
// and draw a red flag in front of the selected preset.
|
||||
bool wide_icons = !selected_preset.is_compatible && m_bitmap_incompatible != nullptr;
|
||||
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
wxString selected = "";
|
||||
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) {
|
||||
if (!this->m_presets.front().is_visible)
|
||||
ui->Append("------- System presets -------", wxNullBitmap);
|
||||
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
|
||||
const Preset &preset = this->m_presets[i];
|
||||
if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
|
||||
continue;
|
||||
const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible;
|
||||
// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
|
||||
// (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
|
||||
// if (i == m_idx_selected)
|
||||
// ui->SetSelection(ui->GetCount() - 1);
|
||||
std::string bitmap_key = "";
|
||||
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
|
||||
// to the filament color image.
|
||||
if (wide_icons)
|
||||
bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
|
||||
bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
|
||||
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
if (wide_icons)
|
||||
// Paint a red flag for incompatible presets.
|
||||
bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(16, 16) : *m_bitmap_incompatible);
|
||||
// Paint the color bars.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(4, 16));
|
||||
bmps.emplace_back(*m_bitmap_main_frame);
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back(m_bitmap_cache->mkclear(6, 16));
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16));
|
||||
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
|
||||
}
|
||||
|
||||
if (preset.is_default || preset.is_system){
|
||||
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
|
||||
|
@ -531,20 +637,18 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
|
|||
}
|
||||
else
|
||||
{
|
||||
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible);
|
||||
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/);
|
||||
if (i == m_idx_selected)
|
||||
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
|
||||
}
|
||||
if (preset.is_default)
|
||||
ui->Append("------------------------------------", wxNullBitmap);
|
||||
ui->Append("------- System presets -------", wxNullBitmap);
|
||||
}
|
||||
if (!nonsys_presets.empty())
|
||||
{
|
||||
ui->Append("------------------------------------", wxNullBitmap);
|
||||
for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
|
||||
ui->Append(it->first,
|
||||
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
|
||||
ui->Append("------- User presets -------", wxNullBitmap);
|
||||
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, *it->second);
|
||||
if (it->first == selected)
|
||||
ui->SetSelection(ui->GetCount() - 1);
|
||||
}
|
||||
|
@ -552,51 +656,63 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
|
|||
ui->Thaw();
|
||||
}
|
||||
|
||||
void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
|
||||
size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
|
||||
{
|
||||
if (ui == nullptr)
|
||||
return;
|
||||
return 0;
|
||||
ui->Freeze();
|
||||
ui->Clear();
|
||||
std::map<wxString, bool> nonsys_presets;
|
||||
size_t selected_preset_item = 0;
|
||||
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
wxString selected = "";
|
||||
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) {
|
||||
if (!this->m_presets.front().is_visible)
|
||||
ui->Append("------- System presets -------", wxNullBitmap);
|
||||
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) {
|
||||
const Preset &preset = this->m_presets[i];
|
||||
if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
|
||||
continue;
|
||||
const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible;
|
||||
// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
|
||||
// (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
|
||||
// if (i == m_idx_selected)
|
||||
// ui->SetSelection(ui->GetCount() - 1);
|
||||
std::string bitmap_key = "tab";
|
||||
bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt";
|
||||
bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst";
|
||||
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
|
||||
if (bmp == nullptr) {
|
||||
// Create the bitmap with color bars.
|
||||
std::vector<wxBitmap> bmps;
|
||||
const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible;
|
||||
bmps.emplace_back((tmp_bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *tmp_bmp);
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16));
|
||||
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
|
||||
}
|
||||
|
||||
if (preset.is_default || preset.is_system){
|
||||
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
|
||||
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
|
||||
if (i == m_idx_selected)
|
||||
ui->SetSelection(ui->GetCount() - 1);
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible);
|
||||
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/);
|
||||
if (i == m_idx_selected)
|
||||
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
|
||||
}
|
||||
if (preset.is_default)
|
||||
ui->Append("------------------------------------", wxNullBitmap);
|
||||
ui->Append("------- System presets -------", wxNullBitmap);
|
||||
}
|
||||
if (!nonsys_presets.empty())
|
||||
{
|
||||
ui->Append("------------------------------------", wxNullBitmap);
|
||||
for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
|
||||
ui->Append(it->first,
|
||||
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
|
||||
ui->Append("------- User presets -------", wxNullBitmap);
|
||||
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, *it->second);
|
||||
if (it->first == selected)
|
||||
ui->SetSelection(ui->GetCount() - 1);
|
||||
selected_preset_item = ui->GetCount() - 1;
|
||||
}
|
||||
}
|
||||
ui->SetSelection(selected_preset_item);
|
||||
ui->Thaw();
|
||||
return selected_preset_item;
|
||||
}
|
||||
|
||||
// Update a dirty floag of the current preset, update the labels of the UI component accordingly.
|
||||
|
@ -629,11 +745,13 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
|
|||
return was_dirty != is_dirty;
|
||||
}
|
||||
|
||||
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference)
|
||||
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type /*= false*/)
|
||||
{
|
||||
std::vector<std::string> changed;
|
||||
if (edited != nullptr && reference != nullptr) {
|
||||
changed = reference->config.diff(edited->config);
|
||||
if (edited != nullptr && reference != nullptr) {
|
||||
changed = is_printer_type ?
|
||||
reference->config.deep_diff(edited->config) :
|
||||
reference->config.diff(edited->config);
|
||||
// The "compatible_printers" option key is handled differently from the others:
|
||||
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
|
||||
// If the key exists and it is empty, it means it is compatible with no printer.
|
||||
|
@ -677,8 +795,8 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
|
|||
// 1) Try to find the preset by its name.
|
||||
auto it = this->find_preset_internal(name);
|
||||
size_t idx = 0;
|
||||
if (it != m_presets.end() && it->name == name)
|
||||
// Preset found by its name.
|
||||
if (it != m_presets.end() && it->name == name && it->is_visible)
|
||||
// Preset found by its name and it is visible.
|
||||
idx = it - m_presets.begin();
|
||||
else {
|
||||
// Find the first visible preset.
|
||||
|
@ -690,6 +808,11 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
|
|||
// If the first visible preset was not found, return the 0th element, which is the default preset.
|
||||
}
|
||||
|
||||
// Temporary decision
|
||||
if (name_w_suffix == "------- System presets -------" ||
|
||||
name_w_suffix == "------- User presets -------")
|
||||
return true;
|
||||
|
||||
// 2) Select the new preset.
|
||||
if (m_idx_selected != idx || force) {
|
||||
this->select_preset(idx);
|
||||
|
@ -699,6 +822,46 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PresetCollection::select_preset_by_name_strict(const std::string &name)
|
||||
{
|
||||
// 1) Try to find the preset by its name.
|
||||
auto it = this->find_preset_internal(name);
|
||||
size_t idx = (size_t)-1;
|
||||
if (it != m_presets.end() && it->name == name && it->is_visible)
|
||||
// Preset found by its name.
|
||||
idx = it - m_presets.begin();
|
||||
// 2) Select the new preset.
|
||||
if (idx != (size_t)-1) {
|
||||
this->select_preset(idx);
|
||||
return true;
|
||||
}
|
||||
m_idx_selected = idx;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||
std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&other, const std::set<VendorProfile> &new_vendors)
|
||||
{
|
||||
std::vector<std::string> duplicates;
|
||||
for (Preset &preset : other.m_presets) {
|
||||
if (preset.is_default || preset.is_external)
|
||||
continue;
|
||||
Preset key(m_type, preset.name);
|
||||
auto it = std::lower_bound(m_presets.begin() + 1, m_presets.end(), key);
|
||||
if (it == m_presets.end() || it->name != preset.name) {
|
||||
if (preset.vendor != nullptr) {
|
||||
// Re-assign a pointer to the vendor structure in the new PresetBundle.
|
||||
auto it = new_vendors.find(*preset.vendor);
|
||||
assert(it != new_vendors.end());
|
||||
preset.vendor = &(*it);
|
||||
}
|
||||
this->m_presets.emplace(it, std::move(preset));
|
||||
} else
|
||||
duplicates.emplace_back(std::move(preset.name));
|
||||
}
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
std::string PresetCollection::name() const
|
||||
{
|
||||
switch (this->type()) {
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
#include <deque>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
|
||||
#include "../../libslic3r/libslic3r.h"
|
||||
#include "../../libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
|
||||
class wxBitmap;
|
||||
class wxChoice;
|
||||
|
@ -13,6 +17,13 @@ class wxItemContainer;
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class AppConfig;
|
||||
class PresetBundle;
|
||||
|
||||
namespace GUI {
|
||||
class BitmapCache;
|
||||
}
|
||||
|
||||
enum ConfigFileType
|
||||
{
|
||||
CONFIG_FILE_TYPE_UNKNOWN,
|
||||
|
@ -28,21 +39,19 @@ class VendorProfile
|
|||
public:
|
||||
std::string name;
|
||||
std::string id;
|
||||
std::string config_version;
|
||||
Semver config_version;
|
||||
std::string config_update_url;
|
||||
|
||||
struct PrinterVariant {
|
||||
PrinterVariant() {}
|
||||
PrinterVariant(const std::string &name) : name(name) {}
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
struct PrinterModel {
|
||||
PrinterModel() {}
|
||||
PrinterModel(const std::string &name) : name(name) {}
|
||||
std::string id;
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
std::vector<PrinterVariant> variants;
|
||||
PrinterVariant* variant(const std::string &name) {
|
||||
for (auto &v : this->variants)
|
||||
|
@ -51,11 +60,14 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
const PrinterVariant* variant(const std::string &name) const { return const_cast<PrinterModel*>(this)->variant(name); }
|
||||
|
||||
bool operator< (const PrinterModel &rhs) const { return this->name < rhs.name; }
|
||||
bool operator==(const PrinterModel &rhs) const { return this->name == rhs.name; }
|
||||
};
|
||||
std::set<PrinterModel> models;
|
||||
std::vector<PrinterModel> models;
|
||||
|
||||
VendorProfile() {}
|
||||
VendorProfile(std::string id) : id(std::move(id)) {}
|
||||
|
||||
static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true);
|
||||
static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
|
||||
|
||||
size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; }
|
||||
|
||||
|
@ -86,7 +98,8 @@ public:
|
|||
bool is_external = false;
|
||||
// System preset is read-only.
|
||||
bool is_system = false;
|
||||
// Preset is visible, if it is compatible with the active Printer.
|
||||
// Preset is visible, if it is associated with a printer model / variant that is enabled in the AppConfig
|
||||
// or if it has no printer model / variant association.
|
||||
// Also the "default" preset is only visible, if it is the only preset in the list.
|
||||
bool is_visible = true;
|
||||
// Has this preset been modified?
|
||||
|
@ -132,6 +145,9 @@ public:
|
|||
// Mark this preset as compatible if it is compatible with active_printer.
|
||||
bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config);
|
||||
|
||||
// Set is_visible according to application config
|
||||
void set_visible_from_appconfig(const AppConfig &app_config);
|
||||
|
||||
// Resize the extruder specific fields, initialize them with the content of the 1st extruder.
|
||||
void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); }
|
||||
|
||||
|
@ -163,6 +179,13 @@ public:
|
|||
PresetCollection(Preset::Type type, const std::vector<std::string> &keys);
|
||||
~PresetCollection();
|
||||
|
||||
typedef std::deque<Preset>::iterator Iterator;
|
||||
typedef std::deque<Preset>::const_iterator ConstIterator;
|
||||
Iterator begin() { return m_presets.begin() + 1; }
|
||||
ConstIterator begin() const { return m_presets.begin() + 1; }
|
||||
Iterator end() { return m_presets.end(); }
|
||||
ConstIterator end() const { return m_presets.end(); }
|
||||
|
||||
void reset(bool delete_files);
|
||||
|
||||
Preset::Type type() const { return m_type; }
|
||||
|
@ -234,37 +257,67 @@ public:
|
|||
{ return const_cast<PresetCollection*>(this)->find_preset(name, first_visible_if_not_found); }
|
||||
|
||||
size_t first_visible_idx() const;
|
||||
size_t first_compatible_idx() const;
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
// If one of the prefered_alternates is compatible, select it.
|
||||
template<typename PreferedCondition>
|
||||
size_t first_compatible_idx(PreferedCondition prefered_condition) const
|
||||
{
|
||||
size_t i = m_default_suppressed ? 1 : 0;
|
||||
size_t n = this->m_presets.size();
|
||||
size_t i_compatible = n;
|
||||
for (; i < n; ++ i)
|
||||
if (m_presets[i].is_compatible) {
|
||||
if (prefered_condition(m_presets[i].name))
|
||||
return i;
|
||||
if (i_compatible == n)
|
||||
// Store the first compatible profile into i_compatible.
|
||||
i_compatible = i;
|
||||
}
|
||||
return (i_compatible == n) ? 0 : i_compatible;
|
||||
}
|
||||
// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible.
|
||||
size_t first_compatible_idx() const { return this->first_compatible_idx([](const std::string&){return true;}); }
|
||||
|
||||
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
|
||||
// Return the first visible preset. Certainly at least the '- default -' preset shall be visible.
|
||||
Preset& first_visible() { return this->preset(this->first_visible_idx()); }
|
||||
const Preset& first_visible() const { return this->preset(this->first_visible_idx()); }
|
||||
Preset& first_compatible() { return this->preset(this->first_compatible_idx()); }
|
||||
template<typename PreferedCondition>
|
||||
Preset& first_compatible(PreferedCondition prefered_condition) { return this->preset(this->first_compatible_idx(prefered_condition)); }
|
||||
const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); }
|
||||
|
||||
// Return number of presets including the "- default -" preset.
|
||||
size_t size() const { return this->m_presets.size(); }
|
||||
|
||||
// For Print / Filament presets, disable those, which are not compatible with the printer.
|
||||
void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible);
|
||||
template<typename PreferedCondition>
|
||||
void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible, PreferedCondition prefered_condition)
|
||||
{
|
||||
if (this->update_compatible_with_printer_internal(active_printer, select_other_if_incompatible) == (size_t)-1)
|
||||
// Find some other compatible preset, or the "-- default --" preset.
|
||||
this->select_preset(this->first_compatible_idx(prefered_condition));
|
||||
}
|
||||
void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible)
|
||||
{ this->update_compatible_with_printer(active_printer, select_other_if_incompatible, [](const std::string&){return true;}); }
|
||||
|
||||
size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
|
||||
|
||||
// Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
|
||||
bool current_is_dirty() const { return ! this->current_dirty_options().empty(); }
|
||||
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
|
||||
std::vector<std::string> current_dirty_options() const
|
||||
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset()); }
|
||||
std::vector<std::string> current_dirty_options(const bool is_printer_type = false) const
|
||||
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), is_printer_type); }
|
||||
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
|
||||
std::vector<std::string> current_different_from_parent_options() const
|
||||
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent()); }
|
||||
std::vector<std::string> current_different_from_parent_options(const bool is_printer_type = false) const
|
||||
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); }
|
||||
// Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal.
|
||||
std::vector<std::string> system_equal_options() const;
|
||||
|
||||
// Update the choice UI from the list of presets.
|
||||
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
|
||||
// If an incompatible preset is selected, it is shown as well.
|
||||
void update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible);
|
||||
size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible);
|
||||
// Update the choice UI from the list of presets.
|
||||
// Only the compatible presets are shown.
|
||||
// If an incompatible preset is selected, it is shown as well.
|
||||
|
@ -282,6 +335,14 @@ public:
|
|||
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
|
||||
std::string path_from_name(const std::string &new_name) const;
|
||||
|
||||
protected:
|
||||
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
|
||||
// This is a temporary state, which shall be fixed immediately by the following step.
|
||||
bool select_preset_by_name_strict(const std::string &name);
|
||||
|
||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||
std::vector<std::string> merge_presets(PresetCollection &&other, const std::set<VendorProfile> &new_vendors);
|
||||
|
||||
private:
|
||||
PresetCollection();
|
||||
PresetCollection(const PresetCollection &other);
|
||||
|
@ -299,7 +360,9 @@ private:
|
|||
std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
|
||||
{ return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
|
||||
|
||||
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference);
|
||||
size_t update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible);
|
||||
|
||||
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
|
||||
|
||||
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
|
||||
Preset::Type m_type;
|
||||
|
@ -324,6 +387,12 @@ private:
|
|||
wxBitmap *m_bitmap_main_frame;
|
||||
// Path to the directory to store the config files into.
|
||||
std::string m_dir_path;
|
||||
|
||||
// Caching color bitmaps for the filament combo box.
|
||||
GUI::BitmapCache *m_bitmap_cache = nullptr;
|
||||
|
||||
// to access select_preset_by_name_strict()
|
||||
friend class PresetBundle;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "PresetBundle.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/clamp.hpp>
|
||||
|
@ -111,6 +112,7 @@ void PresetBundle::setup_directories()
|
|||
std::initializer_list<boost::filesystem::path> paths = {
|
||||
data_dir,
|
||||
data_dir / "vendor",
|
||||
data_dir / "cache",
|
||||
#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR
|
||||
// Store the print/filament/printer presets into a "presets" directory.
|
||||
data_dir / "presets",
|
||||
|
@ -176,6 +178,7 @@ std::string PresetBundle::load_system_presets()
|
|||
// Here the vendor specific read only Config Bundles are stored.
|
||||
boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / "vendor").make_preferred();
|
||||
std::string errors_cummulative;
|
||||
bool first = true;
|
||||
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
||||
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) {
|
||||
std::string name = dir_entry.path().filename().string();
|
||||
|
@ -183,7 +186,25 @@ std::string PresetBundle::load_system_presets()
|
|||
name.erase(name.size() - 4);
|
||||
try {
|
||||
// Load the config bundle, flatten it.
|
||||
this->load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM);
|
||||
if (first) {
|
||||
// Reset this PresetBundle and load the first vendor config.
|
||||
this->load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM);
|
||||
first = false;
|
||||
} else {
|
||||
// Load the other vendor configs, merge them with this PresetBundle.
|
||||
// Report duplicate profiles.
|
||||
PresetBundle other;
|
||||
other.load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM);
|
||||
std::vector<std::string> duplicates = this->merge_presets(std::move(other));
|
||||
if (! duplicates.empty()) {
|
||||
errors_cummulative += "Vendor configuration file " + name + " contains the following presets with names used by other vendors: ";
|
||||
for (size_t i = 0; i < duplicates.size(); ++ i) {
|
||||
if (i > 0)
|
||||
errors_cummulative += ", ";
|
||||
errors_cummulative += duplicates[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
errors_cummulative += "\n";
|
||||
|
@ -192,6 +213,18 @@ std::string PresetBundle::load_system_presets()
|
|||
return errors_cummulative;
|
||||
}
|
||||
|
||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||
std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other)
|
||||
{
|
||||
this->vendors.insert(other.vendors.begin(), other.vendors.end());
|
||||
std::vector<std::string> duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors);
|
||||
std::vector<std::string> duplicate_filaments = this->filaments.merge_presets(std::move(other.filaments), this->vendors);
|
||||
std::vector<std::string> duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors);
|
||||
append(duplicate_prints, std::move(duplicate_filaments));
|
||||
append(duplicate_prints, std::move(duplicate_printers));
|
||||
return duplicate_prints;
|
||||
}
|
||||
|
||||
static inline std::string remove_ini_suffix(const std::string &name)
|
||||
{
|
||||
std::string out = name;
|
||||
|
@ -205,26 +238,44 @@ static inline std::string remove_ini_suffix(const std::string &name)
|
|||
// If the "vendor" section is missing, enable all models and variants of the particular vendor.
|
||||
void PresetBundle::load_installed_printers(const AppConfig &config)
|
||||
{
|
||||
// m_storage
|
||||
for (auto &preset : printers) {
|
||||
preset.set_visible_from_appconfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
// Load selections (current print, current filaments, current printer) from config.ini
|
||||
// This is done just once on application start up.
|
||||
void PresetBundle::load_selections(const AppConfig &config)
|
||||
{
|
||||
prints.select_preset_by_name(remove_ini_suffix(config.get("presets", "print")), true);
|
||||
filaments.select_preset_by_name(remove_ini_suffix(config.get("presets", "filament")), true);
|
||||
printers.select_preset_by_name(remove_ini_suffix(config.get("presets", "printer")), true);
|
||||
// Update visibility of presets based on application vendor / model / variant configuration.
|
||||
this->load_installed_printers(config);
|
||||
|
||||
// Parse the initial print / filament / printer profile names.
|
||||
std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print"));
|
||||
std::vector<std::string> initial_filament_profile_names;
|
||||
std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer"));
|
||||
|
||||
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter"));
|
||||
size_t num_extruders = nozzle_diameter->values.size();
|
||||
this->set_filament_preset(0, filaments.get_selected_preset().name);
|
||||
initial_filament_profile_names.emplace_back(remove_ini_suffix(config.get("presets", "filament")));
|
||||
this->set_filament_preset(0, initial_filament_profile_names.back());
|
||||
for (unsigned int i = 1; i < (unsigned int)num_extruders; ++ i) {
|
||||
char name[64];
|
||||
sprintf(name, "filament_%d", i);
|
||||
if (! config.has("presets", name))
|
||||
break;
|
||||
this->set_filament_preset(i, remove_ini_suffix(config.get("presets", name)));
|
||||
initial_filament_profile_names.emplace_back(remove_ini_suffix(config.get("presets", name)));
|
||||
this->set_filament_preset(i, initial_filament_profile_names.back());
|
||||
}
|
||||
|
||||
// Activate print / filament / printer profiles from the config.
|
||||
// If the printer profile enumerated by the config are not visible, select an alternate preset.
|
||||
// Do not select alternate profiles for the print / filament profiles as those presets
|
||||
// will be selected by the following call of this->update_compatible_with_printer(true).
|
||||
prints.select_preset_by_name_strict(initial_print_profile_name);
|
||||
filaments.select_preset_by_name_strict(initial_filament_profile_names.front());
|
||||
printers.select_preset_by_name(initial_printer_profile_name, true);
|
||||
|
||||
// Update visibility of presets based on their compatibility with the active printer.
|
||||
// Always try to select a compatible print and filament preset to the current printer preset,
|
||||
// as the application may have been closed with an active "external" preset, which does not
|
||||
|
@ -675,48 +726,6 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree)
|
|||
flatten_configbundle_hierarchy(tree, "printer");
|
||||
}
|
||||
|
||||
static void load_vendor_profile(const boost::property_tree::ptree &tree, VendorProfile &vendor_profile)
|
||||
{
|
||||
const std::string printer_model_key = "printer_model:";
|
||||
for (auto §ion : tree)
|
||||
if (section.first == "vendor") {
|
||||
// Load the names of the active presets.
|
||||
for (auto &kvp : section.second) {
|
||||
if (kvp.first == "name")
|
||||
vendor_profile.name = kvp.second.data();
|
||||
else if (kvp.first == "id")
|
||||
vendor_profile.id = kvp.second.data();
|
||||
else if (kvp.first == "config_version")
|
||||
vendor_profile.config_version = kvp.second.data();
|
||||
else if (kvp.first == "config_update_url")
|
||||
vendor_profile.config_update_url = kvp.second.data();
|
||||
}
|
||||
} else if (boost::starts_with(section.first, printer_model_key)) {
|
||||
VendorProfile::PrinterModel model;
|
||||
model.name = section.first.substr(printer_model_key.size());
|
||||
section.second.get<std::string>("variants", "");
|
||||
std::vector<std::string> variants;
|
||||
if (Slic3r::unescape_strings_cstyle(section.second.get<std::string>("variants", ""), variants)) {
|
||||
for (const std::string &variant_name : variants) {
|
||||
if (model.variant(variant_name) == nullptr)
|
||||
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
|
||||
}
|
||||
} else {
|
||||
// Log error?
|
||||
}
|
||||
if (! model.name.empty() && ! model.variants.empty())
|
||||
vendor_profile.models.insert(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Load a config bundle file, into presets and store the loaded presets into separate files
|
||||
// of the local configuration directory.
|
||||
void PresetBundle::install_vendor_configbundle(const std::string &src_path0)
|
||||
{
|
||||
boost::filesystem::path src_path(src_path0);
|
||||
boost::filesystem::copy_file(src_path, (boost::filesystem::path(data_dir()) / "vendor" / src_path.filename()).make_preferred(), boost::filesystem::copy_option::overwrite_if_exists);
|
||||
}
|
||||
|
||||
// Load a config bundle file, into presets and store the loaded presets into separate files
|
||||
// of the local configuration directory.
|
||||
size_t PresetBundle::load_configbundle(const std::string &path, unsigned int flags)
|
||||
|
@ -730,19 +739,21 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||
pt::ptree tree;
|
||||
boost::nowide::ifstream ifs(path);
|
||||
pt::read_ini(ifs, tree);
|
||||
// Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
|
||||
flatten_configbundle_hierarchy(tree);
|
||||
|
||||
const VendorProfile *vendor_profile = nullptr;
|
||||
if (flags & LOAD_CFGBNDLE_SYSTEM) {
|
||||
VendorProfile vp;
|
||||
load_vendor_profile(tree, vp);
|
||||
if (vp.name.empty())
|
||||
throw std::runtime_error(std::string("Vendor Config Bundle is not valid: Missing vendor name key."));
|
||||
if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) {
|
||||
auto vp = VendorProfile::from_ini(tree, path);
|
||||
if (vp.num_variants() == 0)
|
||||
return 0;
|
||||
vendor_profile = &(*this->vendors.insert(vp).first);
|
||||
}
|
||||
|
||||
if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
|
||||
flatten_configbundle_hierarchy(tree);
|
||||
|
||||
// 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
|
||||
std::vector<std::string> loaded_prints;
|
||||
|
@ -814,7 +825,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||
section.first << "\" defines no printer variant, it will be ignored.";
|
||||
continue;
|
||||
}
|
||||
auto it_model = vendor_profile->models.find(VendorProfile::PrinterModel(printer_model));
|
||||
auto it_model = std::find_if(vendor_profile->models.cbegin(), vendor_profile->models.cend(),
|
||||
[&](const VendorProfile::PrinterModel &m) { return m.id == printer_model; }
|
||||
);
|
||||
if (it_model == vendor_profile->models.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||
section.first << "\" defines invalid printer model \"" << printer_model << "\", it will be ignored.";
|
||||
|
@ -916,14 +929,35 @@ void PresetBundle::update_multi_material_filament_presets()
|
|||
|
||||
void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible)
|
||||
{
|
||||
this->prints.update_compatible_with_printer(this->printers.get_edited_preset(), select_other_if_incompatible);
|
||||
this->filaments.update_compatible_with_printer(this->printers.get_edited_preset(), select_other_if_incompatible);
|
||||
const Preset &printer_preset = this->printers.get_edited_preset();
|
||||
const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile");
|
||||
const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
|
||||
prefered_print_profile.empty() ?
|
||||
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
|
||||
this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
|
||||
[&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; });
|
||||
prefered_filament_profiles.empty() ?
|
||||
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) :
|
||||
this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible,
|
||||
[&prefered_filament_profiles](const std::string& profile_name)
|
||||
{ return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
|
||||
if (select_other_if_incompatible) {
|
||||
// Verify validity of the current filament presets.
|
||||
for (std::string &filament_name : this->filament_presets) {
|
||||
Preset *preset = this->filaments.find_preset(filament_name, false);
|
||||
if (preset == nullptr || ! preset->is_compatible)
|
||||
filament_name = this->filaments.first_compatible().name;
|
||||
this->filament_presets.front() = this->filaments.get_edited_preset().name;
|
||||
for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) {
|
||||
std::string &filament_name = this->filament_presets[idx];
|
||||
Preset *preset = this->filaments.find_preset(filament_name, false);
|
||||
if (preset == nullptr || ! preset->is_compatible) {
|
||||
// Pick a compatible profile. If there are prefered_filament_profiles, use them.
|
||||
if (prefered_filament_profiles.empty())
|
||||
filament_name = this->filaments.first_compatible().name;
|
||||
else {
|
||||
const std::string &preferred = (idx < prefered_filament_profiles.size()) ?
|
||||
prefered_filament_profiles[idx] : prefered_filament_profiles.front();
|
||||
filament_name = this->filaments.first_compatible(
|
||||
[&preferred](const std::string& profile_name){ return profile_name == preferred; }).name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -977,6 +1011,10 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami
|
|||
// an optional "(modified)" suffix will be removed from the filament name.
|
||||
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
|
||||
{
|
||||
if (name == "------- System presets -------" ||
|
||||
name == "------- User presets -------")
|
||||
return;
|
||||
|
||||
if (idx >= filament_presets.size())
|
||||
filament_presets.resize(idx + 1, filaments.default_preset().name);
|
||||
filament_presets[idx] = Preset::remove_suffix_modified(name);
|
||||
|
@ -1025,9 +1063,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
|
|||
// and draw a red flag in front of the selected preset.
|
||||
bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr;
|
||||
assert(selected_preset != nullptr);
|
||||
std::map<wxString, wxBitmap> nonsys_presets;
|
||||
std::map<wxString, wxBitmap*> nonsys_presets;
|
||||
wxString selected_str = "";
|
||||
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) {
|
||||
if (!this->filaments().front().is_visible)
|
||||
ui->Append("------- System presets -------", wxNullBitmap);
|
||||
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) {
|
||||
const Preset &preset = this->filaments.preset(i);
|
||||
bool selected = this->filament_presets[idx_extruder] == preset.name;
|
||||
if (! preset.is_visible || (! preset.is_compatible && ! selected))
|
||||
|
@ -1059,14 +1099,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
|
|||
bmps.emplace_back(m_bitmapCache->mksolid(8, 16, rgb));
|
||||
}
|
||||
// Paint a lock at the system presets.
|
||||
bmps.emplace_back(m_bitmapCache->mkclear(4, 16));
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ?
|
||||
(preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
|
||||
bmps.emplace_back(m_bitmapCache->mkclear(2, 16));
|
||||
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(16, 16));
|
||||
// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
|
||||
bitmap = m_bitmapCache->insert(bitmap_key, bmps);
|
||||
}
|
||||
// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap);
|
||||
// if (selected)
|
||||
// ui->SetSelection(ui->GetCount() - 1);
|
||||
|
||||
if (preset.is_default || preset.is_system){
|
||||
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
|
||||
|
@ -1077,19 +1114,19 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
|
|||
else
|
||||
{
|
||||
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
|
||||
(bitmap == 0) ? wxNullBitmap : *bitmap);
|
||||
(bitmap == 0) ? &wxNullBitmap : bitmap);
|
||||
if (selected)
|
||||
selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
|
||||
}
|
||||
if (preset.is_default)
|
||||
ui->Append("------------------------------------", wxNullBitmap);
|
||||
ui->Append("------- System presets -------", wxNullBitmap);
|
||||
}
|
||||
|
||||
if (!nonsys_presets.empty())
|
||||
{
|
||||
ui->Append("------------------------------------", wxNullBitmap);
|
||||
for (std::map<wxString, wxBitmap>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, it->second);
|
||||
ui->Append("------- User presets -------", wxNullBitmap);
|
||||
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
|
||||
ui->Append(it->first, *it->second);
|
||||
if (it->first == selected_str)
|
||||
ui->SetSelection(ui->GetCount() - 1);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Preset.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -86,13 +87,11 @@ public:
|
|||
LOAD_CFGBNDLE_RESET_USER_PROFILE = 2,
|
||||
// Load a system config bundle.
|
||||
LOAD_CFGBNDLE_SYSTEM = 4,
|
||||
LOAD_CFGBUNDLE_VENDOR_ONLY = 8,
|
||||
};
|
||||
// Load the config bundle, store it to the user profile directory by default.
|
||||
size_t load_configbundle(const std::string &path, unsigned int flags = LOAD_CFGBNDLE_SAVE);
|
||||
|
||||
// Install the Vendor specific config bundle into user's directory.
|
||||
void install_vendor_configbundle(const std::string &src_path);
|
||||
|
||||
// Export a config bundle file containing all the presets and the names of the active presets.
|
||||
void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings);
|
||||
|
||||
|
@ -117,10 +116,12 @@ public:
|
|||
// preset if the current print or filament preset is not compatible.
|
||||
void update_compatible_with_printer(bool select_other_if_incompatible);
|
||||
|
||||
static bool parse_color(const std::string &scolor, unsigned char *rgb_out);
|
||||
static bool parse_color(const std::string &scolor, unsigned char *rgb_out);
|
||||
|
||||
private:
|
||||
std::string load_system_presets();
|
||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||
std::vector<std::string> merge_presets(PresetBundle &&other);
|
||||
|
||||
// Set the "enabled" flag for printer vendors, printer models and printer variants
|
||||
// based on the user configuration.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "slic3r/Utils/OctoPrint.hpp"
|
||||
#include "BonjourDialog.hpp"
|
||||
#include "WipeTowerDialog.hpp"
|
||||
#include "ButtonsDescription.hpp"
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/button.h>
|
||||
|
@ -23,6 +24,9 @@
|
|||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include "wxExtensions.hpp"
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
@ -88,9 +92,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
|
|||
if (wxMSW) m_btn_delete_preset->SetBackgroundColour(color);
|
||||
|
||||
m_show_incompatible_presets = false;
|
||||
m_bmp_show_incompatible_presets = new wxBitmap(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_hide_incompatible_presets = new wxBitmap(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG);
|
||||
m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, *m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
m_bmp_show_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_hide_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG);
|
||||
m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
if (wxMSW) m_btn_hide_incompatible_presets->SetBackgroundColour(color);
|
||||
|
||||
m_btn_save_preset->SetToolTip(_(L("Save current ")) + m_title);
|
||||
|
@ -99,14 +103,52 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
|
|||
|
||||
m_undo_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
m_undo_to_sys_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
m_question_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
|
||||
if (wxMSW) {
|
||||
m_undo_btn->SetBackgroundColour(color);
|
||||
m_undo_to_sys_btn->SetBackgroundColour(color);
|
||||
m_question_btn->SetBackgroundColour(color);
|
||||
}
|
||||
m_undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG));
|
||||
m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); }));
|
||||
m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG));
|
||||
m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); }));
|
||||
|
||||
m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n"
|
||||
"or click this button.")));
|
||||
|
||||
// Determine the theme color of OS (dark or light)
|
||||
auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
// Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
|
||||
m_bmp_value_lock .LoadFile(from_u8(var("sys_lock.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_value_unlock .LoadFile(from_u8(var(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_non_system = &m_bmp_white_bullet;
|
||||
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
|
||||
m_bmp_value_revert .LoadFile(from_u8(var(luma >= 128 ? "action_undo.png" : "action_undo_grey.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_white_bullet .LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_question .LoadFile(from_u8(var("question_mark_01.png")), wxBITMAP_TYPE_PNG);
|
||||
|
||||
fill_icon_descriptions();
|
||||
set_tooltips_text();
|
||||
|
||||
m_undo_btn->SetBitmap(m_bmp_white_bullet);
|
||||
m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(); }));
|
||||
m_undo_to_sys_btn->SetBitmap(m_bmp_white_bullet);
|
||||
m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(true); }));
|
||||
m_question_btn->SetBitmap(m_bmp_question);
|
||||
m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent)
|
||||
{
|
||||
auto dlg = new ButtonsDescription(this, &m_icon_descriptions);
|
||||
if (dlg->ShowModal() == wxID_OK){
|
||||
// Colors for ui "decoration"
|
||||
for (Tab *tab : get_tabs_list()){
|
||||
tab->m_sys_label_clr = get_label_clr_sys();
|
||||
tab->m_modified_label_clr = get_label_clr_modified();
|
||||
tab->update_labels_colour();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Colors for ui "decoration"
|
||||
m_sys_label_clr = get_label_clr_sys();
|
||||
m_modified_label_clr = get_label_clr_modified();
|
||||
m_default_text_clr = get_label_clr_default();
|
||||
|
||||
m_hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_hsizer, 0, wxBOTTOM, 3);
|
||||
|
@ -120,7 +162,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
|
|||
m_hsizer->AddSpacer(64);
|
||||
m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
// m_hsizer->AddSpacer(64);
|
||||
m_hsizer->AddSpacer(32);
|
||||
m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
// m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
|
||||
|
||||
//Horizontal sizer to hold the tree and the selected page.
|
||||
|
@ -183,8 +226,16 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
|
|||
//! select_preset(m_presets_choice->GetStringSelection().ToStdString());
|
||||
//! we doing next:
|
||||
int selected_item = m_presets_choice->GetSelection();
|
||||
if (m_selected_preset_item == selected_item && !m_presets->current_is_dirty())
|
||||
return;
|
||||
if (selected_item >= 0){
|
||||
std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data();
|
||||
if (selected_string == "------- System presets -------" ||
|
||||
selected_string == "------- User presets -------"){
|
||||
m_presets_choice->SetSelection(m_selected_preset_item);
|
||||
return;
|
||||
}
|
||||
m_selected_preset_item = selected_item;
|
||||
select_preset(selected_string);
|
||||
}
|
||||
}));
|
||||
|
@ -204,8 +255,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
|
|||
void Tab::load_initial_data()
|
||||
{
|
||||
m_config = &m_presets->get_edited_preset().config;
|
||||
m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ?
|
||||
"bullet_white.png" : "sys_unlock.png";
|
||||
m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
|
||||
m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
|
||||
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
|
||||
}
|
||||
|
||||
PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/)
|
||||
|
@ -234,140 +286,128 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo
|
|||
return page;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void add_correct_opts_to_dirty_options(const std::string &opt_key, std::vector<std::string> *vec, TabPrinter *tab)
|
||||
void Tab::update_labels_colour()
|
||||
{
|
||||
auto opt_init = static_cast<T*>(tab->m_presets->get_selected_preset().config.option(opt_key));
|
||||
auto opt_cur = static_cast<T*>(tab->m_config->option(opt_key));
|
||||
int opt_init_max_id = opt_init->values.size()-1;
|
||||
for (int i = 0; i < opt_cur->values.size(); i++)
|
||||
Freeze();
|
||||
//update options "decoration"
|
||||
for (const auto opt : m_options_list)
|
||||
{
|
||||
int init_id = i <= opt_init_max_id ? i : 0;
|
||||
if (opt_cur->values[i] != opt_init->values[init_id])
|
||||
vec->emplace_back(opt_key + "#" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
const wxColour *color = &m_sys_label_clr;
|
||||
|
||||
template<class T>
|
||||
void add_correct_opts_to_sys_options(const std::string &opt_key, std::vector<std::string> *vec, TabPrinter *tab)
|
||||
{
|
||||
const Preset* sys_preset = tab->m_presets->get_selected_preset_parent();
|
||||
if (sys_preset == nullptr)
|
||||
return;
|
||||
T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key));
|
||||
const T *opt_sys = static_cast<const T*>(sys_preset->config.option(opt_key));
|
||||
int opt_max_id = opt_sys->values.size()-1;
|
||||
for (int i = 0; i < opt_cur->values.size(); i++)
|
||||
{
|
||||
int init_id = i <= opt_max_id ? i : 0;
|
||||
if (opt_cur->values[i] == opt_sys->values[init_id])
|
||||
vec->emplace_back(opt_key + "#" + std::to_string(i));
|
||||
// value isn't equal to system value
|
||||
if ((opt.second & osSystemValue) == 0){
|
||||
// value is equal to last saved
|
||||
if ((opt.second & osInitValue) != 0)
|
||||
color = &m_default_text_clr;
|
||||
// value is modified
|
||||
else
|
||||
color = &m_modified_label_clr;
|
||||
}
|
||||
if (opt.first == "bed_shape" || opt.first == "compatible_printers") {
|
||||
if (m_colored_Label != nullptr) {
|
||||
m_colored_Label->SetForegroundColour(*color);
|
||||
m_colored_Label->Refresh(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Field* field = get_field(opt.first);
|
||||
if (field == nullptr) continue;
|
||||
field->set_label_colour_force(color);
|
||||
}
|
||||
Thaw();
|
||||
|
||||
auto cur_item = m_treectrl->GetFirstVisibleItem();
|
||||
while (cur_item){
|
||||
auto title = m_treectrl->GetItemText(cur_item);
|
||||
for (auto page : m_pages)
|
||||
{
|
||||
if (page->title() != title)
|
||||
continue;
|
||||
|
||||
const wxColor *clr = !page->m_is_nonsys_values ? &m_sys_label_clr :
|
||||
page->m_is_modified_values ? &m_modified_label_clr :
|
||||
&m_default_text_clr;
|
||||
|
||||
m_treectrl->SetItemTextColour(cur_item, *clr);
|
||||
break;
|
||||
}
|
||||
cur_item = m_treectrl->GetNextVisible(cur_item);
|
||||
}
|
||||
}
|
||||
|
||||
// Update UI according to changes
|
||||
void Tab::update_changed_ui()
|
||||
{
|
||||
auto dirty_options = m_presets->current_dirty_options();
|
||||
if (m_postpone_update_ui)
|
||||
return;
|
||||
|
||||
if (name() == "printer"){
|
||||
// Update dirty_options in case changes of Extruder's options
|
||||
const bool is_printer_type = (name() == "printer");
|
||||
auto dirty_options = m_presets->current_dirty_options(is_printer_type);
|
||||
auto nonsys_options = m_presets->current_different_from_parent_options(is_printer_type);
|
||||
if (is_printer_type){
|
||||
TabPrinter* tab = static_cast<TabPrinter*>(this);
|
||||
m_dirty_options.resize(0);
|
||||
for (auto opt_key : dirty_options)
|
||||
{
|
||||
if (opt_key == "bed_shape"){ m_dirty_options.emplace_back(opt_key); continue; }
|
||||
switch (m_config->option(opt_key)->type())
|
||||
{
|
||||
case coInts: add_correct_opts_to_dirty_options<ConfigOptionInts >(opt_key, &m_dirty_options, tab); break;
|
||||
case coBools: add_correct_opts_to_dirty_options<ConfigOptionBools >(opt_key, &m_dirty_options, tab); break;
|
||||
case coFloats: add_correct_opts_to_dirty_options<ConfigOptionFloats >(opt_key, &m_dirty_options, tab); break;
|
||||
case coStrings: add_correct_opts_to_dirty_options<ConfigOptionStrings >(opt_key, &m_dirty_options, tab); break;
|
||||
case coPercents:add_correct_opts_to_dirty_options<ConfigOptionPercents >(opt_key, &m_dirty_options, tab); break;
|
||||
case coPoints: add_correct_opts_to_dirty_options<ConfigOptionPoints >(opt_key, &m_dirty_options, tab); break;
|
||||
default: m_dirty_options.emplace_back(opt_key); break;
|
||||
}
|
||||
}
|
||||
if (tab->m_initial_extruders_count != tab->m_extruders_count)
|
||||
m_dirty_options.emplace_back("extruders_count");
|
||||
|
||||
m_sys_options.resize(0);
|
||||
const auto sys_preset = m_presets->get_selected_preset_parent();
|
||||
if (sys_preset){
|
||||
for (auto opt_key : m_config->keys())
|
||||
{
|
||||
if (opt_key == "bed_shape"){
|
||||
if (*tab->m_config->option(opt_key) == *sys_preset->config.option(opt_key))
|
||||
m_sys_options.emplace_back(opt_key);
|
||||
continue;
|
||||
}
|
||||
switch (m_config->option(opt_key)->type())
|
||||
{
|
||||
case coInts: add_correct_opts_to_sys_options<ConfigOptionInts >(opt_key, &m_sys_options, tab); break;
|
||||
case coBools: add_correct_opts_to_sys_options<ConfigOptionBools >(opt_key, &m_sys_options, tab); break;
|
||||
case coFloats: add_correct_opts_to_sys_options<ConfigOptionFloats >(opt_key, &m_sys_options, tab); break;
|
||||
case coStrings: add_correct_opts_to_sys_options<ConfigOptionStrings >(opt_key, &m_sys_options, tab); break;
|
||||
case coPercents:add_correct_opts_to_sys_options<ConfigOptionPercents>(opt_key, &m_sys_options, tab); break;
|
||||
case coPoints: add_correct_opts_to_sys_options<ConfigOptionPoints >(opt_key, &m_sys_options, tab); break;
|
||||
default:{
|
||||
const ConfigOption *opt_cur = tab->m_config->option(opt_key);
|
||||
const ConfigOption *opt_sys = sys_preset->config.option(opt_key);
|
||||
if (opt_cur != nullptr && opt_sys != nullptr && *opt_cur == *opt_sys)
|
||||
m_sys_options.emplace_back(opt_key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tab->m_sys_extruders_count == tab->m_extruders_count)
|
||||
m_sys_options.emplace_back("extruders_count");
|
||||
}
|
||||
}
|
||||
else{
|
||||
m_sys_options = m_presets->system_equal_options();
|
||||
m_dirty_options = dirty_options;
|
||||
dirty_options.emplace_back("extruders_count");
|
||||
if (tab->m_sys_extruders_count != tab->m_extruders_count)
|
||||
nonsys_options.emplace_back("extruders_count");
|
||||
}
|
||||
|
||||
for (auto& it : m_options_list)
|
||||
it.second = m_opt_status_value;
|
||||
|
||||
for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue;
|
||||
for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue;
|
||||
|
||||
Freeze();
|
||||
//update options "decoration"
|
||||
for (const auto opt_key : m_full_options_list)
|
||||
for (const auto opt : m_options_list)
|
||||
{
|
||||
bool is_nonsys_value = false;
|
||||
bool is_modified_value = true;
|
||||
std::string sys_icon = "sys_lock.png";
|
||||
std::string icon = "action_undo.png";
|
||||
wxColour color = get_sys_label_clr();
|
||||
if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) {
|
||||
const wxBitmap *sys_icon = &m_bmp_value_lock;
|
||||
const wxBitmap *icon = &m_bmp_value_revert;
|
||||
|
||||
const wxColour *color = &m_sys_label_clr;
|
||||
|
||||
const wxString *sys_tt = &m_tt_value_lock;
|
||||
const wxString *tt = &m_tt_value_revert;
|
||||
|
||||
// value isn't equal to system value
|
||||
if ((opt.second & osSystemValue) == 0){
|
||||
is_nonsys_value = true;
|
||||
sys_icon = m_nonsys_btn_icon;
|
||||
if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
|
||||
color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
sys_icon = m_bmp_non_system;
|
||||
sys_tt = m_tt_non_system;
|
||||
// value is equal to last saved
|
||||
if ((opt.second & osInitValue) != 0)
|
||||
color = &m_default_text_clr;
|
||||
// value is modified
|
||||
else
|
||||
color = get_modified_label_clr();
|
||||
color = &m_modified_label_clr;
|
||||
}
|
||||
if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
|
||||
if ((opt.second & osInitValue) != 0)
|
||||
{
|
||||
is_modified_value = false;
|
||||
icon = "bullet_white.png";
|
||||
icon = &m_bmp_white_bullet;
|
||||
tt = &m_tt_white_bullet;
|
||||
}
|
||||
if (opt_key == "bed_shape" || opt_key == "compatible_printers") {
|
||||
if (opt.first == "bed_shape" || opt.first == "compatible_printers") {
|
||||
if (m_colored_Label != nullptr) {
|
||||
m_colored_Label->SetForegroundColour(color);
|
||||
m_colored_Label->SetForegroundColour(*color);
|
||||
m_colored_Label->Refresh(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Field* field = get_field(opt_key);
|
||||
Field* field = get_field(opt.first);
|
||||
if (field == nullptr) continue;
|
||||
field->m_is_nonsys_value = is_nonsys_value;
|
||||
field->m_is_modified_value = is_modified_value;
|
||||
field->m_Undo_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG));
|
||||
field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(sys_icon)), wxBITMAP_TYPE_PNG));
|
||||
if (field->m_Label != nullptr){
|
||||
field->m_Label->SetForegroundColour(color);
|
||||
field->m_Label->Refresh(true);
|
||||
}
|
||||
field->set_undo_bitmap(icon);
|
||||
field->set_undo_to_sys_bitmap(sys_icon);
|
||||
field->set_undo_tooltip(tt);
|
||||
field->set_undo_to_sys_tooltip(sys_tt);
|
||||
field->set_label_colour(color);
|
||||
}
|
||||
Thaw();
|
||||
|
||||
|
@ -376,67 +416,53 @@ void Tab::update_changed_ui()
|
|||
});
|
||||
}
|
||||
|
||||
void Tab::init_options_list()
|
||||
{
|
||||
if (!m_options_list.empty())
|
||||
m_options_list.clear();
|
||||
|
||||
for (const auto opt_key : m_config->keys())
|
||||
m_options_list.emplace(opt_key, m_opt_status_value);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void add_correct_opts_to_full_options_list(const std::string &opt_key, std::vector<std::string> *vec, TabPrinter *tab)
|
||||
void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, TabPrinter *tab, const int& value)
|
||||
{
|
||||
T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key));
|
||||
for (int i = 0; i < opt_cur->values.size(); i++)
|
||||
vec->emplace_back(opt_key + "#" + std::to_string(i));
|
||||
map.emplace(opt_key + "#" + std::to_string(i), value);
|
||||
}
|
||||
|
||||
void Tab::update_full_options_list()
|
||||
void TabPrinter::init_options_list()
|
||||
{
|
||||
if (!m_full_options_list.empty())
|
||||
m_full_options_list.resize(0);
|
||||
if (!m_options_list.empty())
|
||||
m_options_list.clear();
|
||||
|
||||
if (m_name != "printer"){
|
||||
m_full_options_list = m_config->keys();
|
||||
return;
|
||||
}
|
||||
|
||||
TabPrinter* tab = static_cast<TabPrinter*>(this);
|
||||
for (const auto opt_key : m_config->keys())
|
||||
{
|
||||
if (opt_key == "bed_shape"){
|
||||
m_full_options_list.emplace_back(opt_key);
|
||||
m_options_list.emplace(opt_key, m_opt_status_value);
|
||||
continue;
|
||||
}
|
||||
switch (m_config->option(opt_key)->type())
|
||||
{
|
||||
case coInts: add_correct_opts_to_full_options_list<ConfigOptionInts >(opt_key, &m_full_options_list, tab); break;
|
||||
case coBools: add_correct_opts_to_full_options_list<ConfigOptionBools >(opt_key, &m_full_options_list, tab); break;
|
||||
case coFloats: add_correct_opts_to_full_options_list<ConfigOptionFloats >(opt_key, &m_full_options_list, tab); break;
|
||||
case coStrings: add_correct_opts_to_full_options_list<ConfigOptionStrings >(opt_key, &m_full_options_list, tab); break;
|
||||
case coPercents:add_correct_opts_to_full_options_list<ConfigOptionPercents >(opt_key, &m_full_options_list, tab); break;
|
||||
case coPoints: add_correct_opts_to_full_options_list<ConfigOptionPoints >(opt_key, &m_full_options_list, tab); break;
|
||||
default: m_full_options_list.emplace_back(opt_key); break;
|
||||
case coInts: add_correct_opts_to_options_list<ConfigOptionInts >(opt_key, m_options_list, this, m_opt_status_value); break;
|
||||
case coBools: add_correct_opts_to_options_list<ConfigOptionBools >(opt_key, m_options_list, this, m_opt_status_value); break;
|
||||
case coFloats: add_correct_opts_to_options_list<ConfigOptionFloats >(opt_key, m_options_list, this, m_opt_status_value); break;
|
||||
case coStrings: add_correct_opts_to_options_list<ConfigOptionStrings >(opt_key, m_options_list, this, m_opt_status_value); break;
|
||||
case coPercents:add_correct_opts_to_options_list<ConfigOptionPercents >(opt_key, m_options_list, this, m_opt_status_value); break;
|
||||
case coPoints: add_correct_opts_to_options_list<ConfigOptionPoints >(opt_key, m_options_list, this, m_opt_status_value); break;
|
||||
default: m_options_list.emplace(opt_key, m_opt_status_value); break;
|
||||
}
|
||||
}
|
||||
m_full_options_list.emplace_back("extruders_count");
|
||||
}
|
||||
|
||||
void Tab::update_sys_ui_after_sel_preset()
|
||||
{
|
||||
for (const auto opt_key : m_full_options_list){
|
||||
Field* field = get_field(opt_key);
|
||||
if (field != nullptr){
|
||||
field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG));
|
||||
field->m_is_nonsys_value = true;
|
||||
if (field->m_Label != nullptr){
|
||||
field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
field->m_Label->Refresh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_sys_options.resize(0);
|
||||
m_options_list.emplace("extruders_count", m_opt_status_value);
|
||||
}
|
||||
|
||||
void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page)
|
||||
{
|
||||
if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
|
||||
sys_page = false;
|
||||
if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
|
||||
modified_page = true;
|
||||
auto opt = m_options_list.find(opt_key);
|
||||
if (sys_page) sys_page = (opt->second & osSystemValue) != 0;
|
||||
if (!modified_page) modified_page = (opt->second & osInitValue) == 0;
|
||||
}
|
||||
|
||||
void Tab::update_changed_tree_ui()
|
||||
|
@ -457,7 +483,7 @@ void Tab::update_changed_tree_ui()
|
|||
get_sys_and_mod_flags(opt_key, sys_page, modified_page);
|
||||
}
|
||||
}
|
||||
if (title == _("Dependencies")){
|
||||
if (title == _("Dependencies") && name() != "printer"){
|
||||
get_sys_and_mod_flags("compatible_printers", sys_page, modified_page);
|
||||
}
|
||||
for (auto group : page->m_optgroups)
|
||||
|
@ -469,12 +495,13 @@ void Tab::update_changed_tree_ui()
|
|||
get_sys_and_mod_flags(opt_key, sys_page, modified_page);
|
||||
}
|
||||
}
|
||||
if (sys_page)
|
||||
m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr());
|
||||
else if (modified_page)
|
||||
m_treectrl->SetItemTextColour(cur_item, get_modified_label_clr());
|
||||
else
|
||||
m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
|
||||
const wxColor *clr = sys_page ? &m_sys_label_clr :
|
||||
modified_page ? &m_modified_label_clr :
|
||||
&m_default_text_clr;
|
||||
|
||||
if (page->set_item_colour(clr))
|
||||
m_treectrl->SetItemTextColour(cur_item, *clr);
|
||||
|
||||
page->m_is_nonsys_values = !sys_page;
|
||||
page->m_is_modified_values = modified_page;
|
||||
|
@ -493,80 +520,62 @@ void Tab::update_changed_tree_ui()
|
|||
|
||||
void Tab::update_undo_buttons()
|
||||
{
|
||||
const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" : "action_undo.png";
|
||||
const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon : "sys_lock.png";
|
||||
m_undo_btn->SetBitmap(m_is_modified_values ? m_bmp_value_revert : m_bmp_white_bullet);
|
||||
m_undo_to_sys_btn->SetBitmap(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock);
|
||||
|
||||
m_undo_btn->SetBitmap(wxBitmap(from_u8(var(undo_icon)), wxBITMAP_TYPE_PNG));
|
||||
m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(undo_to_sys_icon)), wxBITMAP_TYPE_PNG));
|
||||
m_undo_btn->SetToolTip(m_is_modified_values ? m_ttg_value_revert : m_ttg_white_bullet);
|
||||
m_undo_to_sys_btn->SetToolTip(m_is_nonsys_values ? *m_ttg_non_system : m_ttg_value_lock);
|
||||
}
|
||||
|
||||
void Tab::on_back_to_initial_value()
|
||||
void Tab::on_roll_back_value(const bool to_sys /*= true*/)
|
||||
{
|
||||
if (!m_is_modified_values) return;
|
||||
int os;
|
||||
if (to_sys) {
|
||||
if (!m_is_nonsys_values) return;
|
||||
os = osSystemValue;
|
||||
}
|
||||
else {
|
||||
if (!m_is_modified_values) return;
|
||||
os = osInitValue;
|
||||
}
|
||||
|
||||
m_postpone_update_ui = true;
|
||||
|
||||
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
|
||||
for (auto page : m_pages)
|
||||
if (page->title() == selection) {
|
||||
for (auto group : page->m_optgroups){
|
||||
if (group->title == _("Capabilities")){
|
||||
if (find(m_dirty_options.begin(), m_dirty_options.end(), "extruders_count") != m_dirty_options.end())
|
||||
group->back_to_initial_value("extruders_count");
|
||||
if ((m_options_list["extruders_count"] & os) == 0)
|
||||
to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count");
|
||||
}
|
||||
if (group->title == _("Size and coordinates")){
|
||||
if (find(m_dirty_options.begin(), m_dirty_options.end(), "bed_shape") != m_dirty_options.end())
|
||||
group->back_to_initial_value("bed_shape");
|
||||
}
|
||||
if (group->title == _("Profile dependencies")){
|
||||
if (find(m_dirty_options.begin(), m_dirty_options.end(), "compatible_printers") != m_dirty_options.end())
|
||||
group->back_to_initial_value("compatible_printers");
|
||||
if ((m_options_list["bed_shape"] & os) == 0){
|
||||
to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape");
|
||||
load_key_value("bed_shape", true/*some value*/, true);
|
||||
}
|
||||
|
||||
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
|
||||
m_compatible_printers_checkbox->SetValue(is_empty);
|
||||
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
|
||||
}
|
||||
if (group->title == _("Profile dependencies") && name() != "printer"){
|
||||
if ((m_options_list["compatible_printers"] & os) == 0){
|
||||
to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers");
|
||||
load_key_value("compatible_printers", true/*some value*/, true);
|
||||
|
||||
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
|
||||
m_compatible_printers_checkbox->SetValue(is_empty);
|
||||
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
|
||||
}
|
||||
}
|
||||
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
|
||||
const std::string& opt_key = it->first;
|
||||
if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
|
||||
group->back_to_initial_value(opt_key);
|
||||
if ((m_options_list[opt_key] & os) == 0)
|
||||
to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
update_changed_ui();
|
||||
}
|
||||
|
||||
void Tab::on_back_to_sys_value()
|
||||
{
|
||||
if (!m_is_nonsys_values) return;
|
||||
|
||||
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
|
||||
for (auto page : m_pages)
|
||||
if (page->title() == selection) {
|
||||
for (auto group : page->m_optgroups) {
|
||||
if (group->title == _("Capabilities")){
|
||||
if (find(m_sys_options.begin(), m_sys_options.end(), "extruders_count") == m_sys_options.end())
|
||||
group->back_to_sys_value("extruders_count");
|
||||
}
|
||||
if (group->title == _("Size and coordinates")){
|
||||
if (find(m_sys_options.begin(), m_sys_options.end(), "bed_shape") == m_sys_options.end())
|
||||
group->back_to_sys_value("bed_shape");
|
||||
}
|
||||
if (group->title == _("Profile dependencies")){
|
||||
if (find(m_sys_options.begin(), m_sys_options.end(), "compatible_printers") == m_sys_options.end())
|
||||
group->back_to_sys_value("compatible_printers");
|
||||
|
||||
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
|
||||
m_compatible_printers_checkbox->SetValue(is_empty);
|
||||
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
|
||||
}
|
||||
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
|
||||
const std::string& opt_key = it->first;
|
||||
if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
|
||||
group->back_to_sys_value(opt_key);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_postpone_update_ui = false;
|
||||
update_changed_ui();
|
||||
}
|
||||
|
||||
|
@ -581,7 +590,7 @@ void Tab::update_dirty(){
|
|||
|
||||
void Tab::update_tab_ui()
|
||||
{
|
||||
m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
|
||||
m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
|
||||
// update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
|
||||
// update_presetsctrl(m_presetctrl, m_show_incompatible_presets);
|
||||
}
|
||||
|
@ -636,12 +645,15 @@ bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value)
|
|||
|
||||
// To be called by custom widgets, load a value into a config,
|
||||
// update the preset selection boxes (the dirty flags)
|
||||
void Tab::load_key_value(const std::string& opt_key, const boost::any& value)
|
||||
// If value is saved before calling this function, put saved_value = true,
|
||||
// and value can be some random value because in this case it will not been used
|
||||
void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value /*= false*/)
|
||||
{
|
||||
change_opt_value(*m_config, opt_key, value);
|
||||
if (!saved_value) change_opt_value(*m_config, opt_key, value);
|
||||
// Mark the print & filament enabled if they are compatible with the currently selected preset.
|
||||
if (opt_key.compare("compatible_printers") == 0) {
|
||||
m_preset_bundle->update_compatible_with_printer(0);
|
||||
// Don't select another profile if this profile happens to become incompatible.
|
||||
m_preset_bundle->update_compatible_with_printer(false);
|
||||
}
|
||||
m_presets->update_dirty_ui(m_presets_choice);
|
||||
on_presets_changed();
|
||||
|
@ -716,13 +728,52 @@ void Tab::on_presets_changed()
|
|||
event.SetString(m_name);
|
||||
g_wxMainFrame->ProcessWindowEvent(event);
|
||||
}
|
||||
update_preset_description_line();
|
||||
}
|
||||
|
||||
void Tab::update_preset_description_line()
|
||||
{
|
||||
const Preset* parent = m_presets->get_selected_preset_parent();
|
||||
const wxString description_line = parent == nullptr ?
|
||||
_(L("It's default preset")) : parent == &m_presets->get_selected_preset() ?
|
||||
_(L("It's system preset")) :
|
||||
_(L("Current preset is inherited from")) + ":\n" + parent->name;
|
||||
m_parent_preset_description_line->SetText(description_line);
|
||||
const Preset& preset = m_presets->get_edited_preset();
|
||||
|
||||
wxString description_line = preset.is_default ?
|
||||
_(L("It's a default preset.")) : preset.is_system ?
|
||||
_(L("It's a system preset.")) :
|
||||
_(L("Current preset is inherited from ")) + (parent == nullptr ?
|
||||
"default preset." :
|
||||
":\n\t" + parent->name);
|
||||
|
||||
if (preset.is_default || preset.is_system)
|
||||
description_line += "\n\t" + _(L("It can't be deleted or modified. ")) +
|
||||
"\n\t" + _(L("Any modifications should be saved as a new preset inherited from this one. ")) +
|
||||
"\n\t" + _(L("To do that please specify a new name for the preset."));
|
||||
|
||||
if (parent && parent->vendor)
|
||||
{
|
||||
description_line += "\n\n" + _(L("Additional information:")) + "\n";
|
||||
description_line += "\t" + _(L("vendor")) + ": " + (name()=="printer" ? "\n\t\t" : "") + parent->vendor->name +
|
||||
", ver: " + parent->vendor->config_version.to_string();
|
||||
if (name() == "printer"){
|
||||
const std::string &printer_model = preset.config.opt_string("printer_model");
|
||||
const std::string &default_print_profile = preset.config.opt_string("default_print_profile");
|
||||
const std::vector<std::string> &default_filament_profiles = preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
|
||||
if (!printer_model.empty())
|
||||
description_line += "\n\n\t" + _(L("printer model")) + ": \n\t\t" + printer_model;
|
||||
if (!default_print_profile.empty())
|
||||
description_line += "\n\n\t" + _(L("default print profile")) + ": \n\t\t" + default_print_profile;
|
||||
if (!default_filament_profiles.empty())
|
||||
{
|
||||
description_line += "\n\n\t" + _(L("default filament profile")) + ": \n\t\t";
|
||||
for (auto& profile : default_filament_profiles){
|
||||
if (&profile != &*default_filament_profiles.begin())
|
||||
description_line += ", ";
|
||||
description_line += profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_parent_preset_description_line->SetText(description_line, false);
|
||||
}
|
||||
|
||||
void Tab::update_frequently_changed_parameters()
|
||||
|
@ -1011,29 +1062,6 @@ void TabPrint::update()
|
|||
on_value_change("fill_density", fill_density);
|
||||
}
|
||||
|
||||
auto first_layer_height = m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value;
|
||||
auto layer_height = m_config->opt_float("layer_height");
|
||||
if (m_config->opt_bool("wipe_tower") &&
|
||||
(first_layer_height != 0.2 || layer_height < 0.15 || layer_height > 0.35)) {
|
||||
wxString msg_text = _(L("The Wipe Tower currently supports only:\n"
|
||||
"- first layer height 0.2mm\n"
|
||||
"- layer height from 0.15mm to 0.35mm\n"
|
||||
"\nShall I adjust those settings in order to enable the Wipe Tower?"));
|
||||
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO);
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
if (dialog->ShowModal() == wxID_YES) {
|
||||
const auto &val = *m_config->option<ConfigOptionFloatOrPercent>("first_layer_height");
|
||||
auto percent = val.percent;
|
||||
new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, percent));
|
||||
|
||||
if (m_config->opt_float("layer_height") < 0.15) new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.15));
|
||||
if (m_config->opt_float("layer_height") > 0.35) new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.35));
|
||||
}
|
||||
else
|
||||
new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false));
|
||||
load_config(new_conf);
|
||||
}
|
||||
|
||||
if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") &&
|
||||
m_config->opt_float("support_material_contact_distance") > 0. &&
|
||||
(m_config->opt_int("support_material_extruder") != 0 || m_config->opt_int("support_material_interface_extruder") != 0)) {
|
||||
|
@ -1283,7 +1311,7 @@ void TabFilament::build()
|
|||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Toolchange behaviour")));
|
||||
optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers")));
|
||||
optgroup->append_single_option_line("filament_loading_speed");
|
||||
optgroup->append_single_option_line("filament_unloading_speed");
|
||||
optgroup->append_single_option_line("filament_toolchange_delay");
|
||||
|
@ -1353,6 +1381,7 @@ void TabFilament::reload_config(){
|
|||
|
||||
void TabFilament::update()
|
||||
{
|
||||
Freeze();
|
||||
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
|
||||
m_cooling_description_line->SetText(text);
|
||||
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
|
||||
|
@ -1366,6 +1395,7 @@ void TabFilament::update()
|
|||
|
||||
for (auto el : { "min_fan_speed", "disable_fan_first_layers" })
|
||||
get_field(el)->toggle(fan_always_on);
|
||||
Thaw();
|
||||
}
|
||||
|
||||
void TabFilament::OnActivate()
|
||||
|
@ -1381,7 +1411,7 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex
|
|||
(*StaticText)->SetFont(font);
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(*StaticText);
|
||||
sizer->Add(*StaticText, 1, wxEXPAND|wxALL, 0);
|
||||
return sizer;
|
||||
}
|
||||
|
||||
|
@ -1395,6 +1425,9 @@ void TabPrinter::build()
|
|||
m_presets = &m_preset_bundle->printers;
|
||||
load_initial_data();
|
||||
|
||||
// to avoid redundant memory allocation / deallocation during extruders count changing
|
||||
m_pages.reserve(30);
|
||||
|
||||
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"));
|
||||
m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size();
|
||||
const Preset* parent_preset = m_presets->get_selected_preset_parent();
|
||||
|
@ -1672,12 +1705,34 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){
|
|||
}
|
||||
|
||||
void TabPrinter::build_extruder_pages(){
|
||||
for (auto extruder_idx = m_extruder_pages.size(); extruder_idx < m_extruders_count; ++extruder_idx){
|
||||
size_t n_before_extruders = 2; // Count of pages before Extruder pages
|
||||
size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page
|
||||
|
||||
if (m_extruders_count_old == m_extruders_count || m_extruders_count <= 2)
|
||||
{
|
||||
// if we have a single extruder MM setup, add a page with configuration options:
|
||||
for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already
|
||||
if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
|
||||
m_pages.erase(m_pages.begin() + i);
|
||||
break;
|
||||
}
|
||||
if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material")) {
|
||||
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
|
||||
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true);
|
||||
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
|
||||
optgroup->append_single_option_line("cooling_tube_retraction");
|
||||
optgroup->append_single_option_line("cooling_tube_length");
|
||||
optgroup->append_single_option_line("parking_pos_retraction");
|
||||
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){
|
||||
//# build page
|
||||
char buf[MIN_BUF_LENGTH_FOR_L];
|
||||
sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1);
|
||||
auto page = add_options_page(from_u8(buf), "funnel.png", true);
|
||||
m_extruder_pages.push_back(page);
|
||||
m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page);
|
||||
|
||||
auto optgroup = page->new_optgroup(_(L("Size")));
|
||||
optgroup->append_single_option_line("nozzle_diameter", extruder_idx);
|
||||
|
@ -1715,36 +1770,11 @@ void TabPrinter::build_extruder_pages(){
|
|||
}
|
||||
|
||||
// # remove extra pages
|
||||
if (m_extruders_count <= m_extruder_pages.size()) {
|
||||
m_extruder_pages.resize(m_extruders_count);
|
||||
}
|
||||
if (m_extruders_count < m_extruders_count_old)
|
||||
m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count,
|
||||
m_pages.begin() + n_before_extruders + m_extruders_count_old);
|
||||
|
||||
// # rebuild page list
|
||||
PageShp page_note = m_pages.back();
|
||||
m_pages.pop_back();
|
||||
while (m_pages.back()->title().find(_(L("Extruder"))) != std::string::npos)
|
||||
m_pages.pop_back();
|
||||
for (auto page_extruder : m_extruder_pages)
|
||||
m_pages.push_back(page_extruder);
|
||||
m_pages.push_back(page_note);
|
||||
|
||||
{
|
||||
// if we have a single extruder MM setup, add a page with configuration options:
|
||||
for (int i=0;i<m_pages.size();++i) // first make sure it's not there already
|
||||
if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
|
||||
m_pages.erase(m_pages.begin()+i);
|
||||
break;
|
||||
}
|
||||
if ( m_extruder_pages.size()>1 && m_config->opt_bool("single_extruder_multi_material")) {
|
||||
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
|
||||
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png",true);
|
||||
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
|
||||
optgroup->append_single_option_line("cooling_tube_retraction");
|
||||
optgroup->append_single_option_line("cooling_tube_length");
|
||||
optgroup->append_single_option_line("parking_pos_retraction");
|
||||
m_pages.insert(m_pages.begin()+1,page);
|
||||
}
|
||||
}
|
||||
m_extruders_count_old = m_extruders_count;
|
||||
|
||||
rebuild_page_tree();
|
||||
}
|
||||
|
@ -1846,14 +1876,15 @@ void Tab::load_current_preset()
|
|||
{
|
||||
auto preset = m_presets->get_edited_preset();
|
||||
|
||||
preset.is_default ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true);
|
||||
(preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true);
|
||||
update();
|
||||
// For the printer profile, generate the extruder pages.
|
||||
on_preset_loaded();
|
||||
// Reload preset pages with the new configuration values.
|
||||
reload_config();
|
||||
const Preset* parent = m_presets->get_selected_preset_parent();
|
||||
m_nonsys_btn_icon = parent == nullptr ? "bullet_white.png" : "sys_unlock.png";
|
||||
m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet;
|
||||
m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
|
||||
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
|
||||
|
||||
// use CallAfter because some field triggers schedule on_change calls using CallAfter,
|
||||
// and we don't want them to be called after this update_dirty() as they would mark the
|
||||
|
@ -1874,8 +1905,8 @@ void Tab::load_current_preset()
|
|||
static_cast<TabPrinter*>(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 :
|
||||
static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size();
|
||||
}
|
||||
update_sys_ui_after_sel_preset();
|
||||
update_full_options_list();
|
||||
m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue;
|
||||
init_options_list();
|
||||
update_changed_ui();
|
||||
});
|
||||
}
|
||||
|
@ -1887,11 +1918,13 @@ void Tab::rebuild_page_tree()
|
|||
// get label of the currently selected item
|
||||
auto selected = m_treectrl->GetItemText(m_treectrl->GetSelection());
|
||||
auto rootItem = m_treectrl->GetRootItem();
|
||||
m_treectrl->DeleteChildren(rootItem);
|
||||
|
||||
auto have_selection = 0;
|
||||
m_treectrl->DeleteChildren(rootItem);
|
||||
for (auto p : m_pages)
|
||||
{
|
||||
auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID());
|
||||
m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
|
||||
if (p->title() == selected) {
|
||||
m_disable_tree_sel_changed_event = 1;
|
||||
m_treectrl->SelectItem(itemId);
|
||||
|
@ -1899,7 +1932,7 @@ void Tab::rebuild_page_tree()
|
|||
have_selection = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!have_selection) {
|
||||
// this is triggered on first load, so we don't disable the sel change event
|
||||
m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem));
|
||||
|
@ -2015,6 +2048,8 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
|
|||
void Tab::OnTreeSelChange(wxTreeEvent& event)
|
||||
{
|
||||
if (m_disable_tree_sel_changed_event) return;
|
||||
wxWindowUpdateLocker noUpdates(this);
|
||||
|
||||
Page* page = nullptr;
|
||||
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
|
||||
for (auto p : m_pages)
|
||||
|
@ -2141,7 +2176,7 @@ void Tab::toggle_show_hide_incompatible()
|
|||
void Tab::update_show_hide_incompatible_button()
|
||||
{
|
||||
m_btn_hide_incompatible_presets->SetBitmap(m_show_incompatible_presets ?
|
||||
*m_bmp_show_incompatible_presets : *m_bmp_hide_incompatible_presets);
|
||||
m_bmp_show_incompatible_presets : m_bmp_hide_incompatible_presets);
|
||||
m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ?
|
||||
"Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." :
|
||||
"Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer.");
|
||||
|
@ -2200,9 +2235,9 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
|
|||
presets.Add(preset.name);
|
||||
}
|
||||
|
||||
auto dlg = new wxMultiChoiceDialog(parent,
|
||||
_(L("Select the printers this profile is compatible with.")),
|
||||
_(L("Compatible printers")), presets);
|
||||
wxMultiChoiceDialog dlg(parent,
|
||||
_(L("Select the printers this profile is compatible with.")),
|
||||
_(L("Compatible printers")), presets);
|
||||
// # Collect and set indices of printers marked as compatible.
|
||||
wxArrayInt selections;
|
||||
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(m_config->option("compatible_printers"));
|
||||
|
@ -2214,12 +2249,12 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
|
|||
selections.Add(idx);
|
||||
break;
|
||||
}
|
||||
dlg->SetSelections(selections);
|
||||
dlg.SetSelections(selections);
|
||||
std::vector<std::string> value;
|
||||
// Show the dialog.
|
||||
if (dlg->ShowModal() == wxID_OK) {
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
selections.Clear();
|
||||
selections = dlg->GetSelections();
|
||||
selections = dlg.GetSelections();
|
||||
for (auto idx : selections)
|
||||
value.push_back(presets[idx].ToStdString());
|
||||
if (value.empty()) {
|
||||
|
@ -2401,6 +2436,75 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
|
|||
ui->Thaw();
|
||||
}
|
||||
|
||||
void Tab::fill_icon_descriptions()
|
||||
{
|
||||
m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_lock, L("LOCKED LOCK;"
|
||||
"indicates that the settings are the same as the system values for the current option group")));
|
||||
|
||||
m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_unlock, L("UNLOCKED LOCK;"
|
||||
"indicates that some settings were changed and are not equal to the system values for "
|
||||
"the current option group.\n"
|
||||
"Click the UNLOCKED LOCK icon to reset all settings for current option group to "
|
||||
"the system values.")));
|
||||
|
||||
m_icon_descriptions.push_back(t_icon_description(&m_bmp_white_bullet, L("WHITE BULLET;"
|
||||
"for the left button: \tindicates a non-system preset,\n"
|
||||
"for the right button: \tindicates that the settings hasn't been modified.")));
|
||||
|
||||
m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_revert, L("BACK ARROW;"
|
||||
"indicates that the settings were changed and are not equal to the last saved preset for "
|
||||
"the current option group.\n"
|
||||
"Click the BACK ARROW icon to reset all settings for the current option group to "
|
||||
"the last saved preset.")));
|
||||
}
|
||||
|
||||
void Tab::set_tooltips_text()
|
||||
{
|
||||
// m_undo_to_sys_btn->SetToolTip(_(L( "LOCKED LOCK icon indicates that the settings are the same as the system values "
|
||||
// "for the current option group.\n"
|
||||
// "UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
|
||||
// "to the system values for the current option group.\n"
|
||||
// "WHITE BULLET icon indicates a non system preset.\n\n"
|
||||
// "Click the UNLOCKED LOCK icon to reset all settings for current option group to "
|
||||
// "the system values.")));
|
||||
//
|
||||
// m_undo_btn->SetToolTip(_(L( "WHITE BULLET icon indicates that the settings are the same as in the last saved"
|
||||
// "preset for the current option group.\n"
|
||||
// "BACK ARROW icon indicates that the settings were changed and are not equal to "
|
||||
// "the last saved preset for the current option group.\n\n"
|
||||
// "Click the BACK ARROW icon to reset all settings for the current option group to "
|
||||
// "the last saved preset.")));
|
||||
|
||||
// --- Tooltip text for reset buttons (for whole options group)
|
||||
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
|
||||
m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system values "
|
||||
"for the current option group"));
|
||||
m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
|
||||
"to the system values for the current option group.\n"
|
||||
"Click to reset all settings for current option group to the system values."));
|
||||
m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system preset."));
|
||||
m_ttg_non_system = &m_ttg_white_bullet_ns;
|
||||
// Text to be shown on the "Undo user changes" button next to each input field.
|
||||
m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved "
|
||||
"preset for the current option group."));
|
||||
m_ttg_value_revert = _(L("BACK ARROW icon indicates that the settings were changed and are not equal to "
|
||||
"the last saved preset for the current option group.\n"
|
||||
"Click to reset all settings for the current option group to the last saved preset."));
|
||||
|
||||
// --- Tooltip text for reset buttons (for each option in group)
|
||||
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
|
||||
m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system value."));
|
||||
m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal "
|
||||
"to the system value.\n"
|
||||
"Click to reset current value to the system value."));
|
||||
// m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset."));
|
||||
m_tt_non_system = &m_ttg_white_bullet_ns;
|
||||
// Text to be shown on the "Undo user changes" button next to each input field.
|
||||
m_tt_white_bullet = _(L("WHITE BULLET icon indicates that the value is the same as in the last saved preset."));
|
||||
m_tt_value_revert = _(L("BACK ARROW icon indicates that the value was changed and is not equal to the last saved preset.\n"
|
||||
"Click to reset current value to the last saved preset."));
|
||||
}
|
||||
|
||||
void Page::reload_config()
|
||||
{
|
||||
for (auto group : m_optgroups)
|
||||
|
@ -2459,10 +2563,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
|
|||
return static_cast<Tab*>(GetParent())->m_presets->get_selected_preset_parent() != nullptr;
|
||||
};
|
||||
|
||||
optgroup->nonsys_btn_icon = [this](){
|
||||
return static_cast<Tab*>(GetParent())->m_nonsys_btn_icon;
|
||||
};
|
||||
|
||||
vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10);
|
||||
m_optgroups.push_back(optgroup);
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef slic3r_Tab_hpp_
|
||||
#define slic3r_Tab_hpp_
|
||||
|
||||
// The "Expert" tab at the right of the main tabbed window.
|
||||
//
|
||||
// This file implements following packages:
|
||||
|
@ -34,6 +37,9 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
typedef std::pair<wxBitmap*, std::string> t_icon_description;
|
||||
typedef std::vector<std::pair<wxBitmap*, std::string>> t_icon_descriptions;
|
||||
|
||||
// Single Tab page containing a{ vsizer } of{ optgroups }
|
||||
// package Slic3r::GUI::Tab::Page;
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
|
@ -51,6 +57,7 @@ public:
|
|||
{
|
||||
Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_item_color = &get_label_clr_default();
|
||||
SetSizer(m_vsizer);
|
||||
}
|
||||
~Page(){}
|
||||
|
@ -71,6 +78,22 @@ public:
|
|||
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
|
||||
bool set_value(const t_config_option_key& opt_key, const boost::any& value);
|
||||
ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1);
|
||||
|
||||
bool set_item_colour(const wxColour *clr) {
|
||||
if (m_item_color != clr) {
|
||||
m_item_color = clr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const wxColour get_item_colour() {
|
||||
return *m_item_color;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
|
||||
const wxColour* m_item_color;
|
||||
};
|
||||
|
||||
// Slic3r::GUI::Tab;
|
||||
|
@ -85,8 +108,6 @@ protected:
|
|||
wxBitmapComboBox* m_presets_choice;
|
||||
wxBitmapButton* m_btn_save_preset;
|
||||
wxBitmapButton* m_btn_delete_preset;
|
||||
wxBitmap* m_bmp_show_incompatible_presets;
|
||||
wxBitmap* m_bmp_hide_incompatible_presets;
|
||||
wxBitmapButton* m_btn_hide_incompatible_presets;
|
||||
wxBoxSizer* m_hsizer;
|
||||
wxBoxSizer* m_left_sizer;
|
||||
|
@ -96,10 +117,51 @@ protected:
|
|||
wxButton* m_compatible_printers_btn;
|
||||
wxButton* m_undo_btn;
|
||||
wxButton* m_undo_to_sys_btn;
|
||||
wxButton* m_question_btn;
|
||||
|
||||
wxComboCtrl* m_cc_presets_choice;
|
||||
wxDataViewTreeCtrl* m_presetctrl;
|
||||
wxImageList* m_preset_icons;
|
||||
|
||||
// Cached bitmaps.
|
||||
// A "flag" icon to be displayned next to the preset name in the Tab's combo box.
|
||||
wxBitmap m_bmp_show_incompatible_presets;
|
||||
wxBitmap m_bmp_hide_incompatible_presets;
|
||||
// Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
|
||||
wxBitmap m_bmp_value_lock;
|
||||
wxBitmap m_bmp_value_unlock;
|
||||
wxBitmap m_bmp_white_bullet;
|
||||
// The following bitmap points to either m_bmp_value_unlock or m_bmp_white_bullet, depending on whether the current preset has a parent preset.
|
||||
wxBitmap *m_bmp_non_system;
|
||||
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
|
||||
wxBitmap m_bmp_value_revert;
|
||||
// wxBitmap m_bmp_value_unmodified;
|
||||
wxBitmap m_bmp_question;
|
||||
|
||||
// Colors for ui "decoration"
|
||||
wxColour m_sys_label_clr;
|
||||
wxColour m_modified_label_clr;
|
||||
wxColour m_default_text_clr;
|
||||
|
||||
// Tooltip text for reset buttons (for whole options group)
|
||||
wxString m_ttg_value_lock;
|
||||
wxString m_ttg_value_unlock;
|
||||
wxString m_ttg_white_bullet_ns;
|
||||
// The following text points to either m_ttg_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset.
|
||||
wxString *m_ttg_non_system;
|
||||
// Tooltip text to be shown on the "Undo user changes" button next to each input field.
|
||||
wxString m_ttg_white_bullet;
|
||||
wxString m_ttg_value_revert;
|
||||
|
||||
// Tooltip text for reset buttons (for each option in group)
|
||||
wxString m_tt_value_lock;
|
||||
wxString m_tt_value_unlock;
|
||||
// The following text points to either m_tt_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset.
|
||||
wxString *m_tt_non_system;
|
||||
// Tooltip text to be shown on the "Undo user changes" button next to each input field.
|
||||
wxString m_tt_white_bullet;
|
||||
wxString m_tt_value_revert;
|
||||
|
||||
int m_icon_count;
|
||||
std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index
|
||||
std::vector<PageShp> m_pages;
|
||||
|
@ -108,9 +170,11 @@ protected:
|
|||
bool m_no_controller;
|
||||
|
||||
std::vector<std::string> m_reload_dependent_tabs = {};
|
||||
std::vector<std::string> m_dirty_options = {};
|
||||
std::vector<std::string> m_sys_options = {};
|
||||
std::vector<std::string> m_full_options_list = {};
|
||||
enum OptStatus { osSystemValue = 1, osInitValue = 2 };
|
||||
std::map<std::string, int> m_options_list;
|
||||
int m_opt_status_value;
|
||||
|
||||
t_icon_descriptions m_icon_descriptions = {};
|
||||
|
||||
// The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType.
|
||||
wxEventType m_event_value_change = 0;
|
||||
|
@ -118,13 +182,15 @@ protected:
|
|||
|
||||
bool m_is_modified_values{ false };
|
||||
bool m_is_nonsys_values{ true };
|
||||
bool m_postpone_update_ui {false};
|
||||
|
||||
size_t m_selected_preset_item{ 0 };
|
||||
|
||||
public:
|
||||
PresetBundle* m_preset_bundle;
|
||||
bool m_show_btn_incompatible_presets = false;
|
||||
PresetCollection* m_presets;
|
||||
DynamicPrintConfig* m_config;
|
||||
std::string m_nonsys_btn_icon;
|
||||
ogStaticText* m_parent_preset_description_line;
|
||||
wxStaticText* m_colored_Label = nullptr;
|
||||
|
||||
|
@ -135,7 +201,9 @@ public:
|
|||
Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
||||
get_tabs_list().push_back(this);
|
||||
}
|
||||
~Tab() { delete_tab_from_list(this); }
|
||||
~Tab(){
|
||||
delete_tab_from_list(this);
|
||||
}
|
||||
|
||||
wxWindow* parent() const { return m_parent; }
|
||||
wxString title() const { return m_title; }
|
||||
|
@ -153,7 +221,7 @@ public:
|
|||
wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
|
||||
|
||||
void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible);
|
||||
void load_key_value(const std::string& opt_key, const boost::any& value);
|
||||
void load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value = false);
|
||||
void reload_compatible_printers_widget();
|
||||
|
||||
void OnTreeSelChange(wxTreeEvent& event);
|
||||
|
@ -164,15 +232,13 @@ public:
|
|||
void toggle_show_hide_incompatible();
|
||||
void update_show_hide_incompatible_button();
|
||||
void update_ui_from_settings();
|
||||
void update_labels_colour();
|
||||
void update_changed_ui();
|
||||
void update_full_options_list();
|
||||
void update_sys_ui_after_sel_preset();
|
||||
void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page);
|
||||
void update_changed_tree_ui();
|
||||
void update_undo_buttons();
|
||||
|
||||
void on_back_to_initial_value();
|
||||
void on_back_to_sys_value();
|
||||
void on_roll_back_value(const bool to_sys = false);
|
||||
|
||||
PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false);
|
||||
|
||||
|
@ -180,6 +246,7 @@ public:
|
|||
virtual void on_preset_loaded(){}
|
||||
virtual void build() = 0;
|
||||
virtual void update() = 0;
|
||||
virtual void init_options_list();
|
||||
void load_initial_data();
|
||||
void update_dirty();
|
||||
void update_tab_ui();
|
||||
|
@ -189,20 +256,22 @@ public:
|
|||
bool set_value(const t_config_option_key& opt_key, const boost::any& value);
|
||||
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText);
|
||||
bool current_preset_is_dirty();
|
||||
|
||||
DynamicPrintConfig* get_config() { return m_config; }
|
||||
PresetCollection* get_presets()
|
||||
{
|
||||
return m_presets;
|
||||
}
|
||||
PresetCollection* get_presets() { return m_presets; }
|
||||
std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; }
|
||||
size_t get_selected_preset_item() { return m_selected_preset_item; }
|
||||
|
||||
void on_value_change(const std::string& opt_key, const boost::any& value);
|
||||
|
||||
protected:
|
||||
void on_presets_changed();
|
||||
void update_preset_description_line();
|
||||
void update_frequently_changed_parameters();
|
||||
void update_wiping_button_visibility();
|
||||
void update_tab_presets(wxComboCtrl* ui, bool show_incompatible);
|
||||
void fill_icon_descriptions();
|
||||
void set_tooltips_text();
|
||||
};
|
||||
|
||||
//Slic3r::GUI::Tab::Print;
|
||||
|
@ -248,9 +317,9 @@ public:
|
|||
wxButton* m_octoprint_host_test_btn;
|
||||
|
||||
size_t m_extruders_count;
|
||||
size_t m_extruders_count_old = 0;
|
||||
size_t m_initial_extruders_count;
|
||||
size_t m_sys_extruders_count;
|
||||
std::vector<PageShp> m_extruder_pages;
|
||||
|
||||
TabPrinter() {}
|
||||
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
|
||||
|
@ -262,6 +331,7 @@ public:
|
|||
void extruders_count_changed(size_t extruders_count);
|
||||
void build_extruder_pages();
|
||||
void on_preset_loaded() override;
|
||||
void init_options_list() override;
|
||||
};
|
||||
|
||||
class SavePresetWindow :public wxDialog
|
||||
|
@ -280,3 +350,5 @@ public:
|
|||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif /* slic3r_Tab_hpp_ */
|
||||
|
|
|
@ -11,6 +11,7 @@ void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*con
|
|||
void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); }
|
||||
bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();}
|
||||
void TabIface::OnActivate() { return m_tab->OnActivate();}
|
||||
size_t TabIface::get_selected_preset_item() { return m_tab->get_selected_preset_item(); }
|
||||
std::string TabIface::title() { return m_tab->title().ToUTF8().data(); }
|
||||
DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); }
|
||||
PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; }
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef slic3r_TabIface_hpp_
|
||||
#define slic3r_TabIface_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
@ -27,9 +30,12 @@ public:
|
|||
DynamicPrintConfig* get_config();
|
||||
PresetCollection* get_presets();
|
||||
std::vector<std::string> get_dependent_tabs();
|
||||
size_t get_selected_preset_item();
|
||||
|
||||
protected:
|
||||
GUI::Tab *m_tab;
|
||||
};
|
||||
}; // namespace GUI
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_TabIface_hpp_ */
|
||||
|
|
196
xs/src/slic3r/GUI/UpdateDialogs.cpp
Normal file
196
xs/src/slic3r/GUI/UpdateDialogs.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
#include "UpdateDialogs.hpp"
|
||||
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/checkbox.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
|
||||
|
||||
// MsgUpdateSlic3r
|
||||
|
||||
MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) :
|
||||
MsgDialog(nullptr, _(L("Update available")), _(L("New version of Slic3r PE is available"))),
|
||||
ver_current(ver_current),
|
||||
ver_online(ver_online)
|
||||
{
|
||||
const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string());
|
||||
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url);
|
||||
|
||||
auto *text = new wxStaticText(this, wxID_ANY, _(L("To download, follow the link below.")));
|
||||
const auto link_width = link->GetSize().GetWidth();
|
||||
text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width);
|
||||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:"))));
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, ver_current.to_string()));
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:"))));
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string()));
|
||||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
content_sizer->Add(link);
|
||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||
|
||||
cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more")));
|
||||
content_sizer->Add(cbox);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
MsgUpdateSlic3r::~MsgUpdateSlic3r() {}
|
||||
|
||||
bool MsgUpdateSlic3r::disable_version_check() const
|
||||
{
|
||||
return cbox->GetValue();
|
||||
}
|
||||
|
||||
|
||||
// MsgUpdateConfig
|
||||
|
||||
MsgUpdateConfig::MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates) :
|
||||
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE)
|
||||
{
|
||||
auto *text = new wxStaticText(this, wxID_ANY, _(L(
|
||||
"Would you like to install it?\n\n"
|
||||
"Note that a full configuration snapshot will be created first. It can then be restored at any time "
|
||||
"should there be a problem with the new version.\n\n"
|
||||
"Updated configuration bundles:"
|
||||
)));
|
||||
text->Wrap(CONTENT_WIDTH);
|
||||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
for (const auto &update : updates) {
|
||||
auto *text_vendor = new wxStaticText(this, wxID_ANY, update.first);
|
||||
text_vendor->SetFont(boldfont);
|
||||
versions->Add(text_vendor);
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, update.second));
|
||||
}
|
||||
|
||||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||
|
||||
auto *btn_cancel = new wxButton(this, wxID_CANCEL);
|
||||
btn_sizer->Add(btn_cancel);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
auto *btn_ok = new wxButton(this, wxID_OK);
|
||||
btn_sizer->Add(btn_ok);
|
||||
btn_ok->SetFocus();
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
MsgUpdateConfig::~MsgUpdateConfig() {}
|
||||
|
||||
|
||||
// MsgDataIncompatible
|
||||
|
||||
MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats) :
|
||||
MsgDialog(nullptr, _(L("Slic3r incompatibility")), _(L("Slic3r configuration is incompatible")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), wxID_NONE)
|
||||
{
|
||||
auto *text = new wxStaticText(this, wxID_ANY, _(L(
|
||||
"This version of Slic3r PE is not compatible with currently installed configuration bundles.\n"
|
||||
"This probably happened as a result of running an older Slic3r PE after using a newer one.\n\n"
|
||||
|
||||
"You may either exit Slic3r and try again with a newer version, or you may re-run the initial configuration. "
|
||||
"Doing so will create a backup snapshot of the existing configuration before installing files compatible with this Slic3r.\n"
|
||||
)));
|
||||
text->Wrap(CONTENT_WIDTH);
|
||||
content_sizer->Add(text);
|
||||
|
||||
auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This Slic3r PE version: %s")), SLIC3R_VERSION));
|
||||
text2->Wrap(CONTENT_WIDTH);
|
||||
content_sizer->Add(text2);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *text3 = new wxStaticText(this, wxID_ANY, _(L("Incompatible bundles:")));
|
||||
text3->Wrap(CONTENT_WIDTH);
|
||||
content_sizer->Add(text3);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING);
|
||||
for (const auto &incompat : incompats) {
|
||||
auto *text_vendor = new wxStaticText(this, wxID_ANY, incompat.first);
|
||||
text_vendor->SetFont(boldfont);
|
||||
versions->Add(text_vendor);
|
||||
versions->Add(new wxStaticText(this, wxID_ANY, incompat.second));
|
||||
}
|
||||
|
||||
content_sizer->Add(versions);
|
||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||
|
||||
auto *btn_exit = new wxButton(this, wxID_EXIT, _(L("Exit Slic3r")));
|
||||
btn_sizer->Add(btn_exit);
|
||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||
auto *btn_reconf = new wxButton(this, wxID_REPLACE, _(L("Re-configure")));
|
||||
btn_sizer->Add(btn_reconf);
|
||||
btn_exit->SetFocus();
|
||||
|
||||
auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); };
|
||||
btn_exit->Bind(wxEVT_BUTTON, exiter);
|
||||
btn_reconf->Bind(wxEVT_BUTTON, exiter);
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
MsgDataIncompatible::~MsgDataIncompatible() {}
|
||||
|
||||
|
||||
// MsgDataLegacy
|
||||
|
||||
MsgDataLegacy::MsgDataLegacy() :
|
||||
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update")))
|
||||
{
|
||||
auto *text = new wxStaticText(this, wxID_ANY, wxString::Format(
|
||||
_(L(
|
||||
"Slic3r PE now uses an updated configuration structure.\n\n"
|
||||
|
||||
"So called 'System presets' have been introduced, which hold the built-in default settings for various "
|
||||
"printers. These System presets cannot be modified, instead, users now may create their "
|
||||
"own presets inheriting settings from one of the System presets.\n"
|
||||
"An inheriting preset may either inherit a particular value from its parent or override it with a customized value.\n\n"
|
||||
|
||||
"Please proceed with the %s that follows to set up the new presets "
|
||||
"and to choose whether to enable automatic preset updates."
|
||||
)),
|
||||
ConfigWizard::name()
|
||||
));
|
||||
text->Wrap(CONTENT_WIDTH);
|
||||
content_sizer->Add(text);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
auto *text2 = new wxStaticText(this, wxID_ANY, _(L("For more information please visit our wiki page:")));
|
||||
static const wxString url("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update");
|
||||
// The wiki page name is intentionally not localized:
|
||||
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, "Slic3r PE 1.40 configuration update", CONFIG_UPDATE_WIKI_URL);
|
||||
content_sizer->Add(text2);
|
||||
content_sizer->Add(link);
|
||||
content_sizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
Fit();
|
||||
}
|
||||
|
||||
MsgDataLegacy::~MsgDataLegacy() {}
|
||||
|
||||
|
||||
}
|
||||
}
|
81
xs/src/slic3r/GUI/UpdateDialogs.hpp
Normal file
81
xs/src/slic3r/GUI/UpdateDialogs.hpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
#ifndef slic3r_UpdateDialogs_hpp_
|
||||
#define slic3r_UpdateDialogs_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "slic3r/Utils/Semver.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
|
||||
class wxBoxSizer;
|
||||
class wxCheckBox;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
// A confirmation dialog listing configuration updates
|
||||
class MsgUpdateSlic3r : public MsgDialog
|
||||
{
|
||||
public:
|
||||
MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online);
|
||||
MsgUpdateSlic3r(MsgUpdateSlic3r &&) = delete;
|
||||
MsgUpdateSlic3r(const MsgUpdateSlic3r &) = delete;
|
||||
MsgUpdateSlic3r &operator=(MsgUpdateSlic3r &&) = delete;
|
||||
MsgUpdateSlic3r &operator=(const MsgUpdateSlic3r &) = delete;
|
||||
virtual ~MsgUpdateSlic3r();
|
||||
|
||||
// Tells whether the user checked the "don't bother me again" checkbox
|
||||
bool disable_version_check() const;
|
||||
|
||||
private:
|
||||
const Semver &ver_current;
|
||||
const Semver &ver_online;
|
||||
wxCheckBox *cbox;
|
||||
};
|
||||
|
||||
|
||||
// Confirmation dialog informing about configuration update. Lists updated bundles & their versions.
|
||||
class MsgUpdateConfig : public MsgDialog
|
||||
{
|
||||
public:
|
||||
// updates is a map of "vendor name" -> "version (comment)"
|
||||
MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates);
|
||||
MsgUpdateConfig(MsgUpdateConfig &&) = delete;
|
||||
MsgUpdateConfig(const MsgUpdateConfig &) = delete;
|
||||
MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete;
|
||||
MsgUpdateConfig &operator=(const MsgUpdateConfig &) = delete;
|
||||
~MsgUpdateConfig();
|
||||
};
|
||||
|
||||
// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action.
|
||||
class MsgDataIncompatible : public MsgDialog
|
||||
{
|
||||
public:
|
||||
// incompats is a map of "vendor name" -> "version restrictions"
|
||||
MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats);
|
||||
MsgDataIncompatible(MsgDataIncompatible &&) = delete;
|
||||
MsgDataIncompatible(const MsgDataIncompatible &) = delete;
|
||||
MsgDataIncompatible &operator=(MsgDataIncompatible &&) = delete;
|
||||
MsgDataIncompatible &operator=(const MsgDataIncompatible &) = delete;
|
||||
~MsgDataIncompatible();
|
||||
};
|
||||
|
||||
// Informs about a legacy data directory - an update from Slic3r PE < 1.40
|
||||
class MsgDataLegacy : public MsgDialog
|
||||
{
|
||||
public:
|
||||
MsgDataLegacy();
|
||||
MsgDataLegacy(MsgDataLegacy &&) = delete;
|
||||
MsgDataLegacy(const MsgDataLegacy &) = delete;
|
||||
MsgDataLegacy &operator=(MsgDataLegacy &&) = delete;
|
||||
MsgDataLegacy &operator=(const MsgDataLegacy &) = delete;
|
||||
~MsgDataLegacy();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
559
xs/src/slic3r/Utils/PresetUpdater.cpp
Normal file
559
xs/src/slic3r/Utils/PresetUpdater.cpp
Normal file
|
@ -0,0 +1,559 @@
|
|||
#include "PresetUpdater.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/UpdateDialogs.hpp"
|
||||
#include "slic3r/GUI/ConfigWizard.hpp"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "slic3r/Config/Version.hpp"
|
||||
#include "slic3r/Config/Snapshot.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
using Slic3r::GUI::Config::Index;
|
||||
using Slic3r::GUI::Config::Version;
|
||||
using Slic3r::GUI::Config::Snapshot;
|
||||
using Slic3r::GUI::Config::SnapshotDB;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
enum {
|
||||
SLIC3R_VERSION_BODY_MAX = 256,
|
||||
};
|
||||
|
||||
static const char *INDEX_FILENAME = "index.idx";
|
||||
static const char *TMP_EXTENSION = ".download";
|
||||
|
||||
|
||||
struct Update
|
||||
{
|
||||
fs::path source;
|
||||
fs::path target;
|
||||
Version version;
|
||||
|
||||
Update(fs::path &&source, fs::path &&target, const Version &version) :
|
||||
source(std::move(source)),
|
||||
target(std::move(target)),
|
||||
version(version)
|
||||
{}
|
||||
|
||||
std::string name() const { return source.stem().string(); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os , const Update &self) {
|
||||
os << "Update(" << self.source.string() << " -> " << self.target.string() << ')';
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
struct Incompat
|
||||
{
|
||||
fs::path bundle;
|
||||
Version version;
|
||||
|
||||
Incompat(fs::path &&bundle, const Version &version) :
|
||||
bundle(std::move(bundle)),
|
||||
version(version)
|
||||
{}
|
||||
|
||||
std::string name() const { return bundle.stem().string(); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os , const Incompat &self) {
|
||||
os << "Incompat(" << self.bundle.string() << ')';
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
struct Updates
|
||||
{
|
||||
std::vector<Incompat> incompats;
|
||||
std::vector<Update> updates;
|
||||
};
|
||||
|
||||
|
||||
struct PresetUpdater::priv
|
||||
{
|
||||
int version_online_event;
|
||||
std::vector<Index> index_db;
|
||||
|
||||
bool enabled_version_check;
|
||||
bool enabled_config_update;
|
||||
std::string version_check_url;
|
||||
bool had_config_update;
|
||||
|
||||
fs::path cache_path;
|
||||
fs::path rsrc_path;
|
||||
fs::path vendor_path;
|
||||
|
||||
bool cancel;
|
||||
std::thread thread;
|
||||
|
||||
priv(int version_online_event);
|
||||
|
||||
void set_download_prefs(AppConfig *app_config);
|
||||
bool get_file(const std::string &url, const fs::path &target_path) const;
|
||||
void prune_tmps() const;
|
||||
void sync_version() const;
|
||||
void sync_config(const std::set<VendorProfile> vendors) const;
|
||||
|
||||
void check_install_indices() const;
|
||||
Updates get_config_updates() const;
|
||||
void perform_updates(Updates &&updates, bool snapshot = true) const;
|
||||
};
|
||||
|
||||
PresetUpdater::priv::priv(int version_online_event) :
|
||||
version_online_event(version_online_event),
|
||||
had_config_update(false),
|
||||
cache_path(fs::path(Slic3r::data_dir()) / "cache"),
|
||||
rsrc_path(fs::path(resources_dir()) / "profiles"),
|
||||
vendor_path(fs::path(Slic3r::data_dir()) / "vendor"),
|
||||
cancel(false)
|
||||
{
|
||||
set_download_prefs(GUI::get_app_config());
|
||||
check_install_indices();
|
||||
index_db = std::move(Index::load_db());
|
||||
}
|
||||
|
||||
// Pull relevant preferences from AppConfig
|
||||
void PresetUpdater::priv::set_download_prefs(AppConfig *app_config)
|
||||
{
|
||||
enabled_version_check = app_config->get("version_check") == "1";
|
||||
version_check_url = app_config->version_check_url();
|
||||
enabled_config_update = app_config->get("preset_update") == "1";
|
||||
}
|
||||
|
||||
// Downloads a file (http get operation). Cancels if the Updater is being destroyed.
|
||||
bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &target_path) const
|
||||
{
|
||||
bool res = false;
|
||||
fs::path tmp_path = target_path;
|
||||
tmp_path += (boost::format(".%1%%2%") % get_current_pid() % TMP_EXTENSION).str();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Get: `%1%`\n\t-> `%2%`\n\tvia tmp path `%3%`")
|
||||
% url
|
||||
% target_path.string()
|
||||
% tmp_path.string();
|
||||
|
||||
Http::get(url)
|
||||
.on_progress([this](Http::Progress, bool &cancel) {
|
||||
if (cancel) { cancel = true; }
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
(void)body;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%")
|
||||
% url
|
||||
% http_status
|
||||
% error;
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned http_status) {
|
||||
fs::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
file.write(body.c_str(), body.size());
|
||||
file.close();
|
||||
fs::rename(tmp_path, target_path);
|
||||
res = true;
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Remove leftover paritally downloaded files, if any.
|
||||
void PresetUpdater::priv::prune_tmps() const
|
||||
{
|
||||
for (fs::directory_iterator it(cache_path); it != fs::directory_iterator(); ++it) {
|
||||
if (it->path().extension() == TMP_EXTENSION) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Cache prune: " << it->path().string();
|
||||
fs::remove(it->path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get Slic3rPE version available online, save in AppConfig.
|
||||
void PresetUpdater::priv::sync_version() const
|
||||
{
|
||||
if (! enabled_version_check) { return; }
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Downloading Slic3rPE online version from: `%1%`") % version_check_url;
|
||||
|
||||
Http::get(version_check_url)
|
||||
.size_limit(SLIC3R_VERSION_BODY_MAX)
|
||||
.on_progress([this](Http::Progress, bool &cancel) {
|
||||
cancel = this->cancel;
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
(void)body;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Error getting: `%1%`: HTTP %2%, %3%")
|
||||
% version_check_url
|
||||
% http_status
|
||||
% error;
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned http_status) {
|
||||
boost::trim(body);
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Got Slic3rPE online version: `%1%`. Sending to GUI thread...") % body;
|
||||
wxCommandEvent* evt = new wxCommandEvent(version_online_event);
|
||||
evt->SetString(body);
|
||||
GUI::get_app()->QueueEvent(evt);
|
||||
})
|
||||
.perform_sync();
|
||||
}
|
||||
|
||||
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
|
||||
// Both are saved in cache.
|
||||
void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache";
|
||||
|
||||
if (!enabled_config_update) { return; }
|
||||
|
||||
// Donwload vendor preset bundles
|
||||
for (const auto &index : index_db) {
|
||||
if (cancel) { return; }
|
||||
|
||||
const auto vendor_it = vendors.find(VendorProfile(index.vendor()));
|
||||
if (vendor_it == vendors.end()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "No such vendor: " << index.vendor();
|
||||
continue;
|
||||
}
|
||||
|
||||
const VendorProfile &vendor = *vendor_it;
|
||||
if (vendor.config_update_url.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Vendor has no config_update_url: " << vendor.name;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Download a fresh index
|
||||
BOOST_LOG_TRIVIAL(info) << "Downloading index for vendor: " << vendor.name;
|
||||
const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME;
|
||||
const auto idx_path = cache_path / (vendor.id + ".idx");
|
||||
if (! get_file(idx_url, idx_path)) { continue; }
|
||||
if (cancel) { return; }
|
||||
|
||||
// Load the fresh index up
|
||||
Index new_index;
|
||||
new_index.load(idx_path);
|
||||
|
||||
// See if a there's a new version to download
|
||||
const auto recommended_it = new_index.recommended();
|
||||
if (recommended_it == new_index.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % vendor.name;
|
||||
continue;
|
||||
}
|
||||
const auto recommended = recommended_it->config_version;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("New index for vendor: %1%: current version: %2%, recommended version: %3%")
|
||||
% vendor.name
|
||||
% vendor.config_version.to_string()
|
||||
% recommended.to_string();
|
||||
|
||||
if (vendor.config_version >= recommended) { continue; }
|
||||
|
||||
// Download a fresh bundle
|
||||
BOOST_LOG_TRIVIAL(info) << "Downloading new bundle for vendor: " << vendor.name;
|
||||
const auto bundle_url = (boost::format("%1%/%2%.ini") % vendor.config_update_url % recommended.to_string()).str();
|
||||
const auto bundle_path = cache_path / (vendor.id + ".ini");
|
||||
if (! get_file(bundle_url, bundle_path)) { continue; }
|
||||
if (cancel) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
// Install indicies from resources. Only installs those that are either missing or older than in resources.
|
||||
void PresetUpdater::priv::check_install_indices() const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Checking if indices need to be installed from resources...";
|
||||
|
||||
for (fs::directory_iterator it(rsrc_path); it != fs::directory_iterator(); ++it) {
|
||||
const auto &path = it->path();
|
||||
if (path.extension() == ".idx") {
|
||||
const auto path_in_cache = cache_path / path.filename();
|
||||
|
||||
if (! fs::exists(path_in_cache)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Install index from resources: " << path.filename();
|
||||
fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists);
|
||||
} else {
|
||||
Index idx_rsrc, idx_cache;
|
||||
idx_rsrc.load(path);
|
||||
idx_cache.load(path_in_cache);
|
||||
|
||||
if (idx_cache.version() < idx_rsrc.version()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Update index from resources: " << path.filename();
|
||||
fs::copy_file(path, path_in_cache, fs::copy_option::overwrite_if_exists);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a list of bundle updates that are to be performed
|
||||
Updates PresetUpdater::priv::get_config_updates() const
|
||||
{
|
||||
Updates updates;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Checking for cached configuration updates...";
|
||||
|
||||
for (const auto idx : index_db) {
|
||||
auto bundle_path = vendor_path / (idx.vendor() + ".ini");
|
||||
|
||||
if (! fs::exists(bundle_path)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Bundle not present for index, skipping: " << idx.vendor();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform a basic load and check the version
|
||||
const auto vp = VendorProfile::from_ini(bundle_path, false);
|
||||
|
||||
const auto ver_current = idx.find(vp.config_version);
|
||||
if (ver_current == idx.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Preset bundle (`%1%`) version not found in index: %2%") % idx.vendor() % vp.config_version.to_string();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto recommended = idx.recommended();
|
||||
if (recommended == idx.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%, version cached: %3%")
|
||||
% vp.name
|
||||
% ver_current->config_version.to_string()
|
||||
% recommended->config_version.to_string();
|
||||
|
||||
if (! ver_current->is_current_slic3r_supported()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string();
|
||||
updates.incompats.emplace_back(std::move(bundle_path), *ver_current);
|
||||
} else if (recommended->config_version > ver_current->config_version) {
|
||||
// Config bundle update situation
|
||||
|
||||
// Check if the update is already present in a snapshot
|
||||
const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version);
|
||||
if (recommended_snap != SnapshotDB::singleton().end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...")
|
||||
% vp.name
|
||||
% recommended->config_version.to_string()
|
||||
% recommended_snap->id;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path_in_cache = cache_path / (idx.vendor() + ".ini");
|
||||
if (! fs::exists(path_in_cache)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Index indicates update, but new bundle not found in cache: " << path_in_cache.string();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto cached_vp = VendorProfile::from_ini(path_in_cache, false);
|
||||
if (cached_vp.config_version == recommended->config_version) {
|
||||
updates.updates.emplace_back(std::move(path_in_cache), std::move(bundle_path), *recommended);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const
|
||||
{
|
||||
if (updates.incompats.size() > 0) {
|
||||
if (snapshot) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
||||
SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_DOWNGRADE);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size();
|
||||
|
||||
for (const auto &incompat : updates.incompats) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << incompat;
|
||||
fs::remove(incompat.bundle);
|
||||
}
|
||||
}
|
||||
else if (updates.updates.size() > 0) {
|
||||
if (snapshot) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
||||
SnapshotDB::singleton().take_snapshot(*GUI::get_app_config(), Snapshot::SNAPSHOT_UPGRADE);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Performing %1% updates") % updates.updates.size();
|
||||
|
||||
for (const auto &update : updates.updates) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << update;
|
||||
|
||||
fs::copy_file(update.source, update.target, fs::copy_option::overwrite_if_exists);
|
||||
|
||||
PresetBundle bundle;
|
||||
bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
||||
|
||||
auto preset_remover = [](const Preset &preset) {
|
||||
fs::remove(preset.file);
|
||||
};
|
||||
|
||||
for (const auto &preset : bundle.prints) { preset_remover(preset); }
|
||||
for (const auto &preset : bundle.filaments) { preset_remover(preset); }
|
||||
for (const auto &preset : bundle.printers) { preset_remover(preset); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PresetUpdater::PresetUpdater(int version_online_event) :
|
||||
p(new priv(version_online_event))
|
||||
{}
|
||||
|
||||
|
||||
// Public
|
||||
|
||||
PresetUpdater::~PresetUpdater()
|
||||
{
|
||||
if (p && p->thread.joinable()) {
|
||||
// This will stop transfers being done by the thread, if any.
|
||||
// Cancelling takes some time, but should complete soon enough.
|
||||
p->cancel = true;
|
||||
p->thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void PresetUpdater::sync(PresetBundle *preset_bundle)
|
||||
{
|
||||
p->set_download_prefs(GUI::get_app_config());
|
||||
if (!p->enabled_version_check && !p->enabled_config_update) { return; }
|
||||
|
||||
// Copy the whole vendors data for use in the background thread
|
||||
// Unfortunatelly as of C++11, it needs to be copied again
|
||||
// into the closure (but perhaps the compiler can elide this).
|
||||
std::set<VendorProfile> vendors = preset_bundle->vendors;
|
||||
|
||||
p->thread = std::move(std::thread([this, vendors]() {
|
||||
this->p->prune_tmps();
|
||||
this->p->sync_version();
|
||||
this->p->sync_config(std::move(vendors));
|
||||
}));
|
||||
}
|
||||
|
||||
void PresetUpdater::slic3r_update_notify()
|
||||
{
|
||||
if (! p->enabled_version_check) { return; }
|
||||
|
||||
if (p->had_config_update) {
|
||||
BOOST_LOG_TRIVIAL(info) << "New Slic3r version available, but there was a configuration update, notification won't be displayed";
|
||||
return;
|
||||
}
|
||||
|
||||
auto* app_config = GUI::get_app_config();
|
||||
const auto ver_slic3r = Semver::parse(SLIC3R_VERSION);
|
||||
const auto ver_online_str = app_config->get("version_online");
|
||||
const auto ver_online = Semver::parse(ver_online_str);
|
||||
const auto ver_online_seen = Semver::parse(app_config->get("version_online_seen"));
|
||||
if (! ver_slic3r) {
|
||||
throw std::runtime_error("Could not parse Slic3r version string: " SLIC3R_VERSION);
|
||||
}
|
||||
|
||||
if (ver_online) {
|
||||
// Only display the notification if the version available online is newer AND if we haven't seen it before
|
||||
if (*ver_online > *ver_slic3r && (! ver_online_seen || *ver_online_seen < *ver_online)) {
|
||||
GUI::MsgUpdateSlic3r notification(*ver_slic3r, *ver_online);
|
||||
notification.ShowModal();
|
||||
if (notification.disable_version_check()) {
|
||||
app_config->set("version_check", "0");
|
||||
p->enabled_version_check = false;
|
||||
}
|
||||
}
|
||||
app_config->set("version_online_seen", ver_online_str);
|
||||
}
|
||||
}
|
||||
|
||||
bool PresetUpdater::config_update() const
|
||||
{
|
||||
if (! p->enabled_config_update) { return true; }
|
||||
|
||||
auto updates = p->get_config_updates();
|
||||
if (updates.incompats.size() > 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1% bundles incompatible. Asking for action...") % updates.incompats.size();
|
||||
|
||||
std::unordered_map<std::string, wxString> incompats_map;
|
||||
for (const auto &incompat : updates.incompats) {
|
||||
auto vendor = incompat.name();
|
||||
auto restrictions = wxString::Format(_(L("requires min. %s and max. %s")),
|
||||
incompat.version.min_slic3r_version.to_string(),
|
||||
incompat.version.max_slic3r_version.to_string()
|
||||
);
|
||||
incompats_map.emplace(std::make_pair(std::move(vendor), std::move(restrictions)));
|
||||
}
|
||||
|
||||
GUI::MsgDataIncompatible dlg(std::move(incompats_map));
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_REPLACE) {
|
||||
BOOST_LOG_TRIVIAL(info) << "User wants to re-configure...";
|
||||
p->perform_updates(std::move(updates));
|
||||
GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
|
||||
if (wizard.run(GUI::get_preset_bundle(), this)) {
|
||||
p->had_config_update = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye...";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (updates.updates.size() > 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size();
|
||||
|
||||
std::unordered_map<std::string, std::string> updates_map;
|
||||
for (const auto &update : updates.updates) {
|
||||
auto vendor = update.name();
|
||||
auto ver_str = update.version.config_version.to_string();
|
||||
if (! update.version.comment.empty()) {
|
||||
ver_str += std::string(" (") + update.version.comment + ")";
|
||||
}
|
||||
updates_map.emplace(std::make_pair(std::move(vendor), std::move(ver_str)));
|
||||
}
|
||||
|
||||
GUI::MsgUpdateConfig dlg(std::move(updates_map));
|
||||
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_OK) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
||||
p->perform_updates(std::move(updates));
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "User refused the update";
|
||||
}
|
||||
|
||||
p->had_config_update = true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "No configuration updates available.";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot) const
|
||||
{
|
||||
Updates updates;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Installing %1% bundles from resources ...") % bundles.size();
|
||||
|
||||
for (const auto &bundle : bundles) {
|
||||
auto path_in_rsrc = p->rsrc_path / bundle;
|
||||
auto path_in_vendors = p->vendor_path / bundle;
|
||||
updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version());
|
||||
}
|
||||
|
||||
p->perform_updates(std::move(updates), snapshot);
|
||||
}
|
||||
|
||||
|
||||
}
|
42
xs/src/slic3r/Utils/PresetUpdater.hpp
Normal file
42
xs/src/slic3r/Utils/PresetUpdater.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef slic3r_PresetUpdate_hpp_
|
||||
#define slic3r_PresetUpdate_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
class AppConfig;
|
||||
class PresetBundle;
|
||||
|
||||
class PresetUpdater
|
||||
{
|
||||
public:
|
||||
PresetUpdater(int version_online_event);
|
||||
PresetUpdater(PresetUpdater &&) = delete;
|
||||
PresetUpdater(const PresetUpdater &) = delete;
|
||||
PresetUpdater &operator=(PresetUpdater &&) = delete;
|
||||
PresetUpdater &operator=(const PresetUpdater &) = delete;
|
||||
~PresetUpdater();
|
||||
|
||||
// If either version check or config updating is enabled, get the appropriate data in the background and cache it.
|
||||
void sync(PresetBundle *preset_bundle);
|
||||
|
||||
// If version check is enabled, check if chaced online slic3r version is newer, notify if so.
|
||||
void slic3r_update_notify();
|
||||
|
||||
// If updating is enabled, check if updates are available in cache, if so, ask about installation.
|
||||
// A false return value implies Slic3r should exit due to incompatibility of configuration.
|
||||
bool config_update() const;
|
||||
|
||||
// "Update" a list of bundles from resources (behaves like an online update).
|
||||
void install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot = true) const;
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
#endif
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
|
@ -18,9 +20,33 @@ public:
|
|||
struct Minor { const int i; Minor(int i) : i(i) {} };
|
||||
struct Patch { const int i; Patch(int i) : i(i) {} };
|
||||
|
||||
Semver() : ver(semver_zero()) {}
|
||||
|
||||
Semver(int major, int minor, int patch,
|
||||
boost::optional<const std::string&> metadata = boost::none,
|
||||
boost::optional<const std::string&> prerelease = boost::none)
|
||||
: ver(semver_zero())
|
||||
{
|
||||
ver.major = major;
|
||||
ver.minor = minor;
|
||||
ver.patch = patch;
|
||||
set_metadata(metadata);
|
||||
set_prerelease(prerelease);
|
||||
}
|
||||
|
||||
Semver(const std::string &str) : ver(semver_zero())
|
||||
{
|
||||
auto parsed = parse(str);
|
||||
if (! parsed) {
|
||||
throw std::runtime_error(std::string("Could not parse version string: ") + str);
|
||||
}
|
||||
ver = parsed->ver;
|
||||
parsed->ver = semver_zero();
|
||||
}
|
||||
|
||||
static boost::optional<Semver> parse(const std::string &str)
|
||||
{
|
||||
semver_t ver;
|
||||
semver_t ver = semver_zero();
|
||||
if (::semver_parse(str.c_str(), &ver) == 0) {
|
||||
return Semver(ver);
|
||||
} else {
|
||||
|
@ -28,11 +54,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
static const Semver zero()
|
||||
{
|
||||
static semver_t ver = { 0, 0, 0, nullptr, nullptr };
|
||||
return Semver(ver);
|
||||
}
|
||||
static const Semver zero() { return Semver(semver_zero()); }
|
||||
|
||||
static const Semver inf()
|
||||
{
|
||||
|
@ -46,28 +68,40 @@ public:
|
|||
return Semver(ver);
|
||||
}
|
||||
|
||||
Semver(Semver &&other) { *this = std::move(other); }
|
||||
Semver(const Semver &other) { *this = other; }
|
||||
Semver(Semver &&other) : ver(other.ver) { other.ver = semver_zero(); }
|
||||
Semver(const Semver &other) : ver(::semver_copy(&other.ver)) {}
|
||||
|
||||
Semver &operator=(Semver &&other)
|
||||
{
|
||||
::semver_free(&ver);
|
||||
ver = other.ver;
|
||||
other.ver.major = other.ver.minor = other.ver.patch = 0;
|
||||
other.ver.metadata = other.ver.prerelease = nullptr;
|
||||
other.ver = semver_zero();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Semver &operator=(const Semver &other)
|
||||
{
|
||||
::semver_free(&ver);
|
||||
ver = other.ver;
|
||||
if (other.ver.metadata != nullptr) { std::strcpy(ver.metadata, other.ver.metadata); }
|
||||
if (other.ver.prerelease != nullptr) { std::strcpy(ver.prerelease, other.ver.prerelease); }
|
||||
ver = ::semver_copy(&other.ver);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Semver() { ::semver_free(&ver); }
|
||||
|
||||
// const accessors
|
||||
int maj() const { return ver.major; }
|
||||
int min() const { return ver.minor; }
|
||||
int patch() const { return ver.patch; }
|
||||
const char* prerelease() const { return ver.prerelease; }
|
||||
const char* metadata() const { return ver.metadata; }
|
||||
|
||||
// Setters
|
||||
void set_maj(int maj) { ver.major = maj; }
|
||||
void set_min(int min) { ver.minor = min; }
|
||||
void set_patch(int patch) { ver.patch = patch; }
|
||||
void set_metadata(boost::optional<const std::string&> meta) { ver.metadata = meta ? strdup(*meta) : nullptr; }
|
||||
void set_prerelease(boost::optional<const std::string&> pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; }
|
||||
|
||||
// Comparison
|
||||
bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; }
|
||||
bool operator<=(const Semver &b) const { return ::semver_compare(ver, b.ver) <= 0; }
|
||||
|
@ -107,6 +141,9 @@ private:
|
|||
semver_t ver;
|
||||
|
||||
Semver(semver_t ver) : ver(ver) {}
|
||||
|
||||
static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; }
|
||||
static char * strdup(const std::string &str) { return ::semver_strdup(const_cast<char*>(str.c_str())); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
#include "Time.hpp"
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif /* WIN32 */
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Utils {
|
||||
|
||||
time_t parse_time_ISO8601Z(const std::string &sdate)
|
||||
{
|
||||
int y, M, d, h, m;
|
||||
float s;
|
||||
if (sscanf(sdate.c_str(), "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s) != 6)
|
||||
int y, M, d, h, m, s;
|
||||
if (sscanf(sdate.c_str(), "%04d%02d%02dT%02d%02d%02dZ", &y, &M, &d, &h, &m, &s) != 6)
|
||||
return (time_t)-1;
|
||||
struct tm tms;
|
||||
tms.tm_year = y - 1900; // Year since 1900
|
||||
|
@ -15,7 +20,7 @@ time_t parse_time_ISO8601Z(const std::string &sdate)
|
|||
tms.tm_mday = d; // 1-31
|
||||
tms.tm_hour = h; // 0-23
|
||||
tms.tm_min = m; // 0-59
|
||||
tms.tm_sec = (int)s; // 0-61 (0-60 in C++11)
|
||||
tms.tm_sec = s; // 0-61 (0-60 in C++11)
|
||||
return mktime(&tms);
|
||||
}
|
||||
|
||||
|
@ -23,21 +28,34 @@ std::string format_time_ISO8601Z(time_t time)
|
|||
{
|
||||
struct tm tms;
|
||||
#ifdef WIN32
|
||||
gmtime_s(time, &tms);
|
||||
gmtime_s(&tms, &time);
|
||||
#else
|
||||
gmtime_r(&tms, time);
|
||||
gmtime_r(&time, &tms);
|
||||
#endif
|
||||
char buf[128];
|
||||
sprintf(buf, "%d-%d-%dT%d:%d:%fZ",
|
||||
tms.tm_year + 1900
|
||||
tms.tm_mon + 1
|
||||
tms.tm_mday
|
||||
tms.tm_hour
|
||||
tms.tm_min
|
||||
sprintf(buf, "%04d%02d%02dT%02d%02d%02dZ",
|
||||
tms.tm_year + 1900,
|
||||
tms.tm_mon + 1,
|
||||
tms.tm_mday,
|
||||
tms.tm_hour,
|
||||
tms.tm_min,
|
||||
tms.tm_sec);
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string format_local_date_time(time_t time)
|
||||
{
|
||||
struct tm tms;
|
||||
#ifdef WIN32
|
||||
localtime_s(&tms, &time);
|
||||
#else
|
||||
localtime_r(&time, &tms);
|
||||
#endif
|
||||
char buf[80];
|
||||
strftime(buf, 80, "%x %X", &tms);
|
||||
return buf;
|
||||
}
|
||||
|
||||
time_t get_current_time_utc()
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
@ -53,11 +71,10 @@ time_t get_current_time_utc()
|
|||
tm.tm_isdst = -1;
|
||||
return mktime(&tm);
|
||||
#else
|
||||
return gmtime();
|
||||
const time_t current_local = time(nullptr);
|
||||
return mktime(gmtime(¤t_local));
|
||||
#endif
|
||||
}
|
||||
|
||||
}; // namespace Utils
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Utils_Time_hpp_ */
|
||||
|
|
|
@ -13,8 +13,11 @@ namespace Utils {
|
|||
extern time_t parse_time_ISO8601Z(const std::string &s);
|
||||
extern std::string format_time_ISO8601Z(time_t time);
|
||||
|
||||
// Format the date and time from an UTC time according to the active locales and a local time zone.
|
||||
extern std::string format_local_date_time(time_t time);
|
||||
|
||||
// There is no gmtime() on windows.
|
||||
time_t get_current_time_utc();
|
||||
extern time_t get_current_time_utc();
|
||||
|
||||
}; // namespace Utils
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
%package{Slic3r::GUI};
|
||||
|
||||
void about()
|
||||
%code{% Slic3r::GUI::about(); %};
|
||||
|
||||
void disable_screensaver()
|
||||
%code{% Slic3r::GUI::disable_screensaver(); %};
|
||||
|
||||
|
@ -32,13 +35,16 @@ void set_main_frame(SV *ui)
|
|||
|
||||
void set_tab_panel(SV *ui)
|
||||
%code%{ Slic3r::GUI::set_tab_panel((wxNotebook*)wxPli_sv_2_object(aTHX_ ui, "Wx::Notebook")); %};
|
||||
|
||||
void add_debug_menu(SV *ui, int event_language_change)
|
||||
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
|
||||
|
||||
void add_config_menu(SV *ui, int event_preferences_changed, int event_language_change)
|
||||
%code%{ Slic3r::GUI::add_config_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_preferences_changed, event_language_change); %};
|
||||
|
||||
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
|
||||
%code%{ Slic3r::GUI::create_preset_tabs(no_controller, event_value_change, event_presets_changed); %};
|
||||
|
||||
void show_error_id(int id, std::string msg)
|
||||
%code%{ Slic3r::GUI::show_error_id(id, msg); %};
|
||||
|
||||
TabIface* get_preset_tab(char *name)
|
||||
%code%{ RETVAL=Slic3r::GUI::get_preset_tab_iface(name); %};
|
||||
|
||||
|
@ -54,12 +60,27 @@ int combochecklist_get_flags(SV *ui)
|
|||
void set_app_config(AppConfig *app_config)
|
||||
%code%{ Slic3r::GUI::set_app_config(app_config); %};
|
||||
|
||||
bool check_unsaved_changes()
|
||||
%code%{ RETVAL=Slic3r::GUI::check_unsaved_changes(); %};
|
||||
|
||||
bool config_wizard_startup(int app_config_exists)
|
||||
%code%{
|
||||
try {
|
||||
RETVAL=Slic3r::GUI::config_wizard_startup(app_config_exists != 0);
|
||||
} catch (std::exception& e) {
|
||||
croak("%s\n", e.what());
|
||||
}
|
||||
%};
|
||||
|
||||
void open_preferences_dialog(int preferences_event)
|
||||
%code%{ Slic3r::GUI::open_preferences_dialog(preferences_event); %};
|
||||
|
||||
void set_preset_bundle(PresetBundle *preset_bundle)
|
||||
%code%{ Slic3r::GUI::set_preset_bundle(preset_bundle); %};
|
||||
|
||||
void set_preset_updater(PresetUpdater* updater)
|
||||
%code%{ Slic3r::GUI::set_preset_updater(updater); %};
|
||||
|
||||
void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_sizer)
|
||||
%code%{ Slic3r::GUI::add_frequently_changed_parameters((wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
|
||||
(wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
|
||||
|
|
|
@ -44,9 +44,6 @@
|
|||
|
||||
Ref<Preset> find_preset(char *name, bool first_visible_if_not_found = false) %code%{ RETVAL = THIS->find_preset(name, first_visible_if_not_found); %};
|
||||
|
||||
bool current_is_dirty();
|
||||
std::vector<std::string> current_dirty_options();
|
||||
|
||||
void update_tab_ui(SV *ui, bool show_incompatible)
|
||||
%code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" );
|
||||
THIS->update_tab_ui(cb, show_incompatible); %};
|
||||
|
@ -55,30 +52,6 @@
|
|||
%code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" );
|
||||
THIS->update_platter_ui(cb); %};
|
||||
|
||||
bool update_dirty_ui(SV *ui)
|
||||
%code%{ RETVAL = THIS->update_dirty_ui((wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox")); %};
|
||||
|
||||
void select_preset(int idx);
|
||||
bool select_preset_by_name(char *name) %code%{ RETVAL = THIS->select_preset_by_name(name, true); %};
|
||||
void discard_current_changes();
|
||||
|
||||
void save_current_preset(char *new_name)
|
||||
%code%{
|
||||
try {
|
||||
THIS->save_current_preset(new_name);
|
||||
} catch (std::exception& e) {
|
||||
croak("Error saving a preset %s:\n%s\n", new_name, e.what());
|
||||
}
|
||||
%};
|
||||
void delete_current_preset()
|
||||
%code%{
|
||||
try {
|
||||
THIS->delete_current_preset();
|
||||
} catch (std::exception& e) {
|
||||
croak("Error deleting a preset file %s:\n%s\n", THIS->get_selected_preset().file.c_str(), e.what());
|
||||
}
|
||||
%};
|
||||
|
||||
%{
|
||||
|
||||
SV*
|
||||
|
@ -144,14 +117,6 @@ PresetCollection::arrayref()
|
|||
croak("Loading of a config bundle %s failed:\n%s\n", path, e.what());
|
||||
}
|
||||
%};
|
||||
void install_vendor_configbundle(const char *path)
|
||||
%code%{
|
||||
try {
|
||||
THIS->install_vendor_configbundle(path);
|
||||
} catch (std::exception& e) {
|
||||
croak("Installing a vendor config bundle %s failed:\n%s\n", path, e.what());
|
||||
}
|
||||
%};
|
||||
void export_configbundle(char *path)
|
||||
%code%{
|
||||
try {
|
||||
|
@ -175,9 +140,6 @@ PresetCollection::arrayref()
|
|||
|
||||
std::vector<std::string> filament_presets() %code%{ RETVAL = THIS->filament_presets; %};
|
||||
void set_filament_preset(int idx, const char *name);
|
||||
void update_multi_material_filament_presets();
|
||||
|
||||
void update_compatible_with_printer(bool select_other_if_incompatible);
|
||||
|
||||
Clone<DynamicPrintConfig> full_config() %code%{ RETVAL = THIS->full_config(); %};
|
||||
|
||||
|
@ -185,15 +147,3 @@ PresetCollection::arrayref()
|
|||
%code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox");
|
||||
THIS->update_platter_filament_ui(extruder_idx, cb); %};
|
||||
};
|
||||
|
||||
%name{Slic3r::GUI::PresetHints} class PresetHints {
|
||||
PresetHints();
|
||||
~PresetHints();
|
||||
|
||||
static std::string cooling_description(Preset *preset)
|
||||
%code%{ RETVAL = PresetHints::cooling_description(*preset); %};
|
||||
static std::string maximum_volumetric_flow_description(PresetBundle *preset)
|
||||
%code%{ RETVAL = PresetHints::maximum_volumetric_flow_description(*preset); %};
|
||||
static std::string recommended_thin_wall_thickness(PresetBundle *preset)
|
||||
%code%{ RETVAL = PresetHints::recommended_thin_wall_thickness(*preset); %};
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
bool current_preset_is_dirty();
|
||||
void load_key_value(char* opt_key, char* value);
|
||||
void OnActivate();
|
||||
size_t get_selected_preset_item();
|
||||
std::string title();
|
||||
Ref<DynamicPrintConfig> get_config();
|
||||
Ref<PresetCollection> get_presets();
|
||||
|
|
13
xs/xsp/Utils_PresetUpdater.xsp
Normal file
13
xs/xsp/Utils_PresetUpdater.xsp
Normal file
|
@ -0,0 +1,13 @@
|
|||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::PresetUpdater} class PresetUpdater {
|
||||
PresetUpdater(int version_online_event);
|
||||
void sync(PresetBundle* preset_bundle);
|
||||
void slic3r_update_notify();
|
||||
bool config_update();
|
||||
};
|
|
@ -231,11 +231,12 @@ PresetCollection* O_OBJECT_SLIC3R
|
|||
Ref<PresetCollection> O_OBJECT_SLIC3R_T
|
||||
PresetBundle* O_OBJECT_SLIC3R
|
||||
Ref<PresetBundle> O_OBJECT_SLIC3R_T
|
||||
PresetHints* O_OBJECT_SLIC3R
|
||||
Ref<PresetHints> O_OBJECT_SLIC3R_T
|
||||
TabIface* O_OBJECT_SLIC3R
|
||||
Ref<TabIface> O_OBJECT_SLIC3R_T
|
||||
|
||||
PresetUpdater* O_OBJECT_SLIC3R
|
||||
Ref<PresetUpdater> O_OBJECT_SLIC3R_T
|
||||
|
||||
OctoPrint* O_OBJECT_SLIC3R
|
||||
Ref<OctoPrint> O_OBJECT_SLIC3R_T
|
||||
Clone<OctoPrint> O_OBJECT_SLIC3R_T
|
||||
|
|
|
@ -208,6 +208,8 @@
|
|||
%typemap{Ref<PresetCollection>}{simple};
|
||||
%typemap{PresetBundle*};
|
||||
%typemap{Ref<PresetBundle>}{simple};
|
||||
%typemap{PresetUpdater*};
|
||||
%typemap{Ref<PresetUpdater>}{simple};
|
||||
%typemap{PresetHints*};
|
||||
%typemap{Ref<PresetHints>}{simple};
|
||||
%typemap{TabIface*};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue