diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 3b6210058..dfdc79677 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -229,6 +229,33 @@ std::string AppConfig::get_last_dir() const return std::string(); } +std::vector AppConfig::get_recent_projects() const +{ + std::vector ret; + const auto it = m_storage.find("recent_projects"); + if (it != m_storage.end()) + { + for (const std::map::value_type& item : it->second) + { + ret.push_back(item.second); + } + } + return ret; +} + +void AppConfig::set_recent_projects(const std::vector& recent_projects) +{ + auto it = m_storage.find("recent_projects"); + if (it == m_storage.end()) + it = m_storage.insert(std::map>::value_type("recent_projects", std::map())).first; + + it->second.clear(); + for (unsigned int i = 0; i < (unsigned int)recent_projects.size(); ++i) + { + it->second[std::to_string(i + 1)] = recent_projects[i]; + } +} + void AppConfig::update_config_dir(const std::string &dir) { this->set("recent", "config_directory", dir); diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 5af635a12..230a92294 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -122,6 +122,9 @@ public: // Does the config file exist? static bool exists(); + std::vector get_recent_projects() const; + void set_recent_projects(const std::vector& recent_projects); + private: // Map of section, name -> value std::map> m_storage; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d800f6f38..99389da41 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -35,6 +35,7 @@ namespace GUI { MainFrame::MainFrame() : DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) + , m_recent_projects(9) { // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened. wxGetApp().update_fonts(this); @@ -383,6 +384,40 @@ void MainFrame::init_menubar() append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")), [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr, [this](){return m_plater != nullptr; }, this); + + wxMenu* recent_projects_menu = new wxMenu(); + wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _(L("Recent projects")), ""); + m_recent_projects.UseMenu(recent_projects_menu); + Bind(wxEVT_MENU, [this](wxCommandEvent& evt) { + size_t file_id = evt.GetId() - wxID_FILE1; + wxString filename = m_recent_projects.GetHistoryFile(file_id); + if (wxFileExists(filename)) + m_plater->load_project(filename); + else + { + wxMessageDialog msg(this, _(L("The selected project is no more available")), _(L("Error"))); + msg.ShowModal(); + + m_recent_projects.RemoveFileFromHistory(file_id); + std::vector recent_projects; + size_t count = m_recent_projects.GetCount(); + for (size_t i = 0; i < count; ++i) + { + recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); + } + wxGetApp().app_config->set_recent_projects(recent_projects); + wxGetApp().app_config->save(); + } + }, wxID_FILE1, wxID_FILE9); + + std::vector recent_projects = wxGetApp().app_config->get_recent_projects(); + for (const std::string& project : recent_projects) + { + m_recent_projects.AddFileToHistory(from_u8(project)); + } + + Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId()); + append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr, [this](){return m_plater != nullptr && can_save(); }, this); @@ -1046,6 +1081,23 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const m_plater->on_config_change(*config); // propagate config change events to the plater } +void MainFrame::add_to_recent_projects(const wxString& filename) +{ + if (wxFileExists(filename)) + { + m_recent_projects.AddFileToHistory(filename); + std::vector recent_projects; + size_t count = m_recent_projects.GetCount(); + for (size_t i = 0; i < count; ++i) + { + recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); + } + wxGetApp().app_config->set_recent_projects(recent_projects); + wxGetApp().app_config->save(); + } +} + +// // 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() diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 805b663bb..a41f33824 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,8 @@ class MainFrame : public DPIFrame // vector of a MenuBar items changeable in respect to printer technology std::vector m_changeable_menu_items; + wxFileHistory m_recent_projects; + protected: virtual void on_dpi_changed(const wxRect &suggested_rect); @@ -121,6 +124,8 @@ public: // Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig void on_config_changed(DynamicPrintConfig* cfg) const ; + void add_to_recent_projects(const wxString& filename); + PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } Plater* m_plater { nullptr }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 402acd59c..b99b3a6af 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3277,6 +3277,9 @@ void Plater::priv::set_project_filename(const wxString& filename) m_project_filename = from_path(full_path); wxGetApp().mainframe->update_title(); + + if (!filename.empty()) + wxGetApp().mainframe->add_to_recent_projects(filename); } bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/) @@ -3590,15 +3593,19 @@ void Plater::load_project() { wxString input_file; wxGetApp().load_project(this, input_file); + load_project(input_file); +} - if (input_file.empty()) +void Plater::load_project(const wxString& filename) +{ + if (filename.empty()) return; p->reset(); - p->set_project_filename(input_file); + p->set_project_filename(filename); std::vector input_paths; - input_paths.push_back(into_path(input_file)); + input_paths.push_back(into_path(filename)); load_files(input_paths); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 34fd7984e..9ba63461e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -138,6 +138,7 @@ public: void new_project(); void load_project(); + void load_project(const wxString& filename); void add_model(); void extract_config_from_project();