Merge remote-tracking branch 'remotes/prusa/master' into dev

This commit is contained in:
supermerill 2020-10-27 16:08:00 +01:00
commit 65a40ed58f
77 changed files with 6668 additions and 4053 deletions

View File

@ -397,6 +397,7 @@ endif ()
find_package(PNG REQUIRED)
set(OpenGL_GL_PREFERENCE "LEGACY")
find_package(OpenGL REQUIRED)
# Find glew or use bundled version
@ -513,9 +514,13 @@ endif()
if (WIN32)
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
elseif (SLIC3R_FHS)
# CMAKE_INSTALL_FULL_DATAROOTDIR: read-only architecture-independent data root (share)
set(SLIC3R_FHS_RESOURCES "${CMAKE_INSTALL_FULL_DATAROOTDIR}/SuperSlicer")
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${SLIC3R_FHS_RESOURCES}")
install(FILES src/platform/unix/SuperSlicer.desktop DESTINATION ${SLIC3R_FHS_RESOURCES}/applications)
else ()
install(FILES src/platform/unix/SuperSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
endif ()
configure_file(${LIBDIR}/platform/unix/fhs.hpp.in ${LIBDIR_BIN}/platform/unix/fhs.hpp)

2
deps/PNG/PNG.cmake vendored
View File

@ -1,3 +1,4 @@
prusaslicer_add_cmake_project(PNG
GIT_REPOSITORY https://github.com/glennrp/libpng.git
GIT_TAG v1.6.35
@ -5,6 +6,7 @@ prusaslicer_add_cmake_project(PNG
CMAKE_ARGS
-DPNG_SHARED=OFF
-DPNG_STATIC=ON
-DPNG_PREFIX=prusaslicer_
-DPNG_TESTS=OFF
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="exit" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<path fill="#ED6B21" d="M63.4,45.46l-20-15c-2.51-1.88-6.06-1.37-7.94,1.13c-1.88,2.5-1.37,6.06,1.13,7.94l6.39,4.79H10
c-3.13,0-5.67,2.54-5.67,5.67s2.54,5.67,5.67,5.67h32.99l-6.39,4.8c-2.5,1.88-3.01,5.43-1.13,7.94c1.11,1.49,2.82,2.27,4.54,2.27
c1.18,0,2.38-0.37,3.4-1.13l20-15c1.43-1.07,2.27-2.75,2.27-4.54C65.67,48.22,64.83,46.54,63.4,45.46z"/>
<g>
<path fill="#FFFFFF" d="M90,92.83H40c-1.57,0-2.83-1.27-2.83-2.83V80c0-1.57,1.27-2.83,2.83-2.83s2.83,1.27,2.83,2.83v7.17h44.33
V12.83H42.83V20c0,1.57-1.27,2.83-2.83,2.83s-2.83-1.27-2.83-2.83V10c0-1.57,1.27-2.83,2.83-2.83h50c1.57,0,2.83,1.27,2.83,2.83v80
C92.83,91.57,91.57,92.83,90,92.83z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 997 B

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="change_x5F_profile_copy" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<rect x="-45" y="5" display="none" fill="#FFFFFF" width="55" height="90"/>
<rect x="90" y="5" transform="matrix(-1 -1.224647e-16 1.224647e-16 -1 235 100)" display="none" fill="#FFFFFF" width="55" height="90"/>
<path fill="#ED6B21" d="M88.52,28.64c-1.25-0.94-3.03-0.69-3.97,0.57l-1.72,2.29V15c0-1.57-1.27-2.83-2.83-2.83H65
c-1.57,0-2.83,1.27-2.83,2.83s1.27,2.83,2.83,2.83h12.17V31.5l-1.72-2.29c-0.94-1.25-2.72-1.5-3.97-0.57
c-1.25,0.94-1.51,2.72-0.57,3.97l6.82,9.09c0.54,0.71,1.38,1.13,2.27,1.13s1.73-0.42,2.27-1.13l6.82-9.09
C90.03,31.36,89.77,29.58,88.52,28.64z"/>
<path fill="#ED6B21" d="M35,82.17H22.83V68.5l1.72,2.29c0.56,0.74,1.41,1.13,2.27,1.13c0.59,0,1.19-0.18,1.7-0.57
c1.25-0.94,1.51-2.72,0.57-3.97l-6.82-9.09c-0.54-0.71-1.38-1.13-2.27-1.13s-1.73,0.42-2.27,1.13l-6.82,9.09
c-0.94,1.25-0.69,3.03,0.57,3.97c1.25,0.94,3.03,0.69,3.97-0.57l1.72-2.29V85c0,1.57,1.27,2.83,2.83,2.83h15
c1.57,0,2.83-1.27,2.83-2.83S36.57,82.17,35,82.17z"/>
<polyline display="none" fill="none" stroke="#ED6B21" stroke-width="11.3386" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
35,65 15,50 35,35 "/>
<g>
<path fill="#FFFFFF" d="M50.01,41.11c0-0.75-0.3-1.47-0.83-2l-1.99-1.99H50c1.57,0,2.83-1.27,2.83-2.83v-8.57
c0-1.57-1.27-2.83-2.83-2.83h-2.81l1.99-1.99c0.53-0.53,0.83-1.25,0.83-2s-0.3-1.47-0.83-2l-6.06-6.06c-1.06-1.06-2.95-1.06-4.01,0
l-1.99,1.99V10c0-1.57-1.27-2.83-2.83-2.83h-8.57c-1.57,0-2.83,1.27-2.83,2.83v2.81l-1.99-1.99c-0.53-0.53-1.25-0.83-2-0.83
s-1.47,0.3-2,0.83l-6.06,6.06c-0.53,0.53-0.83,1.25-0.83,2s0.3,1.47,0.83,2l1.99,1.99H10c-1.57,0-2.83,1.27-2.83,2.83v8.57
c0,1.57,1.27,2.83,2.83,2.83h2.81l-1.99,1.99c-0.53,0.53-0.83,1.25-0.83,2s0.3,1.47,0.83,2l6.06,6.06c1.11,1.11,2.9,1.11,4.01,0
l1.99-1.99V50c0,1.57,1.27,2.83,2.83,2.83h8.57c1.57,0,2.83-1.27,2.83-2.83v-2.81l1.99,1.99c1.11,1.11,2.9,1.11,4.01,0l6.06-6.06
C49.71,42.58,50.01,41.86,50.01,41.11z M47.17,31.45h-2.05c-1.27,0-2.38,0.84-2.73,2.06c-0.27,0.96-0.66,1.89-1.14,2.76
c-0.62,1.11-0.43,2.49,0.47,3.39l1.45,1.45l-2.05,2.05l-1.45-1.45c-0.9-0.9-2.28-1.09-3.39-0.47c-0.87,0.49-1.8,0.87-2.76,1.14
c-1.22,0.35-2.06,1.46-2.06,2.73v2.05h-2.9v-2.05c0-1.27-0.84-2.38-2.06-2.73c-0.96-0.27-1.89-0.66-2.76-1.14
c-1.11-0.62-2.49-0.43-3.39,0.47l-1.45,1.45l-2.05-2.05l1.45-1.45c0.9-0.9,1.09-2.28,0.47-3.39c-0.49-0.87-0.87-1.8-1.14-2.76
c-0.35-1.22-1.46-2.06-2.73-2.06h-2.05v-2.9h2.05c1.27,0,2.38-0.84,2.73-2.06c0.27-0.96,0.66-1.89,1.14-2.76
c0.62-1.11,0.43-2.49-0.47-3.39l-1.45-1.45l2.05-2.05l1.45,1.45c0.9,0.9,2.28,1.09,3.39,0.47c0.87-0.49,1.8-0.87,2.76-1.14
c1.22-0.35,2.06-1.46,2.06-2.73v-2.05h2.9v2.05c0,1.27,0.84,2.38,2.06,2.73c0.96,0.27,1.89,0.66,2.76,1.14
c1.11,0.62,2.49,0.43,3.39-0.47l1.45-1.45l2.05,2.05l-1.45,1.45c-0.9,0.9-1.09,2.28-0.47,3.39c0.49,0.87,0.87,1.8,1.14,2.76
c0.35,1.22,1.46,2.06,2.73,2.06h2.05V31.45z"/>
<path fill="#FFFFFF" d="M90,62.88h-2.81l1.99-1.99c1.11-1.11,1.11-2.9,0-4.01l-6.06-6.06c-1.11-1.11-2.9-1.11-4.01,0l-1.99,1.99V50
c0-1.57-1.27-2.83-2.83-2.83h-8.57c-1.57,0-2.83,1.27-2.83,2.83v2.81l-1.99-1.99c-1.11-1.11-2.9-1.11-4.01,0l-6.06,6.06
c-1.11,1.11-1.11,2.9,0,4.01l1.99,1.99H50c-1.57,0-2.83,1.27-2.83,2.83v8.57c0,1.57,1.27,2.83,2.83,2.83h2.81l-1.99,1.99
c-1.11,1.11-1.11,2.9,0,4.01l6.06,6.06c1.11,1.11,2.9,1.11,4.01,0l1.99-1.99V90c0,1.57,1.27,2.83,2.83,2.83h8.57
c1.57,0,2.83-1.27,2.83-2.83v-2.81l1.99,1.99c1.11,1.11,2.9,1.11,4.01,0l6.06-6.06c1.11-1.11,1.11-2.9,0-4.01l-1.99-1.99H90
c1.57,0,2.83-1.27,2.83-2.83v-8.57C92.83,64.15,91.57,62.88,90,62.88z M87.17,71.45h-2.05c-1.27,0-2.38,0.84-2.73,2.06
c-0.27,0.96-0.66,1.89-1.14,2.76c-0.62,1.11-0.43,2.49,0.47,3.39l1.45,1.45l-2.05,2.05l-1.45-1.45c-0.9-0.9-2.28-1.09-3.39-0.47
c-0.87,0.49-1.8,0.87-2.76,1.15c-1.22,0.35-2.06,1.46-2.06,2.73v2.05h-2.9v-2.05c0-1.27-0.84-2.38-2.06-2.73
c-0.96-0.27-1.89-0.66-2.76-1.15c-1.11-0.62-2.49-0.43-3.39,0.47l-1.45,1.45l-2.05-2.05l1.45-1.45c0.9-0.9,1.09-2.28,0.47-3.39
c-0.49-0.87-0.87-1.8-1.14-2.76c-0.35-1.22-1.46-2.06-2.73-2.06h-2.05v-2.9h2.05c1.27,0,2.38-0.84,2.73-2.06
c0.27-0.96,0.66-1.89,1.14-2.76c0.62-1.11,0.43-2.49-0.47-3.39l-1.45-1.45l2.05-2.05l1.45,1.45c0.9,0.9,2.28,1.09,3.39,0.47
c0.87-0.49,1.8-0.87,2.76-1.14c1.22-0.35,2.06-1.46,2.06-2.73v-2.05h2.9v2.05c0,1.27,0.84,2.38,2.06,2.73
c0.96,0.27,1.89,0.66,2.76,1.14c1.11,0.62,2.49,0.43,3.39-0.47l1.45-1.45l2.05,2.05l-1.45,1.45c-0.9,0.9-1.09,2.28-0.47,3.39
c0.49,0.87,0.87,1.8,1.14,2.76c0.35,1.22,1.46,2.06,2.73,2.06h2.05V71.45z"/>
<path fill="#FFFFFF" d="M30,22.17c-4.32,0-7.83,3.51-7.83,7.83s3.51,7.83,7.83,7.83s7.83-3.51,7.83-7.83S34.32,22.17,30,22.17z
M30,32.17c-1.19,0-2.17-0.97-2.17-2.17s0.97-2.17,2.17-2.17s2.17,0.97,2.17,2.17S31.19,32.17,30,32.17z"/>
<path fill="#FFFFFF" d="M70,62.17c-4.32,0-7.83,3.51-7.83,7.83s3.51,7.83,7.83,7.83s7.83-3.51,7.83-7.83S74.32,62.17,70,62.17z
M70,72.17c-1.19,0-2.17-0.97-2.17-2.17s0.97-2.17,2.17-2.17s2.17,0.97,2.17,2.17S71.19,72.17,70,72.17z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
src/slic3r/GUI/AboutDialog.cpp
src/slic3r/GUI/AppConfig.cpp
src/slic3r/GUI/BackgroundSlicingProcess.cpp
src/slic3r/GUI/BedShapeDialog.cpp
src/slic3r/GUI/BedShapeDialog.hpp
@ -9,45 +8,60 @@ src/slic3r/GUI/ConfigManipulation.cpp
src/slic3r/GUI/ConfigSnapshotDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/ExtraRenderers.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/GUI/Field.cpp
src/slic3r/GUI/FirmwareDialog.cpp
src/slic3r/GUI/GCodeViewer.cpp
src/slic3r/GUI/GLCanvas3D.cpp
src/slic3r/GUI/GLCanvas3DManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
src/slic3r/GUI/GUI.cpp
src/slic3r/GUI/GUI_App.cpp
src/slic3r/GUI/GUI_Init.cpp
src/slic3r/GUI/GUI_ObjectLayers.cpp
src/slic3r/GUI/GUI_ObjectList.cpp
src/slic3r/GUI/GUI_ObjectManipulation.cpp
src/slic3r/GUI/GUI_ObjectSettings.cpp
src/slic3r/GUI/GUI_Preview.cpp
src/slic3r/GUI/Job.hpp
src/slic3r/GUI/ImGuiWrapper.cpp
src/slic3r/GUI/Jobs/ArrangeJob.cpp
src/slic3r/GUI/Jobs/Job.cpp
src/slic3r/GUI/Jobs/RotoptimizeJob.cpp
src/slic3r/GUI/Jobs/SLAImportJob.cpp
src/slic3r/GUI/KBShortcutsDialog.cpp
src/slic3r/GUI/MainFrame.cpp
src/slic3r/GUI/Mouse3DController.cpp
src/slic3r/GUI/MsgDialog.cpp
src/slic3r/GUI/NotificationManager.hpp
src/slic3r/GUI/NotificationManager.cpp
src/slic3r/GUI/ObjectDataViewModel.cpp
src/slic3r/GUI/OpenGLManager.cpp
src/slic3r/GUI/OptionsGroup.cpp
src/slic3r/GUI/PhysicalPrinterDialog.cpp
src/slic3r/GUI/Plater.cpp
src/slic3r/GUI/Preferences.cpp
src/slic3r/GUI/Preset.cpp
src/slic3r/GUI/PresetBundle.cpp
src/slic3r/GUI/PresetComboBoxes.cpp
src/slic3r/GUI/PresetHints.cpp
src/slic3r/GUI/PrintHostDialogs.cpp
src/slic3r/GUI/ProgressStatusBar.cpp
src/slic3r/GUI/RammingChart.cpp
src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/Search.cpp
src/slic3r/GUI/Selection.cpp
src/slic3r/GUI/SysInfoDialog.cpp
src/slic3r/GUI/Tab.cpp
src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UnsavedChangesDialog.cpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp
@ -57,18 +71,22 @@ src/slic3r/Utils/FixModelByWin10.cpp
src/slic3r/Utils/FlashAir.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/PresetUpdater.cpp
src/libslic3r/SLA/Pad.cpp
src/libslic3r/SLA/Hollowing.cpp
src/libslic3r/Zipper.cpp
src/slic3r/Utils/Http.cpp
src/slic3r/Utils/Process.cpp
src/libslic3r/GCode.cpp
src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/Flow.cpp
src/libslic3r/Format/3mf.cpp
src/libslic3r/Format/AMF.cpp
src/libslic3r/GCode/PreviewData.cpp
src/libslic3r/miniz_extension.cpp
src/libslic3r/Preset.cpp
src/libslic3r/Print.cpp
src/libslic3r/SLA/Pad.cpp
src/libslic3r/SLA/Hollowing.cpp
src/libslic3r/SLAPrint.cpp
src/libslic3r/SLAPrintSteps.cpp
src/libslic3r/PrintBase.cpp
src/libslic3r/PrintConfig.cpp
src/libslic3r/Zipper.cpp
src/libslic3r/PrintObject.cpp
src/libslic3r/GCode/PreviewData.cpp

View File

@ -1,2 +1,4 @@
min_slic3r_version = 2.3.0-alpha2
0.0.2 Added Anycubic Predator
min_slic3r_version = 2.3.0-alpha0
0.0.1 Initial Version

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -7,7 +7,7 @@ name = BIBO
# This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 0.0.5
# Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/BIBO/
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/BIBO/
# The printer models will be shown by the Configuration Wizard in this order,
# also the first model installed & the first nozzle installed will be activated after install.

View File

@ -4,7 +4,7 @@
# Vendor name will be shown by the Config Wizard.
name = LulzBot
config_version = 0.0.3
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/LulzBot/
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/LulzBot/
[printer_model:MINI_AERO]
name = Mini Aero

View File

@ -204,11 +204,11 @@ if (WIN32)
VERBATIM
)
endif ()
# This has to be a separate target due to the windows command line lenght limits
add_custom_target(slic3rDllsCopy ALL DEPENDS slic3r)
prusaslicer_copy_dlls(slic3rDllsCopy)
else ()
if (APPLE)
# On OSX, the name of the binary matches the name of the Application.
@ -247,4 +247,7 @@ if (WIN32)
endif ()
else ()
install(TARGETS slic3r RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
# Install the symlink for gcodeviewer
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink prusa-slicer prusa-gcodeviewer WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})")
endif ()

View File

@ -40,23 +40,21 @@
#include "libslic3r/Format/OBJ.hpp"
#include "libslic3r/Format/SL1.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Thread.hpp"
#include "PrusaSlicer.hpp"
#ifdef SLIC3R_GUI
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/InstanceCheck.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_Init.hpp"
#endif /* SLIC3R_GUI */
using namespace Slic3r;
int CLI::run(int argc, char **argv)
{
// Mark the main thread for the debugger and for runtime checks.
set_current_thread_name("slic3r_main");
#ifdef __WXGTK__
// On Linux, wxGTK has no support for Wayland, and the app crashes on
// startup if gtk3 is used. This env var has to be set explicitly to
@ -151,7 +149,7 @@ int CLI::run(int argc, char **argv)
#if ENABLE_GCODE_VIEWER
for (const std::string& file : m_input_files) {
std::string ext = boost::filesystem::path(file).extension().string();
if (boost::filesystem::path(file).extension().string() == ".gcode") {
if (ext == ".gcode" || ext == ".g") {
if (boost::filesystem::exists(file)) {
start_as_gcodeviewer = true;
break;
@ -573,75 +571,20 @@ int CLI::run(int argc, char **argv)
if (start_gui) {
#ifdef SLIC3R_GUI
// #ifdef USE_WX
#if ENABLE_GCODE_VIEWER
GUI::GUI_App* gui = new GUI::GUI_App(start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {
// G-code viewer is currently not performing instance check, a new G-code viewer is started every time.
bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1";
if (Slic3r::instance_check(argc, argv, gui_single_instance_setting)) {
//TODO: do we have delete gui and other stuff?
return -1;
}
}
#else
GUI::GUI_App *gui = new GUI::GUI_App();
#endif // ENABLE_GCODE_VIEWER
// gui->autosave = m_config.opt_string("autosave");
GUI::GUI_App::SetInstance(gui);
#if ENABLE_GCODE_VIEWER
gui->after_init_loads.set_params(load_configs, m_extra_config, m_input_files, start_as_gcodeviewer);
#else
gui->after_init_loads.set_params(load_configs, m_extra_config, m_input_files);
#endif // ENABLE_GCODE_VIEWER
/*
#if ENABLE_GCODE_VIEWER
gui->CallAfter([gui, this, &load_configs, start_as_gcodeviewer] {
#else
gui->CallAfter([gui, this, &load_configs] {
#endif // ENABLE_GCODE_VIEWER
if (!gui->initialized()) {
return;
}
#if ENABLE_GCODE_VIEWER
if (start_as_gcodeviewer) {
if (!m_input_files.empty())
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str()));
} else {
#endif // ENABLE_GCODE_VIEWER_AS
#if 0
// Load the cummulative config over the currently active profiles.
//FIXME if multiple configs are loaded, only the last one will have an effect.
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here.
if (!m_print_config.empty())
gui->mainframe->load_config(m_print_config);
#endif
if (!load_configs.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(load_configs.back());
// If loading a 3MF file, the config is loaded from the last one.
if (!m_input_files.empty())
gui->plater()->load_files(m_input_files, true, true);
if (!m_extra_config.empty())
gui->mainframe->load_config(m_extra_config);
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
});
*/
int result = wxEntry(argc, argv);
return result;
#else /* SLIC3R_GUI */
Slic3r::GUI::GUI_InitParams params;
params.argc = argc;
params.argv = argv;
params.load_configs = load_configs;
params.extra_config = std::move(m_extra_config);
params.input_files = std::move(m_input_files);
params.start_as_gcodeviewer = start_as_gcodeviewer;
return Slic3r::GUI::GUI_Run(params);
#else // SLIC3R_GUI
// No GUI support. Just print out a help.
this->print_help(false);
// If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
return (argc == 0) ? 0 : 1;
#endif /* SLIC3R_GUI */
#endif // SLIC3R_GUI
}
return 0;

View File

@ -42,10 +42,16 @@
#include <linux/hidraw.h>
#include <linux/version.h>
#include <linux/input.h>
#include <libudev.h>
#include "hidapi.h"
// Declare udev structures needed in this module. They are passed by pointers
// to udev functions and not used directly.
struct udev_device;
struct udev_list_entry;
struct udev_enumerate;
struct udev;
typedef const char* (*hid_wrapper_udev_device_get_devnode_type)(struct udev_device *udev_device);
typedef struct udev_device* (*hid_wrapper_udev_device_get_parent_with_subsystem_devtype_type)(struct udev_device *udev_device, const char *subsystem, const char *devtype);
typedef const char* (*hid_wrapper_udev_device_get_sysattr_value_type)(struct udev_device *udev_device, const char *sysattr);

View File

@ -2,6 +2,7 @@
#include "libslic3r/Utils.hpp"
#include "AppConfig.hpp"
#include "Exception.hpp"
#include "Thread.hpp"
#include <utility>
#include <vector>
@ -80,9 +81,9 @@ void AppConfig::set_defaults()
set("single_instance",
#ifdef __APPLE__
"1"
#else __APPLE__
#else // __APPLE__
"0"
#endif __APPLE__
#endif // __APPLE__
);
if (get("remember_output_path").empty())
@ -218,6 +219,13 @@ std::string AppConfig::load()
void AppConfig::save()
{
{
// Returns "undefined" if the thread naming functionality is not supported by the operating system.
std::optional<std::string> current_thread_name = get_current_thread_name();
if (current_thread_name && *current_thread_name != "slic3r_main")
throw CriticalException("Calling AppConfig::save() from a worker thread!");
}
// The config is first written to a file with a PID suffix and then moved
// to avoid race conditions with multiple instances of Slic3r
const auto path = config_path();

View File

@ -209,6 +209,8 @@ add_library(libslic3r STATIC
Utils.hpp
Time.cpp
Time.hpp
Thread.cpp
Thread.hpp
TriangleSelector.cpp
TriangleSelector.hpp
MTUtils.hpp

View File

@ -828,7 +828,8 @@ void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_
std::error_code err_code;
if (rename_file(path_tmp, path))
if (copy_file(path_tmp, path, true) != SUCCESS)
if (copy_file(path_tmp, path, ("Failed to rename the output G-code file from " + path_tmp + " to " + path + '\n' +
"Is " + path_tmp + " locked? " + err_code.message() + '\n'), true) != CopyFileResult::SUCCESS)
throw Slic3r::RuntimeError(
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
"Is " + path_tmp + " locked? " + err_code.message() + '\n');
@ -1880,16 +1881,22 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
while (*ptr != 0) {
// Skip whitespaces.
for (; *ptr == ' ' || *ptr == '\t'; ++ ptr);
if (*ptr == 'M') {
// Line starts with 'M'. It is a machine command.
if (*ptr == 'M' || // Line starts with 'M'. It is a machine command.
(*ptr == 'G' && include_g10)) { // Only check for G10 if requested
bool is_gcode = *ptr == 'G';
++ ptr;
// Parse the M code value.
// Parse the M or G code value.
char *endptr = nullptr;
int mcode = int(strtol(ptr, &endptr, 10));
if (endptr != nullptr && endptr != ptr && (mcode == mcode_set_temp_dont_wait || mcode == mcode_set_temp_and_wait)) {
int mgcode = int(strtol(ptr, &endptr, 10));
if (endptr != nullptr && endptr != ptr &&
is_gcode ?
// G10 found
mgcode == 10 :
// M104/M109 or M140/M190 found.
(mgcode == mcode_set_temp_dont_wait || mgcode == mcode_set_temp_and_wait)) {
ptr = endptr;
// Let the caller know that the custom G-code sets the temperature.
if (! is_gcode)
// Let the caller know that the custom M-code sets the temperature.
temp_set_by_gcode = true;
// Now try to parse the temperature value.
// While not at the end of the line:
@ -1905,6 +1912,10 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
if (endptr > ptr) {
ptr = endptr;
temp_out = temp_parsed;
// Let the caller know that the custom G-code sets the temperature
// Only do this after successfully parsing temperature since G10
// can be used for other reasons
temp_set_by_gcode = true;
}
} else {
// Skip this word.
@ -1912,41 +1923,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
}
}
}
} else if (*ptr == 'G' && include_g10) { // Only check for G10 if requested
// Line starts with 'G'.
++ ptr;
// Parse the G code value.
char *endptr = nullptr;
int gcode = int(strtol(ptr, &endptr, 10));
if (endptr != nullptr && endptr != ptr && gcode == 10 /* G10 */) {
// G10 code found
ptr = endptr;
// Now try to parse the temperature value.
// While not at the end of the line:
while (strchr(";\r\n\0", *ptr) == nullptr) {
// Skip whitespaces.
for (; *ptr == ' ' || *ptr == '\t'; ++ ptr);
if (*ptr == 'S') {
// Skip whitespaces.
for (++ ptr; *ptr == ' ' || *ptr == '\t'; ++ ptr);
// Parse an int.
endptr = nullptr;
long temp_parsed = strtol(ptr, &endptr, 10);
if (endptr > ptr) {
ptr = endptr;
temp_out = temp_parsed;
// Let the caller know that the custom G-code sets the temperature
// Only do this after successfully parsing temperature since G10
// can be used for other reasons
temp_set_by_gcode = true;
}
} else {
// Skip this word.
for (; strchr(" \t;\r\n\0", *ptr) == nullptr; ++ ptr);
}
}
}
}
// Skip the rest of the line.
for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr);
// Skip the end of line indicators.
@ -2035,11 +2012,13 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M104 - Set Extruder Temperature
// M109 - Set Extruder Temperature and Wait
// RepRapFirmware: G10 Sxx
void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
if (custom_gcode_sets_temperature(gcode, 104, 109, true, temp_by_gcode)) {
bool include_g10 = print.config().gcode_flavor == gcfRepRap;
if (custom_gcode_sets_temperature(gcode, 104, 109, include_g10, temp_by_gcode)) {
// Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code.
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)

View File

@ -400,7 +400,7 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
const TimeMachine& machine = machines[i];
if (machine.enabled && g1_lines_counter < machine.g1_times_cache.size()) {
float elapsed_time = machine.g1_times_cache[g1_lines_counter];
std::pair<int, int> to_export = { int(::roundf(100.0f * elapsed_time / machine.time)),
std::pair<int, int> to_export = { int(100.0f * elapsed_time / machine.time),
time_in_minutes(machine.time - elapsed_time) };
if (last_exported[i] != to_export) {
export_line += format_line_M73(machine.line_m73_mask.c_str(),
@ -461,7 +461,8 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
std::error_code err_code;
if (err_code = rename_file(out_path, filename))
if(copy_file(out_path, filename, true) != SUCCESS)
if(copy_file(out_path, filename, (std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
"Is " + out_path + " locked? (gcp)" + err_code.message() + '\n'), true) != SUCCESS)
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + filename + '\n' +
"Is " + out_path + " locked? (gcp)" + err_code.message() + '\n');
}
@ -691,7 +692,7 @@ void GCodeProcessor::reset()
m_global_positioning_type = EPositioningType::Absolute;
m_e_local_positioning_type = EPositioningType::Absolute;
m_extruder_offsets = std::vector<Vec3f>(Min_Extruder_Count, Vec3f::Zero());
m_flavor = gcfRepRap;
m_flavor = gcfSprinter;
m_start_position = { 0.0f, 0.0f, 0.0f, 0.0f };
m_end_position = { 0.0f, 0.0f, 0.0f, 0.0f };
@ -1128,6 +1129,8 @@ bool GCodeProcessor::process_cura_tags(const std::string& comment)
m_flavor = gcfRepetier;
else if (flavor == "RepRap")
m_flavor = gcfRepRap;
else if (flavor == "Sprinter")
m_flavor = gcfSprinter;
else if (flavor == "Marlin")
m_flavor = gcfMarlin;
else
@ -1832,7 +1835,7 @@ void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
return;
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
float factor = (m_flavor != gcfRepRap && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
float factor = ((m_flavor != gcfSprinter && m_flavor != gcfRepRap) && m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
if (line.has_x())

View File

@ -100,7 +100,7 @@ std::string GCodeWriter::set_temperature(const unsigned int temperature, bool wa
return "";
std::string code, comment;
if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRap) && FLAVOR_IS_NOT(gcfSprinter)) {
if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRap)) {
code = "M109";
comment = "set temperature and wait for it to be reached";
} else {
@ -124,7 +124,9 @@ std::string GCodeWriter::set_temperature(const unsigned int temperature, bool wa
gcode << temp_w_offset;
bool multiple_tools = this->multiple_extruders && ! m_single_extruder_multi_material;
if (tool != -1 && (multiple_tools || FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) ) {
if (FLAVOR_IS_NOT(gcfRepRap)) {
if (FLAVOR_IS(gcfRepRap)) {
gcode << " P" << tool;
} else {
gcode << " T" << tool;
}
}

View File

@ -12,7 +12,6 @@
#include <CGAL/Exact_integer.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>

View File

@ -1055,6 +1055,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
ModelVolume* vol = new_object->add_volume(mesh);
vol->name = volume->name;
vol->set_type(volume->type());
// Don't copy the config's ID.
vol->config.assign_config(volume->config);
assert(vol->config.id().valid());
@ -1065,7 +1066,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, bool from_imperial
if (volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end()) {
vol->scale_geometry_after_creation(versor);
vol->set_offset(versor.cwiseProduct(vol->get_offset()));
vol->set_offset(versor.cwiseProduct(volume->get_offset()));
}
else
vol->set_offset(volume->get_offset());

View File

@ -11,6 +11,7 @@
#include "I18N.hpp"
#include "ShortestPath.hpp"
#include "SupportMaterial.hpp"
#include "Thread.hpp"
#include "GCode.hpp"
#include "GCode/WipeTower.hpp"
#include "Utils.hpp"
@ -1696,6 +1697,8 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
// Slicing process, running at a background thread.
void Print::process()
{
name_tbb_thread_pool_threads();
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
obj->make_perimeters();

View File

@ -38,17 +38,18 @@ namespace FillAdaptive {
};
// Print step IDs for keeping track of the print state.
// The Print steps are applied in this order.
enum PrintStep {
psSkirt,
psBrim,
// Synonym for the last step before the Wipe Tower / Tool Ordering, for the G-code preview slider to understand that
// all the extrusions are there for the layer slider to add color changes etc.
psExtrusionPaths = psBrim,
psWipeTower,
// Ordering of the tools on PrintObjects for a multi-material print.
// psToolOrdering is a synonym to psWipeTower, as the Wipe Tower calculates and modifies the ToolOrdering,
// while if printing without the Wipe Tower, the ToolOrdering is calculated as well.
psToolOrdering = psWipeTower,
psSlicingFinished = psToolOrdering,
psSkirt,
psBrim,
// Last step before G-code export, after this step is finished, the initial extrusion path preview
// should be refreshed.
psSlicingFinished = psBrim,
psGCodeExport,
psCount,
};

View File

@ -1778,7 +1778,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("sprinter");
def->enum_values.push_back("lerdge");
def->enum_values.push_back("no-extrusion");
def->enum_labels.push_back("RepRap");
def->enum_labels.push_back("RepRapFirmware");
def->enum_labels.push_back("Repetier");
def->enum_labels.push_back("Teacup");
def->enum_labels.push_back("MakerWare (MakerBot)");
@ -1792,7 +1792,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("Lerdge");
def->enum_labels.push_back(L("No extrusion"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfRepRap));
def->set_default_value(new ConfigOptionEnum<GCodeFlavor>(gcfSprinter));
def = this->add("gcode_label_objects", coBool);
def->label = L("Label objects");

View File

@ -861,14 +861,14 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
// propagate to dependent steps
if (step == posPerimeters) {
invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill });
invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
} else if (step == posPrepareInfill) {
invalidated |= this->invalidate_step(posInfill);
invalidated |= this->invalidate_steps({ posInfill, posIroning });
} else if (step == posInfill) {
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial });
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false;
} else if (step == posSupportMaterial) {
@ -2228,12 +2228,6 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
m_typed_slices = false;
#ifdef SLIC3R_PROFILE
// Disable parallelization so the Shiny profiler works
static tbb::task_scheduler_init *tbb_init = nullptr;
tbb_init = new tbb::task_scheduler_init(1);
#endif
// 1) Initialize layers and their slice heights.
std::vector<float> slice_zs;
{

View File

@ -4,6 +4,7 @@
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "MTUtils.hpp"
#include "Thread.hpp"
#include <unordered_set>
#include <numeric>
@ -689,7 +690,10 @@ bool SLAPrint::invalidate_step(SLAPrintStep step)
void SLAPrint::process()
{
if(m_objects.empty()) return;
if (m_objects.empty())
return;
name_tbb_thread_pool_threads();
// Assumption: at this point the print objects should be populated only with
// the model objects we have to process and the instances are also filtered

View File

@ -57,4 +57,5 @@
#define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER)
#define ENABLE_GCODE_VIEWER_DATA_CHECKING (0 && ENABLE_GCODE_VIEWER)
#endif // _prusaslicer_technologies_h_

238
src/libslic3r/Thread.cpp Normal file
View File

@ -0,0 +1,238 @@
#ifdef _WIN32
#include <windows.h>
#include <boost/nowide/convert.hpp>
#else
// any posix system
#include <pthread.h>
#endif
#include <condition_variable>
#include <mutex>
#include <tbb/parallel_for.h>
#include <tbb/tbb_thread.h>
#include <tbb/task_scheduler_init.h>
#include "Thread.hpp"
namespace Slic3r {
#ifdef _WIN32
// The new API is better than the old SEH style thread naming since the names also show up in crash dumpsand ETW traces.
// Because the new API is only available on newer Windows 10, look it up dynamically.
typedef HRESULT(__stdcall* SetThreadDescriptionType)(HANDLE, PCWSTR);
typedef HRESULT(__stdcall* GetThreadDescriptionType)(HANDLE, PWSTR*);
static bool s_SetGetThreadDescriptionInitialized = false;
static HMODULE s_hKernel32 = nullptr;
static SetThreadDescriptionType s_fnSetThreadDescription = nullptr;
static GetThreadDescriptionType s_fnGetThreadDescription = nullptr;
static bool WindowsGetSetThreadNameAPIInitialize()
{
if (! s_SetGetThreadDescriptionInitialized) {
// Not thread safe! It is therefore a good idea to name the main thread before spawning worker threads
// to initialize
s_hKernel32 = LoadLibraryW(L"Kernel32.dll");
if (s_hKernel32) {
s_fnSetThreadDescription = (SetThreadDescriptionType)::GetProcAddress(s_hKernel32, "SetThreadDescription");
s_fnGetThreadDescription = (GetThreadDescriptionType)::GetProcAddress(s_hKernel32, "GetThreadDescription");
}
s_SetGetThreadDescriptionInitialized = true;
}
return s_fnSetThreadDescription && s_fnGetThreadDescription;
}
#ifndef NDEBUG
// Use the old way by throwing an exception, so at least in Debug mode the thread names are shown by the debugger.
static constexpr DWORD MSVC_SEH_EXCEPTION_NAME_THREAD = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
static void WindowsSetThreadNameSEH(HANDLE hThread, const char* thread_name)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = thread_name;
info.dwThreadID = ::GetThreadId(hThread);
info.dwFlags = 0;
__try {
RaiseException(MSVC_SEH_EXCEPTION_NAME_THREAD, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
} __except (EXCEPTION_EXECUTE_HANDLER) {
}
}
#endif // NDEBUG
static bool WindowsSetThreadName(HANDLE hThread, const char *thread_name)
{
if (! WindowsGetSetThreadNameAPIInitialize()) {
#ifdef NDEBUG
return false;
#else // NDEBUG
// Running on Windows 7 or old Windows 7 in debug mode,
// inform the debugger about the thread name by throwing an SEH.
WindowsSetThreadNameSEH(hThread, thread_name);
return true;
#endif // NDEBUG
}
size_t len = strlen(thread_name);
if (len < 1024) {
// Allocate the temp string on stack.
wchar_t buf[1024];
s_fnSetThreadDescription(hThread, boost::nowide::widen(buf, 1024, thread_name));
} else {
// Allocate dynamically.
s_fnSetThreadDescription(hThread, boost::nowide::widen(thread_name).c_str());
}
return true;
}
bool set_thread_name(std::thread &thread, const char *thread_name)
{
return WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
}
bool set_thread_name(boost::thread &thread, const char *thread_name)
{
return WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
}
bool set_current_thread_name(const char *thread_name)
{
return WindowsSetThreadName(::GetCurrentThread(), thread_name);
}
std::optional<std::string> get_current_thread_name()
{
if (! WindowsGetSetThreadNameAPIInitialize())
return std::nullopt;
wchar_t *ptr = nullptr;
s_fnGetThreadDescription(::GetCurrentThread(), &ptr);
return (ptr == nullptr) ? std::string() : boost::nowide::narrow(ptr);
}
#else // _WIN32
#ifdef __APPLE__
// Appe screwed the Posix norm.
bool set_thread_name(std::thread &thread, const char *thread_name)
{
// not supported
// pthread_setname_np(thread.native_handle(), thread_name);
return false;
}
bool set_thread_name(boost::thread &thread, const char *thread_name)
{
// not supported
// pthread_setname_np(thread.native_handle(), thread_name);
return false;
}
bool set_current_thread_name(const char *thread_name)
{
pthread_setname_np(thread_name);
return true;
}
std::optional<std::string> get_current_thread_name()
{
// not supported
// char buf[16];
// return std::string(thread_getname_np(buf, 16) == 0 ? buf : "");
return std::nullopt;
}
#else
// posix
bool set_thread_name(std::thread &thread, const char *thread_name)
{
pthread_setname_np(thread.native_handle(), thread_name);
return true;
}
bool set_thread_name(boost::thread &thread, const char *thread_name)
{
pthread_setname_np(thread.native_handle(), thread_name);
return true;
}
bool set_current_thread_name(const char *thread_name)
{
pthread_setname_np(pthread_self(), thread_name);
return true;
}
std::optional<std::string> get_current_thread_name()
{
char buf[16];
return std::string(pthread_getname_np(pthread_self(), buf, 16) == 0 ? buf : "");
}
#endif
#endif // _WIN32
// Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID.
void name_tbb_thread_pool_threads()
{
static bool initialized = false;
if (initialized)
return;
initialized = true;
const size_t nthreads_hw = std::thread::hardware_concurrency();
size_t nthreads = nthreads_hw;
#ifdef SLIC3R_PROFILE
// Shiny profiler is not thread safe, thus disable parallelization.
nthreads = 1;
#endif
if (nthreads != nthreads_hw)
new tbb::task_scheduler_init(int(nthreads));
std::atomic<size_t> nthreads_running(0);
std::condition_variable cv;
std::mutex cv_m;
auto master_thread_id = tbb::this_tbb_thread::get_id();
tbb::parallel_for(
tbb::blocked_range<size_t>(0, nthreads, 1),
[&nthreads_running, nthreads, &master_thread_id, &cv, &cv_m](const tbb::blocked_range<size_t> &range) {
assert(range.begin() + 1 == range.end());
if (nthreads_running.fetch_add(1) + 1 == nthreads) {
// All threads are spinning.
// Wake them up.
cv.notify_all();
} else {
// Wait for the last thread to wake the others.
std::unique_lock<std::mutex> lk(cv_m);
cv.wait(lk, [&nthreads_running, nthreads]{return nthreads_running == nthreads;});
}
auto thread_id = tbb::this_tbb_thread::get_id();
if (thread_id == master_thread_id) {
// The calling thread runs the 0'th task.
assert(range.begin() == 0);
} else {
assert(range.begin() > 0);
std::ostringstream name;
name << "slic3r_tbb_" << range.begin();
set_current_thread_name(name.str().c_str());
}
});
}
}

57
src/libslic3r/Thread.hpp Normal file
View File

@ -0,0 +1,57 @@
#ifndef GUI_THREAD_HPP
#define GUI_THREAD_HPP
#include <utility>
#include <string>
#include <thread>
#include <boost/thread.hpp>
namespace Slic3r {
// Set / get thread name.
// Returns false if the API is not supported.
//
// It is a good idea to name the main thread before spawning children threads, because dynamic linking is used on Windows 10
// to initialize Get/SetThreadDescription functions, which is not thread safe.
//
// pthread_setname_np supports maximum 15 character thread names! (16th character is the null terminator)
//
// Methods taking the thread as an argument are not supported by OSX.
// Naming threads is only supported on newer Windows 10.
bool set_thread_name(std::thread &thread, const char *thread_name);
inline bool set_thread_name(std::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
bool set_thread_name(boost::thread &thread, const char *thread_name);
inline bool set_thread_name(boost::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
bool set_current_thread_name(const char *thread_name);
inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); }
// Returns nullopt if not supported.
// Not supported by OSX.
// Naming threads is only supported on newer Windows 10.
std::optional<std::string> get_current_thread_name();
// To be called somewhere before the TBB threads are spinned for the first time, to
// give them names recognizible in the debugger.
void name_tbb_thread_pool_threads();
template<class Fn>
inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)
{
// Duplicating the stack allocation size of Thread Building Block worker
// threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB
// on a 32bit system by default.
attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024));
return boost::thread{attrs, std::forward<Fn>(fn)};
}
template<class Fn> inline boost::thread create_thread(Fn &&fn)
{
boost::thread::attributes attrs;
return create_thread(attrs, std::forward<Fn>(fn));
}
}
#endif // GUI_THREAD_HPP

View File

@ -7,6 +7,8 @@
#include <type_traits>
#include <system_error>
#include <boost/system/error_code.hpp>
#include "libslic3r.h"
namespace boost { namespace filesystem { class directory_entry; }}
@ -73,11 +75,12 @@ enum CopyFileResult {
FAIL_CHECK_TARGET_NOT_OPENED
};
// Copy a file, adjust the access attributes, so that the target is writable.
CopyFileResult copy_file_inner(const std::string &from, const std::string &to);
CopyFileResult copy_file_inner(const std::string &from, const std::string &to, std::string& error_message);
// Copy file to a temp file first, then rename it to the final file name.
// If with_check is true, then the content of the copied file is compared to the content
// of the source file before renaming.
extern CopyFileResult copy_file(const std::string &from, const std::string &to, const bool with_check = false);
// Additional error info is passed in error message.
extern CopyFileResult copy_file(const std::string &from, const std::string &to, std::string& error_message, const bool with_check = false);
// Compares two files if identical.
extern CopyFileResult check_copy(const std::string& origin, const std::string& copy);

View File

@ -417,7 +417,7 @@ std::error_code rename_file(const std::string &from, const std::string &to)
#endif
}
CopyFileResult copy_file_inner(const std::string& from, const std::string& to)
CopyFileResult copy_file_inner(const std::string& from, const std::string& to, std::string& error_message)
{
const boost::filesystem::path source(from);
const boost::filesystem::path target(to);
@ -429,20 +429,31 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to)
// the copy_file() function will fail appropriately and we don't want the permission()
// calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.)
// or when the target file doesn't exist.
//This error code is ignored
boost::system::error_code ec;
boost::filesystem::permissions(target, perms, ec);
//if (ec)
// BOOST_LOG_TRIVIAL(error) << "Copy file permisions before copy error message: " << ec.message();
// This error code is passed up
ec.clear();
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
if (ec) {
error_message = ec.message();
return FAIL_COPY_FILE;
}
//ec.clear();
boost::filesystem::permissions(target, perms, ec);
//if (ec)
// BOOST_LOG_TRIVIAL(error) << "Copy file permisions after copy error message: " << ec.message();
return SUCCESS;
}
CopyFileResult copy_file(const std::string &from, const std::string &to, const bool with_check)
CopyFileResult copy_file(const std::string &from, const std::string &to, std::string& error_message, const bool with_check)
{
std::string to_temp = to + ".tmp";
CopyFileResult ret_val = copy_file_inner(from,to_temp);
CopyFileResult ret_val = copy_file_inner(from, to_temp, error_message);
if(ret_val == SUCCESS)
{
if (with_check)

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Name=PrusaSlicer
Exec=prusa-slicer %F
Icon=PrusaSlicer
Terminal=false
Type=Application
MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;
Categories=Graphics;3DGraphics;
Keywords=3D;Printing;Slicer;

View File

@ -91,6 +91,8 @@ set(SLIC3R_GUI_SOURCES
GUI/PresetHints.hpp
GUI/GUI.cpp
GUI/GUI.hpp
GUI/GUI_Init.cpp
GUI/GUI_Init.hpp
GUI/GUI_Preview.cpp
GUI/GUI_Preview.hpp
GUI/GUI_App.cpp
@ -105,6 +107,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Plater.hpp
GUI/PresetComboBoxes.hpp
GUI/PresetComboBoxes.cpp
GUI/SavePresetDialog.hpp
GUI/SavePresetDialog.cpp
GUI/PhysicalPrinterDialog.hpp
GUI/PhysicalPrinterDialog.cpp
GUI/GUI_ObjectList.cpp
@ -220,7 +224,6 @@ set(SLIC3R_GUI_SOURCES
Utils/UndoRedo.hpp
Utils/HexFile.cpp
Utils/HexFile.hpp
Utils/Thread.hpp
)
if (APPLE)

View File

@ -23,6 +23,7 @@
#include "libslic3r/GCode/PreviewData.hpp"
#endif // !ENABLE_GCODE_VIEWER
#include "libslic3r/Format/SL1.hpp"
#include "libslic3r/Thread.hpp"
#include "libslic3r/libslic3r.h"
#include <cassert>
@ -36,7 +37,6 @@
#include "I18N.hpp"
#include "RemovableDriveManager.hpp"
#include "slic3r/Utils/Thread.hpp"
#include "slic3r/GUI/Plater.hpp"
namespace Slic3r {
@ -45,7 +45,7 @@ bool SlicingProcessCompletedEvent::critical_error() const
{
try {
this->rethrow_exception();
} catch (const Slic3r::SlicingError &ex) {
} catch (const Slic3r::SlicingError &) {
// Exception derived from SlicingError is non-critical.
return false;
} catch (...) {
@ -138,11 +138,12 @@ void BackgroundSlicingProcess::process_fff()
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
int copy_ret_val = copy_file(m_temp_output_path, export_path, m_export_path_on_removable_media);
std::string error_message;
int copy_ret_val = copy_file(m_temp_output_path, export_path, error_message, m_export_path_on_removable_media);
switch (copy_ret_val) {
case SUCCESS: break; // no error
case FAIL_COPY_FILE:
throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str());
break;
case FAIL_FILES_DIFFERENT:
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
@ -157,7 +158,8 @@ void BackgroundSlicingProcess::process_fff()
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
break;
default:
BOOST_LOG_TRIVIAL(warning) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << ".";
throw Slic3r::RuntimeError(_utf8(L("Unknown error occured during exporting G-code.")));
BOOST_LOG_TRIVIAL(error) << "Unexpected fail code(" << (int)copy_ret_val << ") durring copy_file() to " << export_path << ".";
break;
}
@ -232,6 +234,9 @@ void BackgroundSlicingProcess::process_sla()
void BackgroundSlicingProcess::thread_proc()
{
set_current_thread_name("slic3r_BgSlcPcs");
name_tbb_thread_pool_threads();
assert(m_print != nullptr);
assert(m_print == m_fff_print || m_print == m_sla_print);
std::unique_lock<std::mutex> lck(m_mutex);
@ -527,7 +532,8 @@ void BackgroundSlicingProcess::prepare_upload()
if (m_print == m_fff_print) {
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
if (copy_file(m_temp_output_path, source_path.string()) != SUCCESS) {
std::string error_message;
if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS) {
throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
}
run_post_process_scripts(source_path.string(), m_fff_print->config());

View File

@ -916,7 +916,7 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
const std::string& type = list_type->get_data(sel_type);
const std::string& vendor = list_vendor->get_data(sel_vendor);
// finst printer preset
std::vector<std::pair<std::reference_wrapper<const std::string>, bool>> to_list;
std::vector<ProfilePrintData> to_list;
for (size_t i = 0; i < sel_printers_count; i++) {
const std::string& printer_name = list_printer->get_data(sel_printers[i]);
const Preset* printer = nullptr;
@ -931,16 +931,19 @@ void PageMaterials::update_lists(int sel_printer, int sel_type, int sel_vendor)
bool was_checked = false;
//size_t printer_counter = materials->get_printer_counter(p);
int cur_i = list_profile->find(p->alias);
bool emplace_to_to_list = false;
if (cur_i == wxNOT_FOUND) {
cur_i = list_profile->append(p->alias + (materials->get_omnipresent(p) ? "" : " *"), &p->alias);
to_list.emplace_back(p->alias, materials->get_omnipresent(p));
emplace_to_to_list = true;
} else
was_checked = list_profile->IsChecked(cur_i);
const std::string& section = materials->appconfig_section();
const bool checked = wizard_p()->appconfig_new.has(section, p->name);
list_profile->Check(cur_i, checked | was_checked);
list_profile->Check(cur_i, checked || was_checked);
if (emplace_to_to_list)
to_list.emplace_back(p->alias, materials->get_omnipresent(p), checked || was_checked);
/* Update preset selection in config.
* If one preset from aliases bundle is selected,
@ -1018,33 +1021,39 @@ void PageMaterials::sort_list_data(StringList* list, bool add_All_item, bool mat
list->append(item, &const_cast<std::string&>(item.get()));
}
void PageMaterials::sort_list_data(PresetList* list, const std::vector<std::pair<std::reference_wrapper<const std::string>, bool>>& data)
void PageMaterials::sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data)
{
// sort data
// then prusa profiles
// then the rest
// in alphabetical order
std::vector<std::pair<std::reference_wrapper<const std::string>, bool>> prusa_profiles;
std::vector<std::pair<std::reference_wrapper<const std::string>, bool>> other_profiles;
std::vector<ProfilePrintData> prusa_profiles;
std::vector<ProfilePrintData> other_profiles;
//for (int i = 0; i < data.size(); ++i) {
for (const auto& item : data) {
const std::string& name = item.first;
const std::string& name = item.name;
if (name.find("Prusa") != std::string::npos)
prusa_profiles.emplace_back(item);
else
other_profiles.emplace_back(item);
}
std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](std::pair<std::reference_wrapper<const std::string>, bool> a, std::pair<std::reference_wrapper<const std::string>, bool> b) {
return a.first.get() < b.first.get();
std::sort(prusa_profiles.begin(), prusa_profiles.end(), [](ProfilePrintData a, ProfilePrintData b) {
return a.name.get() < b.name.get();
});
std::sort(other_profiles.begin(), other_profiles.end(), [](std::pair<std::reference_wrapper<const std::string>, bool> a, std::pair<std::reference_wrapper<const std::string>, bool> b) {
return a.first.get() < b.first.get();
std::sort(other_profiles.begin(), other_profiles.end(), [](ProfilePrintData a, ProfilePrintData b) {
return a.name.get() < b.name.get();
});
list->Clear();
for (const auto& item : prusa_profiles)
list->append(std::string(item.first) + (item.second ? "" : " *"), &const_cast<std::string&>(item.first.get()));
for (const auto& item : other_profiles)
list->append(std::string(item.first) + (item.second ? "" : " *"), &const_cast<std::string&>(item.first.get()));
//for (const auto& item : prusa_profiles)
for (int i = 0; i < prusa_profiles.size(); ++i) {
list->append(std::string(prusa_profiles[i].name) + (prusa_profiles[i].omnipresent ? "" : " *"), &const_cast<std::string&>(prusa_profiles[i].name.get()));
list->Check(i, prusa_profiles[i].checked);
}
//for (const auto& item : other_profiles)
for (int i = 0; i < other_profiles.size(); ++i) {
list->append(std::string(other_profiles[i].name) + (other_profiles[i].omnipresent ? "" : " *"), &const_cast<std::string&>(other_profiles[i].name.get()));
list->Check(i + prusa_profiles.size(), other_profiles[i].checked);
}
}
void PageMaterials::select_material(int i)

View File

@ -315,6 +315,14 @@ template<class T, class D> struct DataList : public T
typedef DataList<wxListBox, std::string> StringList;
typedef DataList<wxCheckListBox, std::string> PresetList;
struct ProfilePrintData
{
std::reference_wrapper<const std::string> name;
bool omnipresent;
bool checked;
ProfilePrintData(const std::string& n, bool o, bool c) : name(n), omnipresent(o), checked(c) {}
};
struct PageMaterials: ConfigWizardPage
{
Materials *materials;
@ -345,7 +353,7 @@ struct PageMaterials: ConfigWizardPage
void clear_compatible_printers_label();
void sort_list_data(StringList* list, bool add_All_item, bool material_type_ordering);
void sort_list_data(PresetList* list, const std::vector<std::pair<std::reference_wrapper<const std::string>, bool>>& data);
void sort_list_data(PresetList* list, const std::vector<ProfilePrintData>& data);
void on_paint();
void on_mouse_move_on_profiles(wxMouseEvent& evt);

View File

@ -12,6 +12,9 @@
#include "I18N.hpp"
#include "ExtruderSequenceDialog.hpp"
#include "libslic3r/Print.hpp"
#if ENABLE_GCODE_VIEWER
#include "libslic3r/AppConfig.hpp"
#endif // ENABLE_GCODE_VIEWER
#include <wx/button.h>
#include <wx/dialog.h>
@ -45,17 +48,18 @@ static std::string gcode(Type type)
{
const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config();
switch (type) {
case ColorChange:
return config.color_change_gcode;
case PausePrint:
return config.pause_print_gcode;
case Template:
return config.template_custom_gcode;
default:
return "";
case ColorChange: return config.color_change_gcode;
case PausePrint: return config.pause_print_gcode;
case Template: return config.template_custom_gcode;
default: return "";
}
}
static bool is_lower_thumb_editable()
{
return Slic3r::GUI::get_app_config()->get("seq_top_layer_only") == "0";
}
Control::Control( wxWindow *parent,
wxWindowID id,
int lowerValue,
@ -963,7 +967,7 @@ int Control::get_value_from_position(const wxCoord x, const wxCoord y)
bool Control::detect_selected_slider(const wxPoint& pt)
{
if (is_point_in_rect(pt, m_rect_lower_thumb))
m_selection = m_lower_editable ? ssLower : ssUndef;
m_selection = is_lower_thumb_editable() ? ssLower : ssUndef;
else if(is_point_in_rect(pt, m_rect_higher_thumb))
m_selection = ssHigher;
else
@ -1419,7 +1423,7 @@ void Control::OnWheel(wxMouseEvent& event)
ssLower : ssHigher;
}
if (m_selection == ssLower && !m_lower_editable)
if (m_selection == ssLower && !is_lower_thumb_editable())
m_selection = ssUndef;
#if ENABLE_GCODE_VIEWER
@ -1472,7 +1476,7 @@ void Control::OnKeyDown(wxKeyEvent &event)
else if (key == WXK_UP || key == WXK_DOWN) {
if (key == WXK_UP)
m_selection = ssHigher;
else if (key == WXK_DOWN && m_lower_editable)
else if (key == WXK_DOWN && is_lower_thumb_editable())
m_selection = ssLower;
Refresh();
}
@ -1487,7 +1491,7 @@ void Control::OnKeyDown(wxKeyEvent &event)
if (key == WXK_LEFT || key == WXK_RIGHT) {
if (key == WXK_LEFT)
m_selection = ssHigher;
else if (key == WXK_RIGHT && m_lower_editable)
else if (key == WXK_RIGHT && is_lower_thumb_editable())
m_selection = ssLower;
Refresh();
}
@ -2006,13 +2010,11 @@ void Control::move_current_thumb_to_pos(wxPoint pos)
const int mouse_val = tick_val >= 0 && m_draw_mode == dmRegular ? tick_val :
get_value_from_position(pos);
if (mouse_val >= 0) {
// if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) {
// if (mouse_val <= m_lower_value) {
if (m_selection == ssLower) {
SetLowerValue(mouse_val);
correct_lower_value();
}
else if (m_selection == ssHigher) {
else { // even m_selection is ssUndef, upper thumb should be selected
SetHigherValue(mouse_val);
correct_higher_value();
}
@ -2099,7 +2101,7 @@ void Control::jump_to_print_z()
void Control::post_ticks_changed_event(Type type /*= Custom*/)
{
m_force_mode_apply = type != ToolChange;
// m_force_mode_apply = type != ToolChange; // It looks like this condition is no needed now. Leave it for the testing
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
}

View File

@ -228,7 +228,6 @@ public:
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
void SetExtruderColors(const std::vector<std::string>& extruder_colors);
void set_lower_editable(bool editable) { m_lower_editable = editable; }
void set_render_as_disabled(bool value) { m_render_as_disabled = value; }
bool is_rendering_as_disabled() const { return m_render_as_disabled; }
@ -340,7 +339,6 @@ private:
int m_lower_value;
int m_higher_value;
bool m_lower_editable{ true };
bool m_render_as_disabled{ false };
ScalableBitmap m_bmp_thumb_higher;

View File

@ -2328,6 +2328,7 @@ void GCodeViewer::render_legend() const
m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role);
// update buffers' render paths
refresh_render_paths(false, false);
wxGetApp().plater()->update_preview_moves_slider();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
wxGetApp().plater()->update_preview_bottom_toolbar();
}
@ -2600,7 +2601,7 @@ void GCodeViewer::render_legend() const
// items
add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions"));
add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Unretractions"));
add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions"));
add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes"));
add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes"));
add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Pause prints"));

View File

@ -2812,8 +2812,7 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, c
void GLCanvas3D::bind_event_handlers()
{
if (m_canvas != nullptr)
{
if (m_canvas != nullptr) {
m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
@ -2835,13 +2834,14 @@ void GLCanvas3D::bind_event_handlers()
m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
m_canvas->Bind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
m_event_handlers_bound = true;
}
}
void GLCanvas3D::unbind_event_handlers()
{
if (m_canvas != nullptr)
{
if (m_canvas != nullptr && m_event_handlers_bound) {
m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
@ -2863,6 +2863,8 @@ void GLCanvas3D::unbind_event_handlers()
m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
m_canvas->Unbind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
m_event_handlers_bound = false;
}
}
@ -2888,8 +2890,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
_refresh_if_shown_on_screen();
if (m_extra_frame_requested || mouse3d_controller_applied)
{
if (m_extra_frame_requested || mouse3d_controller_applied) {
m_dirty = true;
m_extra_frame_requested = false;
evt.RequestMore();
@ -2941,7 +2942,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
break;
#ifdef __linux__
#if defined(__linux__) || defined(__APPLE__)
case WXK_CONTROL_M:
{
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
@ -3512,40 +3513,35 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
}
if (m_main_toolbar.on_mouse(evt, *this))
{
if (m_main_toolbar.on_mouse(evt, *this)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
m_mouse.set_start_position_3D_as_invalid();
return;
}
if (m_undoredo_toolbar.on_mouse(evt, *this))
{
if (m_undoredo_toolbar.on_mouse(evt, *this)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
m_mouse.set_start_position_3D_as_invalid();
return;
}
if (wxGetApp().plater()->get_collapse_toolbar().on_mouse(evt, *this))
{
if (wxGetApp().plater()->get_collapse_toolbar().on_mouse(evt, *this)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
m_mouse.set_start_position_3D_as_invalid();
return;
}
if (wxGetApp().plater()->get_view_toolbar().on_mouse(evt, *this))
{
if (wxGetApp().plater()->get_view_toolbar().on_mouse(evt, *this)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
m_mouse.set_start_position_3D_as_invalid();
return;
}
if (m_gizmos.on_mouse(evt))
{
if (m_gizmos.on_mouse(evt)) {
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
mouse_up_cleanup();
@ -3554,12 +3550,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
return;
}
bool any_gizmo_active = m_gizmos.get_current() != nullptr;
int selected_object_idx = m_selection.get_object_idx();
int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
m_layers_editing.select_object(*m_model, layer_editing_object_idx);
if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos))
{
if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) {
m_mouse.drag.move_requires_threshold = false;
m_mouse.set_move_start_threshold_position_2D_as_invalid();
}
@ -3568,8 +3565,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Grab keyboard focus on any mouse click event.
m_canvas->SetFocus();
if (evt.Entering())
{
if (evt.Entering()) {
//#if defined(__WXMSW__) || defined(__linux__)
// // On Windows and Linux needs focus in order to catch key events
// Set focus in order to remove it from sidebar fields
@ -3594,49 +3590,41 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.set_start_position_2D_as_invalid();
//#endif
}
else if (evt.Leaving())
{
else if (evt.Leaving()) {
_deactivate_undo_redo_toolbar_items();
// to remove hover on objects when the mouse goes out of this canvas
m_mouse.position = Vec2d(-1.0, -1.0);
m_dirty = true;
}
else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown())
{
else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown()) {
if (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item())
return;
// If user pressed left or right button we first check whether this happened
// on a volume or not.
m_layers_editing.state = LayersEditing::Unknown;
if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1)))
{
if (layer_editing_object_idx != -1 && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) {
// A volume is selected and the mouse is inside the layer thickness bar.
// Start editing the layer height.
m_layers_editing.state = LayersEditing::Editing;
_perform_layer_editing_action(&evt);
}
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
{
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) {
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports
&& m_gizmos.get_current_type() != GLGizmosManager::FdmSupports
&& m_gizmos.get_current_type() != GLGizmosManager::Seam)
{
&& m_gizmos.get_current_type() != GLGizmosManager::Seam) {
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
m_dirty = true;
}
}
else
{
else {
// Select volume in this 3D canvas.
// Don't deselect a volume if layer editing is enabled. We want the object to stay selected
// Don't deselect a volume if layer editing is enabled or any gizmo is active. We want the object to stay selected
// during the scene manipulation.
if (m_picking_enabled && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled()))
{
if (evt.LeftDown() && !m_hover_volume_idxs.empty())
{
if (m_picking_enabled && (!any_gizmo_active || !evt.CmdDown()) && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled())) {
if (evt.LeftDown() && !m_hover_volume_idxs.empty()) {
int volume_idx = get_first_hover_volume_idx();
bool already_selected = m_selection.contains_volume(volume_idx);
bool ctrl_down = evt.CmdDown();
@ -3645,8 +3633,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (already_selected && ctrl_down)
m_selection.remove(volume_idx);
else
{
else {
m_selection.add(volume_idx, !ctrl_down, true);
m_mouse.drag.move_requires_threshold = !already_selected;
if (already_selected)
@ -3656,8 +3643,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
// propagate event through callback
if (curr_idxs != m_selection.get_volume_idxs())
{
if (curr_idxs != m_selection.get_volume_idxs()) {
if (m_selection.is_empty())
m_gizmos.reset_all_states();
else
@ -3670,16 +3656,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
if (!m_hover_volume_idxs.empty())
{
if (evt.LeftDown() && m_moving_enabled && (m_mouse.drag.move_volume_idx == -1))
{
if (!m_hover_volume_idxs.empty()) {
if (evt.LeftDown() && m_moving_enabled && m_mouse.drag.move_volume_idx == -1) {
// Only accept the initial position, if it is inside the volume bounding box.
int volume_idx = get_first_hover_volume_idx();
BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
volume_bbox.offset(1.0);
if (volume_bbox.contains(m_mouse.scene_position))
{
if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position)) {
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
// The dragging operation is initiated.
m_mouse.drag.move_volume_idx = volume_idx;
@ -3691,18 +3674,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
else if (evt.Dragging() && evt.LeftIsDown() && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
{
if (!m_mouse.drag.move_requires_threshold)
{
else if (evt.Dragging() && evt.LeftIsDown() && m_layers_editing.state == LayersEditing::Unknown && m_mouse.drag.move_volume_idx != -1) {
if (!m_mouse.drag.move_requires_threshold) {
m_mouse.dragging = true;
Vec3d cur_pos = m_mouse.drag.start_position_3D;
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
if (m_selection.contains_volume(get_first_hover_volume_idx()))
{
if (m_selection.contains_volume(get_first_hover_volume_idx())) {
const Camera& camera = wxGetApp().plater()->get_camera();
if (std::abs(camera.get_dir_forward()(2)) < EPSILON)
{
if (std::abs(camera.get_dir_forward()(2)) < EPSILON) {
// side view -> move selected volumes orthogonally to camera view direction
Linef3 ray = mouse_ray(pos);
Vec3d dir = ray.unit_vector();
@ -3724,8 +3703,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// apply offset
cur_pos = m_mouse.drag.start_position_3D + projection_x * camera_right + projection_z * camera_up;
}
else
{
else {
// Generic view
// Get new position at the same Z of the initial click point.
float z0 = 0.0f;
@ -3739,35 +3717,28 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
}
}
else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging())
{
else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) {
m_rectangle_selection.dragging(pos.cast<double>());
m_dirty = true;
}
else if (evt.Dragging())
{
else if (evt.Dragging()) {
m_mouse.dragging = true;
if ((m_layers_editing.state != LayersEditing::Unknown) && (layer_editing_object_idx != -1))
{
if (m_layers_editing.state == LayersEditing::Editing)
{
if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) {
if (m_layers_editing.state == LayersEditing::Editing) {
_perform_layer_editing_action(&evt);
m_mouse.position = pos.cast<double>();
}
}
// do not process the dragging if the left mouse was set down in another canvas
else if (evt.LeftIsDown())
{
else if (evt.LeftIsDown()) {
// if dragging over blank area with left button, rotate
if (m_hover_volume_idxs.empty() && m_mouse.is_start_position_3D_defined())
{
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.);
if (wxGetApp().app_config->get("use_free_camera") == "1")
// Virtual track ball (similar to the 3DConnexion mouse).
wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.));
else
{
else {
// Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation.
// It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(),
// which checks an atomics (flushes CPU caches).
@ -3781,11 +3752,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0);
}
else if (evt.MiddleIsDown() || evt.RightIsDown())
{
else if (evt.MiddleIsDown() || evt.RightIsDown()) {
// If dragging over blank area with right button, pan.
if (m_mouse.is_start_position_2D_defined())
{
if (m_mouse.is_start_position_2D_defined()) {
// get point in model space at Z = 0
float z = 0.0f;
const Vec3d& cur_pos = _mouse_to_3d(pos, &z);
@ -3805,43 +3774,36 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.drag.start_position_2D = pos;
}
}
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
{
if (m_layers_editing.state != LayersEditing::Unknown)
{
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
if (m_layers_editing.state != LayersEditing::Unknown) {
m_layers_editing.state = LayersEditing::Unknown;
_stop_timer();
m_layers_editing.accept_changes(*this);
}
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
else if (m_mouse.drag.move_volume_idx != -1 && m_mouse.dragging) {
do_move(L("Move Object"));
wxGetApp().obj_manipul()->set_dirty();
// Let the plater know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
}
else if (evt.LeftUp() && m_picking_enabled && m_rectangle_selection.is_dragging())
{
else if (evt.LeftUp() && m_picking_enabled && m_rectangle_selection.is_dragging()) {
if (evt.ShiftDown() || evt.AltDown())
_update_selection_from_hover();
m_rectangle_selection.stop_dragging();
}
else if (evt.LeftUp() && !m_mouse.ignore_left_up && !m_mouse.dragging && m_hover_volume_idxs.empty() && !is_layers_editing_enabled())
{
else if (evt.LeftUp() && !m_mouse.ignore_left_up && !m_mouse.dragging && m_hover_volume_idxs.empty() && !is_layers_editing_enabled()) {
// deselect and propagate event through callback
if (!evt.ShiftDown() && m_picking_enabled)
if (!evt.ShiftDown() && (!any_gizmo_active || !evt.CmdDown()) && m_picking_enabled)
deselect_all();
}
else if (evt.RightUp())
{
else if (evt.RightUp()) {
m_mouse.position = pos.cast<double>();
// forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
// the context menu is already shown
render();
if (!m_hover_volume_idxs.empty())
{
if (!m_hover_volume_idxs.empty()) {
// if right clicking on volume, propagate event through callback (shows context menu)
int volume_idx = get_first_hover_volume_idx();
if (!m_volumes.volumes[volume_idx]->is_wipe_tower // no context menu for the wipe tower
@ -3873,8 +3835,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
mouse_up_cleanup();
}
else if (evt.Moving())
{
else if (evt.Moving()) {
m_mouse.position = pos.cast<double>();
// updates gizmos overlay
@ -5222,8 +5183,7 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
void GLCanvas3D::_picking_pass() const
{
if (m_picking_enabled && !m_mouse.dragging && (m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)))
{
if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) {
m_hover_volume_idxs.clear();
// Render the object for picking.
@ -5257,16 +5217,16 @@ void GLCanvas3D::_picking_pass() const
GLubyte color[4] = { 0, 0, 0, 0 };
const Size& cnv_size = get_canvas_size();
bool inside = (0 <= m_mouse.position(0)) && (m_mouse.position(0) < cnv_size.get_width()) && (0 <= m_mouse.position(1)) && (m_mouse.position(1) < cnv_size.get_height());
if (inside)
{
bool inside = 0 <= m_mouse.position(0) && m_mouse.position(0) < cnv_size.get_width() && 0 <= m_mouse.position(1) && m_mouse.position(1) < cnv_size.get_height();
if (inside) {
glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color));
if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3])
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
volume_id = color[0] + (color[1] << 8) + (color[2] << 16);
}
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
{
if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) {
// do not add the volume id if any gizmo is active and CTRL is pressed
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL))
m_hover_volume_idxs.emplace_back(volume_id);
m_gizmos.set_hover_id(-1);
}

View File

@ -474,6 +474,7 @@ private:
// when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame()
bool m_extra_frame_requested;
bool m_event_handlers_bound{ false };
mutable GLVolumeCollection m_volumes;
#if ENABLE_GCODE_VIEWER

View File

@ -1,5 +1,6 @@
#include "libslic3r/Technologies.hpp"
#include "GUI_App.hpp"
#include "GUI_Init.hpp"
#include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp"
#include "I18N.hpp"
@ -24,7 +25,6 @@
#include <wx/wupdlock.h>
#include <wx/filefn.h>
#include <wx/sysopt.h>
#include <wx/msgdlg.h>
#include <wx/richmsgdlg.h>
#include <wx/log.h>
#include <wx/intl.h>
@ -71,7 +71,7 @@
#include "InstanceCheck.hpp"
#include "NotificationManager.hpp"
#include "UnsavedChangesDialog.hpp"
#include "PresetComboBoxes.hpp"
#include "SavePresetDialog.hpp"
#include "BitmapCache.hpp"
@ -355,6 +355,63 @@ private:
}
};
#ifdef __linux__
bool static check_old_linux_datadir(const wxString& app_name) {
// If we are on Linux and the datadir does not exist yet, look into the old
// location where the datadir was before version 2.3. If we find it there,
// tell the user that he might wanna migrate to the new location.
// (https://github.com/prusa3d/PrusaSlicer/issues/2911)
// To be precise, the datadir should exist, it is created when single instance
// lock happens. Instead of checking for existence, check the contents.
namespace fs = boost::filesystem;
std::string new_path = Slic3r::data_dir();
wxString dir;
if (! wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() )
dir = wxFileName::GetHomeDir() + wxS("/.config");
std::string default_path = (dir + "/" + app_name).ToUTF8().data();
if (new_path != default_path) {
// This happens when the user specifies a custom --datadir.
// Do not show anything in that case.
return true;
}
fs::path data_dir = fs::path(new_path);
if (! fs::is_directory(data_dir))
return true; // This should not happen.
int file_count = std::distance(fs::directory_iterator(data_dir), fs::directory_iterator());
if (file_count <= 1) { // just cache dir with an instance lock
std::string old_path = wxStandardPaths::Get().GetUserDataDir().ToUTF8().data();
if (fs::is_directory(old_path)) {
wxString msg = from_u8((boost::format(_u8L("Starting with %1% 2.3, configuration "
"directory on Linux has changed (according to XDG Base Directory Specification) to \n%2%.\n\n"
"This directory did not exist yet (maybe you run the new version for the first time).\nHowever, "
"an old %1% configuration directory was detected in \n%3%.\n\n"
"Consider moving the contents of the old directory to the new location in order to access "
"your profiles, etc.\nNote that if you decide to downgrade %1% in future, it will use the old "
"location again.\n\n"
"What do you want to do now?")) % SLIC3R_APP_NAME % new_path % old_path).str());
wxString caption = from_u8((boost::format(_u8L("%s - BREAKING CHANGE")) % SLIC3R_APP_NAME).str());
wxRichMessageDialog dlg(nullptr, msg, caption, wxYES_NO);
dlg.SetYesNoLabels(_L("Quit, I will move my data now"), _L("Start the application"));
if (dlg.ShowModal() != wxID_NO)
return false;
}
} else {
// If the new directory exists, be silent. The user likely already saw the message.
}
return true;
}
#endif
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
{
static const std::string defaults[FT_SIZE] = {
@ -545,15 +602,16 @@ static void generic_exception_handle()
}
}
void GUI_App::AfterInitLoads::on_loads(GUI_App* gui)
void GUI_App::post_init()
{
if (!gui->initialized())
return;
assert(initialized());
if (! this->initialized())
throw Slic3r::RuntimeError("Calling post_init() while not yet initialized");
#if ENABLE_GCODE_VIEWER
if (m_start_as_gcodeviewer) {
if (!m_input_files.empty())
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str()));
if (this->init_params->start_as_gcodeviewer) {
if (! this->init_params->input_files.empty())
this->plater()->load_gcode(wxString::FromUTF8(this->init_params->input_files[0].c_str()));
}
else {
#endif // ENABLE_GCODE_VIEWER_AS
@ -563,22 +621,22 @@ void GUI_App::AfterInitLoads::on_loads(GUI_App* gui)
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here.
if (!m_print_config.empty())
gui->mainframe->load_config(m_print_config);
this->gui->mainframe->load_config(m_print_config);
#endif
if (!m_load_configs.empty())
if (! this->init_params->load_configs.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(m_load_configs.back());
this->mainframe->load_config_file(this->init_params->load_configs.back());
// If loading a 3MF file, the config is loaded from the last one.
if (!m_input_files.empty())
gui->plater()->load_files(m_input_files, true, true);
if (!m_extra_config.empty())
gui->mainframe->load_config(m_extra_config);
if (! this->init_params->input_files.empty())
this->plater()->load_files(this->init_params->input_files, true, true);
if (! this->init_params->extra_config.empty())
this->mainframe->load_config(this->init_params->extra_config);
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
}
}
IMPLEMENT_APP(GUI_App)
@ -640,8 +698,18 @@ void GUI_App::init_app_config()
// Windows : "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
// Mac : "~/Library/Application Support/Slic3r"
if (data_dir().empty())
set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data());
if (data_dir().empty()) {
#ifndef __linux__
set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data());
#else
// Since version 2.3, config dir on Linux is in ${XDG_CONFIG_HOME}.
// https://github.com/prusa3d/PrusaSlicer/issues/2911
wxString dir;
if (! wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() )
dir = wxFileName::GetHomeDir() + wxS("/.config");
set_data_dir((dir + "/" + GetAppName()).ToUTF8().data());
#endif
}
if (!app_config)
#if ENABLE_GCODE_VIEWER
@ -654,7 +722,6 @@ void GUI_App::init_app_config()
m_app_conf_exists = app_config->exists();
if (m_app_conf_exists) {
std::string error = app_config->load();
#if ENABLE_GCODE_VIEWER
if (!error.empty()) {
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
if (is_editor()) {
@ -670,14 +737,6 @@ void GUI_App::init_app_config()
"\n\n" + app_config->config_path() + "\n\n" + error);
}
}
#else
if (!error.empty())
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
throw Slic3r::RuntimeError(
_u8L("Error parsing PrusaSlicer config file, it is probably corrupted. "
"Try to manually delete the file to recover from the error. Your user profiles will not be affected.") +
"\n\n" + AppConfig::config_path() + "\n\n" + error);
#endif // ENABLE_GCODE_VIEWER
}
}
@ -704,6 +763,13 @@ bool GUI_App::on_init_inner()
wxCHECK_MSG(wxDirExists(resources_dir), false,
wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
#ifdef __linux__
if (! check_old_linux_datadir(GetAppName())) {
std::cerr << "Quitting, user chose to move his data to new location." << std::endl;
return false;
}
#endif
// Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
// wxSystemOptions::SetOption("msw.staticbox.optimized-paint", 0);
// Enable this to disable Windows Vista themes for all wxNotebooks. The themes seem to lead to terrible
@ -863,7 +929,7 @@ bool GUI_App::on_init_inner()
#ifdef WIN32
this->mainframe->register_win32_callbacks();
#endif
this->after_init_loads.on_loads(this);
this->post_init();
}
// Preset updating & Configwizard are done after the above initializations,
@ -1259,9 +1325,9 @@ void fatal_error(wxWindow* parent)
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void GUI_App::update_ui_from_settings()
void GUI_App::update_ui_from_settings(bool apply_free_camera_correction)
{
mainframe->update_ui_from_settings();
mainframe->update_ui_from_settings(apply_free_camera_correction);
}
void GUI_App::persist_window_geometry(wxTopLevelWindow *window, bool default_maximized)

View File

@ -24,6 +24,7 @@ class wxNotebook;
struct wxLanguageInfo;
namespace Slic3r {
class AppConfig;
class PresetBundle;
class PresetUpdater;
@ -32,6 +33,7 @@ class PrintHostJobQueue;
class Model;
namespace GUI{
class RemovableDriveManager;
class OtherInstanceMessageHandler;
class MainFrame;
@ -41,6 +43,7 @@ class ObjectSettings;
class ObjectList;
class ObjectLayers;
class Plater;
struct GUI_InitParams;
@ -142,37 +145,6 @@ private:
std::string m_instance_hash_string;
size_t m_instance_hash_int;
// parameters needed for the after OnInit() loads
struct AfterInitLoads
{
std::vector<std::string> m_load_configs;
DynamicPrintConfig m_extra_config;
std::vector<std::string> m_input_files;
#if ENABLE_GCODE_VIEWER
bool m_start_as_gcodeviewer;
#endif // ENABLE_GCODE_VIEWER
void set_params(
const std::vector<std::string>& load_configs,
const DynamicPrintConfig& extra_config,
#if ENABLE_GCODE_VIEWER
const std::vector<std::string>& input_files,
bool start_as_gcodeviewer
#else
const std::vector<std::string>& input_files
#endif // ENABLE_GCODE_VIEWER
) {
m_load_configs = load_configs;
m_extra_config = extra_config;
m_input_files = input_files;
#if ENABLE_GCODE_VIEWER
m_start_as_gcodeviewer = start_as_gcodeviewer;
#endif // ENABLE_GCODE_VIEWER
}
void on_loads(GUI_App* gui);
};
public:
bool OnInit() override;
bool initialized() const { return m_initialized; }
@ -191,6 +163,10 @@ public:
bool is_recreating_gui() const { return m_is_recreating_gui; }
#endif // ENABLE_GCODE_VIEWER
// To be called after the GUI is fully built up.
// Process command line parameters cached in this->init_params,
// load configs, STLs etc.
void post_init();
static std::string get_gl_info(bool format_as_html, bool extensions);
wxGLContext* init_glcontext(wxGLCanvas& canvas);
bool init_opengl();
@ -241,7 +217,7 @@ public:
static bool catch_error(std::function<void()> cb, const std::string& err);
void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false);
void update_ui_from_settings();
void update_ui_from_settings(bool apply_free_camera_correction = true);
bool switch_language();
bool load_language(wxString language, bool initial);
@ -278,12 +254,14 @@ public:
Model& model();
// Parameters extracted from the command line to be passed to GUI after initialization.
const GUI_InitParams* init_params { nullptr };
AppConfig* app_config{ nullptr };
PresetBundle* preset_bundle{ nullptr };
PresetUpdater* preset_updater{ nullptr };
MainFrame* mainframe{ nullptr };
Plater* plater_{ nullptr };
AfterInitLoads after_init_loads;
std::mutex not_modal_dialog_mutex;
wxDialog* not_modal_dialog = nullptr;

View File

@ -0,0 +1,96 @@
#include "GUI_Init.hpp"
#include "libslic3r/AppConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/InstanceCheck.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/Plater.hpp"
// To show a message box if GUI initialization ends up with an exception thrown.
#include <wx/msgdlg.h>
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/convert.hpp>
namespace Slic3r {
namespace GUI {
int GUI_Run(GUI_InitParams &params)
{
try {
#if ENABLE_GCODE_VIEWER
GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {
// G-code viewer is currently not performing instance check, a new G-code viewer is started every time.
bool gui_single_instance_setting = gui->app_config->get("single_instance") == "1";
if (Slic3r::instance_check(params.argc, params.argv, gui_single_instance_setting)) {
//TODO: do we have delete gui and other stuff?
return -1;
}
}
#else
GUI::GUI_App *gui = new GUI::GUI_App();
#endif // ENABLE_GCODE_VIEWER
// gui->autosave = m_config.opt_string("autosave");
GUI::GUI_App::SetInstance(gui);
gui->init_params = &params;
/*
#if ENABLE_GCODE_VIEWER
gui->CallAfter([gui, this, &load_configs, params.start_as_gcodeviewer] {
#else
gui->CallAfter([gui, this, &load_configs] {
#endif // ENABLE_GCODE_VIEWER
if (!gui->initialized()) {
return;
}
#if ENABLE_GCODE_VIEWER
if (params.start_as_gcodeviewer) {
if (!m_input_files.empty())
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str()));
} else {
#endif // ENABLE_GCODE_VIEWER_AS
#if 0
// Load the cummulative config over the currently active profiles.
//FIXME if multiple configs are loaded, only the last one will have an effect.
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here.
if (!m_print_config.empty())
gui->mainframe->load_config(m_print_config);
#endif
if (!load_configs.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(load_configs.back());
// If loading a 3MF file, the config is loaded from the last one.
if (!m_input_files.empty())
gui->plater()->load_files(m_input_files, true, true);
if (!m_extra_config.empty())
gui->mainframe->load_config(m_extra_config);
#if ENABLE_GCODE_VIEWER
}
#endif // ENABLE_GCODE_VIEWER
});
*/
int result = wxEntry(params.argc, params.argv);
return result;
} catch (const Slic3r::Exception &ex) {
boost::nowide::cerr << ex.what() << std::endl;
wxMessageBox(boost::nowide::widen(ex.what()), _L("PrusaSlicer GUI initialization failed"), wxICON_STOP);
} catch (const std::exception &ex) {
boost::nowide::cerr << "PrusaSlicer GUI initialization failed: " << ex.what() << std::endl;
wxMessageBox(format_wxstr(_L("Fatal error, exception catched: %1%"), ex.what()), _L("PrusaSlicer GUI initialization failed"), wxICON_STOP);
}
// error
return 1;
}
}
}

View File

@ -0,0 +1,27 @@
#ifndef slic3r_GUI_Init_hpp_
#define slic3r_GUI_Init_hpp_
#include <libslic3r/PrintConfig.hpp>
namespace Slic3r {
namespace GUI {
struct GUI_InitParams
{
int argc;
char **argv;
std::vector<std::string> load_configs;
DynamicPrintConfig extra_config;
std::vector<std::string> input_files;
bool start_as_gcodeviewer;
};
int GUI_Run(GUI_InitParams &params);
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GUI_Init_hpp_

View File

@ -71,7 +71,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
m_canvas = new GLCanvas3D(m_canvas_widget);
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
m_canvas->bind_event_handlers();
m_canvas->allow_multisample(OpenGLManager::can_multisample());
// XXX: If have OpenGL
@ -278,7 +277,6 @@ bool Preview::init(wxWindow* parent, Model* model)
m_canvas = new GLCanvas3D(m_canvas_widget);
m_canvas->set_context(wxGetApp().init_glcontext(*m_canvas_widget));
m_canvas->bind_event_handlers();
m_canvas->allow_multisample(OpenGLManager::can_multisample());
m_canvas->set_config(m_config);
m_canvas->set_model(model);
@ -368,7 +366,7 @@ bool Preview::init(wxWindow* parent, Model* model)
#else
m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel")));
m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L(width_screen == tiny ? "Retr." : "Retractions")));
m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L(width_screen == tiny ? "Unre." : "Unretractions")));
m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L(width_screen == tiny ? "Dere." : "Deretractions")));
m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells")));
m_checkbox_legend = new wxCheckBox(this, wxID_ANY, _(L("Legend")));
m_checkbox_legend->SetValue(true);
@ -382,7 +380,6 @@ bool Preview::init(wxWindow* parent, Model* model)
right_sizer->Add(m_layers_slider_sizer, 1, wxEXPAND, 0);
m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxSize(-1, 3 * GetTextExtent("m").y), wxSL_HORIZONTAL);
m_moves_slider->set_lower_editable(get_app_config()->get("seq_top_layer_only") == "0");
m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView);
wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL);
@ -598,9 +595,6 @@ void Preview::refresh_print()
return;
load_print(true);
#if ENABLE_GCODE_VIEWER
m_moves_slider->set_lower_editable(get_app_config()->get("seq_top_layer_only") == "0");
#endif // ENABLE_GCODE_VIEWER
}
void Preview::msw_rescale()
@ -1314,17 +1308,13 @@ void Preview::load_print_as_fff(bool keep_z_range)
}
#if ENABLE_GCODE_VIEWER
if (wxGetApp().is_editor() && !has_layers)
#else
if (! has_layers)
#endif // ENABLE_GCODE_VIEWER
{
#if ENABLE_GCODE_VIEWER
if (wxGetApp().is_editor() && !has_layers) {
hide_layers_slider();
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
#else
if (! has_layers) {
reset_sliders(true);
m_canvas->reset_legend_texture();
#endif // ENABLE_GCODE_VIEWER
@ -1332,8 +1322,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
return;
}
if (m_preferred_color_mode == "tool_or_feature")
{
if (m_preferred_color_mode == "tool_or_feature") {
// It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature.
// Color by feature if it is a single extruder print.
unsigned int number_extruders = (unsigned int)print->extruders().size();
@ -1362,18 +1351,21 @@ void Preview::load_print_as_fff(bool keep_z_range)
std::vector<CustomGCode::Item> color_print_values = {};
// set color print values, if it si selected "ColorPrint" view type
#if ENABLE_GCODE_VIEWER
if (gcode_view_type == GCodeViewer::EViewType::ColorPrint)
if (gcode_view_type == GCodeViewer::EViewType::ColorPrint) {
#else
if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint)
if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) {
#endif // ENABLE_GCODE_VIEWER
{
colors = wxGetApp().plater()->get_colors_for_color_print();
#if !ENABLE_GCODE_VIEWER
colors.push_back("#808080"); // gray color for pause print or custom G-code
#endif // !ENABLE_GCODE_VIEWER
if (!gcode_preview_data_valid)
if (!gcode_preview_data_valid) {
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
#if ENABLE_GCODE_VIEWER
colors.push_back("#808080"); // gray color for pause print or custom G-code
#endif // ENABLE_GCODE_VIEWER
}
}
#if ENABLE_GCODE_VIEWER
else if (gcode_preview_data_valid || gcode_view_type == GCodeViewer::EViewType::Filament)
@ -1408,8 +1400,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
color_print_values.clear();
}
if (IsShown())
{
if (IsShown()) {
#if ENABLE_GCODE_VIEWER
std::vector<double> zs;
#endif // ENABLE_GCODE_VIEWER
@ -1563,7 +1554,7 @@ wxString Preview::get_option_type_string(OptionType type) const
{
case OptionType::Travel: { return _L("Travel"); }
case OptionType::Retractions: { return _L("Retractions"); }
case OptionType::Unretractions: { return _L("Unretractions"); }
case OptionType::Unretractions: { return _L("Deretractions"); }
case OptionType::ToolChanges: { return _L("Tool changes"); }
case OptionType::ColorChanges: { return _L("Color changes"); }
case OptionType::PausePrints: { return _L("Pause prints"); }

View File

@ -30,7 +30,7 @@ void GLGizmoFdmSupports::on_shutdown()
std::string GLGizmoFdmSupports::on_get_name() const
{
return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data();
return (_L("Paint-on supports") + " [L]").ToUTF8().data();
}

View File

@ -28,7 +28,6 @@ bool GLGizmoFlatten::on_init()
void GLGizmoFlatten::on_set_state()
{
}
CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const
@ -38,7 +37,7 @@ CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const
std::string GLGizmoFlatten::on_get_name() const
{
return (_(L("Place on face")) + " [F]").ToUTF8().data();
return (_L("Place on face") + " [F]").ToUTF8().data();
}
bool GLGizmoFlatten::on_is_activable() const
@ -48,8 +47,7 @@ bool GLGizmoFlatten::on_is_activable() const
void GLGizmoFlatten::on_start_dragging()
{
if (m_hover_id != -1)
{
if (m_hover_id != -1) {
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
m_starting_center = m_parent.get_selection().get_bounding_box().center();
@ -65,16 +63,14 @@ void GLGizmoFlatten::on_render() const
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
if (selection.is_single_full_instance())
{
if (selection.is_single_full_instance()) {
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
for (int i = 0; i < (int)m_planes.size(); ++i) {
if (i == m_hover_id)
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.75f));
else
@ -97,16 +93,14 @@ void GLGizmoFlatten::on_render_for_picking() const
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_BLEND));
if (selection.is_single_full_instance())
{
if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) {
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
for (int i = 0; i < (int)m_planes.size(); ++i) {
glsafe(::glColor4fv(picking_color_component(i).data()));
m_planes[i].vbo.render();
}
@ -129,8 +123,7 @@ void GLGizmoFlatten::update_planes()
{
const ModelObject* mo = m_c->selection_info()->model_object();
TriangleMesh ch;
for (const ModelVolume* vol : mo->volumes)
{
for (const ModelVolume* vol : mo->volumes) {
if (vol->type() != ModelVolumeType::MODEL_PART)
continue;
TriangleMesh vol_ch = vol->get_convex_hull();

View File

@ -80,7 +80,7 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } );
glsafe(::glPolygonOffset(-1.0, 1.0));
glsafe(::glPolygonOffset(-5.0, -5.0));
// Take care of the clipping plane. The normal of the clipping plane is
// saved with opposite sign than we need to pass to OpenGL (FIXME)

View File

@ -43,7 +43,7 @@ bool GLGizmoSeam::on_init()
std::string GLGizmoSeam::on_get_name() const
{
return (_(L("Seam Editing")) + " [P]").ToUTF8().data();
return (_L("Seam painting") + " [P]").ToUTF8().data();
}

View File

@ -504,22 +504,22 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
int selected_object_idx = selection.get_object_idx();
bool processed = false;
// when control is down we allow scene pan and rotation even when clicking over some object
bool control_down = evt.CmdDown();
// mouse anywhere
if (evt.Moving())
m_tooltip = update_hover_state(mouse_pos);
else if (evt.LeftUp())
{
if (m_mouse_capture.left)
{
else if (evt.LeftUp()) {
if (m_mouse_capture.left) {
processed = true;
m_mouse_capture.left = false;
}
else if (is_dragging())
{
else if (is_dragging()) {
switch (m_current) {
case Move: m_parent.do_move(L("Gizmo-Move")); break;
case Scale: m_parent.do_scale(L("Gizmo-Scale")); break;
case Rotate: m_parent.do_rotate(L("Gizmo-Rotate")); break;
case Move: { m_parent.do_move(L("Gizmo-Move")); break; }
case Scale: { m_parent.do_scale(L("Gizmo-Scale")); break; }
case Rotate: { m_parent.do_rotate(L("Gizmo-Rotate")); break; }
default: break;
}
@ -538,41 +538,34 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
// else
// return false;
}
else if (evt.MiddleUp())
{
if (m_mouse_capture.middle)
{
else if (evt.MiddleUp()) {
if (m_mouse_capture.middle) {
processed = true;
m_mouse_capture.middle = false;
}
else
return false;
}
else if (evt.RightUp())
{
if (pending_right_up)
{
else if (evt.RightUp()) {
if (pending_right_up) {
pending_right_up = false;
return true;
}
if (m_mouse_capture.right)
{
if (m_mouse_capture.right) {
processed = true;
m_mouse_capture.right = false;
}
// else
// return false;
}
else if (evt.Dragging() && !is_dragging())
{
else if (evt.Dragging() && !is_dragging()) {
if (m_mouse_capture.any())
// if the button down was done on this toolbar, prevent from dragging into the scene
processed = true;
// else
// return false;
}
else if (evt.Dragging() && is_dragging())
{
else if (evt.Dragging() && is_dragging()) {
if (!m_parent.get_wxglcanvas()->HasCapture())
m_parent.get_wxglcanvas()->CaptureMouse();
@ -595,7 +588,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
if (evt.AltDown())
transformation_type.set_independent();
selection.scale(get_scale(), transformation_type);
if (evt.ControlDown())
if (control_down)
selection.translate(get_scale_offset(), true);
wxGetApp().obj_manipul()->set_dirty();
break;
@ -618,15 +611,13 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true;
}
if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined)
{
if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined) {
// mouse is outside the toolbar
m_tooltip = "";
if (evt.LeftDown())
{
if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ||m_current == Seam)
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) {
if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam)
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown()))
// the gizmo got the event and took some action, there is no need to do anything more
processed = true;
else if (!selection.is_empty() && grabber_contains_mouse()) {
@ -644,71 +635,67 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true;
}
}
else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos))
{
else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) {
// we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object
pending_right_up = true;
// event was taken care of by the SlaSupports gizmo
processed = true;
}
else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == FdmSupports || m_current == Seam)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos))
{
else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) {
// event was taken care of by the FdmSupports / Seam gizmo
processed = true;
}
else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1)
else if (evt.Dragging() && m_parent.get_move_volume_id() != -1
&& (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam))
// don't allow dragging objects with the Sla gizmo on
processed = true;
else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam )
&& gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()))
{
else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam)
&& gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) {
// the gizmo got the event and took some action, no need to do anything more here
m_parent.set_as_dirty();
processed = true;
}
else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging())
{
else if (evt.Dragging() && control_down && (evt.LeftIsDown() || evt.RightIsDown())) {
// CTRL has been pressed while already dragging -> stop current action
if (evt.LeftIsDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true);
else if (evt.RightIsDown())
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true);
}
else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) {
// in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither
// object moving or selecting is suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down);
processed = true;
}
else if (evt.LeftUp() && (m_current == Flatten) && (m_gizmos[m_current]->get_hover_id() != -1))
{
else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) {
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
processed = true;
}
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging())
{
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam) && !m_parent.is_mouse_dragging()) {
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down);
processed = true;
}
}
else
{
else {
// mouse inside toolbar
if (evt.LeftDown() || evt.LeftDClick())
{
if (evt.LeftDown() || evt.LeftDClick()) {
m_mouse_capture.left = true;
m_mouse_capture.parent = &m_parent;
processed = true;
if (!selection.is_empty())
{
if (!selection.is_empty()) {
update_on_off_state(mouse_pos);
update_data();
m_parent.set_as_dirty();
}
}
else if (evt.MiddleDown())
{
else if (evt.MiddleDown()) {
m_mouse_capture.middle = true;
m_mouse_capture.parent = &m_parent;
}
else if (evt.RightDown())
{
else if (evt.RightDown()) {
m_mouse_capture.right = true;
m_mouse_capture.parent = &m_parent;
}

View File

@ -11,6 +11,7 @@
#include "boost/nowide/convert.hpp"
#include <boost/log/trivial.hpp>
#include <boost/filesystem/operations.hpp>
#include <iostream>
#include <unordered_map>
#include <fcntl.h>
@ -136,6 +137,13 @@ namespace instance_check_internal
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 1;
if (! boost::filesystem::is_directory(path)) {
BOOST_LOG_TRIVIAL(debug) << "get_lock(): datadir does not exist yet, creating...";
if (! boost::filesystem::create_directories(path))
BOOST_LOG_TRIVIAL(debug) << "get_lock(): unable to create datadir !!!";
}
if ((fdlock = open(dest_dir.c_str(), O_WRONLY | O_CREAT, 0666)) == -1)
return true;

View File

@ -1,6 +1,7 @@
#include <algorithm>
#include "Job.hpp"
#include <libslic3r/Thread.hpp>
#include <boost/log/trivial.hpp>
namespace Slic3r {

View File

@ -3,7 +3,8 @@
#include <atomic>
#include <slic3r/Utils/Thread.hpp>
#include "libslic3r/libslic3r.h"
#include <slic3r/GUI/I18N.hpp>
#include "ProgressIndicator.hpp"

View File

@ -178,7 +178,7 @@ void KBShortcutsDialog::fill_shortcuts()
{ "O", L("Zoom out") },
{ "Tab", L("Switch between Editor/Preview") },
{ "Shift+Tab", L("Collapse/Expand the sidebar") },
#ifdef __linux__
#if defined(__linux__) || defined(__APPLE__)
{ ctrl + "M", L("Show/Hide 3Dconnexion devices settings dialog") },
#endif // __linux__
#if ENABLE_RENDER_PICKING_PASS
@ -190,10 +190,13 @@ void KBShortcutsDialog::fill_shortcuts()
m_full_shortcuts.push_back(std::make_pair(_L("Plater"), plater_shortcuts));
Shortcuts gizmos_shortcuts = {
{ "Shift+", L("Press to snap by 5% in Gizmo scale\nor to snap by 1mm in Gizmo move") },
{ "F", L("Scale selection to fit print volume\nin Gizmo scale") },
{ ctrl, L("Press to activate one direction scaling in Gizmo scale") },
{ alt, L("Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\nselected objects around their own center") },
{ ctrl, L("All gizmos: Press to rotate view with mouse left or to pan view with mouse right") },
{ "Shift+", L("Gizmo move: Press to snap by 1mm") },
{ "Shift+", L("Gizmo scale: Press to snap by 5%") },
{ "F", L("Gizmo scale: Scale selection to fit print volume") },
{ ctrl, L("Gizmo scale: Press to activate one direction scaling") },
{ alt, L("Gizmo scale: Press to scale selected objects around their own center") },
{ alt, L("Gizmo rotate: Press to rotate selected objects around their own center") },
};
m_full_shortcuts.push_back(std::make_pair(_L("Gizmos"), gizmos_shortcuts));
@ -251,7 +254,11 @@ wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_f
sizer->AddStretchSpacer();
// logo
#if ENABLE_GCODE_VIEWER
m_logo_bmp = ScalableBitmap(this, wxGetApp().is_editor() ? "Slic3r_32px.png" : "PrusaSlicer-gcodeviewer_32px.png", 32);
#else
m_logo_bmp = ScalableBitmap(this, "Slic3r_32px.png", 32);
#endif // ENABLE_GCODE_VIEWER
m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp());
sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);

View File

@ -1302,7 +1302,7 @@ void MainFrame::init_menubar()
append_menu_check_item(viewMenu, wxID_ANY, _L("Show &labels") + sep + "E", _L("Show object/instance labels in 3D scene"),
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar"), _L("Collapse sidebar"),
append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar") + "\tShift+Tab", _L("Collapse sidebar"),
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
[]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
}
@ -1989,7 +1989,7 @@ void MainFrame::add_to_recent_projects(const wxString& filename)
//
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void MainFrame::update_ui_from_settings()
void MainFrame::update_ui_from_settings(bool apply_free_camera_correction)
{
// const bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
// m_menu_item_reslice_now->Enable(!bp_on);
@ -1998,7 +1998,7 @@ void MainFrame::update_ui_from_settings()
// m_plater->sidebar().Layout();
if (m_plater)
m_plater->update_ui_from_settings();
m_plater->update_ui_from_settings(apply_free_camera_correction);
for (auto tab: wxGetApp().tabs_list)
tab->update_ui_from_settings();
}

View File

@ -9,7 +9,7 @@
#include <wx/filehistory.h>
#ifdef __APPLE__
#include <wx/taskbar.h>
#endif __APPLE__
#endif // __APPLE__
#include <string>
#include <map>
@ -167,7 +167,7 @@ public:
#endif // ENABLE_GCODE_VIEWER
void update_menubar();
void update_ui_from_settings();
void update_ui_from_settings(bool apply_free_camera_correction = true);
bool is_loaded() const { return m_loaded; }
bool is_last_input_file() const { return !m_qs_last_input_file.IsEmpty(); }
bool is_dlg_layout() const { return m_layout == ESettingsLayout::Dlg; }

View File

@ -1645,7 +1645,7 @@ struct Plater::priv
#endif // ENABLE_GCODE_VIEWER
void reset_all_gizmos();
void update_ui_from_settings();
void update_ui_from_settings(bool apply_free_camera_correction = true);
void update_main_toolbar_tooltips();
std::shared_ptr<ProgressStatusBar> statusbar();
std::string get_config(const std::string &key) const;
@ -1795,7 +1795,8 @@ struct Plater::priv
// Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it.
mutable bool ready_to_slice = { false };
// Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes.
bool writing_to_removable_device = { false };
bool writing_to_removable_device { false };
bool show_ExportToRemovableFinished_notification { false };
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
bool process_completed_with_error { false };
private:
@ -2153,7 +2154,7 @@ void Plater::priv::select_view_3D(const std::string& name)
set_current_panel(preview);
#if ENABLE_GCODE_VIEWER
wxGetApp().update_ui_from_settings();
wxGetApp().update_ui_from_settings(false);
#endif // ENABLE_GCODE_VIEWER
}
@ -2186,10 +2187,10 @@ void Plater::priv::reset_all_gizmos()
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void Plater::priv::update_ui_from_settings()
void Plater::priv::update_ui_from_settings(bool apply_free_camera_correction)
{
camera.set_type(wxGetApp().app_config->get("use_perspective_camera"));
if (wxGetApp().app_config->get("use_free_camera") != "1")
if (apply_free_camera_correction && wxGetApp().app_config->get("use_free_camera") != "1")
camera.recover_from_free_camera();
view3D->get_canvas3d()->update_ui_from_settings();
@ -3330,16 +3331,14 @@ void Plater::priv::set_current_panel(wxPanel* panel)
if (current_panel == panel)
return;
wxPanel* old_panel = current_panel;
current_panel = panel;
// to reduce flickering when changing view, first set as visible the new current panel
for (wxPanel* p : panels)
{
if (p == current_panel)
{
for (wxPanel* p : panels) {
if (p == current_panel) {
#ifdef __WXMAC__
// On Mac we need also to force a render to avoid flickering when changing view
if (force_render)
{
if (force_render) {
if (p == view3D)
dynamic_cast<View3D*>(p)->get_canvas3d()->render();
else if (p == preview)
@ -3350,21 +3349,22 @@ void Plater::priv::set_current_panel(wxPanel* panel)
}
}
// then set to invisible the other
for (wxPanel* p : panels)
{
for (wxPanel* p : panels) {
if (p != current_panel)
p->Hide();
}
panel_sizer->Layout();
if (current_panel == view3D)
{
if (view3D->is_reload_delayed())
{
if (current_panel == view3D) {
if (old_panel == preview)
preview->get_canvas3d()->unbind_event_handlers();
view3D->get_canvas3d()->bind_event_handlers();
if (view3D->is_reload_delayed()) {
// Delayed loading of the 3D scene.
if (this->printer_technology == ptSLA)
{
if (this->printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data.
this->update_restart_background_process(true, false);
@ -3378,8 +3378,12 @@ void Plater::priv::set_current_panel(wxPanel* panel)
if(notification_manager != nullptr)
notification_manager->set_in_preview(false);
}
else if (current_panel == preview)
{
else if (current_panel == preview) {
if (old_panel == view3D)
view3D->get_canvas3d()->unbind_event_handlers();
preview->get_canvas3d()->bind_event_handlers();
// see: Plater::priv::object_list_changed()
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
bool export_in_progress = this->background_process.is_export_scheduled();
@ -3538,6 +3542,8 @@ void Plater::priv::on_export_began(wxCommandEvent& evt)
{
if (show_warning_dialog)
warnings_dialog();
if (this->writing_to_removable_device)
this->show_ExportToRemovableFinished_notification = true;
}
void Plater::priv::on_slicing_began()
{
@ -3652,11 +3658,12 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
else if (wxGetApp().get_mode() == comSimple && wxGetApp().app_config->get("objects_always_expert") != "1") {
show_action_buttons(false);
}
else if (this->writing_to_removable_device)
{
// If writing to removable drive was scheduled, show notification with eject button
if (this->writing_to_removable_device && this->show_ExportToRemovableFinished_notification) {
show_action_buttons(false);
notification_manager->push_notification(NotificationType::ExportToRemovableFinished, *q->get_current_canvas3D());
}
this->show_ExportToRemovableFinished_notification = false;
this->writing_to_removable_device = false;
}
@ -4736,7 +4743,9 @@ void Plater::load_gcode()
void Plater::load_gcode(const wxString& filename)
{
if (filename.empty() || m_last_loaded_gcode == filename)
if (filename.empty() ||
(!filename.Lower().EndsWith(".gcode") && !filename.Lower().EndsWith(".g")) ||
m_last_loaded_gcode == filename)
return;
m_last_loaded_gcode = filename;
@ -4784,7 +4793,7 @@ void Plater::update() { p->update(); }
void Plater::stop_jobs() { p->m_ui_jobs.stop_all(); }
void Plater::update_ui_from_settings() { p->update_ui_from_settings(); }
void Plater::update_ui_from_settings(bool apply_free_camera_correction) { p->update_ui_from_settings(apply_free_camera_correction); }
void Plater::select_view(const std::string& direction) { p->select_view(direction); }
@ -5022,12 +5031,13 @@ void Plater::export_gcode(bool prefer_removable)
if (! output_path.empty()) {
bool path_on_removable_media = removable_drive_manager.set_and_verify_last_save_path(output_path.string());
p->writing_to_removable_device = path_on_removable_media;
p->export_gcode(output_path, path_on_removable_media, PrintHostJob());
// Storing a path to AppConfig either as path to removable media or a path to internal media.
// is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives
// while the dialog was open.
appconfig.update_last_output_dir(output_path.parent_path().string(), path_on_removable_media);
p->writing_to_removable_device = path_on_removable_media;
}
}

View File

@ -172,7 +172,7 @@ public:
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void update_ui_from_settings();
void update_ui_from_settings(bool apply_free_camera_correction = true);
void select_all();
void deselect_all();

View File

@ -30,6 +30,7 @@
#include "../Utils/UndoRedo.hpp"
#include "BitmapCache.hpp"
#include "PhysicalPrinterDialog.hpp"
#include "SavePresetDialog.hpp"
using Slic3r::GUI::format_wxstr;
@ -247,6 +248,51 @@ void PresetComboBox::update(std::string select_preset_name)
Thaw();
}
void PresetComboBox::edit_physical_printer()
{
if (!m_preset_bundle->physical_printers.has_selection())
return;
PhysicalPrinterDialog dlg(this->GetString(this->GetSelection()));
if (dlg.ShowModal() == wxID_OK)
update();
}
void PresetComboBox::add_physical_printer()
{
if (PhysicalPrinterDialog(wxEmptyString).ShowModal() == wxID_OK)
update();
}
bool PresetComboBox::del_physical_printer(const wxString& note_string/* = wxEmptyString*/)
{
const std::string& printer_name = m_preset_bundle->physical_printers.get_selected_full_printer_name();
if (printer_name.empty())
return false;
wxString msg;
if (!note_string.IsEmpty())
msg += note_string + "\n";
msg += format_wxstr(_L("Are you sure you want to delete \"%1%\" printer?"), printer_name);
if (wxMessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES)
return false;
m_preset_bundle->physical_printers.delete_selected_printer();
this->update();
if (dynamic_cast<PlaterPresetComboBox*>(this) != nullptr)
wxGetApp().get_tab(m_type)->update_preset_choice();
else if (dynamic_cast<TabPresetComboBox*>(this) != nullptr)
{
wxGetApp().get_tab(m_type)->update_btns_enabling();
wxGetApp().plater()->sidebar().update_presets(m_type);
}
return true;
}
void PresetComboBox::update()
{
this->update(into_u8(this->GetString(this->GetSelection())));
@ -312,7 +358,7 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con
// Paint a red flag for incompatible presets.
bmps.emplace_back(is_compatible ? bitmap_cache().mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp());
if (m_type == Preset::TYPE_FILAMENT)
if (m_type == Preset::TYPE_FILAMENT && !filament_rgb.empty())
{
unsigned char rgb[3];
// Paint the color bars.
@ -641,28 +687,11 @@ void PlaterPresetComboBox::show_edit_menu()
[this](wxCommandEvent&) { this->switch_to_tab(); }, "cog", menu, []() { return true; }, wxGetApp().plater());
if (this->is_selected_physical_printer()) {
append_menu_item(menu, wxID_ANY, _L("Edit physical printer"), "",
[this](wxCommandEvent&) {
PhysicalPrinterDialog dlg(this->GetString(this->GetSelection()));
if (dlg.ShowModal() == wxID_OK)
update();
}, "cog", menu, []() { return true; }, wxGetApp().plater());
append_menu_item(menu, wxID_ANY, _L("Edit physical printer"), "",
[this](wxCommandEvent&) { this->edit_physical_printer(); }, "cog", menu, []() { return true; }, wxGetApp().plater());
append_menu_item(menu, wxID_ANY, _L("Delete physical printer"), "",
[this](wxCommandEvent&) {
const std::string& printer_name = m_preset_bundle->physical_printers.get_selected_full_printer_name();
if (printer_name.empty())
return;
const wxString msg = from_u8((boost::format(_u8L("Are you sure you want to delete \"%1%\" printer?")) % printer_name).str());
if (wxMessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES)
return;
m_preset_bundle->physical_printers.delete_selected_printer();
wxGetApp().get_tab(m_type)->update_preset_choice();
update();
}, "cross", menu, []() { return true; }, wxGetApp().plater());
append_menu_item(menu, wxID_ANY, _L("Delete physical printer"), "",
[this](wxCommandEvent&) { this->del_physical_printer(); }, "cross", menu, []() { return true; }, wxGetApp().plater());
}
else
append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "",
@ -671,11 +700,7 @@ void PlaterPresetComboBox::show_edit_menu()
}, "edit_uni", menu, []() { return true; }, wxGetApp().plater());
append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "",
[this](wxCommandEvent&) {
PhysicalPrinterDialog dlg(wxEmptyString);
if (dlg.ShowModal() == wxID_OK)
update();
}, "edit_uni", menu, []() { return true; }, wxGetApp().plater());
[this](wxCommandEvent&) { this->add_physical_printer(); }, "edit_uni", menu, []() { return true; }, wxGetApp().plater());
wxGetApp().plater()->PopupMenu(menu);
}
@ -804,11 +829,13 @@ void PlaterPresetComboBox::update()
}
}
if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_SLA_MATERIAL) {
if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) {
wxBitmap* bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni");
assert(bmp);
if (m_type == Preset::TYPE_SLA_MATERIAL)
if (m_type == Preset::TYPE_FILAMENT)
set_label_marker(Append(separator(L("Add/Remove filaments")), *bmp), LABEL_ITEM_WIZARD_FILAMENTS);
else if (m_type == Preset::TYPE_SLA_MATERIAL)
set_label_marker(Append(separator(L("Add/Remove materials")), *bmp), LABEL_ITEM_WIZARD_MATERIALS);
else
set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS);
@ -1022,348 +1049,4 @@ void TabPresetComboBox::update_dirty()
#endif /* __APPLE __ */
}
//-----------------------------------------------
// SavePresetDialog::Item
//-----------------------------------------------
SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent):
m_type(type),
m_parent(parent)
{
Tab* tab = wxGetApp().get_tab(m_type);
assert(tab);
m_presets = tab->get_presets();
const Preset& sel_preset = m_presets->get_selected_preset();
std::string preset_name = sel_preset.is_default ? "Untitled" :
sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() :
sel_preset.name;
// if name contains extension
if (boost::iends_with(preset_name, ".ini")) {
size_t len = preset_name.length() - 4;
preset_name.resize(len);
}
std::vector<std::string> values;
for (const Preset& preset : *m_presets) {
if (preset.is_default || preset.is_system || preset.is_external)
continue;
values.push_back(preset.name);
}
wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(tab->title())).str()));
m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent));
m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1));
for (const std::string& value : values)
m_combo->Append(from_u8(value));
m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); });
#ifdef __WXOSX__
// Under OSX wxEVT_TEXT wasn't invoked after change selection in combobox,
// So process wxEVT_COMBOBOX too
m_combo->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { update(); });
#endif //__WXOSX__
m_valid_label = new wxStaticText(m_parent, wxID_ANY, "");
m_valid_label->SetFont(wxGetApp().bold_font());
wxBoxSizer* combo_sizer = new wxBoxSizer(wxHORIZONTAL);
combo_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W);
combo_sizer->Add(m_combo, 1, wxEXPAND, BORDER_W);
sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W);
sizer->Add(combo_sizer, 0, wxEXPAND | wxBOTTOM, BORDER_W);
sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W);
if (m_type == Preset::TYPE_PRINTER)
m_parent->add_info_for_edit_ph_printer(sizer);
update();
}
void SavePresetDialog::Item::update()
{
m_preset_name = into_u8(m_combo->GetValue());
m_valid_type = Valid;
wxString info_line;
const char* unusable_symbols = "<>[]:/\\|?*\"";
const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
if (m_preset_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
info_line = _L("The supplied name is not valid;") + "\n" +
_L("the following characters are not allowed:") + " " + unusable_symbols;
m_valid_type = NoValid;
break;
}
}
if (m_valid_type == Valid && m_preset_name.find(unusable_suffix) != std::string::npos) {
info_line = _L("The supplied name is not valid;") + "\n" +
_L("the following suffix is not allowed:") + "\n\t" +
from_u8(PresetCollection::get_suffix_modified());
m_valid_type = NoValid;
}
if (m_valid_type == Valid && m_preset_name == "- default -") {
info_line = _L("The supplied name is not available.");
m_valid_type = NoValid;
}
const Preset* existing = m_presets->find_preset(m_preset_name, false);
if (m_valid_type == Valid && existing && (existing->is_default || existing->is_system)) {
info_line = _L("Cannot overwrite a system profile.");
m_valid_type = NoValid;
}
if (m_valid_type == Valid && existing && (existing->is_external)) {
info_line = _L("Cannot overwrite an external profile.");
m_valid_type = NoValid;
}
if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name())
{
info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str());
if (!existing->is_compatible)
info_line += "\n" + _L("And selected preset is imcopatible with selected printer.");
info_line += "\n" + _L("Note: This preset will be replaced after saving");
m_valid_type = Warning;
}
if (m_valid_type == Valid && m_preset_name.empty()) {
info_line = _L("The empty name is not available.");
m_valid_type = NoValid;
}
m_valid_label->SetLabel(info_line);
m_valid_label->Show(!info_line.IsEmpty());
update_valid_bmp();
if (m_type == Preset::TYPE_PRINTER)
m_parent->update_info_for_edit_ph_printer(m_preset_name);
m_parent->layout();
}
void SavePresetDialog::Item::update_valid_bmp()
{
std::string bmp_name = m_valid_type == Warning ? "exclamation" :
m_valid_type == NoValid ? "cross" : "tick_mark" ;
m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent));
}
void SavePresetDialog::Item::accept()
{
if (m_valid_type == Warning)
m_presets->delete_preset(m_preset_name);
}
//-----------------------------------------------
// SavePresetDialog
//-----------------------------------------------
SavePresetDialog::SavePresetDialog(Preset::Type type, std::string suffix)
: DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER)
{
build(std::vector<Preset::Type>{type}, suffix);
}
SavePresetDialog::SavePresetDialog(std::vector<Preset::Type> types, std::string suffix)
: DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER)
{
build(types, suffix);
}
SavePresetDialog::~SavePresetDialog()
{
for (auto item : m_items) {
delete item;
}
}
void SavePresetDialog::build(std::vector<Preset::Type> types, std::string suffix)
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__)
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
if (suffix.empty())
suffix = _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName");
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
m_presets_sizer = new wxBoxSizer(wxVERTICAL);
// Add first item
for (Preset::Type type : types)
AddItem(type, suffix);
// Add dialog's buttons
wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
wxButton* btnOK = static_cast<wxButton*>(this->FindWindowById(wxID_OK, this));
btnOK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); });
btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(enable_ok_btn()); });
topSizer->Add(m_presets_sizer, 0, wxEXPAND | wxALL, BORDER_W);
topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W);
SetSizer(topSizer);
topSizer->SetSizeHints(this);
this->CenterOnScreen();
}
void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix)
{
m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this});
}
std::string SavePresetDialog::get_name()
{
return m_items.front()->preset_name();
}
std::string SavePresetDialog::get_name(Preset::Type type)
{
for (const Item* item : m_items)
if (item->type() == type)
return item->preset_name();
return "";
}
bool SavePresetDialog::enable_ok_btn() const
{
for (const Item* item : m_items)
if (!item->is_valid())
return false;
return true;
}
void SavePresetDialog::add_info_for_edit_ph_printer(wxBoxSizer* sizer)
{
PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers;
m_ph_printer_name = printers.get_selected_printer_name();
m_old_preset_name = printers.get_selected_printer_preset_name();
wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\" \n"
"with related printer preset \"%2%\"")) %
m_ph_printer_name % m_old_preset_name).str());
m_label = new wxStaticText(this, wxID_ANY, msg_text);
m_label->SetFont(wxGetApp().bold_font());
wxString choices[] = {"","",""};
m_action_radio_box = new wxRadioBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS);
m_action_radio_box->SetSelection(0);
m_action_radio_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) {
m_action = (ActionType)e.GetSelection(); });
m_action = ChangePreset;
m_radio_sizer = new wxBoxSizer(wxHORIZONTAL);
m_radio_sizer->Add(m_action_radio_box, 1, wxEXPAND | wxTOP, 2*BORDER_W);
sizer->Add(m_label, 0, wxEXPAND | wxLEFT | wxTOP, 3*BORDER_W);
sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT, 3*BORDER_W);
}
void SavePresetDialog::update_info_for_edit_ph_printer(const std::string& preset_name)
{
bool show = wxGetApp().preset_bundle->physical_printers.has_selection() && m_old_preset_name != preset_name;
m_label->Show(show);
m_radio_sizer->ShowItems(show);
if (!show) {
this->SetMinSize(wxSize(100,50));
return;
}
wxString msg_text = from_u8((boost::format(_u8L("What would you like to do with \"%1%\" preset after saving?")) % preset_name).str());
m_action_radio_box->SetLabel(msg_text);
wxString choices[] = { from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer \"%3%\"")) % m_old_preset_name % preset_name % m_ph_printer_name).str()),
from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer \"%2%\"")) % preset_name % m_ph_printer_name).str()),
from_u8((boost::format(_u8L("Just switch to \"%1%\" preset")) % preset_name).str()) };
int n = 0;
for(const wxString& label: choices)
m_action_radio_box->SetString(n++, label);
}
void SavePresetDialog::layout()
{
this->Layout();
this->Fit();
}
void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect)
{
const int& em = em_unit();
msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL });
for (Item* item : m_items)
item->update_valid_bmp();
//const wxSize& size = wxSize(45 * em, 35 * em);
SetMinSize(/*size*/wxSize(100, 50));
Fit();
Refresh();
}
void SavePresetDialog::update_physical_printers(const std::string& preset_name)
{
if (m_action == UndefAction)
return;
PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers;
if (!physical_printers.has_selection())
return;
std::string printer_preset_name = physical_printers.get_selected_printer_preset_name();
if (m_action == Switch)
// unselect physical printer, if it was selected
physical_printers.unselect_printer();
else
{
PhysicalPrinter printer = physical_printers.get_selected_printer();
if (m_action == ChangePreset)
printer.delete_preset(printer_preset_name);
if (printer.add_preset(preset_name))
physical_printers.save_printer(printer);
physical_printers.select_printer(printer.get_full_name(preset_name));
}
}
void SavePresetDialog::accept()
{
for (Item* item : m_items) {
item->accept();
if (item->type() == Preset::TYPE_PRINTER)
update_physical_printers(item->preset_name());
}
EndModal(wxID_OK);
}
}} // namespace Slic3r::GUI

