diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5681ed66d..1c3007810 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -195,6 +195,8 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/Process.cpp + Utils/Process.hpp Utils/Profile.hpp Utils/UndoRedo.cpp Utils/UndoRedo.hpp diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f6fd939e2..f4d7f03ec 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -9,7 +9,6 @@ #include //#include #include -#include #include #include @@ -31,6 +30,7 @@ #include "I18N.hpp" #include "GLCanvas3D.hpp" #include "Plater.hpp" +#include "../Utils/Process.hpp" #include #include "GUI_App.hpp" @@ -40,12 +40,6 @@ #include #endif // _WIN32 -// For starting another PrusaSlicer instance on OSX. -// Fails to compile on Windows on the build server. -#ifdef __APPLE__ - #include -#endif - namespace Slic3r { namespace GUI { @@ -1054,8 +1048,8 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _L("&Repair STL file") + dots, _L("Automatically repair an STL file"), [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, [this]() { return true; }, this); -#if ENABLE_GCODE_VIEWER fileMenu->AppendSeparator(); +#if ENABLE_GCODE_VIEWER append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview"), _L("Switch to G-code preview mode"), [this](wxCommandEvent&) { if (m_plater->model().objects.empty() || @@ -1064,6 +1058,8 @@ void MainFrame::init_menubar() set_mode(EMode::GCodeViewer); }, "", nullptr); #endif // ENABLE_GCODE_VIEWER + append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview") + dots, _L("Open G-code viewer"), + [this](wxCommandEvent&) { start_new_gcodeviewer_open_file(this); }, "", nullptr); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1180,20 +1176,11 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _L("Print &Host Upload Queue") + "\tCtrl+J", _L("Display the Print Host Upload Queue window"), - [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, [this]() {return true; }, this); windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _(L("Open new instance")) + "\tCtrl+I", _(L("Open a new PrusaSlicer instance")), - [this](wxCommandEvent&) { - wxString path = wxStandardPaths::Get().GetExecutablePath(); -#ifdef __APPLE__ - boost::process::spawn((const char*)path.c_str()); -#else - wxExecute(wxStandardPaths::Get().GetExecutablePath(), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); -#endif - }, "upload_queue", nullptr, - [this]() {return true; }, this); + [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr); } // View menu diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp new file mode 100644 index 000000000..17e3d6fed --- /dev/null +++ b/src/slic3r/Utils/Process.cpp @@ -0,0 +1,105 @@ +#include "Process.hpp" + +#include + +#include "../GUI/GUI.hpp" +// for file_wildcards() +#include "../GUI/GUI_App.hpp" +// localization +#include "../GUI/I18N.hpp" + +// For starting another PrusaSlicer instance on OSX. +// Fails to compile on Windows on the build server. +#ifdef __APPLE__ + #include +#endif + +#include + +namespace Slic3r { +namespace GUI { + +enum class NewSlicerInstanceType { + Slicer, + GCodeViewer +}; + +// Start a new Slicer process instance either in a Slicer mode or in a G-code mode. +// Optionally load a 3MF, STL or a G-code on start. +static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString *path_to_open) +{ +#ifdef _WIN32 + wxString path; + wxFileName::SplitPath(wxStandardPaths::Get().GetExecutablePath(), &path, nullptr, nullptr, wxPATH_NATIVE); + path += "\\"; + path += (instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer.exe" : "prusa-gcodeviewer.exe"; + std::vector args; + args.reserve(3); + args.emplace_back(path.wc_str()); + if (path_to_open != nullptr) + args.emplace_back(path_to_open->wc_str()); + args.emplace_back(nullptr); + wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); +#else + // Own executable path. + boost::filesystem::path own_path = into_path(wxStandardPaths::Get().GetExecutablePath()); + #if defined(__APPLE__) + { + own_path /= (instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"; + // On Apple the wxExecute fails, thus we use boost::process instead. + path_to_open ? boost::process::spawn(path.string(), into_u8(*path_to_open)) : boost::process::spawn(path.string()); + } + #else // Linux or Unix + { + std::vector args; + args.reserve(3); + #ifdef(__linux) + static const char *gcodeviewer_param = "--gcodeviewer"; + { + // If executed by an AppImage, start the AppImage, not the main process. + // see https://docs.appimage.org/packaging-guide/environment-variables.html#id2 + const char *appimage_binary = std::getenv("APPIMAGE"); + if (appimage_binary) { + args.emplace_back(appimage_binary); + if (instance_type == NewSlicerInstanceType::GCodeViewer) + args.emplace_back(gcodeviewer_param); + if () + } + } + #endif // __linux + std::string to_open; + if (path_to_open) { + to_open = into_u8(*path_to_open); + args.emplace_back(to_open.c_str()); + } + args.emplace_back(nullptr); + wxExecute(const_cast(&args.data()), wxEXEC_ASYNC | wxEXEC_HIDE_CONSOLE | wxEXEC_MAKE_GROUP_LEADER); + } + #endif // Linux or Unix +#endif // Win32 +} + +void start_new_slicer(const wxString *path_to_open) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open); +} + +void start_new_gcodeviewer(const wxString *path_to_open) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open); +} + +void start_new_gcodeviewer_open_file(wxWindow *parent) +{ + wxFileDialog dialog(parent ? parent : wxGetApp().GetTopWindow(), + _L("Open G-code file:"), + from_u8(wxGetApp().app_config->get_last_dir()), wxString(), + file_wildcards(FT_GCODE), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_OK) { + wxString path = dialog.GetPath(); + start_new_gcodeviewer(&path); + } +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/Utils/Process.hpp b/src/slic3r/Utils/Process.hpp new file mode 100644 index 000000000..c6acaa643 --- /dev/null +++ b/src/slic3r/Utils/Process.hpp @@ -0,0 +1,19 @@ +#ifndef GUI_PROCESS_HPP +#define GUI_PROCESS_HPP + +class wxWindow; + +namespace Slic3r { +namespace GUI { + +// Start a new slicer instance, optionally with a file to open. +void start_new_slicer(const wxString *path_to_open = nullptr); +// Start a new G-code viewer instance, optionally with a file to open. +void start_new_gcodeviewer(const wxString *path_to_open = nullptr); +// Open a file dialog, ask the user to select a new G-code to open, start a new G-code viewer. +void start_new_gcodeviewer_open_file(wxWindow *parent = nullptr); + +} // namespace GUI +} // namespace Slic3r + +#endif // GUI_PROCESS_HPP diff --git a/src/slic3r/Utils/Thread.hpp b/src/slic3r/Utils/Thread.hpp index e9c76d2ab..194971c9e 100644 --- a/src/slic3r/Utils/Thread.hpp +++ b/src/slic3r/Utils/Thread.hpp @@ -1,5 +1,5 @@ -#ifndef THREAD_HPP -#define THREAD_HPP +#ifndef GUI_THREAD_HPP +#define GUI_THREAD_HPP #include #include @@ -25,4 +25,4 @@ template inline boost::thread create_thread(Fn &&fn) } -#endif // THREAD_HPP +#endif // GUI_THREAD_HPP