From 3a0694dce6e8b71772733310c275abe7590620b4 Mon Sep 17 00:00:00 2001 From: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Date: Tue, 29 Sep 2026 19:17:45 -0300 Subject: [PATCH] Remember last print action (#15774) Co-authored-by: Ian Bassi --- src/libslic3r/AppConfig.cpp | 4 + src/slic3r/GUI/MainFrame.cpp | 366 ++++++++++++++------------------- src/slic3r/GUI/MainFrame.hpp | 9 + src/slic3r/GUI/Preferences.cpp | 5 +- 4 files changed, 167 insertions(+), 217 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index cdcd9b4dec..d67b48c981 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -511,6 +511,10 @@ void AppConfig::set_defaults() set_bool("enable_high_low_temp_mixed_printing", false); } + if (get("remember_print_action").empty()) { + set_bool("remember_print_action", false); + } + if (get("ignore_ext_filament_in_filament_map").empty()){ set_bool("ignore_ext_filament_in_filament_map", false); } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index a57dbf0b97..343d95d3ff 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1997,6 +1997,126 @@ bool MainFrame::can_reslice() const return (m_plater != nullptr) && !m_plater->model().objects.empty(); } +namespace { +// Orca: stable key persisted by the "remember last print action" preference. Reordering PrintSelectType +// must not silently remap a saved preference, so the key never derives from the enum value. +const char* print_select_type_key(MainFrame::PrintSelectType type) +{ + switch (type) { + case MainFrame::ePrintAll: return "print_all"; + case MainFrame::ePrintPlate: return "print_plate"; + case MainFrame::eExportSlicedFile: return "export_sliced_file"; + case MainFrame::eExportAllSlicedFile: return "export_all_sliced_file"; + case MainFrame::eExportGcode: return "export_gcode"; + case MainFrame::eSendGcode: return "send_gcode"; + case MainFrame::eSendToPrinter: return "send_to_printer"; + case MainFrame::eSendToPrinterAll: return "send_to_printer_all"; + case MainFrame::ePrintMultiMachine: return "print_multi_machine"; + case MainFrame::eUploadGcode: break; // Orca: no dropdown entry, never selectable + } + return ""; +} + +// Orca: single source for the print button and print dropdown labels +wxString print_select_type_label(MainFrame::PrintSelectType type) +{ + switch (type) { + case MainFrame::ePrintAll: return _L("Print all"); + case MainFrame::ePrintPlate: return _L("Print plate"); + case MainFrame::eExportSlicedFile: return _L("Export plate sliced file"); + case MainFrame::eExportAllSlicedFile: return _L("Export all sliced file"); + case MainFrame::eExportGcode: return _L("Export G-code file"); + case MainFrame::eSendGcode: return _L_CONTEXT("Print", "Verb"); + case MainFrame::eSendToPrinter: return _L("Send"); + case MainFrame::eSendToPrinterAll: return _L("Send all"); + case MainFrame::ePrintMultiMachine: return _L("Send to Multi-device"); + case MainFrame::eUploadGcode: break; // Orca: no dropdown entry, never selectable + } + return _L("Print plate"); +} +} // namespace + +std::vector MainFrame::available_print_actions() const +{ + std::vector actions; + const auto preset_bundle = wxGetApp().preset_bundle; + const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents"); + + if (preset_bundle && !preset_bundle->is_bbl_vendor() && !use_printer_agents) { + // ThirdParty actions + actions.push_back(eSendGcode); + // Orca: when the printer accepts a .gcode.3mf (the "Support 3MF as gcode" option), + // also offer exporting the sliced .gcode.3mf bundle + const auto* use_3mf_opt = preset_bundle->printers.get_edited_preset().config.option("use_3mf"); + if (use_3mf_opt != nullptr && use_3mf_opt->value) + actions.push_back(eExportSlicedFile); + actions.push_back(eExportGcode); + return actions; + } + + // Orca Slicer actions + bool support_send = true; + bool support_print_all = true; + if (preset_bundle && !preset_bundle->use_bbl_network() && !use_printer_agents) { + support_send = false; // All 3rd print hosts do not have the send options + + const auto& cfg = preset_bundle->printers.get_edited_preset().config; + const auto* host_type_opt = cfg.option>("host_type"); + // Only simply print support uploading all plates + support_print_all = host_type_opt != nullptr && host_type_opt->value == PrintHostType::htSimplyPrint; + } + + actions.push_back(ePrintPlate); + if (support_print_all) + actions.push_back(ePrintAll); + if (support_send) { + actions.push_back(eSendToPrinter); + actions.push_back(eSendToPrinterAll); + } + if (enable_multi_machine) + actions.push_back(ePrintMultiMachine); + actions.push_back(eExportSlicedFile); + actions.push_back(eExportAllSlicedFile); + actions.push_back(eExportGcode); + return actions; +} + +void MainFrame::select_print_action(PrintSelectType select_type) +{ + m_print_btn->SetLabel(print_select_type_label(select_type)); + m_print_select = select_type; + remember_print_select(select_type); + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); + fit_tab_labels(); // ORCA on label change +} + +void MainFrame::remember_print_select(PrintSelectType select_type) +{ + if (!wxGetApp().app_config->get_bool("remember_print_action")) + return; + // AppConfig is marked dirty here and flushed by the regular autosave + wxGetApp().app_config->set("last_print_action", print_select_type_key(select_type)); +} + +bool MainFrame::get_remembered_print_select(PrintSelectType& out) const +{ + if (!wxGetApp().app_config->get_bool("remember_print_action")) + return false; + const std::string saved = wxGetApp().app_config->get("last_print_action"); + if (saved.empty()) + return false; + // Orca: only restore an action the current printer actually offers in the dropdown + for (PrintSelectType type : available_print_actions()) { + if (saved == print_select_type_key(type)) { + out = type; + return true; + } + } + return false; +} + wxBoxSizer* MainFrame::create_side_tools() { enable_multi_machine = wxGetApp().is_enable_multi_machine(); @@ -2016,6 +2136,14 @@ wxBoxSizer* MainFrame::create_side_tools() m_print_btn = new SideButton(print_panel, _L("Print plate"), ""); m_print_option_btn = new SideButton(print_panel, "", "sidebutton_dropdown", 0, 14); + // Orca: restore the last used print/export action if the user opted to remember it + PrintSelectType remembered_print_select; + if (get_remembered_print_select(remembered_print_select)) { + m_print_select = remembered_print_select; + m_print_btn->SetLabel(print_select_type_label(remembered_print_select)); + fit_tab_labels(); // ORCA on label change + } + auto slice_sizer = new wxBoxSizer(wxHORIZONTAL); slice_sizer->Add(m_slice_option_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, FromDIP(1)); slice_sizer->Add(m_slice_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(1)); @@ -2178,193 +2306,15 @@ wxBoxSizer* MainFrame::create_side_tools() m_print_option_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { SidePopup* p = new SidePopup(this); - - if (wxGetApp().preset_bundle - && !wxGetApp().preset_bundle->is_bbl_vendor() - && !wxGetApp().app_config->get_bool("use_printer_agents")) { - // ThirdParty Buttons - SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), ""); - export_gcode_btn->SetCornerRadius(0); - export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export G-code file")); - m_print_select = eExportGcode; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change + for (PrintSelectType type : available_print_actions()) { + SideButton* btn = new SideButton(p, print_select_type_label(type), ""); + btn->SetCornerRadius(0); + btn->Bind(wxEVT_BUTTON, [this, p, type](wxCommandEvent&) { + select_print_action(type); p->Dismiss(); }); - - // upload and print - SideButton* send_gcode_btn = new SideButton(p, _L_CONTEXT("Print", "Verb"), ""); - send_gcode_btn->SetCornerRadius(0); - send_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L_CONTEXT("Print", "Verb")); - m_print_select = eSendGcode; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - p->append_button(send_gcode_btn); - - // Orca: when the printer accepts a .gcode.3mf (the "Support 3MF as gcode" option), - // also offer exporting the sliced .gcode.3mf bundle - const auto& printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; - const auto* use_3mf_opt = printer_config.option("use_3mf"); - if (use_3mf_opt != nullptr && use_3mf_opt->value) { - SideButton* export_sliced_file_btn = new SideButton(p, _L("Export plate sliced file"), ""); - export_sliced_file_btn->SetCornerRadius(0); - export_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export plate sliced file")); - m_print_select = eExportSlicedFile; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - p->append_button(export_sliced_file_btn); - } - - p->append_button(export_gcode_btn); + p->append_button(btn); } - else { - //Orca Slicer Buttons - SideButton* print_plate_btn = new SideButton(p, _L("Print plate"), ""); - print_plate_btn->SetCornerRadius(0); - - SideButton* send_to_printer_btn = new SideButton(p, _L("Send"), ""); - send_to_printer_btn->SetCornerRadius(0); - - SideButton* export_sliced_file_btn = new SideButton(p, _L("Export plate sliced file"), ""); - export_sliced_file_btn->SetCornerRadius(0); - - SideButton* export_all_sliced_file_btn = new SideButton(p, _L("Export all sliced file"), ""); - export_all_sliced_file_btn->SetCornerRadius(0); - - print_plate_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Print plate")); - m_print_select = ePrintPlate; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - SideButton* print_all_btn = new SideButton(p, _L("Print all"), ""); - print_all_btn->SetCornerRadius(0); - print_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Print all")); - m_print_select = ePrintAll; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - send_to_printer_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Send")); - m_print_select = eSendToPrinter; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - SideButton* send_to_printer_all_btn = new SideButton(p, _L("Send all"), ""); - send_to_printer_all_btn->SetCornerRadius(0); - send_to_printer_all_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Send all")); - m_print_select = eSendToPrinterAll; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - export_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export plate sliced file")); - m_print_select = eExportSlicedFile; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - export_all_sliced_file_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export all sliced file")); - m_print_select = eExportAllSlicedFile; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - - bool support_send = true; - bool support_print_all = true; - - const auto preset_bundle = wxGetApp().preset_bundle; - if (preset_bundle) { - if (preset_bundle->use_bbl_network() || wxGetApp().app_config->get_bool("use_printer_agents")) { - // BBL network support everything - } else { - support_send = false; // All 3rd print hosts do not have the send options - - auto cfg = preset_bundle->printers.get_edited_preset().config; - const auto host_type = cfg.option>("host_type")->value; - - // Only simply print support uploading all plates - support_print_all = host_type == PrintHostType::htSimplyPrint; - } - } - - p->append_button(print_plate_btn); - if (support_print_all) { - p->append_button(print_all_btn); - } - if (support_send) { - p->append_button(send_to_printer_btn); - p->append_button(send_to_printer_all_btn); - } - if (enable_multi_machine) { - SideButton* print_multi_machine_btn = new SideButton(p, _L("Send to Multi-device"), ""); - print_multi_machine_btn->SetCornerRadius(0); - print_multi_machine_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Send to Multi-device")); - m_print_select = ePrintMultiMachine; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - p->append_button(print_multi_machine_btn); - } - p->append_button(export_sliced_file_btn); - p->append_button(export_all_sliced_file_btn); - SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), ""); - export_gcode_btn->SetCornerRadius(0); - export_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Export G-code file")); - m_print_select = eExportGcode; - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - fit_tab_labels(); // ORCA on label change - p->Dismiss(); - }); - p->append_button(export_gcode_btn); - } - p->Popup(m_print_btn); } ); @@ -4224,38 +4174,22 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const void MainFrame::set_print_button_to_default(PrintSelectType select_type) { - if (select_type == PrintSelectType::ePrintPlate) { - m_print_btn->SetLabel(_L("Print plate")); - m_print_select = ePrintPlate; - if (m_print_enable) - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - } else if (select_type == PrintSelectType::eSendGcode) { - m_print_btn->SetLabel(_L_CONTEXT("Print", "Verb")); - m_print_select = eSendGcode; - if (m_print_enable) - m_print_enable = get_enable_print_status() && can_send_gcode(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - } else if (select_type == PrintSelectType::eExportGcode) { - m_print_btn->SetLabel(_L("Export G-code file")); - m_print_select = eExportGcode; - if (m_print_enable) - m_print_enable = get_enable_print_status() && can_send_gcode(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - } else if (select_type == PrintSelectType::eExportSlicedFile) { - m_print_btn->SetLabel(_L("Export plate sliced file")); - m_print_select = eExportSlicedFile; - if (m_print_enable) - m_print_enable = get_enable_print_status(); - m_print_btn->Enable(m_print_enable); - this->Layout(); - } else { - // unsupport - return; - } + // Orca: keep the user's remembered print/export action instead of resetting it to the computed + // default. get_remembered_print_select() already rejects anything this printer does not offer. + PrintSelectType remembered; + if (get_remembered_print_select(remembered)) + select_type = remembered; + + if (select_type == eUploadGcode) + return; // unsupported: no dropdown entry exists for this action + + m_print_btn->SetLabel(print_select_type_label(select_type)); + m_print_select = select_type; + // get_enable_print_status() already applies can_send_gcode() to the actions that need it + if (m_print_enable) + m_print_enable = get_enable_print_status(); + m_print_btn->Enable(m_print_enable); + this->Layout(); } void MainFrame::add_to_recent_projects(const wxString& filename) diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index dce12c3fc1..0033f41a18 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -13,6 +13,7 @@ #include #include +#include #include "GUI_Utils.hpp" #include "Event.hpp" @@ -430,6 +431,14 @@ public: // Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig void on_config_changed(DynamicPrintConfig* cfg) const ; void set_print_button_to_default(PrintSelectType select_type); + // Orca: the print/export actions the current printer offers, in the order the dropdown lists them. + // The dropdown is built from this, and a remembered action is only restored if it appears here. + std::vector available_print_actions() const; + // Orca: apply an action picked from the print dropdown to the print button + void select_print_action(PrintSelectType select_type); + // Orca: remember the user's preferred print/export action across sessions (see "remember_print_action" preference) + void remember_print_select(PrintSelectType select_type); + bool get_remembered_print_select(PrintSelectType& out) const; bool can_save() const; bool can_save_as() const; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 055449c35d..d9aedec79b 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1830,7 +1830,10 @@ void PreferencesDialog::create_items() auto item_mix_print_high_low_temperature = create_item_checkbox(_L("Remove mixed temperature restriction"), _L("With this option enabled, you can print materials with a large temperature difference together."), "enable_high_low_temp_mixed_printing"); g_sizer->Add(item_mix_print_high_low_temperature); - + + auto item_remember_print_action = create_item_checkbox(_L("Remember last print action"), _L("If enabled, OrcaSlicer will remember the last selected option in the print button's dropdown (e.g. Print, Export plate sliced file, Export G-code file) and use it as the default on next startup."), "remember_print_action"); + g_sizer->Add(item_remember_print_action); + //// CONTROL > Camera g_sizer->Add(create_item_title(_L("Camera")), 1, wxEXPAND);