mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into feature_slice_to_png
This commit is contained in:
		
						commit
						3efe6675cb
					
				
					 21 changed files with 393 additions and 89 deletions
				
			
		| 
						 | 
				
			
			@ -53,7 +53,7 @@ sub new {
 | 
			
		|||
        bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
 | 
			
		||||
        serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
 | 
			
		||||
        nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
 | 
			
		||||
	wipe_tower_rotation_angle extruder_colour filament_colour max_print_height
 | 
			
		||||
	wipe_tower_rotation_angle extruder_colour filament_colour max_print_height printer_model
 | 
			
		||||
    )]);
 | 
			
		||||
    # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
 | 
			
		||||
    $self->{model} = Slic3r::Model->new;
 | 
			
		||||
| 
						 | 
				
			
			@ -187,6 +187,7 @@ sub new {
 | 
			
		|||
    if ($Slic3r::GUI::have_OpenGL) {
 | 
			
		||||
        $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{gcode_preview_data}, $self->{config});
 | 
			
		||||
        Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0);
 | 
			
		||||
        Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1);
 | 
			
		||||
        Slic3r::GUI::_3DScene::register_on_viewport_changed_callback($self->{preview3D}->canvas, sub { Slic3r::GUI::_3DScene::set_viewport_from_scene($self->{canvas3D}, $self->{preview3D}->canvas); });
 | 
			
		||||
        $self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview'));
 | 
			
		||||
        $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
 | 
			
		||||