View File

@ -59,6 +59,10 @@ public:
void update(std::string select_preset);
void edit_physical_printer();
void add_physical_printer();
bool del_physical_printer(const wxString& note_string = wxEmptyString);
virtual void update();
virtual void msw_rescale();
@ -187,91 +191,6 @@ public:
Preset::Type type() const { return m_type; }
};
//------------------------------------------------
// SavePresetDialog
//------------------------------------------------
class SavePresetDialog : public DPIDialog
{
enum ActionType
{
ChangePreset,
AddPreset,
Switch,
UndefAction
};
struct Item
{
enum ValidationType
{
Valid,
NoValid,
Warning
};
Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent);
void update_valid_bmp();
void accept();
bool is_valid() const { return m_valid_type != NoValid; }
Preset::Type type() const { return m_type; }
std::string preset_name() const { return m_preset_name; }
private:
Preset::Type m_type;
ValidationType m_valid_type;
std::string m_preset_name;
SavePresetDialog* m_parent {nullptr};
wxStaticBitmap* m_valid_bmp {nullptr};
wxComboBox* m_combo {nullptr};
wxStaticText* m_valid_label {nullptr};
PresetCollection* m_presets {nullptr};
void update();
};
std::vector<Item*> m_items;
wxBoxSizer* m_presets_sizer {nullptr};
wxStaticText* m_label {nullptr};
wxRadioBox* m_action_radio_box {nullptr};
wxBoxSizer* m_radio_sizer {nullptr};
ActionType m_action {UndefAction};
std::string m_ph_printer_name;
std::string m_old_preset_name;
public:
SavePresetDialog(Preset::Type type, std::string suffix = "");
SavePresetDialog(std::vector<Preset::Type> types, std::string suffix = "");
~SavePresetDialog();
void AddItem(Preset::Type type, const std::string& suffix);
std::string get_name();
std::string get_name(Preset::Type type);
bool enable_ok_btn() const;
void add_info_for_edit_ph_printer(wxBoxSizer *sizer);
void update_info_for_edit_ph_printer(const std::string &preset_name);
void layout();
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
void on_sys_color_changed() override {}
private:
void build(std::vector<Preset::Type> types, std::string suffix = "");
void update_physical_printers(const std::string& preset_name);
void accept();
};
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,368 @@
#include "SavePresetDialog.hpp"
#include <cstddef>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/wupdlock.h>
#include "libslic3r/PresetBundle.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "format.hpp"
#include "Tab.hpp"
using Slic3r::GUI::format_wxstr;
namespace Slic3r {
namespace GUI {
#define BORDER_W 10
//-----------------------------------------------
// SavePresetDialog::Item
//-----------------------------------------------
SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent):
m_type(type),
m_parent(parent)
{
Tab* tab = wxGetApp().get_tab(m_type);
assert(tab);
m_presets = tab->get_presets();
const Preset& sel_preset = m_presets->get_selected_preset();
std::string preset_name = sel_preset.is_default ? "Untitled" :
sel_preset.is_system ? (boost::format(("%1% - %2%")) % sel_preset.name % suffix).str() :
sel_preset.name;
// if name contains extension
if (boost::iends_with(preset_name, ".ini")) {
size_t len = preset_name.length() - 4;
preset_name.resize(len);
}
std::vector<std::string> values;
for (const Preset& preset : *m_presets) {
if (preset.is_default || preset.is_system || preset.is_external)
continue;
values.push_back(preset.name);
}
wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(tab->title())).str()));
m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent));
m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1));
for (const std::string& value : values)
m_combo->Append(from_u8(value));
m_combo->Bind(wxEVT_TEXT, [this](wxCommandEvent&) { update(); });
#ifdef __WXOSX__
// Under OSX wxEVT_TEXT wasn't invoked after change selection in combobox,
// So process wxEVT_COMBOBOX too
m_combo->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { update(); });
#endif //__WXOSX__
m_valid_label = new wxStaticText(m_parent, wxID_ANY, "");
m_valid_label->SetFont(wxGetApp().bold_font());
wxBoxSizer* combo_sizer = new wxBoxSizer(wxHORIZONTAL);
combo_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W);
combo_sizer->Add(m_combo, 1, wxEXPAND, BORDER_W);
sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W);
sizer->Add(combo_sizer, 0, wxEXPAND | wxBOTTOM, BORDER_W);
sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W);
if (m_type == Preset::TYPE_PRINTER)
m_parent->add_info_for_edit_ph_printer(sizer);
update();
}
void SavePresetDialog::Item::update()
{
m_preset_name = into_u8(m_combo->GetValue());
m_valid_type = Valid;
wxString info_line;
const char* unusable_symbols = "<>[]:/\\|?*\"";
const std::string unusable_suffix = PresetCollection::get_suffix_modified();//"(modified)";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
if (m_preset_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
info_line = _L("The supplied name is not valid;") + "\n" +
_L("the following characters are not allowed:") + " " + unusable_symbols;
m_valid_type = NoValid;
break;
}
}
if (m_valid_type == Valid && m_preset_name.find(unusable_suffix) != std::string::npos) {
info_line = _L("The supplied name is not valid;") + "\n" +
_L("the following suffix is not allowed:") + "\n\t" +
from_u8(PresetCollection::get_suffix_modified());
m_valid_type = NoValid;
}
if (m_valid_type == Valid && m_preset_name == "- default -") {
info_line = _L("The supplied name is not available.");
m_valid_type = NoValid;
}
const Preset* existing = m_presets->find_preset(m_preset_name, false);
if (m_valid_type == Valid && existing && (existing->is_default || existing->is_system)) {
info_line = _L("Cannot overwrite a system profile.");
m_valid_type = NoValid;
}
if (m_valid_type == Valid && existing && (existing->is_external)) {
info_line = _L("Cannot overwrite an external profile.");
m_valid_type = NoValid;
}
if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name())
{
info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str());
if (!existing->is_compatible)
info_line += "\n" + _L("And selected preset is imcopatible with selected printer.");
info_line += "\n" + _L("Note: This preset will be replaced after saving");
m_valid_type = Warning;
}
if (m_valid_type == Valid && m_preset_name.empty()) {
info_line = _L("The empty name is not available.");
m_valid_type = NoValid;
}
m_valid_label->SetLabel(info_line);
m_valid_label->Show(!info_line.IsEmpty());
update_valid_bmp();
if (m_type == Preset::TYPE_PRINTER)
m_parent->update_info_for_edit_ph_printer(m_preset_name);
m_parent->layout();
}
void SavePresetDialog::Item::update_valid_bmp()
{
std::string bmp_name = m_valid_type == Warning ? "exclamation" :
m_valid_type == NoValid ? "cross" : "tick_mark" ;
m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent));
}
void SavePresetDialog::Item::accept()
{
if (m_valid_type == Warning)
m_presets->delete_preset(m_preset_name);
}
//-----------------------------------------------
// SavePresetDialog
//-----------------------------------------------
SavePresetDialog::SavePresetDialog(Preset::Type type, std::string suffix)
: DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER)
{
build(std::vector<Preset::Type>{type}, suffix);
}
SavePresetDialog::SavePresetDialog(std::vector<Preset::Type> types, std::string suffix)
: DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER)
{
build(types, suffix);
}
SavePresetDialog::~SavePresetDialog()
{
for (auto item : m_items) {
delete item;
}
}
void SavePresetDialog::build(std::vector<Preset::Type> types, std::string suffix)
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__)
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
if (suffix.empty())
suffix = _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName");
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
m_presets_sizer = new wxBoxSizer(wxVERTICAL);
// Add first item
for (Preset::Type type : types)
AddItem(type, suffix);
// Add dialog's buttons
wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
wxButton* btnOK = static_cast<wxButton*>(this->FindWindowById(wxID_OK, this));
btnOK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); });
btnOK->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(enable_ok_btn()); });
topSizer->Add(m_presets_sizer, 0, wxEXPAND | wxALL, BORDER_W);
topSizer->Add(btns, 0, wxEXPAND | wxALL, BORDER_W);
SetSizer(topSizer);
topSizer->SetSizeHints(this);
this->CenterOnScreen();
}
void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix)
{
m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this});
}
std::string SavePresetDialog::get_name()
{
return m_items.front()->preset_name();
}
std::string SavePresetDialog::get_name(Preset::Type type)
{
for (const Item* item : m_items)
if (item->type() == type)
return item->preset_name();
return "";
}
bool SavePresetDialog::enable_ok_btn() const
{
for (const Item* item : m_items)
if (!item->is_valid())
return false;
return true;
}
void SavePresetDialog::add_info_for_edit_ph_printer(wxBoxSizer* sizer)
{
PhysicalPrinterCollection& printers = wxGetApp().preset_bundle->physical_printers;
m_ph_printer_name = printers.get_selected_printer_name();
m_old_preset_name = printers.get_selected_printer_preset_name();
wxString msg_text = from_u8((boost::format(_u8L("You have selected physical printer \"%1%\" \n"
"with related printer preset \"%2%\"")) %
m_ph_printer_name % m_old_preset_name).str());
m_label = new wxStaticText(this, wxID_ANY, msg_text);
m_label->SetFont(wxGetApp().bold_font());
wxString choices[] = {"","",""};
m_action_radio_box = new wxRadioBox(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize,
WXSIZEOF(choices), choices, 3, wxRA_SPECIFY_ROWS);
m_action_radio_box->SetSelection(0);
m_action_radio_box->Bind(wxEVT_RADIOBOX, [this](wxCommandEvent& e) {
m_action = (ActionType)e.GetSelection(); });
m_action = ChangePreset;
m_radio_sizer = new wxBoxSizer(wxHORIZONTAL);
m_radio_sizer->Add(m_action_radio_box, 1, wxEXPAND | wxTOP, 2*BORDER_W);
sizer->Add(m_label, 0, wxEXPAND | wxLEFT | wxTOP, 3*BORDER_W);
sizer->Add(m_radio_sizer, 1, wxEXPAND | wxLEFT, 3*BORDER_W);
}
void SavePresetDialog::update_info_for_edit_ph_printer(const std::string& preset_name)
{
bool show = wxGetApp().preset_bundle->physical_printers.has_selection() && m_old_preset_name != preset_name;
m_label->Show(show);
m_radio_sizer->ShowItems(show);
if (!show) {
this->SetMinSize(wxSize(100,50));
return;
}
wxString msg_text = from_u8((boost::format(_u8L("What would you like to do with \"%1%\" preset after saving?")) % preset_name).str());
m_action_radio_box->SetLabel(msg_text);
wxString choices[] = { from_u8((boost::format(_u8L("Change \"%1%\" to \"%2%\" for this physical printer \"%3%\"")) % m_old_preset_name % preset_name % m_ph_printer_name).str()),
from_u8((boost::format(_u8L("Add \"%1%\" as a next preset for the the physical printer \"%2%\"")) % preset_name % m_ph_printer_name).str()),
from_u8((boost::format(_u8L("Just switch to \"%1%\" preset")) % preset_name).str()) };
int n = 0;
for(const wxString& label: choices)
m_action_radio_box->SetString(n++, label);
}
void SavePresetDialog::layout()
{
this->Layout();
this->Fit();
}
void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect)
{
const int& em = em_unit();
msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL });
for (Item* item : m_items)
item->update_valid_bmp();
//const wxSize& size = wxSize(45 * em, 35 * em);
SetMinSize(/*size*/wxSize(100, 50));
Fit();
Refresh();
}
void SavePresetDialog::update_physical_printers(const std::string& preset_name)
{
if (m_action == UndefAction)
return;
PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers;
if (!physical_printers.has_selection())
return;
std::string printer_preset_name = physical_printers.get_selected_printer_preset_name();
if (m_action == Switch)
// unselect physical printer, if it was selected
physical_printers.unselect_printer();
else
{
PhysicalPrinter printer = physical_printers.get_selected_printer();
if (m_action == ChangePreset)
printer.delete_preset(printer_preset_name);
if (printer.add_preset(preset_name))
physical_printers.save_printer(printer);
physical_printers.select_printer(printer.get_full_name(preset_name));
}
}
void SavePresetDialog::accept()
{
for (Item* item : m_items) {
item->accept();
if (item->type() == Preset::TYPE_PRINTER)
update_physical_printers(item->preset_name());
}
EndModal(wxID_OK);
}
}} // namespace Slic3r::GUI

