Merge branch 'master' of https://github.com/Prusa3d/Slic3r
							
								
								
									
										
											BIN
										
									
								
								resources/icons/Slic3r_32px.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.7 KiB  | 
| 
		 Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2 KiB  | 
| 
		 Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
						 | 
				
			
			@ -1549,7 +1549,7 @@ void GLCanvas3D::Selection::start_dragging()
 | 
			
		|||
    _set_caches();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::Selection::translate(const Vec3d& displacement)
 | 
			
		||||
void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local)
 | 
			
		||||
{
 | 
			
		||||
    if (!m_valid)
 | 
			
		||||
        return;
 | 
			
		||||
| 
						 | 
				
			
			@ -1559,7 +1559,7 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement)
 | 
			
		|||
#if ENABLE_MODELVOLUME_TRANSFORM
 | 
			
		||||
    if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower)
 | 
			
		||||
    {
 | 
			
		||||
        if (_requires_local_axes())
 | 
			
		||||
        if (local)
 | 
			
		||||
            (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -1614,7 +1614,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
 | 
			
		|||
        else if (is_single_volume() || is_single_modifier())
 | 
			
		||||
#if ENABLE_WORLD_ROTATIONS
 | 
			
		||||
        {
 | 
			
		||||
            if (_requires_local_axes())
 | 
			
		||||
            if (requires_local_axes())
 | 
			
		||||
                (*m_volumes)[i]->set_volume_rotation(rotation);
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -2118,6 +2118,11 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel
 | 
			
		|||
}
 | 
			
		||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::Selection::requires_local_axes() const
 | 
			
		||||
{
 | 
			
		||||
    return (m_mode == Volume) && is_from_single_instance();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::Selection::_update_valid()
 | 
			
		||||
{
 | 
			
		||||
    m_valid = (m_volumes != nullptr) && (m_model != nullptr);
 | 
			
		||||
| 
						 | 
				
			
			@ -2746,11 +2751,6 @@ void GLCanvas3D::Selection::_ensure_on_bed()
 | 
			
		|||
}
 | 
			
		||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::Selection::_requires_local_axes() const
 | 
			
		||||
{
 | 
			
		||||
    return (m_mode == Volume) && is_from_single_instance();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f;
 | 
			
		||||
const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f;
 | 
			
		||||
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -578,7 +578,7 @@ public:
 | 
			
		|||
 | 
			
		||||
        void start_dragging();
 | 
			
		||||
 | 
			
		||||
        void translate(const Vec3d& displacement);
 | 
			
		||||
        void translate(const Vec3d& displacement, bool local = false);
 | 
			
		||||
        void rotate(const Vec3d& rotation, bool local);
 | 
			
		||||
        void flattening_rotate(const Vec3d& normal);
 | 
			
		||||
        void scale(const Vec3d& scale, bool local);
 | 
			
		||||
| 
						 | 
				
			
			@ -597,6 +597,8 @@ public:
 | 
			
		|||
        void render_sidebar_hints(const std::string& sidebar_field) const;
 | 
			
		||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
 | 
			
		||||
 | 
			
		||||
        bool requires_local_axes() const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        void _update_valid();
 | 
			
		||||
        void _update_type();
 | 
			
		||||
| 
						 | 
				
			
			@ -626,7 +628,6 @@ public:
 | 
			
		|||
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
 | 
			
		||||
        void _ensure_on_bed();
 | 
			
		||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
 | 
			
		||||
        bool _requires_local_axes() const;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class ClippingPlane
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -137,7 +137,7 @@ bool GUI_App::OnInit()
 | 
			
		|||
    std::cerr << "Creating main frame..." << std::endl;
 | 
			
		||||
    if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
 | 
			
		||||
        wxImage::AddHandler(new wxPNGHandler());
 | 
			
		||||
    mainframe = new MainFrame(no_plater, false);
 | 
			
		||||
    mainframe = new MainFrame();
 | 
			
		||||
    sidebar().obj_list()->init_objects(); // propagate model objects to object list
 | 
			
		||||
    update_mode();
 | 
			
		||||
    SetTopWindow(mainframe);
 | 
			
		||||
| 
						 | 
				
			
			@ -277,8 +277,8 @@ void GUI_App::recreate_GUI()
 | 
			
		|||
{
 | 
			
		||||
    std::cerr << "recreate_GUI" << std::endl;
 | 
			
		||||
 | 
			
		||||
    auto topwindow = GetTopWindow();
 | 
			
		||||
    mainframe = new MainFrame(no_plater,false);
 | 
			
		||||
    MainFrame* topwindow = dynamic_cast<MainFrame*>(GetTopWindow());
 | 
			
		||||
    mainframe = new MainFrame();
 | 
			
		||||
    sidebar().obj_list()->init_objects(); // propagate model objects to object list
 | 
			
		||||
    update_mode();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -287,6 +287,20 @@ void GUI_App::recreate_GUI()
 | 
			
		|||
        topwindow->Destroy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
 | 
			
		||||
 | 
			
		||||
    CallAfter([this]() {
 | 
			
		||||
        // temporary workaround for the correct behavior of the Scrolled sidebar panel
 | 
			
		||||
        auto& panel = sidebar();
 | 
			
		||||
        if (panel.obj_list()->GetMinHeight() > 200) {
 | 
			
		||||
            wxWindowUpdateLocker noUpdates_sidebar(&panel);
 | 
			
		||||
            panel.obj_list()->SetMinSize(wxSize(-1, 200));
 | 
			
		||||
            panel.Layout();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
    mainframe->Show(true);
 | 
			
		||||
 | 
			
		||||
    // On OSX the UI was not initialized correctly if the wizard was called
 | 
			
		||||
    // before the UI was up and running.
 | 
			
		||||
    CallAfter([]() {
 | 
			
		||||
| 
						 | 
				
			
			@ -434,7 +448,7 @@ bool GUI_App::select_language(  wxArrayString & names,
 | 
			
		|||
        m_wxLocale = new wxLocale;
 | 
			
		||||
        m_wxLocale->Init(identifiers[index]);
 | 
			
		||||
        m_wxLocale->AddCatalogLookupPathPrefix(localization_dir());
 | 
			
		||||
        m_wxLocale->AddCatalog(GetAppName());
 | 
			
		||||
        m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE");
 | 
			
		||||
        wxSetlocale(LC_NUMERIC, "C");
 | 
			
		||||
        Preset::update_suffix_modified();
 | 
			
		||||
        return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -461,7 +475,7 @@ bool GUI_App::load_language()
 | 
			
		|||
            m_wxLocale = new wxLocale;
 | 
			
		||||
            m_wxLocale->Init(identifiers[i]);
 | 
			
		||||
            m_wxLocale->AddCatalogLookupPathPrefix(localization_dir());
 | 
			
		||||
            m_wxLocale->AddCatalog(GetAppName());
 | 
			
		||||
            m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE");
 | 
			
		||||
            wxSetlocale(LC_NUMERIC, "C");
 | 
			
		||||
            Preset::update_suffix_modified();
 | 
			
		||||
            return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -504,7 +518,8 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident
 | 
			
		|||
        {
 | 
			
		||||
            auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() +
 | 
			
		||||
                filename + wxFileName::GetPathSeparator() +
 | 
			
		||||
                GetAppName() + wxT(".mo");
 | 
			
		||||
                /*GetAppName()*/"Slic3rPE" + 
 | 
			
		||||
                wxT(".mo");
 | 
			
		||||
            if (wxFileExists(full_file_name))
 | 
			
		||||
            {
 | 
			
		||||
                names.Add(langinfo->Description);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,7 +71,6 @@ static wxString dots("…", wxConvUTF8);
 | 
			
		|||
 | 
			
		||||
class GUI_App : public wxApp
 | 
			
		||||
{
 | 
			
		||||
    bool            no_plater{ false };
 | 
			
		||||
    bool            app_conf_exists{ false };
 | 
			
		||||
 | 
			
		||||
    // Lock to guard the callback stack
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -361,8 +361,9 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation)
 | 
			
		|||
void ObjectManipulation::change_position_value(const Vec3d& position)
 | 
			
		||||
{
 | 
			
		||||
    auto canvas = wxGetApp().plater()->canvas3D();
 | 
			
		||||
    canvas->get_selection().start_dragging();
 | 
			
		||||
    canvas->get_selection().translate(position - cache_position);
 | 
			
		||||
    GLCanvas3D::Selection& selection = canvas->get_selection();
 | 
			
		||||
    selection.start_dragging();
 | 
			
		||||
    selection.translate(position - cache_position, selection.requires_local_axes());
 | 
			
		||||
    canvas->do_move();
 | 
			
		||||
 | 
			
		||||
    cache_position = position;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -586,7 +586,8 @@ void Preview::create_double_slider()
 | 
			
		|||
            auto& config = wxGetApp().preset_bundle->project_config;
 | 
			
		||||
            ((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
 | 
			
		||||
            m_schedule_background_process();
 | 
			
		||||
            int type = m_choice_view_type->FindString(_(L("Color Print")));
 | 
			
		||||
            bool color_print = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty();
 | 
			
		||||
            int type = m_choice_view_type->FindString(color_print ? _(L("Color Print")) : _(L("Feature type")) );
 | 
			
		||||
            if (m_choice_view_type->GetSelection() != type) {
 | 
			
		||||
                m_choice_view_type->SetSelection(type);
 | 
			
		||||
                if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ KBShortcutsDialog::KBShortcutsDialog()
 | 
			
		|||
 | 
			
		||||
    wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
 | 
			
		||||
    font.SetPointSize(10);
 | 
			
		||||
    const wxFont bold_font = font.Bold();
 | 
			
		||||
    wxFont bold_font = font.Bold();
 | 
			
		||||
#ifdef __WXOSX__
 | 
			
		||||
    font.SetPointSize(12);
 | 
			
		||||
    bold_font.SetPointSize(14);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,10 +28,8 @@
 | 
			
		|||
namespace Slic3r {
 | 
			
		||||
namespace GUI {
 | 
			
		||||
 | 
			
		||||
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
 | 
			
		||||
MainFrame::MainFrame() :
 | 
			
		||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
 | 
			
		||||
        m_no_plater(no_plater),
 | 
			
		||||
        m_loaded(loaded),
 | 
			
		||||
        m_printhost_queue_dlg(new PrintHostQueueDialog(this))
 | 
			
		||||
{
 | 
			
		||||
    // Load the icon either from the exe, or from the ico file.
 | 
			
		||||
| 
						 | 
				
			
			@ -125,11 +123,9 @@ void MainFrame::init_tabpanel()
 | 
			
		|||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (!m_no_plater) {
 | 
			
		||||
        m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
 | 
			
		||||
        wxGetApp().plater_ = m_plater;
 | 
			
		||||
        m_tabpanel->AddPage(m_plater, _(L("Plater")));
 | 
			
		||||
    }
 | 
			
		||||
    m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
 | 
			
		||||
    wxGetApp().plater_ = m_plater;
 | 
			
		||||
    m_tabpanel->AddPage(m_plater, _(L("Plater")));
 | 
			
		||||
 | 
			
		||||
    // The following event is emited by Tab implementation on config value change.
 | 
			
		||||
    Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,10 +42,7 @@ struct PresetTab {
 | 
			
		|||
 | 
			
		||||
class MainFrame : public wxFrame
 | 
			
		||||
{
 | 
			
		||||
    bool        m_no_plater;
 | 
			
		||||
    bool        m_loaded;
 | 
			
		||||
    int         m_lang_ch_event;
 | 
			
		||||
    int         m_preferences_event;
 | 
			
		||||
    bool        m_loaded {false};
 | 
			
		||||
 | 
			
		||||
    wxString    m_qs_last_input_file = wxEmptyString;
 | 
			
		||||
    wxString    m_qs_last_output_file = wxEmptyString;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,8 +68,7 @@ class MainFrame : public wxFrame
 | 
			
		|||
    bool can_delete_all() const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    MainFrame() {}
 | 
			
		||||
    MainFrame(const bool no_plater, const bool loaded);
 | 
			
		||||
    MainFrame();
 | 
			
		||||
    ~MainFrame() {}
 | 
			
		||||
 | 
			
		||||
    Plater*     plater() { return m_plater; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
 | 
			
		|||
    job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT);
 | 
			
		||||
 | 
			
		||||
    auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
    auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
 | 
			
		||||
    auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));   // TODO: enable based on status ("show error" for failed jobs)
 | 
			
		||||
    auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
 | 
			
		||||
    btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
 | 
			
		||||
    btnsizer->AddStretchSpacer();
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +140,13 @@ void PrintHostQueueDialog::on_error(Event &evt)
 | 
			
		|||
{
 | 
			
		||||
    wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
 | 
			
		||||
 | 
			
		||||
    // TODO
 | 
			
		||||
    job_list->SetValue(wxVariant(0), evt.job_id, 1);
 | 
			
		||||
    job_list->SetValue(wxVariant(_(L("Error"))), evt.job_id, 2);
 | 
			
		||||
 | 
			
		||||
    // TODO: keep the error for repeated display
 | 
			
		||||
 | 
			
		||||
    auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error);
 | 
			
		||||
    GUI::show_error(nullptr, std::move(errormsg));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1551,7 +1551,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
 | 
			
		|||
 | 
			
		||||
	auto printhost_browse = [this, optgroup] (wxWindow* parent) {
 | 
			
		||||
 | 
			
		||||
		// TODO: SLA
 | 
			
		||||
		// TODO: SLA Bonjour
 | 
			
		||||
 | 
			
		||||
		auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
 | 
			
		||||
		btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
 | 
			
		||||
| 
						 | 
				
			
			@ -1562,6 +1562,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
 | 
			
		|||
			BonjourDialog dialog(parent);
 | 
			
		||||
			if (dialog.show_and_lookup()) {
 | 
			
		||||
				optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
 | 
			
		||||
				// FIXME: emit killfocus on the edit widget
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2058,7 +2058,7 @@ void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter)
 | 
			
		|||
//    -  value decrease (if wxSL_HORIZONTAL) 
 | 
			
		||||
void PrusaDoubleSlider::move_current_thumb(const bool condition)
 | 
			
		||||
{
 | 
			
		||||
    m_is_one_layer = wxGetKeyState(WXK_CONTROL);
 | 
			
		||||
//     m_is_one_layer = wxGetKeyState(WXK_CONTROL);
 | 
			
		||||
    int delta = condition ? -1 : 1;
 | 
			
		||||
    if (is_horizontal())
 | 
			
		||||
        delta *= -1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ bool Duet::test(wxString &msg) const
 | 
			
		|||
 | 
			
		||||
wxString Duet::get_test_ok_msg () const
 | 
			
		||||
{
 | 
			
		||||
	return wxString::Format("%s", _(L("Connection to Duet works correctly.")));
 | 
			
		||||
	return _(L("Connection to Duet works correctly."));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxString Duet::get_test_failed_msg (wxString &msg) const
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +135,7 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
 | 
			
		|||
// 	return res;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
 | 
			
		||||
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
 | 
			
		||||
{
 | 
			
		||||
	// XXX: TODO
 | 
			
		||||
	throw "unimplemented";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,12 +19,12 @@ public:
 | 
			
		|||
	Duet(DynamicPrintConfig *config);
 | 
			
		||||
	virtual ~Duet();
 | 
			
		||||
 | 
			
		||||
	bool test(wxString &curl_msg) const;
 | 
			
		||||
	wxString get_test_ok_msg () const;
 | 
			
		||||
	wxString get_test_failed_msg (wxString &msg) const;
 | 
			
		||||
	bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
 | 
			
		||||
	bool has_auto_discovery() const;
 | 
			
		||||
	bool can_test() const;
 | 
			
		||||
	virtual bool test(wxString &curl_msg) const;
 | 
			
		||||
	virtual wxString get_test_ok_msg () const;
 | 
			
		||||
	virtual wxString get_test_failed_msg (wxString &msg) const;
 | 
			
		||||
	virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
 | 
			
		||||
	virtual bool has_auto_discovery() const;
 | 
			
		||||
	virtual bool can_test() const;
 | 
			
		||||
	virtual std::string get_host() const { return host; }
 | 
			
		||||
private:
 | 
			
		||||
	std::string host;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ class CurlGlobalInit
 | 
			
		|||
struct Http::priv
 | 
			
		||||
{
 | 
			
		||||
	enum {
 | 
			
		||||
		DEFAULT_TIMEOUT = 10,
 | 
			
		||||
		DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +64,7 @@ struct Http::priv
 | 
			
		|||
	static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow);
 | 
			
		||||
	static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
 | 
			
		||||
 | 
			
		||||
	void set_timeout(long timeout);
 | 
			
		||||
	void form_add_file(const char *name, const fs::path &path, const char* filename);
 | 
			
		||||
	void set_post_body(const fs::path &path);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +86,7 @@ Http::priv::priv(const std::string &url)
 | 
			
		|||
		throw std::runtime_error(std::string("Could not construct Curl object"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	set_timeout(DEFAULT_TIMEOUT);
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());   // curl makes a copy internally
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front());
 | 
			
		||||
| 
						 | 
				
			
			@ -167,6 +170,12 @@ size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, v
 | 
			
		|||
	return stream->gcount();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Http::priv::set_timeout(long timeout)
 | 
			
		||||
{
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename)
 | 
			
		||||
{
 | 
			
		||||
	// We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows
 | 
			
		||||
| 
						 | 
				
			
			@ -203,10 +212,10 @@ void Http::priv::set_post_body(const fs::path &path)
 | 
			
		|||
 | 
			
		||||
std::string Http::priv::curl_error(CURLcode curlcode)
 | 
			
		||||
{
 | 
			
		||||
	return (boost::format("%1% (%2%): %3%")
 | 
			
		||||
	return (boost::format("%1%:\n%2%\n[Error %3%]")
 | 
			
		||||
		% ::curl_easy_strerror(curlcode)
 | 
			
		||||
		% error_buffer.c_str()
 | 
			
		||||
		% curlcode
 | 
			
		||||
		% error_buffer
 | 
			
		||||
	).str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -293,6 +302,13 @@ Http::~Http()
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Http& Http::timeout(long timeout)
 | 
			
		||||
{
 | 
			
		||||
	if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT; }
 | 
			
		||||
	if (p) { p->set_timeout(timeout); }
 | 
			
		||||
	return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Http& Http::size_limit(size_t sizeLimit)
 | 
			
		||||
{
 | 
			
		||||
	if (p) { p->limit = sizeLimit; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,6 +55,8 @@ public:
 | 
			
		|||
	Http& operator=(const Http &) = delete;
 | 
			
		||||
	Http& operator=(Http &&) = delete;
 | 
			
		||||
 | 
			
		||||
	// Sets a maximum connection timeout in seconds
 | 
			
		||||
	Http& timeout(long timeout);
 | 
			
		||||
	// Sets a maximum size of the data that can be received.
 | 
			
		||||
	// A value of zero sets the default limit, which is is 5MB.
 | 
			
		||||
	Http& size_limit(size_t sizeLimit);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,12 @@
 | 
			
		|||
#include "OctoPrint.hpp"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <boost/format.hpp>
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
#include <boost/property_tree/ptree.hpp>
 | 
			
		||||
#include <boost/property_tree/json_parser.hpp>
 | 
			
		||||
#include <boost/algorithm/string/predicate.hpp>
 | 
			
		||||
 | 
			
		||||
#include <wx/progdlg.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +16,7 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
namespace fs = boost::filesystem;
 | 
			
		||||
namespace pt = boost::property_tree;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
| 
						 | 
				
			
			@ -41,11 +46,29 @@ bool OctoPrint::test(wxString &msg) const
 | 
			
		|||
            res = false;
 | 
			
		||||
            msg = format_error(body, error, status);
 | 
			
		||||
        })
 | 
			
		||||
        .on_complete([&](std::string body, unsigned) {
 | 
			
		||||
        .on_complete([&, this](std::string body, unsigned) {
 | 
			
		||||
            BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
 | 
			
		||||
 | 
			
		||||
            // TODO: parse body, call validate_version_text
 | 
			
		||||
            try {
 | 
			
		||||
                std::stringstream ss(body);
 | 
			
		||||
                pt::ptree ptree;
 | 
			
		||||
                pt::read_json(ss, ptree);
 | 
			
		||||
 | 
			
		||||
                if (! ptree.get_optional<std::string>("api")) {
 | 
			
		||||
                    res = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const auto text = ptree.get_optional<std::string>("text");
 | 
			
		||||
                res = validate_version_text(text);
 | 
			
		||||
                if (! res) {
 | 
			
		||||
                    msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (...) {
 | 
			
		||||
                res = false;
 | 
			
		||||
                msg = "Could not parse server response";
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .perform_sync();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,28 +77,24 @@ bool OctoPrint::test(wxString &msg) const
 | 
			
		|||
 | 
			
		||||
wxString OctoPrint::get_test_ok_msg () const
 | 
			
		||||
{
 | 
			
		||||
    return wxString::Format("%s", _(L("Connection to OctoPrint works correctly.")));
 | 
			
		||||
    return _(L("Connection to OctoPrint works correctly."));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxString OctoPrint::get_test_failed_msg (wxString &msg) const
 | 
			
		||||
{
 | 
			
		||||
    return wxString::Format("%s: %s\n\n%s",
 | 
			
		||||
                        _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
 | 
			
		||||
        _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
 | 
			
		||||
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
 | 
			
		||||
{
 | 
			
		||||
    const auto upload_filename = upload_data.upload_path.filename();
 | 
			
		||||
    const auto upload_parent_path = upload_data.upload_path.parent_path();
 | 
			
		||||
 | 
			
		||||
    wxString test_msg;
 | 
			
		||||
    if (! test(test_msg)) {
 | 
			
		||||
 | 
			
		||||
        // TODO:
 | 
			
		||||
 | 
			
		||||
        // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
 | 
			
		||||
        // GUI::show_error(&progress_dialog, std::move(errormsg));
 | 
			
		||||
        // return false;
 | 
			
		||||
        error_fn(std::move(test_msg));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool res = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +118,8 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn
 | 
			
		|||
        })
 | 
			
		||||
        .on_error([&](std::string body, std::string error, unsigned status) {
 | 
			
		||||
            BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
 | 
			
		||||
            error_fn(std::move(body), std::move(error), status);
 | 
			
		||||
            // error_fn(std::move(body), std::move(error), status);
 | 
			
		||||
            error_fn(format_error(body, error, status));
 | 
			
		||||
            res = false;
 | 
			
		||||
        })
 | 
			
		||||
        .on_progress([&](Http::Progress progress, bool &cancel) {
 | 
			
		||||
| 
						 | 
				
			
			@ -125,10 +145,9 @@ bool OctoPrint::can_test() const
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OctoPrint::validate_version_text(const std::string &version_text)
 | 
			
		||||
bool OctoPrint::validate_version_text(const boost::optional<std::string> &version_text) const
 | 
			
		||||
{
 | 
			
		||||
    // FIXME
 | 
			
		||||
    return true;
 | 
			
		||||
    return version_text ? boost::starts_with(*version_text, "OctoPrint") : true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OctoPrint::set_auth(Http &http) const
 | 
			
		||||
| 
						 | 
				
			
			@ -164,14 +183,23 @@ wxString OctoPrint::format_error(const std::string &body, const std::string &err
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// SL1
 | 
			
		||||
// SLAHost
 | 
			
		||||
 | 
			
		||||
SL1Host::~SL1Host() {}
 | 
			
		||||
SLAHost::~SLAHost() {}
 | 
			
		||||
 | 
			
		||||
bool SL1Host::validate_version_text(const std::string &version_text)
 | 
			
		||||
wxString SLAHost::get_test_ok_msg () const
 | 
			
		||||
{
 | 
			
		||||
    // FIXME
 | 
			
		||||
    return true;
 | 
			
		||||
    return _(L("Connection to Prusa SLA works correctly."));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxString SLAHost::get_test_failed_msg (wxString &msg) const
 | 
			
		||||
{
 | 
			
		||||
    return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SLAHost::validate_version_text(const boost::optional<std::string> &version_text) const
 | 
			
		||||
{
 | 
			
		||||
    return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <wx/string.h>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
 | 
			
		||||
#include "PrintHost.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,16 +20,16 @@ public:
 | 
			
		|||
    OctoPrint(DynamicPrintConfig *config);
 | 
			
		||||
    virtual ~OctoPrint();
 | 
			
		||||
 | 
			
		||||
    bool test(wxString &curl_msg) const;
 | 
			
		||||
    wxString get_test_ok_msg () const;
 | 
			
		||||
    wxString get_test_failed_msg (wxString &msg) const;
 | 
			
		||||
    bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
 | 
			
		||||
    bool has_auto_discovery() const;
 | 
			
		||||
    bool can_test() const;
 | 
			
		||||
    virtual bool test(wxString &curl_msg) const;
 | 
			
		||||
    virtual wxString get_test_ok_msg () const;
 | 
			
		||||
    virtual wxString get_test_failed_msg (wxString &msg) const;
 | 
			
		||||
    virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
 | 
			
		||||
    virtual bool has_auto_discovery() const;
 | 
			
		||||
    virtual bool can_test() const;
 | 
			
		||||
    virtual std::string get_host() const { return host; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    virtual bool validate_version_text(const std::string &version_text);
 | 
			
		||||
    virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string host;
 | 
			
		||||
| 
						 | 
				
			
			@ -41,14 +42,16 @@ private:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SL1Host: public OctoPrint
 | 
			
		||||
class SLAHost: public OctoPrint
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {}
 | 
			
		||||
    virtual ~SL1Host();
 | 
			
		||||
    SLAHost(DynamicPrintConfig *config) : OctoPrint(config) {}
 | 
			
		||||
    virtual ~SLAHost();
 | 
			
		||||
 | 
			
		||||
    virtual wxString get_test_ok_msg () const;
 | 
			
		||||
    virtual wxString get_test_failed_msg (wxString &msg) const;
 | 
			
		||||
protected:
 | 
			
		||||
    virtual bool validate_version_text(const std::string &version_text);
 | 
			
		||||
    virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,12 @@
 | 
			
		|||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
 | 
			
		||||
#include <wx/string.h>
 | 
			
		||||
#include <wx/app.h>
 | 
			
		||||
 | 
			
		||||
#include "libslic3r/PrintConfig.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +32,8 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
 | 
			
		|||
 | 
			
		||||
    switch (opt->value) {
 | 
			
		||||
        case htOctoPrint: return new OctoPrint(config);
 | 
			
		||||
        case htSL1:       return new SL1Host(config);
 | 
			
		||||
        case htDuet:      return new Duet(config);
 | 
			
		||||
        case htSL1:       return new SLAHost(config);
 | 
			
		||||
        default: return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +60,6 @@ struct PrintHostJobQueue::priv
 | 
			
		|||
    void start_bg_thread();
 | 
			
		||||
    void bg_thread_main();
 | 
			
		||||
    void progress_fn(Http::Progress progress, bool &cancel);
 | 
			
		||||
    void error_fn(std::string body, std::string error, unsigned http_status);
 | 
			
		||||
    void perform_job(PrintHostJob the_job);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +101,9 @@ void PrintHostJobQueue::priv::bg_thread_main()
 | 
			
		|||
            }
 | 
			
		||||
            job_id++;
 | 
			
		||||
        }
 | 
			
		||||
    } catch (const std::exception &e) {
 | 
			
		||||
        auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, e.what());
 | 
			
		||||
        wxQueueEvent(queue_dialog, evt);
 | 
			
		||||
    } catch (...) {
 | 
			
		||||
        wxTheApp->OnUnhandledException();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -136,28 +140,28 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status)
 | 
			
		||||
{
 | 
			
		||||
    // TODO
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
 | 
			
		||||
{
 | 
			
		||||
    if (bg_exit || the_job.empty()) { return; }
 | 
			
		||||
 | 
			
		||||
    BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`")
 | 
			
		||||
    BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%2%`")
 | 
			
		||||
        % the_job.upload_data.upload_path
 | 
			
		||||
        % the_job.printhost->get_host();
 | 
			
		||||
 | 
			
		||||
    const fs::path gcode_path = the_job.upload_data.source_path;
 | 
			
		||||
 | 
			
		||||
    the_job.printhost->upload(std::move(the_job.upload_data),
 | 
			
		||||
    bool success = the_job.printhost->upload(std::move(the_job.upload_data),
 | 
			
		||||
        [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
 | 
			
		||||
        [this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); }
 | 
			
		||||
        [this](wxString error) {
 | 
			
		||||
            auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error));
 | 
			
		||||
            wxQueueEvent(queue_dialog, evt);
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100);
 | 
			
		||||
    wxQueueEvent(queue_dialog, evt);
 | 
			
		||||
    if (success) {
 | 
			
		||||
        auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100);
 | 
			
		||||
        wxQueueEvent(queue_dialog, evt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::system::error_code ec;
 | 
			
		||||
    fs::remove(gcode_path, ec);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
 | 
			
		||||
#include <wx/string.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -28,10 +29,13 @@ class PrintHost
 | 
			
		|||
public:
 | 
			
		||||
    virtual ~PrintHost();
 | 
			
		||||
 | 
			
		||||
    typedef Http::ProgressFn ProgressFn;
 | 
			
		||||
    typedef std::function<void(wxString /* error */)> ErrorFn;
 | 
			
		||||
 | 
			
		||||
    virtual bool test(wxString &curl_msg) const = 0;
 | 
			
		||||
    virtual wxString get_test_ok_msg () const = 0;
 | 
			
		||||
    virtual wxString get_test_failed_msg (wxString &msg) const = 0;
 | 
			
		||||
    virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0;
 | 
			
		||||
    virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0;
 | 
			
		||||
    virtual bool has_auto_discovery() const = 0;
 | 
			
		||||
    virtual bool can_test() const = 0;
 | 
			
		||||
    virtual std::string get_host() const = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||