| 
						 | 
				
			
			@ -200,17 +201,18 @@ sub new {
 | 
			
		|||
    
 | 
			
		||||
    EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub {
 | 
			
		||||
        my $preview = $self->{preview_notebook}->GetCurrentPage;
 | 
			
		||||
        if ($preview == $self->{preview3D})
 | 
			
		||||
        {
 | 
			
		||||
        if (($preview != $self->{preview3D}) && ($preview != $self->{canvas3D})) {
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0);
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 0);
 | 
			
		||||
            Slic3r::GUI::_3DScene::reset_current_canvas();
 | 
			
		||||
            $preview->OnActivate if $preview->can('OnActivate');        
 | 
			
		||||
        } elsif ($preview == $self->{preview3D}) {
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 1);
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 0);
 | 
			
		||||
            Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 1);
 | 
			
		||||
            $self->{preview3D}->load_print(1);
 | 
			
		||||
        } else {
 | 
			
		||||
            Slic3r::GUI::_3DScene::enable_legend_texture($self->{preview3D}->canvas, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($preview == $self->{canvas3D}) {
 | 
			
		||||
            $self->{preview3D}->load_print;
 | 
			
		||||
            # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_as_dirty($self->{preview3D}->canvas);
 | 
			
		||||
        } elsif ($preview == $self->{canvas3D}) {
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_active($self->{canvas3D}, 1);
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_active($self->{preview3D}->canvas, 0);
 | 
			
		||||
            if (Slic3r::GUI::_3DScene::is_reload_delayed($self->{canvas3D})) {
 | 
			
		||||
| 
						 | 
				
			
			@ -218,8 +220,8 @@ sub new {
 | 
			
		|||
                Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
 | 
			
		||||
                Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
 | 
			
		||||
            }            
 | 
			
		||||
        } else {
 | 
			
		||||
            $preview->OnActivate if $preview->can('OnActivate');        
 | 
			
		||||
            # sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
 | 
			
		||||
            Slic3r::GUI::_3DScene::set_as_dirty($self->{canvas3D});
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -1886,6 +1888,9 @@ sub on_config_change {
 | 
			
		|||
            $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors}));
 | 
			
		||||
        } elsif ($opt_key eq 'max_print_height') {
 | 
			
		||||
            $update_scheduled = 1;
 | 
			
		||||
        } elsif ($opt_key eq 'printer_model') {
 | 
			
		||||
            # update to force bed selection (for texturing)
 | 
			
		||||
            $update_scheduled = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -231,8 +231,10 @@ sub mouse_event {
 | 
			
		|||
        }
 | 
			
		||||
        $self->Refresh;
 | 
			
		||||
    } elsif ($event->LeftUp) {
 | 
			
		||||
        $self->{on_instances_moved}->()
 | 
			
		||||
            if $self->{drag_object};
 | 
			
		||||
        if ($self->{drag_object}) {
 | 
			
		||||
            $self->{on_instances_moved}->();
 | 
			
		||||
            Slic3r::GUI::_3DScene::reset_current_canvas();            
 | 
			
		||||
        }
 | 
			
		||||
        $self->{drag_start_pos} = undef;
 | 
			
		||||
        $self->{drag_object} = undef;
 | 
			
		||||
        $self->SetCursor(wxSTANDARD_CURSOR);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
 | 
			
		|||
    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else if (strncmp(buffer, entry_magic_send, recv_size) != 0) {
 | 
			
		||||
    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
 | 
			
		||||
    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code: `%*s`\n", progname, recv_size, buffer);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +116,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
 | 
			
		|||
    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else if (strncmp(buffer, entry_magic_cfm, recv_size) != 0) {
 | 
			
		||||
    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect start code\n", progname);
 | 
			
		||||
    avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer emitted incorrect cfm code: `%*s`\n", progname, recv_size, buffer);
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +140,13 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
 | 
			
		|||
  serial_set_dtr_rts(&pgm->fd, 1);
 | 
			
		||||
  usleep(50*1000);
 | 
			
		||||
 | 
			
		||||
  // Sometimes there may be line noise generating input on the printer's USB-to-serial IC
 | 
			
		||||
  // Here we try to clean its input buffer with a sequence of newlines (a minimum of 9 is needed):
 | 
			
		||||
  const char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n";
 | 
			
		||||
  if (serial_send(&pgm->fd, cleanup_newlines, sizeof(cleanup_newlines) - 1) < 0) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * drain any extraneous input
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,14 +142,11 @@ public:
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		m_gcode += "G1";
 | 
			
		||||
		if (rot.x != rotated_current_pos.x) {
 | 
			
		||||
			m_gcode += set_format_X(rot.x);     // Transform current position back to wipe tower coordinates (was updated by set_format_X)
 | 
			
		||||
            m_current_pos.x = x;
 | 
			
		||||
        }
 | 
			
		||||
		if (rot.y != rotated_current_pos.y) {
 | 
			
		||||
		if (std::abs(dx) > EPSILON)
 | 
			
		||||
			m_gcode += set_format_X(rot.x);
 | 
			
		||||
 | 
			
		||||
		if (std::abs(dy) > EPSILON)
 | 
			
		||||
			m_gcode += set_format_Y(rot.y);
 | 
			
		||||
            m_current_pos.y = y;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		if (e != 0.f)
 | 
			
		||||
			m_gcode += set_format_E(e);
 | 
			
		||||
| 
						 | 
				
			
			@ -157,8 +154,8 @@ public:
 | 
			
		|||
		if (f != 0.f && f != m_current_feedrate)
 | 
			
		||||
			m_gcode += set_format_F(f);
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
        m_current_pos.x = x;
 | 
			
		||||
        m_current_pos.y = y;
 | 
			
		||||
 | 
			
		||||
		// Update the elapsed time with a rough estimate.
 | 
			
		||||
		m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -433,6 +433,7 @@ std::vector<int> GLVolumeCollection::load_object(
 | 
			
		|||
                    v.extruder_id = extruder_id;
 | 
			
		||||
            }
 | 
			
		||||
            v.is_modifier = model_volume->modifier;
 | 
			
		||||
            v.outside_printer_detection_enabled = !model_volume->modifier;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -663,7 +664,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
 | 
			
		|||
    bool contained = true;
 | 
			
		||||
    for (GLVolume* volume : this->volumes)
 | 
			
		||||
    {
 | 
			
		||||
        if (volume != nullptr)
 | 
			
		||||
        if ((volume != nullptr) && !volume->is_modifier)
 | 
			
		||||
        {
 | 
			
		||||
            bool state = print_volume.contains(volume->transformed_bounding_box());
 | 
			
		||||
            contained &= state;
 | 
			
		||||
| 
						 | 
				
			
			@ -1753,11 +1754,26 @@ bool _3DScene::init(wxGLCanvas* canvas)
 | 
			
		|||
    return s_canvas_mgr.init(canvas);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool _3DScene::set_current(wxGLCanvas* canvas, bool force)
 | 
			
		||||
{
 | 
			
		||||
    return s_canvas_mgr.set_current(canvas, force);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::reset_current_canvas()
 | 
			
		||||
{
 | 
			
		||||
    s_canvas_mgr.set_current(nullptr, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::set_active(wxGLCanvas* canvas, bool active)
 | 
			
		||||
{
 | 
			
		||||
    s_canvas_mgr.set_active(canvas, active);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _3DScene::set_as_dirty(wxGLCanvas* canvas)
 | 
			
		||||
{
 | 
			
		||||
    s_canvas_mgr.set_as_dirty(canvas);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas)
 | 
			
		||||
{
 | 
			
		||||
    return s_canvas_mgr.get_volumes_count(canvas);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -516,7 +516,11 @@ public:
 | 
			
		|||
 | 
			
		||||
    static bool init(wxGLCanvas* canvas);
 | 
			
		||||
 | 
			
		||||
    static bool set_current(wxGLCanvas* canvas, bool force);
 | 
			
		||||
    static void reset_current_canvas();
 | 
			
		||||
 | 
			
		||||
    static void set_active(wxGLCanvas* canvas, bool active);
 | 
			
		||||
    static void set_as_dirty(wxGLCanvas* canvas);
 | 
			
		||||
 | 
			
		||||
    static unsigned int get_volumes_count(wxGLCanvas* canvas);
 | 
			
		||||
    static void reset_volumes(wxGLCanvas* canvas);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,10 +12,20 @@ namespace Slic3r { namespace GUI {
 | 
			
		|||
 | 
			
		||||
	wxString double_to_string(double const value)
 | 
			
		||||
	{
 | 
			
		||||
		int precision = 10 * value - int(10 * value) == 0 ? 1 : 2;
 | 
			
		||||
		return value - int(value) == 0 ?
 | 
			
		||||
			wxString::Format(_T("%i"), int(value)) :
 | 
			
		||||
			wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
 | 
			
		||||
		if (value - int(value) == 0)
 | 
			
		||||
			return wxString::Format(_T("%i"), int(value));
 | 
			
		||||
		else {
 | 
			
		||||
			int precision = 4;
 | 
			
		||||
			for (size_t p = 1; p < 4; p++)
 | 
			
		||||
			{
 | 
			
		||||
				double cur_val = pow(10, p)*value;
 | 
			
		||||
				if (cur_val - int(cur_val) == 0) {
 | 
			
		||||
					precision = p;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Field::PostInitialize(){
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -405,16 +405,27 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const
 | 
			
		|||
    const PresetBundle* bundle = get_preset_bundle();
 | 
			
		||||
    if (bundle != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        const Preset& curr = bundle->printers.get_selected_preset();
 | 
			
		||||
        if (curr.config.has("bed_shape") && _are_equal(m_shape, dynamic_cast<const ConfigOptionPoints*>(curr.config.option("bed_shape"))->values))
 | 
			
		||||
        const Preset* curr = &bundle->printers.get_selected_preset();
 | 
			
		||||
        while (curr != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            if ((curr.vendor != nullptr) && (curr.vendor->name == "Prusa Research"))
 | 
			
		||||
            if (curr->config.has("bed_shape") && _are_equal(m_shape, dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values))
 | 
			
		||||
            {
 | 
			
		||||
                if (boost::contains(curr.name, "MK2"))
 | 
			
		||||
                    type = MK2;
 | 
			
		||||
                else if (boost::contains(curr.name, "MK3"))
 | 
			
		||||
                    type = MK3;
 | 
			
		||||
                if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research"))
 | 
			
		||||
                {
 | 
			
		||||
                    if (boost::contains(curr->name, "MK2"))
 | 
			
		||||
                    {
 | 
			
		||||
                        type = MK2;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (boost::contains(curr->name, "MK3"))
 | 
			
		||||
                    {
 | 
			
		||||
                        type = MK3;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            curr = bundle->printers.get_preset_parent(*curr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1509,13 +1520,10 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl)
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3D::set_current()
 | 
			
		||||
bool GLCanvas3D::set_current(bool force)
 | 
			
		||||
{
 | 
			
		||||
    if ((m_canvas != nullptr) && (m_context != nullptr))
 | 
			
		||||
    {
 | 
			
		||||
        m_canvas->SetCurrent(*m_context);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if ((force || m_active) && (m_canvas != nullptr) && (m_context != nullptr))
 | 
			
		||||
        return m_canvas->SetCurrent(*m_context);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1525,6 +1533,11 @@ void GLCanvas3D::set_active(bool active)
 | 
			
		|||
    m_active = active;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::set_as_dirty()
 | 
			
		||||
{
 | 
			
		||||
    m_dirty = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int GLCanvas3D::get_volumes_count() const
 | 
			
		||||
{
 | 
			
		||||
    return (unsigned int)m_volumes.volumes.size();
 | 
			
		||||
| 
						 | 
				
			
			@ -1532,8 +1545,13 @@ unsigned int GLCanvas3D::get_volumes_count() const
 | 
			
		|||
 | 
			
		||||
void GLCanvas3D::reset_volumes()
 | 
			
		||||
{
 | 
			
		||||
    if (set_current())
 | 
			
		||||
 | 
			
		||||
    if (!m_volumes.empty())
 | 
			
		||||
    {
 | 
			
		||||
        // ensures this canvas is current
 | 
			
		||||
        if ((m_canvas == nullptr) || !_3DScene::set_current(m_canvas, true))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        m_volumes.release_geometry();
 | 
			
		||||
        m_volumes.clear();
 | 
			
		||||
        m_dirty = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1831,8 +1849,8 @@ void GLCanvas3D::render()
 | 
			
		|||
    if (!_is_shown_on_screen())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    // ensures that the proper context is selected and that this canvas is initialized
 | 
			
		||||
    if (!set_current() || !_3DScene::init(m_canvas))
 | 
			
		||||
    // ensures this canvas is current and initialized
 | 
			
		||||
    if (!_3DScene::set_current(m_canvas, false) || !_3DScene::init(m_canvas))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (m_force_zoom_to_bed_enabled)
 | 
			
		||||
| 
						 | 
				
			
			@ -1913,6 +1931,11 @@ void GLCanvas3D::reload_scene(bool force)
 | 
			
		|||
        return;
 | 
			
		||||
 | 
			
		||||
    reset_volumes();
 | 
			
		||||
 | 
			
		||||
    // ensures this canvas is current
 | 
			
		||||
    if (!_3DScene::set_current(m_canvas, true))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    set_bed_shape(dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"))->values);
 | 
			
		||||
 | 
			
		||||
    if (!m_canvas->IsShown() && !force)
 | 
			
		||||
| 
						 | 
				
			
			@ -1984,6 +2007,10 @@ void GLCanvas3D::reload_scene(bool force)
 | 
			
		|||
 | 
			
		||||
void GLCanvas3D::load_print_toolpaths()
 | 
			
		||||
{
 | 
			
		||||
    // ensures this canvas is current
 | 
			
		||||
    if (!_3DScene::set_current(m_canvas, true))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (m_print == nullptr)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2348,8 +2375,8 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
 | 
			
		|||
{
 | 
			
		||||
    if ((m_canvas != nullptr) && (m_print != nullptr))
 | 
			
		||||
    {
 | 
			
		||||
        // ensures that the proper context is selected
 | 
			
		||||
        if (!set_current())
 | 
			
		||||
        // ensures that this canvas is current
 | 
			
		||||
        if (!_3DScene::set_current(m_canvas, false))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (m_volumes.empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -2666,7 +2693,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
#endif
 | 
			
		||||
    } 
 | 
			
		||||
    else if (evt.LeftDClick() && (m_hover_volume_id != -1))
 | 
			
		||||
    {
 | 
			
		||||
        m_active = false;
 | 
			
		||||
        m_on_double_click_callback.call();
 | 
			
		||||
        m_active = true;
 | 
			
		||||
    }
 | 
			
		||||
    else if (evt.LeftDown() || evt.RightDown())
 | 
			
		||||
    {
 | 
			
		||||
        // If user pressed left or right button we first check whether this happened
 | 
			
		||||
| 
						 | 
				
			
			@ -2762,7 +2793,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
                {
 | 
			
		||||
                    // if right clicking on volume, propagate event through callback
 | 
			
		||||
                    if (m_volumes.volumes[volume_idx]->hover)
 | 
			
		||||
                    {
 | 
			
		||||
                        m_active = false;
 | 
			
		||||
                        m_on_right_click_callback.call(pos.x, pos.y);
 | 
			
		||||
                        m_active = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -2980,10 +3015,11 @@ void GLCanvas3D::_force_zoom_to_bed()
 | 
			
		|||
 | 
			
		||||
void GLCanvas3D::_resize(unsigned int w, unsigned int h)
 | 
			
		||||
{
 | 
			
		||||
    if (m_context == nullptr)
 | 
			
		||||
    if ((m_canvas == nullptr) && (m_context == nullptr))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    set_current();
 | 
			
		||||
    // ensures that this canvas is current
 | 
			
		||||
    _3DScene::set_current(m_canvas, false);
 | 
			
		||||
    ::glViewport(0, 0, w, h);
 | 
			
		||||
 | 
			
		||||
    ::glMatrixMode(GL_PROJECTION);
 | 
			
		||||
| 
						 | 
				
			
			@ -3569,9 +3605,11 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
 | 
			
		|||
 | 
			
		||||
Pointf3 GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z)
 | 
			
		||||
{
 | 
			
		||||
    if (!set_current())
 | 
			
		||||
    if (m_canvas == nullptr)
 | 
			
		||||
        return Pointf3(DBL_MAX, DBL_MAX, DBL_MAX);
 | 
			
		||||
 | 
			
		||||
    _camera_tranform();
 | 
			
		||||
 | 
			
		||||
    GLint viewport[4];
 | 
			
		||||
    ::glGetIntegerv(GL_VIEWPORT, viewport);
 | 
			
		||||
    GLdouble modelview_matrix[16];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -449,9 +449,10 @@ public:
 | 
			
		|||
 | 
			
		||||
    bool init(bool useVBOs, bool use_legacy_opengl);
 | 
			
		||||
 | 
			
		||||
    bool set_current();
 | 
			
		||||
    bool set_current(bool force);
 | 
			
		||||
 | 
			
		||||
    void set_active(bool active);
 | 
			
		||||
    void set_as_dirty();
 | 
			
		||||
 | 
			
		||||
    unsigned int get_volumes_count() const;
 | 
			
		||||
    void reset_volumes();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,6 +115,7 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
 | 
			
		|||
 | 
			
		||||
GLCanvas3DManager::GLCanvas3DManager()
 | 
			
		||||
    : m_context(nullptr)
 | 
			
		||||
    , m_current(nullptr)
 | 
			
		||||
    , m_gl_initialized(false)
 | 
			
		||||
    , m_use_legacy_opengl(false)
 | 
			
		||||
    , m_use_VBOs(false)
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +213,34 @@ bool GLCanvas3DManager::init(wxGLCanvas* canvas)
 | 
			
		|||
        return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GLCanvas3DManager::set_current(wxGLCanvas* canvas, bool force)
 | 
			
		||||
{
 | 
			
		||||
    // given canvas is already current, return
 | 
			
		||||
    if (m_current == canvas)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    if (canvas == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        m_current = nullptr;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // set given canvas as current
 | 
			
		||||
    CanvasesMap::iterator it = _get_canvas(canvas);
 | 
			
		||||
    if (it != m_canvases.end())
 | 
			
		||||
    {
 | 
			
		||||
        bool res = it->second->set_current(force);
 | 
			
		||||
        if (res)
 | 
			
		||||
        {
 | 
			
		||||
            m_current = canvas;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_current = nullptr;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3DManager::set_active(wxGLCanvas* canvas, bool active)
 | 
			
		||||
{
 | 
			
		||||
    CanvasesMap::iterator it = _get_canvas(canvas);
 | 
			
		||||
| 
						 | 
				
			
			@ -219,6 +248,13 @@ void GLCanvas3DManager::set_active(wxGLCanvas* canvas, bool active)
 | 
			
		|||
        it->second->set_active(active);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3DManager::set_as_dirty(wxGLCanvas* canvas)
 | 
			
		||||
{
 | 
			
		||||
    CanvasesMap::iterator it = _get_canvas(canvas);
 | 
			
		||||
    if (it != m_canvases.end())
 | 
			
		||||
        it->second->set_as_dirty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int GLCanvas3DManager::get_volumes_count(wxGLCanvas* canvas) const
 | 
			
		||||
{
 | 
			
		||||
    CanvasesMap::const_iterator it = _get_canvas(canvas);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ class GLCanvas3DManager
 | 
			
		|||
 | 
			
		||||
    wxGLContext* m_context;
 | 
			
		||||
    CanvasesMap m_canvases;
 | 
			
		||||
    wxGLCanvas* m_current;
 | 
			
		||||
    GLInfo m_gl_info;
 | 
			
		||||
    bool m_gl_initialized;
 | 
			
		||||
    bool m_use_legacy_opengl;
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +70,9 @@ public:
 | 
			
		|||
 | 
			
		||||
    bool init(wxGLCanvas* canvas);
 | 
			
		||||
 | 
			
		||||
    bool set_current(wxGLCanvas* canvas, bool force);
 | 
			
		||||
    void set_active(wxGLCanvas* canvas, bool active);
 | 
			
		||||
    void set_as_dirty(wxGLCanvas* canvas);
 | 
			
		||||
 | 
			
		||||
    unsigned int get_volumes_count(wxGLCanvas* canvas) const;
 | 
			
		||||
    void reset_volumes(wxGLCanvas* canvas);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@
 | 
			
		|||
 | 
			
		||||
#include <wx/image.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +29,9 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
 | 
			
		|||
{
 | 
			
		||||
    reset();
 | 
			
		||||
 | 
			
		||||
    if (!boost::filesystem::exists(filename))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    // Load a PNG with an alpha channel.
 | 
			
		||||
    wxImage image;
 | 
			
		||||
    if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,6 +56,7 @@
 | 
			
		|||
 | 
			
		||||
#include "../Utils/PresetUpdater.hpp"
 | 
			
		||||
#include "../Config/Snapshot.hpp"
 | 
			
		||||
#include "3DScene.hpp"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Slic3r { namespace GUI {
 | 
			
		||||
| 
						 | 
				
			
			@ -378,6 +379,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
 | 
			
		|||
				save_language();
 | 
			
		||||
				show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
 | 
			
		||||
				if (event_language_change > 0) {
 | 
			
		||||
					_3DScene::remove_all_canvases();// remove all canvas before recreate GUI
 | 
			
		||||
					wxCommandEvent event(event_language_change);
 | 
			
		||||
					g_wxApp->ProcessEvent(event);
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,8 @@ struct MsgDialog : wxDialog
 | 
			
		|||
	MsgDialog &operator=(const MsgDialog &) = delete;
 | 
			
		||||
	virtual ~MsgDialog();
 | 
			
		||||
 | 
			
		||||
	// TODO: refactor with CreateStdDialogButtonSizer usage
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	enum {
 | 
			
		||||
		CONTENT_WIDTH = 500,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2619,14 +2619,24 @@ void SavePresetWindow::accept()
 | 
			
		|||
	if (!m_chosen_name.empty()) {
 | 
			
		||||
		const char* unusable_symbols = "<>:/\\|?*\"";
 | 
			
		||||
		bool is_unusable_symbol = false;
 | 
			
		||||
		bool is_unusable_postfix = false;
 | 
			
		||||
		const std::string unusable_postfix = "(modified)";
 | 
			
		||||
		for (size_t i = 0; i < std::strlen(unusable_symbols); i++){
 | 
			
		||||
			if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){
 | 
			
		||||
				is_unusable_symbol = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (m_chosen_name.find(unusable_postfix) != std::string::npos)
 | 
			
		||||
			is_unusable_postfix = true;
 | 
			
		||||
 | 
			
		||||
		if (is_unusable_symbol) {
 | 
			
		||||
			show_error(this, _(L("The supplied name is not valid; the following characters are not allowed:"))+" <>:/\\|?*\"");
 | 
			
		||||
			show_error(this,_(L("The supplied name is not valid;")) + "\n" +
 | 
			
		||||
							_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
 | 
			
		||||
		}
 | 
			
		||||
		else if (is_unusable_postfix){
 | 
			
		||||
			show_error(this,	_(L("The supplied name is not valid;")) + "\n" + 
 | 
			
		||||
								_(L("the following postfix are not allowed:")) + "\n\t" + unusable_postfix);
 | 
			
		||||
		}
 | 
			
		||||
		else if (m_chosen_name.compare("- default -") == 0) {
 | 
			
		||||
			show_error(this, _(L("The supplied name is not available.")));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,13 +3,16 @@
 | 
			
		|||
#include <cstdlib>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <deque>
 | 
			
		||||
#include <boost/filesystem/fstream.hpp>
 | 
			
		||||
#include <boost/format.hpp>
 | 
			
		||||
 | 
			
		||||
#include <curl/curl.h>
 | 
			
		||||
 | 
			
		||||
#include "../../libslic3r/libslic3r.h"
 | 
			
		||||
 | 
			
		||||
namespace fs = boost::filesystem;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +37,11 @@ struct Http::priv
 | 
			
		|||
	::curl_httppost *form;
 | 
			
		||||
	::curl_httppost *form_end;
 | 
			
		||||
	::curl_slist *headerlist;
 | 
			
		||||
	// Used for reading the body
 | 
			
		||||
	std::string buffer;
 | 
			
		||||
	// Used for storing file streams added as multipart form parts
 | 
			
		||||
	// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
 | 
			
		||||
	std::deque<fs::ifstream> form_files;
 | 
			
		||||
	size_t limit;
 | 
			
		||||
	bool cancel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +57,10 @@ struct Http::priv
 | 
			
		|||
	static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
 | 
			
		||||
	static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
 | 
			
		||||
	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 form_add_file(const char *name, const fs::path &path, const char* filename);
 | 
			
		||||
 | 
			
		||||
	std::string curl_error(CURLcode curlcode);
 | 
			
		||||
	std::string body_size_error();
 | 
			
		||||
	void http_perform();
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +146,46 @@ int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double
 | 
			
		|||
	return xfercb(userp, dltotal, dlnow, ultotal, ulnow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp)
 | 
			
		||||
{
 | 
			
		||||
	auto stream = reinterpret_cast<fs::ifstream*>(userp);
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		stream->read(buffer, size * nitems);
 | 
			
		||||
	} catch (...) {
 | 
			
		||||
		return CURL_READFUNC_ABORT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return stream->gcount();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	// and so we use CURLFORM_STREAM with boost ifstream to read the file.
 | 
			
		||||
 | 
			
		||||
	if (filename == nullptr) {
 | 
			
		||||
		filename = path.string().c_str();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	form_files.emplace_back(path, std::ios::in | std::ios::binary);
 | 
			
		||||
	auto &stream = form_files.back();
 | 
			
		||||
	stream.seekg(0, std::ios::end);
 | 
			
		||||
	size_t size = stream.tellg();
 | 
			
		||||
	stream.seekg(0);
 | 
			
		||||
 | 
			
		||||
	if (filename != nullptr) {
 | 
			
		||||
		::curl_formadd(&form, &form_end,
 | 
			
		||||
			CURLFORM_COPYNAME, name,
 | 
			
		||||
			CURLFORM_FILENAME, filename,
 | 
			
		||||
			CURLFORM_CONTENTTYPE, "application/octet-stream",
 | 
			
		||||
			CURLFORM_STREAM, static_cast<void*>(&stream),
 | 
			
		||||
			CURLFORM_CONTENTSLENGTH, static_cast<long>(size),
 | 
			
		||||
			CURLFORM_END
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string Http::priv::curl_error(CURLcode curlcode)
 | 
			
		||||
{
 | 
			
		||||
	return (boost::format("%1% (%2%)")
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +205,7 @@ void Http::priv::http_perform()
 | 
			
		|||
	::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_READFUNCTION, form_file_read_cb);
 | 
			
		||||
 | 
			
		||||
	::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
 | 
			
		||||
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32
 | 
			
		||||
| 
						 | 
				
			
			@ -183,14 +235,20 @@ void Http::priv::http_perform()
 | 
			
		|||
 | 
			
		||||
	if (res != CURLE_OK) {
 | 
			
		||||
		if (res == CURLE_ABORTED_BY_CALLBACK) {
 | 
			
		||||
			Progress dummyprogress(0, 0, 0, 0);
 | 
			
		||||
			bool cancel = true;
 | 
			
		||||
			if (progressfn) { progressfn(dummyprogress, cancel); }
 | 
			
		||||
			if (cancel) {
 | 
			
		||||
				// The abort comes from the request being cancelled programatically
 | 
			
		||||
				Progress dummyprogress(0, 0, 0, 0);
 | 
			
		||||
				bool cancel = true;
 | 
			
		||||
				if (progressfn) { progressfn(dummyprogress, cancel); }
 | 
			
		||||
			} else {
 | 
			
		||||
				// The abort comes from the CURLOPT_READFUNCTION callback, which means reading file failed
 | 
			
		||||
				if (errorfn) { errorfn(std::move(buffer), "Error reading file for file upload", http_status); }
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (res == CURLE_WRITE_ERROR) {
 | 
			
		||||
			if (errorfn) { errorfn(std::move(buffer), std::move(body_size_error()), http_status); }
 | 
			
		||||
			if (errorfn) { errorfn(std::move(buffer), body_size_error(), http_status); }
 | 
			
		||||
		} else {
 | 
			
		||||
			if (errorfn) { errorfn(std::move(buffer), std::move(curl_error(res)), http_status); }
 | 
			
		||||
			if (errorfn) { errorfn(std::move(buffer), curl_error(res), http_status); }
 | 
			
		||||
		};
 | 
			
		||||
	} else {
 | 
			
		||||
		if (completefn) {
 | 
			
		||||
| 
						 | 
				
			
			@ -265,17 +323,15 @@ Http& Http::form_add(const std::string &name, const std::string &contents)
 | 
			
		|||
	return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Http& Http::form_add_file(const std::string &name, const std::string &filename)
 | 
			
		||||
Http& Http::form_add_file(const std::string &name, const fs::path &path)
 | 
			
		||||
{
 | 
			
		||||
	if (p) {
 | 
			
		||||
		::curl_formadd(&p->form, &p->form_end,
 | 
			
		||||
			CURLFORM_COPYNAME, name.c_str(),
 | 
			
		||||
			CURLFORM_FILE, filename.c_str(),
 | 
			
		||||
			CURLFORM_CONTENTTYPE, "application/octet-stream",
 | 
			
		||||
			CURLFORM_END
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr); }
 | 
			
		||||
	return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename)
 | 
			
		||||
{
 | 
			
		||||
	if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str()); }
 | 
			
		||||
	return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +17,10 @@ private:
 | 
			
		|||
public:
 | 
			
		||||
	struct Progress
 | 
			
		||||
	{
 | 
			
		||||
		size_t dltotal;
 | 
			
		||||
		size_t dlnow;
 | 
			
		||||
		size_t ultotal;
 | 
			
		||||
		size_t ulnow;
 | 
			
		||||
		size_t dltotal;   // Total bytes to download
 | 
			
		||||
		size_t dlnow;     // Bytes downloaded so far
 | 
			
		||||
		size_t ultotal;   // Total bytes to upload
 | 
			
		||||
		size_t ulnow;     // Bytes uploaded so far
 | 
			
		||||
 | 
			
		||||
		Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) :
 | 
			
		||||
			dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow)
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +34,10 @@ public:
 | 
			
		|||
 | 
			
		||||
	Http(Http &&other);
 | 
			
		||||
 | 
			
		||||
	// Note: strings are expected to be UTF-8-encoded
 | 
			
		||||
 | 
			
		||||
	// These are the primary constructors that create a HTTP object
 | 
			
		||||
	// for a GET and a POST request respectively.
 | 
			
		||||
	static Http get(std::string url);
 | 
			
		||||
	static Http post(std::string url);
 | 
			
		||||
	~Http();
 | 
			
		||||
| 
						 | 
				
			
			@ -41,21 +46,42 @@ public:
 | 
			
		|||
	Http& operator=(const Http &) = delete;
 | 
			
		||||
	Http& operator=(Http &&) = delete;
 | 
			
		||||
 | 
			
		||||
	// Sets a maximum size of the data that can be received. The default is 5MB.
 | 
			
		||||
	Http& size_limit(size_t sizeLimit);
 | 
			
		||||
	// Sets a HTTP header field.
 | 
			
		||||
	Http& header(std::string name, const std::string &value);
 | 
			
		||||
	// Removes a header field.
 | 
			
		||||
	Http& remove_header(std::string name);
 | 
			
		||||
	// Sets a CA certificate file for usage with HTTPS. This is only supported on some backends,
 | 
			
		||||
	// specifically, this is supported with OpenSSL and NOT supported with Windows and OS X native certificate store.
 | 
			
		||||
	// See also ca_file_supported().
 | 
			
		||||
	Http& ca_file(const std::string &filename);
 | 
			
		||||
	// Add a HTTP multipart form field
 | 
			
		||||
	Http& form_add(const std::string &name, const std::string &contents);
 | 
			
		||||
	Http& form_add_file(const std::string &name, const std::string &filename);
 | 
			
		||||
	// Add a HTTP multipart form file data contents, `name` is the name of the part
 | 
			
		||||
	Http& form_add_file(const std::string &name, const boost::filesystem::path &path);
 | 
			
		||||
	// Same as above except also override the file's filename with a custom one
 | 
			
		||||
	Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
 | 
			
		||||
 | 
			
		||||
	// Callback called on HTTP request complete
 | 
			
		||||
	Http& on_complete(CompleteFn fn);
 | 
			
		||||
	// Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,
 | 
			
		||||
	// TCP connection, HTTP transfer, and finally also when the response indicates an error (status >= 400).
 | 
			
		||||
	// Therefore, a response body may or may not be present.
 | 
			
		||||
	Http& on_error(ErrorFn fn);
 | 
			
		||||
	// Callback called on data download/upload prorgess (called fairly frequently).
 | 
			
		||||
	// See the `Progress` structure for description of the data passed.
 | 
			
		||||
	// Writing a true-ish value into the cancel reference parameter cancels the request.
 | 
			
		||||
	Http& on_progress(ProgressFn fn);
 | 
			
		||||
 | 
			
		||||
	// Starts performing the request in a background thread
 | 
			
		||||
	Ptr perform();
 | 
			
		||||
	// Starts performing the request on the current thread
 | 
			
		||||
	void perform_sync();
 | 
			
		||||
	// Cancels a request in progress
 | 
			
		||||
	void cancel();
 | 
			
		||||
 | 
			
		||||
	// Tells whether current backend supports seting up a CA file using ca_file()
 | 
			
		||||
	static bool ca_file_supported();
 | 
			
		||||
private:
 | 
			
		||||
	Http(const std::string &url);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,65 @@
 | 
			
		|||
#include "OctoPrint.hpp"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
#include <boost/format.hpp>
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
 | 
			
		||||
#include <wx/frame.h>
 | 
			
		||||
#include <wx/event.h>
 | 
			
		||||
#include <wx/progdlg.h>
 | 
			
		||||
#include <wx/sizer.h>
 | 
			
		||||
#include <wx/stattext.h>
 | 
			
		||||
#include <wx/textctrl.h>
 | 
			
		||||
#include <wx/checkbox.h>
 | 
			
		||||
 | 
			
		||||
#include "libslic3r/PrintConfig.hpp"
 | 
			
		||||
#include "slic3r/GUI/GUI.hpp"
 | 
			
		||||
#include "slic3r/GUI/MsgDialog.hpp"
 | 
			
		||||
#include "Http.hpp"
 | 
			
		||||
 | 
			
		||||
namespace fs = boost::filesystem;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct SendDialog : public GUI::MsgDialog
 | 
			
		||||
{
 | 
			
		||||
	wxTextCtrl *txt_filename;
 | 
			
		||||
	wxCheckBox *box_print;
 | 
			
		||||
 | 
			
		||||
	SendDialog(const fs::path &path) :
 | 
			
		||||
		MsgDialog(nullptr, _(L("Send G-Code to printer")), _(L("Upload to OctoPrint with the following filename:")), wxID_NONE),
 | 
			
		||||
		txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())),
 | 
			
		||||
		box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
 | 
			
		||||
	{
 | 
			
		||||
		auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
 | 
			
		||||
		label_dir_hint->Wrap(CONTENT_WIDTH);
 | 
			
		||||
 | 
			
		||||
		content_sizer->Add(txt_filename, 0, wxEXPAND);
 | 
			
		||||
		content_sizer->Add(label_dir_hint);
 | 
			
		||||
		content_sizer->AddSpacer(VERT_SPACING);
 | 
			
		||||
		content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
 | 
			
		||||
 | 
			
		||||
		btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
 | 
			
		||||
 | 
			
		||||
		txt_filename->SetFocus();
 | 
			
		||||
		wxString stem(path.stem().wstring());
 | 
			
		||||
		txt_filename->SetSelection(0, stem.Length());
 | 
			
		||||
 | 
			
		||||
		Fit();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fs::path filename() const {
 | 
			
		||||
		return fs::path(txt_filename->GetValue().wx_str());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool print() const { return box_print->GetValue(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
 | 
			
		||||
	host(config->opt_string("octoprint_host")),
 | 
			
		||||
	apikey(config->opt_string("octoprint_apikey")),
 | 
			
		||||
| 
						 | 
				
			
			@ -27,24 +72,39 @@ bool OctoPrint::test(wxString &msg) const
 | 
			
		|||
	// it is ok to refer to `msg` from within the closure
 | 
			
		||||
 | 
			
		||||
	bool res = true;
 | 
			
		||||
	auto url = make_url("api/version");
 | 
			
		||||
 | 
			
		||||
	BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url;
 | 
			
		||||
 | 
			
		||||
	auto url = std::move(make_url("api/version"));
 | 
			
		||||
	auto http = Http::get(std::move(url));
 | 
			
		||||
	set_auth(http);
 | 
			
		||||
	http.on_error([&](std::string, std::string error, unsigned status) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1% (HTTP %2%)") % error % status;
 | 
			
		||||
			res = false;
 | 
			
		||||
			msg = format_error(error, status);
 | 
			
		||||
		})
 | 
			
		||||
		.on_complete([&](std::string body, unsigned) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
 | 
			
		||||
		})
 | 
			
		||||
		.perform_sync();
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OctoPrint::send_gcode(const std::string &filename, bool print) const
 | 
			
		||||
bool OctoPrint::send_gcode(const std::string &filename) const
 | 
			
		||||
{
 | 
			
		||||
	enum { PROGRESS_RANGE = 1000 };
 | 
			
		||||
 | 
			
		||||
	const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
 | 
			
		||||
	fs::path filepath(filename);
 | 
			
		||||
 | 
			
		||||
	SendDialog send_dialog(filepath.filename());
 | 
			
		||||
	if (send_dialog.ShowModal() != wxID_OK) { return false; }
 | 
			
		||||
 | 
			
		||||
	const bool print = send_dialog.print();
 | 
			
		||||
	const auto upload_filepath = send_dialog.filename();
 | 
			
		||||
	const auto upload_filename = upload_filepath.filename();
 | 
			
		||||
	const auto upload_parent_path = upload_filepath.parent_path();
 | 
			
		||||
 | 
			
		||||
	wxProgressDialog progress_dialog(
 | 
			
		||||
		_(L("OctoPrint upload")),
 | 
			
		||||
| 
						 | 
				
			
			@ -61,14 +121,26 @@ bool OctoPrint::send_gcode(const std::string &filename, bool print) const
 | 
			
		|||
 | 
			
		||||
	bool res = true;
 | 
			
		||||
 | 
			
		||||
	auto http = Http::post(std::move(make_url("api/files/local")));
 | 
			
		||||
	auto url = make_url("api/files/local");
 | 
			
		||||
 | 
			
		||||
	BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
 | 
			
		||||
		% filepath.string()
 | 
			
		||||
		% url
 | 
			
		||||
		% upload_filename.string()
 | 
			
		||||
		% upload_parent_path.string()
 | 
			
		||||
		% print;
 | 
			
		||||
 | 
			
		||||
	auto http = Http::post(std::move(url));
 | 
			
		||||
	set_auth(http);
 | 
			
		||||
	http.form_add("print", print ? "true" : "false")
 | 
			
		||||
		.form_add_file("file", filename)
 | 
			
		||||
		.form_add("path", upload_parent_path.string())
 | 
			
		||||
		.form_add_file("file", filename, upload_filename.string())
 | 
			
		||||
		.on_complete([&](std::string body, unsigned status) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
 | 
			
		||||
			progress_dialog.Update(PROGRESS_RANGE);
 | 
			
		||||
		})
 | 
			
		||||
		.on_error([&](std::string body, std::string error, unsigned status) {
 | 
			
		||||
			BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1% (HTTP %2%)") % error % status;
 | 
			
		||||
			auto errormsg = wxString::Format("%s: %s", errortitle, format_error(error, status));
 | 
			
		||||
			GUI::show_error(&progress_dialog, std::move(errormsg));
 | 
			
		||||
			res = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -102,24 +174,24 @@ std::string OctoPrint::make_url(const std::string &path) const
 | 
			
		|||
{
 | 
			
		||||
	if (host.find("http://") == 0 || host.find("https://") == 0) {
 | 
			
		||||
		if (host.back() == '/') {
 | 
			
		||||
			return std::move((boost::format("%1%%2%") % host % path).str());
 | 
			
		||||
			return (boost::format("%1%%2%") % host % path).str();
 | 
			
		||||
		} else {
 | 
			
		||||
			return std::move((boost::format("%1%/%2%") % host % path).str());
 | 
			
		||||
			return (boost::format("%1%/%2%") % host % path).str();
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return std::move((boost::format("http://%1%/%2%") % host % path).str());
 | 
			
		||||
		return (boost::format("http://%1%/%2%") % host % path).str();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxString OctoPrint::format_error(std::string error, unsigned status)
 | 
			
		||||
wxString OctoPrint::format_error(const std::string &error, unsigned status)
 | 
			
		||||
{
 | 
			
		||||
	const wxString wxerror = error;
 | 
			
		||||
	auto wxerror = wxString::FromUTF8(error.data());
 | 
			
		||||
 | 
			
		||||
	if (status != 0) {
 | 
			
		||||
		return wxString::Format("HTTP %u: %s", status,
 | 
			
		||||
			(status == 401 ? _(L("Invalid API key")) : wxerror));
 | 
			
		||||
	} else {
 | 
			
		||||
		return std::move(wxerror);
 | 
			
		||||
		return wxerror;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,8 @@ public:
 | 
			
		|||
	OctoPrint(DynamicPrintConfig *config);
 | 
			
		||||
 | 
			
		||||
	bool test(wxString &curl_msg) const;
 | 
			
		||||
	bool send_gcode(const std::string &filename, bool print = false) const;
 | 
			
		||||
	// Send gcode file to octoprint, filename is expected to be in UTF-8
 | 
			
		||||
	bool send_gcode(const std::string &filename) const;
 | 
			
		||||
private:
 | 
			
		||||
	std::string host;
 | 
			
		||||
	std::string apikey;
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +26,7 @@ private:
 | 
			
		|||
 | 
			
		||||
	void set_auth(Http &http) const;
 | 
			
		||||
	std::string make_url(const std::string &path) const;
 | 
			
		||||
	static wxString format_error(std::string error, unsigned status);
 | 
			
		||||
	static wxString format_error(const std::string &error, unsigned status);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,6 +190,11 @@ remove_all_canvases()
 | 
			
		|||
    CODE:
 | 
			
		||||
        _3DScene::remove_all_canvases();
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
reset_current_canvas()
 | 
			
		||||
    CODE:
 | 
			
		||||
        _3DScene::reset_current_canvas();
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
set_active(canvas, active)
 | 
			
		||||
        SV   *canvas;
 | 
			
		||||
| 
						 | 
				
			
			@ -197,6 +202,12 @@ set_active(canvas, active)
 | 
			
		|||
    CODE:
 | 
			
		||||
        _3DScene::set_active((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), active);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
set_as_dirty(canvas)
 | 
			
		||||
        SV *canvas;
 | 
			
		||||
    CODE:
 | 
			
		||||
        _3DScene::set_as_dirty((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
 | 
			
		||||
 | 
			
		||||
unsigned int
 | 
			
		||||
get_volumes_count(canvas)
 | 
			
		||||
        SV *canvas;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,5 +9,5 @@
 | 
			
		|||
    OctoPrint(DynamicPrintConfig *config);
 | 
			
		||||
    ~OctoPrint();
 | 
			
		||||
 | 
			
		||||
    bool send_gcode(std::string filename, bool print = false) const;
 | 
			
		||||
    bool send_gcode(std::string filename) const;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue