2019-05-23 09:20:11 +02:00
#include "libslic3r/libslic3r.h"
2018-10-04 16:43:10 +02:00
#include "GUI_ObjectList.hpp"
2018-10-05 23:29:15 +02:00
#include "GUI_ObjectManipulation.hpp"
2018-10-04 16:43:10 +02:00
#include "GUI_App.hpp"
2018-11-26 14:41:58 +01:00
#include "I18N.hpp"
2018-10-04 16:43:10 +02:00
#include "OptionsGroup.hpp"
#include "PresetBundle.hpp"
#include "Tab.hpp"
#include "wxExtensions.hpp"
2018-11-26 14:41:58 +01:00
#include "libslic3r/Model.hpp"
2018-10-05 23:29:15 +02:00
#include "LambdaObjectDialog.hpp"
2018-10-11 15:57:09 +02:00
#include "GLCanvas3D.hpp"
2019-03-19 13:30:21 +01:00
#include "Selection.hpp"
2018-10-04 16:43:10 +02:00
#include <boost/algorithm/string.hpp>
#include "slic3r/Utils/FixModelByWin10.hpp"
namespace Slic3r
{
namespace GUI
{
2018-11-01 14:02:38 +01:00
wxDEFINE_EVENT ( EVT_OBJ_LIST_OBJECT_SELECT , SimpleEvent );
2018-11-01 12:33:56 +01:00
2019-01-25 16:39:50 +01:00
// pt_FFF
FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
{
{ L ( "Layers and Perimeters" ), { "layer_height" , "perimeters" , "top_solid_layers" , "bottom_solid_layers" } },
{ L ( "Infill" ) , { "fill_density" , "fill_pattern" } },
{ L ( "Support material" ) , { "support_material" , "support_material_auto" , "support_material_threshold" ,
"support_material_pattern" , "support_material_buildplate_only" ,
"support_material_spacing" } },
{ L ( "Extruders" ) , { "wipe_into_infill" , "wipe_into_objects" } }
};
// pt_SLA
FreqSettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
{
{ L ( "Pad and Support" ) , { "supports_enable" , "pad_enable" } }
};
2019-05-09 14:53:01 +02:00
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
std :: vector < std :: pair < std :: string , std :: string >> ADD_VOLUME_MENU_ITEMS = {
// menu_item Name menu_item bitmap name
{ L ( "Add part" ), "add_part" }, // ~ModelVolumeType::MODEL_PART
{ L ( "Add modifier" ), "add_modifier" }, // ~ModelVolumeType::PARAMETER_MODIFIER
{ L ( "Add support enforcer" ), "support_enforcer" }, // ~ModelVolumeType::SUPPORT_ENFORCER
{ L ( "Add support blocker" ), "support_blocker" } // ~ModelVolumeType::SUPPORT_BLOCKER
};
2019-03-05 15:33:38 +01:00
static PrinterTechnology printer_technology ()
{
return wxGetApp (). preset_bundle -> printers . get_selected_preset (). printer_technology ();
}
2019-03-14 13:15:04 +01:00
// Config from current edited printer preset
2019-03-14 15:45:52 +01:00
static DynamicPrintConfig & printer_config ()
2019-03-14 13:15:04 +01:00
{
return wxGetApp (). preset_bundle -> printers . get_edited_preset (). config ;
}
2019-03-14 15:45:52 +01:00
static int extruders_count ()
2019-03-14 13:15:04 +01:00
{
2019-03-19 14:36:32 +01:00
return wxGetApp (). extruders_cnt ();
2019-03-14 13:15:04 +01:00
}
2018-10-04 16:43:10 +02:00
ObjectList :: ObjectList ( wxWindow * parent ) :
2018-12-06 14:49:14 +01:00
wxDataViewCtrl ( parent , wxID_ANY , wxDefaultPosition , wxDefaultSize , wxDV_MULTIPLE ),
m_parent ( parent )
2018-10-04 16:43:10 +02:00
{
2018-10-05 23:29:15 +02:00
// Fill CATEGORY_ICON
{
2019-04-12 11:28:07 +02:00
// Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget,
// see note in PresetBundle::load_compatible_bitmaps()
2018-11-22 10:33:16 +01:00
// ptFFF
2019-04-12 11:28:07 +02:00
CATEGORY_ICON [ L ( "Layers and Perimeters" )] = create_scaled_bitmap ( nullptr , "layers" );
CATEGORY_ICON [ L ( "Infill" )] = create_scaled_bitmap ( nullptr , "infill" );
CATEGORY_ICON [ L ( "Support material" )] = create_scaled_bitmap ( nullptr , "support" );
CATEGORY_ICON [ L ( "Speed" )] = create_scaled_bitmap ( nullptr , "time" );
CATEGORY_ICON [ L ( "Extruders" )] = create_scaled_bitmap ( nullptr , "funnel" );
CATEGORY_ICON [ L ( "Extrusion Width" )] = create_scaled_bitmap ( nullptr , "funnel" );
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim");
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time");
CATEGORY_ICON [ L ( "Advanced" )] = create_scaled_bitmap ( nullptr , "wrench" );
// ptSLA
2019-04-12 17:10:04 +02:00
CATEGORY_ICON [ L ( "Supports" )] = create_scaled_bitmap ( nullptr , "support" /*"sla_supports"*/ );
CATEGORY_ICON [ L ( "Pad" )] = create_scaled_bitmap ( nullptr , "pad" );
2018-10-05 23:29:15 +02:00
}
2018-10-04 16:43:10 +02:00
// create control
create_objects_ctrl ();
2018-11-02 23:27:31 +01:00
init_icons ();
2018-10-04 16:43:10 +02:00
// describe control behavior
2019-04-02 16:33:52 +02:00
Bind ( wxEVT_DATAVIEW_SELECTION_CHANGED , [ this ]( wxDataViewEvent & event ) {
2019-01-31 14:12:07 +01:00
#ifndef __APPLE__
// On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called
// before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object
// manipulator cache with the following call to selection_changed()
wxGetApp (). obj_manipul () -> emulate_kill_focus ();
2019-04-09 15:43:16 +02:00
#else
2019-04-08 11:06:15 +02:00
// To avoid selection update from SetSelection() and UnselectAll() under osx
if ( m_prevent_list_events )
return ;
2019-01-31 14:12:07 +01:00
#endif // __APPLE__
2019-04-04 15:07:54 +02:00
/* For multiple selection with pressed SHIFT,
* event.GetItem() returns value of a first item in selection list
* instead of real last clicked item.
* So, let check last selected item in such strange way
*/
if ( wxGetKeyState ( WXK_SHIFT ))
{
wxDataViewItemArray sels ;
GetSelections ( sels );
if ( sels . front () == m_last_selected_item )
m_last_selected_item = sels . back ();
else
m_last_selected_item = event . GetItem ();
}
else
m_last_selected_item = event . GetItem ();
2018-10-11 15:57:09 +02:00
selection_changed ();
2018-10-04 16:43:10 +02:00
#ifndef __WXMSW__
set_tooltip_for_item ( get_mouse_position_in_control ());
#endif //__WXMSW__
});
2019-05-16 10:37:06 +02:00
#ifdef __WXOSX__
// Key events are not correctly processed by the wxDataViewCtrl on OSX.
// Our patched wxWidgets process the keyboard accelerators.
// On the other hand, using accelerators will break in-place editing on Windows & Linux/GTK (there is no in-place editing working on OSX for wxDataViewCtrl for now).
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
{
// Accelerators
wxAcceleratorEntry entries [ 6 ];
entries [ 0 ]. Set ( wxACCEL_CTRL , ( int ) 'C' , wxID_COPY );
entries [ 1 ]. Set ( wxACCEL_CTRL , ( int ) 'X' , wxID_CUT );
entries [ 2 ]. Set ( wxACCEL_CTRL , ( int ) 'V' , wxID_PASTE );
entries [ 3 ]. Set ( wxACCEL_CTRL , ( int ) 'A' , wxID_SELECTALL );
entries [ 4 ]. Set ( wxACCEL_NORMAL , WXK_DELETE , wxID_DELETE );
entries [ 5 ]. Set ( wxACCEL_NORMAL , WXK_BACK , wxID_DELETE );
wxAcceleratorTable accel ( 6 , entries );
SetAcceleratorTable ( accel );
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { wxPostEvent (( wxEvtHandler * ) wxGetApp (). plater () -> canvas3D () -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_COPY )); }, wxID_COPY );
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { wxPostEvent (( wxEvtHandler * ) wxGetApp (). plater () -> canvas3D () -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_PASTE )); }, wxID_PASTE );
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { this -> select_item_all_children (); }, wxID_SELECTALL );
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { this -> remove (); }, wxID_DELETE );
}
#else __WXOSX__
Bind ( wxEVT_CHAR , [ this ]( wxKeyEvent & event ) { key_event ( event ); }); // doesn't work on OSX
#endif
2018-10-04 16:43:10 +02:00
#ifdef __WXMSW__
2018-10-08 16:27:38 +02:00
GetMainWindow () -> Bind ( wxEVT_MOTION , [ this ]( wxMouseEvent & event ) {
2018-10-16 11:08:37 +02:00
set_tooltip_for_item ( /*event.GetPosition()*/ get_mouse_position_in_control ());
2018-10-04 16:43:10 +02:00
event . Skip ();
});
#endif //__WXMSW__
2018-12-11 09:37:58 +01:00
Bind ( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU , & ObjectList :: OnContextMenu , this );
Bind ( wxEVT_DATAVIEW_ITEM_BEGIN_DRAG , & ObjectList :: OnBeginDrag , this );
Bind ( wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE , & ObjectList :: OnDropPossible , this );
Bind ( wxEVT_DATAVIEW_ITEM_DROP , & ObjectList :: OnDrop , this );
2018-12-12 14:35:18 +01:00
Bind ( wxEVT_DATAVIEW_ITEM_EDITING_DONE , & ObjectList :: OnEditingDone , this );
2018-12-11 09:37:58 +01:00
Bind ( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED , & ObjectList :: ItemValueChanged , this );
2018-11-12 13:47:24 +01:00
2018-12-10 13:39:56 +01:00
Bind ( wxCUSTOMEVT_LAST_VOLUME_IS_DELETED , [ this ]( wxCommandEvent & e ) { last_volume_is_deleted ( e . GetInt ()); });
2018-12-12 08:40:10 +01:00
2019-04-09 13:13:42 +02:00
Bind ( wxEVT_SIZE , ([ this ]( wxSizeEvent & e ) { this -> EnsureVisible ( this -> GetCurrentItem ()); e . Skip (); }));
2018-10-05 23:29:15 +02:00
}
ObjectList ::~ ObjectList ()
{
2018-10-04 16:43:10 +02:00
}
void ObjectList :: create_objects_ctrl ()
{
2019-03-26 18:01:44 +01:00
/* Temporary workaround for the correct behavior of the Scrolled sidebar panel:
* 1. set a height of the list to some big value
* 2. change it to the normal min value (15 * wxGetApp().em_unit()) after first whole Mainframe updating/layouting
*/
SetMinSize ( wxSize ( - 1 , 3000 ));
2018-10-04 16:43:10 +02:00
m_sizer = new wxBoxSizer ( wxVERTICAL );
2019-02-07 14:44:05 +01:00
m_sizer -> Add ( this , 1 , wxGROW );
2018-10-04 16:43:10 +02:00
2019-04-25 01:45:00 +02:00
m_objects_model = new ObjectDataViewModel ;
2018-10-08 16:27:38 +02:00
AssociateModel ( m_objects_model );
2018-11-12 13:47:24 +01:00
m_objects_model -> SetAssociatedControl ( this );
2018-10-04 16:43:10 +02:00
#if wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
2018-10-08 16:27:38 +02:00
EnableDragSource ( wxDF_UNICODETEXT );
EnableDropTarget ( wxDF_UNICODETEXT );
2018-10-04 16:43:10 +02:00
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
// column 0(Icon+Text) of the view control:
// And Icon can be consisting of several bitmaps
2019-04-25 01:45:00 +02:00
AppendColumn ( new wxDataViewColumn ( _ ( L ( "Name" )), new BitmapTextRenderer (),
2019-02-06 11:07:32 +01:00
0 , 20 * wxGetApp (). em_unit () /*200*/ , wxALIGN_LEFT , wxDATAVIEW_COL_RESIZABLE ));
2018-10-04 16:43:10 +02:00
// column 1 of the view control:
2018-10-08 16:27:38 +02:00
AppendColumn ( create_objects_list_extruder_column ( 4 ));
2018-10-04 16:43:10 +02:00
2018-10-18 11:28:31 +02:00
// column 2 of the view control:
2019-02-06 11:07:32 +01:00
AppendBitmapColumn ( " " , 2 , wxDATAVIEW_CELL_INERT , int ( 2.5 * wxGetApp (). em_unit ()) /*25*/ ,
2018-10-04 16:43:10 +02:00
wxALIGN_CENTER_HORIZONTAL , wxDATAVIEW_COL_RESIZABLE );
}
2019-01-30 10:05:54 +01:00
void ObjectList :: create_popup_menus ()
{
// create popup menus for object and part
create_object_popupmenu ( & m_menu_object );
create_part_popupmenu ( & m_menu_part );
create_sla_object_popupmenu ( & m_menu_sla_object );
create_instance_popupmenu ( & m_menu_instance );
}
2019-04-24 13:33:05 +02:00
void ObjectList :: get_selected_item_indexes ( int & obj_idx , int & vol_idx , const wxDataViewItem & input_item /* = wxDataViewItem(0)*/ )
{
const wxDataViewItem item = input_item == wxDataViewItem ( 0 ) ? GetSelection () : input_item ;
if ( ! item )
{
obj_idx = vol_idx = - 1 ;
return ;
}
const ItemType type = m_objects_model -> GetItemType ( item );
obj_idx = type & itObject ? m_objects_model -> GetIdByItem ( item ) :
type & itVolume ? m_objects_model -> GetIdByItem ( m_objects_model -> GetTopParent ( item )) : - 1 ;
vol_idx = type & itVolume ? m_objects_model -> GetVolumeIdByItem ( item ) : - 1 ;
}
int ObjectList :: get_mesh_errors_count ( const int obj_idx , const int vol_idx /*= -1*/ ) const
{
if ( obj_idx < 0 )
return 0 ;
2019-04-24 16:04:47 +02:00
return ( * m_objects )[ obj_idx ] -> get_mesh_errors_count ( vol_idx );
2019-04-24 13:33:05 +02:00
}
wxString ObjectList :: get_mesh_errors_list ( const int obj_idx , const int vol_idx /*= -1*/ ) const
{
const int errors = get_mesh_errors_count ( obj_idx , vol_idx );
if ( errors == 0 )
return "" ; // hide tooltip
// Create tooltip string, if there are errors
wxString tooltip = wxString :: Format ( _ ( L ( "Auto-repaired (%d errors): \n " )), errors );
2019-04-24 16:04:47 +02:00
const stl_stats & stats = vol_idx == - 1 ?
( * m_objects )[ obj_idx ] -> get_object_stl_stats () :
2019-06-11 17:08:47 +02:00
( * m_objects )[ obj_idx ] -> volumes [ vol_idx ] -> mesh (). stl . stats ;
2019-04-24 13:33:05 +02:00
std :: map < std :: string , int > error_msg = {
2019-04-24 16:04:47 +02:00
{ L ( "degenerate facets" ), stats . degenerate_facets },
{ L ( "edges fixed" ), stats . edges_fixed },
{ L ( "facets removed" ), stats . facets_removed },
{ L ( "facets added" ), stats . facets_added },
{ L ( "facets reversed" ), stats . facets_reversed },
{ L ( "backwards edges" ), stats . backwards_edges }
2019-04-24 13:33:05 +02:00
};
for ( const auto & error : error_msg )
if ( error . second > 0 )
2019-04-30 15:46:25 +02:00
tooltip += wxString :: Format ( " \t %d %s \n " , error . second , _ ( error . first ));
2019-04-24 13:33:05 +02:00
if ( is_windows10 ())
tooltip += _ ( L ( "Right button click the icon to fix STL through Netfabb" ));
return tooltip ;
}
wxString ObjectList :: get_mesh_errors_list ()
{
if ( ! GetSelection ())
return "" ;
int obj_idx , vol_idx ;
get_selected_item_indexes ( obj_idx , vol_idx );
return get_mesh_errors_list ( obj_idx , vol_idx );
}
2018-10-04 16:43:10 +02:00
void ObjectList :: set_tooltip_for_item ( const wxPoint & pt )
{
wxDataViewItem item ;
wxDataViewColumn * col ;
2018-10-08 16:27:38 +02:00
HitTest ( pt , item , col );
2018-10-04 16:43:10 +02:00
2019-04-30 16:59:40 +02:00
/* GetMainWindow() return window, associated with wxDataViewCtrl.
* And for this window we should to set tooltips.
* Just this->SetToolTip(tooltip) => has no effect.
*/
2019-05-20 13:52:58 +02:00
if ( ! item )
{
GetMainWindow () -> SetToolTip ( "" ); // hide tooltip
return ;
}
2018-10-12 12:00:37 +02:00
if ( col -> GetTitle () == " " && GetSelectedItemsCount () < 2 )
2018-10-08 16:27:38 +02:00
GetMainWindow () -> SetToolTip ( _ ( L ( "Right button click the icon to change the object settings" )));
2019-04-30 15:46:25 +02:00
else if ( col -> GetTitle () == _ ( "Name" ))
2019-04-24 13:33:05 +02:00
{
2019-04-30 15:46:25 +02:00
#ifdef __WXMSW__
if ( pt . x < 2 * wxGetApp (). em_unit () || pt . x > 4 * wxGetApp (). em_unit ()) {
GetMainWindow () -> SetToolTip ( "" ); // hide tooltip
return ;
2018-10-05 23:29:15 +02:00
}
2019-04-30 15:46:25 +02:00
#endif //__WXMSW__
2019-04-24 13:33:05 +02:00
int obj_idx , vol_idx ;
get_selected_item_indexes ( obj_idx , vol_idx , item );
GetMainWindow () -> SetToolTip ( get_mesh_errors_list ( obj_idx , vol_idx ));
2018-10-05 23:29:15 +02:00
}
2018-10-04 16:43:10 +02:00
else
2018-10-08 16:27:38 +02:00
GetMainWindow () -> SetToolTip ( "" ); // hide tooltip
2018-10-04 16:43:10 +02:00
}
2018-10-31 15:41:27 +01:00
wxPoint ObjectList :: get_mouse_position_in_control ()
{
2018-10-04 16:43:10 +02:00
const wxPoint & pt = wxGetMousePosition ();
2018-10-08 16:27:38 +02:00
// wxWindow* win = GetMainWindow();
// wxPoint screen_pos = win->GetScreenPosition();
return wxPoint ( pt . x - /*win->*/ GetScreenPosition (). x , pt . y - /*win->*/ GetScreenPosition (). y );
2018-10-04 16:43:10 +02:00
}
2018-10-12 12:00:37 +02:00
int ObjectList :: get_selected_obj_idx () const
{
2018-10-31 15:41:27 +01:00
if ( GetSelectedItemsCount () == 1 )
return m_objects_model -> GetIdByItem ( m_objects_model -> GetTopParent ( GetSelection ()));
2018-10-12 12:00:37 +02:00
return - 1 ;
}
2019-03-28 16:28:34 +01:00
DynamicPrintConfig & ObjectList :: get_item_config ( const wxDataViewItem & item ) const
{
assert ( item );
const ItemType type = m_objects_model -> GetItemType ( item );
const int obj_idx = type & itObject ? m_objects_model -> GetIdByItem ( item ) :
m_objects_model -> GetIdByItem ( m_objects_model -> GetTopParent ( item ));
const int vol_idx = type & itVolume ? m_objects_model -> GetVolumeIdByItem ( item ) : - 1 ;
assert ( obj_idx >= 0 || (( type & itVolume ) && vol_idx >= 0 ));
2019-05-23 15:12:19 +02:00
return type & itVolume ? ( * m_objects )[ obj_idx ] -> volumes [ vol_idx ] -> config :
( * m_objects )[ obj_idx ] -> config ;
2019-03-28 16:28:34 +01:00
}
2018-10-04 16:43:10 +02:00
wxDataViewColumn * ObjectList :: create_objects_list_extruder_column ( int extruders_count )
{
wxArrayString choices ;
2019-05-10 12:54:20 +02:00
choices . Add ( _ ( L ( "default" )));
2018-10-04 16:43:10 +02:00
for ( int i = 1 ; i <= extruders_count ; ++ i )
choices . Add ( wxString :: Format ( "%d" , i ));
wxDataViewChoiceRenderer * c =
new wxDataViewChoiceRenderer ( choices , wxDATAVIEW_CELL_EDITABLE , wxALIGN_CENTER_HORIZONTAL );
2019-02-06 11:07:32 +01:00
wxDataViewColumn * column = new wxDataViewColumn ( _ ( L ( "Extruder" )), c , 1 ,
8 * wxGetApp (). em_unit () /*80*/ , wxALIGN_CENTER_HORIZONTAL , wxDATAVIEW_COL_RESIZABLE );
2018-10-04 16:43:10 +02:00
return column ;
}
2018-12-10 17:00:28 +01:00
void ObjectList :: update_extruder_values_for_items ( const int max_extruder )
{
for ( int i = 0 ; i < m_objects -> size (); ++ i )
{
wxDataViewItem item = m_objects_model -> GetItemById ( i );
if ( ! item ) continue ;
auto object = ( * m_objects )[ i ];
wxString extruder ;
if ( ! object -> config . has ( "extruder" ) ||
object -> config . option < ConfigOptionInt > ( "extruder" ) -> value > max_extruder )
2019-05-10 12:54:20 +02:00
extruder = _ ( L ( "default" ));
2018-12-10 17:00:28 +01:00
else
extruder = wxString :: Format ( "%d" , object -> config . option < ConfigOptionInt > ( "extruder" ) -> value );
m_objects_model -> SetValue ( extruder , item , 1 );
if ( object -> volumes . size () > 1 ) {
for ( auto id = 0 ; id < object -> volumes . size (); id ++ ) {
item = m_objects_model -> GetItemByVolumeId ( i , id );
if ( ! item ) continue ;
if ( ! object -> volumes [ id ] -> config . has ( "extruder" ) ||
object -> volumes [ id ] -> config . option < ConfigOptionInt > ( "extruder" ) -> value > max_extruder )
2019-05-10 12:54:20 +02:00
extruder = _ ( L ( "default" ));
2018-12-10 17:00:28 +01:00
else
extruder = wxString :: Format ( "%d" , object -> volumes [ id ] -> config . option < ConfigOptionInt > ( "extruder" ) -> value );
m_objects_model -> SetValue ( extruder , item , 1 );
}
}
}
}
2018-10-04 16:43:10 +02:00
void ObjectList :: update_objects_list_extruder_column ( int extruders_count )
{
2018-10-08 16:27:38 +02:00
if ( ! this ) return ; // #ys_FIXME
2019-03-05 10:46:57 +01:00
if ( printer_technology () == ptSLA )
2018-10-04 16:43:10 +02:00
extruders_count = 1 ;
2018-12-10 17:00:28 +01:00
wxDataViewChoiceRenderer * ch_render = dynamic_cast < wxDataViewChoiceRenderer *> ( GetColumn ( 1 ) -> GetRenderer ());
if ( ch_render -> GetChoices (). GetCount () - 1 == extruders_count )
return ;
m_prevent_update_extruder_in_config = true ;
if ( m_objects && extruders_count > 1 )
update_extruder_values_for_items ( extruders_count );
2018-10-18 11:28:31 +02:00
// delete old 2nd column
DeleteColumn ( GetColumn ( 1 ));
2018-10-04 16:43:10 +02:00
// insert new created 3rd column
2018-10-18 11:28:31 +02:00
InsertColumn ( 1 , create_objects_list_extruder_column ( extruders_count ));
2018-10-04 16:43:10 +02:00
// set show/hide for this column
set_extruder_column_hidden ( extruders_count <= 1 );
2018-12-04 14:27:39 +01:00
//a workaround for a wrong last column width updating under OSX
GetColumn ( 2 ) -> SetWidth ( 25 );
2018-12-10 17:00:28 +01:00
m_prevent_update_extruder_in_config = false ;
2018-10-04 16:43:10 +02:00
}
2018-12-11 09:37:58 +01:00
void ObjectList :: set_extruder_column_hidden ( const bool hide ) const
2018-10-05 23:29:15 +02:00
{
2018-10-18 11:28:31 +02:00
GetColumn ( 1 ) -> SetHidden ( hide );
2018-10-05 23:29:15 +02:00
}
2018-10-04 16:43:10 +02:00
2018-12-10 17:00:28 +01:00
void ObjectList :: update_extruder_in_config ( const wxDataViewItem & item )
2018-10-05 23:29:15 +02:00
{
2018-12-10 17:00:28 +01:00
if ( m_prevent_update_extruder_in_config )
return ;
2018-12-10 14:13:01 +01:00
if ( m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 )) {
const int obj_idx = m_objects_model -> GetIdByItem ( item );
m_config = & ( * m_objects )[ obj_idx ] -> config ;
}
else {
const int obj_idx = m_objects_model -> GetIdByItem ( m_objects_model -> GetParent ( item ));
const int volume_id = m_objects_model -> GetVolumeIdByItem ( item );
if ( obj_idx < 0 || volume_id < 0 )
return ;
m_config = & ( * m_objects )[ obj_idx ] -> volumes [ volume_id ] -> config ;
}
wxVariant variant ;
m_objects_model -> GetValue ( variant , item , 1 );
const wxString selection = variant . GetString ();
2018-10-05 23:29:15 +02:00
if ( ! m_config || selection . empty ())
return ;
2018-12-10 14:13:01 +01:00
const int extruder = selection . size () > 1 ? 0 : atoi ( selection . c_str ());
2018-10-05 23:29:15 +02:00
m_config -> set_key_value ( "extruder" , new ConfigOptionInt ( extruder ));
2018-10-15 10:53:47 +02:00
// update scene
wxGetApp (). plater () -> update ();
2018-10-05 23:29:15 +02:00
}
2019-01-30 11:35:37 +01:00
void ObjectList :: update_name_in_model ( const wxDataViewItem & item ) const
2018-12-12 14:35:18 +01:00
{
const int obj_idx = m_objects_model -> GetObjectIdByItem ( item );
if ( obj_idx < 0 ) return ;
if ( m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 )) {
2019-01-03 14:34:53 +01:00
( * m_objects )[ obj_idx ] -> name = m_objects_model -> GetName ( item ). ToUTF8 (). data ();
2018-12-12 14:35:18 +01:00
return ;
}
const int volume_id = m_objects_model -> GetVolumeIdByItem ( item );
if ( volume_id < 0 ) return ;
2019-01-03 14:34:53 +01:00
( * m_objects )[ obj_idx ] -> volumes [ volume_id ] -> name = m_objects_model -> GetName ( item ). ToUTF8 (). data ();
2018-12-12 14:35:18 +01:00
}
2018-10-31 12:56:08 +01:00
void ObjectList :: init_icons ()
{
2019-05-09 14:53:01 +02:00
m_bmp_solidmesh = ScalableBitmap ( nullptr , ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: MODEL_PART ) ]. second );
m_bmp_modifiermesh = ScalableBitmap ( nullptr , ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: PARAMETER_MODIFIER )]. second );
m_bmp_support_enforcer = ScalableBitmap ( nullptr , ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: SUPPORT_ENFORCER ) ]. second );
m_bmp_support_blocker = ScalableBitmap ( nullptr , ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: SUPPORT_BLOCKER ) ]. second );
2018-11-02 12:35:26 +01:00
m_bmp_vector . reserve ( 4 ); // bitmaps for different types of parts
2019-04-16 10:05:45 +02:00
m_bmp_vector . push_back ( & m_bmp_solidmesh . bmp ());
m_bmp_vector . push_back ( & m_bmp_modifiermesh . bmp ());
m_bmp_vector . push_back ( & m_bmp_support_enforcer . bmp ());
m_bmp_vector . push_back ( & m_bmp_support_blocker . bmp ());
// Set volumes default bitmaps for the model
2018-11-02 23:27:31 +01:00
m_objects_model -> SetVolumeBitmaps ( m_bmp_vector );
2018-10-05 23:29:15 +02:00
// init icon for manifold warning
2019-04-25 01:45:00 +02:00
m_bmp_manifold_warning = ScalableBitmap ( nullptr , "exclamation" );
2019-04-29 15:27:59 +02:00
// Set warning bitmap for the model
m_objects_model -> SetWarningBitmap ( & m_bmp_manifold_warning . bmp ());
2018-10-05 23:29:15 +02:00
// init bitmap for "Add Settings" context menu
2019-04-25 01:45:00 +02:00
m_bmp_cog = ScalableBitmap ( nullptr , "cog" );
2019-04-16 10:05:45 +02:00
}
2019-05-09 14:53:01 +02:00
void ObjectList :: msw_rescale_icons ()
2019-04-16 10:05:45 +02:00
{
m_bmp_vector . clear ();
m_bmp_vector . reserve ( 4 ); // bitmaps for different types of parts
2019-05-09 14:53:01 +02:00
for ( ScalableBitmap * bitmap : { & m_bmp_solidmesh , // Add part
2019-04-29 15:27:59 +02:00
& m_bmp_modifiermesh , // Add modifier
2019-04-16 10:05:45 +02:00
& m_bmp_support_enforcer , // Add support enforcer
& m_bmp_support_blocker }) // Add support blocker
{
2019-04-25 01:45:00 +02:00
bitmap -> msw_rescale ();
2019-04-16 10:05:45 +02:00
m_bmp_vector . push_back ( & bitmap -> bmp ());
}
// Set volumes default bitmaps for the model
m_objects_model -> SetVolumeBitmaps ( m_bmp_vector );
2019-04-25 01:45:00 +02:00
m_bmp_manifold_warning . msw_rescale ();
2019-04-29 15:27:59 +02:00
// Set warning bitmap for the model
m_objects_model -> SetWarningBitmap ( & m_bmp_manifold_warning . bmp ());
2019-04-25 01:45:00 +02:00
m_bmp_cog . msw_rescale ();
2019-04-16 10:05:45 +02:00
// Update CATEGORY_ICON according to new scale
{
// Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget,
// see note in PresetBundle::load_compatible_bitmaps()
// ptFFF
CATEGORY_ICON [ L ( "Layers and Perimeters" )] = create_scaled_bitmap ( nullptr , "layers" );
CATEGORY_ICON [ L ( "Infill" )] = create_scaled_bitmap ( nullptr , "infill" );
CATEGORY_ICON [ L ( "Support material" )] = create_scaled_bitmap ( nullptr , "support" );
CATEGORY_ICON [ L ( "Speed" )] = create_scaled_bitmap ( nullptr , "time" );
CATEGORY_ICON [ L ( "Extruders" )] = create_scaled_bitmap ( nullptr , "funnel" );
CATEGORY_ICON [ L ( "Extrusion Width" )] = create_scaled_bitmap ( nullptr , "funnel" );
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim");
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time");
CATEGORY_ICON [ L ( "Advanced" )] = create_scaled_bitmap ( nullptr , "wrench" );
// ptSLA
CATEGORY_ICON [ L ( "Supports" )] = create_scaled_bitmap ( nullptr , "support" /*"sla_supports"*/ );
CATEGORY_ICON [ L ( "Pad" )] = create_scaled_bitmap ( nullptr , "pad" );
}
2018-10-05 23:29:15 +02:00
}
2018-10-11 15:57:09 +02:00
void ObjectList :: selection_changed ()
2018-10-05 23:29:15 +02:00
{
if ( m_prevent_list_events ) return ;
2018-10-11 15:57:09 +02:00
fix_multiselection_conflicts ();
// update object selection on Plater
2019-03-04 09:41:25 +01:00
if ( ! m_prevent_canvas_selection_update )
update_selections_on_canvas ();
2018-10-11 15:57:09 +02:00
2018-11-01 12:33:56 +01:00
// to update the toolbar and info sizer
if ( ! GetSelection () || m_objects_model -> GetItemType ( GetSelection ()) == itObject ) {
auto event = SimpleEvent ( EVT_OBJ_LIST_OBJECT_SELECT );
event . SetEventObject ( this );
wxPostEvent ( this , event );
}
2018-10-05 23:29:15 +02:00
part_selection_changed ();
}
2019-04-10 08:40:58 +02:00
void ObjectList :: paste_volumes_into_list ( int obj_idx , const ModelVolumePtrs & volumes )
{
if (( obj_idx < 0 ) || (( int ) m_objects -> size () <= obj_idx ))
return ;
if ( volumes . empty ())
return ;
ModelObject & model_object = * ( * m_objects )[ obj_idx ];
const auto object_item = m_objects_model -> GetItemById ( obj_idx );
wxDataViewItemArray items ;
for ( const ModelVolume * volume : volumes )
{
2019-05-15 17:16:31 +02:00
const wxDataViewItem & vol_item = m_objects_model -> AddVolumeChild ( object_item , wxString :: FromUTF8 ( volume -> name . c_str ()), volume -> type (),
2019-04-29 15:27:59 +02:00
volume -> get_mesh_errors_count () > 0 ,
2019-04-10 09:27:42 +02:00
volume -> config . has ( "extruder" ) ? volume -> config . option < ConfigOptionInt > ( "extruder" ) -> value : 0 );
2019-04-10 08:40:58 +02:00
auto opt_keys = volume -> config . keys ();
if ( ! opt_keys . empty () && ! (( opt_keys . size () == 1 ) && ( opt_keys [ 0 ] == "extruder" )))
select_item ( m_objects_model -> AddSettingsChild ( vol_item ));
items . Add ( vol_item );
}
2019-04-22 01:51:10 +02:00
changed_object ( obj_idx );
2019-04-10 08:40:58 +02:00
2019-04-12 11:28:24 +02:00
if ( items . size () > 1 )
{
m_selection_mode = smVolume ;
m_last_selected_item = wxDataViewItem ( 0 );
}
2019-04-10 08:40:58 +02:00
select_items ( items );
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed ();
#endif //no __WXOSX__ //__WXMSW__
}
2019-04-10 15:55:32 +02:00
void ObjectList :: paste_objects_into_list ( const std :: vector < size_t >& object_idxs )
2019-04-10 08:40:58 +02:00
{
2019-04-10 15:55:32 +02:00
if ( object_idxs . empty ())
return ;
wxDataViewItemArray items ;
for ( const size_t object : object_idxs )
{
add_object_to_list ( object );
items . Add ( m_objects_model -> GetItemById ( object ));
}
2019-04-15 15:40:40 +02:00
wxGetApp (). plater () -> changed_objects ( object_idxs );
2019-04-10 15:55:32 +02:00
select_items ( items );
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed ();
#endif //no __WXOSX__ //__WXMSW__
2019-04-10 08:40:58 +02:00
}
2019-05-16 10:37:06 +02:00
#ifdef __WXOSX__
/*
2018-12-11 09:37:58 +01:00
void ObjectList::OnChar(wxKeyEvent& event)
{
2018-12-12 08:40:10 +01:00
if (event.GetKeyCode() == WXK_BACK){
2018-12-11 09:37:58 +01:00
remove();
}
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_SHIFT))
select_item_all_children();
2018-12-12 08:40:10 +01:00
event.Skip();
2018-12-11 09:37:58 +01:00
}
2019-05-16 10:37:06 +02:00
*/
#endif /* __WXOSX__ */
2018-12-11 09:37:58 +01:00
void ObjectList :: OnContextMenu ( wxDataViewEvent & )
2018-10-05 23:29:15 +02:00
{
wxDataViewItem item ;
wxDataViewColumn * col ;
const wxPoint pt = get_mouse_position_in_control ();
2018-10-08 16:27:38 +02:00
HitTest ( pt , item , col );
2018-10-05 23:29:15 +02:00
if ( ! item )
#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX
// after Yosemite OS X version, HitTest return undefined item
2018-10-08 16:27:38 +02:00
item = GetSelection ();
2018-10-05 23:29:15 +02:00
if ( item )
show_context_menu ();
else
printf ( "undefined item \n " );
return ;
#else
return ;
#endif // __WXOSX__
const wxString title = col -> GetTitle ();
if ( title == " " )
show_context_menu ();
2019-04-29 15:27:59 +02:00
else if ( title == _ ( "Name" ))
2018-12-12 13:18:38 +01:00
{
2019-04-24 13:33:05 +02:00
int obj_idx , vol_idx ;
get_selected_item_indexes ( obj_idx , vol_idx , item );
2019-04-29 15:27:59 +02:00
if ( is_windows10 () && get_mesh_errors_count ( obj_idx , vol_idx ) > 0 &&
pt . x > 2 * wxGetApp (). em_unit () && pt . x < 4 * wxGetApp (). em_unit () )
2019-03-13 15:34:27 +01:00
fix_through_netfabb ();
2018-12-12 13:18:38 +01:00
}
2019-03-14 13:15:04 +01:00
2018-10-05 23:29:15 +02:00
#ifndef __WXMSW__
2018-10-08 16:27:38 +02:00
GetMainWindow () -> SetToolTip ( "" ); // hide tooltip
2018-10-05 23:29:15 +02:00
#endif //__WXMSW__
}
void ObjectList :: show_context_menu ()
{
2019-03-14 13:15:04 +01:00
if ( multiple_selection ())
2019-01-23 16:01:37 +01:00
{
2019-03-14 13:15:04 +01:00
if ( selected_instances_of_same_object ())
wxGetApp (). plater () -> PopupMenu ( & m_menu_instance );
else
2019-03-21 09:04:11 +01:00
show_multi_selection_menu ();
2019-03-14 13:15:04 +01:00
2019-01-23 16:01:37 +01:00
return ;
}
2018-10-08 16:27:38 +02:00
const auto item = GetSelection ();
2018-10-05 23:29:15 +02:00
if ( item )
{
2019-01-23 16:01:37 +01:00
const ItemType type = m_objects_model -> GetItemType ( item );
if ( ! ( type & ( itObject | itVolume | itInstance )))
2018-10-05 23:29:15 +02:00
return ;
2019-01-23 16:01:37 +01:00
wxMenu * menu = type & itInstance ? & m_menu_instance :
m_objects_model -> GetParent ( item ) != wxDataViewItem ( 0 ) ? & m_menu_part :
2019-03-05 10:46:57 +01:00
printer_technology () == ptFFF ? & m_menu_object : & m_menu_sla_object ;
2018-12-06 14:49:14 +01:00
2019-01-23 16:01:37 +01:00
if ( ! ( type & itInstance ))
append_menu_item_settings ( menu );
2018-12-07 17:50:48 +01:00
2018-12-06 14:49:14 +01:00
wxGetApp (). plater () -> PopupMenu ( menu );
2018-10-05 23:29:15 +02:00
}
}
2019-05-16 10:37:06 +02:00
#ifndef __WXOSX__
2018-10-11 15:57:09 +02:00
void ObjectList :: key_event ( wxKeyEvent & event )
2018-10-05 23:29:15 +02:00
{
if ( event . GetKeyCode () == WXK_TAB )
2018-10-08 16:27:38 +02:00
Navigate ( event . ShiftDown () ? wxNavigationKeyEvent :: IsBackward : wxNavigationKeyEvent :: IsForward );
2018-10-05 23:29:15 +02:00
else if ( event . GetKeyCode () == WXK_DELETE
#ifdef __WXOSX__
|| event . GetKeyCode () == WXK_BACK
#endif //__WXOSX__
2018-10-31 12:56:08 +01:00
) {
2018-10-05 23:29:15 +02:00
remove ();
}
2019-04-04 15:07:54 +02:00
else if ( wxGetKeyState ( wxKeyCode ( 'A' )) && wxGetKeyState ( WXK_CONTROL /*WXK_SHIFT*/ ))
2018-11-08 15:45:55 +01:00
select_item_all_children ();
2019-04-16 13:47:37 +02:00
else if ( wxGetKeyState ( wxKeyCode ( 'C' )) && wxGetKeyState ( WXK_CONTROL ))
wxPostEvent (( wxEvtHandler * ) wxGetApp (). plater () -> canvas3D () -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_COPY ));
else if ( wxGetKeyState ( wxKeyCode ( 'V' )) && wxGetKeyState ( WXK_CONTROL ))
wxPostEvent (( wxEvtHandler * ) wxGetApp (). plater () -> canvas3D () -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_PASTE ));
2018-10-05 23:29:15 +02:00
else
event . Skip ();
}
2019-05-16 10:37:06 +02:00
#endif /* __WXOSX__ */
2018-10-05 23:29:15 +02:00
2018-12-11 09:37:58 +01:00
void ObjectList :: OnBeginDrag ( wxDataViewEvent & event )
2018-10-05 23:29:15 +02:00
{
2018-12-12 10:34:34 +01:00
const wxDataViewItem item ( event . GetItem ());
2018-10-05 23:29:15 +02:00
2019-01-23 16:01:37 +01:00
const bool mult_sel = multiple_selection ();
if ( mult_sel && ! selected_instances_of_same_object () ||
! mult_sel && ( GetSelection () != item ||
m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 ) ) ) {
2019-01-22 16:40:10 +01:00
event . Veto ();
return ;
}
const ItemType & type = m_objects_model -> GetItemType ( item );
if ( ! ( type & ( itVolume | itInstance ))) {
2018-10-05 23:29:15 +02:00
event . Veto ();
return ;
}
2019-01-23 16:01:37 +01:00
if ( mult_sel )
{
m_dragged_data . init ( m_objects_model -> GetObjectIdByItem ( item ), type );
std :: set < int >& sub_obj_idxs = m_dragged_data . inst_idxs ();
wxDataViewItemArray sels ;
GetSelections ( sels );
for ( auto sel : sels )
sub_obj_idxs . insert ( m_objects_model -> GetInstanceIdByItem ( sel ));
}
else
m_dragged_data . init ( m_objects_model -> GetObjectIdByItem ( item ),
2019-01-22 16:40:10 +01:00
type & itVolume ? m_objects_model -> GetVolumeIdByItem ( item ) :
m_objects_model -> GetInstanceIdByItem ( item ),
type );
2018-12-13 13:19:11 +01:00
2018-10-05 23:29:15 +02:00
/* Under MSW or OSX, DnD moves an item to the place of another selected item
* But under GTK, DnD moves an item between another two items.
* And as a result - call EVT_CHANGE_SELECTION to unselect all items.
2018-12-11 09:37:58 +01:00
* To prevent such behavior use m_prevent_list_events
2018-10-05 23:29:15 +02:00
**/
m_prevent_list_events = true ; //it's needed for GTK
2018-12-13 13:19:11 +01:00
/* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid value,
* so set some nonempty string
*/
wxTextDataObject * obj = new wxTextDataObject ;
obj -> SetText ( "Some text" ); //it's needed for GTK
2018-12-12 08:40:10 +01:00
2018-12-13 13:19:11 +01:00
event . SetDataObject ( obj );
2018-12-12 08:40:10 +01:00
event . SetDragFlags ( wxDrag_DefaultMove ); // allows both copy and move;
2018-10-05 23:29:15 +02:00
}
2019-01-22 16:40:10 +01:00
bool ObjectList :: can_drop ( const wxDataViewItem & item ) const
{
return m_dragged_data . type () == itInstance && ! item . IsOk () ||
m_dragged_data . type () == itVolume && item . IsOk () &&
m_objects_model -> GetItemType ( item ) == itVolume &&
m_dragged_data . obj_idx () == m_objects_model -> GetObjectIdByItem ( item );
}
2018-12-11 09:37:58 +01:00
void ObjectList :: OnDropPossible ( wxDataViewEvent & event )
2018-10-05 23:29:15 +02:00
{
2019-01-22 16:40:10 +01:00
const wxDataViewItem & item = event . GetItem ();
2018-10-05 23:29:15 +02:00
2019-01-22 16:40:10 +01:00
if ( ! can_drop ( item ))
2018-10-05 23:29:15 +02:00
event . Veto ();
}
2018-12-11 09:37:58 +01:00
void ObjectList :: OnDrop ( wxDataViewEvent & event )
2018-10-05 23:29:15 +02:00
{
2019-01-22 16:40:10 +01:00
const wxDataViewItem & item = event . GetItem ();
2018-10-05 23:29:15 +02:00
2019-01-22 16:40:10 +01:00
if ( ! can_drop ( item ))
{
2018-10-05 23:29:15 +02:00
event . Veto ();
2018-12-12 08:40:10 +01:00
m_dragged_data . clear ();
2018-10-05 23:29:15 +02:00
return ;
}
2019-01-22 16:40:10 +01:00
if ( m_dragged_data . type () == itInstance )
{
2019-01-23 16:01:37 +01:00
instances_to_separated_object ( m_dragged_data . obj_idx (), m_dragged_data . inst_idxs ());
2019-01-22 16:40:10 +01:00
m_dragged_data . clear ();
return ;
}
const int from_volume_id = m_dragged_data . sub_obj_idx ();
2018-10-05 23:29:15 +02:00
int to_volume_id = m_objects_model -> GetVolumeIdByItem ( item );
2018-12-13 13:19:11 +01:00
// It looks like a fixed in current version of the wxWidgets
// #ifdef __WXGTK__
// /* Under GTK, DnD moves an item between another two items.
// * And event.GetItem() return item, which is under "insertion line"
// * So, if we move item down we should to decrease the to_volume_id value
// **/
// if (to_volume_id > from_volume_id) to_volume_id--;
// #endif // __WXGTK__
2018-10-05 23:29:15 +02:00
2019-01-22 16:40:10 +01:00
auto & volumes = ( * m_objects )[ m_dragged_data . obj_idx ()] -> volumes ;
2018-10-05 23:29:15 +02:00
auto delta = to_volume_id < from_volume_id ? - 1 : 1 ;
int cnt = 0 ;
for ( int id = from_volume_id ; cnt < abs ( from_volume_id - to_volume_id ); id += delta , cnt ++ )
std :: swap ( volumes [ id ], volumes [ id + delta ]);
2018-10-11 15:57:09 +02:00
select_item ( m_objects_model -> ReorganizeChildren ( from_volume_id , to_volume_id ,
m_objects_model -> GetParent ( item )));
2019-04-22 01:51:10 +02:00
changed_object ( m_dragged_data . obj_idx ());
2018-10-05 23:29:15 +02:00
2018-12-12 08:40:10 +01:00
m_dragged_data . clear ();
2018-10-05 23:29:15 +02:00
}
// Context Menu
2019-01-28 12:13:53 +01:00
std :: vector < std :: string > ObjectList :: get_options ( const bool is_part )
2018-10-05 23:29:15 +02:00
{
2019-03-05 10:46:57 +01:00
if ( printer_technology () == ptSLA ) {
2018-11-22 10:33:16 +01:00
SLAPrintObjectConfig full_sla_config ;
auto options = full_sla_config . keys ();
options . erase ( find ( options . begin (), options . end (), "layer_height" ));
return options ;
}
2018-10-05 23:29:15 +02:00
PrintRegionConfig reg_config ;
auto options = reg_config . keys ();
if ( ! is_part ) {
PrintObjectConfig obj_config ;
std :: vector < std :: string > obj_options = obj_config . keys ();
options . insert ( options . end (), obj_options . begin (), obj_options . end ());
}
return options ;
}
2019-01-25 16:39:50 +01:00
2019-01-28 12:13:53 +01:00
const std :: vector < std :: string >& ObjectList :: get_options_for_bundle ( const wxString & bundle_name )
2019-01-25 16:39:50 +01:00
{
2019-03-05 10:46:57 +01:00
const FreqSettingsBundle & bundle = printer_technology () == ptSLA ?
2019-01-25 16:39:50 +01:00
FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF ;
for ( auto & it : bundle )
{
if ( bundle_name == _ ( it . first ))
return it . second ;
}
2019-01-28 12:13:53 +01:00
#if 0
// if "Quick menu" is selected
2019-03-05 10:46:57 +01:00
FreqSettingsBundle& bundle_quick = printer_technology() == ptSLA ?
2019-01-28 12:13:53 +01:00
m_freq_settings_sla: m_freq_settings_fff;
for (auto& it : bundle_quick)
{
if ( bundle_name == wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)) )
return it.second;
}
#endif
2019-01-26 18:51:34 +01:00
static std :: vector < std :: string > empty ;
return empty ;
2019-01-25 16:39:50 +01:00
}
2018-12-06 14:49:14 +01:00
2019-01-28 12:13:53 +01:00
void ObjectList :: get_options_menu ( settings_menu_hierarchy & settings_menu , const bool is_part )
2018-10-05 23:29:15 +02:00
{
2019-01-28 12:13:53 +01:00
auto options = get_options ( is_part );
2018-10-05 23:29:15 +02:00
2019-03-14 13:15:04 +01:00
const int extruders_cnt = extruders_count ();
2018-10-05 23:29:15 +02:00
DynamicPrintConfig config ;
for ( auto & option : options )
{
auto const opt = config . def () -> get ( option );
auto category = opt -> category ;
if ( category . empty () ||
( category == "Extruders" && extruders_cnt == 1 )) continue ;
2019-03-14 09:19:21 +01:00
const std :: string & label = ! opt -> full_label . empty () ? opt -> full_label : opt -> label ;
2018-11-22 10:33:16 +01:00
std :: pair < std :: string , std :: string > option_label ( option , label );
2018-10-05 23:29:15 +02:00
std :: vector < std :: pair < std :: string , std :: string > > new_category ;
auto & cat_opt_label = settings_menu . find ( category ) == settings_menu . end () ? new_category : settings_menu . at ( category );
cat_opt_label . push_back ( option_label );
if ( cat_opt_label . size () == 1 )
settings_menu [ category ] = cat_opt_label ;
}
}
2018-12-07 17:50:48 +01:00
void ObjectList :: get_settings_choice ( const wxString & category_name )
2018-12-06 14:49:14 +01:00
{
2018-10-05 23:29:15 +02:00
wxArrayString names ;
wxArrayInt selections ;
settings_menu_hierarchy settings_menu ;
2018-12-07 17:50:48 +01:00
const bool is_part = m_objects_model -> GetParent ( GetSelection ()) != wxDataViewItem ( 0 );
2018-10-05 23:29:15 +02:00
get_options_menu ( settings_menu , is_part );
std :: vector < std :: pair < std :: string , std :: string > > * settings_list = nullptr ;
auto opt_keys = m_config -> keys ();
for ( auto & cat : settings_menu )
{
if ( _ ( cat . first ) == category_name ) {
int sel = 0 ;
for ( auto & pair : cat . second ) {
names . Add ( _ ( pair . second ));
if ( find ( opt_keys . begin (), opt_keys . end (), pair . first ) != opt_keys . end ())
selections . Add ( sel );
sel ++ ;
}
settings_list = & cat . second ;
break ;
}
}
if ( ! settings_list )
return ;
if ( wxGetSelectedChoices ( selections , _ ( L ( "Select showing settings" )), category_name , names ) == - 1 )
return ;
2019-01-28 12:13:53 +01:00
const int selection_cnt = selections . size ();
#if 0
if (selection_cnt > 0)
{
// Add selected items to the "Quick menu"
2019-03-05 10:46:57 +01:00
FreqSettingsBundle& freq_settings = printer_technology() == ptSLA ?
2019-01-28 12:13:53 +01:00
m_freq_settings_sla : m_freq_settings_fff;
bool changed_existing = false;
std::vector<std::string> tmp_freq_cat = {};
for (auto& cat : freq_settings)
{
if (_(cat.first) == category_name)
{
std::vector<std::string>& freq_settings_category = cat.second;
freq_settings_category.clear();
freq_settings_category.reserve(selection_cnt);
for (auto sel : selections)
freq_settings_category.push_back((*settings_list)[sel].first);
changed_existing = true;
break;
}
}
if (!changed_existing)
{
// Create new "Quick menu" item
for (auto& cat : settings_menu)
{
if (_(cat.first) == category_name)
{
freq_settings[cat.first] = std::vector<std::string> {};
std::vector<std::string>& freq_settings_category = freq_settings.find(cat.first)->second;
freq_settings_category.reserve(selection_cnt);
for (auto sel : selections)
freq_settings_category.push_back((*settings_list)[sel].first);
break;
}
}
}
}
#endif
2018-10-05 23:29:15 +02:00
std :: vector < std :: string > selected_options ;
2019-01-28 12:13:53 +01:00
selected_options . reserve ( selection_cnt );
2018-10-05 23:29:15 +02:00
for ( auto sel : selections )
selected_options . push_back (( * settings_list )[ sel ]. first );
2019-03-05 10:46:57 +01:00
const DynamicPrintConfig & from_config = printer_technology () == ptFFF ?
wxGetApp (). preset_bundle -> prints . get_edited_preset (). config :
wxGetApp (). preset_bundle -> sla_prints . get_edited_preset (). config ;
2019-01-28 12:13:53 +01:00
2018-10-05 23:29:15 +02:00
for ( auto & setting : ( * settings_list ))
{
auto & opt_key = setting . first ;
if ( find ( opt_keys . begin (), opt_keys . end (), opt_key ) != opt_keys . end () &&
find ( selected_options . begin (), selected_options . end (), opt_key ) == selected_options . end ())
m_config -> erase ( opt_key );
if ( find ( opt_keys . begin (), opt_keys . end (), opt_key ) == opt_keys . end () &&
2019-01-28 12:13:53 +01:00
find ( selected_options . begin (), selected_options . end (), opt_key ) != selected_options . end ()) {
const ConfigOption * option = from_config . option ( opt_key );
if ( ! option ) {
// if current option doesn't exist in prints.get_edited_preset(),
2019-01-30 14:45:18 +01:00
// get it from default config values
option = DynamicPrintConfig :: new_from_defaults_keys ({ opt_key }) -> option ( opt_key );
2019-01-28 12:13:53 +01:00
}
m_config -> set_key_value ( opt_key , option -> clone ());
}
2018-10-05 23:29:15 +02:00
}
// Add settings item for object
2019-01-25 16:39:50 +01:00
update_settings_item ();
}
void ObjectList :: get_freq_settings_choice ( const wxString & bundle_name )
{
const std :: vector < std :: string >& options = get_options_for_bundle ( bundle_name );
2019-05-03 16:05:49 +02:00
assert ( m_config );
2019-01-25 16:39:50 +01:00
auto opt_keys = m_config -> keys ();
2019-01-28 12:13:53 +01:00
const DynamicPrintConfig & from_config = wxGetApp (). preset_bundle -> prints . get_edited_preset (). config ;
2019-01-25 16:39:50 +01:00
for ( auto & opt_key : options )
{
2019-01-28 12:13:53 +01:00
if ( find ( opt_keys . begin (), opt_keys . end (), opt_key ) == opt_keys . end ()) {
const ConfigOption * option = from_config . option ( opt_key );
if ( ! option ) {
// if current option doesn't exist in prints.get_edited_preset(),
2019-01-30 14:45:18 +01:00
// get it from default config values
option = DynamicPrintConfig :: new_from_defaults_keys ({ opt_key }) -> option ( opt_key );
2019-01-28 12:13:53 +01:00
}
m_config -> set_key_value ( opt_key , option -> clone ());
}
2019-01-25 16:39:50 +01:00
}
// Add settings item for object
update_settings_item ();
}
void ObjectList :: update_settings_item ()
{
2019-01-09 10:30:29 +01:00
auto item = GetSelection ();
2018-10-05 23:29:15 +02:00
if ( item ) {
2019-01-09 10:30:29 +01:00
if ( m_objects_model -> GetItemType ( item ) == itInstance )
item = m_objects_model -> GetTopParent ( item );
2019-01-07 10:32:33 +01:00
const auto settings_item = m_objects_model -> IsSettingsItem ( item ) ? item : m_objects_model -> GetSettingsItem ( item );
2018-10-11 15:57:09 +02:00
select_item ( settings_item ? settings_item :
2018-10-05 23:29:15 +02:00
m_objects_model -> AddSettingsChild ( item ));
2019-05-03 14:29:28 +02:00
// update object selection on Plater
if ( ! m_prevent_canvas_selection_update )
update_selections_on_canvas ();
2018-10-05 23:29:15 +02:00
}
2018-11-09 18:39:07 +01:00
else {
auto panel = wxGetApp (). sidebar (). scrolled_panel ();
panel -> Freeze ();
2019-01-25 16:39:50 +01:00
wxGetApp (). obj_settings () -> UpdateAndShow ( true );
2018-11-09 18:39:07 +01:00
panel -> Thaw ();
}
2018-10-05 23:29:15 +02:00
}
2019-05-09 14:53:01 +02:00
wxMenu * ObjectList :: append_submenu_add_generic ( wxMenu * menu , const ModelVolumeType type ) {
2018-10-05 23:29:15 +02:00
auto sub_menu = new wxMenu ;
2019-01-25 10:34:32 +01:00
if ( wxGetApp (). get_mode () == comExpert ) {
2018-12-06 14:49:14 +01:00
append_menu_item ( sub_menu , wxID_ANY , _ ( L ( "Load" )) + " " + dots , "" ,
2019-05-09 14:53:01 +02:00
[ this , type ]( wxCommandEvent & ) { load_subobject ( type ); }, "" , menu );
2018-11-06 15:34:44 +01:00
sub_menu -> AppendSeparator ();
2019-01-25 10:34:32 +01:00
}
2018-11-06 15:34:44 +01:00
2019-02-22 12:12:10 +01:00
for ( auto & item : { L ( "Box" ), L ( "Cylinder" ), L ( "Sphere" ), L ( "Slab" ) }) {
2018-12-06 14:49:14 +01:00
append_menu_item ( sub_menu , wxID_ANY , _ ( item ), "" ,
2019-05-09 14:53:01 +02:00
[ this , type , item ]( wxCommandEvent & ) { load_generic_subobject ( item , type ); }, "" , menu );
2018-12-06 14:49:14 +01:00
}
2018-10-05 23:29:15 +02:00
2019-05-09 14:53:01 +02:00
return sub_menu ;
2018-10-05 23:29:15 +02:00
}
2019-01-25 13:16:32 +01:00
void ObjectList :: append_menu_items_add_volume ( wxMenu * menu )
2018-10-05 23:29:15 +02:00
{
2018-12-07 17:50:48 +01:00
// Update "add" items(delete old & create new) settings popupmenu
2019-05-09 14:53:01 +02:00
for ( auto & item : ADD_VOLUME_MENU_ITEMS ){
const auto settings_id = menu -> FindItem ( _ ( item . first ));
2018-12-07 17:50:48 +01:00
if ( settings_id != wxNOT_FOUND )
menu -> Destroy ( settings_id );
}
2019-01-25 10:34:32 +01:00
const ConfigOptionMode mode = wxGetApp (). get_mode ();
2019-05-09 14:53:01 +02:00
if ( mode == comAdvanced ) {
append_menu_item ( menu , wxID_ANY , _ ( ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: MODEL_PART )]. first ), "" ,
[ this ]( wxCommandEvent & ) { load_subobject ( ModelVolumeType :: MODEL_PART ); }, ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: MODEL_PART )]. second );
2019-01-25 10:34:32 +01:00
}
if ( mode == comSimple ) {
2019-05-09 14:53:01 +02:00
append_menu_item ( menu , wxID_ANY , _ ( ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: SUPPORT_ENFORCER )]. first ), "" ,
2019-02-22 12:12:10 +01:00
[ this ]( wxCommandEvent & ) { load_generic_subobject ( L ( "Box" ), ModelVolumeType :: SUPPORT_ENFORCER ); },
2019-05-09 14:53:01 +02:00
ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: SUPPORT_ENFORCER )]. second );
append_menu_item ( menu , wxID_ANY , _ ( ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: SUPPORT_BLOCKER )]. first ), "" ,
2019-02-22 12:12:10 +01:00
[ this ]( wxCommandEvent & ) { load_generic_subobject ( L ( "Box" ), ModelVolumeType :: SUPPORT_BLOCKER ); },
2019-05-09 14:53:01 +02:00
ADD_VOLUME_MENU_ITEMS [ int ( ModelVolumeType :: SUPPORT_BLOCKER )]. second );
2018-12-07 17:50:48 +01:00
return ;
}
2019-01-25 10:34:32 +01:00
2019-05-09 14:53:01 +02:00
for ( int type = mode == comExpert ? 0 : 1 ; type < ADD_VOLUME_MENU_ITEMS . size (); type ++ )
2018-12-07 17:50:48 +01:00
{
2019-05-09 14:53:01 +02:00
auto & item = ADD_VOLUME_MENU_ITEMS [ type ];
2018-12-07 17:50:48 +01:00
2019-05-09 14:53:01 +02:00
wxMenu * sub_menu = append_submenu_add_generic ( menu , ModelVolumeType ( type ));
append_submenu ( menu , sub_menu , wxID_ANY , _ ( item . first ), "" , item . second );
2018-10-05 23:29:15 +02:00
}
2018-12-07 17:50:48 +01:00
}
wxMenuItem * ObjectList :: append_menu_item_split ( wxMenu * menu )
{
return append_menu_item ( menu , wxID_ANY , _ ( L ( "Split to parts" )), "" ,
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { split (); }, "split_parts_SMALL" , menu ,
[ this ]() { return is_splittable (); }, wxGetApp (). plater ());
2018-12-07 17:50:48 +01:00
}
2019-01-25 13:16:32 +01:00
wxMenuItem * ObjectList :: append_menu_item_settings ( wxMenu * menu_ )
2018-12-07 17:50:48 +01:00
{
2019-04-25 01:45:00 +02:00
MenuWithSeparators * menu = dynamic_cast < MenuWithSeparators *> ( menu_ );
2019-03-28 16:28:34 +01:00
const wxString menu_name = _ ( L ( "Add settings" ));
2019-01-25 16:39:50 +01:00
// Delete old items from settings popupmenu
2019-03-28 16:28:34 +01:00
auto settings_id = menu -> FindItem ( menu_name );
2018-12-07 17:50:48 +01:00
if ( settings_id != wxNOT_FOUND )
menu -> Destroy ( settings_id );
2019-01-25 16:39:50 +01:00
for ( auto & it : FREQ_SETTINGS_BUNDLE_FFF )
{
settings_id = menu -> FindItem ( _ ( it . first ));
if ( settings_id != wxNOT_FOUND )
menu -> Destroy ( settings_id );
}
for ( auto & it : FREQ_SETTINGS_BUNDLE_SLA )
{
settings_id = menu -> FindItem ( _ ( it . first ));
if ( settings_id != wxNOT_FOUND )
menu -> Destroy ( settings_id );
}
2019-01-28 12:13:53 +01:00
#if 0
for (auto& it : m_freq_settings_fff)
{
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)));
if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id);
}
for (auto& it : m_freq_settings_sla)
{
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)));
if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id);
}
#endif
2019-01-25 13:16:32 +01:00
menu -> DestroySeparators (); // delete old separators
2018-12-07 17:50:48 +01:00
const auto sel_vol = get_selected_model_volume ();
2019-02-22 12:12:10 +01:00
if ( sel_vol && sel_vol -> type () >= ModelVolumeType :: SUPPORT_ENFORCER )
2019-01-25 13:16:32 +01:00
return nullptr ;
const ConfigOptionMode mode = wxGetApp (). get_mode ();
if ( mode == comSimple )
return nullptr ;
2019-01-25 16:39:50 +01:00
// Create new items for settings popupmenu
2019-03-07 12:09:23 +01:00
if ( printer_technology () == ptFFF ||
menu -> GetMenuItems (). size () > 0 && ! menu -> GetMenuItems (). back () -> IsSeparator ())
2019-04-25 01:45:00 +02:00
menu -> SetFirstSeparator ();
2019-01-25 13:16:32 +01:00
// Add frequently settings
create_freq_settings_popupmenu ( menu );
if ( mode == comAdvanced )
return nullptr ;
2019-04-25 01:45:00 +02:00
menu -> SetSecondSeparator ();
2019-01-25 13:16:32 +01:00
// Add full settings list
2019-03-28 16:28:34 +01:00
auto menu_item = new wxMenuItem ( menu , wxID_ANY , menu_name );
2019-04-16 10:05:45 +02:00
menu_item -> SetBitmap ( m_bmp_cog . bmp ());
2019-01-25 13:16:32 +01:00
2019-03-28 16:28:34 +01:00
menu_item -> SetSubMenu ( create_settings_popupmenu ( menu ));
2018-12-07 17:50:48 +01:00
return menu -> Append ( menu_item );
}
wxMenuItem * ObjectList :: append_menu_item_change_type ( wxMenu * menu )
{
return append_menu_item ( menu , wxID_ANY , _ ( L ( "Change type" )), "" ,
[ this ]( wxCommandEvent & ) { change_part_type (); }, "" , menu );
}
2019-05-13 14:27:51 +02:00
wxMenuItem * ObjectList :: append_menu_item_instance_to_object ( wxMenu * menu , wxWindow * parent )
2019-01-23 16:01:37 +01:00
{
return append_menu_item ( menu , wxID_ANY , _ ( L ( "Set as a Separated Object" )), "" ,
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { split_instances (); }, "" , menu , [](){ return wxGetApp (). plater () -> can_set_instance_to_object (); }, parent );
2019-01-23 16:01:37 +01:00
}
2019-03-21 09:04:11 +01:00
void ObjectList :: append_menu_items_osx ( wxMenu * menu )
2019-01-30 11:35:37 +01:00
{
2019-01-30 16:27:07 +01:00
append_menu_item ( menu , wxID_ANY , _ ( L ( "Rename" )), "" ,
2019-01-30 11:35:37 +01:00
[ this ]( wxCommandEvent & ) { rename_item (); }, "" , menu );
2019-03-21 09:04:11 +01:00
2019-01-30 16:27:07 +01:00
menu -> AppendSeparator ();
}
2019-04-24 16:04:47 +02:00
wxMenuItem * ObjectList :: append_menu_item_fix_through_netfabb ( wxMenu * menu )
2019-01-30 16:27:07 +01:00
{
if ( ! is_windows10 ())
2019-04-25 16:19:50 +02:00
return nullptr ;
2019-05-13 14:27:51 +02:00
Plater * plater = wxGetApp (). plater ();
2019-04-24 16:04:47 +02:00
wxMenuItem * menu_item = append_menu_item ( menu , wxID_ANY , _ ( L ( "Fix through the Netfabb" )), "" ,
2019-05-13 14:27:51 +02:00
[ this ]( wxCommandEvent & ) { fix_through_netfabb (); }, "" , menu ,
[ plater ]() { return plater -> can_fix_through_netfabb (); }, plater );
2019-01-30 16:27:07 +01:00
menu -> AppendSeparator ();
2019-04-24 16:04:47 +02:00
return menu_item ;
2019-01-30 11:35:37 +01:00
}
2019-03-07 12:09:23 +01:00
void ObjectList :: append_menu_item_export_stl ( wxMenu * menu ) const
{
2019-04-04 15:13:43 +02:00
append_menu_item ( menu , wxID_ANY , _ ( L ( "Export as STL" )) + dots , "" ,
2019-05-02 13:46:39 +02:00
[]( wxCommandEvent & ) { wxGetApp (). plater () -> export_stl ( false , true ); }, "" , menu );
2019-03-07 12:09:23 +01:00
menu -> AppendSeparator ();
}
2019-03-28 16:28:34 +01:00
void ObjectList :: append_menu_item_change_extruder ( wxMenu * menu ) const
{
const wxString name = _ ( L ( "Change extruder" ));
// Delete old menu item
const int item_id = menu -> FindItem ( name );
if ( item_id != wxNOT_FOUND )
menu -> Destroy ( item_id );
const int extruders_cnt = extruders_count ();
const wxDataViewItem item = GetSelection ();
if ( item && extruders_cnt > 1 )
{
DynamicPrintConfig & config = get_item_config ( item );
const int initial_extruder = ! config . has ( "extruder" ) ? 0 :
config . option < ConfigOptionInt > ( "extruder" ) -> value ;
wxMenu * extruder_selection_menu = new wxMenu ();
for ( int i = 0 ; i <= extruders_cnt ; i ++ )
{
const wxString & item_name = i == 0 ? _ ( L ( "Default" )) : wxString :: Format ( "%d" , i );
append_menu_radio_item ( extruder_selection_menu , wxID_ANY , item_name , "" ,
[ this , i ]( wxCommandEvent & ) { set_extruder_for_selected_items ( i ); }, menu ) -> Check ( i == initial_extruder );
}
menu -> AppendSubMenu ( extruder_selection_menu , name , _ ( L ( "Select new extruder for the object/part" )));
}
}
2019-04-02 16:33:52 +02:00
void ObjectList :: append_menu_item_delete ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _ ( L ( "Delete" )), "" ,
[ this ]( wxCommandEvent & ) { remove (); }, "" , menu );
}
2019-05-23 09:20:11 +02:00
void ObjectList :: append_menu_item_scale_selection_to_fit_print_volume ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _ ( L ( "Scale to print volume" )), _ ( L ( "Scale the selected object to fit the print volume" )),
[ this ]( wxCommandEvent & ) { wxGetApp (). plater () -> scale_selection_to_fit_print_volume (); }, "" , menu );
}
2018-12-07 17:50:48 +01:00
void ObjectList :: create_object_popupmenu ( wxMenu * menu )
{
2019-01-30 11:35:37 +01:00
#ifdef __WXOSX__
2019-03-21 09:04:11 +01:00
append_menu_items_osx ( menu );
2019-01-30 11:35:37 +01:00
#endif // __WXOSX__
2019-03-07 12:09:23 +01:00
append_menu_item_export_stl ( menu );
2019-01-30 16:27:07 +01:00
append_menu_item_fix_through_netfabb ( menu );
2019-05-23 09:20:11 +02:00
append_menu_item_scale_selection_to_fit_print_volume ( menu );
2019-01-30 16:27:07 +01:00
2018-11-02 12:35:26 +01:00
// Split object to parts
2018-12-07 17:50:48 +01:00
m_menu_item_split = append_menu_item_split ( menu );
2018-10-05 23:29:15 +02:00
menu -> AppendSeparator ();
2019-01-25 10:34:32 +01:00
// rest of a object_menu will be added later in:
// - append_menu_items_add_volume() -> for "Add (volumes)"
// - append_menu_item_settings() -> for "Add (settings)"
2018-10-05 23:29:15 +02:00
}
2018-12-06 14:49:14 +01:00
void ObjectList :: create_sla_object_popupmenu ( wxMenu * menu )
2018-10-05 23:29:15 +02:00
{
2019-01-30 13:26:02 +01:00
#ifdef __WXOSX__
2019-03-21 09:04:11 +01:00
append_menu_items_osx ( menu );
2019-01-30 13:26:02 +01:00
#endif // __WXOSX__
2019-03-07 12:09:23 +01:00
append_menu_item_export_stl ( menu );
2019-01-30 16:27:07 +01:00
append_menu_item_fix_through_netfabb ( menu );
2019-01-25 10:34:32 +01:00
// rest of a object_sla_menu will be added later in:
// - append_menu_item_settings() -> for "Add (settings)"
2018-12-06 14:49:14 +01:00
}
2018-10-05 23:29:15 +02:00
2018-12-06 14:49:14 +01:00
void ObjectList :: create_part_popupmenu ( wxMenu * menu )
{
2019-01-30 11:35:37 +01:00
#ifdef __WXOSX__
2019-03-21 09:04:11 +01:00
append_menu_items_osx ( menu );
2019-01-30 11:35:37 +01:00
#endif // __WXOSX__
2019-01-30 16:27:07 +01:00
append_menu_item_fix_through_netfabb ( menu );
2019-04-04 15:13:43 +02:00
append_menu_item_export_stl ( menu );
2019-01-30 16:27:07 +01:00
2018-12-07 17:50:48 +01:00
m_menu_item_split_part = append_menu_item_split ( menu );
2018-10-05 23:29:15 +02:00
2018-11-02 23:27:31 +01:00
// Append change part type
2018-10-05 23:29:15 +02:00
menu -> AppendSeparator ();
2018-12-07 17:50:48 +01:00
append_menu_item_change_type ( menu );
2018-11-02 23:27:31 +01:00
2019-01-25 13:16:32 +01:00
// rest of a object_sla_menu will be added later in:
// - append_menu_item_settings() -> for "Add (settings)"
2018-10-05 23:29:15 +02:00
}
2019-01-23 16:01:37 +01:00
void ObjectList :: create_instance_popupmenu ( wxMenu * menu )
{
2019-05-13 14:27:51 +02:00
m_menu_item_split_instances = append_menu_item_instance_to_object ( menu , wxGetApp (). plater ());
2019-01-30 10:05:54 +01:00
2019-04-04 18:01:53 +02:00
/* New behavior logic:
* 1. Split Object to several separated object, if ALL instances are selected
* 2. Separate selected instances from the initial object to the separated object,
* if some (not all) instances are selected
*/
2019-01-30 10:05:54 +01:00
wxGetApp (). plater () -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) {
2019-04-04 18:01:53 +02:00
// evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
evt . SetText ( wxGetApp (). plater () -> canvas3D () -> get_selection (). is_single_full_object () ?
_ ( L ( "Set as a Separated Objects" )) : _ ( L ( "Set as a Separated Object" )));
}, m_menu_item_split_instances -> GetId ());
2019-01-23 16:01:37 +01:00
}
2018-12-07 17:50:48 +01:00
wxMenu * ObjectList :: create_settings_popupmenu ( wxMenu * parent_menu )
2018-10-05 23:29:15 +02:00
{
wxMenu * menu = new wxMenu ;
settings_menu_hierarchy settings_menu ;
2018-12-07 17:50:48 +01:00
const bool is_part = m_objects_model -> GetParent ( GetSelection ()) != wxDataViewItem ( 0 );
get_options_menu ( settings_menu , is_part );
2018-10-05 23:29:15 +02:00
2018-12-06 14:49:14 +01:00
for ( auto cat : settings_menu ) {
append_menu_item ( menu , wxID_ANY , _ ( cat . first ), "" ,
2018-12-07 17:50:48 +01:00
[ menu , this ]( wxCommandEvent & event ) { get_settings_choice ( menu -> GetLabel ( event . GetId ())); },
2018-12-06 14:49:14 +01:00
CATEGORY_ICON . find ( cat . first ) == CATEGORY_ICON . end () ? wxNullBitmap : CATEGORY_ICON . at ( cat . first ), parent_menu );
2018-10-05 23:29:15 +02:00
}
2018-12-06 14:49:14 +01:00
2018-10-05 23:29:15 +02:00
return menu ;
}
2019-01-25 16:39:50 +01:00
void ObjectList :: create_freq_settings_popupmenu ( wxMenu * menu )
2019-01-25 13:16:32 +01:00
{
2019-01-28 12:13:53 +01:00
// Add default settings bundles
2019-03-05 10:46:57 +01:00
const FreqSettingsBundle & bundle = printer_technology () == ptFFF ?
2019-01-25 16:39:50 +01:00
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA ;
2019-01-25 13:16:32 +01:00
2019-03-14 13:15:04 +01:00
const int extruders_cnt = extruders_count ();
2019-01-25 16:39:50 +01:00
for ( auto & it : bundle ) {
if ( it . first . empty () || it . first == "Extruders" && extruders_cnt == 1 )
continue ;
append_menu_item ( menu , wxID_ANY , _ ( it . first ), "" ,
[ menu , this ]( wxCommandEvent & event ) { get_freq_settings_choice ( menu -> GetLabel ( event . GetId ())); },
CATEGORY_ICON . find ( it . first ) == CATEGORY_ICON . end () ? wxNullBitmap : CATEGORY_ICON . at ( it . first ), menu );
}
2019-01-28 12:13:53 +01:00
#if 0
// Add "Quick" settings bundles
2019-03-05 10:46:57 +01:00
const FreqSettingsBundle& bundle_quick = printer_technology() == ptFFF ?
2019-01-28 12:13:53 +01:00
m_freq_settings_fff : m_freq_settings_sla;
for (auto& it : bundle_quick) {
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
continue;
append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "",
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu);
}
#endif
2019-01-25 13:16:32 +01:00
}
2018-11-29 15:00:23 +01:00
void ObjectList :: update_opt_keys ( t_config_option_keys & opt_keys )
{
auto full_current_opts = get_options ( false );
for ( int i = opt_keys . size () - 1 ; i >= 0 ; -- i )
if ( find ( full_current_opts . begin (), full_current_opts . end (), opt_keys [ i ]) == full_current_opts . end ())
opt_keys . erase ( opt_keys . begin () + i );
}
2019-02-22 12:12:10 +01:00
void ObjectList :: load_subobject ( ModelVolumeType type )
2018-10-05 23:29:15 +02:00
{
2019-06-17 15:33:58 +02:00
wxDataViewItem item = GetSelection ();
// we can add volumes for Object or Instance
if ( ! item || ! ( m_objects_model -> GetItemType ( item ) & ( itObject | itInstance )))
2018-10-05 23:29:15 +02:00
return ;
2019-06-17 15:33:58 +02:00
const int obj_idx = m_objects_model -> GetObjectIdByItem ( item );
2018-10-05 23:29:15 +02:00
if ( obj_idx < 0 ) return ;
2019-04-29 15:27:59 +02:00
2019-06-17 15:33:58 +02:00
// Get object item, if Instance is selected
if ( m_objects_model -> GetItemType ( item ) & itInstance )
item = m_objects_model -> GetItemById ( obj_idx );
2019-04-29 15:27:59 +02:00
std :: vector < std :: pair < wxString , bool >> volumes_info ;
load_part (( * m_objects )[ obj_idx ], volumes_info , type );
2018-10-05 23:29:15 +02:00
2019-04-22 01:51:10 +02:00
changed_object ( obj_idx );
2018-10-05 23:29:15 +02:00
2019-04-29 15:27:59 +02:00
wxDataViewItem sel_item ;
for ( const auto & volume : volumes_info )
sel_item = m_objects_model -> AddVolumeChild ( item , volume . first , type , volume . second );
if ( sel_item )
select_item ( sel_item );
2018-10-05 23:29:15 +02:00
}
void ObjectList :: load_part ( ModelObject * model_object ,
2019-04-29 15:27:59 +02:00
std :: vector < std :: pair < wxString , bool >> & volumes_info ,
2019-02-22 12:12:10 +01:00
ModelVolumeType type )
2018-10-05 23:29:15 +02:00
{
wxWindow * parent = wxGetApp (). tab_panel () -> GetPage ( 0 );
wxArrayString input_files ;
2018-11-15 15:27:39 +01:00
wxGetApp (). import_model ( parent , input_files );
2018-10-05 23:29:15 +02:00
for ( int i = 0 ; i < input_files . size (); ++ i ) {
2019-01-03 14:34:53 +01:00
std :: string input_file = input_files . Item ( i ). ToUTF8 (). data ();
2018-10-05 23:29:15 +02:00
Model model ;
try {
model = Model :: read_from_file ( input_file );
}
catch ( std :: exception & e ) {
2019-05-11 23:29:25 +02:00
auto msg = _ ( L ( "Error!" )) + " " + input_file + " : " + e . what () + "." ;
2018-10-05 23:29:15 +02:00
show_error ( parent , msg );
exit ( 1 );
}
for ( auto object : model . objects ) {
Vec3d delta = Vec3d :: Zero ();
if ( model_object -> origin_translation != Vec3d :: Zero ())
{
object -> center_around_origin ();
delta = model_object -> origin_translation - object -> origin_translation ;
}
for ( auto volume : object -> volumes ) {
2018-11-12 08:54:22 +01:00
volume -> translate ( delta );
2018-10-05 23:29:15 +02:00
auto new_volume = model_object -> add_volume ( * volume );
2019-02-22 12:12:10 +01:00
new_volume -> set_type ( type );
2018-10-05 23:29:15 +02:00
new_volume -> name = boost :: filesystem :: path ( input_file ). filename (). string ();
2019-04-29 15:27:59 +02:00
volumes_info . push_back ( std :: make_pair ( from_u8 ( new_volume -> name ), new_volume -> get_mesh_errors_count () > 0 ));
2018-10-05 23:29:15 +02:00
// set a default extruder value, since user can't add it manually
new_volume -> config . set_key_value ( "extruder" , new ConfigOptionInt ( 0 ));
}
}
}
}
2019-02-22 12:12:10 +01:00
void ObjectList :: load_generic_subobject ( const std :: string & type_name , const ModelVolumeType type )
2018-10-05 23:29:15 +02:00
{
2018-11-06 17:26:33 +01:00
const auto obj_idx = get_selected_obj_idx ();
2019-02-28 11:20:01 +01:00
if ( obj_idx < 0 )
return ;
2019-03-19 13:30:21 +01:00
const Selection & selection = wxGetApp (). plater () -> canvas3D () -> get_selection ();
2019-02-28 11:20:01 +01:00
assert ( obj_idx == selection . get_object_idx ());
2019-03-07 10:08:21 +01:00
/** Any changes of the Object's composition is duplicated for all Object's Instances
* So, It's enough to take a bounding box of a first selected Instance and calculate Part(generic_subobject) position
*/
int instance_idx = * selection . get_instance_idxs (). begin ();
2019-02-28 11:20:01 +01:00
assert ( instance_idx != - 1 );
if ( instance_idx == - 1 )
return ;
// Selected object
ModelObject & model_object = * ( * m_objects )[ obj_idx ];
// Bounding box of the selected instance in world coordinate system including the translation, without modifiers.
BoundingBoxf3 instance_bb = model_object . instance_bounding_box ( instance_idx );
2018-10-05 23:29:15 +02:00
2019-02-22 14:33:01 +01:00
const wxString name = _ ( L ( "Generic" )) + "-" + _ ( type_name );
2018-10-05 23:29:15 +02:00
TriangleMesh mesh ;
2019-04-12 15:31:33 +02:00
double side = wxGetApp (). plater () -> canvas3D () -> get_size_proportional_to_max_bed_size ( 0.1 );
2018-11-06 17:26:33 +01:00
2019-02-28 11:20:01 +01:00
if ( type_name == "Box" )
// Sitting on the print bed, left front front corner at (0, 0).
2018-11-06 17:26:33 +01:00
mesh = make_cube ( side , side , side );
2019-02-22 12:12:10 +01:00
else if ( type_name == "Cylinder" )
2019-02-28 11:20:01 +01:00
// Centered around 0, sitting on the print bed.
// The cylinder has the same volume as the box above.
mesh = make_cylinder ( 0.564 * side , side );
2019-02-22 12:12:10 +01:00
else if ( type_name == "Sphere" )
2019-02-28 11:20:01 +01:00
// Centered around 0, half the sphere below the print bed, half above.
// The sphere has the same volume as the box above.
mesh = make_sphere ( 0.62 * side , PI / 18 );
else if ( type_name == "Slab" )
// Sitting on the print bed, left front front corner at (0, 0).
mesh = make_cube ( instance_bb . size (). x () * 1.5 , instance_bb . size (). y () * 1.5 , instance_bb . size (). z () * 0.5 );
2018-10-05 23:29:15 +02:00
mesh . repair ();
2018-11-12 08:54:22 +01:00
2019-02-28 11:20:01 +01:00
// Mesh will be centered when loading.
ModelVolume * new_volume = model_object . add_volume ( std :: move ( mesh ));
2019-02-22 12:12:10 +01:00
new_volume -> set_type ( type );
2018-10-05 23:29:15 +02:00
2019-01-14 13:29:06 +01:00
if ( instance_idx != - 1 )
{
2019-02-28 11:20:01 +01:00
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
2019-01-14 13:29:06 +01:00
const GLVolume * v = selection . get_volume ( * selection . get_volume_idxs (). begin ());
2019-02-28 11:20:01 +01:00
// Transform the new modifier to be aligned with the print bed.
2019-06-11 17:08:47 +02:00
const BoundingBoxf3 mesh_bb = new_volume -> mesh (). bounding_box ();
2019-06-24 12:26:11 +02:00
new_volume -> set_transformation ( Geometry :: Transformation :: volume_to_bed_transformation ( v -> get_instance_transformation (), mesh_bb ));
2019-02-28 11:20:01 +01:00
// Set the modifier position.
auto offset = ( type_name == "Slab" ) ?
// Slab: Lift to print bed
Vec3d ( 0. , 0. , 0.5 * mesh_bb . size (). z () + instance_bb . min . z () - v -> get_instance_offset (). z ()) :
// Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
Vec3d ( instance_bb . max ( 0 ), instance_bb . min ( 1 ), instance_bb . min ( 2 )) + 0.5 * mesh_bb . size () - v -> get_instance_offset ();
new_volume -> set_offset ( v -> get_instance_transformation (). get_matrix ( true ). inverse () * offset );
2019-01-14 13:29:06 +01:00
}
2019-02-22 14:33:01 +01:00
new_volume -> name = into_u8 ( name );
2018-10-05 23:29:15 +02:00
// set a default extruder value, since user can't add it manually
new_volume -> config . set_key_value ( "extruder" , new ConfigOptionInt ( 0 ));
2019-04-22 01:51:10 +02:00
changed_object ( obj_idx );
2018-10-05 23:29:15 +02:00
2019-01-09 10:30:29 +01:00
const auto object_item = m_objects_model -> GetTopParent ( GetSelection ());
2019-04-29 15:27:59 +02:00
select_item ( m_objects_model -> AddVolumeChild ( object_item , name , type ,
new_volume -> get_mesh_errors_count () > 0 ));
2018-10-05 23:29:15 +02:00
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
2018-10-11 15:57:09 +02:00
selection_changed ();
2018-10-05 23:29:15 +02:00
#endif //no __WXOSX__ //__WXMSW__
}
2018-11-13 14:17:35 +01:00
void ObjectList :: del_object ( const int obj_idx )
{
wxGetApp (). plater () -> delete_object_from_model ( obj_idx );
}
2018-10-05 23:29:15 +02:00
// Delete subobject
2018-10-12 12:00:37 +02:00
void ObjectList :: del_subobject_item ( wxDataViewItem & item )
2018-10-05 23:29:15 +02:00
{
if ( ! item ) return ;
2018-10-16 11:08:37 +02:00
int obj_idx , idx ;
ItemType type ;
2018-10-12 12:00:37 +02:00
2018-10-16 11:08:37 +02:00
m_objects_model -> GetItemInfo ( item , type , obj_idx , idx );
if ( type == itUndef )
2018-10-05 23:29:15 +02:00
return ;
2018-10-16 11:08:37 +02:00
if ( type == itSettings )
2018-10-05 23:29:15 +02:00
del_settings_from_config ();
2018-10-16 11:08:37 +02:00
else if ( type == itInstanceRoot && obj_idx != - 1 )
del_instances_from_object ( obj_idx );
else if ( idx == - 1 )
return ;
else if ( ! del_subobject_from_object ( obj_idx , idx , type ))
2018-10-05 23:29:15 +02:00
return ;
2019-04-29 15:27:59 +02:00
// If last volume item with warning was deleted, unmark object item
if ( type == itVolume && ( * m_objects )[ obj_idx ] -> get_mesh_errors_count () == 0 )
m_objects_model -> DeleteWarningIcon ( m_objects_model -> GetParent ( item ));
2018-10-12 12:00:37 +02:00
m_objects_model -> Delete ( item );
2018-10-05 23:29:15 +02:00
}
void ObjectList :: del_settings_from_config ()
{
auto opt_keys = m_config -> keys ();
if ( opt_keys . size () == 1 && opt_keys [ 0 ] == "extruder" )
return ;
int extruder = - 1 ;
if ( m_config -> has ( "extruder" ))
extruder = m_config -> option < ConfigOptionInt > ( "extruder" ) -> value ;
m_config -> clear ();
if ( extruder >= 0 )
m_config -> set_key_value ( "extruder" , new ConfigOptionInt ( extruder ));
}
2018-10-16 11:08:37 +02:00
void ObjectList :: del_instances_from_object ( const int obj_idx )
2018-10-05 23:29:15 +02:00
{
2018-10-19 11:02:50 +02:00
auto & instances = ( * m_objects )[ obj_idx ] -> instances ;
2018-10-16 11:08:37 +02:00
if ( instances . size () <= 1 )
return ;
2018-10-05 23:29:15 +02:00
2018-10-16 11:08:37 +02:00
while ( instances . size () > 1 )
instances . pop_back ();
( * m_objects )[ obj_idx ] -> invalidate_bounding_box (); // ? #ys_FIXME
2019-04-22 01:51:10 +02:00
changed_object ( obj_idx );
2018-10-16 11:08:37 +02:00
}
bool ObjectList :: del_subobject_from_object ( const int obj_idx , const int idx , const int type )
{
2018-12-18 19:12:59 +01:00
if ( obj_idx == 1000 )
// Cannot delete a wipe tower.
return false ;
2019-03-13 16:40:11 +01:00
ModelObject * object = ( * m_objects )[ obj_idx ];
2018-10-16 11:08:37 +02:00
if ( type == itVolume ) {
2019-03-13 16:40:11 +01:00
const auto volume = object -> volumes [ idx ];
2018-10-16 11:08:37 +02:00
// if user is deleting the last solid part, throw error
int solid_cnt = 0 ;
2019-03-13 16:40:11 +01:00
for ( auto vol : object -> volumes )
2018-10-16 11:08:37 +02:00
if ( vol -> is_model_part ())
++ solid_cnt ;
if ( volume -> is_model_part () && solid_cnt == 1 ) {
2018-10-18 10:40:26 +02:00
Slic3r :: GUI :: show_error ( nullptr , _ ( L ( "You can't delete the last solid part from object." )));
2018-10-16 11:08:37 +02:00
return false ;
}
2019-03-13 16:40:11 +01:00
object -> delete_volume ( idx );
if ( object -> volumes . size () == 1 )
{
const auto last_volume = object -> volumes [ 0 ];
if ( ! last_volume -> config . empty ()) {
object -> config . apply ( last_volume -> config );
last_volume -> config . clear ();
}
}
2018-10-05 23:29:15 +02:00
}
2018-10-18 10:40:26 +02:00
else if ( type == itInstance ) {
2019-03-13 16:40:11 +01:00
if ( object -> instances . size () == 1 ) {
2018-10-18 10:40:26 +02:00
Slic3r :: GUI :: show_error ( nullptr , _ ( L ( "You can't delete the last intance from object." )));
return false ;
}
2019-03-13 16:40:11 +01:00
object -> delete_instance ( idx );
2018-10-18 10:40:26 +02:00
}
2018-10-16 11:08:37 +02:00
else
return false ;
2018-10-05 23:29:15 +02:00
2019-04-22 01:51:10 +02:00
changed_object ( obj_idx );
2018-10-05 23:29:15 +02:00
return true ;
}
2018-12-06 14:49:14 +01:00
void ObjectList :: split ()
2018-10-05 23:29:15 +02:00
{
2018-10-08 16:27:38 +02:00
const auto item = GetSelection ();
2018-11-06 09:06:26 +01:00
const int obj_idx = get_selected_obj_idx ();
if ( ! item || obj_idx < 0 )
2018-10-05 23:29:15 +02:00
return ;
2018-11-06 09:06:26 +01:00
2018-10-05 23:29:15 +02:00
ModelVolume * volume ;
2018-12-06 14:49:14 +01:00
if ( ! get_volume_by_item ( item , volume )) return ;
2019-03-14 13:15:04 +01:00
DynamicPrintConfig & config = printer_config ();
2018-11-08 20:44:07 +01:00
const ConfigOption * nozzle_dmtrs_opt = config . option ( "nozzle_diameter" , false );
const auto nozzle_dmrs_cnt = ( nozzle_dmtrs_opt == nullptr ) ? size_t ( 1 ) : dynamic_cast < const ConfigOptionFloats *> ( nozzle_dmtrs_opt ) -> values . size ();
2018-10-05 23:29:15 +02:00
if ( volume -> split ( nozzle_dmrs_cnt ) == 1 ) {
wxMessageBox ( _ ( L ( "The selected object couldn't be split because it contains only one part." )));
return ;
}
2019-02-05 20:23:24 +01:00
wxBusyCursor wait ;
2018-11-06 09:06:26 +01:00
auto model_object = ( * m_objects )[ obj_idx ];
2018-10-05 23:29:15 +02:00
2018-11-06 09:06:26 +01:00
auto parent = m_objects_model -> GetTopParent ( item );
if ( parent )
m_objects_model -> DeleteVolumeChildren ( parent );
else
parent = item ;
2018-10-05 23:29:15 +02:00
2019-04-29 15:27:59 +02:00
for ( const ModelVolume * volume : model_object -> volumes ) {
const wxDataViewItem & vol_item = m_objects_model -> AddVolumeChild ( parent , from_u8 ( volume -> name ),
volume -> is_modifier () ? ModelVolumeType :: PARAMETER_MODIFIER : ModelVolumeType :: MODEL_PART ,
volume -> get_mesh_errors_count () > 0 ,
volume -> config . has ( "extruder" ) ?
volume -> config . option < ConfigOptionInt > ( "extruder" ) -> value : 0 ,
2019-04-11 11:09:32 +02:00
false );
2018-11-06 09:06:26 +01:00
// add settings to the part, if it has those
2019-04-29 15:27:59 +02:00
auto opt_keys = volume -> config . keys ();
2018-11-06 09:06:26 +01:00
if ( ! ( opt_keys . size () == 1 && opt_keys [ 0 ] == "extruder" ) ) {
select_item ( m_objects_model -> AddSettingsChild ( vol_item ));
2019-04-29 15:27:59 +02:00
Expand ( vol_item );
2018-11-06 09:06:26 +01:00
}
2018-10-05 23:29:15 +02:00
}
2018-11-06 15:34:44 +01:00
if ( parent == item )
Expand ( parent );
2019-04-22 01:51:10 +02:00
changed_object ( obj_idx );
2018-10-05 23:29:15 +02:00
}
2018-12-06 14:49:14 +01:00
bool ObjectList :: get_volume_by_item ( const wxDataViewItem & item , ModelVolume *& volume )
2018-10-05 23:29:15 +02:00
{
2018-11-06 09:06:26 +01:00
auto obj_idx = get_selected_obj_idx ();
if ( ! item || obj_idx < 0 )
2018-10-05 23:29:15 +02:00
return false ;
const auto volume_id = m_objects_model -> GetVolumeIdByItem ( item );
2018-12-06 14:49:14 +01:00
const bool split_part = m_objects_model -> GetItemType ( item ) == itVolume ;
2018-11-06 09:06:26 +01:00
// object is selected
2018-10-05 23:29:15 +02:00
if ( volume_id < 0 ) {
2018-11-06 09:06:26 +01:00
if ( split_part || ( * m_objects )[ obj_idx ] -> volumes . size () > 1 )
return false ;
volume = ( * m_objects )[ obj_idx ] -> volumes [ 0 ];
2018-10-05 23:29:15 +02:00
}
2018-11-06 09:06:26 +01:00
// volume is selected
2018-10-05 23:29:15 +02:00
else
2018-11-06 09:06:26 +01:00
volume = ( * m_objects )[ obj_idx ] -> volumes [ volume_id ];
return true ;
2018-10-05 23:29:15 +02:00
}
2018-12-06 14:49:14 +01:00
bool ObjectList :: is_splittable ()
2018-10-05 23:29:15 +02:00
{
2018-10-08 16:27:38 +02:00
const wxDataViewItem item = GetSelection ();
2018-10-05 23:29:15 +02:00
if ( ! item ) return false ;
ModelVolume * volume ;
2018-12-06 14:49:14 +01:00
if ( ! get_volume_by_item ( item , volume ) || ! volume )
2018-10-05 23:29:15 +02:00
return false ;
2019-03-13 14:04:59 +01:00
return volume -> is_splittable ();
2018-10-05 23:29:15 +02:00
}
2019-01-23 16:01:37 +01:00
bool ObjectList :: selected_instances_of_same_object ()
{
wxDataViewItemArray sels ;
GetSelections ( sels );
const int obj_idx = m_objects_model -> GetObjectIdByItem ( sels . front ());
for ( auto item : sels ) {
if ( ! ( m_objects_model -> GetItemType ( item ) & itInstance ) ||
obj_idx != m_objects_model -> GetObjectIdByItem ( item ))
return false ;
}
return true ;
}
bool ObjectList :: can_split_instances ()
{
2019-03-19 13:30:21 +01:00
const Selection & selection = wxGetApp (). plater () -> canvas3D () -> get_selection ();
2019-01-23 16:01:37 +01:00
return selection . is_multiple_full_instance () || selection . is_single_full_instance ();
}
2019-04-22 01:51:10 +02:00
// NO_PARAMETERS function call means that changed object index will be determine from Selection()
void ObjectList :: changed_object ( const int obj_idx /* = -1*/ ) const
2018-10-31 15:41:27 +01:00
{
2019-04-22 01:51:10 +02:00
wxGetApp (). plater () -> changed_object ( obj_idx < 0 ? get_selected_obj_idx () : obj_idx );
2018-10-05 23:29:15 +02:00
}
void ObjectList :: part_selection_changed ()
{
int obj_idx = - 1 ;
2019-04-24 13:33:05 +02:00
int volume_id = - 1 ;
2018-10-05 23:29:15 +02:00
m_config = nullptr ;
2018-11-09 18:39:07 +01:00
wxString og_name = wxEmptyString ;
2018-11-08 15:45:55 +01:00
2018-11-09 18:39:07 +01:00
bool update_and_show_manipulations = false ;
bool update_and_show_settings = false ;
2019-03-29 14:36:09 +01:00
const auto item = GetSelection ();
if ( multiple_selection () || item && m_objects_model -> GetItemType ( item ) == itInstanceRoot )
{
2018-11-09 18:39:07 +01:00
og_name = _ ( L ( "Group manipulation" ));
2019-03-29 14:36:09 +01:00
const Selection & selection = wxGetApp (). plater () -> canvas3D () -> get_selection ();
// don't show manipulation panel for case of all Object's parts selection
update_and_show_manipulations = ! selection . is_single_full_instance ();
2018-11-09 18:39:07 +01:00
}
2018-11-08 15:45:55 +01:00
else
2018-10-05 23:29:15 +02:00
{
2018-11-08 15:45:55 +01:00
if ( item )
{
if ( m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 )) {
obj_idx = m_objects_model -> GetIdByItem ( item );
og_name = _ ( L ( "Object manipulation" ));
m_config = & ( * m_objects )[ obj_idx ] -> config ;
2018-11-09 18:39:07 +01:00
update_and_show_manipulations = true ;
2018-11-08 15:45:55 +01:00
}
else {
auto parent = m_objects_model -> GetParent ( item );
// Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
obj_idx = m_objects_model -> GetIdByItem ( parent );
2018-11-09 18:39:07 +01:00
if ( m_objects_model -> GetItemType ( item ) == itSettings ) {
2018-11-08 15:45:55 +01:00
if ( m_objects_model -> GetParent ( parent ) == wxDataViewItem ( 0 )) {
og_name = _ ( L ( "Object Settings to modify" ));
m_config = & ( * m_objects )[ obj_idx ] -> config ;
}
else {
og_name = _ ( L ( "Part Settings to modify" ));
auto main_parent = m_objects_model -> GetParent ( parent );
obj_idx = m_objects_model -> GetIdByItem ( main_parent );
const auto volume_id = m_objects_model -> GetVolumeIdByItem ( parent );
m_config = & ( * m_objects )[ obj_idx ] -> volumes [ volume_id ] -> config ;
}
2018-11-09 18:39:07 +01:00
update_and_show_settings = true ;
2018-10-05 23:29:15 +02:00
}
2018-11-08 15:45:55 +01:00
else if ( m_objects_model -> GetItemType ( item ) == itVolume ) {
og_name = _ ( L ( "Part manipulation" ));
2019-04-24 13:33:05 +02:00
volume_id = m_objects_model -> GetVolumeIdByItem ( item );
2018-10-05 23:29:15 +02:00
m_config = & ( * m_objects )[ obj_idx ] -> volumes [ volume_id ] -> config ;
2018-11-09 18:39:07 +01:00
update_and_show_manipulations = true ;
2018-10-05 23:29:15 +02:00
}
2018-11-08 15:45:55 +01:00
else if ( m_objects_model -> GetItemType ( item ) == itInstance ) {
og_name = _ ( L ( "Instance manipulation" ));
2018-11-09 18:39:07 +01:00
update_and_show_manipulations = true ;
2019-01-09 10:30:29 +01:00
// fill m_config by object's values
const int obj_idx_ = m_objects_model -> GetObjectIdByItem ( item );
m_config = & ( * m_objects )[ obj_idx_ ] -> config ;
2018-11-08 15:45:55 +01:00
}
2018-10-05 23:29:15 +02:00
}
2018-11-08 15:45:55 +01:00
}
2018-10-05 23:29:15 +02:00
}
m_selected_object_id = obj_idx ;
2018-11-09 18:39:07 +01:00
if ( update_and_show_manipulations ) {
wxGetApp (). obj_manipul () -> get_og () -> set_name ( " " + og_name + " " );
2019-04-24 13:33:05 +02:00
if ( item ) {
wxGetApp (). obj_manipul () -> get_og () -> set_value ( "object_name" , m_objects_model -> GetName ( item ));
2019-05-02 09:54:18 +02:00
wxGetApp (). obj_manipul () -> update_warning_icon_state ( get_mesh_errors_list ( obj_idx , volume_id ));
2019-04-24 13:33:05 +02:00
}
2018-11-09 18:39:07 +01:00
}
if ( update_and_show_settings )
wxGetApp (). obj_settings () -> get_og () -> set_name ( " " + og_name + " " );
2018-11-13 12:19:56 +01:00
Sidebar & panel = wxGetApp (). sidebar ();
panel . Freeze ();
2018-11-09 18:39:07 +01:00
wxGetApp (). obj_manipul () -> UpdateAndShow ( update_and_show_manipulations );
wxGetApp (). obj_settings () -> UpdateAndShow ( update_and_show_settings );
2018-11-13 12:19:56 +01:00
wxGetApp (). sidebar (). show_info_sizer ();
2018-11-09 18:39:07 +01:00
2018-11-13 12:19:56 +01:00
panel . Layout ();
panel . Thaw ();
2018-10-05 23:29:15 +02:00
}
2018-10-08 16:27:38 +02:00
void ObjectList :: add_object_to_list ( size_t obj_idx )
2018-10-05 23:29:15 +02:00
{
2018-10-08 16:27:38 +02:00
auto model_object = ( * m_objects )[ obj_idx ];
2019-04-24 13:33:05 +02:00
const wxString & item_name = from_u8 ( model_object -> name );
2018-12-11 08:53:18 +01:00
const auto item = m_objects_model -> Add ( item_name ,
! model_object -> config . has ( "extruder" ) ? 0 :
2019-04-29 15:27:59 +02:00
model_object -> config . option < ConfigOptionInt > ( "extruder" ) -> value ,
get_mesh_errors_count ( obj_idx ) > 0 );
2018-10-05 23:29:15 +02:00
2018-11-06 09:06:26 +01:00
// add volumes to the object
2018-10-05 23:29:15 +02:00
if ( model_object -> volumes . size () > 1 ) {
2019-04-29 15:27:59 +02:00
for ( const ModelVolume * volume : model_object -> volumes ) {
const wxDataViewItem & vol_item = m_objects_model -> AddVolumeChild ( item ,
from_u8 ( volume -> name ),
volume -> type (),
volume -> get_mesh_errors_count () > 0 ,
! volume -> config . has ( "extruder" ) ? 0 :
volume -> config . option < ConfigOptionInt > ( "extruder" ) -> value ,
2018-12-10 10:38:32 +01:00
false );
2019-04-29 15:27:59 +02:00
auto opt_keys = volume -> config . keys ();
2018-12-10 10:38:32 +01:00
if ( ! opt_keys . empty () && ! ( opt_keys . size () == 1 && opt_keys [ 0 ] == "extruder" )) {
select_item ( m_objects_model -> AddSettingsChild ( vol_item ));
2019-04-29 15:27:59 +02:00
Expand ( vol_item );
2018-12-10 10:38:32 +01:00
}
}
2018-10-08 16:27:38 +02:00
Expand ( item );
2018-10-05 23:29:15 +02:00
}
2018-11-06 09:06:26 +01:00
// add instances to the object, if it has those
2018-11-01 16:22:16 +01:00
if ( model_object -> instances . size () > 1 )
increase_object_instances ( obj_idx , model_object -> instances . size ());
2018-11-06 09:06:26 +01:00
// add settings to the object, if it has those
auto opt_keys = model_object -> config . keys ();
2018-11-07 13:40:24 +01:00
if ( ! opt_keys . empty () && ! ( opt_keys . size () == 1 && opt_keys [ 0 ] == "extruder" )) {
2018-11-06 09:06:26 +01:00
select_item ( m_objects_model -> AddSettingsChild ( item ));
2019-04-29 15:27:59 +02:00
Expand ( item );
2018-11-06 09:06:26 +01:00
}
2018-10-05 23:29:15 +02:00
#ifndef __WXOSX__
2018-10-11 15:57:09 +02:00
selection_changed ();
2018-10-05 23:29:15 +02:00
#endif //__WXMSW__
}
void ObjectList :: delete_object_from_list ()
{
2018-10-08 16:27:38 +02:00
auto item = GetSelection ();
2018-10-12 12:00:37 +02:00
if ( ! item )
2018-10-05 23:29:15 +02:00
return ;
2018-10-12 12:00:37 +02:00
if ( m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 ))
select_item ( m_objects_model -> Delete ( item ));
else
select_item ( m_objects_model -> Delete ( m_objects_model -> GetParent ( item )));
}
2018-10-05 23:29:15 +02:00
2018-10-12 12:00:37 +02:00
void ObjectList :: delete_object_from_list ( const size_t obj_idx )
{
select_item ( m_objects_model -> Delete ( m_objects_model -> GetItemById ( obj_idx )));
}
void ObjectList :: delete_volume_from_list ( const size_t obj_idx , const size_t vol_idx )
{
select_item ( m_objects_model -> Delete ( m_objects_model -> GetItemByVolumeId ( obj_idx , vol_idx )));
2018-10-05 23:29:15 +02:00
}
2018-11-13 13:34:31 +01:00
void ObjectList :: delete_instance_from_list ( const size_t obj_idx , const size_t inst_idx )
{
select_item ( m_objects_model -> Delete ( m_objects_model -> GetItemByInstanceId ( obj_idx , inst_idx )));
}
2018-11-13 14:17:35 +01:00
void ObjectList :: delete_from_model_and_list ( const ItemType type , const int obj_idx , const int sub_obj_idx )
{
if ( ! ( type & ( itObject | itVolume | itInstance )) )
return ;
if ( type & itObject ) {
del_object ( obj_idx );
delete_object_from_list ( obj_idx );
}
else {
del_subobject_from_object ( obj_idx , sub_obj_idx , type );
type == itVolume ? delete_volume_from_list ( obj_idx , sub_obj_idx ) :
delete_instance_from_list ( obj_idx , sub_obj_idx );
}
}
2018-11-14 08:53:56 +01:00
void ObjectList :: delete_from_model_and_list ( const std :: vector < ItemForDelete >& items_for_delete )
2018-11-13 14:17:35 +01:00
{
2018-11-14 11:22:13 +01:00
if ( items_for_delete . empty ())
return ;
for ( std :: vector < ItemForDelete >:: const_reverse_iterator item = items_for_delete . rbegin (); item != items_for_delete . rend (); ++ item )
2018-11-13 14:17:35 +01:00
{
2018-11-14 11:22:13 +01:00
if ( ! ( item -> type & ( itObject | itVolume | itInstance )))
2018-11-13 14:17:35 +01:00
continue ;
2018-11-14 11:22:13 +01:00
if ( item -> type & itObject ) {
del_object ( item -> obj_idx );
m_objects_model -> Delete ( m_objects_model -> GetItemById ( item -> obj_idx ));
2018-11-13 14:17:35 +01:00
}
else {
2018-11-23 11:54:06 +01:00
if ( ! del_subobject_from_object ( item -> obj_idx , item -> sub_obj_idx , item -> type ))
continue ;
2018-11-14 11:22:13 +01:00
if ( item -> type & itVolume )
2018-11-21 12:27:20 +01:00
{
2018-11-14 11:22:13 +01:00
m_objects_model -> Delete ( m_objects_model -> GetItemByVolumeId ( item -> obj_idx , item -> sub_obj_idx ));
2019-03-14 09:22:15 +01:00
if (( * m_objects )[ item -> obj_idx ] -> volumes . size () == 1 &&
( * m_objects )[ item -> obj_idx ] -> config . has ( "extruder" ))
2019-03-13 16:40:11 +01:00
{
const wxString extruder = wxString :: Format ( "%d" , ( * m_objects )[ item -> obj_idx ] -> config . option < ConfigOptionInt > ( "extruder" ) -> value );
m_objects_model -> SetValue ( extruder , m_objects_model -> GetItemById ( item -> obj_idx ), 1 );
}
2018-11-23 12:47:32 +01:00
wxGetApp (). plater () -> canvas3D () -> ensure_on_bed ( item -> obj_idx );
2018-11-21 12:27:20 +01:00
}
2018-11-13 14:17:35 +01:00
else
2018-11-14 11:22:13 +01:00
m_objects_model -> Delete ( m_objects_model -> GetItemByInstanceId ( item -> obj_idx , item -> sub_obj_idx ));
2018-11-13 14:17:35 +01:00
}
}
part_selection_changed ();
}
2018-10-05 23:29:15 +02:00
void ObjectList :: delete_all_objects_from_list ()
{
2019-05-20 18:49:32 +02:00
m_prevent_list_events = true ;
this -> UnselectAll ();
2018-10-05 23:29:15 +02:00
m_objects_model -> DeleteAll ();
2019-05-20 18:49:32 +02:00
m_prevent_list_events = false ;
2018-10-05 23:29:15 +02:00
part_selection_changed ();
}
2018-10-18 10:40:26 +02:00
void ObjectList :: increase_object_instances ( const size_t obj_idx , const size_t num )
2018-10-05 23:29:15 +02:00
{
2018-10-18 10:40:26 +02:00
select_item ( m_objects_model -> AddInstanceChild ( m_objects_model -> GetItemById ( obj_idx ), num ));
}
void ObjectList :: decrease_object_instances ( const size_t obj_idx , const size_t num )
{
select_item ( m_objects_model -> DeleteLastInstance ( m_objects_model -> GetItemById ( obj_idx ), num ));
2018-10-05 23:29:15 +02:00
}
void ObjectList :: unselect_objects ()
{
2018-10-08 16:27:38 +02:00
if ( ! GetSelection ())
2018-10-05 23:29:15 +02:00
return ;
m_prevent_list_events = true ;
2018-10-08 16:27:38 +02:00
UnselectAll ();
2018-10-05 23:29:15 +02:00
part_selection_changed ();
m_prevent_list_events = false ;
}
void ObjectList :: select_current_object ( int idx )
{
m_prevent_list_events = true ;
2018-10-08 16:27:38 +02:00
UnselectAll ();
2018-10-05 23:29:15 +02:00
if ( idx >= 0 )
2018-10-08 16:27:38 +02:00
Select ( m_objects_model -> GetItemById ( idx ));
2018-10-05 23:29:15 +02:00
part_selection_changed ();
m_prevent_list_events = false ;
}
void ObjectList :: select_current_volume ( int idx , int vol_idx )
{
if ( vol_idx < 0 ) {
select_current_object ( idx );
return ;
}
m_prevent_list_events = true ;
2018-10-08 16:27:38 +02:00
UnselectAll ();
2018-10-05 23:29:15 +02:00
if ( idx >= 0 )
2018-10-08 16:27:38 +02:00
Select ( m_objects_model -> GetItemByVolumeId ( idx , vol_idx ));
2018-10-05 23:29:15 +02:00
part_selection_changed ();
m_prevent_list_events = false ;
}
void ObjectList :: remove ()
{
2018-10-12 12:00:37 +02:00
if ( GetSelectedItemsCount () == 0 )
2018-10-05 23:29:15 +02:00
return ;
2018-10-12 12:00:37 +02:00
wxDataViewItemArray sels ;
GetSelections ( sels );
for ( auto & item : sels )
{
if ( m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 ))
2018-11-23 11:54:06 +01:00
delete_from_model_and_list ( itObject , m_objects_model -> GetIdByItem ( item ), - 1 );
2019-03-21 09:04:11 +01:00
else {
if ( sels . size () == 1 )
select_item ( m_objects_model -> GetParent ( item ));
2018-10-12 12:00:37 +02:00
del_subobject_item ( item );
2019-03-21 09:04:11 +01:00
}
2018-10-05 23:29:15 +02:00
}
}
void ObjectList :: init_objects ()
{
m_objects = wxGetApp (). model_objects ();
}
2018-10-04 16:43:10 +02:00
2018-10-10 16:22:20 +02:00
bool ObjectList :: multiple_selection () const
{
2018-10-11 15:57:09 +02:00
return GetSelectedItemsCount () > 1 ;
}
2019-03-29 14:36:09 +01:00
bool ObjectList :: is_selected ( const ItemType type ) const
{
const wxDataViewItem & item = GetSelection ();
if ( item )
return m_objects_model -> GetItemType ( item ) == type ;
return false ;
}
2018-10-11 15:57:09 +02:00
void ObjectList :: update_selections ()
{
2019-03-19 13:30:21 +01:00
const Selection & selection = wxGetApp (). plater () -> canvas3D () -> get_selection ();
2018-10-11 15:57:09 +02:00
wxDataViewItemArray sels ;
2019-04-02 16:33:52 +02:00
m_selection_mode = smInstance ;
2018-12-18 15:29:09 +01:00
// We doesn't update selection if SettingsItem for the current object/part is selected
if ( GetSelectedItemsCount () == 1 && m_objects_model -> GetItemType ( GetSelection ()) == itSettings )
{
const auto item = GetSelection ();
2019-06-13 15:35:10 +02:00
if ( selection . is_single_full_object ()) {
if ( m_objects_model -> GetIdByItem ( m_objects_model -> GetParent ( item )) == selection . get_object_idx ())
return ;
sels . Add ( m_objects_model -> GetItemById ( selection . get_object_idx ()));
}
2019-04-09 15:42:00 +02:00
if ( selection . is_single_volume () || selection . is_any_modifier ()) {
2018-12-18 15:29:09 +01:00
const auto gl_vol = selection . get_volume ( * selection . get_volume_idxs (). begin ());
if ( m_objects_model -> GetVolumeIdByItem ( m_objects_model -> GetParent ( item )) == gl_vol -> volume_idx ())
return ;
}
2019-05-03 16:05:49 +02:00
// but if there is selected only one of several instances by context menu,
// then select this instance in ObjectList
if ( selection . is_single_full_instance ())
sels . Add ( m_objects_model -> GetItemByInstanceId ( selection . get_object_idx (), selection . get_instance_idx ()));
2018-12-18 15:29:09 +01:00
}
2019-03-29 14:36:09 +01:00
else if ( selection . is_single_full_object () || selection . is_multiple_full_object ())
2018-11-08 15:45:55 +01:00
{
2019-03-29 14:36:09 +01:00
const Selection :: ObjectIdxsToInstanceIdxsMap & objects_content = selection . get_content ();
for ( const auto & object : objects_content ) {
if ( object . second . size () == 1 ) // object with 1 instance
sels . Add ( m_objects_model -> GetItemById ( object . first ));
else if ( object . second . size () > 1 ) // object with several instances
{
wxDataViewItemArray current_sels ;
GetSelections ( current_sels );
const wxDataViewItem frst_inst_item = m_objects_model -> GetItemByInstanceId ( object . first , 0 );
bool root_is_selected = false ;
for ( const auto & item : current_sels )
2019-04-02 16:33:52 +02:00
if ( item == m_objects_model -> GetParent ( frst_inst_item ) ||
item == m_objects_model -> GetTopParent ( frst_inst_item )) {
2019-03-29 14:36:09 +01:00
root_is_selected = true ;
2019-04-02 16:33:52 +02:00
sels . Add ( item );
2019-03-29 14:36:09 +01:00
break ;
}
if ( root_is_selected )
continue ;
const Selection :: InstanceIdxsList & instances = object . second ;
for ( const auto & inst : instances )
sels . Add ( m_objects_model -> GetItemByInstanceId ( object . first , inst ));
}
}
2018-11-08 15:45:55 +01:00
}
2019-04-09 15:42:00 +02:00
else if ( selection . is_any_volume () || selection . is_any_modifier ())
2019-03-29 14:36:09 +01:00
{
2018-11-05 13:53:30 +01:00
for ( auto idx : selection . get_volume_idxs ()) {
const auto gl_vol = selection . get_volume ( idx );
2019-03-29 14:36:09 +01:00
if ( gl_vol -> volume_idx () >= 0 )
2018-11-17 17:23:56 +01:00
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
// are not associated with ModelVolumes, but they are temporarily generated by the backend
// (for example, SLA supports or SLA pad).
2018-11-08 15:45:55 +01:00
sels . Add ( m_objects_model -> GetItemByVolumeId ( gl_vol -> object_idx (), gl_vol -> volume_idx ()));
2018-11-05 13:53:30 +01:00
}
2019-04-02 16:33:52 +02:00
m_selection_mode = smVolume ;
2018-10-11 15:57:09 +02:00
}
2019-03-29 14:36:09 +01:00
else if ( selection . is_single_full_instance () || selection . is_multiple_full_instance ())
{
2018-11-05 13:53:30 +01:00
for ( auto idx : selection . get_instance_idxs ()) {
sels . Add ( m_objects_model -> GetItemByInstanceId ( selection . get_object_idx (), idx ));
}
}
2018-11-30 11:21:25 +01:00
else if ( selection . is_mixed ())
{
2019-04-02 16:33:52 +02:00
const Selection :: ObjectIdxsToInstanceIdxsMap & objects_content_list = selection . get_content ();
2018-11-30 11:21:25 +01:00
for ( auto idx : selection . get_volume_idxs ()) {
const auto gl_vol = selection . get_volume ( idx );
const auto & glv_obj_idx = gl_vol -> object_idx ();
const auto & glv_ins_idx = gl_vol -> instance_idx ();
bool is_selected = false ;
for ( auto obj_ins : objects_content_list ) {
if ( obj_ins . first == glv_obj_idx ) {
if ( obj_ins . second . find ( glv_ins_idx ) != obj_ins . second . end ()) {
if ( glv_ins_idx == 0 && ( * m_objects )[ glv_obj_idx ] -> instances . size () == 1 )
sels . Add ( m_objects_model -> GetItemById ( glv_obj_idx ));
else
sels . Add ( m_objects_model -> GetItemByInstanceId ( glv_obj_idx , glv_ins_idx ));
is_selected = true ;
break ;
}
}
}
if ( is_selected )
continue ;
const auto & glv_vol_idx = gl_vol -> volume_idx ();
if ( glv_vol_idx == 0 && ( * m_objects )[ glv_obj_idx ] -> volumes . size () == 1 )
sels . Add ( m_objects_model -> GetItemById ( glv_obj_idx ));
else
sels . Add ( m_objects_model -> GetItemByVolumeId ( glv_obj_idx , glv_vol_idx ));
}
}
2019-04-02 16:33:52 +02:00
if ( sels . size () == 0 )
m_selection_mode = smUndef ;
2018-11-05 13:53:30 +01:00
2018-10-11 15:57:09 +02:00
select_items ( sels );
2018-12-13 13:05:47 +01:00
2019-03-07 15:26:19 +01:00
// Scroll selected Item in the middle of an object list
2019-04-09 13:13:42 +02:00
this -> EnsureVisible ( this -> GetCurrentItem ());
2018-10-11 15:57:09 +02:00
}
void ObjectList :: update_selections_on_canvas ()
{
2019-03-19 13:30:21 +01:00
Selection & selection = wxGetApp (). plater () -> canvas3D () -> get_selection ();
2018-10-11 15:57:09 +02:00
const int sel_cnt = GetSelectedItemsCount ();
if ( sel_cnt == 0 ) {
selection . clear ();
2018-12-03 13:29:07 +01:00
wxGetApp (). plater () -> canvas3D () -> update_gizmos_on_off_state ();
2018-10-11 15:57:09 +02:00
return ;
}
2019-03-29 10:48:20 +01:00
auto add_to_selection = [ this ]( const wxDataViewItem & item , Selection & selection , int instance_idx , bool as_single_selection )
2019-03-19 13:30:21 +01:00
{
2019-04-02 16:33:52 +02:00
const ItemType & type = m_objects_model -> GetItemType ( item );
if ( type == itInstanceRoot || m_objects_model -> GetParent ( item ) == wxDataViewItem ( 0 ) ) {
wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model -> GetParent ( item ) : item ;
selection . add_object ( m_objects_model -> GetIdByItem ( obj_item ), as_single_selection );
2018-10-11 15:57:09 +02:00
return ;
}
2019-04-02 16:33:52 +02:00
if ( type == itVolume ) {
2018-10-18 10:40:26 +02:00
const int obj_idx = m_objects_model -> GetIdByItem ( m_objects_model -> GetParent ( item ));
const int vol_idx = m_objects_model -> GetVolumeIdByItem ( item );
2019-03-29 10:48:20 +01:00
selection . add_volume ( obj_idx , vol_idx , std :: max ( instance_idx , 0 ), as_single_selection );
2018-10-18 10:40:26 +02:00
}
2019-04-02 16:33:52 +02:00
else if ( type == itInstance ) {
2018-10-18 10:40:26 +02:00
const int obj_idx = m_objects_model -> GetIdByItem ( m_objects_model -> GetTopParent ( item ));
const int inst_idx = m_objects_model -> GetInstanceIdByItem ( item );
selection . add_instance ( obj_idx , inst_idx , as_single_selection );
}
2018-10-11 15:57:09 +02:00
};
2019-04-04 09:35:13 +02:00
// stores current instance idx before to clear the selection
int instance_idx = selection . get_instance_idx ();
2018-10-11 15:57:09 +02:00
if ( sel_cnt == 1 ) {
wxDataViewItem item = GetSelection ();
2018-10-18 10:40:26 +02:00
if ( m_objects_model -> GetItemType ( item ) & ( itSettings | itInstanceRoot ))
2019-04-02 16:33:52 +02:00
add_to_selection ( m_objects_model -> GetParent ( item ), selection , instance_idx , true );
2018-10-11 15:57:09 +02:00
else
2019-04-04 09:35:13 +02:00
add_to_selection ( item , selection , instance_idx , true );
2019-03-29 10:48:20 +01:00
2018-12-03 13:29:07 +01:00
wxGetApp (). plater () -> canvas3D () -> update_gizmos_on_off_state ();
2019-04-09 11:38:56 +02:00
wxGetApp (). plater () -> canvas3D () -> render ();
2018-10-11 15:57:09 +02:00
return ;
}
2018-10-10 16:22:20 +02:00
wxDataViewItemArray sels ;
GetSelections ( sels );
2018-10-11 15:57:09 +02:00
selection . clear ();
for ( auto item : sels )
2019-03-29 10:48:20 +01:00
add_to_selection ( item , selection , instance_idx , false );
2018-10-11 15:57:09 +02:00
2018-12-03 13:29:07 +01:00
wxGetApp (). plater () -> canvas3D () -> update_gizmos_on_off_state ();
2019-04-10 14:03:40 +02:00
wxGetApp (). plater () -> canvas3D () -> render ();
2018-10-11 15:57:09 +02:00
}
void ObjectList :: select_item ( const wxDataViewItem & item )
{
2019-03-18 16:18:28 +01:00
if ( ! item . IsOk ()) { return ; }
2018-10-11 15:57:09 +02:00
m_prevent_list_events = true ;
UnselectAll ();
Select ( item );
part_selection_changed ();
m_prevent_list_events = false ;
}
void ObjectList :: select_items ( const wxDataViewItemArray & sels )
{
m_prevent_list_events = true ;
UnselectAll ();
SetSelections ( sels );
part_selection_changed ();
m_prevent_list_events = false ;
}
2018-10-12 12:00:37 +02:00
void ObjectList :: select_all ()
{
SelectAll ();
selection_changed ();
}
2018-11-08 15:45:55 +01:00
void ObjectList :: select_item_all_children ()
{
wxDataViewItemArray sels ;
// There is no selection before OR some object is selected => select all objects
if ( ! GetSelection () || m_objects_model -> GetItemType ( GetSelection ()) == itObject ) {
for ( int i = 0 ; i < m_objects -> size (); i ++ )
sels . Add ( m_objects_model -> GetItemById ( i ));
2019-04-02 16:33:52 +02:00
m_selection_mode = smInstance ;
2018-11-08 15:45:55 +01:00
}
else {
const auto item = GetSelection ();
// Some volume(instance) is selected => select all volumes(instances) inside the current object
2019-04-02 16:33:52 +02:00
if ( m_objects_model -> GetItemType ( item ) & ( itVolume | itInstance ))
2018-11-08 15:45:55 +01:00
m_objects_model -> GetChildren ( m_objects_model -> GetParent ( item ), sels );
2019-04-02 16:33:52 +02:00
m_selection_mode = m_objects_model -> GetItemType ( item ) & itVolume ? smVolume : smInstance ;
2018-11-08 15:45:55 +01:00
}
SetSelections ( sels );
selection_changed ();
}
2019-04-02 16:33:52 +02:00
// update selection mode for non-multiple selection
void ObjectList :: update_selection_mode ()
{
// All items are unselected
if ( ! GetSelection ())
{
2019-04-04 15:07:54 +02:00
m_last_selected_item = wxDataViewItem ( 0 );
2019-04-02 16:33:52 +02:00
m_selection_mode = smUndef ;
return ;
}
const ItemType type = m_objects_model -> GetItemType ( GetSelection ());
m_selection_mode = type & itSettings ? smUndef :
type & itVolume ? smVolume : smInstance ;
}
// check last selected item. If is it possible to select it
2019-04-04 15:07:54 +02:00
bool ObjectList :: check_last_selection ( wxString & msg_str )
2019-04-02 16:33:52 +02:00
{
if ( ! m_last_selected_item )
return true ;
2019-04-04 15:07:54 +02:00
const bool is_shift_pressed = wxGetKeyState ( WXK_SHIFT );
2019-04-02 16:33:52 +02:00
/* We can't mix Parts and Objects/Instances.
2019-04-04 15:07:54 +02:00
* So, show information about it
2019-04-02 16:33:52 +02:00
*/
const ItemType type = m_objects_model -> GetItemType ( m_last_selected_item );
2019-04-04 15:07:54 +02:00
// check a case of a selection of the Parts from different Objects
bool impossible_multipart_selection = false ;
if ( type & itVolume && m_selection_mode == smVolume )
{
wxDataViewItemArray sels ;
GetSelections ( sels );
for ( const auto & sel : sels )
if ( sel != m_last_selected_item &&
m_objects_model -> GetParent ( sel ) != m_objects_model -> GetParent ( m_last_selected_item ))
{
impossible_multipart_selection = true ;
break ;
}
}
if ( impossible_multipart_selection ||
type & itSettings ||
2019-04-02 16:33:52 +02:00
type & itVolume && m_selection_mode == smInstance ||
! ( type & itVolume ) && m_selection_mode == smVolume )
{
// Inform user why selection isn't complited
const wxString item_type = m_selection_mode == smInstance ? _ ( L ( "Object or Instance" )) : _ ( L ( "Part" ));
2019-04-04 15:07:54 +02:00
msg_str = wxString :: Format ( _ ( L ( "Unsupported selection" )) + " \n\n " +
_ ( L ( "You started your selection with %s Item." )) + " \n " +
_ ( L ( "In this mode you can select only other %s Items%s" )),
item_type , item_type ,
m_selection_mode == smInstance ? "." :
" " + _ ( L ( "of a current Object" )));
2019-04-02 16:33:52 +02:00
2019-04-04 15:07:54 +02:00
// Unselect last selected item, if selection is without SHIFT
if ( ! is_shift_pressed ) {
Unselect ( m_last_selected_item );
show_info ( this , msg_str , _ ( L ( "Info" )));
}
return is_shift_pressed ;
2019-04-02 16:33:52 +02:00
}
return true ;
}
2018-10-11 15:57:09 +02:00
void ObjectList :: fix_multiselection_conflicts ()
{
2019-04-02 16:33:52 +02:00
if ( GetSelectedItemsCount () <= 1 ) {
update_selection_mode ();
return ;
}
2019-04-04 15:07:54 +02:00
wxString msg_string ;
if ( ! check_last_selection ( msg_string ))
2018-10-11 15:57:09 +02:00
return ;
m_prevent_list_events = true ;
wxDataViewItemArray sels ;
GetSelections ( sels );
2019-04-04 15:07:54 +02:00
if ( m_selection_mode == smVolume )
{
// identify correct parent of the initial selected item
const wxDataViewItem & parent = m_objects_model -> GetParent ( m_last_selected_item == sels . front () ? sels . back () : sels . front ());
2019-03-29 14:36:09 +01:00
2019-04-04 15:07:54 +02:00
sels . clear ();
wxDataViewItemArray children ; // selected volumes from current parent
m_objects_model -> GetChildren ( parent , children );
2019-03-29 14:36:09 +01:00
2019-04-04 15:07:54 +02:00
for ( const auto child : children )
if ( IsSelected ( child ) && m_objects_model -> GetItemType ( child ) & itVolume )
sels . Add ( child );
2019-03-29 14:36:09 +01:00
2019-04-04 15:07:54 +02:00
// If some part is selected, unselect all items except of selected parts of the current object
UnselectAll ();
SetSelections ( sels );
}
else
{
for ( const auto item : sels )
2019-03-29 14:36:09 +01:00
{
2019-04-04 15:07:54 +02:00
if ( ! IsSelected ( item )) // if this item is unselected now (from previous actions)
continue ;
2019-04-02 16:33:52 +02:00
2019-04-04 15:07:54 +02:00
if ( m_objects_model -> GetItemType ( item ) & itSettings ) {
Unselect ( item );
continue ;
2019-04-02 16:33:52 +02:00
}
2019-04-04 15:07:54 +02:00
const wxDataViewItem & parent = m_objects_model -> GetParent ( item );
if ( parent != wxDataViewItem ( 0 ) && IsSelected ( parent ))
Unselect ( parent );
else
{
wxDataViewItemArray unsels ;
m_objects_model -> GetAllChildren ( item , unsels );
for ( const auto unsel_item : unsels )
Unselect ( unsel_item );
}
2019-04-02 16:33:52 +02:00
2019-04-04 15:07:54 +02:00
if ( m_objects_model -> GetItemType ( item ) & itVolume )
Unselect ( item );
m_selection_mode = smInstance ;
2019-03-29 14:36:09 +01:00
}
2018-10-11 15:57:09 +02:00
}
2019-04-04 15:07:54 +02:00
if ( ! msg_string . IsEmpty ())
show_info ( this , msg_string , _ ( L ( "Info" )));
if ( ! IsSelected ( m_last_selected_item ))
m_last_selected_item = wxDataViewItem ( 0 );
2018-10-11 15:57:09 +02:00
m_prevent_list_events = false ;
2018-10-10 16:22:20 +02:00
}
2018-11-02 23:27:31 +01:00
ModelVolume * ObjectList :: get_selected_model_volume ()
{
auto item = GetSelection ();
if ( ! item || m_objects_model -> GetItemType ( item ) != itVolume )
return nullptr ;
const auto vol_idx = m_objects_model -> GetVolumeIdByItem ( item );
const auto obj_idx = get_selected_obj_idx ();
if ( vol_idx < 0 || obj_idx < 0 )
return nullptr ;
return ( * m_objects )[ obj_idx ] -> volumes [ vol_idx ];
}
void ObjectList :: change_part_type ()
{
ModelVolume * volume = get_selected_model_volume ();
if ( ! volume )
return ;
2018-12-10 10:38:32 +01:00
2019-02-22 12:12:10 +01:00
const ModelVolumeType type = volume -> type ();
if ( type == ModelVolumeType :: MODEL_PART )
2018-12-10 10:38:32 +01:00
{
const int obj_idx = get_selected_obj_idx ();
if ( obj_idx < 0 ) return ;
int model_part_cnt = 0 ;
for ( auto vol : ( * m_objects )[ obj_idx ] -> volumes ) {
2019-02-22 12:12:10 +01:00
if ( vol -> type () == ModelVolumeType :: MODEL_PART )
2018-12-10 10:38:32 +01:00
++ model_part_cnt ;
}
if ( model_part_cnt == 1 ) {
Slic3r :: GUI :: show_error ( nullptr , _ ( L ( "You can't change a type of the last solid part of the object." )));
return ;
}
}
2018-11-02 23:27:31 +01:00
2019-05-13 14:27:51 +02:00
const wxString names [] = { _ ( L ( "Part" )), _ ( L ( "Modifier" )), _ ( L ( "Support Enforcer" )), _ ( L ( "Support Blocker" )) };
2018-11-02 23:27:31 +01:00
2019-05-13 15:07:53 +02:00
auto new_type = ModelVolumeType ( wxGetSingleChoiceIndex ( _ ( L ( "Type:" )), _ ( L ( "Select type of part" )), wxArrayString ( 4 , names ), int ( type )));
2018-11-02 23:27:31 +01:00
2019-02-22 12:12:10 +01:00
if ( new_type == type || new_type == ModelVolumeType :: INVALID )
2018-11-02 23:27:31 +01:00
return ;
2018-11-05 08:55:44 +01:00
const auto item = GetSelection ();
2019-02-22 12:12:10 +01:00
volume -> set_type ( new_type );
2018-11-05 08:55:44 +01:00
m_objects_model -> SetVolumeType ( item , new_type );
2018-11-02 23:27:31 +01:00
2019-04-22 01:51:10 +02:00
changed_object ( get_selected_obj_idx ());
2018-11-05 08:55:44 +01:00
// Update settings showing, if we have it
//(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
const auto settings_item = m_objects_model -> GetSettingsItem ( item );
if ( settings_item &&
2019-02-22 12:12:10 +01:00
( new_type == ModelVolumeType :: SUPPORT_ENFORCER || new_type == ModelVolumeType :: SUPPORT_BLOCKER )) {
2018-11-05 08:55:44 +01:00
m_objects_model -> Delete ( settings_item );
}
else if ( ! settings_item &&
2019-02-22 12:12:10 +01:00
( new_type == ModelVolumeType :: MODEL_PART || new_type == ModelVolumeType :: PARAMETER_MODIFIER )) {
2018-11-05 08:55:44 +01:00
select_item ( m_objects_model -> AddSettingsChild ( item ));
}
2018-11-02 23:27:31 +01:00
}
2018-11-12 13:47:24 +01:00
void ObjectList :: last_volume_is_deleted ( const int obj_idx )
{
2018-12-14 16:07:28 +01:00
if ( obj_idx < 0 || m_objects -> empty () ||
obj_idx <= m_objects -> size () ||
( * m_objects )[ obj_idx ] -> volumes . empty ())
2018-11-12 13:47:24 +01:00
return ;
auto volume = ( * m_objects )[ obj_idx ] -> volumes [ 0 ];
// clear volume's config values
volume -> config . clear ();
// set a default extruder value, since user can't add it manually
volume -> config . set_key_value ( "extruder" , new ConfigOptionInt ( 0 ));
}
2018-11-29 12:33:04 +01:00
bool ObjectList :: has_multi_part_objects ()
{
if ( ! m_objects_model -> IsEmpty ()) {
wxDataViewItemArray items ;
m_objects_model -> GetChildren ( wxDataViewItem ( 0 ), items );
for ( auto & item : items )
if ( m_objects_model -> GetItemByType ( item , itVolume ))
return true ;
}
return false ;
}
2018-11-29 15:00:23 +01:00
void ObjectList :: update_settings_items ()
{
2019-03-04 09:41:25 +01:00
m_prevent_canvas_selection_update = true ;
wxDataViewItemArray sel ;
GetSelections ( sel ); // stash selection
2018-11-29 15:00:23 +01:00
wxDataViewItemArray items ;
m_objects_model -> GetChildren ( wxDataViewItem ( 0 ), items );
for ( auto & item : items ) {
const wxDataViewItem & settings_item = m_objects_model -> GetSettingsItem ( item );
select_item ( settings_item ? settings_item : m_objects_model -> AddSettingsChild ( item ));
2019-03-06 09:28:55 +01:00
// If settings item was deleted from the list,
// it's need to be deleted from selection array, if it was there
if ( settings_item != m_objects_model -> GetSettingsItem ( item ) &&
sel . Index ( settings_item ) != wxNOT_FOUND ) {
sel . Remove ( settings_item );
}
2018-11-29 15:00:23 +01:00
}
2019-03-06 09:28:55 +01:00
2019-03-04 09:41:25 +01:00
// restore selection:
SetSelections ( sel );
m_prevent_canvas_selection_update = false ;
2018-11-29 15:00:23 +01:00
}
2019-01-25 10:34:32 +01:00
void ObjectList :: update_object_menu ()
{
2019-01-25 13:16:32 +01:00
append_menu_items_add_volume ( & m_menu_object );
2019-01-25 10:34:32 +01:00
}
2019-01-23 16:01:37 +01:00
void ObjectList :: instances_to_separated_object ( const int obj_idx , const std :: set < int >& inst_idxs )
2019-01-22 16:40:10 +01:00
{
2019-04-05 09:04:52 +02:00
if (( * m_objects )[ obj_idx ] -> instances . size () == inst_idxs . size ())
{
instances_to_separated_objects ( obj_idx );
return ;
}
2019-01-22 16:40:10 +01:00
// create new object from selected instance
ModelObject * model_object = ( * m_objects )[ obj_idx ] -> get_model () -> add_object ( * ( * m_objects )[ obj_idx ]);
2019-01-23 16:01:37 +01:00
for ( int inst_idx = model_object -> instances . size () - 1 ; inst_idx >= 0 ; inst_idx -- )
2019-01-22 16:40:10 +01:00
{
2019-01-23 16:01:37 +01:00
if ( find ( inst_idxs . begin (), inst_idxs . end (), inst_idx ) != inst_idxs . end ())
2019-01-22 16:40:10 +01:00
continue ;
2019-01-23 16:01:37 +01:00
model_object -> delete_instance ( inst_idx );
2019-01-22 16:40:10 +01:00
}
// Add new object to the object_list
add_object_to_list ( m_objects -> size () - 1 );
2019-01-23 16:01:37 +01:00
for ( std :: set < int >:: const_reverse_iterator it = inst_idxs . rbegin (); it != inst_idxs . rend (); ++ it )
{
// delete selected instance from the object
del_subobject_from_object ( obj_idx , * it , itInstance );
delete_instance_from_list ( obj_idx , * it );
}
}
2019-04-04 18:01:53 +02:00
void ObjectList :: instances_to_separated_objects ( const int obj_idx )
{
const int inst_cnt = ( * m_objects )[ obj_idx ] -> instances . size ();
for ( int i = inst_cnt - 1 ; i > 0 ; i -- )
{
// create new object from initial
ModelObject * object = ( * m_objects )[ obj_idx ] -> get_model () -> add_object ( * ( * m_objects )[ obj_idx ]);
for ( int inst_idx = object -> instances . size () - 1 ; inst_idx >= 0 ; inst_idx -- )
{
if ( inst_idx == i )
continue ;
// delete unnecessary instances
object -> delete_instance ( inst_idx );
}
// Add new object to the object_list
add_object_to_list ( m_objects -> size () - 1 );
// delete current instance from the initial object
del_subobject_from_object ( obj_idx , i , itInstance );
delete_instance_from_list ( obj_idx , i );
}
}
2019-01-23 16:01:37 +01:00
void ObjectList :: split_instances ()
{
2019-03-19 13:30:21 +01:00
const Selection & selection = wxGetApp (). plater () -> canvas3D () -> get_selection ();
2019-01-23 16:01:37 +01:00
const int obj_idx = selection . get_object_idx ();
if ( obj_idx == - 1 )
return ;
2019-04-04 18:01:53 +02:00
if ( selection . is_single_full_object ())
{
instances_to_separated_objects ( obj_idx );
return ;
}
2019-01-23 16:01:37 +01:00
const int inst_idx = selection . get_instance_idx ();
const std :: set < int > inst_idxs = inst_idx < 0 ?
selection . get_instance_idxs () :
std :: set < int > { inst_idx };
instances_to_separated_object ( obj_idx , inst_idxs );
2019-01-22 16:40:10 +01:00
}
2019-01-30 11:35:37 +01:00
void ObjectList :: rename_item ()
{
const wxDataViewItem item = GetSelection ();
if ( ! item || ! ( m_objects_model -> GetItemType ( item ) & ( itVolume | itObject )))
return ;
const wxString new_name = wxGetTextFromUser ( _ ( L ( "Enter new name" )) + ":" , _ ( L ( "Renaming" )),
m_objects_model -> GetName ( item ), this );
2019-01-30 13:26:02 +01:00
if ( new_name . IsEmpty ())
return ;
2019-01-30 11:35:37 +01:00
bool is_unusable_symbol = false ;
std :: string chosen_name = Slic3r :: normalize_utf8_nfc ( new_name . ToUTF8 ());
const char * unusable_symbols = "<>:/ \\ |?* \" " ;
for ( size_t i = 0 ; i < std :: strlen ( unusable_symbols ); i ++ ) {
if ( chosen_name . find_first_of ( unusable_symbols [ i ]) != std :: string :: npos ) {
is_unusable_symbol = true ;
}
}
if ( is_unusable_symbol ) {
show_error ( this , _ ( L ( "The supplied name is not valid;" )) + " \n " +
_ ( L ( "the following characters are not allowed:" )) + " <>:/ \\ |?* \" " );
return ;
}
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld ;
m_objects_model -> GetValue ( valueOld , item , 0 );
2019-04-25 01:45:00 +02:00
DataViewBitmapText bmpText ;
2019-01-30 11:35:37 +01:00
bmpText << valueOld ;
// But replace the text with the value entered by user.
bmpText . SetText ( new_name );
wxVariant value ;
value << bmpText ;
m_objects_model -> SetValue ( value , item , 0 );
m_objects_model -> ItemChanged ( item );
update_name_in_model ( item );
}
2019-04-24 13:33:05 +02:00
void ObjectList :: fix_through_netfabb ()
2019-01-30 16:27:07 +01:00
{
2019-04-24 13:33:05 +02:00
int obj_idx , vol_idx ;
get_selected_item_indexes ( obj_idx , vol_idx );
2019-03-13 15:34:27 +01:00
wxGetApp (). plater () -> fix_through_netfabb ( obj_idx , vol_idx );
2019-01-30 16:27:07 +01:00
2019-03-13 15:34:27 +01:00
update_item_error_icon ( obj_idx , vol_idx );
}
void ObjectList :: update_item_error_icon ( const int obj_idx , const int vol_idx ) const
{
const wxDataViewItem item = vol_idx < 0 ? m_objects_model -> GetItemById ( obj_idx ) :
m_objects_model -> GetItemByVolumeId ( obj_idx , vol_idx );
if ( ! item )
return ;
2019-04-29 15:27:59 +02:00
if ( get_mesh_errors_count ( obj_idx , vol_idx ) == 0 )
{
// if whole object has no errors more,
if ( get_mesh_errors_count ( obj_idx ) == 0 )
// unmark all items in the object
m_objects_model -> DeleteWarningIcon ( vol_idx >= 0 ? m_objects_model -> GetParent ( item ) : item , true );
else
// unmark fixed item only
m_objects_model -> DeleteWarningIcon ( item );
2019-03-13 15:34:27 +01:00
}
2019-01-30 16:27:07 +01:00
}
2019-04-25 01:45:00 +02:00
void ObjectList :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2019-04-25 16:19:50 +02:00
const int em = wxGetApp (). em_unit ();
2019-04-16 10:05:45 +02:00
// update min size !!! A width of control shouldn't be a wxDefaultCoord
2019-04-25 16:19:50 +02:00
SetMinSize ( wxSize ( 1 , 15 * em ));
2019-04-13 23:46:52 +02:00
2019-04-25 16:19:50 +02:00
GetColumn ( 0 ) -> SetWidth ( 19 * em );
GetColumn ( 1 ) -> SetWidth ( 8 * em );
GetColumn ( 2 ) -> SetWidth ( 2 * em );
2019-04-13 23:46:52 +02:00
2019-04-16 10:05:45 +02:00
// rescale all icons, used by ObjectList
2019-05-09 14:53:01 +02:00
msw_rescale_icons ();
2019-04-16 10:05:45 +02:00
2019-05-09 14:53:01 +02:00
// rescale/update existing items with bitmaps
2019-04-16 10:05:45 +02:00
m_objects_model -> Rescale ();
2019-05-09 14:53:01 +02:00
// rescale menus
for ( MenuWithSeparators * menu : { & m_menu_object ,
& m_menu_part ,
& m_menu_sla_object ,
& m_menu_instance })
msw_rescale_menu ( menu );
2019-04-13 23:46:52 +02:00
Layout ();
}
2018-12-10 13:39:56 +01:00
void ObjectList :: ItemValueChanged ( wxDataViewEvent & event )
2018-12-04 14:27:39 +01:00
{
2018-12-12 14:35:18 +01:00
if ( event . GetColumn () == 0 )
update_name_in_model ( event . GetItem ());
else if ( event . GetColumn () == 1 )
update_extruder_in_config ( event . GetItem ());
}
void ObjectList :: OnEditingDone ( wxDataViewEvent & event )
{
if ( event . GetColumn () != 0 )
return ;
2019-04-25 01:45:00 +02:00
const auto renderer = dynamic_cast < BitmapTextRenderer *> ( GetColumn ( 0 ) -> GetRenderer ());
2018-12-12 14:35:18 +01:00
if ( renderer -> WasCanceled ())
2019-05-23 12:18:28 +02:00
wxTheApp -> CallAfter ([ this ]{
show_error ( this , _ ( L ( "The supplied name is not valid;" )) + " \n " +
_ ( L ( "the following characters are not allowed:" )) + " <>:/ \\ |?* \" " );
});
2018-12-04 14:27:39 +01:00
}
2019-03-21 09:04:11 +01:00
void ObjectList :: show_multi_selection_menu ()
2019-03-14 13:15:04 +01:00
{
wxDataViewItemArray sels ;
GetSelections ( sels );
for ( const wxDataViewItem & item : sels )
if ( ! ( m_objects_model -> GetItemType ( item ) & ( itVolume | itObject )))
// show this menu only for Object(s)/Volume(s) selection
return ;
wxMenu * menu = new wxMenu ();
2019-03-21 09:04:11 +01:00
if ( extruders_count () > 1 )
append_menu_item ( menu , wxID_ANY , _ ( L ( "Set extruder for selected items" )),
_ ( L ( "Select extruder number for selected objects and/or parts" )),
[ this ]( wxCommandEvent & ) { extruder_selection (); }, "" , menu );
2019-03-14 13:15:04 +01:00
PopupMenu ( menu );
}
void ObjectList :: extruder_selection ()
{
wxArrayString choices ;
2019-05-10 12:54:20 +02:00
choices . Add ( _ ( L ( "default" )));
2019-03-14 13:15:04 +01:00
for ( int i = 1 ; i <= extruders_count (); ++ i )
choices . Add ( wxString :: Format ( "%d" , i ));
const wxString & selected_extruder = wxGetSingleChoice ( _ ( L ( "Select extruder number:" )),
_ ( L ( "This extruder will be set for selected items" )),
choices , 0 , this );
if ( selected_extruder . IsEmpty ())
return ;
2019-05-10 12:54:20 +02:00
const int extruder_num = selected_extruder == _ ( L ( "default" )) ? 0 : atoi ( selected_extruder . c_str ());
2019-03-14 13:15:04 +01:00
// /* Another variant for an extruder selection */
// extruder_num = wxGetNumberFromUser(_(L("Attention!!! \n"
// "It's a possibile to set an extruder number \n"
// "for whole Object(s) and/or object Part(s), \n"
// "not for an Instance. ")),
// _(L("Enter extruder number:")),
// _(L("This extruder will be set for selected items")),
// 1, 1, 5, this);
set_extruder_for_selected_items ( extruder_num );
}
void ObjectList :: set_extruder_for_selected_items ( const int extruder ) const
{
wxDataViewItemArray sels ;
GetSelections ( sels );
for ( const wxDataViewItem & item : sels )
{
2019-03-28 16:28:34 +01:00
DynamicPrintConfig & config = get_item_config ( item );
2019-03-14 13:15:04 +01:00
if ( config . has ( "extruder" )) {
if ( extruder == 0 )
config . erase ( "extruder" );
else
config . option < ConfigOptionInt > ( "extruder" ) -> value = extruder ;
}
else if ( extruder > 0 )
config . set_key_value ( "extruder" , new ConfigOptionInt ( extruder ));
2019-05-10 12:54:20 +02:00
const wxString extruder_str = extruder == 0 ? wxString ( _ ( L ( "default" ))) :
2019-03-14 13:15:04 +01:00
wxString :: Format ( "%d" , config . option < ConfigOptionInt > ( "extruder" ) -> value );
2019-03-28 16:28:34 +01:00
auto const type = m_objects_model -> GetItemType ( item );
/* We can change extruder for Object/Volume only.
* So, if Instance is selected, get its Object item and change it
*/
m_objects_model -> SetValue ( extruder_str , type & itInstance ? m_objects_model -> GetTopParent ( item ) : item , 1 );
const int obj_idx = type & itObject ? m_objects_model -> GetIdByItem ( item ) :
m_objects_model -> GetIdByItem ( m_objects_model -> GetTopParent ( item ));
2019-03-14 13:15:04 +01:00
wxGetApp (). plater () -> canvas3D () -> ensure_on_bed ( obj_idx );
}
// update scene
wxGetApp (). plater () -> update ();
}
2018-10-04 16:43:10 +02:00
} //namespace GUI
} //namespace Slic3r