2020-07-30 16:16:56 +02:00
#include "UnsavedChangesDialog.hpp"
#include <cstddef>
#include <string>
2020-08-07 15:09:58 +02:00
#include <vector>
2020-07-30 16:16:56 +02:00
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
2020-12-05 00:02:23 +01:00
#include <boost/nowide/convert.hpp>
2020-07-30 16:16:56 +02:00
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
2020-10-17 14:04:05 +02:00
#include "format.hpp"
2020-07-30 16:16:56 +02:00
#include "GUI_App.hpp"
#include "Plater.hpp"
#include "Tab.hpp"
2020-08-10 19:07:45 +02:00
#include "ExtraRenderers.hpp"
#include "wxExtensions.hpp"
2020-10-19 14:17:45 +02:00
#include "SavePresetDialog.hpp"
2020-10-15 16:48:48 +02:00
#include "MainFrame.hpp"
2020-07-30 16:16:56 +02:00
2020-08-07 15:09:58 +02:00
//#define FTS_FUZZY_MATCH_IMPLEMENTATION
//#include "fts_fuzzy_match.h"
2020-07-30 16:16:56 +02:00
2020-08-05 19:25:04 +02:00
#include "BitmapCache.hpp"
2020-07-30 16:16:56 +02:00
using boost :: optional ;
2020-12-04 17:15:38 +01:00
#ifdef __linux__
2020-11-30 13:48:22 +01:00
#define wxLinux true
#else
#define wxLinux false
#endif
2020-07-30 16:16:56 +02:00
namespace Slic3r {
namespace GUI {
// ----------------------------------------------------------------------------
// ModelNode: a node inside UnsavedChangesModel
// ----------------------------------------------------------------------------
2020-08-05 19:25:04 +02:00
static const std :: map < Preset :: Type , std :: string > type_icon_names = {
{ Preset :: TYPE_PRINT , "cog" },
{ Preset :: TYPE_SLA_PRINT , "cog" },
{ Preset :: TYPE_FILAMENT , "spool" },
{ Preset :: TYPE_SLA_MATERIAL , "resin" },
2020-08-06 15:54:12 +02:00
{ Preset :: TYPE_PRINTER , "printer" },
2020-08-05 19:25:04 +02:00
};
2020-10-07 20:13:12 +02:00
static std :: string get_icon_name ( Preset :: Type type , PrinterTechnology pt ) {
return pt == ptSLA && type == Preset :: TYPE_PRINTER ? "sla_printer" : type_icon_names . at ( type );
}
2020-12-15 14:58:34 +01:00
static std :: string def_text_color ()
{
wxColour def_colour = wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOWTEXT );
auto clr_str = wxString :: Format ( wxT ( "#%02X%02X%02X" ), def_colour . Red (), def_colour . Green (), def_colour . Blue ());
return clr_str . ToStdString ();
}
2020-08-05 19:25:04 +02:00
static std :: string grey = "#808080" ;
static std :: string orange = "#ed6b21" ;
static void color_string ( wxString & str , const std :: string & color )
{
2020-08-10 11:24:31 +02:00
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
2020-08-05 19:25:04 +02:00
str = from_u8 (( boost :: format ( "<span color= \" %1% \" >%2%</span>" ) % color % into_u8 ( str )). str ());
#endif
}
static void make_string_bold ( wxString & str )
{
2020-08-10 11:24:31 +02:00
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
2020-08-05 19:25:04 +02:00
str = from_u8 (( boost :: format ( "<b>%1%</b>" ) % into_u8 ( str )). str ());
#endif
}
2020-07-30 16:16:56 +02:00
// preset(root) node
2020-10-07 20:13:12 +02:00
ModelNode :: ModelNode ( Preset :: Type preset_type , wxWindow * parent_win , const wxString & text , const std :: string & icon_name ) :
2020-08-12 13:36:26 +02:00
m_parent_win ( parent_win ),
2020-07-30 16:16:56 +02:00
m_parent ( nullptr ),
m_preset_type ( preset_type ),
2020-10-07 20:13:12 +02:00
m_icon_name ( icon_name ),
2020-07-30 16:16:56 +02:00
m_text ( text )
{
2020-08-12 13:36:26 +02:00
UpdateIcons ();
2020-07-30 16:16:56 +02:00
}
2020-10-07 20:13:12 +02:00
// category node
2020-07-30 16:16:56 +02:00
ModelNode :: ModelNode ( ModelNode * parent , const wxString & text , const std :: string & icon_name ) :
2020-08-12 13:36:26 +02:00
m_parent_win ( parent -> m_parent_win ),
2020-07-30 16:16:56 +02:00
m_parent ( parent ),
2020-08-12 13:36:26 +02:00
m_icon_name ( icon_name ),
2020-07-30 16:16:56 +02:00
m_text ( text )
{
2020-08-12 13:36:26 +02:00
UpdateIcons ();
2020-07-30 16:16:56 +02:00
}
2020-10-07 20:13:12 +02:00
// group node
2020-08-05 19:25:04 +02:00
ModelNode :: ModelNode ( ModelNode * parent , const wxString & text ) :
2020-08-12 13:36:26 +02:00
m_parent_win ( parent -> m_parent_win ),
2020-07-30 16:16:56 +02:00
m_parent ( parent ),
2020-10-07 20:13:12 +02:00
m_text ( text ),
m_icon_name ( "dot_small" )
2020-07-30 16:16:56 +02:00
{
2020-10-07 20:13:12 +02:00
UpdateIcons ();
2020-07-30 16:16:56 +02:00
}
2020-08-10 11:24:31 +02:00
#ifdef __linux__
2020-08-10 11:41:19 +02:00
wxIcon ModelNode :: get_bitmap ( const wxString & color )
2020-08-10 11:24:31 +02:00
#else
2020-08-05 19:25:04 +02:00
wxBitmap ModelNode :: get_bitmap ( const wxString & color )
2020-08-10 11:24:31 +02:00
#endif // __linux__
2020-08-05 19:25:04 +02:00
{
/* It's supposed that standard size of an icon is 48px*16px for 100% scaled display.
* So set sizes for solid_colored icons used for filament preset
* and scale them in respect to em_unit value
*/
const double em = em_unit ( m_parent_win );
const int icon_width = lround ( 6.4 * em );
const int icon_height = lround ( 1.6 * em );
BitmapCache bmp_cache ;
unsigned char rgb [ 3 ];
BitmapCache :: parse_color ( into_u8 ( color ), rgb );
// there is no need to scale created solid bitmap
2020-08-10 19:07:45 +02:00
#ifndef __linux__
return bmp_cache . mksolid ( icon_width , icon_height , rgb , true );
2020-08-10 11:24:31 +02:00
#else
2020-08-10 19:07:45 +02:00
wxIcon icon ;
icon . CopyFromBitmap ( bmp_cache . mksolid ( icon_width , icon_height , rgb , true ));
return icon ;
2020-08-10 11:24:31 +02:00
#endif // __linux__
2020-08-05 19:25:04 +02:00
}
// option node
ModelNode :: ModelNode ( ModelNode * parent , const wxString & text , const wxString & old_value , const wxString & new_value ) :
m_parent ( parent ),
m_old_color ( old_value . StartsWith ( "#" ) ? old_value : "" ),
m_new_color ( new_value . StartsWith ( "#" ) ? new_value : "" ),
m_container ( false ),
m_text ( text ),
2020-10-07 20:13:12 +02:00
m_icon_name ( "empty" ),
2020-08-05 19:25:04 +02:00
m_old_value ( old_value ),
m_new_value ( new_value )
{
// check if old/new_value is color
if ( m_old_color . IsEmpty ()) {
if ( ! m_new_color . IsEmpty ())
m_old_value = _L ( "Undef" );
}
else {
m_old_color_bmp = get_bitmap ( m_old_color );
m_old_value . Clear ();
}
if ( m_new_color . IsEmpty ()) {
if ( ! m_old_color . IsEmpty ())
m_new_value = _L ( "Undef" );
}
else {
m_new_color_bmp = get_bitmap ( m_new_color );
m_new_value . Clear ();
}
// "color" strings
2020-12-15 14:58:34 +01:00
color_string ( m_old_value , def_text_color ());
2020-08-05 19:25:04 +02:00
color_string ( m_new_value , orange );
2020-10-07 20:13:12 +02:00
UpdateIcons ();
2020-08-05 19:25:04 +02:00
}
void ModelNode :: UpdateEnabling ()
{
auto change_text_color = []( wxString & str , const std :: string & clr_from , const std :: string & clr_to )
{
2020-08-12 13:36:26 +02:00
#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)
2020-08-05 19:25:04 +02:00
std :: string old_val = into_u8 ( str );
boost :: replace_all ( old_val , clr_from , clr_to );
str = from_u8 ( old_val );
2020-08-12 13:36:26 +02:00
#endif
2020-08-05 19:25:04 +02:00
};
if ( ! m_toggle ) {
2020-12-15 14:58:34 +01:00
change_text_color ( m_text , def_text_color (), grey );
change_text_color ( m_old_value , def_text_color (), grey );
2020-08-05 19:25:04 +02:00
change_text_color ( m_new_value , orange , grey );
}
else {
2020-12-15 14:58:34 +01:00
change_text_color ( m_text , grey , def_text_color ());
change_text_color ( m_old_value , grey , def_text_color ());
2020-08-05 19:25:04 +02:00
change_text_color ( m_new_value , grey , orange );
}
// update icons for the colors
2020-08-12 13:36:26 +02:00
UpdateIcons ();
}
void ModelNode :: UpdateIcons ()
{
// update icons for the colors, if any exists
2020-08-05 19:25:04 +02:00
if ( ! m_old_color . IsEmpty ())
2020-08-12 13:36:26 +02:00
m_old_color_bmp = get_bitmap ( m_toggle ? m_old_color : grey );
2020-08-05 19:25:04 +02:00
if ( ! m_new_color . IsEmpty ())
2020-08-12 13:36:26 +02:00
m_new_color_bmp = get_bitmap ( m_toggle ? m_new_color : grey );
// update main icon, if any exists
if ( m_icon_name . empty ())
return ;
#ifdef __linux__
m_icon . CopyFromBitmap ( create_scaled_bitmap ( m_icon_name , m_parent_win , 16 , ! m_toggle ));
#else
m_icon = create_scaled_bitmap ( m_icon_name , m_parent_win , 16 , ! m_toggle );
#endif //__linux__
2020-08-05 19:25:04 +02:00
}
2020-07-30 16:16:56 +02:00
// ----------------------------------------------------------------------------
// UnsavedChangesModel
// ----------------------------------------------------------------------------
2020-08-05 19:25:04 +02:00
UnsavedChangesModel :: UnsavedChangesModel ( wxWindow * parent ) :
m_parent_win ( parent )
2020-07-30 16:16:56 +02:00
{
}
UnsavedChangesModel ::~ UnsavedChangesModel ()
{
2020-08-05 19:25:04 +02:00
for ( ModelNode * preset_node : m_preset_nodes )
delete preset_node ;
}
2020-10-07 20:13:12 +02:00
wxDataViewItem UnsavedChangesModel :: AddPreset ( Preset :: Type type , wxString preset_name , PrinterTechnology pt )
2020-08-05 19:25:04 +02:00
{
// "color" strings
2020-12-15 14:58:34 +01:00
color_string ( preset_name , def_text_color ());
2020-08-05 19:25:04 +02:00
make_string_bold ( preset_name );
2020-10-07 20:13:12 +02:00
auto preset = new ModelNode ( type , m_parent_win , preset_name , get_icon_name ( type , pt ));
2020-08-05 19:25:04 +02:00
m_preset_nodes . emplace_back ( preset );
wxDataViewItem child (( void * ) preset );
wxDataViewItem parent ( nullptr );
ItemAdded ( parent , child );
return child ;
}
ModelNode * UnsavedChangesModel :: AddOption ( ModelNode * group_node , wxString option_name , wxString old_value , wxString new_value )
{
2020-10-16 12:20:51 +02:00
group_node -> Append ( std :: make_unique < ModelNode > ( group_node , option_name , old_value , new_value ));
ModelNode * option = group_node -> GetChildren (). back (). get ();
2020-08-07 15:09:58 +02:00
wxDataViewItem group_item = wxDataViewItem (( void * ) group_node );
ItemAdded ( group_item , wxDataViewItem (( void * ) option ));
2020-08-05 19:25:04 +02:00
2020-08-07 15:09:58 +02:00
m_ctrl -> Expand ( group_item );
2020-08-05 19:25:04 +02:00
return option ;
}
ModelNode * UnsavedChangesModel :: AddOptionWithGroup ( ModelNode * category_node , wxString group_name , wxString option_name , wxString old_value , wxString new_value )
{
2020-10-16 12:20:51 +02:00
category_node -> Append ( std :: make_unique < ModelNode > ( category_node , group_name ));
ModelNode * group_node = category_node -> GetChildren (). back (). get ();
2020-08-07 15:09:58 +02:00
ItemAdded ( wxDataViewItem (( void * ) category_node ), wxDataViewItem (( void * ) group_node ));
2020-08-05 19:25:04 +02:00
return AddOption ( group_node , option_name , old_value , new_value );
}
2020-10-07 20:13:12 +02:00
ModelNode * UnsavedChangesModel :: AddOptionWithGroupAndCategory ( ModelNode * preset_node , wxString category_name , wxString group_name ,
wxString option_name , wxString old_value , wxString new_value , const std :: string category_icon_name )
2020-08-05 19:25:04 +02:00
{
2020-10-16 12:20:51 +02:00
preset_node -> Append ( std :: make_unique < ModelNode > ( preset_node , category_name , category_icon_name ));
ModelNode * category_node = preset_node -> GetChildren (). back (). get ();
2020-08-05 19:25:04 +02:00
ItemAdded ( wxDataViewItem (( void * ) preset_node ), wxDataViewItem (( void * ) category_node ));
return AddOptionWithGroup ( category_node , group_name , option_name , old_value , new_value );
}
wxDataViewItem UnsavedChangesModel :: AddOption ( Preset :: Type type , wxString category_name , wxString group_name , wxString option_name ,
2020-10-07 20:13:12 +02:00
wxString old_value , wxString new_value , const std :: string category_icon_name )
2020-08-05 19:25:04 +02:00
{
// "color" strings
2020-12-15 14:58:34 +01:00
color_string ( category_name , def_text_color ());
color_string ( group_name , def_text_color ());
color_string ( option_name , def_text_color ());
2020-08-05 19:25:04 +02:00
// "make" strings bold
make_string_bold ( category_name );
make_string_bold ( group_name );
// add items
for ( ModelNode * preset : m_preset_nodes )
if ( preset -> type () == type )
{
2020-10-16 12:20:51 +02:00
for ( std :: unique_ptr < ModelNode > & category : preset -> GetChildren ())
2020-08-05 19:25:04 +02:00
if ( category -> text () == category_name )
{
2020-10-16 12:20:51 +02:00
for ( std :: unique_ptr < ModelNode > & group : category -> GetChildren ())
2020-08-05 19:25:04 +02:00
if ( group -> text () == group_name )
2020-10-16 12:20:51 +02:00
return wxDataViewItem (( void * ) AddOption ( group . get (), option_name , old_value , new_value ));
2020-08-05 19:25:04 +02:00
2020-10-16 12:20:51 +02:00
return wxDataViewItem (( void * ) AddOptionWithGroup ( category . get (), group_name , option_name , old_value , new_value ));
2020-08-05 19:25:04 +02:00
}
2020-10-07 20:13:12 +02:00
return wxDataViewItem (( void * ) AddOptionWithGroupAndCategory ( preset , category_name , group_name , option_name , old_value , new_value , category_icon_name ));
2020-08-05 19:25:04 +02:00
}
return wxDataViewItem ( nullptr );
}
static void update_children ( ModelNode * parent )
{
if ( parent -> IsContainer ()) {
bool toggle = parent -> IsToggled ();
2020-10-16 12:20:51 +02:00
for ( std :: unique_ptr < ModelNode > & child : parent -> GetChildren ()) {
2020-08-05 19:25:04 +02:00
child -> Toggle ( toggle );
child -> UpdateEnabling ();
2020-10-16 12:20:51 +02:00
update_children ( child . get ());
2020-08-05 19:25:04 +02:00
}
}
}
static void update_parents ( ModelNode * node )
{
ModelNode * parent = node -> GetParent ();
if ( parent ) {
bool toggle = false ;
2020-10-16 12:20:51 +02:00
for ( std :: unique_ptr < ModelNode > & child : parent -> GetChildren ()) {
2020-08-05 19:25:04 +02:00
if ( child -> IsToggled ()) {
toggle = true ;
break ;
}
}
parent -> Toggle ( toggle );
parent -> UpdateEnabling ();
update_parents ( parent );
}
}
void UnsavedChangesModel :: UpdateItemEnabling ( wxDataViewItem item )
{
assert ( item . IsOk ());
2020-11-26 13:45:30 +01:00
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
2020-08-05 19:25:04 +02:00
node -> UpdateEnabling ();
update_children ( node );
update_parents ( node );
2020-07-30 16:16:56 +02:00
}
2020-08-10 11:24:31 +02:00
bool UnsavedChangesModel :: IsEnabledItem ( const wxDataViewItem & item )
{
assert ( item . IsOk ());
2020-11-26 13:45:30 +01:00
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
2020-08-10 11:24:31 +02:00
return node -> IsToggled ();
}
2020-07-30 16:16:56 +02:00
void UnsavedChangesModel :: GetValue ( wxVariant & variant , const wxDataViewItem & item , unsigned int col ) const
{
2020-08-05 19:25:04 +02:00
assert ( item . IsOk ());
2020-07-30 16:16:56 +02:00
2020-11-26 13:45:30 +01:00
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
2020-07-30 16:16:56 +02:00
switch ( col )
{
case colToggle :
variant = node -> m_toggle ;
break ;
2020-08-10 09:45:32 +02:00
#ifdef __linux__
2020-08-10 11:24:31 +02:00
case colIconText :
2020-08-10 09:45:32 +02:00
variant << wxDataViewIconText ( node -> m_text , node -> m_icon );
2020-08-10 11:24:31 +02:00
break ;
case colOldValue :
variant << wxDataViewIconText ( node -> m_old_value , node -> m_old_color_bmp );
break ;
case colNewValue :
variant << wxDataViewIconText ( node -> m_new_value , node -> m_new_color_bmp );
break ;
2020-08-10 09:45:32 +02:00
#else
2020-08-10 11:24:31 +02:00
case colIconText :
2020-08-05 19:25:04 +02:00
variant << DataViewBitmapText ( node -> m_text , node -> m_icon );
2020-07-30 16:16:56 +02:00
break ;
case colOldValue :
2020-08-05 19:25:04 +02:00
variant << DataViewBitmapText ( node -> m_old_value , node -> m_old_color_bmp );
2020-07-30 16:16:56 +02:00
break ;
case colNewValue :
2020-08-05 19:25:04 +02:00
variant << DataViewBitmapText ( node -> m_new_value , node -> m_new_color_bmp );
2020-07-30 16:16:56 +02:00
break ;
2020-08-10 11:24:31 +02:00
#endif //__linux__
2020-07-30 16:16:56 +02:00
default :
wxLogError ( "UnsavedChangesModel::GetValue: wrong column %d" , col );
}
}
bool UnsavedChangesModel :: SetValue ( const wxVariant & variant , const wxDataViewItem & item , unsigned int col )
{
assert ( item . IsOk ());
2020-11-26 13:45:30 +01:00
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
2020-07-30 16:16:56 +02:00
switch ( col )
{
case colToggle :
node -> m_toggle = variant . GetBool ();
return true ;
2020-08-10 11:24:31 +02:00
#ifdef __linux__
case colIconText : {
wxDataViewIconText data ;
data << variant ;
node -> m_icon = data . GetIcon ();
node -> m_text = data . GetText ();
return true ; }
case colOldValue : {
wxDataViewIconText data ;
data << variant ;
node -> m_old_color_bmp = data . GetIcon ();
node -> m_old_value = data . GetText ();
return true ; }
case colNewValue : {
wxDataViewIconText data ;
data << variant ;
node -> m_new_color_bmp = data . GetIcon ();
node -> m_new_value = data . GetText ();
return true ; }
#else
case colIconText : {
DataViewBitmapText data ;
data << variant ;
node -> m_icon = data . GetBitmap ();
node -> m_text = data . GetText ();
return true ; }
2020-08-05 19:25:04 +02:00
case colOldValue : {
DataViewBitmapText data ;
data << variant ;
node -> m_old_color_bmp = data . GetBitmap ();
node -> m_old_value = data . GetText ();
return true ; }
case colNewValue : {
DataViewBitmapText data ;
data << variant ;
node -> m_new_color_bmp = data . GetBitmap ();
node -> m_new_value = data . GetText ();
return true ; }
2020-08-10 11:24:31 +02:00
#endif //__linux__
2020-07-30 16:16:56 +02:00
default :
wxLogError ( "UnsavedChangesModel::SetValue: wrong column" );
}
return false ;
}
bool UnsavedChangesModel :: IsEnabled ( const wxDataViewItem & item , unsigned int col ) const
{
assert ( item . IsOk ());
2020-08-05 19:25:04 +02:00
if ( col == colToggle )
return true ;
2020-07-30 16:16:56 +02:00
// disable unchecked nodes
2020-11-26 13:45:30 +01:00
return ( static_cast < ModelNode *> ( item . GetID ())) -> IsToggled ();
2020-07-30 16:16:56 +02:00
}
wxDataViewItem UnsavedChangesModel :: GetParent ( const wxDataViewItem & item ) const
{
// the invisible root node has no parent
if ( ! item . IsOk ())
return wxDataViewItem ( nullptr );
2020-11-26 13:45:30 +01:00
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
2020-07-30 16:16:56 +02:00
// "MyMusic" also has no parent
2020-08-05 19:25:04 +02:00
if ( node -> IsRoot ())
2020-07-30 16:16:56 +02:00
return wxDataViewItem ( nullptr );
return wxDataViewItem (( void * ) node -> GetParent ());
}
bool UnsavedChangesModel :: IsContainer ( const wxDataViewItem & item ) const
{
// the invisble root node can have children
if ( ! item . IsOk ())
return true ;
2020-11-26 13:45:30 +01:00
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
2020-07-30 16:16:56 +02:00
return node -> IsContainer ();
}
unsigned int UnsavedChangesModel :: GetChildren ( const wxDataViewItem & parent , wxDataViewItemArray & array ) const
{
ModelNode * node = ( ModelNode * ) parent . GetID ();
if ( ! node ) {
2020-08-05 19:25:04 +02:00
for ( auto preset_node : m_preset_nodes )
array . Add ( wxDataViewItem (( void * ) preset_node ));
return m_preset_nodes . size ();
2020-07-30 16:16:56 +02:00
}
2020-10-16 12:20:51 +02:00
for ( std :: unique_ptr < ModelNode > & child : node -> GetChildren ())
array . Add ( wxDataViewItem (( void * ) child . get ()));
2020-07-30 16:16:56 +02:00
2020-10-16 12:20:51 +02:00
return node -> GetChildCount ();
2020-07-30 16:16:56 +02:00
}
wxString UnsavedChangesModel :: GetColumnType ( unsigned int col ) const
{
2020-08-05 19:25:04 +02:00
switch ( col )
{
case colToggle :
2020-07-30 16:16:56 +02:00
return "bool" ;
2020-08-05 19:25:04 +02:00
case colIconText :
case colOldValue :
case colNewValue :
default :
return "DataViewBitmapText" ; //"string";
}
2020-07-30 16:16:56 +02:00
}
2020-08-12 13:36:26 +02:00
static void rescale_children ( ModelNode * parent )
{
if ( parent -> IsContainer ()) {
2020-10-16 12:20:51 +02:00
for ( std :: unique_ptr < ModelNode > & child : parent -> GetChildren ()) {
2020-08-12 13:36:26 +02:00
child -> UpdateIcons ();
2020-10-16 12:20:51 +02:00
rescale_children ( child . get ());
2020-08-12 13:36:26 +02:00
}
}
}
void UnsavedChangesModel :: Rescale ()
{
for ( ModelNode * node : m_preset_nodes ) {
node -> UpdateIcons ();
rescale_children ( node );
}
}
2020-07-30 16:16:56 +02:00
//------------------------------------------
// UnsavedChangesDialog
//------------------------------------------
2020-08-14 18:17:16 +02:00
UnsavedChangesDialog :: UnsavedChangesDialog ( const wxString & header )
2020-11-26 13:45:30 +01:00
: DPIDialog ( static_cast < wxWindow *> ( wxGetApp (). mainframe ), wxID_ANY , _L ( "PrusaSlicer is closing: Unsaved Changes" ), wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
2020-08-14 18:17:16 +02:00
{
2020-10-15 16:48:48 +02:00
m_app_config_key = "default_action_on_close_application" ;
2020-08-14 18:17:16 +02:00
build ( Preset :: TYPE_INVALID , nullptr , "" , header );
2020-10-15 16:48:48 +02:00
const std :: string & def_action = wxGetApp (). app_config -> get ( m_app_config_key );
if ( def_action == "none" )
this -> CenterOnScreen ();
else {
m_exit_action = def_action == ActSave ? Action :: Save : Action :: Discard ;
if ( m_exit_action == Action :: Save )
save ( nullptr );
}
2020-08-14 18:17:16 +02:00
}
2020-08-13 15:45:16 +02:00
UnsavedChangesDialog :: UnsavedChangesDialog ( Preset :: Type type , PresetCollection * dependent_presets , const std :: string & new_selected_preset )
2020-11-26 13:45:30 +01:00
: DPIDialog ( static_cast < wxWindow *> ( wxGetApp (). mainframe ), wxID_ANY , _L ( "Switching Presets: Unsaved Changes" ), wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
2020-08-14 18:17:16 +02:00
{
2020-10-15 16:48:48 +02:00
m_app_config_key = "default_action_on_select_preset" ;
2020-08-14 18:17:16 +02:00
build ( type , dependent_presets , new_selected_preset );
2020-10-15 16:48:48 +02:00
const std :: string & def_action = wxGetApp (). app_config -> get ( m_app_config_key );
if ( def_action == "none" ) {
2020-10-16 13:19:59 +02:00
if ( wxGetApp (). mainframe -> is_dlg_layout () && wxGetApp (). mainframe -> m_settings_dialog . HasFocus ())
2020-10-15 16:48:48 +02:00
this -> SetPosition ( wxGetApp (). mainframe -> m_settings_dialog . GetPosition ());
this -> CenterOnScreen ();
}
else {
m_exit_action = def_action == ActTransfer ? Action :: Transfer :
def_action == ActSave ? Action :: Save : Action :: Discard ;
const PresetCollection & printers = wxGetApp (). preset_bundle -> printers ;
if ( m_exit_action == Action :: Save ||
( m_exit_action == Action :: Transfer && dependent_presets && ( type == dependent_presets -> type () ?
dependent_presets -> get_edited_preset (). printer_technology () != dependent_presets -> find_preset ( new_selected_preset ) -> printer_technology () :
printers . get_edited_preset (). printer_technology () != printers . find_preset ( new_selected_preset ) -> printer_technology ())) )
save ( dependent_presets );
}
2020-08-14 18:17:16 +02:00
}
void UnsavedChangesDialog :: build ( Preset :: Type type , PresetCollection * dependent_presets , const std :: string & new_selected_preset , const wxString & header )
2020-07-30 16:16:56 +02:00
{
wxColour bgr_clr = wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW );
SetBackgroundColour ( bgr_clr );
2020-09-03 10:44:54 +02:00
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT && defined(__WXMSW__)
2020-08-12 13:36:26 +02:00
// ys_FIXME! temporary workaround for correct font scaling
// Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts,
// From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT
this -> SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ));
#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
2020-07-30 16:16:56 +02:00
int border = 10 ;
int em = em_unit ();
2020-08-13 15:45:16 +02:00
m_action_line = new wxStaticText ( this , wxID_ANY , "" );
m_action_line -> SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ). Bold ());
2020-10-15 16:48:48 +02:00
m_tree = new wxDataViewCtrl ( this , wxID_ANY , wxDefaultPosition , wxSize ( em * 60 , em * 30 ), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES );
2020-08-05 19:25:04 +02:00
m_tree_model = new UnsavedChangesModel ( this );
m_tree -> AssociateModel ( m_tree_model );
m_tree_model -> SetAssociatedControl ( m_tree );
2020-07-30 16:16:56 +02:00
2020-11-30 13:48:22 +01:00
m_tree -> AppendToggleColumn ( L " \u2714 " , UnsavedChangesModel :: colToggle , wxDATAVIEW_CELL_ACTIVATABLE , ( wxLinux ? 8 : 6 ) * em );
2020-08-10 09:45:32 +02:00
2020-08-11 09:17:52 +02:00
auto append_bmp_text_column = [ this ]( const wxString & label , unsigned model_column , int width , bool set_expander = false )
{
2020-08-10 09:45:32 +02:00
#ifdef __linux__
2020-08-11 09:17:52 +02:00
wxDataViewIconTextRenderer * rd = new wxDataViewIconTextRenderer ();
2020-08-10 09:45:32 +02:00
#ifdef SUPPORTS_MARKUP
2020-08-11 09:17:52 +02:00
rd -> EnableMarkup ( true );
2020-08-10 09:45:32 +02:00
#endif
2020-08-11 09:17:52 +02:00
wxDataViewColumn * column = new wxDataViewColumn ( label , rd , model_column , width , wxALIGN_TOP , wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT );
2020-08-10 09:45:32 +02:00
#else
2020-10-18 13:44:15 +02:00
wxDataViewColumn * column = new wxDataViewColumn ( label , new BitmapTextRenderer ( true , wxDATAVIEW_CELL_INERT ), model_column , width , wxALIGN_TOP , wxDATAVIEW_COL_RESIZABLE );
2020-08-10 09:45:32 +02:00
#endif //__linux__
2020-08-11 09:17:52 +02:00
m_tree -> AppendColumn ( column );
if ( set_expander )
m_tree -> SetExpanderColumn ( column );
};
2020-07-30 16:16:56 +02:00
2020-10-15 16:48:48 +02:00
append_bmp_text_column ( "" , UnsavedChangesModel :: colIconText , 28 * em );
append_bmp_text_column ( _L ( "Old Value" ), UnsavedChangesModel :: colOldValue , 12 * em );
append_bmp_text_column ( _L ( "New Value" ), UnsavedChangesModel :: colNewValue , 12 * em );
2020-08-07 15:09:58 +02:00
2020-08-05 19:25:04 +02:00
m_tree -> Bind ( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED , & UnsavedChangesDialog :: item_value_changed , this );
2020-08-10 19:07:45 +02:00
m_tree -> Bind ( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU , & UnsavedChangesDialog :: context_menu , this );
2020-07-30 16:16:56 +02:00
2020-08-11 09:17:52 +02:00
// Add Buttons
2020-10-15 16:48:48 +02:00
wxFont btn_font = this -> GetFont (). Scaled ( 1.4f );
wxBoxSizer * buttons = new wxBoxSizer ( wxHORIZONTAL );
2020-08-05 19:25:04 +02:00
2020-10-15 16:48:48 +02:00
auto add_btn = [ this , buttons , btn_font , dependent_presets ]( ScalableButton ** btn , int & btn_id , const std :: string & icon_name , Action close_act , const wxString & label , bool process_enable = true )
2020-08-13 15:45:16 +02:00
{
2020-10-15 16:48:48 +02:00
* btn = new ScalableButton ( this , btn_id = NewControlId (), icon_name , label , wxDefaultSize , wxDefaultPosition , wxBORDER_DEFAULT , true , 24 );
2020-08-05 19:25:04 +02:00
2020-10-15 16:48:48 +02:00
buttons -> Add ( * btn , 1 , wxLEFT , 5 );
( * btn ) -> SetFont ( btn_font );
( * btn ) -> Bind ( wxEVT_BUTTON , [ this , close_act , dependent_presets ]( wxEvent & ) {
update_config ( close_act );
2020-10-15 19:10:34 +02:00
if ( close_act == Action :: Save && ! save ( dependent_presets ))
return ;
2020-10-15 16:48:48 +02:00
close ( close_act );
});
2020-08-13 15:45:16 +02:00
if ( process_enable )
( * btn ) -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( ! m_empty_selection ); });
( * btn ) -> Bind ( wxEVT_LEAVE_WINDOW , [ this ]( wxMouseEvent & e ) { show_info_line ( Action :: Undef ); e . Skip (); });
};
2020-08-05 19:25:04 +02:00
2020-09-17 15:25:05 +02:00
const PresetCollection & printers = wxGetApp (). preset_bundle -> printers ;
2020-10-15 16:48:48 +02:00
if ( dependent_presets && ( type == dependent_presets -> type () ?
dependent_presets -> get_edited_preset (). printer_technology () == dependent_presets -> find_preset ( new_selected_preset ) -> printer_technology () :
printers . get_edited_preset (). printer_technology () == printers . find_preset ( new_selected_preset ) -> printer_technology ()))
add_btn ( & m_transfer_btn , m_move_btn_id , "paste_menu" , Action :: Transfer , _L ( "Transfer" ));
2020-10-16 13:19:59 +02:00
add_btn ( & m_discard_btn , m_continue_btn_id , dependent_presets ? "switch_presets" : "exit" , Action :: Discard , _L ( "Discard" ), false );
2020-10-15 16:48:48 +02:00
add_btn ( & m_save_btn , m_save_btn_id , "save" , Action :: Save , _L ( "Save" ));
ScalableButton * cancel_btn = new ScalableButton ( this , wxID_CANCEL , "cross" , _L ( "Cancel" ), wxDefaultSize , wxDefaultPosition , wxBORDER_DEFAULT , true , 24 );
buttons -> Add ( cancel_btn , 1 , wxLEFT | wxRIGHT , 5 );
cancel_btn -> SetFont ( btn_font );
cancel_btn -> Bind ( wxEVT_BUTTON , [ this ]( wxEvent & ) { this -> EndModal ( wxID_CANCEL ); });
2020-08-10 19:07:45 +02:00
m_info_line = new wxStaticText ( this , wxID_ANY , "" );
2020-08-12 13:36:26 +02:00
m_info_line -> SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ). Bold ());
m_info_line -> Hide ();
2020-07-30 16:16:56 +02:00
2020-10-15 16:48:48 +02:00
m_remember_choice = new wxCheckBox ( this , wxID_ANY , _L ( "Remember my choice" ));
m_remember_choice -> SetValue ( wxGetApp (). app_config -> get ( m_app_config_key ) != "none" );
2020-10-15 19:10:34 +02:00
m_remember_choice -> Bind ( wxEVT_CHECKBOX , [ type , this ]( wxCommandEvent & evt )
2020-10-15 16:48:48 +02:00
{
if ( ! evt . IsChecked ())
return ;
2020-10-17 14:04:05 +02:00
wxString preferences_item = type == Preset :: TYPE_INVALID ? _L ( "Ask for unsaved changes when closing application" ) :
_L ( "Ask for unsaved changes when selecting new preset" );
wxString msg =
_L ( "PrusaSlicer will remember your action." ) + " \n\n " +
( type == Preset :: TYPE_INVALID ?
_L ( "You will not be asked about the unsaved changes the next time you close PrusaSlicer." ) :
_L ( "You will not be asked about the unsaved changes the next time you switch a preset." )) + " \n\n " +
format_wxstr ( _L ( "Visit \" Preferences \" and check \" %1% \"\n to be asked about unsaved changes again." ), preferences_item );
2020-10-15 16:48:48 +02:00
2020-10-17 14:04:05 +02:00
wxMessageDialog dialog ( nullptr , msg , _L ( "PrusaSlicer: Don't ask me again" ), wxOK | wxCANCEL | wxICON_INFORMATION );
2020-10-15 19:10:34 +02:00
if ( dialog . ShowModal () == wxID_CANCEL )
m_remember_choice -> SetValue ( false );
2020-10-15 16:48:48 +02:00
});
2020-07-30 16:16:56 +02:00
wxBoxSizer * topSizer = new wxBoxSizer ( wxVERTICAL );
2020-08-13 15:45:16 +02:00
topSizer -> Add ( m_action_line , 0 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , border );
2020-08-11 09:17:52 +02:00
topSizer -> Add ( m_tree , 1 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , border );
topSizer -> Add ( m_info_line , 0 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , 2 * border );
2020-10-15 16:48:48 +02:00
topSizer -> Add ( buttons , 0 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , border );
topSizer -> Add ( m_remember_choice , 0 , wxEXPAND | wxALL , border );
2020-08-05 19:25:04 +02:00
2020-08-14 18:17:16 +02:00
update ( type , dependent_presets , new_selected_preset , header );
2020-07-30 16:16:56 +02:00
SetSizer ( topSizer );
topSizer -> SetSizeHints ( this );
2020-08-11 09:17:52 +02:00
show_info_line ( Action :: Undef );
2020-07-30 16:16:56 +02:00
}
2020-08-05 19:25:04 +02:00
void UnsavedChangesDialog :: item_value_changed ( wxDataViewEvent & event )
{
if ( event . GetColumn () != UnsavedChangesModel :: colToggle )
return ;
wxDataViewItem item = event . GetItem ();
m_tree_model -> UpdateItemEnabling ( item );
m_tree -> Refresh ();
2020-08-10 11:24:31 +02:00
// update an enabling of the "save/move" buttons
m_empty_selection = get_selected_options (). empty ();
2020-08-05 19:25:04 +02:00
}
2020-08-10 19:07:45 +02:00
void UnsavedChangesDialog :: context_menu ( wxDataViewEvent & event )
{
if ( ! m_has_long_strings )
return ;
wxDataViewItem item = event . GetItem ();
if ( ! item )
{
wxPoint mouse_pos = wxGetMousePosition () - m_tree -> GetScreenPosition ();
wxDataViewColumn * col = nullptr ;
m_tree -> HitTest ( mouse_pos , item , col );
if ( ! item )
item = m_tree -> GetSelection ();
if ( ! item )
return ;
}
auto it = m_items_map . find ( item );
if ( it == m_items_map . end () || ! it -> second . is_long )
return ;
FullCompareDialog ( it -> second . opt_name , it -> second . old_val , it -> second . new_val ). ShowModal ();
}
void UnsavedChangesDialog :: show_info_line ( Action action , std :: string preset_name )
{
if ( action == Action :: Undef && ! m_has_long_strings )
m_info_line -> Hide ();
else {
wxString text ;
if ( action == Action :: Undef )
2020-10-17 14:04:05 +02:00
text = _L ( "Some fields are too long to fit. Right mouse click reveals the full text." );
2020-10-15 16:48:48 +02:00
else if ( action == Action :: Discard )
2020-11-12 21:35:05 +01:00
text = _L ( "All settings changes will be discarded." );
2020-08-10 19:07:45 +02:00
else {
2020-09-02 09:06:42 +02:00
if ( preset_name . empty ())
2020-11-05 22:17:49 +01:00
text = action == Action :: Save ? _L ( "Save the selected options." ) : _L ( "Transfer the selected settings to the newly selected preset." );
2020-09-02 09:06:42 +02:00
else
2020-10-17 14:04:05 +02:00
text = format_wxstr (
action == Action :: Save ?
_L ( "Save the selected options to preset \" %1% \" ." ) :
_L ( "Transfer the selected options to the newly selected preset \" %1% \" ." ),
preset_name );
//text += "\n" + _L("Unselected options will be reverted.");
2020-08-10 19:07:45 +02:00
}
m_info_line -> SetLabel ( text );
m_info_line -> Show ();
}
Layout ();
Refresh ();
}
2020-10-15 16:48:48 +02:00
void UnsavedChangesDialog :: update_config ( Action action )
{
if ( ! m_remember_choice -> GetValue ())
return ;
std :: string act = action == Action :: Transfer ? ActTransfer :
action == Action :: Discard ? ActDiscard : ActSave ;
wxGetApp (). app_config -> set ( m_app_config_key , act );
}
2020-08-05 19:25:04 +02:00
void UnsavedChangesDialog :: close ( Action action )
{
2020-08-10 19:07:45 +02:00
m_exit_action = action ;
2020-08-05 19:25:04 +02:00
this -> EndModal ( wxID_CLOSE );
}
2020-10-15 19:10:34 +02:00
bool UnsavedChangesDialog :: save ( PresetCollection * dependent_presets )
2020-10-03 03:15:54 +02:00
{
names_and_types . clear ();
// save one preset
if ( dependent_presets ) {
const Preset & preset = dependent_presets -> get_edited_preset ();
std :: string name = preset . name ;
// for system/default/external presets we should take an edited name
if ( preset . is_system || preset . is_default || preset . is_external ) {
2020-11-05 20:50:49 +01:00
SavePresetDialog save_dlg ( this , preset . type );
2020-10-15 16:48:48 +02:00
if ( save_dlg . ShowModal () != wxID_OK ) {
m_exit_action = Action :: Discard ;
2020-10-15 19:10:34 +02:00
return false ;
2020-10-15 16:48:48 +02:00
}
2020-10-03 03:15:54 +02:00
name = save_dlg . get_name ();
}
names_and_types . emplace_back ( make_pair ( name , preset . type ));
}
// save all presets
else
{
std :: vector < Preset :: Type > types_for_save ;
PrinterTechnology printer_technology = wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology ();
for ( Tab * tab : wxGetApp (). tabs_list )
if ( tab -> supports_printer_technology ( printer_technology ) && tab -> current_preset_is_dirty ()) {
const Preset & preset = tab -> get_presets () -> get_edited_preset ();
if ( preset . is_system || preset . is_default || preset . is_external )
types_for_save . emplace_back ( preset . type );
names_and_types . emplace_back ( make_pair ( preset . name , preset . type ));
}
if ( ! types_for_save . empty ()) {
2020-11-05 20:50:49 +01:00
SavePresetDialog save_dlg ( this , types_for_save );
2020-10-15 16:48:48 +02:00
if ( save_dlg . ShowModal () != wxID_OK ) {
m_exit_action = Action :: Discard ;
2020-10-15 19:10:34 +02:00
return false ;
2020-10-15 16:48:48 +02:00
}
2020-10-03 03:15:54 +02:00
for ( std :: pair < std :: string , Preset :: Type >& nt : names_and_types ) {
const std :: string & name = save_dlg . get_name ( nt . second );
if ( ! name . empty ())
nt . first = name ;
}
}
}
2020-10-15 19:10:34 +02:00
return true ;
2020-10-03 03:15:54 +02:00
}
2020-08-05 19:25:04 +02:00
template < class T >
2020-08-07 15:09:58 +02:00
wxString get_string_from_enum ( const std :: string & opt_key , const DynamicPrintConfig & config , bool is_infill = false )
2020-08-05 19:25:04 +02:00
{
2020-08-07 15:09:58 +02:00
const ConfigOptionDef & def = config . def () -> options . at ( opt_key );
const std :: vector < std :: string >& names = def . enum_labels ; //ConfigOptionEnum<T>::get_enum_names();
2020-08-05 19:25:04 +02:00
T val = config . option < ConfigOptionEnum < T >> ( opt_key ) -> value ;
2020-08-07 15:09:58 +02:00
// Each infill doesn't use all list of infill declared in PrintConfig.hpp.
// So we should "convert" val to the correct one
if ( is_infill ) {
for ( auto key_val : * def . enum_keys_map )
if (( int ) key_val . second == ( int ) val ) {
auto it = std :: find ( def . enum_values . begin (), def . enum_values . end (), key_val . first );
if ( it == def . enum_values . end ())
return "" ;
return from_u8 ( _utf8 ( names [ it - def . enum_values . begin ()]));
}
return _L ( "Undef" );
}
return from_u8 ( _utf8 ( names [ static_cast < int > ( val )]));
2020-08-05 19:25:04 +02:00
}
2020-08-18 16:49:06 +02:00
static int get_id_from_opt_key ( std :: string opt_key )
2020-08-05 19:25:04 +02:00
{
2020-08-18 16:49:06 +02:00
int pos = opt_key . find ( "#" );
if ( pos > 0 ) {
boost :: erase_head ( opt_key , pos + 1 );
return atoi ( opt_key . c_str ());
}
return 0 ;
}
static std :: string get_pure_opt_key ( std :: string opt_key )
{
int pos = opt_key . find ( "#" );
if ( pos > 0 )
boost :: erase_tail ( opt_key , opt_key . size () - pos );
return opt_key ;
}
static wxString get_string_value ( std :: string opt_key , const DynamicPrintConfig & config )
{
2020-08-19 15:35:58 +02:00
int opt_idx = get_id_from_opt_key ( opt_key );
2020-08-18 16:49:06 +02:00
opt_key = get_pure_opt_key ( opt_key );
if ( config . option ( opt_key ) -> is_nil ())
return _L ( "N/A" );
2020-08-05 19:25:04 +02:00
wxString out ;
2020-08-18 16:49:06 +02:00
const ConfigOptionDef * opt = config . def () -> get ( opt_key );
bool is_nullable = opt -> nullable ;
2020-08-05 19:25:04 +02:00
2020-08-18 16:49:06 +02:00
switch ( opt -> type ) {
2020-08-05 19:25:04 +02:00
case coInt :
return from_u8 (( boost :: format ( "%1%" ) % config . opt_int ( opt_key )). str ());
case coInts : {
2020-08-18 16:49:06 +02:00
int val = is_nullable ?
config . opt < ConfigOptionIntsNullable > ( opt_key ) -> get_at ( opt_idx ) :
config . opt < ConfigOptionInts > ( opt_key ) -> get_at ( opt_idx );
return from_u8 (( boost :: format ( "%1%" ) % val ). str ());
2020-08-05 19:25:04 +02:00
}
case coBool :
return config . opt_bool ( opt_key ) ? "true" : "false" ;
case coBools : {
2020-08-18 16:49:06 +02:00
bool val = is_nullable ?
config . opt < ConfigOptionBoolsNullable > ( opt_key ) -> get_at ( opt_idx ) :
config . opt < ConfigOptionBools > ( opt_key ) -> get_at ( opt_idx );
return val ? "true" : "false" ;
2020-08-05 19:25:04 +02:00
}
case coPercent :
return from_u8 (( boost :: format ( "%1%%%" ) % int ( config . optptr ( opt_key ) -> getFloat ())). str ());
case coPercents : {
2020-08-18 16:49:06 +02:00
double val = is_nullable ?
config . opt < ConfigOptionPercentsNullable > ( opt_key ) -> get_at ( opt_idx ) :
config . opt < ConfigOptionPercents > ( opt_key ) -> get_at ( opt_idx );
return from_u8 (( boost :: format ( "%1%%%" ) % int ( val )). str ());
2020-08-05 19:25:04 +02:00
}
case coFloat :
return double_to_string ( config . opt_float ( opt_key ));
case coFloats : {
2020-08-18 16:49:06 +02:00
double val = is_nullable ?
config . opt < ConfigOptionFloatsNullable > ( opt_key ) -> get_at ( opt_idx ) :
config . opt < ConfigOptionFloats > ( opt_key ) -> get_at ( opt_idx );
return double_to_string ( val );
2020-08-05 19:25:04 +02:00
}
case coString :
return from_u8 ( config . opt_string ( opt_key ));
case coStrings : {
const ConfigOptionStrings * strings = config . opt < ConfigOptionStrings > ( opt_key );
if ( strings ) {
if ( opt_key == "compatible_printers" || opt_key == "compatible_prints" ) {
if ( strings -> empty ())
return _L ( "All" );
for ( size_t id = 0 ; id < strings -> size (); id ++ )
out += from_u8 ( strings -> get_at ( id )) + " \n " ;
out . RemoveLast ( 1 );
return out ;
}
if ( ! strings -> empty ())
return from_u8 ( strings -> get_at ( opt_idx ));
}
break ;
}
case coFloatOrPercent : {
const ConfigOptionFloatOrPercent * opt = config . opt < ConfigOptionFloatOrPercent > ( opt_key );
if ( opt )
out = double_to_string ( opt -> value ) + ( opt -> percent ? "%" : "" );
return out ;
}
case coEnum : {
if ( opt_key == "top_fill_pattern" ||
opt_key == "bottom_fill_pattern" ||
opt_key == "fill_pattern" )
2020-08-07 15:09:58 +02:00
return get_string_from_enum < InfillPattern > ( opt_key , config , true );
2020-08-05 19:25:04 +02:00
if ( opt_key == "gcode_flavor" )
return get_string_from_enum < GCodeFlavor > ( opt_key , config );
2020-10-02 17:31:55 +02:00
if ( opt_key == "machine_limits_usage" )
return get_string_from_enum < MachineLimitsUsage > ( opt_key , config );
2020-08-05 19:25:04 +02:00
if ( opt_key == "ironing_type" )
return get_string_from_enum < IroningType > ( opt_key , config );
if ( opt_key == "support_material_pattern" )
return get_string_from_enum < SupportMaterialPattern > ( opt_key , config );
if ( opt_key == "seam_position" )
return get_string_from_enum < SeamPosition > ( opt_key , config );
if ( opt_key == "display_orientation" )
return get_string_from_enum < SLADisplayOrientation > ( opt_key , config );
if ( opt_key == "support_pillar_connection_mode" )
return get_string_from_enum < SLAPillarConnectionMode > ( opt_key , config );
break ;
}
case coPoints : {
if ( opt_key == "bed_shape" ) {
2020-08-19 15:35:58 +02:00
BedShape shape ( * config . option < ConfigOptionPoints > ( opt_key ));
return shape . get_full_name_with_params ();
2020-08-05 19:25:04 +02:00
}
2020-12-07 16:14:40 +01:00
if ( opt_key == "thumbnails" )
return get_thumbnails_string ( config . option < ConfigOptionPoints > ( opt_key ) -> values );
2020-08-19 15:35:58 +02:00
Vec2d val = config . opt < ConfigOptionPoints > ( opt_key ) -> get_at ( opt_idx );
return from_u8 (( boost :: format ( "[%1%]" ) % ConfigOptionPoint ( val ). serialize ()). str ());
2020-08-05 19:25:04 +02:00
}
default :
break ;
}
return out ;
}
2020-08-10 19:07:45 +02:00
wxString UnsavedChangesDialog :: get_short_string ( wxString full_string )
{
int max_len = 30 ;
if ( full_string . IsEmpty () || full_string . StartsWith ( "#" ) ||
2021-01-29 07:39:27 +01:00
( full_string . Find ( " \n " ) == wxNOT_FOUND && full_string . Length () < size_t ( max_len )))
2020-08-10 19:07:45 +02:00
return full_string ;
m_has_long_strings = true ;
int n_pos = full_string . Find ( " \n " );
if ( n_pos != wxNOT_FOUND && n_pos < max_len )
max_len = n_pos ;
full_string . Truncate ( max_len );
return full_string + dots ;
}
2020-08-14 18:17:16 +02:00
void UnsavedChangesDialog :: update ( Preset :: Type type , PresetCollection * dependent_presets , const std :: string & new_selected_preset , const wxString & header )
2020-08-05 19:25:04 +02:00
{
2020-08-13 15:45:16 +02:00
PresetCollection * presets = dependent_presets ;
// activate buttons and labels
2020-08-14 18:17:16 +02:00
m_save_btn -> Bind ( wxEVT_ENTER_WINDOW , [ this , presets ] ( wxMouseEvent & e ) { show_info_line ( Action :: Save , presets ? presets -> get_selected_preset (). name : "" ); e . Skip (); });
2020-10-15 16:48:48 +02:00
if ( m_transfer_btn ) {
2020-09-02 09:06:42 +02:00
bool is_empty_name = type != dependent_presets -> type ();
2020-10-15 16:48:48 +02:00
m_transfer_btn -> Bind ( wxEVT_ENTER_WINDOW , [ this , new_selected_preset , is_empty_name ] ( wxMouseEvent & e ) { show_info_line ( Action :: Transfer , is_empty_name ? "" : new_selected_preset ); e . Skip (); });
2020-09-02 09:06:42 +02:00
}
2020-10-15 16:48:48 +02:00
m_discard_btn -> Bind ( wxEVT_ENTER_WINDOW , [ this ] ( wxMouseEvent & e ) { show_info_line ( Action :: Discard ); e . Skip (); });
2020-08-13 15:45:16 +02:00
2020-08-14 18:17:16 +02:00
if ( type == Preset :: TYPE_INVALID ) {
2020-10-17 14:04:05 +02:00
m_action_line -> SetLabel ( header + " \n " + _L ( "The following presets were modified:" ));
2020-08-13 15:45:16 +02:00
}
else {
2020-08-14 18:17:16 +02:00
wxString action_msg ;
if ( type == dependent_presets -> type ()) {
2020-10-17 14:04:05 +02:00
action_msg = format_wxstr ( _L ( "Preset \" %1% \" has the following unsaved changes:" ), presets -> get_edited_preset (). name );
2020-08-14 18:17:16 +02:00
}
else {
2020-10-19 10:08:21 +02:00
action_msg = format_wxstr ( type == Preset :: TYPE_PRINTER ?
2020-10-17 14:04:05 +02:00
_L ( "Preset \" %1% \" is not compatible with the new printer profile and it has the following unsaved changes:" ) :
_L ( "Preset \" %1% \" is not compatible with the new print profile and it has the following unsaved changes:" ),
presets -> get_edited_preset (). name );
2020-08-14 18:17:16 +02:00
}
2020-10-17 14:04:05 +02:00
m_action_line -> SetLabel ( action_msg );
2020-08-13 15:45:16 +02:00
}
2020-08-05 19:25:04 +02:00
2020-08-14 18:17:16 +02:00
update_tree ( type , presets );
}
2020-08-05 19:25:04 +02:00
2020-08-14 18:17:16 +02:00
void UnsavedChangesDialog :: update_tree ( Preset :: Type type , PresetCollection * presets_ )
{
2020-08-05 19:25:04 +02:00
Search :: OptionsSearcher & searcher = wxGetApp (). sidebar (). get_searcher ();
searcher . sort_options_by_opt_key ();
2020-08-14 18:17:16 +02:00
// list of the presets with unsaved changes
std :: vector < PresetCollection *> presets_list ;
if ( type == Preset :: TYPE_INVALID )
{
PrinterTechnology printer_technology = wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology ();
2020-08-05 19:25:04 +02:00
2020-08-14 18:17:16 +02:00
for ( Tab * tab : wxGetApp (). tabs_list )
if ( tab -> supports_printer_technology ( printer_technology ) && tab -> current_preset_is_dirty ())
presets_list . emplace_back ( tab -> get_presets ());
}
else
presets_list . emplace_back ( presets_ );
2020-08-10 19:07:45 +02:00
2020-08-14 18:17:16 +02:00
// Display a dialog showing the dirty options in a human readable form.
for ( PresetCollection * presets : presets_list )
{
const DynamicPrintConfig & old_config = presets -> get_selected_preset (). config ;
2020-10-07 20:13:12 +02:00
const PrinterTechnology & old_pt = presets -> get_selected_preset (). printer_technology ();
2020-08-14 18:17:16 +02:00
const DynamicPrintConfig & new_config = presets -> get_edited_preset (). config ;
type = presets -> type ();
2020-08-10 19:07:45 +02:00
2020-10-07 20:13:12 +02:00
const std :: map < wxString , std :: string >& category_icon_map = wxGetApp (). get_tab ( type ) -> get_category_icon_map ();
m_tree_model -> AddPreset ( type , from_u8 ( presets -> get_edited_preset (). name ), old_pt );
2020-08-14 18:17:16 +02:00
// Collect dirty options.
2020-08-18 16:49:06 +02:00
const bool deep_compare = ( type == Preset :: TYPE_PRINTER || type == Preset :: TYPE_SLA_MATERIAL );
auto dirty_options = presets -> current_dirty_options ( deep_compare );
// process changes of extruders count
2020-10-07 20:13:12 +02:00
if ( type == Preset :: TYPE_PRINTER && old_pt == ptFFF &&
2020-08-18 16:49:06 +02:00
old_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size () != new_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size ()) {
wxString local_label = _L ( "Extruders count" );
wxString old_val = from_u8 (( boost :: format ( "%1%" ) % old_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size ()). str ());
wxString new_val = from_u8 (( boost :: format ( "%1%" ) % new_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size ()). str ());
ItemData item_data = { "extruders_count" , local_label , old_val , new_val , type };
2020-10-07 20:13:12 +02:00
m_items_map . emplace ( m_tree_model -> AddOption ( type , _L ( "General" ), _L ( "Capabilities" ), local_label , old_val , new_val , category_icon_map . at ( "General" )), item_data );
2020-08-18 16:49:06 +02:00
}
2020-09-17 15:25:05 +02:00
for ( const std :: string & opt_key : dirty_options ) {
2020-08-14 18:17:16 +02:00
const Search :: Option & option = searcher . get_option ( opt_key );
2020-12-04 17:15:38 +01:00
if ( option . opt_key != boost :: nowide :: widen ( opt_key )) {
// When founded option isn't the correct one.
// It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id",
// because of they don't exist in searcher
continue ;
}
2020-08-14 18:17:16 +02:00
ItemData item_data = { opt_key , option . label_local , get_string_value ( opt_key , old_config ), get_string_value ( opt_key , new_config ), type };
wxString old_val = get_short_string ( item_data . old_val );
wxString new_val = get_short_string ( item_data . new_val );
if ( old_val != item_data . old_val || new_val != item_data . new_val )
item_data . is_long = true ;
2020-10-07 20:13:12 +02:00
m_items_map . emplace ( m_tree_model -> AddOption ( type , option . category_local , option . group_local , option . label_local , old_val , new_val , category_icon_map . at ( option . category )), item_data );
2020-08-14 18:17:16 +02:00
}
2020-08-05 19:25:04 +02:00
}
}
2020-08-14 18:17:16 +02:00
std :: vector < std :: string > UnsavedChangesDialog :: get_unselected_options ( Preset :: Type type )
2020-08-12 17:33:22 +02:00
{
std :: vector < std :: string > ret ;
2020-08-18 16:49:06 +02:00
for ( auto item : m_items_map ) {
if ( item . second . opt_key == "extruders_count" )
continue ;
2020-08-14 18:17:16 +02:00
if ( item . second . type == type && ! m_tree_model -> IsEnabledItem ( item . first ))
2020-08-18 16:49:06 +02:00
ret . emplace_back ( get_pure_opt_key ( item . second . opt_key ));
}
2020-08-12 17:33:22 +02:00
return ret ;
}
2020-08-05 19:25:04 +02:00
2020-08-10 11:24:31 +02:00
std :: vector < std :: string > UnsavedChangesDialog :: get_selected_options ()
{
std :: vector < std :: string > ret ;
2020-08-18 16:49:06 +02:00
for ( auto item : m_items_map )
2020-08-10 11:24:31 +02:00
if ( m_tree_model -> IsEnabledItem ( item . first ))
2020-08-18 16:49:06 +02:00
ret . emplace_back ( get_pure_opt_key ( item . second . opt_key ));
2020-08-10 11:24:31 +02:00
return ret ;
}
2020-07-30 16:16:56 +02:00
void UnsavedChangesDialog :: on_dpi_changed ( const wxRect & suggested_rect )
{
2020-08-12 13:36:26 +02:00
int em = em_unit ();
2020-07-30 16:16:56 +02:00
2020-08-05 19:25:04 +02:00
msw_buttons_rescale ( this , em , { wxID_CANCEL , m_save_btn_id , m_move_btn_id , m_continue_btn_id });
2020-10-15 16:48:48 +02:00
for ( auto btn : { m_save_btn , m_transfer_btn , m_discard_btn } )
if ( btn ) btn -> msw_rescale ();
2020-07-30 16:16:56 +02:00
2020-08-05 19:25:04 +02:00
const wxSize & size = wxSize ( 80 * em , 30 * em );
2020-07-30 16:16:56 +02:00
SetMinSize ( size );
2020-08-12 13:36:26 +02:00
m_tree -> GetColumn ( UnsavedChangesModel :: colToggle ) -> SetWidth ( 6 * em );
m_tree -> GetColumn ( UnsavedChangesModel :: colIconText ) -> SetWidth ( 30 * em );
m_tree -> GetColumn ( UnsavedChangesModel :: colOldValue ) -> SetWidth ( 20 * em );
m_tree -> GetColumn ( UnsavedChangesModel :: colNewValue ) -> SetWidth ( 20 * em );
m_tree_model -> Rescale ();
m_tree -> Refresh ();
2020-07-30 16:16:56 +02:00
Fit ();
Refresh ();
}
void UnsavedChangesDialog :: on_sys_color_changed ()
{
2020-10-15 16:48:48 +02:00
for ( auto btn : { m_save_btn , m_transfer_btn , m_discard_btn } )
2020-08-12 13:36:26 +02:00
btn -> msw_rescale ();
2020-07-30 16:16:56 +02:00
// msw_rescale updates just icons, so use it
2020-08-12 13:36:26 +02:00
m_tree_model -> Rescale ();
m_tree -> Refresh ();
2020-07-30 16:16:56 +02:00
Refresh ();
}
2020-08-10 19:07:45 +02:00
//------------------------------------------
// FullCompareDialog
//------------------------------------------
FullCompareDialog :: FullCompareDialog ( const wxString & option_name , const wxString & old_value , const wxString & new_value )
: wxDialog ( nullptr , wxID_ANY , option_name , wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
{
wxColour bgr_clr = wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW );
SetBackgroundColour ( bgr_clr );
int border = 10 ;
wxStaticBoxSizer * sizer = new wxStaticBoxSizer ( wxVERTICAL , this );
wxFlexGridSizer * grid_sizer = new wxFlexGridSizer ( 2 , 2 , 1 , 0 );
grid_sizer -> SetFlexibleDirection ( wxBOTH );
grid_sizer -> AddGrowableCol ( 0 , 1 );
grid_sizer -> AddGrowableCol ( 1 , 1 );
grid_sizer -> AddGrowableRow ( 1 , 1 );
auto add_header = [ grid_sizer , border , this ]( wxString label ) {
wxStaticText * text = new wxStaticText ( this , wxID_ANY , label );
2020-08-12 13:36:26 +02:00
text -> SetFont ( this -> GetFont (). Bold ());
2020-08-10 19:07:45 +02:00
grid_sizer -> Add ( text , 0 , wxALL , border );
};
auto add_value = [ grid_sizer , border , this ]( wxString label , bool is_colored = false ) {
2020-08-11 10:37:08 +02:00
wxTextCtrl * text = new wxTextCtrl ( this , wxID_ANY , label , wxDefaultPosition , wxSize ( 300 , - 1 ), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH );
2020-08-12 17:33:22 +02:00
text -> SetStyle ( 0 , label . Len (), wxTextAttr ( is_colored ? wxColour ( orange ) : wxNullColour , wxNullColour , this -> GetFont ()));
2020-08-10 19:07:45 +02:00
grid_sizer -> Add ( text , 1 , wxALL | wxEXPAND , border );
};
add_header ( _L ( "Old value" ));
add_header ( _L ( "New value" ));
add_value ( old_value );
add_value ( new_value , true );
sizer -> Add ( grid_sizer , 1 , wxEXPAND );
wxStdDialogButtonSizer * buttons = this -> CreateStdDialogButtonSizer ( wxOK );
wxBoxSizer * topSizer = new wxBoxSizer ( wxVERTICAL );
topSizer -> Add ( sizer , 1 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , border );
topSizer -> Add ( buttons , 0 , wxEXPAND | wxALL , border );
SetSizer ( topSizer );
topSizer -> SetSizeHints ( this );
}
2020-12-10 10:58:40 +01:00
2020-07-30 16:16:56 +02:00
}
} // namespace Slic3r::GUI