2024-09-03 09:34:33 +08:00
# include "libslic3r/libslic3r.h"
# include "libslic3r/PresetBundle.hpp"
# include "libslic3r/Model.hpp"
# include "GUI_Factories.hpp"
# include "GUI_ObjectList.hpp"
# include "GUI_App.hpp"
# include "I18N.hpp"
# include "Plater.hpp"
# include "ObjectDataViewModel.hpp"
# include "OptionsGroup.hpp"
# include "GLCanvas3D.hpp"
# include "Selection.hpp"
# include "format.hpp"
//QDS: add partplate related logic
# include "PartPlate.hpp"
2024-11-28 15:19:12 +08:00
# include "Gizmos/GLGizmoSVG.hpp"
2024-09-03 09:34:33 +08:00
# include <boost/algorithm/string.hpp>
# include "slic3r/Utils/FixModelByWin10.hpp"
# include "ParamsPanel.hpp"
namespace Slic3r
{
namespace GUI
{
static PrinterTechnology printer_technology ( )
{
return wxGetApp ( ) . preset_bundle - > printers . get_selected_preset ( ) . printer_technology ( ) ;
}
static int filaments_count ( )
{
return wxGetApp ( ) . filaments_cnt ( ) ;
}
static bool is_improper_category ( const std : : string & category , const int filaments_cnt , const bool is_object_settings = true )
{
return category . empty ( ) | |
( filaments_cnt = = 1 & & ( category = = " Extruders " | | category = = " Wipe options " ) ) | |
( ! is_object_settings & & category = = " Support material " ) ;
}
//-------------------------------------
// SettingsFactory
//-------------------------------------
// pt_FFF
static SettingsFactory : : Bundle FREQ_SETTINGS_BUNDLE_FFF =
{
//QDS
{ L ( " Quality " ) , { " layer_height " } } ,
{ L ( " Shell " ) , { " wall_loops " , " top_shell_layers " , " bottom_shell_layers " } } ,
{ L ( " Infill " ) , { " sparse_infill_density " , " sparse_infill_pattern " } } ,
// QDS
{ L ( " Support " ) , { " enable_support " , " support_type " , " support_threshold_angle " ,
" support_base_pattern " , " support_on_build_plate_only " , " support_critical_regions_only " ,
" support_remove_small_overhang " ,
" support_base_pattern_spacing " , " support_expansion " } } ,
//QDS
{ L ( " Flush options " ) , { " flush_into_infill " , " flush_into_objects " , " flush_into_support " } }
} ;
// pt_SLA
static SettingsFactory : : Bundle FREQ_SETTINGS_BUNDLE_SLA =
{
// QDS: remove SLA freq settings
} ;
//QDS: add setting data for table
std : : map < std : : string , std : : vector < SimpleSettingData > > SettingsFactory : : OBJECT_CATEGORY_SETTINGS =
{
{ L ( " Quality " ) , { { " layer_height " , " " , 1 } ,
//{"initial_layer_print_height", "",2},
{ " wall_sequence " , " " , 2 } ,
{ " seam_position " , " " , 3 } , { " seam_gap " , " " , 4 } , { " wipe_speed " , " " , 5 } ,
{ " slice_closing_radius " , " " , 6 } , { " resolution " , " " , 7 } ,
{ " xy_hole_compensation " , " " , 8 } , { " xy_contour_compensation " , " " , 9 } , { " elefant_foot_compensation " , " " , 10 } ,
{ " precise_z_height " , " " , 10 }
} } ,
{ L ( " Support " ) , { { " brim_type " , " " , 1 } , { " brim_width " , " " , 2 } , { " brim_object_gap " , " " , 3 } ,
{ " enable_support " , " " , 4 } , { " support_type " , " " , 5 } , { " support_threshold_angle " , " " , 6 } , { " support_on_build_plate_only " , " " , 7 } ,
{ " support_filament " , " " , 8 } , { " support_interface_filament " , " " , 9 } , { " support_expansion " , " " , 24 } , { " support_style " , " " , 25 } ,
2024-11-28 15:19:12 +08:00
{ " tree_support_branch_angle " , " " , 10 } , { " tree_support_wall_count " , " " , 11 } , { " tree_support_branch_diameter_angle " , " " , 11 } , //tree support
2024-09-03 09:34:33 +08:00
{ " support_top_z_distance " , " " , 13 } , { " support_bottom_z_distance " , " " , 12 } , { " support_base_pattern " , " " , 14 } , { " support_base_pattern_spacing " , " " , 15 } ,
{ " support_interface_top_layers " , " " , 16 } , { " support_interface_bottom_layers " , " " , 17 } , { " support_interface_spacing " , " " , 18 } , { " support_bottom_interface_spacing " , " " , 19 } ,
{ " support_object_xy_distance " , " " , 20 } , { " bridge_no_support " , " " , 21 } , { " max_bridge_length " , " " , 22 } , { " support_critical_regions_only " , " " , 23 } , { " support_remove_small_overhang " , " " , 27 } ,
{ " support_object_first_layer_gap " , " " , 28 }
} } ,
{ L ( " Speed " ) , { { " support_speed " , " " , 12 } , { " support_interface_speed " , " " , 13 }
} }
} ;
2025-05-08 15:05:30 +08:00
// todo multi_extruders: Does the following need to be modified?
2024-09-03 09:34:33 +08:00
std : : map < std : : string , std : : vector < SimpleSettingData > > SettingsFactory : : PART_CATEGORY_SETTINGS =
2024-11-28 15:19:12 +08:00
{ { L ( " Quality " ) , { { " ironing_type " , " " , 8 } , { " ironing_flow " , " " , 9 } , { " ironing_spacing " , " " , 10 } , { " ironing_inset " , " " , 11 } , { " ironing_speed " , " " , 12 } , { " ironing_direction " , " " , 13 }
2024-09-03 09:34:33 +08:00
} } ,
{ L ( " Strength " ) , { { " wall_loops " , " " , 1 } , { " top_shell_layers " , " " , 1 } , { " top_shell_thickness " , " " , 1 } ,
{ " bottom_shell_layers " , " " , 1 } , { " bottom_shell_thickness " , " " , 1 } , { " sparse_infill_density " , " " , 1 } ,
{ " sparse_infill_pattern " , " " , 1 } , { " sparse_infill_anchor " , " " , 1 } , { " sparse_infill_anchor_max " , " " , 1 } , { " top_surface_pattern " , " " , 1 } , { " bottom_surface_pattern " , " " , 1 } , { " internal_solid_infill_pattern " , " " , 1 } ,
{ " infill_combination " , " " , 1 } , { " infill_wall_overlap " , " " , 1 } , { " infill_direction " , " " , 1 } , { " bridge_angle " , " " , 1 } , { " minimum_sparse_infill_area " , " " , 1 }
} } ,
{ L ( " Speed " ) , { { " outer_wall_speed " , " " , 1 } , { " inner_wall_speed " , " " , 2 } , { " sparse_infill_speed " , " " , 3 } , { " top_surface_speed " , " " , 4 } , { " internal_solid_infill_speed " , " " , 5 } ,
2024-09-16 16:07:29 +08:00
{ " enable_overhang_speed " , " " , 6 } , { " overhang_1_4_speed " , " " , 7 } , { " overhang_2_4_speed " , " " , 8 } , { " overhang_3_4_speed " , " " , 9 } , { " overhang_4_4_speed " , " " , 10 } , { " overhang_totally_speed " , " " , 11 } ,
2025-08-04 16:30:53 +08:00
{ " bridge_speed " , " " , 12 } , { " gap_infill_speed " , " " , 13 } , { " enable_height_slowdown " , " " , 14 } , { " slowdown_start_height " , " " , 15 } , { " slowdown_start_speed " , " " , 16 } , { " slowdown_start_acc " , " " , 17 } ,
{ " slowdown_end_height " , " " , 18 } , { " slowdown_end_speed " , " " , 19 } , { " slowdown_end_acc " , " " , 20 }
2024-09-03 09:34:33 +08:00
} }
} ;
std : : vector < std : : string > SettingsFactory : : get_options ( const bool is_part )
{
if ( printer_technology ( ) = = ptSLA ) {
SLAPrintObjectConfig full_sla_config ;
auto options = full_sla_config . keys ( ) ;
options . erase ( find ( options . begin ( ) , options . end ( ) , " layer_height " ) ) ;
return options ;
}
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 ;
}
std : : vector < SimpleSettingData > SettingsFactory : : get_visible_options ( const std : : string & category , const bool is_part )
{
/*t_config_option_keys options = {
//Quality
" wall_infill_order " , " ironing_type " , " inner_wall_line_width " , " outer_wall_line_width " , " top_surface_line_width " ,
//Shell
" wall_loops " , " top_shell_layers " , " bottom_shell_layers " , " top_shell_thickness " , " bottom_shell_thickness " ,
//Infill
" sparse_infill_density " , " sparse_infill_pattern " , " top_surface_pattern " , " bottom_surface_pattern " , " infill_combination " , " infill_direction " , " infill_wall_overlap " ,
//speed
" inner_wall_speed " , " outer_wall_speed " , " sparse_infill_speed " , " internal_solid_infill_speed " , " top_surface_speed " , " gap_infill_speed "
} ;
t_config_option_keys object_options = {
//Quality
" layer_height " , " initial_layer_print_height " , " adaptive_layer_height " , " seam_position " , " xy_hole_compensation " , " xy_contour_compensation " , " elefant_foot_compensation " , " support_line_width " ,
//Support
" enable_support " , " support_type " , " support_threshold_angle " , " support_on_build_plate_only " , " support_critical_regions_only " , " enforce_support_layers " , " support_remove_small_overhang " ,
//tree support
" tree_support_wall_count " ,
//support
" support_top_z_distance " , " support_base_pattern " , " support_base_pattern_spacing " , " support_interface_top_layers " , " support_interface_bottom_layers " , " support_interface_spacing " , " support_bottom_interface_spacing " , " support_object_xy_distance " ,
" support_object_first_layer_gap " ,
//adhesion
" brim_type " , " brim_width " , " brim_object_gap " , " raft_layers "
} ; */
std : : vector < SimpleSettingData > options ;
std : : map < std : : string , std : : vector < SimpleSettingData > > : : iterator it ;
it = PART_CATEGORY_SETTINGS . find ( category ) ;
if ( it ! = PART_CATEGORY_SETTINGS . end ( ) )
{
options = PART_CATEGORY_SETTINGS [ category ] ;
}
if ( ! is_part ) {
it = OBJECT_CATEGORY_SETTINGS . find ( category ) ;
if ( it ! = OBJECT_CATEGORY_SETTINGS . end ( ) )
options . insert ( options . end ( ) , OBJECT_CATEGORY_SETTINGS [ category ] . begin ( ) , OBJECT_CATEGORY_SETTINGS [ category ] . end ( ) ) ;
}
auto sort_func = [ ] ( SimpleSettingData & setting1 , SimpleSettingData & setting2 ) {
return ( setting1 . priority < setting2 . priority ) ;
} ;
std : : sort ( options . begin ( ) , options . end ( ) , sort_func ) ;
return options ;
}
std : : map < std : : string , std : : vector < SimpleSettingData > > SettingsFactory : : get_all_visible_options ( const bool is_part )
{
std : : map < std : : string , std : : vector < SimpleSettingData > > option_maps ;
std : : map < std : : string , std : : vector < SimpleSettingData > > : : iterator it1 , it2 ;
option_maps = PART_CATEGORY_SETTINGS ;
if ( ! is_part ) {
for ( it1 = OBJECT_CATEGORY_SETTINGS . begin ( ) ; it1 ! = OBJECT_CATEGORY_SETTINGS . end ( ) ; it1 + + )
{
std : : string category = it1 - > first ;
it2 = PART_CATEGORY_SETTINGS . find ( category ) ;
if ( it2 ! = PART_CATEGORY_SETTINGS . end ( ) )
{
std : : vector < SimpleSettingData > & options = option_maps [ category ] ;
options . insert ( options . end ( ) , it1 - > second . begin ( ) , it1 - > second . end ( ) ) ;
auto sort_func = [ ] ( SimpleSettingData & setting1 , SimpleSettingData & setting2 ) {
return ( setting1 . priority < setting2 . priority ) ;
} ;
std : : sort ( options . begin ( ) , options . end ( ) , sort_func ) ;
}
else {
option_maps . insert ( * it1 ) ;
}
}
}
return option_maps ;
}
SettingsFactory : : Bundle SettingsFactory : : get_bundle ( const DynamicPrintConfig * config , bool is_object_settings , bool is_layer_settings /* = false*/ )
{
auto opt_keys = config - > keys ( ) ;
if ( opt_keys . empty ( ) )
return Bundle ( ) ;
// update options list according to print technology
auto full_current_opts = get_options ( ! is_object_settings ) ;
if ( is_layer_settings )
full_current_opts . push_back ( " layer_height " ) ;
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 ) ;
if ( opt_keys . empty ( ) )
return Bundle ( ) ;
const int filaments_cnt = wxGetApp ( ) . filaments_cnt ( ) ;
Bundle bundle ;
for ( auto & opt_key : opt_keys )
{
auto category = config - > def ( ) - > get ( opt_key ) - > category ;
if ( is_improper_category ( category , filaments_cnt , is_object_settings ) )
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 ;
}
// Fill CategoryItem
std : : map < std : : string , std : : string > SettingsFactory : : CATEGORY_ICON =
{
// settings category name related bitmap name
// ptFFF
{ L ( " Quality " ) , " blank2 " } ,
{ L ( " Shell " ) , " blank_14 " } ,
{ L ( " Infill " ) , " blank_14 " } ,
{ L ( " Ironing " ) , " blank_14 " } ,
2025-08-04 16:30:53 +08:00
{ L ( " Fuzzy Skin " ) , " fuzzy_skin " } ,
2024-09-03 09:34:33 +08:00
{ L ( " Support " ) , " support " } ,
{ L ( " Speed " ) , " blank_14 " } ,
{ L ( " Extruders " ) , " blank_14 " } ,
{ L ( " Extrusion Width " ) , " blank_14 " } ,
{ L ( " Wipe options " ) , " blank_14 " } ,
{ L ( " Bed adhension " ) , " blank_14 " } ,
// { L("Speed > Acceleration") , "time" },
{ L ( " Advanced " ) , " blank_14 " } ,
// QDS: remove SLA categories
} ;
wxBitmap SettingsFactory : : get_category_bitmap ( const std : : string & category_name , bool menu_bmp )
{
if ( CATEGORY_ICON . find ( category_name ) = = CATEGORY_ICON . end ( ) )
return wxNullBitmap ;
return create_scaled_bitmap ( CATEGORY_ICON . at ( category_name ) ) ;
}
//-------------------------------------
// MenuFactory
//-------------------------------------
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
2024-11-28 15:19:12 +08:00
static const std : : vector < std : : pair < std : : string , std : : string > > ADD_VOLUME_MENU_ITEMS = {
2024-09-03 09:34:33 +08:00
{ L ( " Add part " ) , " menu_add_part " } , // ~ModelVolumeType::MODEL_PART
{ L ( " Add negative part " ) , " menu_add_negative " } , // ~ModelVolumeType::NEGATIVE_VOLUME
{ L ( " Add modifier " ) , " menu_add_modifier " } , // ~ModelVolumeType::PARAMETER_MODIFIER
{ L ( " Add support blocker " ) , " menu_support_blocker " } , // ~ModelVolumeType::SUPPORT_BLOCKER
{ L ( " Add support enforcer " ) , " menu_support_enforcer " } // ~ModelVolumeType::SUPPORT_ENFORCER
} ;
2024-11-28 15:19:12 +08:00
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
static const constexpr std : : array < std : : pair < const char * , const char * > , 3 > TEXT_VOLUME_ICONS { {
// menu_item Name menu_item bitmap name
{ L ( " Add text " ) , " add_text_part " } , // ~ModelVolumeType::MODEL_PART
{ L ( " Add negative text " ) , " add_text_negative " } , // ~ModelVolumeType::NEGATIVE_VOLUME
{ L ( " Add text modifier " ) , " add_text_modifier " } , // ~ModelVolumeType::PARAMETER_MODIFIER
} } ;
// Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
static const constexpr std : : array < std : : pair < const char * , const char * > , 3 > SVG_VOLUME_ICONS { {
{ L ( " Add SVG part " ) , " svg_part " } , // ~ModelVolumeType::MODEL_PART
{ L ( " Add negative SVG " ) , " svg_negative " } , // ~ModelVolumeType::NEGATIVE_VOLUME
{ L ( " Add SVG modifier " ) , " svg_modifier " } , // ~ModelVolumeType::PARAMETER_MODIFIER
} } ;
2024-09-03 09:34:33 +08:00
static Plater * plater ( )
{
return wxGetApp ( ) . plater ( ) ;
}
static ObjectList * obj_list ( )
{
return wxGetApp ( ) . obj_list ( ) ;
}
static ObjectDataViewModel * list_model ( )
{
return wxGetApp ( ) . obj_list ( ) - > GetModel ( ) ;
}
static const Selection & get_selection ( )
{
return plater ( ) - > get_current_canvas3D ( true ) - > get_selection ( ) ;
}
// category -> vector ( option ; label )
typedef std : : map < std : : string , std : : vector < std : : pair < std : : string , std : : string > > > FullSettingsHierarchy ;
static void get_full_settings_hierarchy ( FullSettingsHierarchy & settings_menu , const bool is_part )
{
auto options = SettingsFactory : : get_options ( is_part ) ;
const int filaments_cnt = filaments_count ( ) ;
DynamicPrintConfig config ;
for ( auto & option : options )
{
auto const opt = config . def ( ) - > get ( option ) ;
auto category = opt - > category ;
if ( is_improper_category ( category , filaments_cnt , ! is_part ) )
continue ;
const std : : string & label = ! opt - > full_label . empty ( ) ? opt - > full_label : opt - > label ;
std : : pair < std : : string , std : : string > option_label ( option , label ) ;
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 ;
}
}
static wxMenu * create_settings_popupmenu ( wxMenu * parent_menu , const bool is_object_settings , wxDataViewItem item /*, ModelConfig& config*/ )
{
wxMenu * menu = new wxMenu ;
FullSettingsHierarchy categories ;
get_full_settings_hierarchy ( categories , ! is_object_settings ) ;
auto get_selected_options_for_category = [ categories , item ] ( const wxString & category_name ) {
wxArrayString names ;
wxArrayInt selections ;
std : : vector < std : : pair < std : : string , bool > > category_options ;
for ( auto & cat : categories ) {
if ( _ ( cat . first ) = = category_name ) {
ModelConfig & config = obj_list ( ) - > get_item_config ( item ) ;
auto opt_keys = config . keys ( ) ;
int sel = 0 ;
for ( const std : : pair < std : : string , std : : string > & pair : cat . second ) {
names . Add ( _ ( pair . second ) ) ;
if ( find ( opt_keys . begin ( ) , opt_keys . end ( ) , pair . first ) ! = opt_keys . end ( ) )
selections . Add ( sel ) ;
sel + + ;
category_options . push_back ( std : : make_pair ( pair . first , false ) ) ;
}
break ;
}
}
if ( ! category_options . empty ( ) & &
wxGetSelectedChoices ( selections , _L ( " Select settings " ) , category_name , names ) ! = - 1 ) {
for ( auto sel : selections )
category_options [ sel ] . second = true ;
}
return category_options ;
} ;
for ( auto cat : categories ) {
append_menu_item ( menu , wxID_ANY , _ ( cat . first ) , " " ,
[ menu , item , get_selected_options_for_category ] ( wxCommandEvent & event ) {
std : : vector < std : : pair < std : : string , bool > > category_options = get_selected_options_for_category ( menu - > GetLabel ( event . GetId ( ) ) ) ;
obj_list ( ) - > add_category_to_settings_from_selection ( category_options , item ) ;
} , SettingsFactory : : get_category_bitmap ( cat . first ) , parent_menu ,
[ ] ( ) { return true ; } , plater ( ) ) ;
}
return menu ;
}
static void create_freq_settings_popupmenu ( wxMenu * menu , const bool is_object_settings , wxDataViewItem item )
{
// Add default settings bundles
const SettingsFactory : : Bundle & bundle = printer_technology ( ) = = ptFFF ? FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA ;
const int filaments_cnt = filaments_count ( ) ;
for ( auto & category : bundle ) {
if ( is_improper_category ( category . first , filaments_cnt , is_object_settings ) )
continue ;
append_menu_item ( menu , wxID_ANY , _ ( category . first ) , " " ,
[ menu , item , is_object_settings , bundle ] ( wxCommandEvent & event ) {
wxString category_name = menu - > GetLabel ( event . GetId ( ) ) ;
std : : vector < std : : string > options ;
for ( auto & category : bundle )
if ( category_name = = _ ( category . first ) ) {
options = category . second ;
break ;
}
if ( options . empty ( ) )
return ;
// Because of we couldn't edited layer_height for ItVolume from settings list,
// correct options according to the selected item type : remove "layer_height" option
if ( ! is_object_settings & & category_name = = _ ( " Quality " ) ) {
const auto layer_height_it = std : : find ( options . begin ( ) , options . end ( ) , " layer_height " ) ;
if ( layer_height_it ! = options . end ( ) )
options . erase ( layer_height_it ) ;
}
obj_list ( ) - > add_category_to_settings_from_frequent ( options , item ) ;
} ,
SettingsFactory : : get_category_bitmap ( category . first ) , menu ,
[ ] ( ) { return true ; } , plater ( ) ) ;
}
}
std : : vector < wxBitmap > MenuFactory : : get_volume_bitmaps ( )
{
std : : vector < wxBitmap > volume_bmps ;
volume_bmps . reserve ( ADD_VOLUME_MENU_ITEMS . size ( ) ) ;
2024-11-28 15:19:12 +08:00
for ( const auto & item : ADD_VOLUME_MENU_ITEMS ) {
volume_bmps . push_back ( create_scaled_bitmap ( item . second ) ) ;
2024-09-03 09:34:33 +08:00
}
return volume_bmps ;
}
2024-11-28 15:19:12 +08:00
std : : vector < wxBitmap > MenuFactory : : get_text_volume_bitmaps ( )
{
std : : vector < wxBitmap > volume_bmps ;
volume_bmps . reserve ( TEXT_VOLUME_ICONS . size ( ) ) ;
for ( const auto & item : TEXT_VOLUME_ICONS ) volume_bmps . push_back ( create_scaled_bitmap ( item . second ) ) ;
return volume_bmps ;
}
std : : vector < wxBitmap > MenuFactory : : get_svg_volume_bitmaps ( )
{
std : : vector < wxBitmap > volume_bmps ;
volume_bmps . reserve ( SVG_VOLUME_ICONS . size ( ) ) ;
for ( const auto & item : SVG_VOLUME_ICONS ) volume_bmps . push_back ( create_scaled_bitmap ( item . second ) ) ;
return volume_bmps ;
}
2024-09-03 09:34:33 +08:00
void MenuFactory : : append_menu_item_set_visible ( wxMenu * menu )
{
bool has_one_shown = false ;
const Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
for ( unsigned int i : selection . get_volume_idxs ( ) ) {
has_one_shown | = selection . get_volume ( i ) - > visible ;
}
append_menu_item ( menu , wxID_ANY , has_one_shown ? _L ( " Hide " ) : _L ( " Show " ) , " " ,
[ has_one_shown ] ( wxCommandEvent & ) { plater ( ) - > set_selected_visible ( ! has_one_shown ) ; } , " " , nullptr ,
[ ] ( ) { return true ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_delete ( wxMenu * menu )
{
2025-05-08 15:05:30 +08:00
# ifdef __APPLE__
2024-09-03 09:34:33 +08:00
append_menu_item ( menu , wxID_ANY , _L ( " Delete " ) + " \t BackSpace " , _L ( " Delete the selected object " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > remove_selected ( ) ; } , " " , nullptr ,
[ ] ( ) { return plater ( ) - > can_delete ( ) ; } , m_parent ) ;
2025-05-08 15:05:30 +08:00
# else
append_menu_item ( menu , wxID_ANY , _L ( " Delete " ) + " \t Delete " , _L ( " Delete the selected object " ) , [ ] ( wxCommandEvent & ) { plater ( ) - > remove_selected ( ) ; } , " menu_delete " , nullptr ,
[ ] ( ) { return plater ( ) - > can_delete ( ) ; } , m_parent ) ;
2024-09-03 09:34:33 +08:00
# endif
}
2024-11-28 15:19:12 +08:00
void MenuFactory : : append_menu_item_delete_all_cutter ( wxMenu * menu )
{
# ifdef __WINDOWS__
append_menu_item (
menu , wxID_ANY , _L ( " Delete all cutter " ) + " \t " + _L ( " Del " ) , _L ( " Delete all cutter " ) , [ ] ( wxCommandEvent & ) { plater ( ) - > remove_selected ( ) ; } , " menu_delete " , nullptr ,
[ ] ( ) { return plater ( ) - > can_delete ( ) ; } , m_parent ) ;
# else
append_menu_item (
menu , wxID_ANY , _L ( " Delete all cutter " ) + " \t BackSpace " , _L ( " Delete all cutter " ) , [ ] ( wxCommandEvent & ) { plater ( ) - > remove_selected ( ) ; } , " " , nullptr ,
[ ] ( ) { return plater ( ) - > can_delete ( ) ; } , m_parent ) ;
# endif
}
2024-09-03 09:34:33 +08:00
void MenuFactory : : append_menu_item_edit_text ( wxMenu * menu )
{
2025-08-04 16:30:53 +08:00
wxString name = _L ( " Edit Text " ) ;
if ( menu ! = & m_text_part_menu ) {
const int menu_item_id = menu - > FindItem ( name ) ;
if ( menu_item_id ! = wxNOT_FOUND )
menu - > Destroy ( menu_item_id ) ;
if ( plater ( ) = = nullptr )
return ;
if ( ! plater ( ) - > can_edit_text ( ) )
return ;
}
2024-09-03 09:34:33 +08:00
append_menu_item (
menu , wxID_ANY , _L ( " Edit Text " ) , " " , [ ] ( wxCommandEvent & ) { plater ( ) - > edit_text ( ) ; } , " " , nullptr ,
[ ] ( ) { return plater ( ) - > can_edit_text ( ) ; } , m_parent ) ;
}
2024-11-28 15:19:12 +08:00
void MenuFactory : : append_menu_item_edit_svg ( wxMenu * menu )
{
wxString name = _L ( " Edit SVG " ) ;
auto can_edit_svg = [ ] ( ) {
if ( plater ( ) = = nullptr ) return false ;
const Selection & selection = plater ( ) - > get_selection ( ) ;
if ( selection . volumes_count ( ) ! = 1 ) return false ;
const GLVolume * gl_volume = selection . get_first_volume ( ) ;
if ( gl_volume = = nullptr ) return false ;
const ModelVolume * volume = get_model_volume ( * gl_volume , selection . get_model ( ) - > objects ) ;
if ( volume = = nullptr ) return false ;
return volume - > is_svg ( ) ;
} ;
if ( menu ! = & m_svg_part_menu ) {
const int menu_item_id = menu - > FindItem ( name ) ;
2025-08-04 16:30:53 +08:00
if ( menu_item_id ! = wxNOT_FOUND )
menu - > Destroy ( menu_item_id ) ;
2024-11-28 15:19:12 +08:00
if ( ! can_edit_svg ( ) ) return ;
}
wxString description = _L ( " Change SVG source file, projection, size, ... " ) ;
std : : string icon = " svg_part " ;
auto open_svg = [ ] ( const wxCommandEvent & ) {
2025-08-04 16:30:53 +08:00
const auto & p_plater = plater ( ) ;
if ( ! p_plater ) {
return ;
}
const auto & p_canvas = p_plater - > get_current_canvas3D ( ) ;
if ( ! p_canvas ) {
return ;
}
GLGizmosManager & mng = p_canvas - > get_gizmos_manager ( ) ;
if ( mng . get_current_type ( ) = = GLGizmosManager : : Svg ) {
mng . open_gizmo ( GLGizmosManager : : Svg ) ; // close() and reopen - move to be visible
}
2024-11-28 15:19:12 +08:00
mng . open_gizmo ( GLGizmosManager : : Svg ) ;
} ;
append_menu_item ( menu , wxID_ANY , name , description , open_svg , icon , nullptr , can_edit_svg , m_parent ) ;
}
2024-09-03 09:34:33 +08:00
wxMenu * MenuFactory : : append_submenu_add_generic ( wxMenu * menu , ModelVolumeType type ) {
auto sub_menu = new wxMenu ;
if ( type ! = ModelVolumeType : : INVALID ) {
append_menu_item ( sub_menu , wxID_ANY , _L ( " Load... " ) , " " ,
2025-02-26 20:14:36 +08:00
[ type ] ( wxCommandEvent & ) { obj_list ( ) - > load_subobject ( type ) ; } , " " , menu ) ;
2024-09-03 09:34:33 +08:00
sub_menu - > AppendSeparator ( ) ;
}
2024-11-28 15:19:12 +08:00
std : : vector < std : : string > icons = { " Cube " , " Cylinder " , " Sphere " , " Cone " , " double_tear_romboid_cylinder " , " Disc " , " Torus " , " rounded_rectangle " } ;
2024-09-03 09:34:33 +08:00
size_t i = 0 ;
2024-11-28 15:19:12 +08:00
for ( auto & item : { L ( " Cube " ) , L ( " Cylinder " ) , L ( " Sphere " ) , L ( " Cone " ) , L ( " Double Tear Romboid Cylinder " ) , L ( " Disc " ) , L ( " Torus " ) , L ( " Rounded Rectangle " ) } )
2024-09-03 09:34:33 +08:00
{
append_menu_item ( sub_menu , wxID_ANY , _ ( item ) , " " ,
[ type , item ] ( wxCommandEvent & ) { obj_list ( ) - > load_generic_subobject ( item , type ) ; } , Slic3r : : resources_dir ( ) + " /model/ " + icons [ i + + ] + " .png " , menu ) ;
}
//B
if ( type = = ModelVolumeType : : INVALID ) {
sub_menu - > AppendSeparator ( ) ;
2024-09-20 09:38:18 +08:00
for ( auto & item : { L ( " QIDI " ) , L ( " 3DBenchy " ) , L ( " ksr FDMTest " ) } ) {
2024-09-03 09:34:33 +08:00
append_menu_item (
sub_menu , wxID_ANY , _ ( item ) , " " , [ type , item ] ( wxCommandEvent & ) { obj_list ( ) - > load_generic_subobject ( item , type ) ; } , " " , menu ) ;
}
}
2024-11-28 15:19:12 +08:00
append_menu_item_add_svg ( sub_menu , type ) ;
2024-09-03 09:34:33 +08:00
return sub_menu ;
}
2024-11-28 15:19:12 +08:00
static void append_menu_itemm_add_ ( const wxString & name , GLGizmosManager : : EType gizmo_type , wxMenu * menu , ModelVolumeType type , bool is_submenu_item )
{
auto add_ = [ type , gizmo_type ] ( const wxCommandEvent & /*unnamed*/ ) {
const GLCanvas3D * canvas = plater ( ) - > canvas3D ( ) ;
const GLGizmosManager & mng = canvas - > get_gizmos_manager ( ) ;
GLGizmoBase * gizmo_base = mng . get_gizmo ( gizmo_type ) ;
ModelVolumeType volume_type = type ;
// no selected object means create new object
if ( volume_type = = ModelVolumeType : : INVALID )
volume_type = ModelVolumeType : : MODEL_PART ;
auto screen_position = canvas - > get_popup_menu_position ( ) ;
/* if (gizmo_type == GLGizmosManager::Emboss) {//todo
auto emboss = dynamic_cast < GLGizmoEmboss * > ( gizmo_base ) ;
assert ( emboss ! = nullptr ) ;
if ( emboss = = nullptr ) return ;
if ( screen_position . has_value ( ) ) {
emboss - > create_volume ( volume_type , * screen_position ) ;
} else {
emboss - > create_volume ( volume_type ) ;
}
} else */ if ( gizmo_type = = GLGizmosManager : : Svg ) {
auto svg = dynamic_cast < GLGizmoSVG * > ( gizmo_base ) ;
assert ( svg ! = nullptr ) ;
if ( svg = = nullptr ) return ;
if ( screen_position . has_value ( ) ) {
svg - > create_volume ( volume_type , * screen_position ) ;
} else {
svg - > create_volume ( volume_type ) ;
}
}
} ;
if ( type = = ModelVolumeType : : MODEL_PART | | type = = ModelVolumeType : : NEGATIVE_VOLUME | | type = = ModelVolumeType : : PARAMETER_MODIFIER | |
type = = ModelVolumeType : : INVALID // cannot use gizmo without selected object
) {
wxString item_name = wxString ( is_submenu_item ? " " : _ ( ADD_VOLUME_MENU_ITEMS [ int ( type ) ] . first ) + " : " ) + name ;
menu - > AppendSeparator ( ) ;
auto def_icon_name = ( gizmo_type = = GLGizmosManager : : Svg ) ? " menu_obj_svg " : " menu_obj_text " ;
const std : : string icon_name = is_submenu_item ? def_icon_name : ADD_VOLUME_MENU_ITEMS [ int ( type ) ] . second ;
append_menu_item ( menu , wxID_ANY , item_name , " " , add_ , icon_name , menu ) ;
}
}
void MenuFactory : : append_menu_item_add_svg ( wxMenu * menu , ModelVolumeType type , bool is_submenu_item /* = true*/ )
{
append_menu_itemm_add_ ( _L ( " SVG " ) , GLGizmosManager : : Svg , menu , type , is_submenu_item ) ;
}
2024-09-03 09:34:33 +08:00
void MenuFactory : : append_menu_items_add_volume ( wxMenu * menu )
{
// Update "add" items(delete old & create new) settings popupmenu
for ( auto & item : ADD_VOLUME_MENU_ITEMS ) {
const auto settings_id = menu - > FindItem ( _ ( item . first ) ) ;
if ( settings_id ! = wxNOT_FOUND )
menu - > Destroy ( settings_id ) ;
}
for ( size_t type = 0 ; type < ADD_VOLUME_MENU_ITEMS . size ( ) ; type + + )
{
auto & item = ADD_VOLUME_MENU_ITEMS [ type ] ;
wxMenu * sub_menu = append_submenu_add_generic ( menu , ModelVolumeType ( type ) ) ;
append_submenu ( menu , sub_menu , wxID_ANY , _ ( item . first ) , " " , item . second ,
[ ] ( ) { return obj_list ( ) - > is_instance_or_object_selected ( ) ; } , m_parent ) ;
}
append_menu_item_layers_editing ( menu ) ;
}
wxMenuItem * MenuFactory : : append_menu_item_layers_editing ( wxMenu * menu )
{
return append_menu_item ( menu , wxID_ANY , _L ( " Height range Modifier " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > layers_editing ( ) ; wxGetApp ( ) . params_panel ( ) - > switch_to_object ( ) ; } , " " , menu ,
[ ] ( ) { return obj_list ( ) - > is_instance_or_object_selected ( ) ; } , m_parent ) ;
}
wxMenuItem * MenuFactory : : append_menu_item_settings ( wxMenu * menu_ )
{
MenuWithSeparators * menu = dynamic_cast < MenuWithSeparators * > ( menu_ ) ;
const wxString menu_name = _L ( " Add settings " ) ;
// Delete old items from settings popupmenu
auto settings_id = menu - > FindItem ( menu_name ) ;
if ( settings_id ! = wxNOT_FOUND )
menu - > Destroy ( settings_id ) ;
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 ) ;
}
menu - > DestroySeparators ( ) ; // delete old separators
// If there are selected more then one instance but not all of them
// don't add settings menu items
const Selection & selection = get_selection ( ) ;
if ( ( selection . is_multiple_full_instance ( ) & & ! selection . is_single_full_object ( ) ) | |
selection . is_multiple_volume ( ) | | selection . is_mixed ( ) ) // more than one volume(part) is selected on the scene
return nullptr ;
const auto sel_vol = obj_list ( ) - > get_selected_model_volume ( ) ;
if ( sel_vol & & sel_vol - > type ( ) > = ModelVolumeType : : SUPPORT_ENFORCER )
return nullptr ;
// Create new items for settings popupmenu
2025-05-08 15:05:30 +08:00
if ( printer_technology ( ) = = ptFFF | | ( menu - > GetMenuItems ( ) . size ( ) > 0 & & ! menu - > GetMenuItems ( ) . back ( ) - > IsSeparator ( ) ) ) {
// menu->SetFirstSeparator();
}
2024-09-03 09:34:33 +08:00
// detect itemm for adding of the setting
ObjectList * object_list = obj_list ( ) ;
ObjectDataViewModel * obj_model = list_model ( ) ;
const wxDataViewItem sel_item = // when all instances in object are selected
object_list - > GetSelectedItemsCount ( ) > 1 & & selection . is_single_full_object ( ) ?
obj_model - > GetItemById ( selection . get_object_idx ( ) ) :
object_list - > GetSelection ( ) ;
if ( ! sel_item )
return nullptr ;
// If we try to add settings for object/part from 3Dscene,
// for the second try there is selected ItemSettings in ObjectList.
// So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes
wxDataViewItem item = obj_model - > GetItemType ( sel_item ) & itSettings ? obj_model - > GetParent ( sel_item ) : sel_item ;
const ItemType item_type = obj_model - > GetItemType ( item ) ;
const bool is_object_settings = ! ( item_type & itVolume | | item_type & itLayer ) ;
// Add frequently settings
// QDS remvoe freq setting popupmenu
// create_freq_settings_popupmenu(menu, is_object_settings, item);
//menu->SetSecondSeparator();
// Add full settings list
auto menu_item = new wxMenuItem ( menu , wxID_ANY , menu_name ) ;
menu_item - > SetBitmap ( create_scaled_bitmap ( " cog " ) ) ;
menu_item - > SetSubMenu ( create_settings_popupmenu ( menu , is_object_settings , item ) ) ;
return menu - > Append ( menu_item ) ;
}
wxMenuItem * MenuFactory : : append_menu_item_change_type ( wxMenu * menu )
{
return append_menu_item ( menu , wxID_ANY , _L ( " Change type " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > change_part_type ( ) ; } , " " , menu ,
[ ] ( ) {
2024-11-28 15:19:12 +08:00
const Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
if ( selection . get_volume_idxs ( ) . size ( ) ! = 1 ) {
return false ;
}
2024-09-03 09:34:33 +08:00
wxDataViewItem item = obj_list ( ) - > GetSelection ( ) ;
return item . IsOk ( ) | | obj_list ( ) - > GetModel ( ) - > GetItemType ( item ) = = itVolume ;
} , m_parent ) ;
}
wxMenuItem * MenuFactory : : append_menu_item_instance_to_object ( wxMenu * menu )
{
wxMenuItem * menu_item = append_menu_item ( menu , wxID_ANY , _L ( " Set as an individual object " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > split_instances ( ) ; } , " " , menu ) ;
/* 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
*/
m_parent - > Bind ( wxEVT_UPDATE_UI , [ ] ( wxUpdateUIEvent & evt )
{
const Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
evt . SetText ( selection . is_single_full_object ( ) ?
_L ( " Set as individual objects " ) : _L ( " Set as an individual object " ) ) ;
evt . Enable ( plater ( ) - > can_set_instance_to_object ( ) ) ;
} , menu_item - > GetId ( ) ) ;
return menu_item ;
}
wxMenuItem * MenuFactory : : append_menu_item_printable ( wxMenu * menu )
{
// QDS: to be checked
wxMenuItem * menu_item_printable = append_menu_check_item ( menu , wxID_ANY , _L ( " Printable " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > toggle_printable_state ( ) ; } , menu ) ;
m_parent - > Bind ( wxEVT_UPDATE_UI , [ ] ( wxUpdateUIEvent & evt ) {
ObjectList * list = obj_list ( ) ;
wxDataViewItemArray sels ;
list - > GetSelections ( sels ) ;
wxDataViewItem frst_item = sels [ 0 ] ;
ItemType type = list - > GetModel ( ) - > GetItemType ( frst_item ) ;
bool check ;
if ( type ! = itInstance & & type ! = itObject )
check = false ;
else {
int obj_idx = list - > GetModel ( ) - > GetObjectIdByItem ( frst_item ) ;
int inst_idx = type = = itObject ? 0 : list - > GetModel ( ) - > GetInstanceIdByItem ( frst_item ) ;
check = list - > object ( obj_idx ) - > instances [ inst_idx ] - > printable ;
}
evt . Check ( check ) ;
plater ( ) - > set_current_canvas_as_dirty ( ) ;
} , menu_item_printable - > GetId ( ) ) ;
return menu_item_printable ;
}
void MenuFactory : : append_menu_item_rename ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Rename " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > rename_item ( ) ; } , " " , menu ) ;
menu - > AppendSeparator ( ) ;
}
wxMenuItem * MenuFactory : : append_menu_item_fix_through_netfabb ( wxMenu * menu )
{
if ( ! is_windows10 ( ) )
return nullptr ;
wxMenuItem * menu_item = append_menu_item ( menu , wxID_ANY , _L ( " Fix model " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > fix_through_netfabb ( ) ; } , " " , menu ,
[ ] ( ) { return plater ( ) - > can_fix_through_netfabb ( ) ; } , plater ( ) ) ;
return menu_item ;
}
void MenuFactory : : append_menu_item_export_stl ( wxMenu * menu , bool is_mulity_menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Export as one STL " ) , " " ,
[ ] ( wxCommandEvent & ) { plater ( ) - > export_stl ( false , true ) ; } , " " , nullptr ,
[ is_mulity_menu ] ( ) {
const Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
if ( is_mulity_menu )
return selection . is_multiple_full_instance ( ) | | selection . is_multiple_full_object ( ) ;
else
return selection . is_single_full_instance ( ) | | selection . is_single_full_object ( ) ;
} , m_parent ) ;
if ( ! is_mulity_menu )
return ;
append_menu_item ( menu , wxID_ANY , _L ( " Export as STLs " ) + dots , " " ,
[ ] ( wxCommandEvent & ) { plater ( ) - > export_stl ( false , true , true ) ; } , " " , nullptr ,
[ ] ( ) {
const Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
return selection . is_multiple_full_instance ( ) | | selection . is_multiple_full_object ( ) ;
} , m_parent ) ;
}
void MenuFactory : : append_menu_item_reload_from_disk ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Reload from disk " ) , _L ( " Reload the selected parts from disk " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > reload_from_disk ( ) ; } , " " , menu ,
[ ] ( ) { return plater ( ) - > can_reload_from_disk ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_replace_with_stl ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Replace with STL " ) , _L ( " Replace the selected part with new STL " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > replace_with_stl ( ) ; } , " " , menu ,
[ ] ( ) { return plater ( ) - > can_replace_with_stl ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_change_extruder ( wxMenu * menu )
{
// QDS
const std : : vector < wxString > names = { _L ( " Change filament " ) , _L ( " Set filament for selected items " ) } ;
// Delete old menu item
for ( const wxString & name : names ) {
const int item_id = menu - > FindItem ( name ) ;
if ( item_id ! = wxNOT_FOUND )
menu - > Destroy ( item_id ) ;
}
const int filaments_cnt = filaments_count ( ) ;
if ( filaments_cnt < = 1 )
return ;
wxDataViewItemArray sels ;
obj_list ( ) - > GetSelections ( sels ) ;
if ( sels . IsEmpty ( ) )
return ;
std : : vector < wxBitmap * > icons = get_extruder_color_icons ( true ) ;
wxMenu * extruder_selection_menu = new wxMenu ( ) ;
const wxString & name = sels . Count ( ) = = 1 ? names [ 0 ] : names [ 1 ] ;
int initial_extruder = - 1 ; // negative value for multiple object/part selection
if ( sels . Count ( ) = = 1 ) {
const ModelConfig & config = obj_list ( ) - > get_item_config ( sels [ 0 ] ) ;
// QDS: set default extruder to 1
initial_extruder = config . has ( " extruder " ) ? config . extruder ( ) : 1 ;
}
for ( int i = 0 ; i < = filaments_cnt ; i + + )
{
bool is_active_extruder = i = = initial_extruder ;
int icon_idx = i = = 0 ? 0 : i - 1 ;
wxString item_name = _L ( " Default " ) ;
if ( i > 0 ) {
auto preset = wxGetApp ( ) . preset_bundle - > filaments . find_preset ( wxGetApp ( ) . preset_bundle - > filament_presets [ i - 1 ] ) ;
if ( preset = = nullptr ) {
item_name = wxString : : Format ( _L ( " Filament %d " ) , i ) ;
} else {
item_name = from_u8 ( preset - > label ( false ) ) ;
}
}
if ( is_active_extruder ) {
item_name < < " ( " + _L ( " current " ) + " ) " ;
}
if ( icon_idx > = 0 & & icon_idx < icons . size ( ) ) {
append_menu_item (
extruder_selection_menu , wxID_ANY , item_name , " " , [ i ] ( wxCommandEvent & ) { obj_list ( ) - > set_extruder_for_selected_items ( i ) ; } , * icons [ icon_idx ] , menu ,
[ is_active_extruder ] ( ) { return ! is_active_extruder ; } , m_parent ) ;
} else {
append_menu_item (
extruder_selection_menu , wxID_ANY , item_name , " " , [ i ] ( wxCommandEvent & ) { obj_list ( ) - > set_extruder_for_selected_items ( i ) ; } , " " , menu ,
[ is_active_extruder ] ( ) { return ! is_active_extruder ; } , m_parent ) ;
}
}
menu - > AppendSubMenu ( extruder_selection_menu , name ) ;
}
void MenuFactory : : append_menu_item_scale_selection_to_fit_print_volume ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Scale to build volume " ) , _L ( " Scale an object to fit the build volume " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > scale_selection_to_fit_print_volume ( ) ; } , " " , menu ) ;
}
void MenuFactory : : append_menu_items_flush_options ( wxMenu * menu )
{
const wxString name = _L ( " Flush Options " ) ;
// Delete old menu item
const int item_id = menu - > FindItem ( name ) ;
if ( item_id ! = wxNOT_FOUND )
menu - > Destroy ( item_id ) ;
bool show_flush_option_menu = false ;
ObjectList * object_list = obj_list ( ) ;
const Selection & selection = get_selection ( ) ;
if ( selection . get_object_idx ( ) < 0 )
return ;
if ( wxGetApp ( ) . plater ( ) - > get_partplate_list ( ) . get_curr_plate ( ) - > contains ( selection . get_bounding_box ( ) ) ) {
auto plate_extruders = wxGetApp ( ) . plater ( ) - > get_partplate_list ( ) . get_curr_plate ( ) - > get_extruders ( ) ;
for ( auto extruder : plate_extruders ) {
if ( extruder ! = plate_extruders [ 0 ] )
show_flush_option_menu = true ;
}
}
if ( ! show_flush_option_menu )
return ;
DynamicPrintConfig & global_config = wxGetApp ( ) . preset_bundle - > prints . get_edited_preset ( ) . config ;
ModelConfig & select_object_config = object_list - > object ( selection . get_object_idx ( ) ) - > config ;
wxMenu * flush_options_menu = new wxMenu ( ) ;
auto can_flush = [ & global_config ] ( ) {
auto option = global_config . option ( " enable_prime_tower " ) ;
return option ? option - > getBool ( ) : false ;
} ;
append_menu_check_item ( flush_options_menu , wxID_ANY , _L ( " Flush into objects' infill " ) , " " ,
[ & select_object_config , & global_config ] ( wxCommandEvent & ) {
const ConfigOption * option = select_object_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 0 ] ) ;
if ( ! option ) {
option = global_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 0 ] ) ;
}
select_object_config . set_key_value ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 0 ] , new ConfigOptionBool ( ! option - > getBool ( ) ) ) ;
wxGetApp ( ) . obj_settings ( ) - > UpdateAndShow ( true ) ;
} , menu , can_flush ,
[ & select_object_config , & global_config ] ( ) {
const ConfigOption * option = select_object_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 0 ] ) ;
if ( ! option ) {
option = global_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 0 ] ) ;
}
return option - > getBool ( ) ;
} , m_parent ) ;
append_menu_check_item ( flush_options_menu , wxID_ANY , _L ( " Flush into this object " ) , " " ,
[ & select_object_config , & global_config ] ( wxCommandEvent & ) {
const ConfigOption * option = select_object_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 1 ] ) ;
if ( ! option ) {
option = global_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 1 ] ) ;
}
select_object_config . set_key_value ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 1 ] , new ConfigOptionBool ( ! option - > getBool ( ) ) ) ;
wxGetApp ( ) . obj_settings ( ) - > UpdateAndShow ( true ) ;
} , menu , can_flush ,
[ & select_object_config , & global_config ] ( ) {
const ConfigOption * option = select_object_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 1 ] ) ;
if ( ! option ) {
option = global_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 1 ] ) ;
}
return option - > getBool ( ) ;
} , m_parent ) ;
append_menu_check_item ( flush_options_menu , wxID_ANY , _L ( " Flush into objects' support " ) , " " ,
[ & select_object_config , & global_config ] ( wxCommandEvent & ) {
const ConfigOption * option = select_object_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 2 ] ) ;
if ( ! option ) {
option = global_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 2 ] ) ;
}
select_object_config . set_key_value ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 2 ] , new ConfigOptionBool ( ! option - > getBool ( ) ) ) ;
wxGetApp ( ) . obj_settings ( ) - > UpdateAndShow ( true ) ;
} , menu , can_flush ,
[ & select_object_config , & global_config ] ( ) {
const ConfigOption * option = select_object_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 2 ] ) ;
if ( ! option ) {
option = global_config . option ( FREQ_SETTINGS_BUNDLE_FFF [ " Flush options " ] [ 2 ] ) ;
}
return option - > getBool ( ) ;
} , m_parent ) ;
size_t i = 0 ;
for ( auto node = menu - > GetMenuItems ( ) . GetFirst ( ) ; node ; node = node - > GetNext ( ) )
{
i + + ;
wxMenuItem * item = node - > GetData ( ) ;
if ( item - > GetItemLabelText ( ) = = _L ( " Edit in Parameter Table " ) )
break ;
}
menu - > Insert ( i , wxID_ANY , _L ( " Flush Options " ) , flush_options_menu ) ;
}
void MenuFactory : : append_menu_items_convert_unit ( wxMenu * menu )
{
std : : vector < int > obj_idxs , vol_idxs ;
obj_list ( ) - > get_selection_indexes ( obj_idxs , vol_idxs ) ;
if ( obj_idxs . empty ( ) & & vol_idxs . empty ( ) )
return ;
auto volume_respects_conversion = [ ] ( ModelVolume * volume , ConversionType conver_type )
{
return ( conver_type = = ConversionType : : CONV_FROM_INCH & & volume - > source . is_converted_from_inches ) | |
( conver_type = = ConversionType : : CONV_TO_INCH & & ! volume - > source . is_converted_from_inches ) | |
( conver_type = = ConversionType : : CONV_FROM_METER & & volume - > source . is_converted_from_meters ) | |
( conver_type = = ConversionType : : CONV_TO_METER & & ! volume - > source . is_converted_from_meters ) ;
} ;
auto can_append = [ obj_idxs , vol_idxs , volume_respects_conversion ] ( ConversionType conver_type )
{
ModelObjectPtrs objects ;
for ( int obj_idx : obj_idxs ) {
ModelObject * object = obj_list ( ) - > object ( obj_idx ) ;
if ( vol_idxs . empty ( ) ) {
for ( ModelVolume * volume : object - > volumes )
if ( volume_respects_conversion ( volume , conver_type ) )
return false ;
}
else {
for ( int vol_idx : vol_idxs )
if ( volume_respects_conversion ( object - > volumes [ vol_idx ] , conver_type ) )
return false ;
}
}
return true ;
} ;
std : : vector < std : : pair < ConversionType , wxString > > items = {
{ ConversionType : : CONV_FROM_INCH , _L ( " Convert from inch " ) } ,
{ ConversionType : : CONV_TO_INCH , _L ( " Restore to inch " ) } ,
{ ConversionType : : CONV_FROM_METER , _L ( " Convert from meter " ) } ,
{ ConversionType : : CONV_TO_METER , _L ( " Restore to meter " ) } } ;
for ( auto item : items ) {
int menu_id = menu - > FindItem ( item . second ) ;
if ( can_append ( item . first ) ) {
// Add menu item if it doesn't exist
if ( menu_id = = wxNOT_FOUND )
append_menu_item ( menu , wxID_ANY , item . second , item . second ,
[ item ] ( wxCommandEvent & ) { plater ( ) - > convert_unit ( item . first ) ; } , " " , menu ,
[ ] ( ) { return true ; } , m_parent ) ;
}
else if ( menu_id ! = wxNOT_FOUND ) {
// Delete menu item
menu - > Destroy ( menu_id ) ;
}
}
}
void MenuFactory : : append_menu_item_merge_to_multipart_object ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Merge " ) , _L ( " Assemble the selected objects to an object with multiple parts " ) ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > merge ( true ) ; } , " " , menu ,
[ ] ( ) { return obj_list ( ) - > can_merge_to_multipart_object ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_merge_to_single_object ( wxMenu * menu )
{
menu - > AppendSeparator ( ) ;
append_menu_item ( menu , wxID_ANY , _L ( " Merge " ) , _L ( " Assemble the selected objects to an object with single part " ) ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > merge ( false ) ; } , " " , menu ,
[ ] ( ) { return obj_list ( ) - > can_merge_to_single_object ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_merge_parts_to_single_part ( wxMenu * menu )
{
menu - > AppendSeparator ( ) ;
append_menu_item ( menu , wxID_ANY , _L ( " Mesh boolean " ) , _L ( " Mesh boolean operations including union and subtraction " ) ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > boolean /*merge_volumes*/ ( ) ; } , " " , menu ,
[ ] ( ) { return obj_list ( ) - > can_mesh_boolean ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_items_mirror ( wxMenu * menu )
{
wxMenu * mirror_menu = new wxMenu ( ) ;
if ( ! mirror_menu )
return ;
append_menu_item ( mirror_menu , wxID_ANY , _L ( " Along X axis " ) , _L ( " Mirror along the X axis " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > mirror ( X ) ; } , " " , menu ) ;
append_menu_item ( mirror_menu , wxID_ANY , _L ( " Along Y axis " ) , _L ( " Mirror along the Y axis " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > mirror ( Y ) ; } , " " , menu ) ;
append_menu_item ( mirror_menu , wxID_ANY , _L ( " Along Z axis " ) , _L ( " Mirror along the Z axis " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > mirror ( Z ) ; } , " " , menu ) ;
append_submenu ( menu , mirror_menu , wxID_ANY , _L ( " Mirror " ) , _L ( " Mirror object " ) , " " ,
[ ] ( ) { return plater ( ) - > can_mirror ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_invalidate_cut_info ( wxMenu * menu )
{
const wxString menu_name = _L ( " Invalidate cut info " ) ;
auto menu_item_id = menu - > FindItem ( menu_name ) ;
if ( menu_item_id ! = wxNOT_FOUND )
// Delete old menu item if selected object isn't cut
menu - > Destroy ( menu_item_id ) ;
if ( obj_list ( ) - > has_selected_cut_object ( ) )
append_menu_item ( menu , wxID_ANY , menu_name , " " , [ ] ( wxCommandEvent & ) { obj_list ( ) - > invalidate_cut_info_for_selection ( ) ; } ,
" " , menu , [ ] ( ) { return true ; } , m_parent ) ;
}
MenuFactory : : MenuFactory ( )
{
for ( int i = 0 ; i < mtCount ; i + + ) {
items_increase [ i ] = nullptr ;
items_decrease [ i ] = nullptr ;
items_set_number_of_copies [ i ] = nullptr ;
}
}
void MenuFactory : : create_default_menu ( )
{
wxMenu * sub_menu = append_submenu_add_generic ( & m_default_menu , ModelVolumeType : : INVALID ) ;
# ifdef __WINDOWS__
append_submenu ( & m_default_menu , sub_menu , wxID_ANY , _L ( " Add Primitive " ) , " " , " menu_add_part " ,
[ ] ( ) { return true ; } , m_parent ) ;
# else
append_submenu ( & m_default_menu , sub_menu , wxID_ANY , _L ( " Add Primitive " ) , " " , " " ,
[ ] ( ) { return true ; } , m_parent ) ;
# endif
m_default_menu . AppendSeparator ( ) ;
append_menu_check_item ( & m_default_menu , wxID_ANY , _L ( " Show Labels " ) , " " ,
[ ] ( wxCommandEvent & ) { plater ( ) - > show_view3D_labels ( ! plater ( ) - > are_view3D_labels_shown ( ) ) ; plater ( ) - > get_current_canvas3D ( ) - > post_event ( SimpleEvent ( wxEVT_PAINT ) ) ; } , & m_default_menu ,
[ ] ( ) { return plater ( ) - > is_view3D_shown ( ) ; } , [ this ] ( ) { return plater ( ) - > are_view3D_labels_shown ( ) ; } , m_parent ) ;
}
void MenuFactory : : create_common_object_menu ( wxMenu * menu )
{
append_menu_item_rename ( menu ) ;
// QDS
//append_menu_items_instance_manipulation(menu);
// Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake.
append_menu_item_delete ( menu ) ;
//append_menu_item_instance_to_object(menu);
menu - > AppendSeparator ( ) ;
// QDS
append_menu_item_reload_from_disk ( menu ) ;
append_menu_item_export_stl ( menu ) ;
// "Scale to print volume" makes a sense just for whole object
append_menu_item_scale_selection_to_fit_print_volume ( menu ) ;
append_menu_item_fix_through_netfabb ( menu ) ;
append_menu_items_mirror ( menu ) ;
}
void MenuFactory : : create_object_menu ( )
{
create_common_object_menu ( & m_object_menu ) ;
wxMenu * split_menu = new wxMenu ( ) ;
if ( ! split_menu )
return ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To objects " ) , _L ( " Split the selected object into multiple objects " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_object ( ) ; } , " split_objects " , & m_object_menu ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To parts " ) , _L ( " Split the selected object into multiple parts " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_volume ( ) ; } , " split_parts " , & m_object_menu ,
[ ] ( ) { return plater ( ) - > can_split ( false ) ; } , m_parent ) ;
append_submenu ( & m_object_menu , split_menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object " ) , " " ,
[ ] ( ) { return plater ( ) - > can_split ( true ) | | plater ( ) - > can_split ( false ) ; } , m_parent ) ;
m_object_menu . AppendSeparator ( ) ;
// QDS: remove Layers Editing
m_object_menu . AppendSeparator ( ) ;
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
}
void MenuFactory : : create_qdt_object_menu ( )
{
append_menu_item_fill_bed ( & m_object_menu ) ;
// Object Clone
append_menu_item_clone ( & m_object_menu ) ;
// Object Repair
append_menu_item_fix_through_netfabb ( & m_object_menu ) ;
// Object Simplify
append_menu_item_simplify ( & m_object_menu ) ;
// merge to single part
append_menu_item_merge_parts_to_single_part ( & m_object_menu ) ;
// Object Center
append_menu_item_center ( & m_object_menu ) ;
// Object Split
wxMenu * split_menu = new wxMenu ( ) ;
if ( ! split_menu )
return ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To objects " ) , _L ( " Split the selected object into multiple objects " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_object ( ) ; } , " split_objects " , & m_object_menu ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To parts " ) , _L ( " Split the selected object into multiple parts " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_volume ( ) ; } , " split_parts " , & m_object_menu ,
[ ] ( ) { return plater ( ) - > can_split ( false ) ; } , m_parent ) ;
append_submenu ( & m_object_menu , split_menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object " ) , " " ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
// Mirror
append_menu_items_mirror ( & m_object_menu ) ;
// Delete
append_menu_item_delete ( & m_object_menu ) ;
m_object_menu . AppendSeparator ( ) ;
// Modifier Part
// QDS
append_menu_items_add_volume ( & m_object_menu ) ;
m_object_menu . AppendSeparator ( ) ;
// Set filament insert menu item here
// Set Printable
wxMenuItem * menu_item_printable = append_menu_item_printable ( & m_object_menu ) ;
append_menu_item_per_object_process ( & m_object_menu ) ;
// Enter per object parameters
append_menu_item_per_object_settings ( & m_object_menu ) ;
m_object_menu . AppendSeparator ( ) ;
append_menu_item_reload_from_disk ( & m_object_menu ) ;
append_menu_item_replace_with_stl ( & m_object_menu ) ;
append_menu_item_export_stl ( & m_object_menu ) ;
}
void MenuFactory : : create_qdt_assemble_object_menu ( )
{
// Delete
append_menu_item_delete ( & m_assemble_object_menu ) ;
// Object Repair
append_menu_item_fix_through_netfabb ( & m_assemble_object_menu ) ;
// Object Simplify
append_menu_item_simplify ( & m_assemble_object_menu ) ;
m_assemble_object_menu . AppendSeparator ( ) ;
}
void MenuFactory : : create_sla_object_menu ( )
{
create_common_object_menu ( & m_sla_object_menu ) ;
append_menu_item ( & m_sla_object_menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object into multiple objects " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_object ( ) ; } , " split_objects " , nullptr ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
m_sla_object_menu . AppendSeparator ( ) ;
// Add the automatic rotation sub-menu
append_menu_item ( & m_sla_object_menu , wxID_ANY , _L ( " Auto orientation " ) , _L ( " Auto orient the object to improve print quality. " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > optimize_rotation ( ) ; } ) ;
}
void MenuFactory : : create_part_menu ( )
{
wxMenu * menu = & m_part_menu ;
append_menu_item_rename ( menu ) ;
append_menu_item_delete ( menu ) ;
append_menu_item_reload_from_disk ( menu ) ;
append_menu_item_export_stl ( menu ) ;
append_menu_item_fix_through_netfabb ( menu ) ;
append_menu_items_mirror ( menu ) ;
append_menu_item_merge_parts_to_single_part ( menu ) ;
append_menu_item ( menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object into multiple parts " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_volume ( ) ; } , " split_parts " , nullptr ,
[ ] ( ) { return plater ( ) - > can_split ( false ) ; } , m_parent ) ;
menu - > AppendSeparator ( ) ;
append_menu_item_change_type ( menu ) ;
append_menu_items_mirror ( & m_part_menu ) ;
append_menu_item ( & m_part_menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object into multiple parts " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_volume ( ) ; } , " split_parts " , nullptr ,
[ ] ( ) { return plater ( ) - > can_split ( false ) ; } , m_parent ) ;
m_part_menu . AppendSeparator ( ) ;
2025-07-10 09:14:38 +08:00
append_menu_item_per_object_process ( & m_part_menu ) ;
2024-09-03 09:34:33 +08:00
append_menu_item_per_object_settings ( & m_part_menu ) ;
}
2024-11-28 15:19:12 +08:00
void MenuFactory : : create_text_part_menu ( )
{
wxMenu * menu = & m_text_part_menu ;
append_menu_item_edit_text ( menu ) ;
append_menu_item_delete ( menu ) ;
append_menu_item_fix_through_netfabb ( menu ) ;
append_menu_item_simplify ( menu ) ;
append_menu_items_mirror ( menu ) ;
menu - > AppendSeparator ( ) ;
2025-07-10 09:14:38 +08:00
append_menu_item_per_object_process ( menu ) ;
2024-11-28 15:19:12 +08:00
append_menu_item_per_object_settings ( menu ) ;
append_menu_item_change_type ( menu ) ;
}
void MenuFactory : : create_svg_part_menu ( )
{
wxMenu * menu = & m_svg_part_menu ;
append_menu_item_edit_svg ( menu ) ;
append_menu_item_delete ( menu ) ;
append_menu_item_fix_through_netfabb ( menu ) ;
append_menu_item_simplify ( menu ) ;
append_menu_items_mirror ( menu ) ;
menu - > AppendSeparator ( ) ;
2025-07-10 09:14:38 +08:00
append_menu_item_per_object_process ( menu ) ;
2024-11-28 15:19:12 +08:00
append_menu_item_per_object_settings ( menu ) ;
append_menu_item_change_type ( menu ) ;
}
2024-09-03 09:34:33 +08:00
void MenuFactory : : create_qdt_part_menu ( )
{
wxMenu * menu = & m_part_menu ;
append_menu_item_delete ( menu ) ;
append_menu_item_edit_text ( menu ) ;
append_menu_item_fix_through_netfabb ( menu ) ;
append_menu_item_simplify ( menu ) ;
append_menu_item_center ( menu ) ;
append_menu_items_mirror ( menu ) ;
wxMenu * split_menu = new wxMenu ( ) ;
if ( ! split_menu )
return ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To objects " ) , _L ( " Split the selected object into multiple objects " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_object ( ) ; } , " split_objects " , menu ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To parts " ) , _L ( " Split the selected object into multiple parts " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_volume ( ) ; } , " split_parts " , menu ,
[ ] ( ) { return plater ( ) - > can_split ( false ) ; } , m_parent ) ;
append_submenu ( menu , split_menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object " ) , " " ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
menu - > AppendSeparator ( ) ;
2025-07-10 09:14:38 +08:00
append_menu_item_per_object_process ( menu ) ;
2024-09-03 09:34:33 +08:00
append_menu_item_per_object_settings ( menu ) ;
append_menu_item_change_type ( menu ) ;
append_menu_item_reload_from_disk ( menu ) ;
append_menu_item_replace_with_stl ( menu ) ;
}
void MenuFactory : : create_qdt_assemble_part_menu ( )
{
wxMenu * menu = & m_assemble_part_menu ;
append_menu_item_delete ( menu ) ;
append_menu_item_simplify ( menu ) ;
menu - > AppendSeparator ( ) ;
}
2024-11-28 15:19:12 +08:00
void MenuFactory : : create_cut_cutter_menu ( )
{
wxMenu * menu = & m_cut_cutter_menu ;
append_menu_item_delete_all_cutter ( menu ) ;
append_menu_item_change_type ( menu ) ;
}
2025-05-08 15:05:30 +08:00
void MenuFactory : : create_filament_action_menu ( bool init , int active_filament_menu_id )
{
wxMenu * menu = & m_filament_action_menu ;
if ( init ) {
append_menu_item (
menu , wxID_ANY , _L ( " Edit " ) , " " , [ ] ( wxCommandEvent & ) {
plater ( ) - > sidebar ( ) . edit_filament ( ) ; } , " " , nullptr ,
[ ] ( ) { return true ; } , m_parent ) ;
}
if ( init ) {
append_menu_item (
menu , wxID_ANY , _L ( " Delete " ) , _L ( " Delete this filament " ) , [ ] ( wxCommandEvent & ) {
plater ( ) - > sidebar ( ) . delete_filament ( - 2 ) ; } , " " , nullptr ,
[ ] ( ) { return plater ( ) - > sidebar ( ) . combos_filament ( ) . size ( ) > 1 ; } , m_parent ) ;
}
const int item_id = menu - > FindItem ( _L ( " Merge with " ) ) ;
if ( item_id ! = wxNOT_FOUND )
menu - > Destroy ( item_id ) ;
wxMenu * sub_menu = new wxMenu ( ) ;
std : : vector < wxBitmap * > icons = get_extruder_color_icons ( true ) ;
int filaments_cnt = icons . size ( ) ;
for ( int i = 0 ; i < filaments_cnt ; i + + ) {
if ( i = = active_filament_menu_id )
continue ;
auto preset = wxGetApp ( ) . preset_bundle - > filaments . find_preset ( wxGetApp ( ) . preset_bundle - > filament_presets [ i ] ) ;
wxString item_name = preset ? from_u8 ( preset - > label ( false ) ) : wxString : : Format ( _L ( " Filament %d " ) , i + 1 ) ;
append_menu_item ( sub_menu , wxID_ANY , item_name , " " ,
[ i ] ( wxCommandEvent & ) { plater ( ) - > sidebar ( ) . change_filament ( - 2 , i ) ; } , * icons [ i ] , menu ,
[ ] ( ) { return true ; } , m_parent ) ;
}
append_submenu ( menu , sub_menu , wxID_ANY , _L ( " Merge with " ) , " " , " " ,
[ filaments_cnt ] ( ) { return filaments_cnt > 1 ; } , m_parent ) ;
}
2024-09-03 09:34:33 +08:00
//QDS: add part plate related logic
void MenuFactory : : create_plate_menu ( )
{
wxMenu * menu = & m_plate_menu ;
// select objects on current plate
append_menu_item ( menu , wxID_ANY , _L ( " Select All " ) , _L ( " select all objects on current plate " ) ,
[ ] ( wxCommandEvent & ) {
plater ( ) - > select_curr_plate_all ( ) ;
} , " " , nullptr , [ ] ( ) {
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
return ! plate - > get_objects ( ) . empty ( ) ;
} , m_parent ) ;
// delete objects on current plate
append_menu_item ( menu , wxID_ANY , _L ( " Delete All " ) , _L ( " delete all objects on current plate " ) ,
[ ] ( wxCommandEvent & ) {
plater ( ) - > remove_curr_plate_all ( ) ;
} , " " , nullptr , [ ] ( ) {
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
return ! plate - > get_objects ( ) . empty ( ) ;
} , m_parent ) ;
// arrange objects on current plate
append_menu_item ( menu , wxID_ANY , _L ( " Arrange " ) , _L ( " arrange current plate " ) ,
[ ] ( wxCommandEvent & ) {
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
2024-11-28 15:19:12 +08:00
if ( ! plate - > get_objects ( ) . empty ( ) & & ! plater ( ) - > is_background_process_slicing ( ) )
{
plater ( ) - > set_prepare_state ( Job : : PREPARE_STATE_MENU ) ;
plater ( ) - > arrange ( ) ;
}
2024-09-03 09:34:33 +08:00
} , " " , nullptr ,
[ ] ( ) {
2024-11-28 15:19:12 +08:00
return ! plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) - > get_objects ( ) . empty ( ) & & ! plater ( ) - > is_background_process_slicing ( ) ;
2024-09-03 09:34:33 +08:00
} ,
m_parent ) ;
// orient objects on current plate
append_menu_item ( menu , wxID_ANY , _L ( " Auto Rotate " ) , _L ( " auto rotate current plate " ) ,
[ ] ( wxCommandEvent & ) {
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
2024-11-28 15:19:12 +08:00
if ( ! plate - > get_objects ( ) . empty ( ) & & ! plater ( ) - > is_background_process_slicing ( ) )
{
//QDS TODO call auto rotate for current plate
plater ( ) - > set_prepare_state ( Job : : PREPARE_STATE_MENU ) ;
plater ( ) - > orient ( ) ;
}
2024-09-03 09:34:33 +08:00
} , " " , nullptr ,
[ ] ( ) {
2024-11-28 15:19:12 +08:00
return ! plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) - > get_objects ( ) . empty ( ) & & ! plater ( ) - > is_background_process_slicing ( ) ;
2024-09-03 09:34:33 +08:00
} , m_parent ) ;
// delete current plate
# ifdef __WINDOWS__
append_menu_item ( menu , wxID_ANY , _L ( " Delete Plate " ) , _L ( " Remove the selected plate " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > delete_plate ( ) ; } , " menu_delete " , nullptr ,
[ ] ( ) { return plater ( ) - > can_delete_plate ( ) ; } , m_parent ) ;
# else
append_menu_item ( menu , wxID_ANY , _L ( " Delete Plate " ) , _L ( " Remove the selected plate " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > delete_plate ( ) ; } , " " , nullptr ,
[ ] ( ) { return plater ( ) - > can_delete_plate ( ) ; } , m_parent ) ;
# endif
// add shapes
menu - > AppendSeparator ( ) ;
wxMenu * sub_menu = append_submenu_add_generic ( menu , ModelVolumeType : : INVALID ) ;
# ifdef __WINDOWS__
append_submenu ( menu , sub_menu , wxID_ANY , _L ( " Add Primitive " ) , " " , " menu_add_part " ,
[ ] ( ) { return true ; } , m_parent ) ;
# else
append_submenu ( menu , sub_menu , wxID_ANY , _L ( " Add Primitive " ) , " " , " " ,
[ ] ( ) { return true ; } , m_parent ) ;
# endif
return ;
}
void MenuFactory : : init ( wxWindow * parent )
{
m_parent = parent ;
create_default_menu ( ) ;
//QDS
//create_object_menu();
create_sla_object_menu ( ) ;
//create_part_menu();
2025-08-04 16:30:53 +08:00
create_text_part_menu ( ) ;
2024-11-28 15:19:12 +08:00
create_svg_part_menu ( ) ;
2024-09-03 09:34:33 +08:00
create_qdt_object_menu ( ) ;
create_qdt_part_menu ( ) ;
create_qdt_assemble_object_menu ( ) ;
create_qdt_assemble_part_menu ( ) ;
2024-11-28 15:19:12 +08:00
create_cut_cutter_menu ( ) ;
2024-09-03 09:34:33 +08:00
//QDS: add part plate related logic
create_plate_menu ( ) ;
2025-05-08 15:05:30 +08:00
create_filament_action_menu ( true , - 1 ) ;
2024-09-03 09:34:33 +08:00
// create "Instance to Object" menu item
append_menu_item_instance_to_object ( & m_instance_menu ) ;
}
void MenuFactory : : update ( )
{
update_default_menu ( ) ;
update_object_menu ( ) ;
}
wxMenu * MenuFactory : : default_menu ( )
{
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " default_menu " , std : : to_string ( + + default_menu_count ) ) ;
}
return & m_default_menu ;
}
wxMenu * MenuFactory : : object_menu ( )
{
append_menu_items_convert_unit ( & m_object_menu ) ;
append_menu_items_flush_options ( & m_object_menu ) ;
append_menu_item_invalidate_cut_info ( & m_object_menu ) ;
2025-08-04 16:30:53 +08:00
append_menu_item_edit_text ( & m_object_menu ) ;
2024-11-28 15:19:12 +08:00
append_menu_item_edit_svg ( & m_object_menu ) ;
2024-09-03 09:34:33 +08:00
append_menu_item_change_filament ( & m_object_menu ) ;
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " object_menu " , std : : to_string ( + + object_menu_count ) ) ;
}
return & m_object_menu ;
}
wxMenu * MenuFactory : : sla_object_menu ( )
{
append_menu_items_convert_unit ( & m_sla_object_menu ) ;
append_menu_item_settings ( & m_sla_object_menu ) ;
2025-08-04 16:30:53 +08:00
append_menu_item_edit_text ( & m_sla_object_menu ) ;
2024-11-28 15:19:12 +08:00
append_menu_item_edit_svg ( & m_object_menu ) ;
2024-09-03 09:34:33 +08:00
//update_menu_items_instance_manipulation(mtObjectSLA);
return & m_sla_object_menu ;
}
wxMenu * MenuFactory : : part_menu ( )
{
append_menu_items_convert_unit ( & m_part_menu ) ;
append_menu_item_change_filament ( & m_part_menu ) ;
append_menu_item_per_object_settings ( & m_part_menu ) ;
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " part_menu " , std : : to_string ( + + part_menu_count ) ) ;
}
return & m_part_menu ;
}
2024-11-28 15:19:12 +08:00
wxMenu * MenuFactory : : text_part_menu ( )
{
append_menu_item_change_filament ( & m_text_part_menu ) ;
append_menu_item_per_object_settings ( & m_text_part_menu ) ;
return & m_text_part_menu ;
}
wxMenu * MenuFactory : : svg_part_menu ( )
{
append_menu_item_change_filament ( & m_svg_part_menu ) ;
append_menu_item_per_object_settings ( & m_svg_part_menu ) ;
return & m_svg_part_menu ;
}
wxMenu * MenuFactory : : cut_connector_menu ( )
{
return & m_cut_cutter_menu ;
}
2024-09-03 09:34:33 +08:00
wxMenu * MenuFactory : : instance_menu ( )
{
return & m_instance_menu ;
}
wxMenu * MenuFactory : : layer_menu ( )
{
MenuWithSeparators * menu = new MenuWithSeparators ( ) ;
append_menu_item_settings ( menu ) ;
return menu ;
}
wxMenu * MenuFactory : : multi_selection_menu ( )
{
//QDS
wxDataViewItemArray sels ;
obj_list ( ) - > GetSelections ( sels ) ;
bool multi_volume = true ;
2025-07-10 09:14:38 +08:00
int count = 0 ;
int obj_idx = - 1 ;
2024-09-03 09:34:33 +08:00
for ( const wxDataViewItem & item : sels ) {
multi_volume = list_model ( ) - > GetItemType ( item ) & itVolume ;
if ( ! ( list_model ( ) - > GetItemType ( item ) & ( itVolume | itObject | itInstance ) ) )
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
return nullptr ;
2025-07-10 09:14:38 +08:00
if ( multi_volume ) {
auto temp_obj_idx = obj_list ( ) - > GetModel ( ) - > GetObjectIdByItem ( item ) ;
if ( temp_obj_idx > = 0 & & temp_obj_idx ! = obj_idx ) {
if ( obj_idx = = - 1 ) {
obj_idx = temp_obj_idx ;
}
else {
multi_volume = false ;
break ;
}
}
}
count + + ;
2024-09-03 09:34:33 +08:00
}
wxMenu * menu = new MenuWithSeparators ( ) ;
if ( ! multi_volume ) {
int index = 0 ;
if ( obj_list ( ) - > can_merge_to_multipart_object ( ) ) {
append_menu_item_merge_to_multipart_object ( menu ) ;
index + + ;
}
append_menu_item_center ( menu ) ;
append_menu_item_fix_through_netfabb ( menu ) ;
//append_menu_item_simplify(menu);
append_menu_item_delete ( menu ) ;
menu - > AppendSeparator ( ) ;
append_menu_item_set_printable ( menu ) ;
append_menu_item_per_object_process ( menu ) ;
menu - > AppendSeparator ( ) ;
append_menu_items_convert_unit ( menu ) ;
//QDS
append_menu_item_change_filament ( menu ) ;
menu - > AppendSeparator ( ) ;
append_menu_item_export_stl ( menu , true ) ;
}
else {
append_menu_item_center ( menu ) ;
2025-07-10 09:14:38 +08:00
auto mo = ( * obj_list ( ) - > objects ( ) ) [ obj_idx ] ;
if ( count < mo - > volumes . size ( ) ) {
append_menu_item_sub_merge ( menu ) ;
}
2024-09-03 09:34:33 +08:00
append_menu_item_fix_through_netfabb ( menu ) ;
//append_menu_item_simplify(menu);
append_menu_item_delete ( menu ) ;
append_menu_items_convert_unit ( menu ) ;
append_menu_item_change_filament ( menu ) ;
wxMenu * split_menu = new wxMenu ( ) ;
if ( split_menu ) {
append_menu_item ( split_menu , wxID_ANY , _L ( " To objects " ) , _L ( " Split the selected object into multiple objects " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_object ( ) ; } , " split_objects " , menu ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
append_menu_item ( split_menu , wxID_ANY , _L ( " To parts " ) , _L ( " Split the selected object into multiple parts " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > split_volume ( ) ; } , " split_parts " , menu ,
[ ] ( ) { return plater ( ) - > can_split ( false ) ; } , m_parent ) ;
append_submenu ( menu , split_menu , wxID_ANY , _L ( " Split " ) , _L ( " Split the selected object " ) , " " ,
[ ] ( ) { return plater ( ) - > can_split ( true ) ; } , m_parent ) ;
}
2025-07-10 09:14:38 +08:00
append_menu_item_per_object_process ( menu ) ;
2024-09-03 09:34:33 +08:00
}
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " multi_selection_menu " , std : : to_string ( + + multi_selection_menu_count ) ) ;
}
return menu ;
}
wxMenu * MenuFactory : : assemble_multi_selection_menu ( )
{
wxDataViewItemArray sels ;
obj_list ( ) - > GetSelections ( sels ) ;
for ( const wxDataViewItem & item : sels )
if ( ! ( list_model ( ) - > GetItemType ( item ) & ( itVolume | itObject | itInstance ) ) )
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
return nullptr ;
wxMenu * menu = new MenuWithSeparators ( ) ;
append_menu_item_set_visible ( menu ) ;
//append_menu_item_fix_through_netfabb(menu);
//append_menu_item_simplify(menu);
append_menu_item_delete ( menu ) ;
menu - > AppendSeparator ( ) ;
append_menu_item_change_extruder ( menu ) ;
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " asseble_multi_selection_menu " , std : : to_string ( + + assemble_multi_selection_menu_count ) ) ;
}
return menu ;
}
2025-05-08 15:05:30 +08:00
wxMenu * MenuFactory : : filament_action_menu ( int active_filament_menu_id ) {
create_filament_action_menu ( false , active_filament_menu_id ) ;
return & m_filament_action_menu ;
}
2024-09-03 09:34:33 +08:00
//QDS: add partplate related logic
wxMenu * MenuFactory : : plate_menu ( )
{
append_menu_item_locked ( & m_plate_menu ) ;
append_menu_item_plate_name ( & m_plate_menu ) ;
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " plate_menu " , std : : to_string ( + + plate_menu_count ) ) ;
}
return & m_plate_menu ;
}
wxMenu * MenuFactory : : assemble_object_menu ( )
{
wxMenu * menu = new MenuWithSeparators ( ) ;
// Set Visible
append_menu_item_set_visible ( menu ) ;
// Delete
append_menu_item_delete ( menu ) ;
//// Object Repair
//append_menu_item_fix_through_netfabb(menu);
//// Object Simplify
//append_menu_item_simplify(menu);
menu - > AppendSeparator ( ) ;
// Set filament
append_menu_item_change_extruder ( menu ) ;
//// Enter per object parameters
//append_menu_item_per_object_settings(menu);
{
NetworkAgent * agent = GUI : : wxGetApp ( ) . getAgent ( ) ;
if ( agent ) agent - > track_update_property ( " assemble_object_menu " , std : : to_string ( + + assemble_object_menu_ocunt ) ) ;
}
return menu ;
}
wxMenu * MenuFactory : : assemble_part_menu ( )
{
wxMenu * menu = new MenuWithSeparators ( ) ;
append_menu_item_set_visible ( menu ) ;
append_menu_item_delete ( menu ) ;
//append_menu_item_simplify(menu);
menu - > AppendSeparator ( ) ;
append_menu_item_change_extruder ( menu ) ;
//append_menu_item_per_object_settings(menu);
return menu ;
}
void MenuFactory : : append_menu_item_clone ( wxMenu * menu )
{
# ifdef __APPLE__
static const wxString ctrl = ( " Ctrl+ " ) ;
# else
static const wxString ctrl = _L ( " Ctrl+ " ) ;
# endif
append_menu_item ( menu , wxID_ANY , _L ( " Clone " ) + " \t " + ctrl + " K " , " " ,
[ this ] ( wxCommandEvent & ) {
plater ( ) - > clone_selection ( ) ;
} , " " , nullptr ,
[ ] ( ) {
return true ;
} , m_parent ) ;
}
void MenuFactory : : append_menu_item_simplify ( wxMenu * menu )
{
wxMenuItem * menu_item = append_menu_item ( menu , wxID_ANY , _L ( " Simplify Model " ) , " " ,
[ ] ( wxCommandEvent & ) { obj_list ( ) - > simplify ( ) ; } , " " , menu ,
[ ] ( ) { return plater ( ) - > can_simplify ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_center ( wxMenu * menu )
{
append_menu_item ( menu , wxID_ANY , _L ( " Center " ) , " " ,
[ this ] ( wxCommandEvent & ) {
2024-11-28 15:19:12 +08:00
auto canvas3d = plater ( ) - > get_view3D_canvas3D ( ) ;
canvas3d - > get_gizmos_manager ( ) . check_object_located_outside_plate ( true ) ;
2024-09-03 09:34:33 +08:00
plater ( ) - > center_selection ( ) ;
} , " " , nullptr ,
[ ] ( ) {
if ( plater ( ) - > canvas3D ( ) - > get_canvas_type ( ) ! = GLCanvas3D : : ECanvasType : : CanvasView3D )
return false ;
else {
2024-11-28 15:19:12 +08:00
auto canvas3d = plater ( ) - > get_view3D_canvas3D ( ) ;
canvas3d - > get_gizmos_manager ( ) . check_object_located_outside_plate ( false ) ;
if ( canvas3d - > get_gizmos_manager ( ) . get_object_located_outside_plate ( ) ) { //_outside_plate
return false ;
}
Selection & selection = canvas3d - > get_selection ( ) ;
2024-09-03 09:34:33 +08:00
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
Vec3d model_pos = selection . get_bounding_box ( ) . center ( ) ;
Vec3d center_pos = plate - > get_center_origin ( ) ;
return ! ( ( model_pos . x ( ) = = center_pos . x ( ) ) & & ( model_pos . y ( ) = = center_pos . y ( ) ) ) ;
} //disable if model is at center / not in View3D
} , m_parent ) ;
}
2025-07-10 09:14:38 +08:00
void MenuFactory : : append_menu_item_sub_merge ( wxMenu * menu )
{
append_menu_item (
menu , wxID_ANY , _L ( " Sub merge " ) , " " ,
[ this ] ( wxCommandEvent & ) {
obj_list ( ) - > add_new_model_object_from_old_object ( ) ;
} ,
" " , nullptr ,
[ ] ( ) {
if ( plater ( ) - > canvas3D ( ) - > get_canvas_type ( ) ! = GLCanvas3D : : ECanvasType : : CanvasView3D )
return false ;
else {
if ( obj_list ( ) - > has_selected_cut_object ( ) )
return false ;
return true ;
}
} ,
m_parent ) ;
}
2024-09-03 09:34:33 +08:00
void MenuFactory : : append_menu_item_per_object_process ( wxMenu * menu )
{
const std : : vector < wxString > names = { _L ( " Edit Process Settings " ) , _L ( " Edit Process Settings " ) } ;
append_menu_item ( menu , wxID_ANY , names [ 0 ] , names [ 1 ] ,
[ ] ( wxCommandEvent & ) {
wxGetApp ( ) . obj_list ( ) - > switch_to_object_process ( ) ;
} , " " , nullptr ,
[ ] ( ) {
Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
return selection . is_single_full_object ( ) | |
selection . is_multiple_full_object ( ) | |
selection . is_single_full_instance ( ) | |
selection . is_multiple_full_instance ( ) | |
selection . is_single_volume ( ) | |
selection . is_multiple_volume ( ) ;
} , m_parent ) ;
2025-07-10 09:14:38 +08:00
const std : : vector < wxString > names2 = { _L ( " Copy Process Settings " ) , _L ( " Copy Process Settings " ) } ;
append_menu_item (
menu , wxID_ANY , names2 [ 0 ] , names2 [ 1 ] , [ ] ( wxCommandEvent & ) {
wxGetApp ( ) . obj_list ( ) - > copy_settings_to_clipboard ( ) ;
} , " " , nullptr ,
[ ] ( ) {
Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
return selection . is_single_full_object ( ) | | selection . is_single_full_instance ( ) | |
selection . is_single_volume_or_modifier ( ) ;
} ,
m_parent ) ;
const std : : vector < wxString > names3 = { _L ( " Paste Process Settings " ) , _L ( " Paste Process Settings " ) } ;
append_menu_item (
menu , wxID_ANY , names3 [ 0 ] , names3 [ 1 ] , [ ] ( wxCommandEvent & ) {
wxGetApp ( ) . obj_list ( ) - > paste_settings_into_list ( ) ;
} , " " , nullptr ,
[ ] ( ) {
return wxGetApp ( ) . obj_list ( ) - > can_paste_settings_into_list ( ) ;
} ,
m_parent ) ;
2024-09-03 09:34:33 +08:00
}
void MenuFactory : : append_menu_item_per_object_settings ( wxMenu * menu )
{
const std : : vector < wxString > names = { _L ( " Edit in Parameter Table " ) , _L ( " Edit print parameters for a single object " ) } ;
// Delete old menu item
for ( const wxString & name : names ) {
const int item_id = menu - > FindItem ( name ) ;
if ( item_id ! = wxNOT_FOUND )
menu - > Destroy ( item_id ) ;
}
append_menu_item ( menu , wxID_ANY , names [ 0 ] , names [ 1 ] ,
[ ] ( wxCommandEvent & ) {
plater ( ) - > PopupObjectTableBySelection ( ) ;
} , " " , nullptr ,
[ ] ( ) {
Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
return selection . is_single_full_object ( ) | | selection . is_single_full_instance ( ) | | selection . is_single_volume ( ) ;
} , m_parent ) ;
}
void MenuFactory : : append_menu_item_change_filament ( wxMenu * menu )
{
const std : : vector < wxString > names = { _L ( " Change Filament " ) , _L ( " Set Filament for selected items " ) } ;
// Delete old menu item
for ( const wxString & name : names ) {
const int item_id = menu - > FindItem ( name ) ;
if ( item_id ! = wxNOT_FOUND )
menu - > Destroy ( item_id ) ;
}
int filaments_cnt = filaments_count ( ) ;
if ( filaments_cnt < = 1 )
return ;
wxDataViewItemArray sels ;
obj_list ( ) - > GetSelections ( sels ) ;
if ( sels . IsEmpty ( ) )
return ;
if ( sels . Count ( ) = = 1 ) {
const auto sel_vol = obj_list ( ) - > get_selected_model_volume ( ) ;
if ( sel_vol & & sel_vol - > type ( ) ! = ModelVolumeType : : MODEL_PART & & sel_vol - > type ( ) ! = ModelVolumeType : : PARAMETER_MODIFIER )
return ;
}
std : : vector < wxBitmap * > icons = get_extruder_color_icons ( true ) ;
if ( icons . size ( ) < filaments_cnt ) {
BOOST_LOG_TRIVIAL ( warning ) < < boost : : format ( " Warning: icons size %1%, filaments_cnt=%2% " ) % icons . size ( ) % filaments_cnt ;
if ( icons . size ( ) < = 1 )
return ;
else
filaments_cnt = icons . size ( ) ;
}
wxMenu * extruder_selection_menu = new wxMenu ( ) ;
const wxString & name = sels . Count ( ) = = 1 ? names [ 0 ] : names [ 1 ] ;
int initial_extruder = - 1 ; // negative value for multiple object/part selection
if ( sels . Count ( ) = = 1 ) {
const ModelConfig & config = obj_list ( ) - > get_item_config ( sels [ 0 ] ) ;
// QDS
const auto sel_vol = obj_list ( ) - > get_selected_model_volume ( ) ;
if ( sel_vol & & sel_vol - > type ( ) = = ModelVolumeType : : PARAMETER_MODIFIER )
initial_extruder = config . has ( " extruder " ) ? config . extruder ( ) : 0 ;
else
initial_extruder = config . has ( " extruder " ) ? config . extruder ( ) : 1 ;
}
// QDS
bool has_modifier = false ;
for ( auto sel : sels ) {
if ( obj_list ( ) - > GetModel ( ) - > GetVolumeType ( sel ) = = ModelVolumeType : : PARAMETER_MODIFIER ) {
has_modifier = true ;
break ;
}
}
for ( int i = has_modifier ? 0 : 1 ; i < = filaments_cnt ; i + + )
{
// QDS
//bool is_active_extruder = i == initial_extruder;
bool is_active_extruder = false ;
wxString item_name = _L ( " Default " ) ;
if ( i > 0 ) {
auto preset = wxGetApp ( ) . preset_bundle - > filaments . find_preset ( wxGetApp ( ) . preset_bundle - > filament_presets [ i - 1 ] ) ;
if ( preset = = nullptr ) {
item_name = wxString : : Format ( _L ( " Filament %d " ) , i ) ;
} else {
item_name = from_u8 ( preset - > label ( false ) ) ;
}
}
if ( is_active_extruder ) {
item_name < < " ( " + _L ( " current " ) + " ) " ;
}
append_menu_item ( extruder_selection_menu , wxID_ANY , item_name , " " ,
[ i ] ( wxCommandEvent & ) { obj_list ( ) - > set_extruder_for_selected_items ( i ) ; } , i = = 0 ? wxNullBitmap : * icons [ i - 1 ] , menu ,
[ is_active_extruder ] ( ) { return ! is_active_extruder ; } , m_parent ) ;
}
menu - > Append ( wxID_ANY , name , extruder_selection_menu , _L ( " Change Filament " ) ) ;
}
void MenuFactory : : append_menu_item_set_printable ( wxMenu * menu )
{
const Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
bool all_printable = true ;
ObjectList * list = obj_list ( ) ;
wxDataViewItemArray sels ;
list - > GetSelections ( sels ) ;
for ( wxDataViewItem item : sels ) {
ItemType type = list - > GetModel ( ) - > GetItemType ( item ) ;
if ( type ! = itInstance & & type ! = itObject )
continue ;
else {
int obj_idx = list - > GetModel ( ) - > GetObjectIdByItem ( item ) ;
int inst_idx = type = = itObject ? 0 : list - > GetModel ( ) - > GetInstanceIdByItem ( item ) ;
all_printable & = list - > object ( obj_idx ) - > instances [ inst_idx ] - > printable ;
}
}
wxString menu_text = _L ( " Printable " ) ;
wxMenuItem * menu_item_set_printable = append_menu_check_item ( menu , wxID_ANY , menu_text , " " , [ this , all_printable ] ( wxCommandEvent & ) {
Selection & selection = plater ( ) - > canvas3D ( ) - > get_selection ( ) ;
selection . set_printable ( ! all_printable ) ;
} , menu ) ;
m_parent - > Bind ( wxEVT_UPDATE_UI , [ all_printable ] ( wxUpdateUIEvent & evt ) {
evt . Check ( all_printable ) ;
plater ( ) - > set_current_canvas_as_dirty ( ) ;
} , menu_item_set_printable - > GetId ( ) ) ;
}
void MenuFactory : : append_menu_item_locked ( wxMenu * menu )
{
const std : : vector < wxString > names = { _L ( " Unlock " ) , _L ( " Lock " ) } ;
// Delete old menu item
for ( const wxString & name : names ) {
const int item_id = menu - > FindItem ( name ) ;
if ( item_id ! = wxNOT_FOUND )
menu - > Destroy ( item_id ) ;
}
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
wxString lock_text = plate - > is_locked ( ) ? names [ 0 ] : names [ 1 ] ;
auto item = append_menu_item ( menu , wxID_ANY , lock_text , " " ,
[ plate ] ( wxCommandEvent & ) {
bool lock = plate - > is_locked ( ) ;
plate - > lock ( ! lock ) ;
} , " " , nullptr , [ ] ( ) { return true ; } , m_parent ) ;
m_parent - > Bind ( wxEVT_UPDATE_UI , [ ] ( wxUpdateUIEvent & evt ) {
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
//bool check = plate->is_locked();
//evt.Check(check);
plater ( ) - > set_current_canvas_as_dirty ( ) ;
} , item - > GetId ( ) ) ;
}
void MenuFactory : : append_menu_item_fill_bed ( wxMenu * menu )
{
append_menu_item (
menu , wxID_ANY , _L ( " Fill bed with copies " ) , _L ( " Fill the remaining area of bed with copies of the selected object " ) ,
[ ] ( wxCommandEvent & ) { plater ( ) - > fill_bed_with_instances ( ) ; } , " " , nullptr , [ ] ( ) { return plater ( ) - > can_increase_instances ( ) ; } , m_parent ) ;
}
void MenuFactory : : append_menu_item_plate_name ( wxMenu * menu )
{
wxString name = _L ( " Edit Plate Name " ) ;
// Delete old menu item
const int item_id = menu - > FindItem ( name ) ;
if ( item_id ! = wxNOT_FOUND ) menu - > Destroy ( item_id ) ;
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
auto item = append_menu_item (
menu , wxID_ANY , name , " " ,
[ plate ] ( wxCommandEvent & e ) {
int hover_idx = plater ( ) - > canvas3D ( ) - > GetHoverId ( ) ;
if ( hover_idx = = - 1 ) {
int plate_idx = plater ( ) - > GetPlateIndexByRightMenuInLeftUI ( ) ;
plater ( ) - > select_plate_by_hover_id ( plate_idx * PartPlate : : GRABBER_COUNT , false , true ) ;
}
else
{
plater ( ) - > select_plate_by_hover_id ( hover_idx , false , true ) ;
}
plater ( ) - > get_current_canvas3D ( ) - > post_event ( SimpleEvent ( EVT_GLCANVAS_PLATE_NAME_CHANGE ) ) ;
} ,
" " , nullptr , [ ] ( ) { return true ; } , m_parent ) ;
m_parent - > Bind (
wxEVT_UPDATE_UI ,
[ ] ( wxUpdateUIEvent & evt ) {
PartPlate * plate = plater ( ) - > get_partplate_list ( ) . get_selected_plate ( ) ;
assert ( plate ) ;
plater ( ) - > set_current_canvas_as_dirty ( ) ;
} ,
item - > GetId ( ) ) ;
}
void MenuFactory : : update_object_menu ( )
{
append_menu_items_add_volume ( & m_object_menu ) ;
}
void MenuFactory : : update_default_menu ( )
{
for ( auto & name : { _L ( " Add Primitive " ) , _L ( " Show Labels " ) } ) {
const auto menu_item_id = m_default_menu . FindItem ( name ) ;
if ( menu_item_id ! = wxNOT_FOUND )
m_default_menu . Destroy ( menu_item_id ) ;
}
create_default_menu ( ) ;
}
void MenuFactory : : msw_rescale ( )
{
for ( MenuWithSeparators * menu : { & m_object_menu , & m_sla_object_menu , & m_part_menu , & m_default_menu } )
msw_rescale_menu ( dynamic_cast < wxMenu * > ( menu ) ) ;
}
# ifdef _WIN32
// For this class is used code from stackoverflow:
// https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence
// Using this code we can to inspect of an existence of IsWheelInverted() function in class T
template < typename T >
class menu_has_update_def_colors
{
typedef char one ;
struct two { char x [ 2 ] ; } ;
template < typename C > static one test ( decltype ( & C : : UpdateDefColors ) ) ;
template < typename C > static two test ( . . . ) ;
public :
static constexpr bool value = sizeof ( test < T > ( 0 ) ) = = sizeof ( char ) ;
} ;
template < typename T >
static void update_menu_item_def_colors ( T * item )
{
if constexpr ( menu_has_update_def_colors < wxMenuItem > : : value ) {
item - > UpdateDefColors ( ) ;
}
}
# endif
void MenuFactory : : sys_color_changed ( )
{
for ( MenuWithSeparators * menu : { & m_object_menu , & m_sla_object_menu , & m_part_menu , & m_default_menu } ) {
msw_rescale_menu ( dynamic_cast < wxMenu * > ( menu ) ) ; // msw_rescale_menu updates just icons, so use it
# ifdef _WIN32
// but under MSW we have to update item's bachground color
for ( wxMenuItem * item : menu - > GetMenuItems ( ) )
update_menu_item_def_colors ( item ) ;
# endif
}
}
void MenuFactory : : sys_color_changed ( wxMenuBar * menubar )
{
// QDS: fix
#if 0
for ( size_t id = 0 ; id < menubar - > GetMenuCount ( ) ; id + + ) {
wxMenu * menu = menubar - > GetMenu ( id ) ;
msw_rescale_menu ( menu ) ;
# ifdef _WIN32
// but under MSW we have to update item's bachground color
for ( wxMenuItem * item : menu - > GetMenuItems ( ) )
update_menu_item_def_colors ( item ) ;
# endif
}
menubar - > Refresh ( ) ;
# endif
}
} //namespace GUI
} //namespace Slic3r