View File

@ -0,0 +1,103 @@
#ifndef slic3r_SavePresetDialog_hpp_
#define slic3r_SavePresetDialog_hpp_
//#include <wx/gdicmn.h>
#include "libslic3r/Preset.hpp"
#include "wxExtensions.hpp"
#include "GUI_Utils.hpp"
class wxString;
class wxStaticText;
class wxComboBox;
class wxRadioBox;
class wxStaticBitmap;
namespace Slic3r {
namespace GUI {
class SavePresetDialog : public DPIDialog
{
enum ActionType
{
ChangePreset,
AddPreset,
Switch,
UndefAction
};
struct Item
{
enum ValidationType
{
Valid,
NoValid,
Warning
};
Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent);
void update_valid_bmp();
void accept();
bool is_valid() const { return m_valid_type != NoValid; }
Preset::Type type() const { return m_type; }
std::string preset_name() const { return m_preset_name; }
private:
Preset::Type m_type;
ValidationType m_valid_type;
std::string m_preset_name;
SavePresetDialog* m_parent {nullptr};
wxStaticBitmap* m_valid_bmp {nullptr};
wxComboBox* m_combo {nullptr};
wxStaticText* m_valid_label {nullptr};
PresetCollection* m_presets {nullptr};
void update();
};
std::vector<Item*> m_items;
wxBoxSizer* m_presets_sizer {nullptr};
wxStaticText* m_label {nullptr};
wxRadioBox* m_action_radio_box {nullptr};
wxBoxSizer* m_radio_sizer {nullptr};
ActionType m_action {UndefAction};
std::string m_ph_printer_name;
std::string m_old_preset_name;
public:
SavePresetDialog(Preset::Type type, std::string suffix = "");
SavePresetDialog(std::vector<Preset::Type> types, std::string suffix = "");
~SavePresetDialog();
void AddItem(Preset::Type type, const std::string& suffix);
std::string get_name();
std::string get_name(Preset::Type type);
bool enable_ok_btn() const;
void add_info_for_edit_ph_printer(wxBoxSizer *sizer);
void update_info_for_edit_ph_printer(const std::string &preset_name);
void layout();
protected:
void on_dpi_changed(const wxRect& suggested_rect) override;
void on_sys_color_changed() override {}
private:
void build(std::vector<Preset::Type> types, std::string suffix = "");
void update_physical_printers(const std::string& preset_name);
void accept();
};
} // namespace GUI
} // namespace Slic3r
#endif

