Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_sinking_objects_collision
This commit is contained in:
commit
2e779d8594
67
resources/icons/notification_info.svg
Normal file
67
resources/icons/notification_info.svg
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
id="warning"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 200 200"
|
||||||
|
enable-background="new 0 0 100 100"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="notification_info.svg"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||||
|
id="metadata19"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs17" /><sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1377"
|
||||||
|
id="namedview15"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="5.04"
|
||||||
|
inkscape:cx="108.69674"
|
||||||
|
inkscape:cy="117.65848"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="warning" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
style="fill:#ed6b21;fill-opacity:1"
|
||||||
|
transform="matrix(2,0,0,2,0.17117674,0.68464711)"
|
||||||
|
id="g8"><path
|
||||||
|
style="fill:#ed6b21;fill-opacity:1"
|
||||||
|
class="st1"
|
||||||
|
d="m 40.1,43.5 c 4.5,-1.2 8.2,-1.7 11,-1.7 1.7,0 3,0.3 4,0.9 0.9,0.6 1.4,1.5 1.4,2.8 0,0.5 -0.1,1.1 -0.2,1.6 l -5,23.9 c -0.1,0.7 -0.2,1.1 -0.2,1.4 0,1 0.4,1.7 1.3,2 0.8,0.3 2.4,0.5 4.6,0.5 l -0.2,1.8 c -4.3,1.1 -7.8,1.6 -10.5,1.6 -1.9,0 -3.3,-0.4 -4.4,-1.1 -1,-0.7 -1.6,-1.8 -1.6,-3.3 0,-0.7 0.1,-1.6 0.3,-2.7 l 4.6,-22 c 0.2,-1 0.3,-1.7 0.3,-2 0,-0.7 -0.3,-1.2 -0.9,-1.4 C 44,45.5 42.4,45.4 39.7,45.4 Z M 59.2,28.4 c 0,1.7 -0.5,3.1 -1.6,4.2 -1.1,1.1 -2.4,1.6 -4,1.6 -1.5,0 -2.8,-0.5 -3.8,-1.5 -1,-1 -1.5,-2.3 -1.5,-3.9 0,-1.8 0.5,-3.2 1.6,-4.2 1.1,-1 2.4,-1.5 4,-1.5 1.5,0 2.8,0.5 3.8,1.4 1,0.9 1.5,2.2 1.5,3.9 z"
|
||||||
|
id="path6" /></g><path
|
||||||
|
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.198413"
|
||||||
|
d="m 16.939336,52.048564 c 0.24576,-0.15442 19.08533,-11.001046 41.86572,-24.103596 L 100.22396,4.1221459 142.03917,28.183626 183.85441,52.245107 V 100.734 149.2229 l -41.81468,24.06212 -41.81467,24.06214 -41.816284,-24.06216 -41.81626,-24.06216 -0.05,-48.44676 -0.05,-48.446739 z M 63.764716,163.5414 c 19.88816,11.44426 36.295164,20.80774 36.460014,20.80774 0.16481,0 16.57129,-9.36332 36.45875,-20.80738 l 36.15903,-20.80738 V 100.73468 58.734992 L 136.68178,37.925746 C 116.79341,26.480662 100.38715,17.116638 100.22347,17.116804 100.05968,17.116972 83.653536,26.48106 63.765126,37.925887 l -36.16072,20.808776 v 41.999497 41.9995 z"
|
||||||
|
id="path853" /><path
|
||||||
|
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.198413"
|
||||||
|
d="m 89.952923,157.02042 c -4.242772,-0.58044 -7.113036,-2.32486 -8.380448,-5.09326 -0.626294,-1.368 -0.807444,-3.89074 -0.464458,-6.46814 0.1277,-0.95968 2.419926,-12.20538 5.093816,-24.99044 2.67389,-12.78508 4.908156,-23.917618 4.965016,-24.738958 0.115,-1.6621 -0.189601,-2.54084 -1.114721,-3.21606 -0.792772,-0.57856 -2.966462,-0.9222 -6.787666,-1.07302 -3.481347,-0.1374 -3.555832,-0.1494 -3.458944,-0.5573 0.05446,-0.22926 0.206042,-0.97334 0.336845,-1.6535 0.1308,-0.68016 0.338118,-1.31832 0.4607,-1.41814 0.28243,-0.22998 6.4844,-1.66782 9.46857,-2.19514 5.863452,-1.0361 12.531417,-1.47854 15.428597,-1.02374 5.36116,0.84161 7.82828,3.38728 7.50956,7.74872 -0.0602,0.82388 -2.44364,12.614038 -5.2965,26.200338 -3.08108,14.67312 -5.23336,25.36606 -5.30112,26.33702 -0.24186,3.46504 0.94802,4.64856 5.32416,5.29562 1.15984,0.1716 3.06554,0.31182 4.23488,0.31182 h 2.1261 l -0.1262,1.04166 c -0.0694,0.57292 -0.174,1.37074 -0.23232,1.77292 l -0.1062,0.73126 -2.51448,0.59446 c -8.31236,1.96515 -17.063901,2.95498 -21.165277,2.39389 z"
|
||||||
|
id="path855" /><path
|
||||||
|
id="path859"
|
||||||
|
d="m 103.80449,68.371429 c -4.85388,-1.747057 -7.401434,-6.318219 -6.705139,-12.031262 0.68816,-5.646301 5.025269,-9.262423 11.052899,-9.215502 4.20991,0.03277 7.4957,1.946636 9.16606,5.33893 0.89045,1.808402 0.93519,2.048665 0.92966,4.992684 -0.007,3.886952 -0.60744,5.58627 -2.81498,7.970671 -2.18267,2.35755 -4.19782,3.227419 -7.70164,3.324532 -1.90628,0.05284 -3.02496,-0.05544 -3.92686,-0.380053 z"
|
||||||
|
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.280598" /><path
|
||||||
|
id="path861"
|
||||||
|
d="m 88.159837,156.51971 c -4.689261,-1.3224 -6.765722,-3.65088 -6.999725,-7.84929 -0.143463,-2.57397 -0.367006,-1.35766 5.236874,-28.49422 2.02374,-9.79989 3.976706,-19.42817 4.339925,-21.396181 1.189159,-6.443166 0.608576,-7.052275 -7.07692,-7.424629 -1.896078,-0.09186 -3.506416,-0.262489 -3.578529,-0.37917 -0.07211,-0.116682 0.01749,-0.855929 0.199113,-1.642772 0.380336,-1.647704 6.64e-4,-1.47536 6.265635,-2.844151 5.6684,-1.238451 9.941962,-1.750196 14.87169,-1.780835 6.11275,-0.03799 8.21381,0.630882 10.35608,3.296884 0.84162,1.047378 0.86697,1.161816 0.83925,3.788072 -0.0245,2.316986 -0.5437,5.179915 -3.59555,19.824542 -7.12659,34.19777 -7.24867,34.91174 -6.23405,36.46024 0.25629,0.39114 0.76211,0.90521 1.12406,1.14236 1.05449,0.69093 4.30303,1.25008 7.29332,1.25534 l 2.76816,0.005 -0.20111,1.61344 c -0.11061,0.88739 -0.33162,1.69456 -0.49112,1.79371 -0.42321,0.26308 -8.03018,1.8551 -11.29784,2.36446 -4.118692,0.64201 -11.953193,0.79357 -13.819263,0.26733 z"
|
||||||
|
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.280598" /></svg>
|
After Width: | Height: | Size: 5.7 KiB |
@ -149,10 +149,11 @@ namespace ImGui
|
|||||||
const wchar_t CustomSupportsMarker = 0x1D;
|
const wchar_t CustomSupportsMarker = 0x1D;
|
||||||
const wchar_t CustomSeamMarker = 0x1E;
|
const wchar_t CustomSeamMarker = 0x1E;
|
||||||
const wchar_t MmuSegmentationMarker = 0x1F;
|
const wchar_t MmuSegmentationMarker = 0x1F;
|
||||||
|
// Do not forget use following letters only in wstring
|
||||||
const wchar_t DocumentationButton = 0x2600;
|
const wchar_t DocumentationButton = 0x2600;
|
||||||
const wchar_t DocumentationHoverButton = 0x2601;
|
const wchar_t DocumentationHoverButton = 0x2601;
|
||||||
const wchar_t ClippyMarker = 0x2602;
|
const wchar_t ClippyMarker = 0x2602;
|
||||||
|
const wchar_t InfoMarker = 0x2603;
|
||||||
|
|
||||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||||
}
|
}
|
||||||
|
@ -617,19 +617,35 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
case NODE_TYPE_VOLUME:
|
case NODE_TYPE_VOLUME:
|
||||||
{
|
{
|
||||||
assert(m_object && m_volume);
|
assert(m_object && m_volume);
|
||||||
// Verify validity of face indices.
|
if (m_volume_facets.empty()) {
|
||||||
for (Vec3i face : m_volume_facets)
|
this->stop("An empty triangle mesh found");
|
||||||
for (unsigned int tri_id : face)
|
return;
|
||||||
if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
|
}
|
||||||
this->stop("Malformed triangle mesh");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
|
// Verify validity of face indices, find the vertex span.
|
||||||
if (triangle_mesh.volume() < 0)
|
int min_id = m_volume_facets.front()[0];
|
||||||
triangle_mesh.flip_triangles();
|
int max_id = min_id;
|
||||||
m_volume->set_mesh(std::move(triangle_mesh));
|
for (const Vec3i& face : m_volume_facets) {
|
||||||
|
for (const int tri_id : face) {
|
||||||
|
if (tri_id < 0 || tri_id >= int(m_object_vertices.size())) {
|
||||||
|
this->stop("Malformed triangle mesh");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
min_id = std::min(min_id, tri_id);
|
||||||
|
max_id = std::max(max_id, tri_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rebase indices to the current vertices list
|
||||||
|
for (Vec3i &face : m_volume_facets)
|
||||||
|
face -= Vec3i(min_id, min_id, min_id);
|
||||||
|
|
||||||
|
indexed_triangle_set its { std::move(m_volume_facets), { m_object_vertices.begin() + min_id, m_object_vertices.begin() + max_id + 1 } };
|
||||||
|
its_compactify_vertices(its);
|
||||||
|
if (its_volume(its) < 0)
|
||||||
|
its_flip_triangles(its);
|
||||||
|
m_volume->set_mesh(std::move(its));
|
||||||
}
|
}
|
||||||
|
|
||||||
// stores the volume matrix taken from the metadata, if present
|
// stores the volume matrix taken from the metadata, if present
|
||||||
@ -646,7 +662,6 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
|
|
||||||
m_volume->calculate_convex_hull();
|
m_volume->calculate_convex_hull();
|
||||||
m_volume_facets.clear();
|
m_volume_facets.clear();
|
||||||
m_object_vertices.clear();
|
|
||||||
m_volume = nullptr;
|
m_volume = nullptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1456,7 +1456,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||||||
|
|
||||||
begin = skip_whitespaces(begin, end);
|
begin = skip_whitespaces(begin, end);
|
||||||
end = remove_eols(begin, end);
|
end = remove_eols(begin, end);
|
||||||
if (begin != end)
|
if (begin != end) {
|
||||||
if (*begin == ';') {
|
if (*begin == ';') {
|
||||||
// Comment.
|
// Comment.
|
||||||
begin = skip_whitespaces(++ begin, end);
|
begin = skip_whitespaces(++ begin, end);
|
||||||
@ -1485,6 +1485,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||||||
// Some non-empty G-code line detected, stop parsing config comments.
|
// Some non-empty G-code line detected, stop parsing config comments.
|
||||||
reader.quit_parsing();
|
reader.quit_parsing();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_result.extruders_count == 0)
|
if (m_result.extruders_count == 0)
|
||||||
|
@ -1300,7 +1300,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
num_extruders,
|
num_extruders,
|
||||||
painting_extruders,
|
painting_extruders,
|
||||||
*print_object_regions,
|
*print_object_regions,
|
||||||
[&print_object, it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
[it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
||||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||||
if ((*it)->m_shared_regions != nullptr)
|
if ((*it)->m_shared_regions != nullptr)
|
||||||
update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
||||||
|
@ -325,20 +325,24 @@ void TriangleMesh::mirror(const Axis axis)
|
|||||||
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
|
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
|
||||||
{
|
{
|
||||||
its_transform(its, t);
|
its_transform(its, t);
|
||||||
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.)
|
double det = t.matrix().block(0, 0, 3, 3).determinant();
|
||||||
|
if (fix_left_handed && det < 0.) {
|
||||||
its_flip_triangles(its);
|
its_flip_triangles(its);
|
||||||
else
|
det = -det;
|
||||||
m_stats.volume = - m_stats.volume;
|
}
|
||||||
|
m_stats.volume *= det;
|
||||||
update_bounding_box(this->its, this->m_stats);
|
update_bounding_box(this->its, this->m_stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
|
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
|
||||||
{
|
{
|
||||||
its_transform(its, m);
|
its_transform(its, m);
|
||||||
if (fix_left_handed && m.determinant() < 0.)
|
double det = m.block(0, 0, 3, 3).determinant();
|
||||||
|
if (fix_left_handed && det < 0.) {
|
||||||
its_flip_triangles(its);
|
its_flip_triangles(its);
|
||||||
else
|
det = -det;
|
||||||
m_stats.volume = - m_stats.volume;
|
}
|
||||||
|
m_stats.volume *= det;
|
||||||
update_bounding_box(this->its, this->m_stats);
|
update_bounding_box(this->its, this->m_stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,7 +490,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
|||||||
std::vector<int> map_dst_vertices;
|
std::vector<int> map_dst_vertices;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
Vec3f centroid = Vec3f::Zero();
|
Vec3f centroid = Vec3f::Zero();
|
||||||
for (auto pt : this->its.vertices)
|
for (const stl_vertex& pt : this->its.vertices)
|
||||||
centroid += pt;
|
centroid += pt;
|
||||||
centroid /= float(this->its.vertices.size());
|
centroid /= float(this->its.vertices.size());
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
@ -1282,7 +1286,7 @@ bool its_write_stl_ascii(const char *file, const char *label, const std::vector<
|
|||||||
|
|
||||||
fprintf(fp, "solid %s\n", label);
|
fprintf(fp, "solid %s\n", label);
|
||||||
|
|
||||||
for (const stl_triangle_vertex_indices face : indices) {
|
for (const stl_triangle_vertex_indices& face : indices) {
|
||||||
Vec3f vertex[3] = { vertices[face(0)], vertices[face(1)], vertices[face(2)] };
|
Vec3f vertex[3] = { vertices[face(0)], vertices[face(1)], vertices[face(2)] };
|
||||||
Vec3f normal = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized();
|
Vec3f normal = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized();
|
||||||
fprintf(fp, " facet normal % .8E % .8E % .8E\n", normal(0), normal(1), normal(2));
|
fprintf(fp, " facet normal % .8E % .8E % .8E\n", normal(0), normal(1), normal(2));
|
||||||
@ -1322,7 +1326,7 @@ bool its_write_stl_binary(const char *file, const char *label, const std::vector
|
|||||||
stl_facet f;
|
stl_facet f;
|
||||||
f.extra[0] = 0;
|
f.extra[0] = 0;
|
||||||
f.extra[1] = 0;
|
f.extra[1] = 0;
|
||||||
for (const stl_triangle_vertex_indices face : indices) {
|
for (const stl_triangle_vertex_indices& face : indices) {
|
||||||
f.vertex[0] = vertices[face(0)];
|
f.vertex[0] = vertices[face(0)];
|
||||||
f.vertex[1] = vertices[face(1)];
|
f.vertex[1] = vertices[face(1)];
|
||||||
f.vertex[2] = vertices[face(2)];
|
f.vertex[2] = vertices[face(2)];
|
||||||
|
@ -727,7 +727,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
|||||||
if( bottom_area - top_area > delta_area) {
|
if( bottom_area - top_area > delta_area) {
|
||||||
NotificationManager *notif_mngr = wxGetApp().plater()->get_notification_manager();
|
NotificationManager *notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||||
notif_mngr->push_notification(
|
notif_mngr->push_notification(
|
||||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotificationLevel,
|
NotificationType::SignDetected, NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||||
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
|
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
|
||||||
_u8L("Apply auto color change to print"),
|
_u8L("Apply auto color change to print"),
|
||||||
[this](wxEvtHandler*) {
|
[this](wxEvtHandler*) {
|
||||||
|
@ -23,7 +23,7 @@ static inline void show_notification_extruders_limit_exceeded()
|
|||||||
wxGetApp()
|
wxGetApp()
|
||||||
.plater()
|
.plater()
|
||||||
->get_notification_manager()
|
->get_notification_manager()
|
||||||
->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::RegularNotificationLevel,
|
->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||||
GUI::format(_L("Your printer has more extruders than the multi-material painting gizmo supports. For this reason, only the "
|
GUI::format(_L("Your printer has more extruders than the multi-material painting gizmo supports. For this reason, only the "
|
||||||
"first %1% extruders will be able to be used for painting."), GLGizmoMmuSegmentation::EXTRUDERS_LIMIT));
|
"first %1% extruders will be able to be used for painting."), GLGizmoMmuSegmentation::EXTRUDERS_LIMIT));
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,10 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||||||
set_its(*m_original_its);
|
set_its(*m_original_its);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close suggestion notification
|
||||||
|
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||||
|
notification_manager->remove_simplify_suggestion_with_id(act_volume->get_object()->id());
|
||||||
|
|
||||||
m_obj_index = obj_index; // to remember correct object
|
m_obj_index = obj_index; // to remember correct object
|
||||||
m_volume = act_volume;
|
m_volume = act_volume;
|
||||||
m_original_its = {};
|
m_original_its = {};
|
||||||
@ -363,7 +367,7 @@ void GLGizmoSimplify::on_set_state()
|
|||||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||||
notification_manager->push_notification(
|
notification_manager->push_notification(
|
||||||
NotificationType::CustomNotification,
|
NotificationType::CustomNotification,
|
||||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||||
_u8L("ERROR: Wait until Simplification ends or Cancel process."));
|
_u8L("ERROR: Wait until Simplification ends or Cancel process."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
|
|||||||
if (get_current_type() != type && get_current_type() != Undefined) {
|
if (get_current_type() != type && get_current_type() != Undefined) {
|
||||||
wxGetApp().plater()->get_notification_manager()->push_notification(
|
wxGetApp().plater()->get_notification_manager()->push_notification(
|
||||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||||
_u8L("ERROR: Please close all manipulators available from "
|
_u8L("ERROR: Please close all manipulators available from "
|
||||||
"the left toolbar first"));
|
"the left toolbar first"));
|
||||||
return false;
|
return false;
|
||||||
|
@ -917,29 +917,14 @@ void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapp
|
|||||||
}
|
}
|
||||||
void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||||
{
|
{
|
||||||
ImVec2 win_size(win_size_x, win_size_y);
|
|
||||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
|
||||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
|
||||||
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
|
||||||
|
|
||||||
std::wstring button_text;
|
|
||||||
button_text = ImGui::ClippyMarker;//LeftArrowButton;
|
|
||||||
std::string placeholder_text;
|
std::string placeholder_text;
|
||||||
placeholder_text = ImGui::EjectButton;
|
placeholder_text = ImGui::EjectButton;
|
||||||
|
|
||||||
ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str());
|
ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str());
|
||||||
ImVec2 button_size(button_pic_size.x * 1.25f * 2.f, button_pic_size.y * 1.25f * 2.f);
|
std::wstring text;
|
||||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y * 1.1f);
|
text = ImGui::ClippyMarker;
|
||||||
ImGui::SetCursorPosX(0);
|
ImGui::SetCursorPosX(button_pic_size.x / 3);
|
||||||
// shouldnt it render as text?
|
ImGui::SetCursorPosY(win_size_y / 2 - button_pic_size.y * 2.f);
|
||||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
imgui.text(text.c_str());
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::PopStyleColor(5);
|
|
||||||
}
|
}
|
||||||
void NotificationManager::HintNotification::render_documentation_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
void NotificationManager::HintNotification::render_documentation_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||||
{
|
{
|
||||||
@ -1012,7 +997,7 @@ void NotificationManager::HintNotification::retrieve_data(bool new_hint/* = true
|
|||||||
if(hint_data != nullptr)
|
if(hint_data != nullptr)
|
||||||
{
|
{
|
||||||
NotificationData nd { NotificationType::DidYouKnowHint,
|
NotificationData nd { NotificationType::DidYouKnowHint,
|
||||||
NotificationLevel::RegularNotificationLevel,
|
NotificationLevel::HintNotificationLevel,
|
||||||
0,
|
0,
|
||||||
hint_data->text,
|
hint_data->text,
|
||||||
hint_data->hypertext, nullptr,
|
hint_data->hypertext, nullptr,
|
||||||
|
@ -48,6 +48,7 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||||||
{ImGui::RightArrowHoverButton , "notification_right_hover" },
|
{ImGui::RightArrowHoverButton , "notification_right_hover" },
|
||||||
{ImGui::PreferencesButton , "notification_preferences" },
|
{ImGui::PreferencesButton , "notification_preferences" },
|
||||||
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
|
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
|
||||||
|
|
||||||
};
|
};
|
||||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||||
{ImGui::CloseNotifButton , "notification_close" },
|
{ImGui::CloseNotifButton , "notification_close" },
|
||||||
@ -65,6 +66,8 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
|
|||||||
{ImGui::VarLayerHeightMarker , "layers" },
|
{ImGui::VarLayerHeightMarker , "layers" },
|
||||||
{ImGui::DocumentationButton , "notification_documentation" },
|
{ImGui::DocumentationButton , "notification_documentation" },
|
||||||
{ImGui::DocumentationHoverButton, "notification_documentation_hover"},
|
{ImGui::DocumentationHoverButton, "notification_documentation_hover"},
|
||||||
|
{ImGui::InfoMarker , "notification_info" },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::map<const wchar_t, std::string> font_icons_extra_large = {
|
static const std::map<const wchar_t, std::string> font_icons_extra_large = {
|
||||||
|
@ -153,8 +153,8 @@ void SLAImportJob::process()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (MissingProfileError &) {
|
} catch (MissingProfileError &) {
|
||||||
p->err = _L("The archive doesn't contain any profile data. Try to import after switching "
|
p->err = _L("The SLA archive doesn't contain any presets. "
|
||||||
"to an SLA profile that can be used as fallback.").ToStdString();
|
"Please activate some SLA printer preset first before importing that SLA archive.").ToStdString();
|
||||||
} catch (std::exception &ex) {
|
} catch (std::exception &ex) {
|
||||||
p->err = ex.what();
|
p->err = ex.what();
|
||||||
}
|
}
|
||||||
@ -207,8 +207,8 @@ void SLAImportJob::finalize()
|
|||||||
m_plater->get_notification_manager()->push_notification(
|
m_plater->get_notification_manager()->push_notification(
|
||||||
NotificationType::CustomNotification,
|
NotificationType::CustomNotification,
|
||||||
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||||
_L("Loaded archive did not contain any profile data. "
|
_L("The imported SLA archive did not contain any presets. "
|
||||||
"The current SLA profile was used as fallback.").ToStdString());
|
"The current SLA presets were used as fallback.").ToStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->sel != Sel::modelOnly) {
|
if (p->sel != Sel::modelOnly) {
|
||||||
|
@ -43,10 +43,10 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat
|
|||||||
},
|
},
|
||||||
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||||
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::ObjectInfoNotificationLevel, 10,
|
{NotificationType::EmptyColorChangeCode, NotificationLevel::PrintInfoNotificationLevel, 10,
|
||||||
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
||||||
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
||||||
{NotificationType::EmptyAutoColorChange, NotificationLevel::ObjectInfoNotificationLevel, 10,
|
{NotificationType::EmptyAutoColorChange, NotificationLevel::PrintInfoNotificationLevel, 10,
|
||||||
_u8L("No color change event was added to the print. The print does not look like a sign.") },
|
_u8L("No color change event was added to the print. The print does not look like a sign.") },
|
||||||
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotificationLevel, 10,
|
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotificationLevel, 10,
|
||||||
_u8L("Desktop integration was successful.") },
|
_u8L("Desktop integration was successful.") },
|
||||||
@ -276,7 +276,9 @@ void NotificationManager::PopNotification::count_spaces()
|
|||||||
m_line_height = ImGui::CalcTextSize("A").y;
|
m_line_height = ImGui::CalcTextSize("A").y;
|
||||||
|
|
||||||
m_left_indentation = m_line_height;
|
m_left_indentation = m_line_height;
|
||||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::WarningNotificationLevel) {
|
if (m_data.level == NotificationLevel::ErrorNotificationLevel
|
||||||
|
|| m_data.level == NotificationLevel::WarningNotificationLevel
|
||||||
|
|| m_data.level == NotificationLevel::PrintInfoNotificationLevel) {
|
||||||
std::string text;
|
std::string text;
|
||||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||||
@ -511,6 +513,12 @@ void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
|
|||||||
ImGui::SetCursorPosX(m_line_height / 3);
|
ImGui::SetCursorPosX(m_line_height / 3);
|
||||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||||
imgui.text(text.c_str());
|
imgui.text(text.c_str());
|
||||||
|
} else if (m_data.level == NotificationLevel::PrintInfoNotificationLevel) {
|
||||||
|
std::wstring text;
|
||||||
|
text = ImGui::InfoMarker;
|
||||||
|
ImGui::SetCursorPosX(m_line_height / 3);
|
||||||
|
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||||
|
imgui.text(text.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
|
void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
|
||||||
@ -1604,7 +1612,7 @@ void NotificationManager::close_slicing_error_notification(const std::string& te
|
|||||||
}
|
}
|
||||||
void NotificationManager::push_simplify_suggestion_notification(const std::string& text, ObjectID object_id, const std::string& hypertext/* = ""*/, std::function<bool(wxEvtHandler*)> callback/* = std::function<bool(wxEvtHandler*)>()*/)
|
void NotificationManager::push_simplify_suggestion_notification(const std::string& text, ObjectID object_id, const std::string& hypertext/* = ""*/, std::function<bool(wxEvtHandler*)> callback/* = std::function<bool(wxEvtHandler*)>()*/)
|
||||||
{
|
{
|
||||||
NotificationData data{ NotificationType::SimplifySuggestion, NotificationLevel::ObjectInfoNotificationLevel, 10, text, hypertext, callback };
|
NotificationData data{ NotificationType::SimplifySuggestion, NotificationLevel::PrintInfoNotificationLevel, 10, text, hypertext, callback };
|
||||||
auto notification = std::make_unique<NotificationManager::ObjectIDNotification>(data, m_id_provider, m_evt_handler);
|
auto notification = std::make_unique<NotificationManager::ObjectIDNotification>(data, m_id_provider, m_evt_handler);
|
||||||
notification->object_id = object_id;
|
notification->object_id = object_id;
|
||||||
push_notification_data(std::move(notification), 0);
|
push_notification_data(std::move(notification), 0);
|
||||||
@ -1636,6 +1644,15 @@ void NotificationManager::remove_simplify_suggestion_of_released_objects(const s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotificationManager::remove_simplify_suggestion_with_id(const ObjectID oid)
|
||||||
|
{
|
||||||
|
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications)
|
||||||
|
if (notification->get_type() == NotificationType::SimplifySuggestion) {
|
||||||
|
if (static_cast<ObjectIDNotification*>(notification.get())->object_id == oid)
|
||||||
|
notification->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NotificationManager::push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable)
|
void NotificationManager::push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable)
|
||||||
{
|
{
|
||||||
close_notification_of_type(NotificationType::ExportFinished);
|
close_notification_of_type(NotificationType::ExportFinished);
|
||||||
@ -1914,7 +1931,7 @@ void NotificationManager::push_updated_item_info_notification(InfoItemType type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::ObjectInfoNotificationLevel, 10, "" };
|
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::PrintInfoNotificationLevel, 10, "" };
|
||||||
auto notification = std::make_unique<NotificationManager::UpdatedItemsInfoNotification>(data, m_id_provider, m_evt_handler, type);
|
auto notification = std::make_unique<NotificationManager::UpdatedItemsInfoNotification>(data, m_id_provider, m_evt_handler, type);
|
||||||
if (push_notification_data(std::move(notification), 0)) {
|
if (push_notification_data(std::move(notification), 0)) {
|
||||||
(dynamic_cast<UpdatedItemsInfoNotification*>(m_pop_notifications.back().get()))->add_type(type);
|
(dynamic_cast<UpdatedItemsInfoNotification*>(m_pop_notifications.back().get()))->add_type(type);
|
||||||
|
@ -122,7 +122,7 @@ public:
|
|||||||
// "Good to know" notification, usually but not always with a quick fade-out.
|
// "Good to know" notification, usually but not always with a quick fade-out.
|
||||||
RegularNotificationLevel,
|
RegularNotificationLevel,
|
||||||
// Regular level notifiaction containing info about objects or print. Has Icon.
|
// Regular level notifiaction containing info about objects or print. Has Icon.
|
||||||
ObjectInfoNotificationLevel,
|
PrintInfoNotificationLevel,
|
||||||
// Information notification without a fade-out or with a longer fade-out.
|
// Information notification without a fade-out or with a longer fade-out.
|
||||||
ImportantNotificationLevel,
|
ImportantNotificationLevel,
|
||||||
// Warning, no fade-out.
|
// Warning, no fade-out.
|
||||||
@ -174,6 +174,7 @@ public:
|
|||||||
// Close object warnings, whose ObjectID is not in the list.
|
// Close object warnings, whose ObjectID is not in the list.
|
||||||
// living_oids is expected to be sorted.
|
// living_oids is expected to be sorted.
|
||||||
void remove_simplify_suggestion_of_released_objects(const std::vector<ObjectID>& living_oids);
|
void remove_simplify_suggestion_of_released_objects(const std::vector<ObjectID>& living_oids);
|
||||||
|
void remove_simplify_suggestion_with_id(const ObjectID oid);
|
||||||
// Called when the side bar changes its visibility, as the "slicing complete" notification supplements
|
// Called when the side bar changes its visibility, as the "slicing complete" notification supplements
|
||||||
// the "slicing info" normally shown at the side bar.
|
// the "slicing info" normally shown at the side bar.
|
||||||
void set_sidebar_collapsed(bool collapsed);
|
void set_sidebar_collapsed(bool collapsed);
|
||||||
@ -704,13 +705,14 @@ private:
|
|||||||
size_t get_standart_duration(NotificationLevel level)
|
size_t get_standart_duration(NotificationLevel level)
|
||||||
{
|
{
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case NotificationLevel::RegularNotificationLevel: return 20;
|
|
||||||
case NotificationLevel::ErrorNotificationLevel: return 0;
|
case NotificationLevel::ErrorNotificationLevel: return 0;
|
||||||
case NotificationLevel::WarningNotificationLevel: return 0;
|
case NotificationLevel::WarningNotificationLevel: return 0;
|
||||||
case NotificationLevel::ImportantNotificationLevel: return 0;
|
case NotificationLevel::ImportantNotificationLevel: return 0;
|
||||||
case NotificationLevel::ProgressBarNotificationLevel: return 2;
|
case NotificationLevel::ProgressBarNotificationLevel: return 2;
|
||||||
|
case NotificationLevel::RegularNotificationLevel: return 10;
|
||||||
|
case NotificationLevel::PrintInfoNotificationLevel: return 10;
|
||||||
case NotificationLevel::HintNotificationLevel: return 300;
|
case NotificationLevel::HintNotificationLevel: return 300;
|
||||||
case NotificationLevel::ObjectInfoNotificationLevel: return 20;
|
|
||||||
default: return 10;
|
default: return 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2346,7 +2346,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
for (std::string& name : names)
|
for (std::string& name : names)
|
||||||
notif_text += "\n - " + name;
|
notif_text += "\n - " + name;
|
||||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||||
NotificationManager::NotificationLevel::RegularNotificationLevel, notif_text);
|
NotificationManager::NotificationLevel::PrintInfoNotificationLevel, notif_text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2442,7 +2442,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
for (ModelObject* model_object : model.objects) {
|
for (ModelObject* model_object : model.objects) {
|
||||||
if (!type_3mf && !type_zip_amf)
|
if (!type_3mf && !type_zip_amf)
|
||||||
model_object->center_around_origin(false);
|
model_object->center_around_origin(false);
|
||||||
model_object->ensure_on_bed(is_project_file || type_3mf || type_zip_amf);
|
model_object->ensure_on_bed(is_project_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check multi-part object adding for the SLA-printing
|
// check multi-part object adding for the SLA-printing
|
||||||
@ -2459,7 +2459,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
if (one_by_one) {
|
if (one_by_one) {
|
||||||
if (type_3mf && !is_project_file)
|
if (type_3mf && !is_project_file)
|
||||||
model.center_instances_around_point(bed_shape_bb().center());
|
model.center_instances_around_point(bed_shape_bb().center());
|
||||||
auto loaded_idxs = load_model_objects(model.objects, is_project_file || type_3mf || type_zip_amf);
|
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
|
||||||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||||
} else {
|
} else {
|
||||||
// This must be an .stl or .obj file, which may contain a maximum of one volume.
|
// This must be an .stl or .obj file, which may contain a maximum of one volume.
|
||||||
@ -2910,7 +2910,7 @@ void Plater::priv::split_object()
|
|||||||
// If we splited object which is contain some parts/modifiers then all non-solid parts (modifiers) were deleted
|
// If we splited object which is contain some parts/modifiers then all non-solid parts (modifiers) were deleted
|
||||||
if (current_model_object->volumes.size() > 1 && current_model_object->volumes.size() != new_objects.size())
|
if (current_model_object->volumes.size() > 1 && current_model_object->volumes.size() != new_objects.size())
|
||||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||||
_u8L("All non-solid parts (modifiers) were deleted"));
|
_u8L("All non-solid parts (modifiers) were deleted"));
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
||||||
@ -6432,7 +6432,7 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
|||||||
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
|
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
|
||||||
get_notification_manager()->push_notification(
|
get_notification_manager()->push_notification(
|
||||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||||
_u8L("Custom supports, seams and multimaterial painting were "
|
_u8L("Custom supports, seams and multimaterial painting were "
|
||||||
"removed after repairing the mesh."));
|
"removed after repairing the mesh."));
|
||||||
// _u8L("Undo the repair"),
|
// _u8L("Undo the repair"),
|
||||||
|
Loading…
Reference in New Issue
Block a user