diff --git a/resources/icons/export_to_sd.svg b/resources/icons/export_to_sd.svg
new file mode 100644
index 000000000..c836b00a1
--- /dev/null
+++ b/resources/icons/export_to_sd.svg
@@ -0,0 +1,145 @@
+
+
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index 12302a5dc..a410f3ad8 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -78,6 +78,9 @@ void AppConfig::set_defaults()
if (get("remember_output_path").empty())
set("remember_output_path", "1");
+ if (get("remember_output_path_removable").empty())
+ set("remember_output_path_removable", "1");
+
if (get("use_custom_toolbar_size").empty())
set("use_custom_toolbar_size", "0");
@@ -388,7 +391,7 @@ void AppConfig::update_skein_dir(const std::string &dir)
{
this->set("recent", "skein_directory", dir);
}
-
+/*
std::string AppConfig::get_last_output_dir(const std::string &alt) const
{
@@ -406,6 +409,26 @@ void AppConfig::update_last_output_dir(const std::string &dir)
{
this->set("", "last_output_path", dir);
}
+*/
+std::string AppConfig::get_last_output_dir(const std::string& alt, const bool removable) const
+{
+ std::string s1 = (removable ? "last_output_path_removable" : "last_output_path");
+ std::string s2 = (removable ? "remember_output_path_removable" : "remember_output_path");
+ const auto it = m_storage.find("");
+ if (it != m_storage.end()) {
+ const auto it2 = it->second.find(s1);
+ const auto it3 = it->second.find(s2);
+ if (it2 != it->second.end() && it3 != it->second.end() && !it2->second.empty() && it3->second == "1")
+ return it2->second;
+ }
+ return alt;
+}
+
+void AppConfig::update_last_output_dir(const std::string& dir, const bool removable)
+{
+ this->set("", (removable ? "last_output_path_removable" : "last_output_path"), dir);
+}
+
void AppConfig::reset_selections()
{
diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp
index b432367b6..32f1c32c8 100644
--- a/src/slic3r/GUI/AppConfig.hpp
+++ b/src/slic3r/GUI/AppConfig.hpp
@@ -102,8 +102,10 @@ public:
void update_config_dir(const std::string &dir);
void update_skein_dir(const std::string &dir);
- std::string get_last_output_dir(const std::string &alt) const;
- void update_last_output_dir(const std::string &dir);
+ //std::string get_last_output_dir(const std::string &alt) const;
+ //void update_last_output_dir(const std::string &dir);
+ std::string get_last_output_dir(const std::string& alt, const bool removable = false) const;
+ void update_last_output_dir(const std::string &dir, const bool removable = false);
// reset the current print / filament / printer selections, so that
// the PresetBundle::load_selections(const AppConfig &config) call will select
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index b388ad1d2..15820c68a 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -701,6 +701,7 @@ struct Sidebar::priv
wxButton *btn_reslice;
ScalableButton *btn_send_gcode;
ScalableButton *btn_remove_device;
+ ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
priv(Plater *plater) : plater(plater) {}
~priv();
@@ -866,6 +867,7 @@ Sidebar::Sidebar(Plater *parent)
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")));
init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device")));
+ init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card/ USB thumb drive")));
// regular buttons "Slice now" and "Export G-code"
@@ -886,7 +888,9 @@ Sidebar::Sidebar(Plater *parent)
auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL);
complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
complect_btns_sizer->Add(p->btn_send_gcode);
+ complect_btns_sizer->Add(p->btn_export_gcode_removable);
complect_btns_sizer->Add(p->btn_remove_device);
+
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5);
@@ -897,7 +901,7 @@ Sidebar::Sidebar(Plater *parent)
SetSizer(sizer);
// Events
- p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); });
+ p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); });
p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&)
{
const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT);
@@ -909,6 +913,7 @@ Sidebar::Sidebar(Plater *parent)
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
+ p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
}
Sidebar::~Sidebar() {}
@@ -1056,6 +1061,7 @@ void Sidebar::msw_rescale()
p->btn_send_gcode->msw_rescale();
p->btn_remove_device->msw_rescale();
+ p->btn_export_gcode_removable->msw_rescale();
const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
@@ -1289,12 +1295,14 @@ void Sidebar::enable_buttons(bool enable)
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
p->btn_remove_device->Enable(enable);
+ p->btn_export_gcode_removable->Enable(enable);
}
-bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
-bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
-bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
-bool Sidebar::show_disconnect(bool show)const { return p->btn_remove_device->Show(show); }
+bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
+bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
+bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
+bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); }
+bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); }
bool Sidebar::is_multifilament()
{
@@ -2192,6 +2200,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// Initialize the Undo / Redo stack with a first snapshot.
this->take_snapshot(_(L("New Project")));
+
+ //void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
+ RemovableDriveManager::get_instance().set_drive_count_changed_callback(std::bind(&Plater::priv::show_action_buttons, this, std::placeholders::_1));
}
Plater::priv::~priv()
@@ -3596,12 +3607,8 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
if(!canceled && RemovableDriveManager::get_instance().get_is_writing())
{
- //if (!RemovableDriveManager::get_instance().is_last_drive_removed())
- //{
- RemovableDriveManager::get_instance().set_is_writing(false);
- show_action_buttons(false);
- //}
-
+ RemovableDriveManager::get_instance().set_is_writing(false);
+ show_action_buttons(false);
}
}
@@ -4156,18 +4163,21 @@ void Plater::priv::update_object_menu()
void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
{
+ RemovableDriveManager::get_instance().set_plater_ready_to_slice(is_ready_to_slice);
wxWindowUpdateLocker noUpdater(sidebar);
const auto prin_host_opt = config->option("print_host");
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
- bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed() ; // #dk_FIXME
+ bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed();
+ bool export_removable_shown = RemovableDriveManager::get_instance().get_drives_count() > 0;
// when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1")
{
- if (sidebar->show_reslice(false) |
- sidebar->show_export(true) |
- sidebar->show_send(send_gcode_shown) |
- sidebar->show_disconnect(disconnect_shown))
+ if (sidebar->show_reslice(false) |
+ sidebar->show_export(true) |
+ sidebar->show_send(send_gcode_shown) |
+ sidebar->show_export_removable(export_removable_shown) |
+ sidebar->show_disconnect(disconnect_shown))
sidebar->Layout();
}
else
@@ -4175,6 +4185,7 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
if (sidebar->show_reslice(is_ready_to_slice) |
sidebar->show_export(!is_ready_to_slice) |
sidebar->show_send(send_gcode_shown && !is_ready_to_slice) |
+ sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice) |
sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice))
sidebar->Layout();
}
@@ -4684,7 +4695,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
}
}
-void Plater::export_gcode()
+void Plater::export_gcode(bool prefer_removable)
{
if (p->model.objects.empty())
return;
@@ -4706,11 +4717,19 @@ void Plater::export_gcode()
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
- if (GUI::RemovableDriveManager::get_instance().update())
+ bool removable_drives_connected = GUI::RemovableDriveManager::get_instance().update();
+ if(prefer_removable)
{
- if (!RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir))
+ if(removable_drives_connected)
{
- start_dir = RemovableDriveManager::get_instance().get_drive_path();
+ auto start_dir_removable = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string(), true);
+ if (RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir_removable))
+ {
+ start_dir = start_dir_removable;
+ }else
+ {
+ start_dir = RemovableDriveManager::get_instance().get_drive_path();
+ }
}
}
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
@@ -4723,7 +4742,7 @@ void Plater::export_gcode()
fs::path output_path;
if (dlg.ShowModal() == wxID_OK) {
fs::path path = into_path(dlg.GetPath());
- wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
+ wxGetApp().app_config->update_last_output_dir(path.parent_path().string(), RemovableDriveManager::get_instance().is_path_on_removable_drive(path.parent_path().string()));
output_path = std::move(path);
}
if (! output_path.empty())
@@ -4739,7 +4758,7 @@ void Plater::export_gcode()
{
RemovableDriveManager::get_instance().set_is_writing(true);
RemovableDriveManager::get_instance().erase_callbacks();
- RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this));
+ RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
}
}
@@ -5041,8 +5060,8 @@ void Plater::send_gcode()
void Plater::eject_drive()
{
RemovableDriveManager::get_instance().update(0, true);
- //RemovableDriveManager::get_instance().erase_callbacks();
- //RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this));
+ RemovableDriveManager::get_instance().erase_callbacks();
+ RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path());
}
@@ -5051,7 +5070,7 @@ void Plater::drive_ejected_callback()
if (RemovableDriveManager::get_instance().get_did_eject())
{
RemovableDriveManager::get_instance().set_did_eject(false);
- wxString message = "Unmounting succesesful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer.";
+ wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_ejected_name() + "(" + RemovableDriveManager::get_instance().get_ejected_path() + ")" + " can now be safely removed from the computer.";
wxMessageBox(message);
}
p->show_action_buttons(false);
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 1bea07795..f9891a252 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -120,6 +120,7 @@ public:
bool show_export(bool show) const;
bool show_send(bool show) const;
bool show_disconnect(bool show)const;
+ bool show_export_removable(bool show) const;
bool is_multifilament();
void update_mode();
@@ -186,7 +187,7 @@ public:
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);
- void export_gcode();
+ void export_gcode(bool prefer_removable = true);
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
index 5041e2120..4ed6d36a4 100644
--- a/src/slic3r/GUI/RemovableDriveManager.cpp
+++ b/src/slic3r/GUI/RemovableDriveManager.cpp
@@ -109,6 +109,8 @@ void RemovableDriveManager::eject_drive(const std::string &path)
CloseHandle(handle);
m_did_eject = true;
m_current_drives.erase(it);
+ m_ejected_path = m_last_save_path;
+ m_ejected_name = m_last_save_name;
break;
}
}
@@ -239,35 +241,10 @@ void RemovableDriveManager::search_for_drives()
//search /media/* folder
search_path("/media/*", "/media");
- //search /Volumes/* folder (OSX)
//search_path("/Volumes/*", "/Volumes");
std::string path(std::getenv("USER"));
std::string pp(path);
- //std::cout << "user: "<< path << "\n";
- //if program is run with sudo, we have to search for all users
- // but do we want that?
- /*
- if(path == "root"){
- while (true) {
- passwd* entry = getpwent();
- if (!entry) {
- break;
- }
- path = entry->pw_name;
- pp = path;
- //search /media/USERNAME/* folder
- pp = "/media/"+pp;
- path = "/media/" + path + "/*";
- search_path(path, pp);
- //search /run/media/USERNAME/* folder
- path = "/run" + path;
- pp = "/run"+pp;
- search_path(path, pp);
- }
- endpwent();
- }else
- */
{
//search /media/USERNAME/* folder
pp = "/media/"+pp;
@@ -310,7 +287,6 @@ void RemovableDriveManager::inspect_file(const std::string &path, const std::str
{
//free space
boost::filesystem::space_info si = boost::filesystem::space(path);
- //std::cout << "Free space: " << fs_si.free << "Available space: " << fs_si.available << " " << path << '\n';
if(si.available != 0)
{
//user id
@@ -353,7 +329,7 @@ void RemovableDriveManager::eject_drive(const std::string &path)
i++;
}
}
- std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
+ //std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
// there is no usable command in c++ so terminal command is used instead
// but neither triggers "succesful safe removal messege"
std::string command = "";
@@ -373,7 +349,8 @@ void RemovableDriveManager::eject_drive(const std::string &path)
m_did_eject = true;
m_current_drives.erase(it);
-
+ m_ejected_path = m_last_save_path;
+ m_ejected_name = m_last_save_name;
break;
}
@@ -385,7 +362,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
if (m_current_drives.empty())
return false;
std::size_t found = path.find_last_of("/");
- std::string new_path = path.substr(0,found);
+ std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if(compare_filesystem_id(new_path, (*it).path))
@@ -396,7 +373,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
{
std::size_t found = path.find_last_of("/");
- std::string new_path = path.substr(0, found);
+ std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
//check if same filesystem
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
@@ -414,7 +391,10 @@ RemovableDriveManager::RemovableDriveManager():
m_last_save_name(""),
m_last_save_path_verified(false),
m_is_writing(false),
- m_did_eject(false)
+ m_did_eject(false),
+ m_plater_ready_to_slice(true),
+ m_ejected_path(""),
+ m_ejected_name("")
#if __APPLE__
, m_rdmmm(new RDMMMWrapper())
#endif
@@ -451,7 +431,10 @@ bool RemovableDriveManager::update(const long time,const bool check)
search_for_drives();
if (m_drives_count != m_current_drives.size())
{
- if (check)check_and_notify();
+ if (check)
+ {
+ check_and_notify();
+ }
m_drives_count = m_current_drives.size();
}
return !m_current_drives.empty();
@@ -495,7 +478,11 @@ std::vector RemovableDriveManager::get_all_drives()
}
void RemovableDriveManager::check_and_notify()
{
- if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path))
+ if(m_drive_count_changed_callback)
+ {
+ m_drive_count_changed_callback(m_plater_ready_to_slice);
+ }
+ if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path))
{
for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
{
@@ -503,18 +490,35 @@ void RemovableDriveManager::check_and_notify()
}
}
}
-void RemovableDriveManager::add_callback(std::function callback)
+void RemovableDriveManager::add_remove_callback(std::function callback)
{
m_callbacks.push_back(callback);
}
-void RemovableDriveManager::erase_callbacks()
+void RemovableDriveManager::erase_callbacks()
{
m_callbacks.clear();
}
+void RemovableDriveManager::set_drive_count_changed_callback(std::function callback)
+{
+ m_drive_count_changed_callback = callback;
+}
+void RemovableDriveManager::set_plater_ready_to_slice(bool b)
+{
+ m_plater_ready_to_slice = b;
+}
void RemovableDriveManager::set_last_save_path(const std::string& path)
{
- m_last_save_path_verified = false;
- m_last_save_path = path;
+ if(m_last_save_path_verified)// if old path is on drive
+ {
+ if(get_drive_from_path(path) != "") //and new is too, rewrite the path
+ {
+ m_last_save_path_verified = false;
+ m_last_save_path = path;
+ }//else do nothing
+ }else
+ {
+ m_last_save_path = path;
+ }
}
void RemovableDriveManager::verify_last_save_path()
{
@@ -544,16 +548,15 @@ std::string RemovableDriveManager::get_drive_name(const std::string& path)
}
bool RemovableDriveManager::is_last_drive_removed()
{
- //std::cout<<"is last: "< get_all_drives();
bool is_path_on_removable_drive(const std::string &path);
// callback will notify only if device with last save path was removed
- void add_callback(std::function callback);
- // erases all callbacks added by add_callback()
+ void add_remove_callback(std::function callback);
+ // erases all remove callbacks added by add_remove_callback()
void erase_callbacks();
+ //drive_count_changed callback is called on every added or removed device
+ void set_drive_count_changed_callback(std::function callback);
+ //thi serves to set correct value for drive_count_changed callback
+ void set_plater_ready_to_slice(bool b);
// marks one of the eveices in vector as last used
void set_last_save_path(const std::string &path);
void verify_last_save_path();
@@ -59,6 +63,9 @@ public:
bool get_did_eject();
void set_did_eject(const bool b);
std::string get_drive_name(const std::string& path);
+ size_t get_drives_count();
+ std::string get_ejected_path();
+ std::string get_ejected_name();
private:
RemovableDriveManager();
void search_for_drives();
@@ -70,6 +77,7 @@ private:
std::vector m_current_drives;
std::vector> m_callbacks;
+ std::function m_drive_count_changed_callback;
size_t m_drives_count;
long m_last_update;
std::string m_last_save_path;
@@ -77,6 +85,9 @@ private:
std::string m_last_save_name;
bool m_is_writing;//on device
bool m_did_eject;
+ bool m_plater_ready_to_slice;
+ std::string m_ejected_path;
+ std::string m_ejected_name;
#if _WIN32
//registers for notifications by creating invisible window
void register_window();