diff --git a/resources/images/custom-gcode_gcode.svg b/resources/images/custom-gcode_gcode.svg
new file mode 100644
index 0000000000..38bcd21729
--- /dev/null
+++ b/resources/images/custom-gcode_gcode.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_measure.svg b/resources/images/custom-gcode_measure.svg
new file mode 100644
index 0000000000..3c13dd7cc7
--- /dev/null
+++ b/resources/images/custom-gcode_measure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_object-info.svg b/resources/images/custom-gcode_object-info.svg
new file mode 100644
index 0000000000..2c24bdc8a2
--- /dev/null
+++ b/resources/images/custom-gcode_object-info.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_single.svg b/resources/images/custom-gcode_single.svg
new file mode 100644
index 0000000000..d177860bc9
--- /dev/null
+++ b/resources/images/custom-gcode_single.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_slicing-state.svg b/resources/images/custom-gcode_slicing-state.svg
new file mode 100644
index 0000000000..4b4bef6ecf
--- /dev/null
+++ b/resources/images/custom-gcode_slicing-state.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_slicing-state_global.svg b/resources/images/custom-gcode_slicing-state_global.svg
new file mode 100644
index 0000000000..7f4e685a1b
--- /dev/null
+++ b/resources/images/custom-gcode_slicing-state_global.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_stats.svg b/resources/images/custom-gcode_stats.svg
new file mode 100644
index 0000000000..96dfe8decf
--- /dev/null
+++ b/resources/images/custom-gcode_stats.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_temperature.svg b/resources/images/custom-gcode_temperature.svg
new file mode 100644
index 0000000000..d14cd1a31c
--- /dev/null
+++ b/resources/images/custom-gcode_temperature.svg
@@ -0,0 +1,14 @@
+
diff --git a/resources/images/custom-gcode_vector-index.svg b/resources/images/custom-gcode_vector-index.svg
new file mode 100644
index 0000000000..68aef590bb
--- /dev/null
+++ b/resources/images/custom-gcode_vector-index.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/images/custom-gcode_vector.svg b/resources/images/custom-gcode_vector.svg
new file mode 100644
index 0000000000..396f0e7b82
--- /dev/null
+++ b/resources/images/custom-gcode_vector.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp
index 9bf81790e4..1bebecfb96 100644
--- a/src/OrcaSlicer.cpp
+++ b/src/OrcaSlicer.cpp
@@ -5329,6 +5329,7 @@ bool CLI::setup(int argc, char **argv)
set_var_dir((path_resources / "images").string());
set_local_dir((path_resources / "i18n").string());
set_sys_shapes_dir((path_resources / "shapes").string());
+ set_custom_gcodes_dir((path_resources / "custom_gcodes").string());
// Parse all command line options into a DynamicConfig.
// If any option is unsupported, print usage and abort immediately.
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 2d8abfabfd..ebabfab426 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -13,6 +13,10 @@ include(PrecompiledHeader)
string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S)
set(SLIC3R_BUILD_TIME ${COMPILE_TIME})
+if(NOT DEFINED ORCA_CHECK_GCODE_PLACEHOLDERS)
+ set(ORCA_CHECK_GCODE_PLACEHOLDERS "0")
+endif()
+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY)
if (MINGW)
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 959519e60e..a9973b4682 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -1786,6 +1786,8 @@ public:
// Create a default option to be inserted into a DynamicConfig.
ConfigOption* create_default_option() const;
+ bool is_scalar() const { return (int(this->type) & int(coVectorType)) == 0; }
+
template ConfigOption* load_option_from_archive(Archive &archive) const {
if (this->nullable) {
switch (this->type) {
@@ -1973,6 +1975,7 @@ public:
out.push_back(kvp.first);
return out;
}
+ bool empty() { return options.empty(); }
// Iterate through all of the CLI options and write them to a stream.
std::ostream& print_cli_help(
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 89a68997c6..626d3d6d72 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1585,6 +1585,21 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
check_placeholder_parser_failed();
+#if ORCA_CHECK_GCODE_PLACEHOLDERS
+ if (!m_placeholder_error_messages.empty()){
+ std::ostringstream message;
+ message << "Some EditGcodeDialog defs were not specified properly. Do so in PrintConfig under SlicingStatesConfigDef:" << std::endl;
+ for (const auto& error : m_placeholder_error_messages) {
+ message << std::endl << error.first << ": " << std::endl;
+ for (const auto& str : error.second)
+ message << str << ", ";
+ message.seekp(-2, std::ios_base::end);
+ message << std::endl;
+ }
+ throw Slic3r::PlaceholderParserError(message.str());
+ }
+#endif
+
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
// Post-process the G-code to update time stamps.
@@ -2936,6 +2951,42 @@ void GCode::process_layers(
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
{
+ // Orca: Added CMake config option since debug is rarely used in current workflow.
+ // Also changed from throwing error immediately to storing messages till slicing is completed
+ // to raise all errors at the same time.
+#if !defined(NDEBUG) || ORCA_CHECK_GCODE_PLACEHOLDERS // CHECK_CUSTOM_GCODE_PLACEHOLDERS
+ if (config_override) {
+ const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders();
+
+ // 1-st check: custom G-code "name" have to be present in s_CustomGcodeSpecificOptions;
+ //if (custom_gcode_placeholders.count(name) > 0) {
+ // const auto& placeholders = custom_gcode_placeholders.at(name);
+ if (auto it = custom_gcode_placeholders.find(name); it != custom_gcode_placeholders.end()) {
+ const auto& placeholders = it->second;
+
+ for (const std::string& key : config_override->keys()) {
+ // 2-nd check: "key" have to be present in s_CustomGcodeSpecificOptions for "name" custom G-code ;
+ if (std::find(placeholders.begin(), placeholders.end(), key) == placeholders.end()) {
+ auto& vector = m_placeholder_error_messages[name + " - option not specified for custom gcode type (s_CustomGcodeSpecificOptions)"];
+ if (std::find(vector.begin(), vector.end(), key) == vector.end())
+ vector.emplace_back(key);
+ }
+ // 3-rd check: "key" have to be present in CustomGcodeSpecificConfigDef for "key" placeholder;
+ if (!custom_gcode_specific_config_def.has(key)) {
+ auto& vector = m_placeholder_error_messages[name + " - option has no definition (CustomGcodeSpecificConfigDef)"];
+ if (std::find(vector.begin(), vector.end(), key) == vector.end())
+ vector.emplace_back(key);
+ }
+ }
+ }
+ else {
+ auto& vector = m_placeholder_error_messages[name + " - gcode type not found in s_CustomGcodeSpecificOptions"];
+ if (vector.empty())
+ vector.emplace_back("");
+ }
+ }
+#endif
+
PlaceholderParserIntegration &ppi = m_placeholder_parser_integration;
try {
ppi.update_from_gcodewriter(m_writer);
@@ -3528,7 +3579,7 @@ LayerResult GCode::process_layer(
m_last_height = height;
// Set new layer - this will change Z and force a retraction if retract_when_changing_layer is enabled.
- if (! print.config().before_layer_change_gcode.value.empty()) {
+ if (! m_config.before_layer_change_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
@@ -3551,7 +3602,7 @@ LayerResult GCode::process_layer(
auto insert_timelapse_gcode = [this, print_z, &print]() -> std::string {
std::string gcode_res;
- if (!print.config().time_lapse_gcode.value.empty()) {
+ if (!m_config.time_lapse_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
@@ -3579,7 +3630,7 @@ LayerResult GCode::process_layer(
}
}
} else {
- if (!print.config().time_lapse_gcode.value.empty()) {
+ if (!m_config.time_lapse_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
@@ -3589,7 +3640,7 @@ LayerResult GCode::process_layer(
"\n";
}
}
- if (! print.config().layer_change_gcode.value.empty()) {
+ if (! m_config.layer_change_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
@@ -4287,6 +4338,33 @@ void GCode::apply_print_config(const PrintConfig &print_config)
m_writer.apply_print_config(print_config);
m_config.apply(print_config);
m_scaled_resolution = scaled(print_config.resolution.value);
+
+#if ORCA_CHECK_GCODE_PLACEHOLDERS
+ // If the gcode value is empty, set a value so that the check code within the parser is run
+ for (auto opt : std::initializer_list{
+ &m_config.machine_start_gcode,
+ &m_config.machine_end_gcode,
+ &m_config.before_layer_change_gcode,
+ &m_config.layer_change_gcode,
+ &m_config.time_lapse_gcode,
+ &m_config.change_filament_gcode,
+ &m_config.change_extrusion_role_gcode,
+ &m_config.printing_by_object_gcode,
+ &m_config.machine_pause_gcode,
+ &m_config.template_custom_gcode,
+ }) {
+ if (opt->empty())
+ opt->set(new ConfigOptionString(";VALUE FOR TESTING"));
+ }
+ for (auto opt : std::initializer_list{
+ &m_config.filament_start_gcode,
+ &m_config.filament_end_gcode
+ }) {
+ if (opt->empty())
+ for (int i = 0; i < opt->size(); ++i)
+ opt->set_at(new ConfigOptionString(";VALUE FOR TESTING"), i, 0);
+ }
+#endif
}
void GCode::append_full_config(const Print &print, std::string &str)
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 935cb95fc9..2260920dc7 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -158,6 +158,7 @@ struct LayerResult {
};
class GCode {
+
public:
GCode() :
m_origin(Vec2d::Zero()),
@@ -522,6 +523,10 @@ private:
double m_last_mm3_per_mm;
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
+#if ORCA_CHECK_GCODE_PLACEHOLDERS
+ std::map> m_placeholder_error_messages;
+#endif
+
Point m_last_pos;
bool m_last_pos_defined;
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index b8dacbcf91..7993f6a7ba 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -2041,6 +2041,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
const Vec3d origin = this->get_plate_origin();
gcode.set_gcode_offset(origin(0), origin(1));
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
+
//BBS
result->conflict_result = m_conflict_result;
return path.c_str();
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 0059a8d5b2..792d8815d4 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -6614,6 +6614,386 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::
}
}
+// SlicingStatesConfigDefs
+
+// Create a new config definition with a label and tooltip
+// Note: the L() macro is already used for LABEL and TOOLTIP
+#define new_def(OPT_KEY, TYPE, LABEL, TOOLTIP) \
+ def = this->add(OPT_KEY, TYPE); \
+ def->label = L(LABEL); \
+ def->tooltip = L(TOOLTIP);
+
+ReadOnlySlicingStatesConfigDef::ReadOnlySlicingStatesConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("zhop", coFloat);
+ def->label = L("Current z-hop");
+ def->tooltip = L("Contains z-hop present at the beginning of the custom G-code block.");
+}
+
+ReadWriteSlicingStatesConfigDef::ReadWriteSlicingStatesConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("position", coFloats);
+ def->label = L("Position");
+ def->tooltip = L("Position of the extruder at the beginning of the custom G-code block. If the custom G-code travels somewhere else, "
+ "it should write to this variable so PrusaSlicer knows where it travels from when it gets control back.");
+
+ def = this->add("e_retracted", coFloats);
+ def->label = L("Retraction");
+ def->tooltip = L("Retraction state at the beginning of the custom G-code block. If the custom G-code moves the extruder axis, "
+ "it should write to this variable so PrusaSlicer deretracts correctly when it gets control back.");
+
+ def = this->add("e_restart_extra", coFloats);
+ def->label = L("Extra deretraction");
+ def->tooltip = L("Currently planned extra extruder priming after deretraction.");
+
+ // Options from PS not used in Orca
+// def = this->add("e_position", coFloats);
+// def->label = L("Absolute E position");
+// def->tooltip = L("Current position of the extruder axis. Only used with absolute extruder addressing.");
+}
+
+OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("current_extruder", coInt);
+ def->label = L("Current extruder");
+ def->tooltip = L("Zero-based index of currently used extruder.");
+
+ def = this->add("current_object_idx", coInt);
+ def->label = L("Current object index");
+ def->tooltip = L("Specific for sequential printing. Zero-based index of currently printed object.");
+
+ def = this->add("has_wipe_tower", coBool);
+ def->label = L("Has wipe tower");
+ def->tooltip = L("Whether or not wipe tower is being generated in the print.");
+
+ def = this->add("initial_extruder", coInt);
+ def->label = L("Initial extruder");
+ def->tooltip = L("Zero-based index of the first extruder used in the print. Same as initial_tool.");
+
+ def = this->add("initial_tool", coInt);
+ def->label = L("Initial tool");
+ def->tooltip = L("Zero-based index of the first extruder used in the print. Same as initial_extruder.");
+
+ def = this->add("is_extruder_used", coBools);
+ def->label = L("Is extruder used?");
+ def->tooltip = L("Vector of bools stating whether a given extruder is used in the print.");
+
+ // Options from PS not used in Orca
+ // def = this->add("initial_filament_type", coString);
+ // def->label = L("Initial filament type");
+ // def->tooltip = L("String containing filament type of the first used extruder.");
+
+ // def = this->add("has_single_extruder_multi_material_priming", coBool);
+ // def->label = L("Has single extruder MM priming");
+ // def->tooltip = L("Are the extra multi-material priming regions used in this print?");
+
+ new_def("initial_no_support_extruder", coInt, "Initial no support extruder", "Zero-based index of the first extruder used for printing without support. Same as initial_no_support_tool.");
+ new_def("in_head_wrap_detect_zone", coBool, "In head wrap detect zone", "Indicates if the first layer overlaps with the head wrap zone.");
+}
+
+PrintStatisticsConfigDef::PrintStatisticsConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("extruded_volume", coFloats);
+ def->label = L("Volume per extruder");
+ def->tooltip = L("Total filament volume extruded per extruder during the entire print.");
+
+ def = this->add("total_toolchanges", coInt);
+ def->label = L("Total toolchanges");
+ def->tooltip = L("Number of toolchanges during the print.");
+
+ def = this->add("extruded_volume_total", coFloat);
+ def->label = L("Total volume");
+ def->tooltip = L("Total volume of filament used during the entire print.");
+
+ def = this->add("extruded_weight", coFloats);
+ def->label = L("Weight per extruder");
+ def->tooltip = L("Weight per extruder extruded during the entire print. Calculated from filament_density value in Filament Settings.");
+
+ def = this->add("extruded_weight_total", coFloat);
+ def->label = L("Total weight");
+ def->tooltip = L("Total weight of the print. Calculated from filament_density value in Filament Settings.");
+
+ def = this->add("total_layer_count", coInt);
+ def->label = L("Total layer count");
+ def->tooltip = L("Number of layers in the entire print.");
+
+ // Options from PS not used in Orca
+ /* def = this->add("normal_print_time", coString);
+ def->label = L("Print time (normal mode)");
+ def->tooltip = L("Estimated print time when printed in normal mode (i.e. not in silent mode). Same as print_time.");
+
+ def = this->add("num_printing_extruders", coInt);
+ def->label = L("Number of printing extruders");
+ def->tooltip = L("Number of extruders used during the print.");
+
+ def = this->add("print_time", coString);
+ def->label = L("Print time (normal mode)");
+ def->tooltip = L("Estimated print time when printed in normal mode (i.e. not in silent mode). Same as normal_print_time.");
+
+ def = this->add("printing_filament_types", coString);
+ def->label = L("Used filament types");
+ def->tooltip = L("Comma-separated list of all filament types used during the print.");
+
+ def = this->add("silent_print_time", coString);
+ def->label = L("Print time (silent mode)");
+ def->tooltip = L("Estimated print time when printed in silent mode.");
+
+ def = this->add("total_cost", coFloat);
+ def->label = L("Total cost");
+ def->tooltip = L("Total cost of all material used in the print. Calculated from filament_cost value in Filament Settings.");
+
+ def = this->add("total_weight", coFloat);
+ def->label = L("Total weight");
+ def->tooltip = L("Total weight of the print. Calculated from filament_density value in Filament Settings.");
+
+ def = this->add("total_wipe_tower_cost", coFloat);
+ def->label = L("Total wipe tower cost");
+ def->tooltip = L("Total cost of the material wasted on the wipe tower. Calculated from filament_cost value in Filament Settings.");
+
+ def = this->add("total_wipe_tower_filament", coFloat);
+ def->label = L("Wipe tower volume");
+ def->tooltip = L("Total filament volume extruded on the wipe tower.");
+
+ def = this->add("used_filament", coFloat);
+ def->label = L("Used filament");
+ def->tooltip = L("Total length of filament used in the print.");*/
+}
+
+ObjectsInfoConfigDef::ObjectsInfoConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("num_objects", coInt);
+ def->label = L("Number of objects");
+ def->tooltip = L("Total number of objects in the print.");
+
+ def = this->add("num_instances", coInt);
+ def->label = L("Number of instances");
+ def->tooltip = L("Total number of object instances in the print, summed over all objects.");
+
+ def = this->add("scale", coStrings);
+ def->label = L("Scale per object");
+ def->tooltip = L("Contains a string with the information about what scaling was applied to the individual objects. "
+ "Indexing of the objects is zero-based (first object has index 0).\n"
+ "Example: 'x:100% y:50% z:100'.");
+
+ def = this->add("input_filename_base", coString);
+ def->label = L("Input filename without extension");
+ def->tooltip = L("Source filename of the first object, without extension.");
+
+ new_def("input_filename", coString, "Full input filename", "Source filename of the first object.");
+ new_def("plate_name", coString, "Plate name", "Name of the plate sliced.");
+}
+
+DimensionsConfigDef::DimensionsConfigDef()
+{
+ ConfigOptionDef* def;
+
+ const std::string point_tooltip = L("The vector has two elements: x and y coordinate of the point. Values in mm.");
+ const std::string bb_size_tooltip = L("The vector has two elements: x and y dimension of the bounding box. Values in mm.");
+
+ def = this->add("first_layer_print_convex_hull", coPoints);
+ def->label = L("First layer convex hull");
+ def->tooltip = L("Vector of points of the first layer convex hull. Each element has the following format:"
+ "'[x, y]' (x and y are floating-point numbers in mm).");
+
+ def = this->add("first_layer_print_min", coFloats);
+ def->label = L("Bottom-left corner of first layer bounding box");
+ def->tooltip = point_tooltip;
+
+ def = this->add("first_layer_print_max", coFloats);
+ def->label = L("Top-right corner of first layer bounding box");
+ def->tooltip = point_tooltip;
+
+ def = this->add("first_layer_print_size", coFloats);
+ def->label = L("Size of the first layer bounding box");
+ def->tooltip = bb_size_tooltip;
+
+ def = this->add("print_bed_min", coFloats);
+ def->label = L("Bottom-left corner of print bed bounding box");
+ def->tooltip = point_tooltip;
+
+ def = this->add("print_bed_max", coFloats);
+ def->label = L("Top-right corner of print bed bounding box");
+ def->tooltip = point_tooltip;
+
+ def = this->add("print_bed_size", coFloats);
+ def->label = L("Size of the print bed bounding box");
+ def->tooltip = bb_size_tooltip;
+
+ new_def("first_layer_center_no_wipe_tower", coFloats, "First layer center without wipe tower", point_tooltip);
+ new_def("first_layer_height", coFloat, "First layer height", "Height of the first layer.");
+}
+
+TemperaturesConfigDef::TemperaturesConfigDef()
+{
+ ConfigOptionDef* def;
+
+ new_def("bed_temperature", coInts, "Bed temperature", "Vector of bed temperatures for each extruder/filament.")
+ new_def("bed_temperature_initial_layer", coInts, "Initial layer bed temperature", "Vector of initial layer bed temperatures for each extruder/filament. Provides the same value as first_layer_bed_temperature.")
+ new_def("bed_temperature_initial_layer_single", coInt, "Initial layer bed temperature (initial extruder)", "Initial layer bed temperature for the initial extruder. Same as bed_temperature_initial_layer[initial_extruder]")
+ new_def("chamber_temperature", coInts, "Chamber temperature", "Vector of chamber temperatures for each extruder/filament.")
+ new_def("overall_chamber_temperature", coInt, "Overall chamber temperature", "Overall chamber temperature. This value is the maximum chamber temperature of any extruder/filament used.")
+ new_def("first_layer_bed_temperature", coInts, "First layer bed temperature", "Vector of first layer bed temperatures for each extruder/filament. Provides the same value as bed_temperature_initial_layer.")
+ new_def("first_layer_temperature", coInts, "First layer temperature", "Vector of first layer temperatures for each extruder/filament.")
+}
+
+
+TimestampsConfigDef::TimestampsConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("timestamp", coString);
+ def->label = L("Timestamp");
+ def->tooltip = L("String containing current time in yyyyMMdd-hhmmss format.");
+
+ def = this->add("year", coInt);
+ def->label = L("Year");
+
+ def = this->add("month", coInt);
+ def->label = L("Month");
+
+ def = this->add("day", coInt);
+ def->label = L("Day");
+
+ def = this->add("hour", coInt);
+ def->label = L("Hour");
+
+ def = this->add("minute", coInt);
+ def->label = L("Minute");
+
+ def = this->add("second", coInt);
+ def->label = L("Second");
+}
+
+OtherPresetsConfigDef::OtherPresetsConfigDef()
+{
+ ConfigOptionDef* def;
+
+ def = this->add("print_preset", coString);
+ def->label = L("Print preset name");
+ def->tooltip = L("Name of the print preset used for slicing.");
+
+ def = this->add("filament_preset", coString);
+ def->label = L("Filament preset name");
+ def->tooltip = L("Names of the filament presets used for slicing. The variable is a vector "
+ "containing one name for each extruder.");
+
+ def = this->add("printer_preset", coString);
+ def->label = L("Printer preset name");
+ def->tooltip = L("Name of the printer preset used for slicing.");
+
+ def = this->add("physical_printer_preset", coString);
+ def->label = L("Physical printer name");
+ def->tooltip = L("Name of the physical printer used for slicing.");
+
+ // Options from PS not used in Orca
+ // def = this->add("num_extruders", coInt);
+ // def->label = L("Number of extruders");
+ // def->tooltip = L("Total number of extruders, regardless of whether they are used in the current print.");
+}
+
+
+static std::map s_CustomGcodeSpecificPlaceholders{
+ // Machine Gcode
+ {"machine_start_gcode", {}},
+ {"machine_end_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}},
+ {"before_layer_change_gcode", {"layer_num", "layer_z", "max_layer_z"}},
+ {"layer_change_gcode", {"layer_num", "layer_z", "max_layer_z"}},
+ {"timelapse_gcode", {"layer_num", "layer_z", "max_layer_z"}},
+ {"change_filament_gcode", {"layer_num", "layer_z", "max_layer_z", "next_extruder", "previous_extruder", "fan_speed",
+ "first_flush_volume", "flush_length_1", "flush_length_2", "flush_length_3", "flush_length_4",
+ "new_filament_e_feedrate", "new_filament_temp", "new_retract_length",
+ "new_retract_length_toolchange", "old_filament_e_feedrate", "old_filament_temp", "old_retract_length",
+ "old_retract_length_toolchange", "relative_e_axis", "second_flush_volume", "toolchange_count", "toolchange_z",
+ "travel_point_1_x", "travel_point_1_y", "travel_point_2_x", "travel_point_2_y", "travel_point_3_x",
+ "travel_point_3_y", "x_after_toolchange", "y_after_toolchange", "z_after_toolchange"}},
+ {"change_extrusion_role_gcode", {"layer_num", "layer_z", "extrusion_role", "last_extrusion_role"}},
+ {"printing_by_object_gcode", {}},
+ {"machine_pause_gcode", {}},
+ {"template_custom_gcode", {}},
+ //Filament Gcode
+ {"filament_start_gcode", {"filament_extruder_id"}},
+ {"filament_end_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}},
+};
+
+const std::map& custom_gcode_specific_placeholders()
+{
+ return s_CustomGcodeSpecificPlaceholders;
+}
+
+CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef()
+{
+ ConfigOptionDef* def;
+
+// Common Defs
+ def = this->add("layer_num", coInt);
+ def->label = L("Layer number");
+ def->tooltip = L("Index of the current layer. One-based (i.e. first layer is number 1).");
+
+ def = this->add("layer_z", coFloat);
+ def->label = L("Layer z");
+ def->tooltip = L("Height of the current layer above the print bed, measured to the top of the layer.");
+
+ def = this->add("max_layer_z", coFloat);
+ def->label = L("Maximal layer z");
+ def->tooltip = L("Height of the last layer above the print bed.");
+
+ def = this->add("filament_extruder_id", coInt);
+ def->label = L("Filament extruder ID");
+ def->tooltip = L("The current extruder ID. The same as current_extruder.");
+
+// change_filament_gcode
+ new_def("previous_extruder", coInt, "Previous extruder", "Index of the extruder that is being unloaded. The index is zero based (first extruder has index 0).");
+ new_def("next_extruder", coInt, "Next extruder", "Index of the extruder that is being loaded. The index is zero based (first extruder has index 0).");
+ new_def("relative_e_axis", coBool, "Relative e-axis", "Indicates if relative positioning is being used");
+ new_def("toolchange_count", coInt, "Toolchange count", "The number of toolchanges throught the print");
+ new_def("fan_speed", coNone, "", ""); //Option is no longer used and is zeroed by placeholder parser for compatability
+ new_def("old_retract_length", coFloat, "Old retract length", "The retraction length of the previous filament");
+ new_def("new_retract_length", coFloat, "New retract length", "The retraction lenght of the new filament");
+ new_def("old_retract_length_toolchange", coFloat, "Old retract length toolchange", "The toolchange retraction length of the previous filament");
+ new_def("new_retract_length_toolchange", coFloat, "New retract length toolchange", "The toolchange retraction length of the new filament");
+ new_def("old_filament_temp", coInt, "Old filament temp", "The old filament temp");
+ new_def("new_filament_temp", coInt, "New filament temp", "The new filament temp");
+ new_def("x_after_toolchange", coFloat, "X after toolchange", "The x pos after toolchange");
+ new_def("y_after_toolchange", coFloat, "Y after toolchange", "The y pos after toolchange");
+ new_def("z_after_toolchange", coFloat, "Z after toolchange", "The z pos after toolchange");
+ new_def("first_flush_volume", coFloat, "First flush volume", "The first flush volume");
+ new_def("second_flush_volume", coFloat, "Second flush volume", "The second flush volume");
+ new_def("old_filament_e_feedrate", coInt, "Old filament e feedrate", "The old filament extruder feedrate");
+ new_def("new_filament_e_feedrate", coInt, "New filament e feedrate", "The new filament extruder feedrate");
+ new_def("travel_point_1_x", coFloat, "Travel point 1 x", "The travel point 1 x");
+ new_def("travel_point_1_y", coFloat, "Travel point 1 y", "The travel point 1 y");
+ new_def("travel_point_2_x", coFloat, "Travel point 2 x", "The travel point 2 x");
+ new_def("travel_point_2_y", coFloat, "Travel point 2 y", "The travel point 2 y");
+ new_def("travel_point_3_x", coFloat, "Travel point 3 x", "The travel point 3 x");
+ new_def("travel_point_3_y", coFloat, "Travel point 3 y", "The travel point 3 y");
+ new_def("flush_length_1", coFloat, "Flush Length 1", "The first flush length");
+ new_def("flush_length_2", coFloat, "Flush Length 2", "The second flush length");
+ new_def("flush_length_3", coFloat, "Flush Length 3", "The third flush length");
+ new_def("flush_length_4", coFloat, "Flush Length 4", "The fourth flush length");
+
+// change_extrusion_role_gcode
+ std::string extrusion_role_types = "Possible Values:\n[\"Perimeter\", \"ExternalPerimeter\", "
+ "\"OverhangPerimeter\", \"InternalInfill\", \"SolidInfill\", \"TopSolidInfill\", \"BottomSurface\", \"BridgeInfill\", \"GapFill\", \"Ironing\", "
+ "\"Skirt\", \"Brim\", \"SupportMaterial\", \"SupportMaterialInterface\", \"SupportTransition\", \"WipeTower\", \"Mixed\"]";
+
+ new_def("extrusion_role", coString, "Extrusion role", "The new extrusion role/type that is going to be used\n" + extrusion_role_types);
+ new_def("last_extrusion_role", coString, "Last extrusion role", "The previously used extrusion role/type\nPossible Values:\n" + extrusion_role_types);
+}
+
+const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def;
+
+#undef new_def
+
uint64_t ModelConfig::s_last_timestamp = 1;
static Points to_points(const std::vector &dpts)
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 7638471c56..f894a24ca8 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -1464,6 +1464,74 @@ public:
CLIMiscConfigDef();
};
+typedef std::string t_custom_gcode_key;
+// This map containes list of specific placeholders for each custom G-code, if any exist
+const std::map& custom_gcode_specific_placeholders();
+
+// Next classes define placeholders used by GUI::EditGCodeDialog.
+
+class ReadOnlySlicingStatesConfigDef : public ConfigDef
+{
+public:
+ ReadOnlySlicingStatesConfigDef();
+};
+
+class ReadWriteSlicingStatesConfigDef : public ConfigDef
+{
+public:
+ ReadWriteSlicingStatesConfigDef();
+};
+
+class OtherSlicingStatesConfigDef : public ConfigDef
+{
+public:
+ OtherSlicingStatesConfigDef();
+};
+
+class PrintStatisticsConfigDef : public ConfigDef
+{
+public:
+ PrintStatisticsConfigDef();
+};
+
+class ObjectsInfoConfigDef : public ConfigDef
+{
+public:
+ ObjectsInfoConfigDef();
+};
+
+class DimensionsConfigDef : public ConfigDef
+{
+public:
+ DimensionsConfigDef();
+};
+
+class TemperaturesConfigDef : public ConfigDef
+{
+public:
+ TemperaturesConfigDef();
+};
+
+class TimestampsConfigDef : public ConfigDef
+{
+public:
+ TimestampsConfigDef();
+};
+
+class OtherPresetsConfigDef : public ConfigDef
+{
+public:
+ OtherPresetsConfigDef();
+};
+
+// This classes defines all custom G-code specific placeholders.
+class CustomGcodeSpecificConfigDef : public ConfigDef
+{
+public:
+ CustomGcodeSpecificConfigDef();
+};
+extern const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def;
+
// This class defines the command line options representing actions.
extern const CLIActionsConfigDef cli_actions_config_def;
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index eb1599baf0..3fd171528e 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -144,6 +144,11 @@ const std::string& sys_shapes_dir();
// Return a full path to the custom shapes gallery directory.
std::string custom_shapes_dir();
+// Set a path with shapes gallery files.
+void set_custom_gcodes_dir(const std::string &path);
+// Return a full path to the system shapes gallery directory.
+const std::string& custom_gcodes_dir();
+
// Set a path with preset files.
void set_data_dir(const std::string &path);
// Return a full path to the GUI resource files.
diff --git a/src/libslic3r/libslic3r_version.h.in b/src/libslic3r/libslic3r_version.h.in
index 78809f1f6d..90383c93af 100644
--- a/src/libslic3r/libslic3r_version.h.in
+++ b/src/libslic3r/libslic3r_version.h.in
@@ -10,5 +10,6 @@
//#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@"
#define BBL_RELEASE_TO_PUBLIC @BBL_RELEASE_TO_PUBLIC@
#define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@
+#define ORCA_CHECK_GCODE_PLACEHOLDERS @ORCA_CHECK_GCODE_PLACEHOLDERS@
#endif /* __SLIC3R_VERSION_H */
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index 387d367385..1a5422b7bb 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -265,6 +265,18 @@ const std::string& sys_shapes_dir()
return g_sys_shapes_dir;
}
+static std::string g_custom_gcodes_dir;
+
+void set_custom_gcodes_dir(const std::string &dir)
+{
+ g_custom_gcodes_dir = dir;
+}
+
+const std::string& custom_gcodes_dir()
+{
+ return g_custom_gcodes_dir;
+}
+
// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
static std::string g_data_dir;
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index c55ff32741..5386aad933 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -212,6 +212,8 @@ set(SLIC3R_GUI_SOURCES
GUI/PresetComboBoxes.cpp
GUI/BitmapComboBox.hpp
GUI/BitmapComboBox.cpp
+ GUI/EditGCodeDialog.hpp
+ GUI/EditGCodeDialog.cpp
GUI/SavePresetDialog.hpp
GUI/SavePresetDialog.cpp
GUI/GUI_Colors.hpp
diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp
new file mode 100644
index 0000000000..54ec6db8bc
--- /dev/null
+++ b/src/slic3r/GUI/EditGCodeDialog.cpp
@@ -0,0 +1,976 @@
+#include "EditGCodeDialog.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "GUI.hpp"
+#include "GUI_App.hpp"
+#include "MainFrame.hpp"
+#include "format.hpp"
+#include "Tab.hpp"
+#include "wxExtensions.hpp"
+#include "BitmapCache.hpp"
+#include "ExtraRenderers.hpp"
+#include "MsgDialog.hpp"
+#include "Plater.hpp"
+
+#include "libslic3r/PlaceholderParser.hpp"
+#include "libslic3r/Preset.hpp"
+#include "libslic3r/Print.hpp"
+
+#define BTN_GAP FromDIP(20)
+#define BTN_SIZE wxSize(FromDIP(58), FromDIP(24))
+
+namespace Slic3r {
+namespace GUI {
+
+//------------------------------------------
+// EditGCodeDialog
+//------------------------------------------
+
+EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const std::string& value) :
+ DPIDialog(parent, wxID_ANY, format_wxstr(_L("Edit Custom G-code (%1%)"), key), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
+{
+ SetFont(wxGetApp().normal_font());
+ SetBackgroundColour(*wxWHITE);
+ wxGetApp().UpdateDarkUI(this);
+ wxGetApp().UpdateDlgDarkUI(this);
+
+ int border = 10;
+ int em = em_unit();
+
+ wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Built-in placeholders (Double click item to add to G-code)") + ":");
+
+ auto* grid_sizer = new wxFlexGridSizer(1, 3, 5, 15);
+ grid_sizer->SetFlexibleDirection(wxBOTH);
+
+ auto* param_sizer = new wxBoxSizer(wxVERTICAL);
+
+ m_search_bar = new wxSearchCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
+ m_search_bar->ShowSearchButton(true);
+ m_search_bar->ShowCancelButton(true);
+ m_search_bar->SetDescriptiveText(_L("Search gcode placeholders"));
+ m_search_bar->SetForegroundColour(*wxBLACK);
+ wxGetApp().UpdateDarkUI(m_search_bar);
+
+ m_search_bar->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent&) {
+// this->on_search_update();
+ });
+ m_search_bar->Bind(wxEVT_COMMAND_TEXT_UPDATED, [this](wxCommandEvent&) {
+ this->on_search_update();
+ });
+
+ param_sizer->Add(m_search_bar, 0, wxEXPAND | wxALL, border);
+
+ m_params_list = new ParamsViewCtrl(this, wxSize(em * 45, em * 70));
+ m_params_list->SetFont(wxGetApp().code_font());
+ wxGetApp().UpdateDarkUI(m_params_list);
+ param_sizer->Add(m_params_list, 0, wxEXPAND | wxALL, border);
+
+ m_add_btn = new ScalableButton(this, wxID_ANY, "add_copies");
+ m_add_btn->SetToolTip(_L("Add selected placeholder to G-code"));
+
+ m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 75, em * 70), wxTE_MULTILINE
+#ifdef _WIN32
+ | wxBORDER_SIMPLE
+#endif
+ );
+ m_gcode_editor->SetFont(wxGetApp().code_font());
+ m_gcode_editor->SetInsertionPointEnd();
+ wxGetApp().UpdateDarkUI(m_gcode_editor);
+
+ grid_sizer->Add(param_sizer, 1, wxEXPAND);
+ grid_sizer->Add(m_add_btn, 0, wxTOP, m_params_list->GetSize().y/2);
+ grid_sizer->Add(m_gcode_editor, 2, wxEXPAND);
+
+ grid_sizer->AddGrowableRow(0, 1);
+ grid_sizer->AddGrowableCol(0, 1);
+ grid_sizer->AddGrowableCol(2, 1);
+
+ m_param_label = new wxStaticText(this, wxID_ANY, _L("Select placeholder"));
+ m_param_label->SetFont(wxGetApp().bold_font());
+
+ m_param_description = new wxStaticText(this, wxID_ANY, wxEmptyString);
+
+ //Orca: use custom buttons
+ auto btn_sizer = create_btn_sizer(wxOK | wxCANCEL);
+ for(auto btn : m_button_list)
+ wxGetApp().UpdateDarkUI(btn.second);
+
+ wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
+
+ topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+ topSizer->Add(grid_sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+ topSizer->Add(m_param_label , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+ topSizer->Add(m_param_description , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
+ topSizer->Add(btn_sizer , 0, wxEXPAND | wxALL, border);
+
+ SetSizer(topSizer);
+ topSizer->SetSizeHints(this);
+
+ this->Fit();
+ this->Layout();
+
+ this->CenterOnScreen();
+
+ init_params_list(key);
+ bind_list_and_button();
+}
+
+EditGCodeDialog::~EditGCodeDialog()
+{
+ // To avoid redundant process of wxEVT_DATAVIEW_SELECTION_CHANGED after dialog distroing (on Linux)
+ // unbind this event from params_list
+ m_params_list->Unbind(wxEVT_DATAVIEW_SELECTION_CHANGED, &EditGCodeDialog::selection_changed, this);
+}
+
+std::string EditGCodeDialog::get_edited_gcode() const
+{
+ return into_u8(m_gcode_editor->GetValue());
+}
+
+void EditGCodeDialog::on_search_update()
+{
+ wxString search_text = m_search_bar->GetValue().Lower();
+ if (search_text.empty())
+ m_params_list->model->FinishSearch();
+ else
+ m_params_list->model->RefreshSearch(search_text);
+}
+
+static ParamType get_type(const std::string& opt_key, const ConfigOptionDef& opt_def)
+{
+ return opt_def.is_scalar() ? ParamType::Scalar : ParamType::Vector;
+}
+
+void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name)
+{
+ const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders();
+ const auto& specific_params = custom_gcode_placeholders.count(custom_gcode_name) > 0 ?
+ custom_gcode_placeholders.at(custom_gcode_name) : t_config_option_keys({});
+
+ // Add slicing states placeholders
+
+ wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing State"), "custom-gcode_slicing-state_global");
+ if (!cgp_ro_slicing_states_config_def.empty()) {
+ wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed");
+ for (const auto& [opt_key, def]: cgp_ro_slicing_states_config_def.options)
+ m_params_list->AppendParam(read_only, get_type(opt_key, def), opt_key);
+ }
+
+ if (!cgp_rw_slicing_states_config_def.empty()) {
+ wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read Write"), "lock_open");
+ for (const auto& [opt_key, def] : cgp_rw_slicing_states_config_def.options)
+ m_params_list->AppendParam(read_write, get_type(opt_key, def), opt_key);
+ }
+
+ // add other universal params, which are related to slicing state
+ if (!cgp_other_slicing_states_config_def.empty()) {
+ slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "custom-gcode_slicing-state");
+ for (const auto& [opt_key, def] : cgp_other_slicing_states_config_def.options)
+ m_params_list->AppendParam(slicing_state, get_type(opt_key, def), opt_key);
+ }
+
+ // Add universal placeholders
+
+ {
+ // Add print statistics subgroup
+
+ if (!cgp_print_statistics_config_def.empty()) {
+ wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print Statistics"), "custom-gcode_stats");
+ for (const auto& [opt_key, def] : cgp_print_statistics_config_def.options)
+ m_params_list->AppendParam(statistics, get_type(opt_key, def), opt_key);
+ }
+
+ // Add objects info subgroup
+
+ if (!cgp_objects_info_config_def.empty()) {
+ wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects Info"), "custom-gcode_object-info");
+ for (const auto& [opt_key, def] : cgp_objects_info_config_def.options)
+ m_params_list->AppendParam(objects_info, get_type(opt_key, def), opt_key);
+ }
+
+ // Add dimensions subgroup
+
+ if (!cgp_dimensions_config_def.empty()) {
+ wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Dimensions"), "custom-gcode_measure");
+ for (const auto& [opt_key, def] : cgp_dimensions_config_def.options)
+ m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key);
+ }
+
+ // Add temperature subgroup
+
+ if (!cgp_temperatures_config_def.empty()) {
+ wxDataViewItem temperatures = m_params_list->AppendGroup(_L("Temperatures"), "custom-gcode_temperature");
+ for (const auto& [opt_key, def] : cgp_temperatures_config_def.options)
+ m_params_list->AppendParam(temperatures, get_type(opt_key, def), opt_key);
+ }
+
+ // Add timestamp subgroup
+
+ if (!cgp_timestamps_config_def.empty()) {
+ wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Timestamps"), "print-time");
+ for (const auto& [opt_key, def] : cgp_timestamps_config_def.options)
+ m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key);
+ }
+ }
+
+ // Add specific placeholders
+
+ if (!specific_params.empty()) {
+ wxDataViewItem group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "custom-gcode_gcode");
+ for (const auto& opt_key : specific_params)
+ if (auto def = custom_gcode_specific_config_def.get(opt_key); def && def->type != coNone) {
+ m_params_list->AppendParam(group, get_type(opt_key, *def), opt_key);
+ }
+ m_params_list->Expand(group);
+ }
+
+ // Add placeholders from presets
+
+ wxDataViewItem presets = add_presets_placeholders();
+ // add other params which are related to presets
+ if (!cgp_other_presets_config_def.empty())
+ for (const auto& [opt_key, def] : cgp_other_presets_config_def.options)
+ m_params_list->AppendParam(presets, get_type(opt_key, def), opt_key);
+}
+
+wxDataViewItem EditGCodeDialog::add_presets_placeholders()
+{
+ auto get_set_from_vec = [](const std::vector&vec) {
+ return std::set(vec.begin(), vec.end());
+ };
+
+ const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF;
+ const std::set print_options = get_set_from_vec(is_fff ? Preset::print_options() : Preset::sla_print_options());
+ const std::set material_options = get_set_from_vec(is_fff ? Preset::filament_options() : Preset::sla_material_options());
+ const std::set printer_options = get_set_from_vec(is_fff ? Preset::printer_options() : Preset::sla_printer_options());
+ const auto& full_config = wxGetApp().preset_bundle->full_config();
+ const auto& tab_list = wxGetApp().tabs_list;
+
+ Tab* tab_print;
+ Tab* tab_filament;
+ Tab* tab_printer;
+ for (const auto tab : tab_list) {
+ if (tab->m_type == Preset::TYPE_PRINT)
+ tab_print = tab;
+ else if (tab->m_type == Preset::TYPE_FILAMENT)
+ tab_filament = tab;
+ else if (tab->m_type == Preset::TYPE_PRINTER)
+ tab_printer = tab;
+ }
+
+
+ // Orca: create subgroups from the pages of the tabs
+ auto init_from_tab = [this, full_config](wxDataViewItem parent, Tab* tab, const set& preset_keys){
+ set extra_keys(preset_keys);
+ for (const auto& page : tab->m_pages) {
+ wxDataViewItem subgroup = m_params_list->AppendSubGroup(parent, page->title(), "empty");
+ std::set opt_keys;
+ for (const auto& optgroup : page->m_optgroups)
+ for (const auto& opt : optgroup->opt_map())
+ opt_keys.emplace(opt.first);
+
+ for (const auto& opt_key : opt_keys)
+ if (const ConfigOption* optptr = full_config.optptr(opt_key)) {
+ extra_keys.erase(opt_key);
+ m_params_list->AppendParam(subgroup, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt_key);
+ }
+ }
+ for (auto opt_key : extra_keys)
+ if (const ConfigOption* optptr = full_config.optptr(opt_key))
+ m_params_list->AppendParam(parent, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt_key);
+ };
+
+ wxDataViewItem group = m_params_list->AppendGroup(_L("Presets"), "cog");
+
+ wxDataViewItem print = m_params_list->AppendSubGroup(group, _L("Print settings"), "cog");
+ init_from_tab(print, tab_print, print_options);
+
+ wxDataViewItem material = m_params_list->AppendSubGroup(group, _(is_fff ? L("Filament settings") : L("SLA Materials settings")), is_fff ? "filament" : "resin");
+ init_from_tab(material, tab_filament, material_options);
+
+ wxDataViewItem printer = m_params_list->AppendSubGroup(group, _L("Printer settings"), is_fff ? "printer" : "sla_printer");
+ init_from_tab(printer, tab_printer, printer_options);
+
+ return group;
+}
+
+void EditGCodeDialog::add_selected_value_to_gcode()
+{
+ const wxString val = m_params_list->GetSelectedValue();
+ if (val.IsEmpty())
+ return;
+
+ m_gcode_editor->WriteText(m_gcode_editor->GetInsertionPoint() == m_gcode_editor->GetLastPosition() ? "\n" + val : val);
+
+ if (val.Last() == ']') {
+ const long new_pos = m_gcode_editor->GetInsertionPoint();
+ if (val[val.Len() - 2] == '[')
+ m_gcode_editor->SetInsertionPoint(new_pos - 1); // set cursor into brackets
+ else
+ m_gcode_editor->SetSelection(new_pos - 17, new_pos - 1); // select "current_extruder"
+ }
+
+ m_gcode_editor->SetFocus();
+}
+
+void EditGCodeDialog::selection_changed(wxDataViewEvent& evt)
+{
+ wxString label;
+ wxString description;
+
+ const std::string opt_key = m_params_list->GetSelectedParamKey();
+ if (!opt_key.empty()) {
+ const ConfigOptionDef* def { nullptr };
+
+ for (const ConfigDef* config: std::initializer_list {
+ &custom_gcode_specific_config_def,
+ &cgp_ro_slicing_states_config_def,
+ &cgp_rw_slicing_states_config_def,
+ &cgp_other_slicing_states_config_def,
+ &cgp_print_statistics_config_def,
+ &cgp_objects_info_config_def,
+ &cgp_dimensions_config_def,
+ &cgp_temperatures_config_def,
+ &cgp_timestamps_config_def,
+ &cgp_other_presets_config_def
+ }) {
+ if (config->has(opt_key)) {
+ def = config->get(opt_key);
+ break;
+ }
+ }
+ // Orca: move below checking for def in custom defined gcode placeholders
+ // This allows custom placeholders to override the default ones for this dialog
+ // Override custom def if selection is within the preset category
+ if (!def || m_params_list->GetSelectedTopLevelCategory() == "Presets") {
+ const auto& full_config = wxGetApp().preset_bundle->full_config();
+ if (const ConfigDef* config_def = full_config.def(); config_def && config_def->has(opt_key)) {
+ def = config_def->get(opt_key);
+ }
+ }
+
+ if (def) {
+ const ConfigOptionType scalar_type = def->is_scalar() ? def->type : static_cast(def->type - coVectorType);
+ wxString type_str = scalar_type == coNone ? "none" :
+ scalar_type == coFloat ? "float" :
+ scalar_type == coInt ? "integer" :
+ scalar_type == coString ? "string" :
+ scalar_type == coPercent ? "percent" :
+ scalar_type == coFloatOrPercent ? "float or percent" :
+ scalar_type == coPoint ? "point" :
+ scalar_type == coBool ? "bool" :
+ scalar_type == coEnum ? "enum" : "undef";
+ if (!def->is_scalar())
+ type_str += "[]";
+
+ label = (!def || (def->full_label.empty() && def->label.empty()) ) ? format_wxstr("%1%\n(%2%)", opt_key, type_str) :
+ (!def->full_label.empty() && !def->label.empty() ) ?
+ format_wxstr("%1% > %2%\n(%3%)", _(def->full_label), _(def->label), type_str) :
+ format_wxstr("%1%\n(%2%)", def->label.empty() ? _(def->full_label) : _(def->label), type_str);
+
+ if (def)
+ description = get_wraped_wxString(_(def->tooltip), 120);
+ }
+ else
+ label = "Undef optptr";
+ }
+
+ m_param_label->SetLabel(label);
+ m_param_description->SetLabel(description);
+
+ Layout();
+}
+
+void EditGCodeDialog::bind_list_and_button()
+{
+ m_params_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &EditGCodeDialog::selection_changed, this);
+
+ m_params_list->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& ) {
+ add_selected_value_to_gcode();
+ });
+
+ m_add_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
+ add_selected_value_to_gcode();
+ });
+}
+
+void EditGCodeDialog::on_dpi_changed(const wxRect&suggested_rect)
+{
+ const int& em = em_unit();
+
+ //Orca: use custom buttons
+ for (auto button_item : m_button_list)
+ {
+ if (button_item.first == wxOK) {
+ button_item.second->SetMinSize(BTN_SIZE);
+ button_item.second->SetCornerRadius(FromDIP(12));
+ }
+ if (button_item.first == wxCANCEL) {
+ button_item.second->SetMinSize(BTN_SIZE);
+ button_item.second->SetCornerRadius(FromDIP(12));
+ }
+ }
+
+ const wxSize& size = wxSize(45 * em, 35 * em);
+ SetMinSize(size);
+
+ Fit();
+ Refresh();
+}
+
+void EditGCodeDialog::on_sys_color_changed()
+{
+ m_add_btn->msw_rescale();
+}
+
+//Orca
+wxBoxSizer* EditGCodeDialog::create_btn_sizer(long flags)
+{
+ auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
+ btn_sizer->AddStretchSpacer();
+
+ StateColor ok_btn_bg(
+ std::pair(wxColour(0, 137, 123), StateColor::Pressed),
+ std::pair(wxColour(38, 166, 154), StateColor::Hovered),
+ std::pair(wxColour(0, 150, 136), StateColor::Normal)
+ );
+
+ StateColor ok_btn_bd(
+ std::pair(wxColour(0, 150, 136), StateColor::Normal)
+ );
+
+ StateColor ok_btn_text(
+ std::pair(wxColour(255, 255, 254), StateColor::Normal)
+ );
+
+ StateColor cancel_btn_bg(
+ std::pair(wxColour(206, 206, 206), StateColor::Pressed),
+ std::pair(wxColour(238, 238, 238), StateColor::Hovered),
+ std::pair(wxColour(255, 255, 255), StateColor::Normal)
+ );
+
+ StateColor cancel_btn_bd_(
+ std::pair(wxColour(38, 46, 48), StateColor::Normal)
+ );
+
+ StateColor cancel_btn_text(
+ std::pair(wxColour(38, 46, 48), StateColor::Normal)
+ );
+
+
+ StateColor calc_btn_bg(
+ std::pair(wxColour(0, 137, 123), StateColor::Pressed),
+ std::pair(wxColour(38, 166, 154), StateColor::Hovered),
+ std::pair(wxColour(0, 150, 136), StateColor::Normal)
+ );
+
+ StateColor calc_btn_bd(
+ std::pair(wxColour(0, 150, 136), StateColor::Normal)
+ );
+
+ StateColor calc_btn_text(
+ std::pair(wxColour(255, 255, 254), StateColor::Normal)
+ );
+
+ if (flags & wxOK) {
+ Button* ok_btn = new Button(this, _L("OK"));
+ ok_btn->SetMinSize(BTN_SIZE);
+ ok_btn->SetCornerRadius(FromDIP(12));
+ ok_btn->SetBackgroundColor(ok_btn_bg);
+ ok_btn->SetBorderColor(ok_btn_bd);
+ ok_btn->SetTextColor(ok_btn_text);
+ ok_btn->SetFocus();
+ ok_btn->SetId(wxID_OK);
+ btn_sizer->Add(ok_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, BTN_GAP);
+ m_button_list[wxOK] = ok_btn;
+ }
+ if (flags & wxCANCEL) {
+ Button* cancel_btn = new Button(this, _L("Cancel"));
+ cancel_btn->SetMinSize(BTN_SIZE);
+ cancel_btn->SetCornerRadius(FromDIP(12));
+ cancel_btn->SetBackgroundColor(cancel_btn_bg);
+ cancel_btn->SetBorderColor(cancel_btn_bd_);
+ cancel_btn->SetTextColor(cancel_btn_text);
+ cancel_btn->SetId(wxID_CANCEL);
+ btn_sizer->Add(cancel_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, BTN_GAP / 2);
+ m_button_list[wxCANCEL] = cancel_btn;
+ }
+
+ return btn_sizer;
+}
+
+
+const std::map ParamsInfo {
+// Type BitmapName
+ { ParamType::Scalar, "custom-gcode_single" },
+ { ParamType::Vector, "custom-gcode_vector" },
+ { ParamType::FilamentVector,"custom-gcode_vector-index" },
+};
+
+static void make_bold(wxString& str)
+{
+#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
+ str = format_wxstr("%1%", str);
+#endif
+}
+
+static void highlight(wxString& str)
+{
+#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
+ str = format_wxstr("%1%", str);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// ParamsModelNode: a node inside ParamsModel
+// ----------------------------------------------------------------------------
+
+ParamsNode::ParamsNode(const wxString& group_name, const std::string& icon_name, wxDataViewCtrl* ctrl)
+: icon_name(icon_name)
+, text(group_name)
+, m_ctrl(ctrl)
+, m_bold(true)
+{
+}
+
+ParamsNode::ParamsNode( ParamsNode * parent,
+ const wxString& sub_group_name,
+ const std::string& icon_name,
+ wxDataViewCtrl* ctrl)
+ : m_parent(parent)
+ , icon_name(icon_name)
+ , text(sub_group_name)
+ , m_ctrl(ctrl)
+ , m_bold(true)
+{
+}
+
+ParamsNode::ParamsNode( ParamsNode* parent,
+ ParamType param_type,
+ const std::string& param_key,
+ wxDataViewCtrl* ctrl)
+ : m_parent(parent)
+ , m_param_type(param_type)
+ , m_container(false)
+ , param_key(param_key)
+ , m_ctrl(ctrl)
+{
+ text = from_u8(param_key);
+ if (param_type == ParamType::Vector)
+ text += "[]";
+ else if (param_type == ParamType::FilamentVector)
+ text += "[current_extruder]";
+
+ icon_name = ParamsInfo.at(param_type);
+}
+
+wxString ParamsNode::GetFormattedText()
+{
+ wxString formatted_text(text);
+ if (m_highlight_index) {
+ wxString substr = formatted_text.substr(m_highlight_index->first, m_highlight_index->second);
+ formatted_text = formatted_text.Remove(m_highlight_index->first, m_highlight_index->second);
+ highlight(substr);
+ formatted_text.insert(m_highlight_index->first, substr);
+ }
+
+ if (m_bold)
+ make_bold(formatted_text);
+
+ return formatted_text;
+}
+
+void ParamsNode::StartSearch()
+{
+ const wxDataViewItem item(this);
+ m_expanded_before_search = m_ctrl->IsExpanded(item);
+ if (!GetChildren().empty())
+ for (const auto& child : GetChildren())
+ child->StartSearch();
+}
+
+void ParamsNode::RefreshSearch(const wxString& search_text)
+{
+ if (!GetChildren().empty())
+ for (auto& child : GetChildren())
+ child->RefreshSearch(search_text);
+
+ if (GetEnabledChildren().empty())
+ if (auto pos = text.find(search_text); IsParamNode() && pos != wxString::npos) {
+ m_highlight_index = make_unique>(pos, search_text.Len());
+ Enable();
+ } else {
+ Disable();
+ }
+ else
+ Enable();
+}
+
+void ParamsNode::FinishSearch()
+{
+ Enable();
+ m_highlight_index.reset();
+ const wxDataViewItem item(this);
+ if (!GetChildren().empty())
+ for (const auto& child : GetChildren())
+ child->FinishSearch();
+ m_expanded_before_search ? m_ctrl->Expand(item) : m_ctrl->Collapse(item);
+}
+
+wxDataViewItemArray ParamsNode::GetEnabledChildren() {
+ wxDataViewItemArray array;
+ for (const std::unique_ptr& child : m_children)
+ if (child->IsEnabled())
+ array.Add(wxDataViewItem(child.get()));
+ return array;
+}
+
+
+// ----------------------------------------------------------------------------
+// ParamsModel
+// ----------------------------------------------------------------------------
+
+ParamsModel::ParamsModel()
+{
+}
+
+wxDataViewItem ParamsModel::AppendGroup(const wxString& group_name,
+ const std::string& icon_name)
+{
+ m_group_nodes.emplace_back(std::make_unique(group_name, icon_name, m_ctrl));
+
+ wxDataViewItem parent(nullptr);
+ wxDataViewItem child((void*)m_group_nodes.back().get());
+
+ ItemAdded(parent, child);
+ m_ctrl->Expand(parent);
+ return child;
+}
+
+wxDataViewItem ParamsModel::AppendSubGroup(wxDataViewItem parent,
+ const wxString& sub_group_name,
+ const std::string& icon_name)
+{
+ ParamsNode* parent_node = static_cast(parent.GetID());
+ if (!parent_node)
+ return wxDataViewItem(0);
+
+ parent_node->Append(std::make_unique(parent_node, sub_group_name, icon_name, m_ctrl));
+ const wxDataViewItem sub_group_item((void*)parent_node->GetChildren().back().get());
+
+ ItemAdded(parent, sub_group_item);
+ return sub_group_item;
+}
+
+wxDataViewItem ParamsModel::AppendParam(wxDataViewItem parent,
+ ParamType param_type,
+ const std::string& param_key)
+{
+ ParamsNode* parent_node = static_cast(parent.GetID());
+ if (!parent_node)
+ return wxDataViewItem(0);
+
+ parent_node->Append(std::make_unique(parent_node, param_type, param_key, m_ctrl));
+
+ const wxDataViewItem child_item((void*)parent_node->GetChildren().back().get());
+
+ ItemAdded(parent, child_item);
+ return child_item;
+}
+
+wxString ParamsModel::GetParamName(wxDataViewItem item)
+{
+ if (item.IsOk()) {
+ ParamsNode* node = static_cast(item.GetID());
+ if (node->IsParamNode())
+ return node->text;
+ }
+ return wxEmptyString;
+}
+
+std::string ParamsModel::GetParamKey(wxDataViewItem item)
+{
+ if (item.IsOk()) {
+ ParamsNode* node = static_cast(item.GetID());
+ return node->param_key;
+ }
+ return std::string();
+}
+
+std::string ParamsModel::GetTopLevelCategory(wxDataViewItem item)
+{
+ if (item.IsOk()) {
+ ParamsNode* node = static_cast(item.GetID());
+ while (!node->IsGroupNode())
+ node = node->GetParent();
+ return node->text.ToStdString();
+ }
+ return std::string();
+}
+
+void ParamsModel::RefreshSearch(const wxString& search_text)
+{
+ if (!m_currently_searching) { // if not currently searching, save expansion state for all items
+ for (const auto& node : m_group_nodes)
+ node->StartSearch();
+ m_currently_searching = true;
+ }
+
+ for (const auto& node : m_group_nodes)
+ node->RefreshSearch(search_text); //Enable/Disable node based on search
+
+ Cleared(); //Reload the model into the control
+
+ for (const auto& node : m_group_nodes) // (re)expand all
+ m_ctrl->ExpandChildren(wxDataViewItem(node.get()));
+}
+
+void ParamsModel::FinishSearch()
+{
+ RefreshSearch("");
+ Cleared();
+ if (m_currently_searching) {
+ for (const auto& node : m_group_nodes)
+ node->FinishSearch();
+ m_currently_searching = false;
+ }
+}
+
+wxDataViewItem ParamsModel::Delete(const wxDataViewItem& item)
+{
+ auto ret_item = wxDataViewItem(nullptr);
+ ParamsNode* node = static_cast(item.GetID());
+ if (!node) // happens if item.IsOk()==false
+ return ret_item;
+
+ // first remove the node from the parent's array of children;
+ // NOTE: m_group_nodes is only a vector of _pointers_
+ // thus removing the node from it doesn't result in freeing it
+ ParamsNodePtrArray& children = node->GetChildren();
+ // Delete all children
+ while (!children.empty())
+ Delete(wxDataViewItem(children.back().get()));
+
+ auto node_parent = node->GetParent();
+
+ ParamsNodePtrArray& parents_children = node_parent ? node_parent->GetChildren() : m_group_nodes;
+ auto it = find_if(parents_children.begin(), parents_children.end(),
+ [node](std::unique_ptr& child) { return child.get() == node; });
+ assert(it != parents_children.end());
+ it = parents_children.erase(it);
+
+ if (it != parents_children.end())
+ ret_item = wxDataViewItem(it->get());
+
+ wxDataViewItem parent(node_parent);
+ // set m_container to FALSE if parent has no child
+ if (node_parent) {
+#ifndef __WXGTK__
+ if (node_parent->GetChildren().empty())
+ node_parent->SetContainer(false);
+#endif //__WXGTK__
+ ret_item = parent;
+ }
+
+ // notify control
+ ItemDeleted(parent, item);
+ return ret_item;
+}
+
+void ParamsModel::Clear()
+{
+ while (!m_group_nodes.empty())
+ Delete(wxDataViewItem(m_group_nodes.back().get()));
+}
+
+void ParamsModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const
+{
+ assert(item.IsOk());
+
+ ParamsNode* node = static_cast(item.GetID());
+ if (col == (unsigned int)0)
+#ifdef __linux__
+// variant << wxDataViewIconText(node->GetFormattedText(), get_bmp_bundle(node->icon_name)->GetIconFor(m_ctrl->GetParent())); //TODO: update to bundle with wx update
+ {
+ wxIcon icon;
+ icon.CopyFromBitmap(create_scaled_bitmap(node->icon_name, m_ctrl->GetParent()));
+ variant << wxDataViewIconText(node->GetFormattedText(), icon);
+ }
+#else
+// variant << DataViewBitmapText(node->GetFormattedText(), get_bmp_bundle(node->icon_name)->GetBitmapFor(m_ctrl->GetParent())); //TODO: update to bundle with wx update
+ variant << DataViewBitmapText(node->GetFormattedText(), create_scaled_bitmap(node->icon_name, m_ctrl->GetParent()));
+#endif //__linux__
+ else
+ wxLogError("DiffModel::GetValue: wrong column %d", col);
+}
+
+bool ParamsModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col)
+{
+ assert(item.IsOk());
+
+ ParamsNode* node = static_cast(item.GetID());
+ if (col == (unsigned int)0) {
+#ifdef __linux__
+ wxDataViewIconText data;
+ data << variant;
+ node->icon = data.GetIcon();
+#else
+ DataViewBitmapText data;
+ data << variant;
+ node->icon = data.GetBitmap();
+#endif
+ node->text = data.GetText();
+ return true;
+ }
+
+ wxLogError("DiffModel::SetValue: wrong column");
+ return false;
+}
+
+wxDataViewItem ParamsModel::GetParent(const wxDataViewItem&item) const
+{
+ // the invisible root node has no parent
+ if (!item.IsOk())
+ return wxDataViewItem(nullptr);
+
+ ParamsNode* node = static_cast(item.GetID());
+
+ if (node->IsGroupNode())
+ return wxDataViewItem(nullptr);
+
+ return wxDataViewItem((void*)node->GetParent());
+}
+
+bool ParamsModel::IsContainer(const wxDataViewItem& item) const
+{
+ // the invisble root node can have children
+ if (!item.IsOk())
+ return true;
+
+ ParamsNode* node = static_cast(item.GetID());
+ return node->IsContainer();
+}
+unsigned int ParamsModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const
+{
+ ParamsNode* parent_node = (ParamsNode*)parent.GetID();
+
+ if (parent_node == nullptr) {
+ for (const auto& group : m_group_nodes)
+ if (group->IsEnabled())
+ array.Add(wxDataViewItem((void*)group.get()));
+ }
+ else {
+ const ParamsNodePtrArray& children = parent_node->GetChildren();
+ for (const std::unique_ptr& child : children)
+ if (child->IsEnabled())
+ array.Add(wxDataViewItem((void*)child.get()));
+ }
+
+ return array.Count();
+}
+unsigned int ParamsModel::GetColumnCount() const { return 1; }
+wxString ParamsModel::GetColumnType(unsigned int col) const {
+#ifdef __linux__
+ return wxT("wxDataViewIconText");
+#else
+ return wxT("DataViewBitmapText");
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// ParamsViewCtrl
+// ----------------------------------------------------------------------------
+
+ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSize size)
+ : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_SINGLE | wxDV_NO_HEADER// | wxDV_ROW_LINES
+#ifdef _WIN32
+ | wxBORDER_SIMPLE
+#endif
+ ),
+ m_em_unit(em_unit(parent))
+{
+ wxGetApp().UpdateDVCDarkUI(this);
+
+ model = new ParamsModel();
+ this->AssociateModel(model);
+ model->SetAssociatedControl(this);
+
+#ifdef __linux__
+ wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer();
+#ifdef SUPPORTS_MARKUP
+ rd->EnableMarkup(true);
+#endif
+ wxDataViewColumn* column = new wxDataViewColumn("", rd, 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT);
+#else
+ wxDataViewColumn* column = new wxDataViewColumn("", new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
+#endif //__linux__
+ this->AppendColumn(column);
+ this->SetExpanderColumn(column);
+}
+
+wxDataViewItem ParamsViewCtrl::AppendGroup(const wxString& group_name, const std::string& icon_name)
+{
+ return model->AppendGroup(group_name, icon_name);
+}
+
+wxDataViewItem ParamsViewCtrl::AppendSubGroup( wxDataViewItem parent,
+ const wxString& sub_group_name,
+ const std::string& icon_name)
+{
+ return model->AppendSubGroup(parent, sub_group_name, icon_name);
+}
+
+wxDataViewItem ParamsViewCtrl::AppendParam( wxDataViewItem parent,
+ ParamType param_type,
+ const std::string& param_key)
+{
+ return model->AppendParam(parent, param_type, param_key);
+}
+
+wxString ParamsViewCtrl::GetValue(wxDataViewItem item)
+{
+ return model->GetParamName(item);
+}
+
+wxString ParamsViewCtrl::GetSelectedValue()
+{
+ return model->GetParamName(this->GetSelection());
+}
+
+std::string ParamsViewCtrl::GetSelectedParamKey()
+{
+ return model->GetParamKey(this->GetSelection());
+}
+
+std::string ParamsViewCtrl::GetSelectedTopLevelCategory()
+{
+ return model->GetTopLevelCategory(this->GetSelection());
+}
+
+void ParamsViewCtrl::CheckAndDeleteIfEmpty(wxDataViewItem item)
+{
+ wxDataViewItemArray children;
+ model->GetChildren(item, children);
+ if (children.IsEmpty())
+ model->Delete(item);
+}
+
+void ParamsViewCtrl::Clear()
+{
+ model->Clear();
+}
+
+void ParamsViewCtrl::Rescale(int em/* = 0*/)
+{
+// model->Rescale();
+ Refresh();
+}
+}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp
new file mode 100644
index 0000000000..28cd5809f9
--- /dev/null
+++ b/src/slic3r/GUI/EditGCodeDialog.hpp
@@ -0,0 +1,269 @@
+#ifndef slic3r_EditGCodeDialog_hpp_
+#define slic3r_EditGCodeDialog_hpp_
+
+#include
+
+#include
+#include
+
+#include "GUI_Utils.hpp"
+#include "wxExtensions.hpp"
+#include "libslic3r/Preset.hpp"
+#include "libslic3r/PrintConfig.hpp"
+#include
+
+class wxListBox;
+class wxTextCtrl;
+class ScalableButton;
+
+namespace Slic3r {
+
+namespace GUI {
+
+class ParamsViewCtrl;
+
+//------------------------------------------
+// EditGCodeDialog
+//------------------------------------------
+
+class EditGCodeDialog : public DPIDialog
+{
+ ParamsViewCtrl* m_params_list {nullptr};
+ ScalableButton* m_add_btn {nullptr};
+ wxTextCtrl* m_gcode_editor {nullptr};
+ wxStaticText* m_param_label {nullptr};
+ wxStaticText* m_param_description {nullptr};
+ wxSearchCtrl* m_search_bar {nullptr};
+
+ ReadOnlySlicingStatesConfigDef cgp_ro_slicing_states_config_def;
+ ReadWriteSlicingStatesConfigDef cgp_rw_slicing_states_config_def;
+ OtherSlicingStatesConfigDef cgp_other_slicing_states_config_def;
+ PrintStatisticsConfigDef cgp_print_statistics_config_def;
+ ObjectsInfoConfigDef cgp_objects_info_config_def;
+ DimensionsConfigDef cgp_dimensions_config_def;
+ TemperaturesConfigDef cgp_temperatures_config_def;
+ TimestampsConfigDef cgp_timestamps_config_def;
+ OtherPresetsConfigDef cgp_other_presets_config_def;
+
+public:
+ EditGCodeDialog(wxWindow*parent, const std::string&key, const std::string&value);
+ ~EditGCodeDialog();
+
+ std::string get_edited_gcode() const;
+ void on_search_update();
+
+ void init_params_list(const std::string& custom_gcode_name);
+ wxDataViewItem add_presets_placeholders();
+
+ void add_selected_value_to_gcode();
+ void bind_list_and_button();
+
+protected:
+ std::unordered_map m_button_list;
+
+ void on_dpi_changed(const wxRect& suggested_rect) override;
+ void on_sys_color_changed() override;
+
+ void selection_changed(wxDataViewEvent& evt);
+
+ wxBoxSizer* create_btn_sizer(long flags);
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+// ParamsModelNode: a node inside ParamsModel
+// ----------------------------------------------------------------------------
+
+class ParamsNode;
+using ParamsNodePtrArray = std::vector>;
+
+enum class ParamType {
+ Undef,
+ Scalar,
+ Vector,
+ FilamentVector,
+};
+
+// On all of 3 different platforms Bitmap+Text icon column looks different
+// because of Markup text is missed or not implemented.
+// As a temporary workaround, we will use:
+// MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text)
+// OSX - -//-, but Markup text is not implemented right now
+// GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text)
+class ParamsNode
+{
+ ParamsNode* m_parent{ nullptr };
+ ParamsNodePtrArray m_children;
+ wxDataViewCtrl* m_ctrl;
+
+ ParamType m_param_type{ ParamType::Undef };
+
+ // TODO/FIXME:
+ // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded)
+ // needs to know in advance if a node is or _will be_ a container.
+ // Thus implementing:
+ // bool IsContainer() const
+ // { return m_children.size()>0; }
+ // doesn't work with wxGTK when DiffModel::AddToClassical is called
+ // AND the classical node was removed (a new node temporary without children
+ // would be added to the control)
+ bool m_container{ true };
+ bool m_expanded_before_search{false};
+ bool m_enabled{true};
+
+ bool m_bold{false};
+ // first is pos, second is length
+ std::unique_ptr> m_highlight_index{nullptr};
+
+public:
+
+#ifdef __linux__
+ wxIcon icon;
+#else
+ wxBitmap icon;
+#endif //__linux__
+ std::string icon_name;
+ std::string param_key;
+ wxString text;
+
+ // Group params(root) node
+ ParamsNode(const wxString& group_name, const std::string& icon_name, wxDataViewCtrl* ctrl);
+
+ // sub SlicingState node
+ ParamsNode(ParamsNode* parent,
+ const wxString& sub_group_name,
+ const std::string& icon_name,
+ wxDataViewCtrl* ctrl);
+
+ // parametre node
+ ParamsNode( ParamsNode* parent,
+ ParamType param_type,
+ const std::string& param_key,
+ wxDataViewCtrl* ctrl);
+
+ wxString GetFormattedText();
+
+ bool IsContainer() const { return m_container; }
+ bool IsGroupNode() const { return m_parent == nullptr; }
+ bool IsParamNode() const { return m_param_type != ParamType::Undef; }
+ void SetContainer(bool is_container) { m_container = is_container; }
+
+ bool IsEnabled() { return m_enabled; }
+ void Enable(bool enable = true) { m_enabled = enable; }
+ void Disable() { Enable(false); }
+
+ void StartSearch();
+ void RefreshSearch(const wxString& search_text);
+ void FinishSearch();
+
+ ParamsNode* GetParent() { return m_parent; }
+ ParamsNodePtrArray& GetChildren() { return m_children; }
+ wxDataViewItemArray GetEnabledChildren();
+
+ void Append(std::unique_ptr child) { m_children.emplace_back(std::move(child)); }
+};
+
+
+// ----------------------------------------------------------------------------
+// ParamsModel
+// ----------------------------------------------------------------------------
+
+class ParamsModel : public wxDataViewModel
+{
+ ParamsNodePtrArray m_group_nodes;
+ wxDataViewCtrl* m_ctrl{ nullptr };
+ bool m_currently_searching{false};
+
+public:
+
+ ParamsModel();
+ ~ParamsModel() override = default;
+
+ void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
+
+ wxDataViewItem AppendGroup(const wxString& group_name,
+ const std::string& icon_name);
+
+ wxDataViewItem AppendSubGroup(wxDataViewItem parent,
+ const wxString& sub_group_name,
+ const std::string&icon_name);
+
+ wxDataViewItem AppendParam( wxDataViewItem parent,
+ ParamType param_type,
+ const std::string& param_key);
+
+ wxDataViewItem Delete(const wxDataViewItem& item);
+
+ wxString GetParamName(wxDataViewItem item);
+ std::string GetParamKey(wxDataViewItem item);
+ std::string GetTopLevelCategory(wxDataViewItem item);
+
+ void RefreshSearch(const wxString& search_text);
+ void FinishSearch();
+
+ void Clear();
+
+ wxDataViewItem GetParent(const wxDataViewItem& item) const override;
+ unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override;
+ unsigned int GetColumnCount() const override;
+ wxString GetColumnType(unsigned int col) const override;
+
+ void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override;
+ bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override;
+
+ bool IsContainer(const wxDataViewItem& item) const override;
+ // Is the container just a header or an item with all columns
+ // In our case it is an item with all columns
+ bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
+};
+
+
+// ----------------------------------------------------------------------------
+// ParamsViewCtrl
+// ----------------------------------------------------------------------------
+
+class ParamsViewCtrl : public wxDataViewCtrl
+{
+ int m_em_unit;
+
+public:
+ ParamsViewCtrl(wxWindow* parent, wxSize size);
+ ~ParamsViewCtrl() override {
+ if (model) {
+ Clear();
+ model->DecRef();
+ }
+ }
+
+ ParamsModel* model{ nullptr };
+
+ wxDataViewItem AppendGroup(const wxString& group_name,
+ const std::string& icon_name);
+
+ wxDataViewItem AppendSubGroup(wxDataViewItem parent,
+ const wxString& sub_group_name,
+ const std::string&icon_name);
+
+ wxDataViewItem AppendParam( wxDataViewItem parent,
+ ParamType param_type,
+ const std::string& param_key);
+
+ wxString GetValue(wxDataViewItem item);
+ wxString GetSelectedValue();
+ std::string GetSelectedParamKey();
+ std::string GetSelectedTopLevelCategory();
+
+ void CheckAndDeleteIfEmpty(wxDataViewItem item);
+
+ void Clear();
+ void Rescale(int em = 0);
+
+ void set_em_unit(int em) { m_em_unit = em; }
+};
+
+} // namespace GUI
+} // namespace Slic3r
+
+#endif
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 235ba99509..65cac2cc5e 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -196,6 +196,11 @@ void Field::on_back_to_sys_value()
m_back_to_sys_value(m_opt_id);
}
+void Field::on_edit_value()
+{
+ if (m_fn_edit_value)
+ m_fn_edit_value(m_opt_id);
+}
/// Fires the enable or disable function, based on the input.
@@ -206,7 +211,7 @@ wxString Field::get_tooltip_text(const wxString &default_string)
wxString tooltip_text("");
#ifdef NDEBUG
wxString tooltip = _(m_opt.tooltip);
- edit_tooltip(tooltip);
+ ::edit_tooltip(tooltip);
std::string opt_id = m_opt_id;
auto hash_pos = opt_id.find("#");
diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp
index a951c02132..d86a790eb4 100644
--- a/src/slic3r/GUI/Field.hpp
+++ b/src/slic3r/GUI/Field.hpp
@@ -41,7 +41,120 @@ wxString double_to_string(double const value, const int max_precision = 4);
wxString get_thumbnail_string(const Vec2d& value);
wxString get_thumbnails_string(const std::vector& values);
-class Field {
+class UndoValueUIManager
+{
+ struct UndoValueUI {
+ // 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 ScalableBitmap* undo_bitmap{ nullptr };
+ const wxString* undo_tooltip{ 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 ScalableBitmap* undo_to_sys_bitmap{ nullptr };
+ const wxString* undo_to_sys_tooltip{ nullptr };
+ // Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
+ const wxColour* label_color{ nullptr };
+ // State of the blinker icon
+ bool blink{ false };
+
+ bool set_undo_bitmap(const ScalableBitmap* bmp) {
+ if (undo_bitmap != bmp) {
+ undo_bitmap = bmp;
+ return true;
+ }
+ return false;
+ }
+
+ bool set_undo_to_sys_bitmap(const ScalableBitmap* bmp) {
+ if (undo_to_sys_bitmap != bmp) {
+ undo_to_sys_bitmap = bmp;
+ return true;
+ }
+ return false;
+ }
+
+ bool set_label_colour(const wxColour* clr) {
+ if (label_color != clr) {
+ label_color = clr;
+ }
+ return false;
+ }
+
+ bool set_undo_tooltip(const wxString* tip) {
+ if (undo_tooltip != tip) {
+ undo_tooltip = tip;
+ return true;
+ }
+ return false;
+ }
+
+ bool set_undo_to_sys_tooltip(const wxString* tip) {
+ if (undo_to_sys_tooltip != tip) {
+ undo_to_sys_tooltip = tip;
+ return true;
+ }
+ return false;
+ }
+ };
+
+ UndoValueUI m_undo_ui;
+
+ struct EditValueUI {
+ // Bitmap and Tooltip text for m_Edit_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
+ const ScalableBitmap* bitmap{ nullptr };
+ wxString tooltip { wxEmptyString };
+
+ bool set_bitmap(const ScalableBitmap* bmp) {
+ if (bitmap != bmp) {
+ bitmap = bmp;
+ return true;
+ }
+ return false;
+ }
+
+ bool set_tooltip(const wxString& tip) {
+ if (tooltip != tip) {
+ tooltip = tip;
+ return true;
+ }
+ return false;
+ }
+ };
+
+ EditValueUI m_edit_ui;
+
+public:
+ UndoValueUIManager() {}
+ ~UndoValueUIManager() {}
+
+ bool set_undo_bitmap(const ScalableBitmap* bmp) { return m_undo_ui.set_undo_bitmap(bmp); }
+ bool set_undo_to_sys_bitmap(const ScalableBitmap* bmp) { return m_undo_ui.set_undo_to_sys_bitmap(bmp); }
+ bool set_label_colour(const wxColour* clr) { return m_undo_ui.set_label_colour(clr); }
+ bool set_undo_tooltip(const wxString* tip) { return m_undo_ui.set_undo_tooltip(tip); }
+ bool set_undo_to_sys_tooltip(const wxString* tip) { return m_undo_ui.set_undo_to_sys_tooltip(tip); }
+
+ bool set_edit_bitmap(const ScalableBitmap* bmp) { return m_edit_ui.set_bitmap(bmp); }
+ bool set_edit_tooltip(const wxString& tip) { return m_edit_ui.set_tooltip(tip); }
+
+ // ui items used for revert line value
+ bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; }
+ const ScalableBitmap* undo_bitmap() const { return m_undo_ui.undo_bitmap; }
+ const wxString* undo_tooltip() const { return m_undo_ui.undo_tooltip; }
+ const ScalableBitmap* undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap; }
+ const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; }
+ const wxColour* label_color() const { return m_undo_ui.label_color; }
+
+ // Extentions
+
+ // Search blinker
+ const bool blink() const { return m_undo_ui.blink; }
+ bool* get_blink_ptr() { return &m_undo_ui.blink; }
+
+ // Edit field button
+ bool has_edit_ui() const { return !m_edit_ui.tooltip.IsEmpty(); }
+ const wxBitmap* edit_bitmap() const { return &m_edit_ui.bitmap->bmp(); }
+ const wxString* edit_tooltip() const { return &m_edit_ui.tooltip; }
+};
+
+class Field : public UndoValueUIManager {
protected:
// factory function to defer and enforce creation of derived type.
virtual void PostInitialize();
@@ -70,6 +183,8 @@ public:
void on_back_to_initial_value();
/// Call the attached m_back_to_sys_value method.
void on_back_to_sys_value();
+ /// Call the attached m_fn_edit_value method.
+ void on_edit_value();
public:
/// parent wx item, opportunity to refactor (probably not necessary - data duplication)
@@ -85,6 +200,9 @@ public:
t_back_to_init m_back_to_initial_value{ nullptr };
t_back_to_init m_back_to_sys_value{ nullptr };
+ /// Callback function to edit field value
+ t_back_to_init m_fn_edit_value{ nullptr };
+
// This is used to avoid recursive invocation of the field change/update by wxWidgets.
bool m_disable_change_event {false};
bool m_is_modified_value {false};
@@ -139,49 +257,6 @@ public:
return std::move(p); //!p;
}
- bool set_undo_bitmap(const ScalableBitmap *bmp) {
- if (m_undo_bitmap != bmp) {
- m_undo_bitmap = bmp;
- return true;
- }
- return false;
- }
-
- bool set_undo_to_sys_bitmap(const ScalableBitmap *bmp) {
- if (m_undo_to_sys_bitmap != bmp) {
- m_undo_to_sys_bitmap = bmp;
- return true;
- }
- return false;
- }
-
- bool set_label_colour(const wxColour *clr) {
- if (m_label_color != clr) {
- m_label_color = clr;
- }
- return false;
- }
-
- bool set_undo_tooltip(const wxString *tip) {
- if (m_undo_tooltip != tip) {
- m_undo_tooltip = 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;
- return true;
- }
- return false;
- }
-
- bool* get_blink_ptr() {
- return &m_blink;
- }
-
virtual void msw_rescale();
virtual void sys_color_changed();
@@ -193,27 +268,9 @@ public:
static int def_width_wider() ;
static int def_width_thinner() ;
- const ScalableBitmap* undo_bitmap() { return m_undo_bitmap; }
- const wxString* undo_tooltip() { return m_undo_tooltip; }
- const ScalableBitmap* undo_to_sys_bitmap() { return m_undo_to_sys_bitmap; }
- const wxString* undo_to_sys_tooltip() { return m_undo_to_sys_tooltip; }
- const wxColour* label_color() { return m_label_color; }
- const bool blink() { return m_blink; }
const bool combine_side_text() { return m_combine_side_text; } // BBS: new param style
protected:
- // 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 ScalableBitmap* m_undo_bitmap = nullptr;
- const wxString* m_undo_tooltip = 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 ScalableBitmap* m_undo_to_sys_bitmap = nullptr;
- const wxString* m_undo_to_sys_tooltip = nullptr;
-
- bool m_blink{ false };
-
- // 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;
// last maeningful value
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index a2a72824a1..2273a91f2c 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -3185,14 +3185,17 @@ void GUI_App::UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited/* = false*/)
UpdateDarkUI(dvc, highlited ? dark_mode() : false);
#ifdef _MSW_DARK_MODE
//dvc->RefreshHeaderDarkMode(&m_normal_font);
- HWND hwnd = (HWND)dvc->GenericGetHeader()->GetHandle();
- hwnd = GetWindow(hwnd, GW_CHILD);
- if (hwnd != NULL)
- NppDarkMode::SetDarkListViewHeader(hwnd);
- wxItemAttr attr;
- attr.SetTextColour(NppDarkMode::GetTextColor());
- attr.SetFont(m_normal_font);
- dvc->SetHeaderAttr(attr);
+ HWND hwnd;
+ if (!dvc->HasFlag(wxDV_NO_HEADER)) {
+ hwnd = (HWND) dvc->GenericGetHeader()->GetHandle();
+ hwnd = GetWindow(hwnd, GW_CHILD);
+ if (hwnd != NULL)
+ NppDarkMode::SetDarkListViewHeader(hwnd);
+ wxItemAttr attr;
+ attr.SetTextColour(NppDarkMode::GetTextColor());
+ attr.SetFont(m_normal_font);
+ dvc->SetHeaderAttr(attr);
+ }
#endif //_MSW_DARK_MODE
if (dvc->HasFlag(wxDV_ROW_LINES))
dvc->SetAlternateRowColour(m_color_highlight_default);
diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp
index 2aef316b48..dadde84ebc 100644
--- a/src/slic3r/GUI/OG_CustomCtrl.cpp
+++ b/src/slic3r/GUI/OG_CustomCtrl.cpp
@@ -195,7 +195,7 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/)
if (line.widget) {
#ifndef DISABLE_BLINKING
- h_pos += blinking_button_width;
+ h_pos += (line.has_undo_ui() ? 3 : 1) * blinking_button_width;
#endif
for (auto child : line.widget_sizer->GetChildren())
@@ -373,22 +373,31 @@ void OG_CustomCtrl::OnMotion(wxMouseEvent& event)
break;
}
- for (size_t opt_idx = 0; opt_idx < line.rects_undo_icon.size(); opt_idx++)
+ size_t undo_icons_cnt = line.rects_undo_icon.size();
+ assert(line.rects_undo_icon.size() == line.rects_undo_to_sys_icon.size());
+ const std::vector