View File

@ -44,6 +44,7 @@
#include "format.hpp"
#include "PhysicalPrinterDialog.hpp"
#include "UnsavedChangesDialog.hpp"
#include "SavePresetDialog.hpp"
#ifdef WIN32
#include <commctrl.h>
@ -208,9 +209,7 @@ void Tab::create_preset_tab()
// TRN "Save current Settings"
m_btn_save_preset->SetToolTip(from_u8((boost::format(_utf8(L("Save current %s"))) % m_title).str()));
m_btn_delete_preset->SetToolTip(_(L("Delete this preset")));
m_btn_delete_preset->Disable();
if (m_btn_edit_ph_printer)
m_btn_edit_ph_printer->Disable();
m_btn_delete_preset->Hide();
add_scaled_button(panel, &m_question_btn, "question");
m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n"
@ -345,11 +344,12 @@ void Tab::create_preset_tab()
}));
if (m_btn_edit_ph_printer)
m_btn_edit_ph_printer->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) {
PhysicalPrinterDialog dlg(m_presets_choice->GetString(m_presets_choice->GetSelection()));
if (dlg.ShowModal() == wxID_OK)
update_tab_ui();
}));
m_btn_edit_ph_printer->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
if (m_preset_bundle->physical_printers.has_selection())
m_presets_choice->edit_physical_printer();
else
m_presets_choice->add_physical_printer();
});
// Fill cache for mode bitmaps
m_mode_bitmap_cache.reserve(3);
@ -2999,17 +2999,15 @@ void Tab::rebuild_page_tree()
void Tab::update_btns_enabling()
{
// we can't delete last preset from the physical printer
if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection())
m_btn_delete_preset->Enable(m_preset_bundle->physical_printers.get_selected_printer().preset_names.size() > 1);
else {
// we can delete any preset from the physical printer
// and any user preset
const Preset& preset = m_presets->get_edited_preset();
m_btn_delete_preset->Enable(!preset.is_default && !preset.is_system);
}
m_btn_delete_preset->Show(m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection() ||
!preset.is_default && !preset.is_system);
// we can edit physical printer only if it's selected in the list
if (m_btn_edit_ph_printer)
m_btn_edit_ph_printer->Enable(m_preset_bundle->physical_printers.has_selection());
m_btn_edit_ph_printer->SetToolTip( m_preset_bundle->physical_printers.has_selection() ?
_L("Edit physical printer") : _L("Add physical printer"));
}
void Tab::update_preset_choice()
@ -3410,7 +3408,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach)
// Update the selection boxes at the plater.
on_presets_changed();
// If current profile is saved, "delete preset" button have to be enabled
m_btn_delete_preset->Enable(true);
m_btn_delete_preset->Show();
if (m_type == Preset::TYPE_PRINTER)
static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count;
@ -3465,8 +3463,16 @@ void Tab::delete_preset()
PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers;
wxString msg;
if (m_presets_choice->is_selected_physical_printer())
msg = from_u8((boost::format(_u8L("Are you sure you want to delete \"%1%\" preset from the physical printer \"%2%\"?"))
% current_preset.name % physical_printers.get_selected_printer_name()).str());
{
PhysicalPrinter& printer = physical_printers.get_selected_printer();
if (printer.preset_names.size() == 1) {
if (m_presets_choice->del_physical_printer(_L("It's a last preset for this physical printer.")))
Layout();
return;
}
msg = format_wxstr(_L("Are you sure you want to delete \"%1%\" preset from the physical printer \"%2%\"?"), current_preset.name, printer.name);
}
else
{
if (m_type == Preset::TYPE_PRINTER && !physical_printers.empty())
@ -3507,11 +3513,6 @@ void Tab::delete_preset()
if (m_presets_choice->is_selected_physical_printer()) {
PhysicalPrinter& printer = physical_printers.get_selected_printer();
if (printer.preset_names.size() == 1) {
wxMessageDialog dialog(nullptr, _L("It's a last for this physical printer. We can't delete it"), _L("Information"), wxICON_INFORMATION | wxOK);
dialog.ShowModal();
return;
}
// just delete this preset from the current physical printer
printer.delete_preset(m_presets->get_edited_preset().name);
// select first from the possible presets for this printer

View File

@ -14,7 +14,7 @@
#include "Tab.hpp"
#include "ExtraRenderers.hpp"
#include "wxExtensions.hpp"
#include "PresetComboBoxes.hpp"
#include "SavePresetDialog.hpp"
#include "MainFrame.hpp"
//#define FTS_FUZZY_MATCH_IMPLEMENTATION
@ -597,7 +597,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
#endif
wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT);
#else
wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true), model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE);
#endif //__linux__
m_tree->AppendColumn(column);
if (set_expander)
@ -727,8 +727,6 @@ void UnsavedChangesDialog::context_menu(wxDataViewEvent& event)
void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name)
{
if (m_motion_action == action)
return;
if (action == Action::Undef && !m_has_long_strings)
m_info_line->Hide();
else {
@ -752,8 +750,6 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name
m_info_line->Show();
}
m_motion_action = action;
Layout();
Refresh();
}
@ -1013,7 +1009,7 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent
action_msg = format_wxstr(_L("Preset \"%1%\" has the following unsaved changes:"), presets->get_edited_preset().name);
}
else {
action_msg = format_wxstr(Preset::TYPE_PRINTER ?
action_msg = format_wxstr(type == Preset::TYPE_PRINTER ?
_L("Preset \"%1%\" is not compatible with the new printer profile and it has the following unsaved changes:") :
_L("Preset \"%1%\" is not compatible with the new print profile and it has the following unsaved changes:"),
presets->get_edited_preset().name);

View File

@ -210,9 +210,6 @@ class UnsavedChangesDialog : public DPIDialog
// selected action after Dialog closing
Action m_exit_action {Action::Undef};
// Action during mouse motion
Action m_motion_action {Action::Undef};
struct ItemData
{
std::string opt_key;

View File

@ -25,7 +25,7 @@ namespace Slic3r {
namespace GUI {
static const char* URL_CHANGELOG = "http://files.prusa3d.com/?latest=slicer-stable&lng=%1%";
static const char* URL_CHANGELOG = "https://files.prusa3d.com/?latest=slicer-stable&lng=%1%";
static const char* URL_DOWNLOAD = "https://www.prusa3d.com/downloads&lng=%1%";
static const char* URL_DEV = "https://github.com/supermerill/SuperSlicer/releases/tag/version_%1%";

View File

@ -36,12 +36,10 @@ const char* Duet::get_name() const { return "Duet"; }
bool Duet::test(wxString &msg) const
{
bool connected = connect(msg);
if (connected) {
disconnect();
}
auto connectionType = connect(msg);
disconnect(connectionType);
return connected;
return connectionType != ConnectionType::error;
}
wxString Duet::get_test_ok_msg () const
@ -59,33 +57,39 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
wxString connect_msg;
if (!connect(connect_msg)) {
auto connectionType = connect(connect_msg);
if (connectionType == ConnectionType::error) {
error_fn(std::move(connect_msg));
return false;
}
bool res = true;
bool dsf = (connectionType == ConnectionType::dsf);
auto upload_cmd = get_upload_url(upload_data.upload_path.string());
auto upload_cmd = get_upload_url(upload_data.upload_path.string(), connectionType);
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filepath: %2%, print: %3%, command: %4%")
% upload_data.source_path
% upload_data.upload_path
% upload_data.start_print
% upload_cmd;
auto http = Http::post(std::move(upload_cmd));
http.set_post_body(upload_data.source_path)
.on_complete([&](std::string body, unsigned status) {
auto http = (dsf ? Http::put(std::move(upload_cmd)) : Http::post(std::move(upload_cmd)));
if (dsf) {
http.set_put_body(upload_data.source_path);
} else {
http.set_post_body(upload_data.source_path);
}
http.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
int err_code = get_err_code_from_body(body);
int err_code = dsf ? (status == 201 ? 0 : 1) : get_err_code_from_body(body);
if (err_code != 0) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Request completed but error code was received: %1%") % err_code;
error_fn(format_error(body, L("Unknown error occured"), 0));
res = false;
} else if (upload_data.start_print) {
wxString errormsg;
res = start_print(errormsg, upload_data.upload_path.string());
res = start_print(errormsg, upload_data.upload_path.string(), connectionType);
if (! res) {
error_fn(std::move(errormsg));
}
@ -106,20 +110,28 @@ bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn e
})
.perform_sync();
disconnect();
disconnect(connectionType);
return res;
}
bool Duet::connect(wxString &msg) const
Duet::ConnectionType Duet::connect(wxString &msg) const
{
bool res = false;
auto url = get_connect_url();
auto res = ConnectionType::error;
auto url = get_connect_url(false);
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
auto dsfUrl = get_connect_url(true);
auto dsfHttp = Http::get(std::move(dsfUrl));
dsfHttp.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
res = ConnectionType::dsf;
})
.perform_sync();
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: Got: %1%") % body;
@ -127,7 +139,7 @@ bool Duet::connect(wxString &msg) const
int err_code = get_err_code_from_body(body);
switch (err_code) {
case 0:
res = true;
res = ConnectionType::rrf;
break;
case 1:
msg = format_error(body, L("Wrong password"), 0);
@ -146,8 +158,12 @@ bool Duet::connect(wxString &msg) const
return res;
}
void Duet::disconnect() const
void Duet::disconnect(ConnectionType connectionType) const
{
// we don't need to disconnect from DSF or if it failed anyway
if (connectionType != ConnectionType::rrf) {
return;
}
auto url = (boost::format("%1%rr_disconnect")
% get_base_url()).str();
@ -159,20 +175,33 @@ void Duet::disconnect() const
.perform_sync();
}
std::string Duet::get_upload_url(const std::string &filename) const
std::string Duet::get_upload_url(const std::string &filename, ConnectionType connectionType) const
{
return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%")
% get_base_url()
% Http::url_encode(filename)
% timestamp_str()).str();
assert(connectionType != ConnectionType::error);
if (connectionType == ConnectionType::dsf) {
return (boost::format("%1%machine/file/gcodes/%2%")
% get_base_url()
% Http::url_encode(filename)).str();
} else {
return (boost::format("%1%rr_upload?name=0:/gcodes/%2%&%3%")
% get_base_url()
% Http::url_encode(filename)
% timestamp_str()).str();
}
}
std::string Duet::get_connect_url() const
std::string Duet::get_connect_url(const bool dsfUrl) const
{
return (boost::format("%1%rr_connect?password=%2%&%3%")
% get_base_url()
% (password.empty() ? "reprap" : password)
% timestamp_str()).str();
if (dsfUrl) {
return (boost::format("%1%machine/status")
% get_base_url()).str();
} else {
return (boost::format("%1%rr_connect?password=%2%&%3%")
% get_base_url()
% (password.empty() ? "reprap" : password)
% timestamp_str()).str();
}
}
std::string Duet::get_base_url() const
@ -201,15 +230,27 @@ std::string Duet::timestamp_str() const
return std::string(buffer);
}
bool Duet::start_print(wxString &msg, const std::string &filename) const
bool Duet::start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const
{
bool res = false;
assert(connectionType != ConnectionType::error);
auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"")
bool res = false;
bool dsf = (connectionType == ConnectionType::dsf);
auto url = dsf
? (boost::format("%1%machine/code")
% get_base_url()).str()
: (boost::format("%1%rr_gcode?gcode=M32%%20\"0:/gcodes/%2%\"")
% get_base_url()
% Http::url_encode(filename)).str();
auto http = Http::get(std::move(url));
auto http = (dsf ? Http::post(std::move(url)) : Http::get(std::move(url)));
if (dsf) {
http.set_post_body(
(boost::format("M32 \"0:/gcodes/%1%\"")
% filename).str()
);
}
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error starting print: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);

View File

@ -14,7 +14,7 @@ class Http;
class Duet : public PrintHost
{
public:
Duet(DynamicPrintConfig *config);
explicit Duet(DynamicPrintConfig *config);
~Duet() override = default;
const char* get_name() const override;
@ -29,16 +29,17 @@ public:
std::string get_host() const override { return host; }
private:
enum class ConnectionType { rrf, dsf, error };
std::string host;
std::string password;
std::string get_upload_url(const std::string &filename) const;
std::string get_connect_url() const;
std::string get_upload_url(const std::string &filename, ConnectionType connectionType) const;
std::string get_connect_url(const bool dsfUrl) const;
std::string get_base_url() const;
std::string timestamp_str() const;
bool connect(wxString &msg) const;
void disconnect() const;
bool start_print(wxString &msg, const std::string &filename) const;
ConnectionType connect(wxString &msg) const;
void disconnect(ConnectionType connectionType) const;
bool start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const;
int get_err_code_from_body(const std::string &body) const;
};

View File

@ -35,11 +35,11 @@ struct CurlGlobalInit
{
static std::unique_ptr<CurlGlobalInit> instance;
std::string message;
CurlGlobalInit()
{
#ifdef OPENSSL_CERT_OVERRIDE // defined if SLIC3R_STATIC=ON
// Look for a set of distro specific directories. Don't change the
// order: https://bugzilla.redhat.com/show_bug.cgi?id=1053882
static const char * CA_BUNDLES[] = {
@ -48,17 +48,17 @@ struct CurlGlobalInit
"/usr/share/ssl/certs/ca-bundle.crt",
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD
"/etc/ssl/cert.pem",
"/etc/ssl/ca-bundle.pem" // OpenSUSE Tumbleweed
"/etc/ssl/ca-bundle.pem" // OpenSUSE Tumbleweed
};
namespace fs = boost::filesystem;
// Env var name for the OpenSSL CA bundle (SSL_CERT_FILE nomally)
const char *const SSL_CA_FILE = X509_get_default_cert_file_env();
const char * ssl_cafile = ::getenv(SSL_CA_FILE);
if (!ssl_cafile)
ssl_cafile = X509_get_default_cert_file();
int replace = true;
if (!ssl_cafile || !fs::exists(fs::path(ssl_cafile))) {
const char * bundle = nullptr;
@ -86,15 +86,15 @@ struct CurlGlobalInit
}
#endif // OPENSSL_CERT_OVERRIDE
if (CURLcode ec = ::curl_global_init(CURL_GLOBAL_DEFAULT)) {
message += _u8L("CURL init has failed. PrusaSlicer will be unable to establish "
"network connections. See logs for additional details.");
BOOST_LOG_TRIVIAL(error) << ::curl_easy_strerror(ec);
}
}
~CurlGlobalInit() { ::curl_global_cleanup(); }
};
@ -120,6 +120,7 @@ struct Http::priv
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
size_t limit;
bool cancel;
std::unique_ptr<fs::ifstream> putFile;
std::thread io_thread;
Http::CompleteFn completefn;
@ -138,6 +139,8 @@ struct Http::priv
void set_timeout_connect(long timeout);
void form_add_file(const char *name, const fs::path &path, const char* filename);
void set_post_body(const fs::path &path);
void set_post_body(const std::string &body);
void set_put_body(const fs::path &path);
std::string curl_error(CURLcode curlcode);
std::string body_size_error();
@ -154,7 +157,7 @@ Http::priv::priv(const std::string &url)
, cancel(false)
{
Http::tls_global_init();
if (curl == nullptr) {
throw Slic3r::RuntimeError(std::string("Could not construct Curl object"));
}
@ -277,11 +280,28 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha
}
}
//FIXME may throw! Is the caller aware of it?
void Http::priv::set_post_body(const fs::path &path)
{
std::ifstream file(path.string());
std::string file_content { std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
postfields = file_content;
postfields = std::move(file_content);
}
void Http::priv::set_post_body(const std::string &body)
{
postfields = body;
}
void Http::priv::set_put_body(const fs::path &path)
{
boost::system::error_code ec;
boost::uintmax_t filesize = file_size(path, ec);
if (!ec) {
putFile = std::make_unique<fs::ifstream>(path);
::curl_easy_setopt(curl, CURLOPT_READDATA, (void *) (putFile.get()));
::curl_easy_setopt(curl, CURLOPT_INFILESIZE, filesize);
}
}
std::string Http::priv::curl_error(CURLcode curlcode)
@ -335,6 +355,8 @@ void Http::priv::http_perform()
CURLcode res = ::curl_easy_perform(curl);
putFile.reset();
if (res != CURLE_OK) {
if (res == CURLE_ABORTED_BY_CALLBACK) {
if (cancel) {
@ -373,6 +395,7 @@ Http::Http(Http &&other) : p(std::move(other.p)) {}
Http::~Http()
{
assert(! p || ! p->putFile);
if (p && p->io_thread.joinable()) {
p->io_thread.detach();
}
@ -465,6 +488,18 @@ Http& Http::set_post_body(const fs::path &path)
return *this;
}
Http& Http::set_post_body(const std::string &body)
{
if (p) { p->set_post_body(body); }
return *this;
}
Http& Http::set_put_body(const fs::path &path)
{
if (p) { p->set_put_body(path);}
return *this;
}
Http& Http::on_complete(CompleteFn fn)
{
if (p) { p->completefn = std::move(fn); }
@ -519,6 +554,13 @@ Http Http::post(std::string url)
return http;
}
Http Http::put(std::string url)
{
Http http{std::move(url)};
curl_easy_setopt(http.p->curl, CURLOPT_UPLOAD, 1L);
return http;
}
bool Http::ca_file_supported()
{
::CURL *curl = ::curl_easy_init();
@ -531,7 +573,7 @@ std::string Http::tls_global_init()
{
if (!CurlGlobalInit::instance)
CurlGlobalInit::instance = std::make_unique<CurlGlobalInit>();
return CurlGlobalInit::instance->message;
}
@ -542,7 +584,7 @@ std::string Http::tls_system_cert_store()
#ifdef OPENSSL_CERT_OVERRIDE
ret = ::getenv(X509_get_default_cert_file_env());
#endif
return ret;
}

View File

@ -49,6 +49,7 @@ public:
// for a GET and a POST request respectively.
static Http get(std::string url);
static Http post(std::string url);
static Http put(std::string url);
~Http();
Http(const Http &) = delete;
@ -82,6 +83,16 @@ public:
// This can be used for hosts which do not support multipart requests.
Http& set_post_body(const boost::filesystem::path &path);
// Set the POST request body.
// The data is used verbatim, it is not additionally encoded in any way.
// This can be used for hosts which do not support multipart requests.
Http& set_post_body(const std::string &body);
// Set the file contents as a PUT request body.
// The data is used verbatim, it is not additionally encoded in any way.
// This can be used for hosts which do not support multipart requests.
Http& set_put_body(const boost::filesystem::path &path);
// Callback called on HTTP request complete
Http& on_complete(CompleteFn fn);
// Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,
@ -102,7 +113,7 @@ public:
// Tells whether current backend supports seting up a CA file using ca_file()
static bool ca_file_supported();
// Return empty string on success or error message on fail.
static std::string tls_global_init();
static std::string tls_system_cert_store();

View File

@ -309,7 +309,8 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors)
const std::string idx_path = (cache_path / (vendor.id + ".idx")).string();
const std::string idx_path_temp = idx_path + "-update";
//check if idx_url is leading to a safe site
//if (! boost::starts_with(idx_url, "http://files.my_company.com/wp-content/uploads/repository/"))
//if (! boost::starts_with(idx_url, "http://files.my_company.com/wp-content/uploads/repository/")
// && ! boost::starts_with(idx_url, "https://files.my_company.com/wp-content/uploads/repository/"))
//{
// BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor \"" << vendor.name << "\" rejected: " << idx_url;
// continue;

View File

@ -1,28 +0,0 @@
#ifndef GUI_THREAD_HPP
#define GUI_THREAD_HPP
#include <utility>
#include <boost/thread.hpp>
namespace Slic3r {
template<class Fn>
inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)
{
// Duplicating the stack allocation size of Thread Building Block worker
// threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB
// on a 32bit system by default.
attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024));
return boost::thread{attrs, std::forward<Fn>(fn)};
}
template<class Fn> inline boost::thread create_thread(Fn &&fn)
{
boost::thread::attributes attrs;
return create_thread(attrs, std::forward<Fn>(fn));
}
}
#endif // GUI_THREAD_HPP