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"
2019-05-28 16:38:04 +02:00
#include "GUI_ObjectLayers.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"
2019-08-22 11:47:58 +02:00
#ifdef __WXMSW__
#include "wx/uiaction.h"
#endif /* __WXMSW__ */
2018-10-04 16:43:10 +02:00
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
2019-07-11 23:46:23 +02:00
SettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
2019-01-25 16:39:50 +01:00
{
{ 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" } },
2019-07-29 16:56:00 +02:00
{ L ( "Wipe options" ) , { "wipe_into_infill" , "wipe_into_objects" } }
2019-01-25 16:39:50 +01:00
};
// pt_SLA
2019-07-11 23:46:23 +02:00
SettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
2019-01-25 16:39:50 +01:00
{
{ 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
}
2019-07-11 16:00:01 +02:00
static void take_snapshot ( const wxString & snapshot_name )
2019-07-04 17:33:19 +02:00
{
wxGetApp (). plater () -> take_snapshot ( snapshot_name );
}
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" );
2019-07-29 16:56:00 +02:00
CATEGORY_ICON [ L ( "Wipe options" )] = create_scaled_bitmap ( nullptr , "funnel" );
2019-04-12 11:28:07 +02:00
// 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
*/
2019-08-22 11:47:58 +02:00
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
int new_selected_column = - 1 ;
#endif __WXMSW__
2019-04-04 15:07:54 +02:00
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 ();
}
2019-08-22 11:47:58 +02:00
else {
wxDataViewItem new_selected_item = event . GetItem ();
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
wxDataViewItem item ;
wxDataViewColumn * col ;
this -> HitTest ( get_mouse_position_in_control (), item , col );
new_selected_column = ( col == nullptr ) ? - 1 : ( int ) col -> GetModelColumn ();
if ( new_selected_item == m_last_selected_item && m_last_selected_column != - 1 && m_last_selected_column != new_selected_column ) {
// Mouse clicked on another column of the active row. Simulate keyboard enter to enter the editing mode of the current column.
wxUIActionSimulator sim ;
sim . Char ( WXK_RETURN );
}
#endif __WXMSW__
m_last_selected_item = new_selected_item ;
}
#ifdef __WXMSW__
m_last_selected_column = new_selected_column ;
#endif __WXMSW__
2019-04-04 15:07:54 +02:00
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 ());
2019-08-20 13:01:01 +02:00
#endif //__WXMSW__
#ifndef __WXOSX__
list_manipulation ();
#endif //__WXOSX__
2018-10-04 16:43:10 +02:00
});
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
2019-07-05 20:27:44 +02:00
wxAcceleratorEntry entries [ 8 ];
2019-05-16 10:37:06 +02:00
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 );
2019-07-05 20:27:44 +02:00
entries [ 4 ]. Set ( wxACCEL_CTRL , ( int ) 'Z' , wxID_UNDO );
entries [ 5 ]. Set ( wxACCEL_CTRL , ( int ) 'Y' , wxID_REDO );
entries [ 6 ]. Set ( wxACCEL_NORMAL , WXK_DELETE , wxID_DELETE );
entries [ 7 ]. Set ( wxACCEL_NORMAL , WXK_BACK , wxID_DELETE );
wxAcceleratorTable accel ( 8 , entries );
2019-05-16 10:37:06 +02:00
SetAcceleratorTable ( accel );
2019-06-11 10:11:42 +02:00
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { this -> copy (); }, wxID_COPY );
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { this -> 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 );
2019-07-05 20:27:44 +02:00
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { this -> undo (); }, wxID_UNDO );
this -> Bind ( wxEVT_MENU , [ this ]( wxCommandEvent & evt ) { this -> redo (); }, wxID_REDO );
2019-05-16 10:37:06 +02:00
}
#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 ) {
2019-08-20 13:01:01 +02:00
set_tooltip_for_item ( 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 );
2019-08-22 11:47:58 +02:00
#ifdef __WXMSW__
Bind ( wxEVT_DATAVIEW_ITEM_EDITING_STARTED , & ObjectList :: OnEditingStarted , this );
#endif /* __WXMSW__ */
Bind ( wxEVT_DATAVIEW_ITEM_EDITING_DONE , & ObjectList :: OnEditingDone , this );
2018-12-12 14:35:18 +01:00
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-08-06 19:53:20 +02:00
Bind ( wxEVT_SIZE , ([ this ]( wxSizeEvent & e ) {
#ifdef __WXGTK__
// On GTK, the EnsureVisible call is postponed to Idle processing (see wxDataViewCtrl::m_ensureVisibleDefered).
// So the postponed EnsureVisible() call is planned for an item, which may not exist at the Idle processing time, if this wxEVT_SIZE
// event is succeeded by a delete of the currently active item. We are trying our luck by postponing the wxEVT_SIZE triggered EnsureVisible(),
// which seems to be working as of now.
this -> CallAfter ([ this ](){ this -> EnsureVisible ( this -> GetCurrentItem ()); });
#else
this -> EnsureVisible ( this -> GetCurrentItem ());
#endif
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
2019-08-01 14:58:04 +02:00
// column ItemName(Icon+Text) of the view control:
2018-10-04 16:43:10 +02:00
// And Icon can be consisting of several bitmaps
2019-04-25 01:45:00 +02:00
AppendColumn ( new wxDataViewColumn ( _ ( L ( "Name" )), new BitmapTextRenderer (),
2019-08-01 14:58:04 +02:00
colName , 20 * wxGetApp (). em_unit () /*200*/ , wxALIGN_LEFT , wxDATAVIEW_COL_RESIZABLE ));
2018-10-04 16:43:10 +02:00
2019-08-01 14:58:04 +02:00
// column PrintableProperty (Icon) of the view control:
AppendBitmapColumn ( " " , colPrint , wxDATAVIEW_CELL_INERT , int ( 2 * wxGetApp (). em_unit ()),
wxALIGN_CENTER_HORIZONTAL , wxDATAVIEW_COL_RESIZABLE );
// column Extruder 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
2019-08-01 14:58:04 +02:00
// column ItemEditing of the view control:
AppendBitmapColumn ( "Editing" , colEditing , 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-08-20 13:01:01 +02:00
if ( ! item || GetSelectedItemsCount () > 1 )
2019-05-20 13:52:58 +02:00
{
GetMainWindow () -> SetToolTip ( "" ); // hide tooltip
return ;
}
2019-08-20 13:01:01 +02:00
wxString tooltip = "" ;
if ( col -> GetTitle () == _ ( L ( "Editing" )))
#ifdef __WXOSX__
tooltip = _ ( L ( "Right button click the icon to change the object settings" ));
#else
tooltip = _ ( L ( "Click the icon to change the object settings" ));
2019-04-30 15:46:25 +02:00
#endif //__WXMSW__
2019-08-20 13:01:01 +02:00
else if ( col -> GetTitle () == " " )
#ifdef __WXOSX__
tooltip = _ ( L ( "Right button click the icon to change the object printable property" ));
#else
tooltip = _ ( L ( "Click the icon to change the object printable property" ));
#endif //__WXMSW__
else if ( col -> GetTitle () == _ ( "Name" ) && ( pt . x >= 2 * wxGetApp (). em_unit () && pt . x <= 4 * wxGetApp (). em_unit ()))
{
2019-04-24 13:33:05 +02:00
int obj_idx , vol_idx ;
get_selected_item_indexes ( obj_idx , vol_idx , item );
2019-08-20 13:01:01 +02:00
tooltip = get_mesh_errors_list ( obj_idx , vol_idx );
2018-10-05 23:29:15 +02:00
}
2019-08-20 13:01:01 +02:00
GetMainWindow () -> SetToolTip ( 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 );
2019-07-22 09:41:34 +02:00
const int obj_idx = m_objects_model -> GetObjectIdByItem ( item );
2019-03-28 16:28:34 +01:00
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 :
2019-06-06 14:14:29 +02:00
type & itLayer ? ( * m_objects )[ obj_idx ] -> layer_config_ranges [ m_objects_model -> GetLayerRangeByItem ( item )] :
2019-05-23 15:12:19 +02:00
( * 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-08-01 14:58:04 +02:00
wxDataViewColumn * column = new wxDataViewColumn ( _ ( L ( "Extruder" )), c , colExtruder ,
2019-02-06 11:07:32 +01:00
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 );
2019-08-01 14:58:04 +02:00
m_objects_model -> SetValue ( extruder , item , colExtruder );
2018-12-10 17:00:28 +01:00
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 );
2019-08-01 14:58:04 +02:00
m_objects_model -> SetValue ( extruder , item , colExtruder );
2018-12-10 17:00:28 +01:00
}
}
}
}
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 ;
2019-08-01 14:58:04 +02:00
wxDataViewChoiceRenderer * ch_render = dynamic_cast < wxDataViewChoiceRenderer *> ( GetColumn ( colExtruder ) -> GetRenderer ());
2018-12-10 17:00:28 +01:00
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 );
2019-08-01 14:58:04 +02:00
// delete old extruder column
DeleteColumn ( GetColumn ( colExtruder ));
// insert new created extruder column
InsertColumn ( colExtruder , 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
2019-08-01 14:58:04 +02:00
GetColumn ( colEditing ) -> 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
{
2019-08-01 14:58:04 +02:00
GetColumn ( colExtruder ) -> 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 ;
2019-06-06 14:14:29 +02:00
const ItemType item_type = m_objects_model -> GetItemType ( item );
if ( item_type & itObject ) {
2018-12-10 14:13:01 +01:00
const int obj_idx = m_objects_model -> GetIdByItem ( item );
m_config = & ( * m_objects )[ obj_idx ] -> config ;
}
else {
2019-06-06 14:14:29 +02:00
const int obj_idx = m_objects_model -> GetIdByItem ( m_objects_model -> GetTopParent ( item ));
if ( item_type & itVolume )
{
2018-12-10 14:13:01 +01:00
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 ;
2019-06-06 14:14:29 +02:00
}
else if ( item_type & itLayer )
m_config = & get_item_config ( item );
2018-12-10 14:13:01 +01:00
}
wxVariant variant ;
2019-08-01 14:58:04 +02:00
m_objects_model -> GetValue ( variant , item , colExtruder );
2018-12-10 14:13:01 +01:00
const wxString selection = variant . GetString ();
2018-10-05 23:29:15 +02:00
if ( ! m_config || selection . empty ())
return ;
2019-06-26 13:12:25 +02: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 ;
2019-07-22 13:38:53 +02:00
const int volume_id = m_objects_model -> GetVolumeIdByItem ( item );
2018-12-12 14:35:18 +01:00
2019-08-01 15:38:00 +02:00
take_snapshot ( volume_id < 0 ? _ ( L ( "Rename Object" )) : _ ( L ( "Rename Sub-object" )));
2019-07-22 13:38:53 +02:00
if ( m_objects_model -> GetItemType ( item ) & itObject ) {
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 ;
}
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" );
2019-07-29 16:56:00 +02:00
CATEGORY_ICON [ L ( "Wipe options" )] = create_scaled_bitmap ( nullptr , "funnel" );
2019-04-16 10:05:45 +02:00
// 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 );
}
2019-07-02 15:26:11 +02:00
if ( const wxDataViewItem item = GetSelection ())
{
const ItemType type = m_objects_model -> GetItemType ( item );
// to correct visual hints for layers editing on the Scene
if ( type & ( itLayer | itLayerRoot )) {
wxGetApp (). obj_layers () -> reset_selection ();
if ( type & itLayerRoot )
wxGetApp (). plater () -> canvas3D () -> handle_sidebar_focus_event ( "" , false );
else {
wxGetApp (). obj_layers () -> set_selectable_range ( m_objects_model -> GetLayerRangeByItem ( item ));
wxGetApp (). obj_layers () -> update_scene_from_editor_selection ();
}
}
}
2018-10-05 23:29:15 +02:00
part_selection_changed ();
}
2019-06-10 15:22:09 +02:00
void ObjectList :: fill_layer_config_ranges_cache ()
{
wxDataViewItemArray sel_layers ;
GetSelections ( sel_layers );
const int obj_idx = m_objects_model -> GetObjectIdByItem ( sel_layers [ 0 ]);
if ( obj_idx < 0 || ( int ) m_objects -> size () <= obj_idx )
return ;
const t_layer_config_ranges & ranges = object ( obj_idx ) -> layer_config_ranges ;
m_layer_config_ranges_cache . clear ();
for ( const auto layer_item : sel_layers )
if ( m_objects_model -> GetItemType ( layer_item ) & itLayer ) {
auto range = m_objects_model -> GetLayerRangeByItem ( layer_item );
auto it = ranges . find ( range );
if ( it != ranges . end ())
m_layer_config_ranges_cache [ it -> first ] = it -> second ;
}
}
void ObjectList :: paste_layers_into_list ()
{
const int obj_idx = m_objects_model -> GetObjectIdByItem ( GetSelection ());
if ( obj_idx < 0 || ( int ) m_objects -> size () <= obj_idx ||
m_layer_config_ranges_cache . empty () || printer_technology () == ptSLA )
return ;
const wxDataViewItem object_item = m_objects_model -> GetItemById ( obj_idx );
wxDataViewItem layers_item = m_objects_model -> GetLayerRootItem ( object_item );
if ( layers_item )
m_objects_model -> Delete ( layers_item );
t_layer_config_ranges & ranges = object ( obj_idx ) -> layer_config_ranges ;
// and create Layer item(s) according to the layer_config_ranges
for ( const auto range : m_layer_config_ranges_cache )
ranges . emplace ( range );
layers_item = add_layer_root_item ( object_item );
changed_object ( obj_idx );
select_item ( layers_item );
#ifndef __WXOSX__
selection_changed ();
#endif //no __WXOSX__
}
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 ;
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-07-11 23:46:23 +02:00
add_settings_item ( vol_item , & volume -> config );
2019-04-10 08:40:58 +02:00
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 & )
2019-08-20 13:01:01 +02:00
{
list_manipulation ();
}
void ObjectList :: list_manipulation ()
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 );
2019-06-06 14:14:29 +02:00
#ifdef __WXOSX__ // temporary workaround for OSX
2019-08-05 11:02:56 +02:00
// after Yosemite OS X version, HitTest return undefined item
if ( ! item ) item = GetSelection ();
2018-10-05 23:29:15 +02:00
#endif // __WXOSX__
2019-08-05 11:02:56 +02:00
if ( ! item ) {
printf ( "undefined item \n " );
return ;
}
2018-10-05 23:29:15 +02:00
const wxString title = col -> GetTitle ();
2019-08-05 10:05:28 +02:00
if ( title == " " )
toggle_printable_state ( item );
2019-08-01 14:58:04 +02:00
else if ( title == _ ( "Editing" ))
2018-10-05 23:29:15 +02:00
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 );
2019-06-03 15:35:21 +02:00
if ( ! ( type & ( itObject | itVolume | itLayer | 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 :
2019-06-03 15:35:21 +02:00
type & itLayer ? & m_menu_layer :
2019-01-23 16:01:37 +01:00
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-06-11 10:11:42 +02:00
void ObjectList :: copy ()
{
2019-07-31 09:46:45 +02:00
// if (m_selection_mode & smLayer)
// fill_layer_config_ranges_cache();
// else {
// m_layer_config_ranges_cache.clear();
2019-06-11 10:11:42 +02:00
wxPostEvent (( wxEvtHandler * ) wxGetApp (). plater () -> canvas3D () -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_COPY ));
2019-07-31 09:46:45 +02:00
// }
2019-06-11 10:11:42 +02:00
}
void ObjectList :: paste ()
{
2019-07-31 09:46:45 +02:00
// if (!m_layer_config_ranges_cache.empty())
// paste_layers_into_list();
// else
2019-06-11 10:11:42 +02:00
wxPostEvent (( wxEvtHandler * ) wxGetApp (). plater () -> canvas3D () -> get_wxglcanvas (), SimpleEvent ( EVT_GLTOOLBAR_PASTE ));
}
2019-07-05 20:27:44 +02:00
void ObjectList :: undo ()
{
wxGetApp (). plater () -> undo ();
}
void ObjectList :: redo ()
{
wxGetApp (). plater () -> redo ();
}
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-06-11 10:11:42 +02:00
else if ( wxGetKeyState ( wxKeyCode ( 'C' )) && wxGetKeyState ( WXK_CONTROL ))
copy ();
2019-04-16 13:47:37 +02:00
else if ( wxGetKeyState ( wxKeyCode ( 'V' )) && wxGetKeyState ( WXK_CONTROL ))
2019-06-11 10:11:42 +02:00
paste ();
2019-07-05 20:27:44 +02:00
else if ( wxGetKeyState ( wxKeyCode ( 'Y' )) && wxGetKeyState ( WXK_CONTROL ))
redo ();
else if ( wxGetKeyState ( wxKeyCode ( 'Z' )) && wxGetKeyState ( WXK_CONTROL ))
undo ();
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-07-22 13:38:53 +02:00
Plater :: TakeSnapshot snapshot ( wxGetApp (). plater (), _ ( L ( "Instances to Separated Objects" )));
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-07-22 13:38:53 +02:00
take_snapshot ( _ ( L ( "Remove Volume(s)" )));
2019-07-04 17:33:19 +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-07-11 23:46:23 +02:00
const SettingsBundle & 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-07-11 23:46:23 +02:00
SettingsBundle& 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-08-06 19:01:59 +02:00
static bool improper_category ( const std :: string & category , const int extruders_cnt )
{
return category . empty () || ( extruders_cnt == 1 && ( category == "Extruders" || category == "Wipe options" ));
}
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 ;
2019-08-06 19:01:59 +02:00
if ( improper_category ( category , extruders_cnt ))
continue ;
2018-10-05 23:29:15 +02:00
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 ;
2019-07-22 09:41:34 +02:00
wxDataViewItem item = GetSelection ();
2018-10-05 23:29:15 +02:00
2019-08-01 15:38:00 +02:00
const ItemType item_type = m_objects_model -> GetItemType ( item );
2018-10-05 23:29:15 +02:00
settings_menu_hierarchy settings_menu ;
2019-08-01 15:38:00 +02:00
const bool is_part = item_type & ( itVolume | itLayer );
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 ;
2019-07-22 09:41:34 +02:00
if ( ! m_config )
m_config = & get_item_config ( item );
assert ( m_config );
2018-10-05 23:29:15 +02:00
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-07-11 23:46:23 +02:00
SettingsBundle& 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
2019-08-01 15:38:00 +02:00
const wxString snapshot_text = item_type & itLayer ? _ ( L ( "Add Settings for Layers" )) :
item_type & itVolume ? _ ( L ( "Add Settings for Sub-object" )) :
_ ( L ( "Add Settings for Object" ));
take_snapshot ( snapshot_text );
2019-07-10 11:28:11 +02:00
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
}
2019-07-11 23:46:23 +02:00
// Add settings item for object/sub-object and show them
2019-08-01 15:38:00 +02:00
if ( ! ( item_type & ( itObject | itVolume | itLayer )))
2019-07-22 09:41:34 +02:00
item = m_objects_model -> GetTopParent ( item );
show_settings ( add_settings_item ( item , m_config ));
2019-01-25 16:39:50 +01:00
}
void ObjectList :: get_freq_settings_choice ( const wxString & bundle_name )
{
2019-06-05 13:52:53 +02:00
std :: vector < std :: string > options = get_options_for_bundle ( bundle_name );
2019-07-22 09:41:34 +02:00
wxDataViewItem item = GetSelection ();
2019-06-05 13:52:53 +02:00
2019-08-01 15:38:00 +02:00
ItemType item_type = m_objects_model -> GetItemType ( item );
2019-06-05 16:47:09 +02:00
/* Because of we couldn't edited layer_height for ItVolume from settings list,
2019-06-05 13:52:53 +02:00
* correct options according to the selected item type :
* remove "layer_height" option
*/
2019-08-01 15:38:00 +02:00
if (( item_type & itVolume ) && bundle_name == _ ( "Layers and Perimeters" )) {
2019-06-05 13:52:53 +02:00
const auto layer_height_it = std :: find ( options . begin (), options . end (), "layer_height" );
if ( layer_height_it != options . end ())
options . erase ( layer_height_it );
}
2019-01-25 16:39:50 +01:00
2019-07-22 09:41:34 +02:00
if ( ! m_config )
m_config = & get_item_config ( item );
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-08-01 15:38:00 +02:00
const wxString snapshot_text = item_type & itLayer ? _ ( L ( "Add Settings Bundle for Layers" )) :
item_type & itVolume ? _ ( L ( "Add Settings Bundle for Sub-object" )) :
_ ( L ( "Add Settings Bundle for Object" ));
take_snapshot ( snapshot_text );
2019-07-10 11:28:11 +02:00
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
}
2019-07-11 23:46:23 +02:00
// Add settings item for object/sub-object and show them
2019-08-01 15:38:00 +02:00
if ( ! ( item_type & ( itObject | itVolume | itLayer )))
2019-07-22 09:41:34 +02:00
item = m_objects_model -> GetTopParent ( item );
show_settings ( add_settings_item ( item , m_config ));
2019-01-25 16:39:50 +01:00
}
2019-07-11 23:46:23 +02:00
void ObjectList :: show_settings ( const wxDataViewItem settings_item )
2019-01-25 16:39:50 +01:00
{
2019-07-11 23:46:23 +02:00
if ( ! settings_item )
return ;
select_item ( settings_item );
// update object selection on Plater
if ( ! m_prevent_canvas_selection_update )
update_selections_on_canvas ();
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-05-27 13:07:37 +02:00
wxMenuItem * ObjectList :: append_menu_item_layers_editing ( wxMenu * menu )
{
return append_menu_item ( menu , wxID_ANY , _ ( L ( "Edit Layers" )), "" ,
2019-07-30 15:45:32 +02:00
[ this ]( wxCommandEvent & ) { layers_editing (); }, "edit_layers_all" , menu );
2019-05-27 13:07:37 +02: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-07-31 10:12:13 +02:00
wxMenuItem * ObjectList :: append_menu_item_printable ( wxMenu * menu , wxWindow * parent )
{
return append_menu_check_item ( menu , wxID_ANY , _ ( L ( "Printable" )), "" , [ this ]( wxCommandEvent & ) {
2019-07-31 11:01:50 +02:00
wxGetApp (). plater () -> canvas3D () -> get_selection (). toggle_instance_printable_state ();
2019-07-31 10:12:13 +02:00
}, menu );
}
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
2019-05-27 13:07:37 +02:00
append_menu_item_split ( menu );
menu -> AppendSeparator ();
// Layers Editing for object
append_menu_item_layers_editing ( 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
2019-05-27 13:07:37 +02:00
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-07-11 23:46:23 +02:00
const SettingsBundle & 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 ) {
2019-08-06 19:01:59 +02:00
if ( improper_category ( it . first , extruders_cnt ))
2019-01-25 16:39:50 +01:00
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-07-11 23:46:23 +02:00
const SettingsBundle& 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) {
2019-08-06 19:01:59 +02:00
if (improper_category(it.first, extruders_cnt))
2019-01-28 12:13:53 +01:00
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
}
2019-07-25 17:24:00 +02:00
void ObjectList :: update_opt_keys ( t_config_option_keys & opt_keys , const bool is_object )
2018-11-29 15:00:23 +01:00
{
2019-07-25 17:24:00 +02:00
auto full_current_opts = get_options ( ! is_object );
2018-11-29 15:00:23 +01:00
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-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Load Part" )));
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 );
2019-08-16 13:14:51 +02:00
if ( type == ModelVolumeType :: MODEL_PART )
// update printable state on canvas
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_object (( size_t ) 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 ;
2019-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Add Generic Subobject" )));
2019-02-28 11:20:01 +01:00
// 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 );
2019-08-16 13:14:51 +02:00
if ( type == ModelVolumeType :: MODEL_PART )
// update printable state on canvas
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_object (( size_t ) 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 );
2019-07-23 17:09:19 +02:00
if ( type == itUndef )
2018-10-05 23:29:15 +02:00
return ;
2019-06-06 14:14:29 +02:00
if ( type & itSettings )
del_settings_from_config ( m_objects_model -> GetParent ( item ));
else if ( type & itInstanceRoot && obj_idx != - 1 )
2018-10-16 11:08:37 +02:00
del_instances_from_object ( obj_idx );
2019-06-06 14:14:29 +02:00
else if ( type & itLayerRoot && obj_idx != - 1 )
2019-05-30 16:53:17 +02:00
del_layers_from_object ( obj_idx );
2019-06-06 14:14:29 +02:00
else if ( type & itLayer && obj_idx != - 1 )
del_layer_from_object ( obj_idx , m_objects_model -> GetLayerRangeByItem ( item ));
2018-10-16 11:08:37 +02:00
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
2019-06-06 14:14:29 +02:00
if ( type & itVolume && ( * m_objects )[ obj_idx ] -> get_mesh_errors_count () == 0 )
2019-04-29 15:27:59 +02:00
m_objects_model -> DeleteWarningIcon ( m_objects_model -> GetParent ( item ));
2019-08-02 17:53:12 +02:00
// If last two Instances of object is selected, show the message about impossible action
bool show_msg = false ;
if ( type & itInstance ) {
wxDataViewItemArray instances ;
m_objects_model -> GetChildren ( m_objects_model -> GetParent ( item ), instances );
if ( instances . Count () == 2 && IsSelected ( instances [ 0 ]) && IsSelected ( instances [ 1 ]))
show_msg = true ;
}
2018-10-12 12:00:37 +02:00
m_objects_model -> Delete ( item );
2019-08-02 17:53:12 +02:00
if ( show_msg )
2019-08-05 14:54:29 +02:00
Slic3r :: GUI :: show_error ( nullptr , _ ( L ( "Last instance of an object cannot be deleted." )));
2018-10-05 23:29:15 +02:00
}
2019-06-06 14:14:29 +02:00
void ObjectList :: del_settings_from_config ( const wxDataViewItem & parent_item )
2018-10-05 23:29:15 +02:00
{
2019-06-06 14:14:29 +02:00
const bool is_layer_settings = m_objects_model -> GetItemType ( parent_item ) == itLayer ;
const int opt_cnt = m_config -> keys (). size ();
if ( opt_cnt == 1 && m_config -> has ( "extruder" ) ||
is_layer_settings && opt_cnt == 2 && m_config -> has ( "extruder" ) && m_config -> has ( "layer_height" ))
2018-10-05 23:29:15 +02:00
return ;
2019-06-06 14:14:29 +02:00
2019-07-11 18:42:02 +02:00
take_snapshot ( _ ( L ( "Delete Settings" )));
2018-10-05 23:29:15 +02:00
int extruder = - 1 ;
if ( m_config -> has ( "extruder" ))
extruder = m_config -> option < ConfigOptionInt > ( "extruder" ) -> value ;
2019-06-06 14:14:29 +02:00
coordf_t layer_height = 0.0 ;
if ( is_layer_settings )
layer_height = m_config -> opt_float ( "layer_height" );
2018-10-05 23:29:15 +02:00
m_config -> clear ();
if ( extruder >= 0 )
m_config -> set_key_value ( "extruder" , new ConfigOptionInt ( extruder ));
2019-06-06 14:14:29 +02:00
if ( is_layer_settings )
m_config -> set_key_value ( "layer_height" , new ConfigOptionFloat ( layer_height ));
2018-10-05 23:29:15 +02:00
}
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
2019-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Delete All Instances from Object" )));
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
}
2019-06-06 14:14:29 +02:00
void ObjectList :: del_layer_from_object ( const int obj_idx , const t_layer_height_range & layer_range )
{
const auto del_range = object ( obj_idx ) -> layer_config_ranges . find ( layer_range );
if ( del_range == object ( obj_idx ) -> layer_config_ranges . end ())
return ;
2019-07-11 18:42:02 +02:00
take_snapshot ( _ ( L ( "Delete Layers Range" )));
2019-06-06 14:14:29 +02:00
object ( obj_idx ) -> layer_config_ranges . erase ( del_range );
changed_object ( obj_idx );
}
2019-05-30 16:53:17 +02:00
void ObjectList :: del_layers_from_object ( const int obj_idx )
{
2019-06-03 15:35:21 +02:00
object ( obj_idx ) -> layer_config_ranges . clear ();
2019-05-30 16:53:17 +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 ) {
2019-08-02 17:53:12 +02:00
Slic3r :: GUI :: show_error ( nullptr , _ ( L ( "From Object List You can't delete the last solid part from object." )));
2018-10-16 11:08:37 +02:00
return false ;
}
2019-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Delete Subobject" )));
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 ) {
2019-08-05 14:54:29 +02:00
Slic3r :: GUI :: show_error ( nullptr , _ ( L ( "Last instance of an object cannot be deleted." )));
2018-10-18 10:40:26 +02:00
return false ;
}
2019-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Delete Instance" )));
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-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Split to Parts" )));
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-07-11 23:46:23 +02:00
add_settings_item ( vol_item , & volume -> config );
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
}
2019-05-27 13:07:37 +02:00
void ObjectList :: layers_editing ()
{
const auto item = GetSelection ();
const int obj_idx = get_selected_obj_idx ();
if ( ! item || obj_idx < 0 )
return ;
2019-05-31 15:29:09 +02:00
const wxDataViewItem obj_item = m_objects_model -> GetTopParent ( item );
2019-05-31 10:54:52 +02:00
wxDataViewItem layers_item = m_objects_model -> GetLayerRootItem ( obj_item );
2019-05-31 15:29:09 +02:00
// if it doesn't exist now
2019-05-27 13:07:37 +02:00
if ( ! layers_item . IsOk ())
2019-05-28 16:38:04 +02:00
{
2019-06-03 15:35:21 +02:00
t_layer_config_ranges & ranges = object ( obj_idx ) -> layer_config_ranges ;
2019-06-10 10:48:43 +02:00
// set some default value
2019-07-11 18:42:02 +02:00
if ( ranges . empty ()) {
take_snapshot ( _ ( L ( "Add Layers" )));
2019-07-02 15:26:11 +02:00
ranges [{ 0.0f , 2.0f }] = get_default_layer_config ( obj_idx );
2019-07-11 18:42:02 +02:00
}
2019-06-03 15:35:21 +02:00
2019-06-10 10:48:43 +02:00
// create layer root item
layers_item = add_layer_root_item ( obj_item );
2019-05-30 14:41:16 +02:00
}
2019-06-10 10:48:43 +02:00
if ( ! layers_item . IsOk ())
return ;
2019-05-30 14:41:16 +02:00
2019-07-04 14:25:40 +02:00
// to correct visual hints for layers editing on the Scene, reset previous selection
wxGetApp (). obj_layers () -> reset_selection ();
wxGetApp (). plater () -> canvas3D () -> handle_sidebar_focus_event ( "" , false );
2019-05-31 15:29:09 +02:00
// select LayerRoor item and expand
2019-05-27 13:07:37 +02:00
select_item ( layers_item );
2019-05-31 15:29:09 +02:00
Expand ( layers_item );
2019-05-27 13:07:37 +02:00
}
2019-06-10 10:48:43 +02:00
wxDataViewItem ObjectList :: add_layer_root_item ( const wxDataViewItem obj_item )
{
const int obj_idx = m_objects_model -> GetIdByItem ( obj_item );
if ( obj_idx < 0 ||
object ( obj_idx ) -> layer_config_ranges . empty () ||
printer_technology () == ptSLA )
return wxDataViewItem ( 0 );
// create LayerRoot item
wxDataViewItem layers_item = m_objects_model -> AddLayersRoot ( obj_item );
// and create Layer item(s) according to the layer_config_ranges
for ( const auto range : object ( obj_idx ) -> layer_config_ranges )
add_layer_item ( range . first , layers_item );
2019-07-11 18:42:02 +02:00
Expand ( layers_item );
2019-06-10 10:48:43 +02:00
return layers_item ;
}
2019-06-05 16:47:09 +02:00
DynamicPrintConfig ObjectList :: get_default_layer_config ( const int obj_idx )
{
DynamicPrintConfig config ;
coordf_t layer_height = object ( obj_idx ) -> config . has ( "layer_height" ) ?
object ( obj_idx ) -> config . opt_float ( "layer_height" ) :
wxGetApp (). preset_bundle -> prints . get_edited_preset (). config . opt_float ( "layer_height" );
config . set_key_value ( "layer_height" , new ConfigOptionFloat ( layer_height ));
config . set_key_value ( "extruder" , new ConfigOptionInt ( 0 ));
return config ;
}
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-05-28 16:38:04 +02:00
bool update_and_show_layers = false ;
2018-11-09 18:39:07 +01:00
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 {
2019-06-05 16:47:09 +02:00
obj_idx = m_objects_model -> GetObjectIdByItem ( item );
2019-05-28 16:38:04 +02:00
const ItemType type = m_objects_model -> GetItemType ( item );
if ( type & itSettings ) {
2019-06-05 16:47:09 +02:00
const auto parent = m_objects_model -> GetParent ( item );
const ItemType parent_type = m_objects_model -> GetItemType ( parent );
if ( parent_type & itObject ) {
2018-11-08 15:45:55 +01:00
og_name = _ ( L ( "Object Settings to modify" ));
m_config = & ( * m_objects )[ obj_idx ] -> config ;
}
2019-06-05 16:47:09 +02:00
else if ( parent_type & itVolume ) {
2018-11-08 15:45:55 +01:00
og_name = _ ( L ( "Part Settings to modify" ));
2019-06-05 16:47:09 +02:00
volume_id = m_objects_model -> GetVolumeIdByItem ( parent );
2018-11-08 15:45:55 +01:00
m_config = & ( * m_objects )[ obj_idx ] -> volumes [ volume_id ] -> config ;
}
2019-06-05 16:47:09 +02:00
else if ( parent_type & itLayer ) {
og_name = _ ( L ( "Layer range Settings to modify" ));
2019-06-06 14:14:29 +02:00
m_config = & get_item_config ( parent );
2019-06-05 16:47:09 +02:00
}
2018-11-09 18:39:07 +01:00
update_and_show_settings = true ;
2018-10-05 23:29:15 +02:00
}
2019-05-28 16:38:04 +02:00
else if ( type & itVolume ) {
2018-11-08 15:45:55 +01:00
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
}
2019-05-28 16:38:04 +02:00
else if ( type & itInstance ) {
2018-11-08 15:45:55 +01:00
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
2019-06-05 16:47:09 +02:00
m_config = & ( * m_objects )[ obj_idx ] -> config ;
2018-11-08 15:45:55 +01:00
}
2019-05-28 16:38:04 +02:00
else if ( type & ( itLayerRoot | itLayer )) {
og_name = type & itLayerRoot ? _ ( L ( "Layers Editing" )) : _ ( L ( "Layer Editing" ));
update_and_show_layers = true ;
2019-06-05 16:47:09 +02:00
2019-06-06 14:14:29 +02:00
if ( type & itLayer )
m_config = & get_item_config ( item );
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 + " " );
2019-06-10 10:48:43 +02:00
if ( printer_technology () == ptSLA )
update_and_show_layers = false ;
else if ( update_and_show_layers )
2019-05-28 16:38:04 +02:00
wxGetApp (). obj_layers () -> 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
2019-07-02 10:34:30 +02:00
wxGetApp (). plater () -> canvas3D () -> handle_sidebar_focus_event ( "" , false );
2018-11-09 18:39:07 +01:00
wxGetApp (). obj_manipul () -> UpdateAndShow ( update_and_show_manipulations );
wxGetApp (). obj_settings () -> UpdateAndShow ( update_and_show_settings );
2019-05-28 16:38:04 +02:00
wxGetApp (). obj_layers () -> UpdateAndShow ( update_and_show_layers );
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
}
2019-07-25 17:24:00 +02:00
SettingsBundle ObjectList :: get_item_settings_bundle ( const DynamicPrintConfig * config , const bool is_object_settings )
2019-07-11 23:46:23 +02:00
{
auto opt_keys = config -> keys ();
if ( opt_keys . empty ())
return SettingsBundle ();
2019-07-25 17:24:00 +02:00
update_opt_keys ( opt_keys , is_object_settings ); // update options list according to print technology
2019-07-11 23:46:23 +02:00
2019-07-25 17:24:00 +02:00
if ( opt_keys . empty ())
2019-07-11 23:46:23 +02:00
return SettingsBundle ();
const int extruders_cnt = wxGetApp (). extruders_edited_cnt ();
SettingsBundle bundle ;
for ( auto & opt_key : opt_keys )
{
auto category = config -> def () -> get ( opt_key ) -> category ;
2019-08-06 19:01:59 +02:00
if ( improper_category ( category , extruders_cnt ))
2019-07-11 23:46:23 +02:00
continue ;
std :: vector < std :: string > new_category ;
auto & cat_opt = bundle . find ( category ) == bundle . end () ? new_category : bundle . at ( category );
cat_opt . push_back ( opt_key );
if ( cat_opt . size () == 1 )
bundle [ category ] = cat_opt ;
}
return bundle ;
}
2019-07-12 13:55:46 +02:00
// Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config
2019-07-11 23:46:23 +02:00
wxDataViewItem ObjectList :: add_settings_item ( wxDataViewItem parent_item , const DynamicPrintConfig * config )
{
wxDataViewItem ret = wxDataViewItem ( 0 );
if ( ! parent_item )
return ret ;
2019-07-25 17:24:00 +02:00
const bool is_object_settings = m_objects_model -> GetItemType ( parent_item ) == itObject ;
SettingsBundle cat_options = get_item_settings_bundle ( config , is_object_settings );
2019-07-11 23:46:23 +02:00
if ( cat_options . empty ())
return ret ;
std :: vector < std :: string > categories ;
categories . reserve ( cat_options . size ());
for ( auto & cat : cat_options )
categories . push_back ( cat . first );
if ( m_objects_model -> GetItemType ( parent_item ) & itInstance )
parent_item = m_objects_model -> GetTopParent ( parent_item );
ret = m_objects_model -> IsSettingsItem ( parent_item ) ? parent_item : m_objects_model -> GetSettingsItem ( parent_item );
if ( ! ret ) ret = m_objects_model -> AddSettingsChild ( parent_item );
m_objects_model -> UpdateSettingsDigest ( ret , categories );
Expand ( parent_item );
return ret ;
}
2019-07-05 19:06:19 +02:00
void ObjectList :: add_object_to_list ( size_t obj_idx , bool call_selection_changed )
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-07-11 23:46:23 +02:00
add_settings_item ( vol_item , & volume -> config );
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 )
2019-08-01 14:58:04 +02:00
{
std :: vector < bool > print_idicator ( model_object -> instances . size ());
for ( int i = 0 ; i < model_object -> instances . size (); ++ i )
2019-08-20 15:26:57 +02:00
print_idicator [ i ] = model_object -> instances [ i ] -> printable ;
2019-08-01 14:58:04 +02:00
2019-08-05 14:57:30 +02:00
const wxDataViewItem object_item = m_objects_model -> GetItemById ( obj_idx );
m_objects_model -> AddInstanceChild ( object_item , print_idicator );
Expand ( m_objects_model -> GetInstanceRootItem ( object_item ));
2019-08-01 14:58:04 +02:00
}
2019-08-05 08:44:55 +02:00
else
2019-08-20 15:26:57 +02:00
m_objects_model -> SetPrintableState ( model_object -> instances [ 0 ] -> printable ? piPrintable : piUnprintable , obj_idx );
2018-11-01 16:22:16 +01:00
2018-11-06 09:06:26 +01:00
// add settings to the object, if it has those
2019-07-11 23:46:23 +02:00
add_settings_item ( item , & model_object -> config );
2018-11-06 09:06:26 +01:00
2019-06-10 15:22:09 +02:00
// Add layers if it has
add_layer_root_item ( item );
2018-10-05 23:29:15 +02:00
#ifndef __WXOSX__
2019-07-05 19:06:19 +02:00
if ( call_selection_changed )
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 ;
2019-07-04 17:33:19 +02:00
take_snapshot ( _ ( L ( "Delete Selected Item" )));
2018-11-13 14:17:35 +01:00
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 );
2019-08-01 14:58:04 +02:00
m_objects_model -> SetValue ( extruder , m_objects_model -> GetItemById ( item -> obj_idx ), colExtruder );
2019-03-13 16:40:11 +01:00
}
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 );
2019-06-17 13:09:11 +02:00
wxDataViewItem parent = wxDataViewItem ( 0 );
2019-07-17 15:48:53 +02:00
Plater :: TakeSnapshot snapshot ( wxGetApp (). plater (), _ ( L ( "Delete Selected" )));
2019-07-11 16:00:01 +02:00
2018-10-12 12:00:37 +02:00
for ( auto & item : sels )
{
2019-08-02 17:53:12 +02:00
if ( m_objects_model -> InvalidItem ( item )) // item can be deleted for this moment (like last 2 Instances or Volumes)
continue ;
2018-10-12 12:00:37 +02:00
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 {
2019-06-17 13:09:11 +02:00
if ( m_objects_model -> GetItemType ( item ) & itLayer ) {
parent = m_objects_model -> GetParent ( item );
wxDataViewItemArray children ;
if ( m_objects_model -> GetChildren ( parent , children ) == 1 )
parent = m_objects_model -> GetTopParent ( item );
2019-05-30 16:53:17 +02:00
}
2019-06-17 13:09:11 +02:00
else if ( sels . size () == 1 )
2019-03-21 09:04:11 +01:00
select_item ( m_objects_model -> GetParent ( item ));
2019-05-30 16:53:17 +02:00
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
}
2019-06-17 13:09:11 +02:00
if ( parent )
select_item ( parent );
2018-10-05 23:29:15 +02:00
}
2019-06-06 14:14:29 +02:00
void ObjectList :: del_layer_range ( const t_layer_height_range & range )
2019-05-31 10:54:52 +02:00
{
const int obj_idx = get_selected_obj_idx ();
if ( obj_idx < 0 ) return ;
2019-06-05 11:03:46 +02:00
t_layer_config_ranges & ranges = object ( obj_idx ) -> layer_config_ranges ;
2019-05-31 10:54:52 +02:00
wxDataViewItem selectable_item = GetSelection ();
if ( ranges . size () == 1 )
selectable_item = m_objects_model -> GetParent ( selectable_item );
2019-06-06 14:14:29 +02:00
wxDataViewItem layer_item = m_objects_model -> GetItemByLayerRange ( obj_idx , range );
2019-05-31 10:54:52 +02:00
del_subobject_item ( layer_item );
select_item ( selectable_item );
}
2019-08-06 19:01:59 +02:00
static double get_min_layer_height ( const int extruder_idx )
2019-06-12 16:28:25 +02:00
{
const DynamicPrintConfig & config = wxGetApp (). preset_bundle -> printers . get_edited_preset (). config ;
return config . opt_float ( "min_layer_height" , extruder_idx <= 0 ? 0 : extruder_idx - 1 );
}
2019-08-06 19:01:59 +02:00
static double get_max_layer_height ( const int extruder_idx )
2019-06-12 16:28:25 +02:00
{
const DynamicPrintConfig & config = wxGetApp (). preset_bundle -> printers . get_edited_preset (). config ;
return config . opt_float ( "max_layer_height" , extruder_idx <= 0 ? 0 : extruder_idx - 1 );
}
2019-06-06 14:14:29 +02:00
void ObjectList :: add_layer_range_after_current ( const t_layer_height_range & current_range )
2019-05-31 15:29:09 +02:00
{
const int obj_idx = get_selected_obj_idx ();
if ( obj_idx < 0 ) return ;
2019-06-06 14:14:29 +02:00
const wxDataViewItem layers_item = GetSelection ();
2019-05-31 15:29:09 +02:00
2019-06-03 15:35:21 +02:00
t_layer_config_ranges & ranges = object ( obj_idx ) -> layer_config_ranges ;
2019-05-31 15:29:09 +02:00
2019-06-06 14:14:29 +02:00
const t_layer_height_range & last_range = ( -- ranges . end ()) -> first ;
2019-05-31 15:29:09 +02:00
2019-06-06 14:14:29 +02:00
if ( current_range == last_range )
2019-05-31 15:29:09 +02:00
{
2019-07-11 18:42:02 +02:00
take_snapshot ( _ ( L ( "Add New Layers Range" )));
2019-07-02 15:26:11 +02:00
const t_layer_height_range & new_range = { last_range . second , last_range . second + 2.0f };
2019-06-05 16:47:09 +02:00
ranges [ new_range ] = get_default_layer_config ( obj_idx );
2019-05-31 15:29:09 +02:00
add_layer_item ( new_range , layers_item );
}
else
{
2019-06-06 14:14:29 +02:00
const t_layer_height_range & next_range = ( ++ ranges . find ( current_range )) -> first ;
2019-05-31 15:29:09 +02:00
2019-06-06 14:14:29 +02:00
if ( current_range . second > next_range . first )
return ; // range division has no sense
2019-06-04 17:30:44 +02:00
2019-06-06 14:14:29 +02:00
const int layer_idx = m_objects_model -> GetItemIdByLayerRange ( obj_idx , next_range );
if ( layer_idx < 0 )
return ;
if ( current_range . second == next_range . first )
2019-05-31 15:29:09 +02:00
{
2019-06-12 16:28:25 +02:00
const auto old_config = ranges . at ( next_range );
2019-06-06 14:14:29 +02:00
const coordf_t delta = ( next_range . second - next_range . first );
2019-06-12 16:28:25 +02:00
if ( delta < get_min_layer_height ( old_config . opt_int ( "extruder" )) /*0.05f*/ ) // next range division has no sense
2019-05-31 15:29:09 +02:00
return ;
2019-06-06 14:14:29 +02:00
const coordf_t midl_layer = next_range . first + 0.5f * delta ;
t_layer_height_range new_range = { midl_layer , next_range . second };
2019-05-31 15:29:09 +02:00
2019-07-17 15:48:53 +02:00
Plater :: TakeSnapshot snapshot ( wxGetApp (). plater (), _ ( L ( "Add New Layers Range" )));
2019-07-11 18:42:02 +02:00
// create new 2 layers instead of deleted one
2019-05-31 15:29:09 +02:00
// delete old layer
2019-06-06 14:14:29 +02:00
wxDataViewItem layer_item = m_objects_model -> GetItemByLayerRange ( obj_idx , next_range );
2019-05-31 15:29:09 +02:00
del_subobject_item ( layer_item );
2019-06-03 15:35:21 +02:00
ranges [ new_range ] = old_config ;
2019-05-31 15:29:09 +02:00
add_layer_item ( new_range , layers_item , layer_idx );
2019-06-06 14:14:29 +02:00
new_range = { current_range . second , midl_layer };
2019-06-05 16:47:09 +02:00
ranges [ new_range ] = get_default_layer_config ( obj_idx );
2019-05-31 15:29:09 +02:00
add_layer_item ( new_range , layers_item , layer_idx );
}
else
{
2019-07-11 18:42:02 +02:00
take_snapshot ( _ ( L ( "Add New Layers Range" )));
2019-06-06 14:14:29 +02:00
const t_layer_height_range new_range = { current_range . second , next_range . first };
2019-06-05 16:47:09 +02:00
ranges [ new_range ] = get_default_layer_config ( obj_idx );
2019-05-31 15:29:09 +02:00
add_layer_item ( new_range , layers_item , layer_idx );
}
}
changed_object ( obj_idx );
// select item to update layers sizer
select_item ( layers_item );
}
void ObjectList :: add_layer_item ( const t_layer_height_range & range ,
const wxDataViewItem layers_item ,
const int layer_idx /* = -1*/ )
{
2019-06-10 10:48:43 +02:00
const int obj_idx = m_objects_model -> GetObjectIdByItem ( layers_item );
2019-06-05 16:47:09 +02:00
if ( obj_idx < 0 ) return ;
const DynamicPrintConfig & config = object ( obj_idx ) -> layer_config_ranges [ range ];
2019-06-07 11:32:46 +02:00
if ( ! config . has ( "extruder" ))
return ;
const auto layer_item = m_objects_model -> AddLayersChild ( layers_item ,
range ,
config . opt_int ( "extruder" ),
layer_idx );
2019-07-11 23:46:23 +02:00
add_settings_item ( layer_item , & config );
2019-05-31 15:29:09 +02:00
}
2019-06-12 16:28:25 +02:00
bool ObjectList :: edit_layer_range ( const t_layer_height_range & range , coordf_t layer_height )
2019-05-31 10:54:52 +02:00
{
2019-06-04 15:22:29 +02:00
const int obj_idx = get_selected_obj_idx ();
2019-06-12 16:28:25 +02:00
if ( obj_idx < 0 )
return false ;
2019-05-31 10:54:52 +02:00
2019-06-12 16:28:25 +02:00
DynamicPrintConfig * config = & object ( obj_idx ) -> layer_config_ranges [ range ];
if ( fabs ( layer_height - config -> opt_float ( "layer_height" )) < EPSILON )
return false ;
2019-06-04 15:22:29 +02:00
2019-06-12 16:28:25 +02:00
const int extruder_idx = config -> opt_int ( "extruder" );
if ( layer_height >= get_min_layer_height ( extruder_idx ) &&
layer_height <= get_max_layer_height ( extruder_idx ))
{
config -> set_key_value ( "layer_height" , new ConfigOptionFloat ( layer_height ));
return true ;
}
return false ;
2019-05-31 10:54:52 +02:00
}
2019-06-12 16:28:25 +02:00
bool ObjectList :: edit_layer_range ( const t_layer_height_range & range , const t_layer_height_range & new_range )
2019-06-04 17:30:44 +02:00
{
const int obj_idx = get_selected_obj_idx ();
2019-06-12 16:28:25 +02:00
if ( obj_idx < 0 ) return false ;
2019-06-04 17:30:44 +02:00
2019-07-11 18:42:02 +02:00
take_snapshot ( _ ( L ( "Edit Layers Range" )));
2019-06-17 13:46:56 +02:00
const ItemType sel_type = m_objects_model -> GetItemType ( GetSelection ());
2019-06-04 17:30:44 +02:00
t_layer_config_ranges & ranges = object ( obj_idx ) -> layer_config_ranges ;
const DynamicPrintConfig config = ranges [ range ];
ranges . erase ( range );
ranges [ new_range ] = config ;
wxDataViewItem root_item = m_objects_model -> GetLayerRootItem ( m_objects_model -> GetItemById ( obj_idx ));
2019-08-09 17:47:35 +02:00
// To avoid update selection after deleting of a selected item (under GTK)
// set m_prevent_list_events to true
m_prevent_list_events = true ;
2019-06-04 17:30:44 +02:00
m_objects_model -> DeleteChildren ( root_item );
if ( root_item . IsOk ())
// create Layer item(s) according to the layer_config_ranges
for ( const auto r : ranges )
add_layer_item ( r . first , root_item );
2019-06-17 13:46:56 +02:00
select_item ( sel_type & itLayer ? m_objects_model -> GetItemByLayerRange ( obj_idx , new_range ) : root_item );
2019-06-04 17:30:44 +02:00
Expand ( root_item );
2019-06-12 16:28:25 +02:00
return true ;
2018-10-05 23:29:15 +02:00
}
void ObjectList :: init_objects ()
{
2019-07-18 17:41:47 +02:00
m_objects = & wxGetApp (). model (). objects ;
2018-10-05 23:29:15 +02:00
}
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 ;
}
2019-07-24 12:32:38 +02:00
int ObjectList :: get_selected_layers_range_idx () const
2019-07-23 18:17:57 +02:00
{
const wxDataViewItem & item = GetSelection ();
if ( ! item )
2019-07-24 12:32:38 +02:00
return - 1 ;
2019-07-23 18:17:57 +02:00
const ItemType type = m_objects_model -> GetItemType ( item );
if ( type & itSettings && m_objects_model -> GetItemType ( m_objects_model -> GetParent ( item )) != itLayer )
2019-07-24 12:32:38 +02:00
return - 1 ;
2019-07-23 18:17:57 +02:00
2019-07-24 12:32:38 +02:00
return m_objects_model -> GetLayerIdByItem ( type & itLayer ? item : m_objects_model -> GetParent ( item ));
2019-07-23 18:17:57 +02:00
}
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-07-23 18:17:57 +02:00
if ( ( m_selection_mode & ( smSettings | smLayer | smLayerRoot ) ) == 0 )
2019-07-23 16:17:37 +02:00
m_selection_mode = smInstance ;
2019-04-02 16:33:52 +02:00
2018-12-18 15:29:09 +01:00
// We doesn't update selection if SettingsItem for the current object/part is selected
2019-05-27 16:13:24 +02:00
// if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
if ( GetSelectedItemsCount () == 1 && m_objects_model -> GetItemType ( GetSelection ()) & ( itSettings | itLayerRoot | itLayer ))
2018-12-18 15:29:09 +01:00
{
const auto item = GetSelection ();
2019-06-13 15:35:10 +02:00
if ( selection . is_single_full_object ()) {
2019-06-13 16:17:54 +02:00
if ( m_objects_model -> GetObjectIdByItem ( item ) == selection . get_object_idx ())
2019-06-13 15:35:10 +02:00
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 ();
2019-07-23 18:17:57 +02:00
if ( m_selection_mode & ( smSettings | smLayer | smLayerRoot ))
2019-07-23 16:17:37 +02:00
{
2019-07-23 18:17:57 +02:00
auto obj_idx = objects_content . begin () -> first ;
wxDataViewItem obj_item = m_objects_model -> GetItemById ( obj_idx );
if ( m_selection_mode & smSettings )
{
2019-07-24 12:32:38 +02:00
if ( m_selected_layers_range_idx < 0 )
2019-07-23 18:17:57 +02:00
sels . Add ( m_objects_model -> GetSettingsItem ( obj_item ));
else
2019-07-24 12:32:38 +02:00
sels . Add ( m_objects_model -> GetSettingsItem ( m_objects_model -> GetItemByLayerId ( obj_idx , m_selected_layers_range_idx )));
2019-07-23 18:17:57 +02:00
}
else if ( m_selection_mode & smLayerRoot )
sels . Add ( m_objects_model -> GetLayerRootItem ( obj_item ));
else if ( m_selection_mode & smLayer )
2019-07-24 12:32:38 +02:00
sels . Add ( m_objects_model -> GetItemByLayerId ( obj_idx , m_selected_layers_range_idx ));
2019-07-23 16:17:37 +02:00
}
else {
2019-03-29 14:36:09 +01:00
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 ));
}
2019-07-23 16:17:37 +02:00
} }
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
{
2019-07-23 16:17:37 +02:00
if ( m_selection_mode & smSettings )
{
const auto idx = * selection . get_volume_idxs (). begin ();
const auto gl_vol = selection . get_volume ( idx );
if ( gl_vol -> volume_idx () >= 0 ) {
// 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).
wxDataViewItem vol_item = m_objects_model -> GetItemByVolumeId ( gl_vol -> object_idx (), gl_vol -> volume_idx ());
sels . Add ( m_objects_model -> GetSettingsItem ( vol_item ));
}
}
else {
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-07-23 16:17:37 +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
2019-07-23 16:17:37 +02:00
if ( sels . size () == 0 || m_selection_mode & smSettings )
2019-04-02 16:33:52 +02:00
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 ) {
2019-07-23 15:14:08 +02:00
selection . remove_all ();
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-07-23 15:14:08 +02:00
std :: vector < unsigned int > volume_idxs ;
Selection :: EMode mode = Selection :: Volume ;
2019-07-31 11:52:24 +02:00
bool single_selection = sel_cnt == 1 ;
auto add_to_selection = [ this , & volume_idxs , & single_selection ]( const wxDataViewItem & item , const Selection & selection , int instance_idx , Selection :: EMode & mode )
2019-03-19 13:30:21 +01:00
{
2019-04-02 16:33:52 +02:00
const ItemType & type = m_objects_model -> GetItemType ( item );
2019-06-10 15:22:09 +02:00
const int obj_idx = m_objects_model -> GetObjectIdByItem ( item );
2018-10-11 15:57:09 +02:00
2019-04-02 16:33:52 +02:00
if ( type == itVolume ) {
2018-10-18 10:40:26 +02:00
const int vol_idx = m_objects_model -> GetVolumeIdByItem ( item );
2019-07-23 15:14:08 +02:00
std :: vector < unsigned int > idxs = selection . get_volume_idxs_from_volume ( obj_idx , std :: max ( instance_idx , 0 ), vol_idx );
volume_idxs . insert ( volume_idxs . end (), idxs . begin (), idxs . end ());
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 inst_idx = m_objects_model -> GetInstanceIdByItem ( item );
2019-07-23 15:14:08 +02:00
mode = Selection :: Instance ;
std :: vector < unsigned int > idxs = selection . get_volume_idxs_from_instance ( obj_idx , inst_idx );
volume_idxs . insert ( volume_idxs . end (), idxs . begin (), idxs . end ());
2018-10-18 10:40:26 +02:00
}
2019-06-10 15:22:09 +02:00
else
2019-07-23 15:14:08 +02:00
{
mode = Selection :: Instance ;
2019-07-31 15:30:03 +02:00
single_selection &= ( obj_idx != selection . get_object_idx ());
2019-07-23 15:14:08 +02:00
std :: vector < unsigned int > idxs = selection . get_volume_idxs_from_object ( obj_idx );
volume_idxs . insert ( volume_idxs . end (), idxs . begin (), idxs . end ());
}
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 ();
2019-07-23 15:14:08 +02:00
if ( m_objects_model -> GetItemType ( item ) & ( itSettings | itInstanceRoot | itLayerRoot | itLayer ))
add_to_selection ( m_objects_model -> GetParent ( item ), selection , instance_idx , mode );
else
add_to_selection ( item , selection , instance_idx , mode );
2018-10-11 15:57:09 +02:00
}
2019-07-23 15:14:08 +02:00
else
{
wxDataViewItemArray sels ;
GetSelections ( sels );
2019-08-06 09:41:09 +02:00
// clear selection before adding new elements
selection . clear (); //OR remove_all()?
2019-07-23 15:14:08 +02:00
for ( auto item : sels )
{
add_to_selection ( item , selection , instance_idx , mode );
}
}
if ( selection . contains_all_volumes ( volume_idxs ))
{
// remove
volume_idxs = selection . get_missing_volume_idxs_from ( volume_idxs );
if ( volume_idxs . size () > 0 )
{
2019-07-26 11:32:44 +02:00
Plater :: TakeSnapshot snapshot ( wxGetApp (). plater (), _ ( L ( "Selection-Remove from list" )));
2019-07-23 15:14:08 +02:00
selection . remove_volumes ( mode , volume_idxs );
}
}
else
{
// add
volume_idxs = selection . get_unselected_volume_idxs_from ( volume_idxs );
2019-07-26 11:32:44 +02:00
Plater :: TakeSnapshot snapshot ( wxGetApp (). plater (), _ ( L ( "Selection-Add from list" )));
2019-07-31 11:52:24 +02:00
selection . add_volumes ( mode , volume_idxs , single_selection );
2019-07-23 15:14:08 +02:00
}
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 ();
2019-06-10 15:22:09 +02:00
const ItemType item_type = m_objects_model -> GetItemType ( item );
// Some volume/layer/instance is selected => select all volumes/layers/instances inside the current object
if ( item_type & ( itVolume | itInstance | itLayer ))
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
2019-06-10 15:22:09 +02:00
m_selection_mode = item_type & itVolume ? smVolume :
item_type & itLayer ? smLayer : 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 ()
{
2019-07-31 09:46:45 +02:00
m_selected_layers_range_idx =- 1 ;
2019-04-02 16:33:52 +02:00
// 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 ());
2019-06-10 15:22:09 +02:00
m_selection_mode = type & itSettings ? smUndef :
type & itLayer ? smLayer :
type & itVolume ? smVolume : smInstance ;
2019-04-02 16:33:52 +02:00
}
// 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
2019-06-10 15:22:09 +02:00
/* We can't mix Volumes, Layers 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
2019-06-10 15:22:09 +02:00
// check a case of a selection of the same type items from different Objects
auto impossible_multi_selection = [ type , this ]( const ItemType item_type , const SELECTION_MODE selection_mode ) {
if ( ! ( type & item_type && m_selection_mode & selection_mode ))
return false ;
2019-04-04 15:07:54 +02:00
wxDataViewItemArray sels ;
GetSelections ( sels );
2019-06-10 15:22:09 +02:00
for ( const auto & sel : sels )
if ( sel != m_last_selected_item &&
m_objects_model -> GetTopParent ( sel ) != m_objects_model -> GetTopParent ( m_last_selected_item ))
return true ;
2019-04-04 15:07:54 +02:00
2019-06-10 15:22:09 +02:00
return false ;
};
if ( impossible_multi_selection ( itVolume , smVolume ) ||
impossible_multi_selection ( itLayer , smLayer ) ||
2019-04-04 15:07:54 +02:00
type & itSettings ||
2019-06-10 15:22:09 +02:00
type & itVolume && ! ( m_selection_mode & smVolume ) ||
type & itLayer && ! ( m_selection_mode & smLayer ) ||
type & itInstance && ! ( m_selection_mode & smInstance )
)
2019-04-02 16:33:52 +02:00
{
// Inform user why selection isn't complited
2019-06-10 15:22:09 +02:00
const wxString item_type = m_selection_mode & smInstance ? _ ( L ( "Object or Instance" )) :
m_selection_mode & smVolume ? _ ( L ( "Part" )) : _ ( L ( "Layer" ));
2019-04-02 16:33:52 +02:00
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-06-10 15:22:09 +02:00
if ( m_selection_mode & ( smVolume | smLayer ))
2019-04-04 15:07:54 +02:00
{
// 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-06-10 15:22:09 +02:00
const ItemType item_type = m_selection_mode & smVolume ? itVolume : itLayer ;
2019-04-04 15:07:54 +02:00
for ( const auto child : children )
2019-06-10 15:22:09 +02:00
if ( IsSelected ( child ) && m_objects_model -> GetItemType ( child ) & item_type )
2019-04-04 15:07:54 +02:00
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 ;
2019-07-22 13:38:53 +02:00
take_snapshot ( _ ( L ( "Change Part Type" )));
2019-07-04 17:33:19 +02:00
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 )) {
2019-07-11 23:46:23 +02:00
add_settings_item ( item , & volume -> config );
2018-11-05 08:55:44 +01:00
}
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 )
{
2019-07-23 17:09:19 +02:00
if ( obj_idx < 0 || obj_idx >= m_objects -> size () || ( * m_objects )[ obj_idx ] -> volumes . size () != 1 )
2018-11-12 13:47:24 +01:00
return ;
2019-07-05 19:06:19 +02:00
auto volume = ( * m_objects )[ obj_idx ] -> volumes . front ();
2018-11-12 13:47:24 +01:00
// 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 ));
}
2019-07-12 13:55:46 +02:00
void ObjectList :: update_and_show_object_settings_item ()
{
const wxDataViewItem item = GetSelection ();
if ( ! item ) return ;
const wxDataViewItem & obj_item = m_objects_model -> IsSettingsItem ( item ) ? m_objects_model -> GetParent ( item ) : item ;
select_item ( add_settings_item ( obj_item , & get_item_config ( obj_item )));
}
2018-11-29 15:00:23 +01:00
2019-06-10 10:48:43 +02:00
// Update settings item for item had it
2019-06-10 15:22:09 +02:00
void ObjectList :: update_settings_item_and_selection ( wxDataViewItem item , wxDataViewItemArray & selections )
2019-06-10 10:48:43 +02:00
{
2019-07-12 13:55:46 +02:00
const wxDataViewItem old_settings_item = m_objects_model -> GetSettingsItem ( item );
const wxDataViewItem new_settings_item = add_settings_item ( item , & get_item_config ( item ));
2019-06-10 10:48:43 +02:00
2019-07-12 13:55:46 +02:00
if ( ! new_settings_item && old_settings_item )
m_objects_model -> Delete ( old_settings_item );
2019-06-10 10:48:43 +02:00
2019-07-12 13:55:46 +02:00
// if ols settings item was is selected area
if ( selections . Index ( old_settings_item ) != wxNOT_FOUND )
{
// If settings item was just updated
if ( old_settings_item == new_settings_item )
{
Sidebar & panel = wxGetApp (). sidebar ();
panel . Freeze ();
// update settings list
wxGetApp (). obj_settings () -> UpdateAndShow ( true );
panel . Layout ();
panel . Thaw ();
}
else
// If settings item was deleted from the list,
// it's need to be deleted from selection array, if it was there
{
selections . Remove ( old_settings_item );
// Select item, if settings_item doesn't exist for item anymore, but was selected
if ( selections . Index ( item ) == wxNOT_FOUND ) {
selections . Add ( item );
select_item ( item ); // to correct update of the SettingsList and ManipulationPanel sizers
}
}
2019-06-10 10:48:43 +02:00
}
}
void ObjectList :: update_object_list_by_printer_technology ()
{
m_prevent_canvas_selection_update = true ;
wxDataViewItemArray sel ;
GetSelections ( sel ); // stash selection
wxDataViewItemArray object_items ;
m_objects_model -> GetChildren ( wxDataViewItem ( 0 ), object_items );
for ( auto & object_item : object_items ) {
// Update Settings Item for object
2019-06-10 15:22:09 +02:00
update_settings_item_and_selection ( object_item , sel );
2019-06-10 10:48:43 +02:00
// Update settings for Volumes
wxDataViewItemArray all_object_subitems ;
m_objects_model -> GetChildren ( object_item , all_object_subitems );
for ( auto item : all_object_subitems )
if ( m_objects_model -> GetItemType ( item ) & itVolume )
// update settings for volume
2019-06-10 15:22:09 +02:00
update_settings_item_and_selection ( item , sel );
2019-06-10 10:48:43 +02:00
// Update Layers Items
wxDataViewItem layers_item = m_objects_model -> GetLayerRootItem ( object_item );
if ( ! layers_item )
layers_item = add_layer_root_item ( object_item );
else if ( printer_technology () == ptSLA ) {
// If layers root item will be deleted from the list, so
// it's need to be deleted from selection array, if it was there
wxDataViewItemArray del_items ;
bool some_layers_was_selected = false ;
m_objects_model -> GetAllChildren ( layers_item , del_items );
for ( auto & del_item : del_items )
if ( sel . Index ( del_item ) != wxNOT_FOUND ) {
some_layers_was_selected = true ;
sel . Remove ( del_item );
}
if ( sel . Index ( layers_item ) != wxNOT_FOUND ) {
some_layers_was_selected = true ;
sel . Remove ( layers_item );
}
// delete all "layers" items
m_objects_model -> Delete ( layers_item );
// Select object_item, if layers_item doesn't exist for item anymore, but was some of layer items was/were selected
if ( some_layers_was_selected )
sel . Add ( object_item );
}
else {
wxDataViewItemArray all_obj_layers ;
m_objects_model -> GetChildren ( layers_item , all_obj_layers );
for ( auto item : all_obj_layers )
// update settings for layer
2019-06-10 15:22:09 +02:00
update_settings_item_and_selection ( item , sel );
2019-06-10 10:48:43 +02:00
}
}
// restore selection:
SetSelections ( sel );
m_prevent_canvas_selection_update = false ;
}
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
2019-08-05 14:57:30 +02:00
const size_t new_obj_indx = static_cast < size_t > ( m_objects -> size () - 1 );
add_object_to_list ( new_obj_indx );
2019-01-22 16:40:10 +01:00
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-08-05 14:57:30 +02:00
// update printable state for new volumes on canvas3D
2019-08-16 13:14:51 +02:00
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_object ( new_obj_indx );
2019-01-23 16:01:37 +01:00
}
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 ();
2019-08-05 14:57:30 +02:00
std :: vector < size_t > object_idxs ;
2019-04-04 18:01:53 +02:00
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
2019-08-05 14:57:30 +02:00
const size_t new_obj_indx = static_cast < size_t > ( m_objects -> size () - 1 );
add_object_to_list ( new_obj_indx );
object_idxs . push_back ( new_obj_indx );
2019-04-04 18:01:53 +02:00
// delete current instance from the initial object
del_subobject_from_object ( obj_idx , i , itInstance );
delete_instance_from_list ( obj_idx , i );
}
2019-08-05 14:57:30 +02:00
// update printable state for new volumes on canvas3D
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_objects ( object_idxs );
2019-04-04 18:01:53 +02:00
}
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-07-22 13:38:53 +02:00
Plater :: TakeSnapshot snapshot ( wxGetApp (). plater (), _ ( L ( "Instances to Separated Objects" )));
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 ;
2019-08-01 14:58:04 +02:00
m_objects_model -> GetValue ( valueOld , item , colName );
2019-01-30 11:35:37 +01:00
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 ;
2019-08-01 14:58:04 +02:00
m_objects_model -> SetValue ( value , item , colName );
2019-01-30 11:35:37 +01:00
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-08-01 14:58:04 +02:00
GetColumn ( colName ) -> SetWidth ( 19 * em );
GetColumn ( colPrint ) -> SetWidth ( 2 * em );
GetColumn ( colExtruder ) -> SetWidth ( 8 * em );
GetColumn ( colEditing ) -> 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 ,
2019-06-03 15:35:21 +02:00
& m_menu_instance ,
& m_menu_layer })
2019-05-09 14:53:01 +02:00
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
{
2019-08-01 14:58:04 +02:00
if ( event . GetColumn () == colName )
2018-12-12 14:35:18 +01:00
update_name_in_model ( event . GetItem ());
2019-08-01 14:58:04 +02:00
else if ( event . GetColumn () == colExtruder )
2018-12-12 14:35:18 +01:00
update_extruder_in_config ( event . GetItem ());
}
2019-08-22 11:47:58 +02:00
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
// Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column.
void ObjectList :: OnEditingStarted ( wxDataViewEvent & event )
{
m_last_selected_column = - 1 ;
}
#endif __WXMSW__
2018-12-12 14:35:18 +01:00
void ObjectList :: OnEditingDone ( wxDataViewEvent & event )
{
2019-08-01 14:58:04 +02:00
if ( event . GetColumn () != colName )
2018-12-12 14:35:18 +01:00
return ;
2019-08-01 14:58:04 +02:00
const auto renderer = dynamic_cast < BitmapTextRenderer *> ( GetColumn ( colName ) -> 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:" )) + " <>:/ \\ |?* \" " );
});
2019-08-22 11:47:58 +02:00
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
// Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column.
m_last_selected_column = - 1 ;
#endif __WXMSW__
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
*/
2019-08-01 14:58:04 +02:00
m_objects_model -> SetValue ( extruder_str , type & itInstance ? m_objects_model -> GetTopParent ( item ) : item , colExtruder );
2019-03-28 16:28:34 +01:00
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 ();
}
2019-07-11 18:42:02 +02:00
void ObjectList :: update_after_undo_redo ()
2019-07-04 17:33:19 +02:00
{
m_prevent_canvas_selection_update = true ;
2019-07-17 15:48:53 +02:00
Plater :: SuppressSnapshots suppress ( wxGetApp (). plater ());
2019-07-11 18:42:02 +02:00
2019-07-05 19:06:19 +02:00
// Unselect all objects before deleting them, so that no change of selection is emitted during deletion.
2019-07-23 16:17:37 +02:00
unselect_objects (); //this->UnselectAll();
2019-07-04 17:33:19 +02:00
m_objects_model -> DeleteAll ();
size_t obj_idx = 0 ;
2019-08-07 14:28:46 +02:00
std :: vector < size_t > obj_idxs ;
obj_idxs . reserve ( m_objects -> size ());
2019-07-04 17:33:19 +02:00
while ( obj_idx < m_objects -> size ()) {
2019-07-05 19:06:19 +02:00
add_object_to_list ( obj_idx , false );
2019-08-07 14:28:46 +02:00
obj_idxs . push_back ( obj_idx );
2019-07-04 17:33:19 +02:00
++ obj_idx ;
}
2019-07-11 18:42:02 +02:00
update_selections ();
2019-07-04 17:33:19 +02:00
m_prevent_canvas_selection_update = false ;
2019-08-07 14:28:46 +02:00
// update printable states on canvas
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_objects ( obj_idxs );
// update scene
wxGetApp (). plater () -> update ();
2019-07-04 17:33:19 +02:00
}
2019-08-05 08:44:55 +02:00
void ObjectList :: update_printable_state ( int obj_idx , int instance_idx )
{
ModelObject * object = ( * m_objects )[ obj_idx ];
2019-08-07 14:11:41 +02:00
const PrintIndicator printable = object -> instances [ instance_idx ] -> printable ? piPrintable : piUnprintable ;
2019-08-05 08:44:55 +02:00
if ( object -> instances . size () == 1 )
instance_idx = - 1 ;
2019-08-05 10:05:28 +02:00
m_objects_model -> SetPrintableState ( printable , obj_idx , instance_idx );
}
void ObjectList :: toggle_printable_state ( wxDataViewItem item )
{
const ItemType type = m_objects_model -> GetItemType ( item );
if ( ! ( type & ( itObject | itInstance /*|itVolume*/ )))
return ;
2019-08-07 14:11:41 +02:00
if ( type & itObject )
{
const int obj_idx = m_objects_model -> GetObjectIdByItem ( item );
ModelObject * object = ( * m_objects )[ obj_idx ];
// get object's printable and change it
2019-08-07 15:39:46 +02:00
const bool printable = ! m_objects_model -> IsPrintable ( item );
const wxString snapshot_text = wxString :: Format ( "%s %s" ,
printable ? _ ( L ( "Set Printable" )) : _ ( L ( "Set Unprintable" )),
object -> name );
take_snapshot ( snapshot_text );
2019-08-07 14:11:41 +02:00
// set printable value for all instances in object
for ( auto inst : object -> instances )
inst -> printable = printable ;
// update printable state on canvas
2019-08-16 13:14:51 +02:00
wxGetApp (). plater () -> canvas3D () -> update_instance_printable_state_for_object (( size_t ) obj_idx );
2019-08-07 14:11:41 +02:00
// update printable state in ObjectList
m_objects_model -> SetObjectPrintableState ( printable ? piPrintable : piUnprintable , item );
}
else
wxGetApp (). plater () -> canvas3D () -> get_selection (). toggle_instance_printable_state ();
2019-08-05 10:05:28 +02:00
// update scene
wxGetApp (). plater () -> update ();
2019-08-05 08:44:55 +02:00
}
2019-07-04 20:49:46 +02:00
ModelObject * ObjectList :: object ( const int obj_idx ) const
{
if ( obj_idx < 0 )
return nullptr ;
return ( * m_objects )[ obj_idx ];
}
2018-10-04 16:43:10 +02:00
} //namespace GUI
2019-08-06 19:53:20 +02:00
} //namespace Slic3r