diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index d645b8a2d..3f3570c6a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -838,7 +838,6 @@ bool GUI_App::on_init_inner() if (! plater_) return; - //m_other_instance_message_handler->report(); if (app_config->dirty() && app_config->get("autosave") == "1") app_config->save(); @@ -861,6 +860,7 @@ bool GUI_App::on_init_inner() static bool once = true; if (once) { once = false; + #if ENABLE_GCODE_VIEWER if (preset_updater != nullptr) { #endif // ENABLE_GCODE_VIEWER @@ -904,6 +904,10 @@ bool GUI_App::on_init_inner() update_mode(); // update view mode after fix of the object_list size +#ifdef __APPLE__ + other_instance_message_handler()->bring_instance_forward(); +#endif //__APPLE__ + m_initialized = true; return true; } @@ -1714,6 +1718,10 @@ void GUI_App::OSXStoreOpenFiles(const wxArrayString &fileNames) // Opening PrusaSlicer by drag & dropping a G-Code onto PrusaSlicer icon in Finder, // just G-codes were passed. Switch to G-code viewer mode. m_app_mode = EAppMode::GCodeViewer; + if(app_config != nullptr) + delete app_config; + app_config = nullptr; + init_app_config(); } wxApp::OSXStoreOpenFiles(fileNames); } @@ -1722,18 +1730,24 @@ void GUI_App::MacOpenFiles(const wxArrayString &fileNames) { std::vector files; std::vector gcode_files; + std::vector non_gcode_files; for (const auto& filename : fileNames) { wxString fn = filename.Upper(); if (fn.EndsWith(".G") || fn.EndsWith(".GCODE")) gcode_files.emplace_back(filename); - else + else { files.emplace_back(into_u8(filename)); + non_gcode_files.emplace_back(filename); + } } if (m_app_mode == EAppMode::GCodeViewer) { // Running in G-code viewer. // Load the first G-code into the G-code viewer. + // Or if no G-codes, send other files to slicer. if (! gcode_files.empty()) this->plater()->load_gcode(gcode_files.front()); + if (!non_gcode_files.empty()) + start_new_slicer(non_gcode_files, true); } else { if (! files.empty()) this->plater()->load_files(files, true, true); diff --git a/src/slic3r/GUI/InstanceCheck.hpp b/src/slic3r/GUI/InstanceCheck.hpp index 781cb139b..c4831df2e 100644 --- a/src/slic3r/GUI/InstanceCheck.hpp +++ b/src/slic3r/GUI/InstanceCheck.hpp @@ -89,6 +89,8 @@ private: void unregister_for_messages(); // Opaque pointer to RemovableDriveManagerMM void* m_impl_osx; +public: + void bring_instance_forward(); #endif //__APPLE__ }; diff --git a/src/slic3r/GUI/InstanceCheckMac.h b/src/slic3r/GUI/InstanceCheckMac.h index ed9d17418..30943c4d5 100644 --- a/src/slic3r/GUI/InstanceCheckMac.h +++ b/src/slic3r/GUI/InstanceCheckMac.h @@ -5,4 +5,5 @@ -(instancetype) init; -(void) add_observer:(NSString *)version; -(void) message_update:(NSNotification *)note; +-(void) bring_forward; @end diff --git a/src/slic3r/GUI/InstanceCheckMac.mm b/src/slic3r/GUI/InstanceCheckMac.mm index 67bf64980..c16bf15c2 100644 --- a/src/slic3r/GUI/InstanceCheckMac.mm +++ b/src/slic3r/GUI/InstanceCheckMac.mm @@ -20,6 +20,13 @@ -(void)message_update:(NSNotification *)msg { //NSLog(@"recieved msg %@", msg); + [self bring_forward]; + //pass message + Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String])); +} + +-(void) bring_forward +{ //demiaturize all windows for(NSWindow* win in [NSApp windows]) { @@ -30,8 +37,6 @@ } //bring window to front [[NSApplication sharedApplication] activateIgnoringOtherApps : YES]; - //pass message - Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String])); } @end @@ -67,6 +72,13 @@ void OtherInstanceMessageHandler::unregister_for_messages() NSLog(@"warning: unregister instance InstanceCheck notifications not required"); } } + +void OtherInstanceMessageHandler::bring_instance_forward() +{ + if (m_impl_osx) { + [m_impl_osx bring_forward]; + } +} }//namespace GUI }//namespace Slicer diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index c16aa3bf6..83074bab4 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -68,15 +68,15 @@ class GCodeViewerTaskBarIcon : public wxTaskBarIcon { public: GCodeViewerTaskBarIcon(wxTaskBarIconType iconType = wxTBI_DEFAULT_TYPE) : wxTaskBarIcon(iconType) {} -#if 0 wxMenu *CreatePopupMenu() override { wxMenu *menu = new wxMenu; - int id; - auto *item = menu->Append(id = wxNewId(), "&Test menu"); - menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) { wxMessageBox("Test menu - GCode Viewer"); }, id); + //int id; + //auto *item = menu->Append(id = wxNewId(), "&Test menu"); + //menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) { wxMessageBox("Test menu - GCode Viewer"); }, id); + append_menu_item(menu, wxID_ANY, _(L("Open PrusaSlicer")), _(L("")), + [this](wxCommandEvent&) { start_new_slicer(nullptr, true); }, "", nullptr); return menu; } -#endif }; #endif // __APPLE__ @@ -1258,7 +1258,8 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _(L("Open new instance")) + "\tCtrl+I", _(L("Open a new PrusaSlicer instance")), - [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr); + [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, [this]() {return m_plater != nullptr && wxGetApp().app_config->get("single_instance") != "1"; }, this); + } // View menu @@ -1392,6 +1393,9 @@ void MainFrame::init_menubar_as_gcodeviewer() append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); + append_menu_item(fileMenu, wxID_ANY, _L("Open &PrusaSlicer") + dots, _L("Open PrusaSlicer"), + [this](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, + [this]() {return true; }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d02f152c7..1828d3727 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1409,7 +1409,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi else if (paths.size() == 1) { plater->load_gcode(from_path(paths.front())); return true; - } + } return false; } #endif // ENABLE_GCODE_VIEWER @@ -1759,6 +1759,8 @@ struct Plater::priv void msw_rescale_object_menu(); + void bring_instance_forward() const; + // returns the path to project file with the given extension (none if extension == wxEmptyString) // extension should contain the leading dot, i.e.: ".3mf" wxString get_project_filename(const wxString& extension = wxEmptyString) const; @@ -2040,23 +2042,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->load_files(evt.data, true, true); }); this->q->Bind(EVT_INSTANCE_GO_TO_FRONT, [this](InstanceGoToFrontEvent &) { - BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward"; - //this code maximize app window on Fedora - { - wxGetApp().mainframe->Iconize(false); - if (wxGetApp().mainframe->IsMaximized()) - wxGetApp().mainframe->Maximize(true); - else - wxGetApp().mainframe->Maximize(false); - } - //this code maximize window on Ubuntu - { - wxGetApp().mainframe->Restore(); - wxGetApp().GetTopWindow()->SetFocus(); // focus on my window - wxGetApp().GetTopWindow()->Raise(); // bring window to front - wxGetApp().GetTopWindow()->Show(true); // show the window - } - + bring_instance_forward(); }); wxGetApp().other_instance_message_handler()->init(this->q); @@ -4545,6 +4531,34 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo BOOST_LOG_TRIVIAL(info) << "Undo / Redo snapshot reloaded. Undo / Redo stack memory: " << Slic3r::format_memsize_MB(this->undo_redo_stack().memsize()) << log_memory_info(); } +void Plater::priv::bring_instance_forward() const +{ +#ifdef __APPLE__ + wxGetApp().other_instance_message_handler()->bring_instance_forward(); + return; +#endif //__APPLE__ + if (main_frame == nullptr) { + BOOST_LOG_TRIVIAL(debug) << "Couldnt bring instance forward - mainframe is null"; + return; + } + BOOST_LOG_TRIVIAL(debug) << "prusaslicer window going forward"; + //this code maximize app window on Fedora + { + main_frame->Iconize(false); + if (main_frame->IsMaximized()) + main_frame->Maximize(true); + else + main_frame->Maximize(false); + } + //this code maximize window on Ubuntu + { + main_frame->Restore(); + wxGetApp().GetTopWindow()->SetFocus(); // focus on my window + wxGetApp().GetTopWindow()->Raise(); // bring window to front + wxGetApp().GetTopWindow()->Show(true); // show the window + } +} + void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const { switch (btn_type) @@ -6016,6 +6030,10 @@ bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos) } return out; } +void Plater::bring_instance_forward() +{ + p->bring_instance_forward(); +} SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() : m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled()) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7abfd3345..9797a5060 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -333,6 +333,8 @@ public: const NotificationManager* get_notification_manager() const; NotificationManager* get_notification_manager(); + void bring_instance_forward(); + // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots { diff --git a/src/slic3r/Utils/Process.cpp b/src/slic3r/Utils/Process.cpp index d2a326221..bc6961f9a 100644 --- a/src/slic3r/Utils/Process.cpp +++ b/src/slic3r/Utils/Process.cpp @@ -33,7 +33,7 @@ enum class NewSlicerInstanceType { // 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) +static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const std::vector paths_to_open, bool single_instance) { #ifdef _WIN32 wxString path; @@ -41,10 +41,14 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance path += "\\"; path += (instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer.exe" : "prusa-gcodeviewer.exe"; std::vector args; - args.reserve(3); + args.reserve(4); args.emplace_back(path.wc_str()); - if (path_to_open != nullptr) - args.emplace_back(path_to_open->wc_str()); + if (!paths_to_open.empty()) { + for (const auto& file : paths_to_open) + args.emplace_back(file); + } + if (instance_type == NewSlicerInstanceType::Slicer && single_instance) + args.emplace_back(L"--single-instance"); args.emplace_back(nullptr); BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << into_u8(path) << "\""; // Don't call with wxEXEC_HIDE_CONSOLE, PrusaSlicer in GUI mode would just show the splash screen. It would not open the main window though, it would @@ -53,72 +57,92 @@ static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << into_u8(path); #else // Own executable path. - boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); - #if defined(__APPLE__) + boost::filesystem::path bin_path = into_path(wxStandardPaths::Get().GetExecutablePath()); +#if defined(__APPLE__) { // Maybe one day we will be able to run PrusaGCodeViewer, but for now the Apple notarization // process refuses Apps with multiple binaries and Vojtech does not know any workaround. // ((instance_type == NewSlicerInstanceType::Slicer) ? "PrusaSlicer" : "PrusaGCodeViewer"); // Just run PrusaSlicer and give it a --gcodeviewer parameter. - bin_path = bin_path.parent_path() / "PrusaSlicer"; + bin_path = bin_path.parent_path() / "PrusaSlicer"; // On Apple the wxExecute fails, thus we use boost::process instead. BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << bin_path.string() << "\""; try { - std::vector args; - if (instance_type == NewSlicerInstanceType::GCodeViewer) - args.emplace_back("--gcodeviewer"); - if (path_to_open) - args.emplace_back(into_u8(*path_to_open)); - boost::process::spawn(bin_path, args); - } catch (const std::exception &ex) { + std::vector args; + if (instance_type == NewSlicerInstanceType::GCodeViewer) + args.emplace_back("--gcodeviewer"); + if (!paths_to_open.empty()) { + for (const auto& file : paths_to_open) + args.emplace_back(into_u8(file)); + } + if (instance_type == NewSlicerInstanceType::Slicer && single_instance) + args.emplace_back("--single-instance"); + boost::process::spawn(bin_path, args); + } + catch (const std::exception& ex) { BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << bin_path.string() << "\": " << ex.what(); - } + } } - #else // Linux or Unix +#else // Linux or Unix { std::vector args; - args.reserve(3); - #ifdef __linux - static const char *gcodeviewer_param = "--gcodeviewer"; - { + 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"); + 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); } } - #endif // __linux +#endif // __linux std::string my_path; if (args.empty()) { // Binary path was not set to the AppImage in the Linux specific block above, call the application directly. my_path = (bin_path.parent_path() / ((instance_type == NewSlicerInstanceType::Slicer) ? "prusa-slicer" : "prusa-gcodeviewer")).string(); args.emplace_back(my_path.c_str()); } - 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); + std::string to_open; + if (!paths_to_open.empty()) { + for (const auto& file : paths_to_open) { + to_open = into_u8(file); + args.emplace_back(to_open.c_str()); + } + } + if (instance_type == NewSlicerInstanceType::Slicer && single_instance) + args.emplace_back("--single-instance"); + args.emplace_back(nullptr); BOOST_LOG_TRIVIAL(info) << "Trying to spawn a new slicer \"" << args[0] << "\""; - if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER) <= 0) - BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << args[0]; + if (wxExecute(const_cast(args.data()), wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER) <= 0) + BOOST_LOG_TRIVIAL(error) << "Failed to spawn a new slicer \"" << args[0]; } - #endif // Linux or Unix +#endif // Linux or Unix #endif // Win32 } - -void start_new_slicer(const wxString *path_to_open) +static void start_new_slicer_or_gcodeviewer(const NewSlicerInstanceType instance_type, const wxString* path_to_open, bool single_instance) { - start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open); + std::vector paths; + if (path_to_open != nullptr) + paths.emplace_back(path_to_open->wc_str()); + start_new_slicer_or_gcodeviewer(instance_type, paths, single_instance); +} + +void start_new_slicer(const wxString *path_to_open, bool single_instance) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, path_to_open, single_instance); +} +void start_new_slicer(const std::vector& files, bool single_instance) +{ + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::Slicer, files, single_instance); } void start_new_gcodeviewer(const wxString *path_to_open) { - start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open); + start_new_slicer_or_gcodeviewer(NewSlicerInstanceType::GCodeViewer, path_to_open, false); } void start_new_gcodeviewer_open_file(wxWindow *parent) diff --git a/src/slic3r/Utils/Process.hpp b/src/slic3r/Utils/Process.hpp index 65f90222d..9a3fc65e9 100644 --- a/src/slic3r/Utils/Process.hpp +++ b/src/slic3r/Utils/Process.hpp @@ -8,7 +8,9 @@ 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); +void start_new_slicer(const wxString *path_to_open = nullptr, bool single_instance = false); +void start_new_slicer(const std::vector& files, bool single_instance = false); + // 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.