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
2021-02-04 09:42:32 +01:00
#include <wx/tokenzr.h>
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"
2021-02-04 09:42:32 +01:00
#include "PresetComboBoxes.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 {
// ----------------------------------------------------------------------------
2021-02-04 09:42:32 +01:00
// ModelNode: a node inside DiffModel
2020-07-30 16:16:56 +02:00
// ----------------------------------------------------------------------------
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 ())
2021-02-12 17:37:57 +01:00
m_old_color_bmp = get_bitmap ( m_toggle ? m_old_color : wxString :: FromUTF8 ( grey . c_str ()));
2020-08-05 19:25:04 +02:00
if ( ! m_new_color . IsEmpty ())
2021-02-12 17:37:57 +01:00
m_new_color_bmp = get_bitmap ( m_toggle ? m_new_color : wxString :: FromUTF8 ( grey . c_str ()));
2020-08-12 13:36:26 +02:00
// 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
// ----------------------------------------------------------------------------
2021-02-04 09:42:32 +01:00
// DiffModel
2020-07-30 16:16:56 +02:00
// ----------------------------------------------------------------------------
2021-02-04 09:42:32 +01:00
DiffModel :: DiffModel ( wxWindow * parent ) :
2020-08-05 19:25:04 +02:00
m_parent_win ( parent )
2020-07-30 16:16:56 +02:00
{
}
2021-02-04 09:42:32 +01:00
wxDataViewItem DiffModel :: 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 ;
}
2021-02-04 09:42:32 +01:00
ModelNode * DiffModel :: AddOption ( ModelNode * group_node , wxString option_name , wxString old_value , wxString new_value )
2020-08-05 19:25:04 +02:00
{
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 ;
}
2021-02-04 09:42:32 +01:00
ModelNode * DiffModel :: AddOptionWithGroup ( ModelNode * category_node , wxString group_name , wxString option_name , wxString old_value , wxString new_value )
2020-08-05 19:25:04 +02:00
{
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 );
}
2021-02-04 09:42:32 +01:00
ModelNode * DiffModel :: AddOptionWithGroupAndCategory ( ModelNode * preset_node , wxString category_name , wxString group_name ,
2020-10-07 20:13:12 +02:00
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 );
}
2021-02-04 09:42:32 +01:00
wxDataViewItem DiffModel :: 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
2021-02-04 09:42:32 +01:00
for ( std :: unique_ptr < ModelNode >& preset : m_preset_nodes )
2020-08-05 19:25:04 +02:00
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
}
2021-02-04 09:42:32 +01:00
return wxDataViewItem (( void * ) AddOptionWithGroupAndCategory ( preset . get (), 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 );
}
}
2021-02-04 09:42:32 +01:00
void DiffModel :: UpdateItemEnabling ( wxDataViewItem item )
2020-08-05 19:25:04 +02:00
{
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
}
2021-02-04 09:42:32 +01:00
bool DiffModel :: IsEnabledItem ( const wxDataViewItem & item )
2020-08-10 11:24:31 +02:00
{
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 ();
}
2021-02-04 09:42:32 +01:00
void DiffModel :: GetValue ( wxVariant & variant , const wxDataViewItem & item , unsigned int col ) const
2020-07-30 16:16:56 +02:00
{
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 :
2021-02-04 09:42:32 +01:00
wxLogError ( "DiffModel::GetValue: wrong column %d" , col );
2020-07-30 16:16:56 +02:00
}
}
2021-02-04 09:42:32 +01:00
bool DiffModel :: SetValue ( const wxVariant & variant , const wxDataViewItem & item , unsigned int col )
2020-07-30 16:16:56 +02:00
{
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 :
2021-02-04 09:42:32 +01:00
wxLogError ( "DiffModel::SetValue: wrong column" );
2020-07-30 16:16:56 +02:00
}
return false ;
}
2021-02-04 09:42:32 +01:00
bool DiffModel :: IsEnabled ( const wxDataViewItem & item , unsigned int col ) const
2020-07-30 16:16:56 +02:00
{
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
}
2021-02-04 09:42:32 +01:00
wxDataViewItem DiffModel :: GetParent ( const wxDataViewItem & item ) const
2020-07-30 16:16:56 +02:00
{
// 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
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 ());
}
2021-02-04 09:42:32 +01:00
bool DiffModel :: IsContainer ( const wxDataViewItem & item ) const
2020-07-30 16:16:56 +02:00
{
// 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 ();
}
2021-02-04 09:42:32 +01:00
unsigned int DiffModel :: GetChildren ( const wxDataViewItem & parent , wxDataViewItemArray & array ) const
2020-07-30 16:16:56 +02:00
{
2021-02-04 09:42:32 +01:00
ModelNode * parent_node = ( ModelNode * ) parent . GetID ();
2020-07-30 16:16:56 +02:00
2021-02-04 09:42:32 +01:00
const ModelNodePtrArray & children = parent_node ? parent_node -> GetChildren () : m_preset_nodes ;
for ( const std :: unique_ptr < ModelNode >& child : children )
2020-10-16 12:20:51 +02:00
array . Add ( wxDataViewItem (( void * ) child . get ()));
2020-07-30 16:16:56 +02:00
2021-02-04 09:42:32 +01:00
return array . size ();
2020-07-30 16:16:56 +02:00
}
2021-02-04 09:42:32 +01:00
wxString DiffModel :: GetColumnType ( unsigned int col ) const
2020-07-30 16:16:56 +02:00
{
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
}
}
}
2021-02-04 09:42:32 +01:00
void DiffModel :: Rescale ()
2020-08-12 13:36:26 +02:00
{
2021-02-04 09:42:32 +01:00
for ( std :: unique_ptr < ModelNode > & node : m_preset_nodes ) {
2020-08-12 13:36:26 +02:00
node -> UpdateIcons ();
2021-02-04 09:42:32 +01:00
rescale_children ( node . get ());
2020-08-12 13:36:26 +02:00
}
}
2021-02-04 09:42:32 +01:00
wxDataViewItem DiffModel :: Delete ( const wxDataViewItem & item )
{
auto ret_item = wxDataViewItem ( nullptr );
ModelNode * node = static_cast < ModelNode *> ( item . GetID ());
if ( ! node ) // happens if item.IsOk()==false
return ret_item ;
// first remove the node from the parent's array of children;
// NOTE: m_preset_nodes is only a vector of _pointers_
// thus removing the node from it doesn't result in freeing it
ModelNodePtrArray & children = node -> GetChildren ();
// Delete all children
while ( ! children . empty ())
Delete ( wxDataViewItem ( children . back (). get ()));
auto node_parent = node -> GetParent ();
wxDataViewItem parent ( node_parent );
ModelNodePtrArray & parents_children = node_parent ? node_parent -> GetChildren () : m_preset_nodes ;
auto it = find_if ( parents_children . begin (), parents_children . end (),
[ node ]( std :: unique_ptr < ModelNode >& child ) { return child . get () == node ; });
assert ( it != parents_children . end ());
it = parents_children . erase ( it );
if ( it != parents_children . end ())
ret_item = wxDataViewItem ( it -> get ());
// set m_container to FALSE if parent has no child
if ( node_parent ) {
#ifndef __WXGTK__
if ( node_parent -> GetChildCount () == 0 )
node_parent -> m_container = false ;
#endif //__WXGTK__
ret_item = parent ;
}
// notify control
ItemDeleted ( parent , item );
return ret_item ;
}
void DiffModel :: Clear ()
{
while ( ! m_preset_nodes . empty ())
Delete ( wxDataViewItem ( m_preset_nodes . back (). get ()));
}
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 ;
}
// ----------------------------------------------------------------------------
// DiffViewCtrl
// ----------------------------------------------------------------------------
DiffViewCtrl :: DiffViewCtrl ( wxWindow * parent , wxSize size )
: wxDataViewCtrl ( parent , wxID_ANY , wxDefaultPosition , size , wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES ),
m_em_unit ( em_unit ( parent ))
{
model = new DiffModel ( parent );
this -> AssociateModel ( model );
model -> SetAssociatedControl ( this );
this -> Bind ( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU , & DiffViewCtrl :: context_menu , this );
this -> Bind ( wxEVT_DATAVIEW_ITEM_ACTIVATED , & DiffViewCtrl :: context_menu , this );
this -> Bind ( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED , & DiffViewCtrl :: item_value_changed , this );
}
void DiffViewCtrl :: AppendBmpTextColumn ( const wxString & label , unsigned model_column , int width , bool set_expander /* = false*/ )
{
m_columns_width . emplace ( this -> GetColumnCount (), width );
#ifdef __linux__
wxDataViewIconTextRenderer * rd = new wxDataViewIconTextRenderer ();
#ifdef SUPPORTS_MARKUP
rd -> EnableMarkup ( true );
#endif
2021-03-01 16:01:53 +01:00
wxDataViewColumn * column = new wxDataViewColumn ( label , rd , model_column , width * m_em_unit , wxALIGN_TOP , wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT );
2021-02-04 09:42:32 +01:00
#else
wxDataViewColumn * column = new wxDataViewColumn ( label , new BitmapTextRenderer ( true , wxDATAVIEW_CELL_INERT ), model_column , width * m_em_unit , wxALIGN_TOP , wxDATAVIEW_COL_RESIZABLE );
#endif //__linux__
this -> AppendColumn ( column );
if ( set_expander )
this -> SetExpanderColumn ( column );
}
void DiffViewCtrl :: AppendToggleColumn_ ( const wxString & label , unsigned model_column , int width )
{
m_columns_width . emplace ( this -> GetColumnCount (), width );
AppendToggleColumn ( label , model_column , wxDATAVIEW_CELL_ACTIVATABLE , width * m_em_unit );
}
void DiffViewCtrl :: Rescale ( int em /*= 0*/ )
{
if ( em > 0 ) {
for ( auto item : m_columns_width )
GetColumn ( item . first ) -> SetWidth ( item . second * em );
m_em_unit = em ;
}
model -> Rescale ();
Refresh ();
}
void DiffViewCtrl :: Append ( const std :: string & opt_key , Preset :: Type type ,
wxString category_name , wxString group_name , wxString option_name ,
wxString old_value , wxString new_value , const std :: string category_icon_name )
{
ItemData item_data = { opt_key , option_name , old_value , new_value , 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 ;
m_items_map . emplace ( model -> AddOption ( type , category_name , group_name , option_name , old_val , new_val , category_icon_name ), item_data );
}
void DiffViewCtrl :: Clear ()
{
model -> Clear ();
m_items_map . clear ();
}
wxString DiffViewCtrl :: get_short_string ( wxString full_string )
{
2021-02-08 13:14:35 +01:00
size_t max_len = 30 ;
2021-02-04 09:42:32 +01:00
if ( full_string . IsEmpty () || full_string . StartsWith ( "#" ) ||
( full_string . Find ( " \n " ) == wxNOT_FOUND && full_string . Length () < max_len ))
return full_string ;
m_has_long_strings = true ;
int n_pos = full_string . Find ( " \n " );
2021-02-08 13:14:35 +01:00
if ( n_pos != wxNOT_FOUND && n_pos < ( int ) max_len )
2021-02-04 09:42:32 +01:00
max_len = n_pos ;
full_string . Truncate ( max_len );
return full_string + dots ;
}
void DiffViewCtrl :: context_menu ( wxDataViewEvent & event )
{
if ( ! m_has_long_strings )
return ;
wxDataViewItem item = event . GetItem ();
if ( ! item ) {
wxPoint mouse_pos = wxGetMousePosition () - this -> GetScreenPosition ();
wxDataViewColumn * col = nullptr ;
this -> HitTest ( mouse_pos , item , col );
if ( ! item )
item = this -> 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 ();
#ifdef __WXOSX__
wxWindow * parent = this -> GetParent ();
if ( parent && parent -> IsShown ()) {
// if this dialog is shown it have to be Hide and show again to be placed on the very Top of windows
parent -> Hide ();
parent -> Show ();
}
#endif // __WXOSX__
}
void DiffViewCtrl :: item_value_changed ( wxDataViewEvent & event )
{
if ( event . GetColumn () != DiffModel :: colToggle )
return ;
wxDataViewItem item = event . GetItem ();
model -> UpdateItemEnabling ( item );
Refresh ();
// update an enabling of the "save/move" buttons
m_empty_selection = selected_options (). empty ();
}
std :: vector < std :: string > DiffViewCtrl :: unselected_options ( Preset :: Type type )
{
std :: vector < std :: string > ret ;
for ( auto item : m_items_map ) {
if ( item . second . opt_key == "extruders_count" )
continue ;
if ( item . second . type == type && ! model -> IsEnabledItem ( item . first ))
ret . emplace_back ( get_pure_opt_key ( item . second . opt_key ));
}
return ret ;
}
std :: vector < std :: string > DiffViewCtrl :: selected_options ()
{
std :: vector < std :: string > ret ;
for ( auto item : m_items_map )
if ( model -> IsEnabledItem ( item . first ))
ret . emplace_back ( get_pure_opt_key ( item . second . opt_key ));
return ret ;
}
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 );
2021-02-25 12:40:42 +01:00
#if 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 ));
2021-02-25 12:40:42 +01:00
#endif // __WXMSW__
2020-08-12 13:36:26 +02:00
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 ());
2021-02-04 09:42:32 +01:00
m_tree = new DiffViewCtrl ( this , wxSize ( em * 60 , em * 30 ));
2021-03-01 16:01:53 +01:00
m_tree -> AppendToggleColumn_ ( L " \u2714 " , DiffModel :: colToggle , wxLinux ? 9 : 6 );
2021-02-04 09:42:32 +01:00
m_tree -> AppendBmpTextColumn ( "" , DiffModel :: colIconText , 28 );
m_tree -> AppendBmpTextColumn ( _L ( "Old Value" ), DiffModel :: colOldValue , 12 );
m_tree -> AppendBmpTextColumn ( _L ( "New Value" ), DiffModel :: colNewValue , 12 );
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 )
2021-02-04 09:42:32 +01:00
( * btn ) -> Bind ( wxEVT_UPDATE_UI , [ this ]( wxUpdateUIEvent & evt ) { evt . Enable ( m_tree -> has_selection ()); });
2020-08-13 15:45:16 +02:00
( * 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-10 19:07:45 +02:00
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-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();
2021-02-24 15:59:09 +01:00
int val = config . option ( opt_key ) -> getInt ();
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 )
2021-02-24 15:59:09 +01:00
if ( int ( key_val . second ) == val ) {
2020-08-07 15:09:58 +02:00
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" );
}
2021-02-24 15:59:09 +01:00
return from_u8 ( _utf8 ( names [ val ]));
2020-08-05 19:25:04 +02:00
}
2021-02-08 13:14:35 +01:00
static size_t 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 );
2021-02-08 13:14:35 +01:00
return static_cast < size_t > ( atoi ( opt_key . c_str ()));
2020-08-18 16:49:06 +02:00
}
return 0 ;
}
2021-02-04 09:42:32 +01:00
static wxString get_full_label ( std :: string opt_key , const DynamicPrintConfig & config )
2020-08-18 16:49:06 +02:00
{
2021-02-04 09:42:32 +01:00
opt_key = get_pure_opt_key ( opt_key );
if ( config . option ( opt_key ) -> is_nil ())
return _L ( "N/A" );
const ConfigOptionDef * opt = config . def () -> get ( opt_key );
return opt -> full_label . empty () ? opt -> label : opt -> full_label ;
2020-08-18 16:49:06 +02:00
}
static wxString get_string_value ( std :: string opt_key , const DynamicPrintConfig & config )
{
2021-02-08 13:14:35 +01:00
size_t 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 : {
2021-02-04 09:42:32 +01:00
if ( is_nullable ) {
auto values = config . opt < ConfigOptionIntsNullable > ( opt_key );
if ( opt_idx < values -> size ())
return from_u8 (( boost :: format ( "%1%" ) % values -> get_at ( opt_idx )). str ());
}
else {
auto values = config . opt < ConfigOptionInts > ( opt_key );
if ( opt_idx < values -> size ())
return from_u8 (( boost :: format ( "%1%" ) % values -> get_at ( opt_idx )). str ());
}
return _L ( "Undef" );
2020-08-05 19:25:04 +02:00
}
case coBool :
return config . opt_bool ( opt_key ) ? "true" : "false" ;
case coBools : {
2021-02-04 09:42:32 +01:00
if ( is_nullable ) {
auto values = config . opt < ConfigOptionBoolsNullable > ( opt_key );
if ( opt_idx < values -> size ())
return values -> get_at ( opt_idx ) ? "true" : "false" ;
}
else {
auto values = config . opt < ConfigOptionBools > ( opt_key );
if ( opt_idx < values -> size ())
return values -> get_at ( opt_idx ) ? "true" : "false" ;
}
return _L ( "Undef" );
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 : {
2021-02-04 09:42:32 +01:00
if ( is_nullable ) {
auto values = config . opt < ConfigOptionPercentsNullable > ( opt_key );
if ( opt_idx < values -> size ())
return from_u8 (( boost :: format ( "%1%%%" ) % values -> get_at ( opt_idx )). str ());
}
else {
auto values = config . opt < ConfigOptionPercents > ( opt_key );
if ( opt_idx < values -> size ())
return from_u8 (( boost :: format ( "%1%%%" ) % values -> get_at ( opt_idx )). str ());
}
return _L ( "Undef" );
2020-08-05 19:25:04 +02:00
}
case coFloat :
return double_to_string ( config . opt_float ( opt_key ));
case coFloats : {
2021-02-04 09:42:32 +01:00
if ( is_nullable ) {
auto values = config . opt < ConfigOptionFloatsNullable > ( opt_key );
if ( opt_idx < values -> size ())
return double_to_string ( values -> get_at ( opt_idx ));
}
else {
auto values = config . opt < ConfigOptionFloats > ( opt_key );
if ( opt_idx < values -> size ())
return double_to_string ( values -> get_at ( opt_idx ));
}
return _L ( "Undef" );
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 ;
}
2021-02-09 14:09:16 +01:00
if ( ! strings -> empty () && opt_idx < strings -> values . size ())
2020-08-05 19:25:04 +02:00
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 : {
2021-02-24 15:59:09 +01:00
return get_string_from_enum ( opt_key , config ,
opt_key == "top_fill_pattern" ||
2020-08-05 19:25:04 +02:00
opt_key == "bottom_fill_pattern" ||
2021-02-24 15:59:09 +01:00
opt_key == "fill_pattern" );
2020-08-05 19:25:04 +02:00
}
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-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 ();
2021-02-04 09:42:32 +01:00
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 ());
2021-02-04 09:42:32 +01:00
m_tree -> Append ( "extruders_count" , type , _L ( "General" ), _L ( "Capabilities" ), local_label , old_val , new_val , category_icon_map . at ( "General" ));
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
2021-02-04 09:42:32 +01:00
m_tree -> Append ( opt_key , type , option . category_local , option . group_local , option . label_local ,
get_string_value ( opt_key , old_config ), get_string_value ( opt_key , new_config ), category_icon_map . at ( option . category ));
2020-08-14 18:17:16 +02:00
}
2020-08-05 19:25:04 +02:00
}
}
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
2021-02-04 09:42:32 +01:00
const wxSize & size = wxSize ( 70 * em , 30 * em );
2020-07-30 16:16:56 +02:00
SetMinSize ( size );
2021-02-04 09:42:32 +01:00
m_tree -> Rescale ( em );
2020-08-12 13:36:26 +02:00
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
2021-02-04 09:42:32 +01:00
m_tree -> Rescale ();
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 );
};
add_header ( _L ( "Old value" ));
add_header ( _L ( "New value" ));
2021-02-04 09:42:32 +01:00
auto get_set_from_val = []( wxString str ) {
if ( str . Find ( " \n " ) == wxNOT_FOUND )
str . Replace ( " " , " \n " );
std :: set < wxString > str_set ;
wxStringTokenizer strings ( str , " \n " );
while ( strings . HasMoreTokens ())
str_set . emplace ( strings . GetNextToken ());
return str_set ;
};
std :: set < wxString > old_set = get_set_from_val ( old_value );
std :: set < wxString > new_set = get_set_from_val ( new_value );
std :: set < wxString > old_new_diff_set ;
std :: set < wxString > new_old_diff_set ;
std :: set_difference ( old_set . begin (), old_set . end (), new_set . begin (), new_set . end (), std :: inserter ( old_new_diff_set , old_new_diff_set . begin ()));
std :: set_difference ( new_set . begin (), new_set . end (), old_set . begin (), old_set . end (), std :: inserter ( new_old_diff_set , new_old_diff_set . begin ()));
auto add_value = [ grid_sizer , border , this ]( wxString label , const std :: set < wxString >& diff_set , bool is_colored = false ) {
wxTextCtrl * text = new wxTextCtrl ( this , wxID_ANY , label , wxDefaultPosition , wxSize ( 400 , 400 ), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH );
text -> SetStyle ( 0 , label . Len (), wxTextAttr ( is_colored ? wxColour ( orange ) : wxNullColour , wxNullColour , this -> GetFont ()));
for ( const wxString & str : diff_set ) {
int pos = label . First ( str );
if ( pos == wxNOT_FOUND )
continue ;
text -> SetStyle ( pos , pos + ( int ) str . Len (), wxTextAttr ( is_colored ? wxColour ( orange ) : wxNullColour , wxNullColour , this -> GetFont (). Bold ()));
}
grid_sizer -> Add ( text , 1 , wxALL | wxEXPAND , border );
};
add_value ( old_value , old_new_diff_set );
add_value ( new_value , new_old_diff_set , true );
2020-08-10 19:07:45 +02:00
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 );
}
2021-02-04 09:42:32 +01:00
static PresetCollection * get_preset_collection ( Preset :: Type type , PresetBundle * preset_bundle = nullptr ) {
if ( ! preset_bundle )
preset_bundle = wxGetApp (). preset_bundle ;
return type == Preset :: Type :: TYPE_PRINT ? & preset_bundle -> prints :
type == Preset :: Type :: TYPE_SLA_PRINT ? & preset_bundle -> sla_prints :
type == Preset :: Type :: TYPE_FILAMENT ? & preset_bundle -> filaments :
type == Preset :: Type :: TYPE_SLA_MATERIAL ? & preset_bundle -> sla_materials :
type == Preset :: Type :: TYPE_PRINTER ? & preset_bundle -> printers :
nullptr ;
}
//------------------------------------------
// DiffPresetDialog
//------------------------------------------
static std :: string get_selection ( PresetComboBox * preset_combo )
{
return into_u8 ( preset_combo -> GetString ( preset_combo -> GetSelection ()));
}
2021-02-09 09:55:26 +01:00
DiffPresetDialog :: DiffPresetDialog ( MainFrame * mainframe )
: DPIDialog ( mainframe , wxID_ANY , wxEmptyString , wxDefaultPosition , wxDefaultSize , wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
2021-02-04 09:42:32 +01:00
m_pr_technology ( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology ())
{
wxColour bgr_clr = wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW );
SetBackgroundColour ( bgr_clr );
2021-02-25 12:40:42 +01:00
#if defined(__WXMSW__)
2021-02-04 09:42:32 +01: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 ));
2021-02-25 12:40:42 +01:00
#endif // __WXMSW__
2021-02-04 09:42:32 +01:00
int border = 10 ;
int em = em_unit ();
assert ( wxGetApp (). preset_bundle );
m_preset_bundle_left = std :: make_unique < PresetBundle > ( * wxGetApp (). preset_bundle );
m_preset_bundle_right = std :: make_unique < PresetBundle > ( * wxGetApp (). preset_bundle );
m_top_info_line = new wxStaticText ( this , wxID_ANY , "Select presets to compare" );
m_top_info_line -> SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ). Bold ());
m_bottom_info_line = new wxStaticText ( this , wxID_ANY , "" );
m_bottom_info_line -> SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ). Bold ());
wxBoxSizer * presets_sizer = new wxBoxSizer ( wxVERTICAL );
for ( auto new_type : { Preset :: TYPE_PRINT , Preset :: TYPE_FILAMENT , Preset :: TYPE_SLA_PRINT , Preset :: TYPE_SLA_MATERIAL , Preset :: TYPE_PRINTER })
{
const PresetCollection * collection = get_preset_collection ( new_type );
wxBoxSizer * sizer = new wxBoxSizer ( wxHORIZONTAL );
PresetComboBox * presets_left ;
PresetComboBox * presets_right ;
ScalableButton * equal_bmp = new ScalableButton ( this , wxID_ANY , "equal" );
auto add_preset_combobox = [ collection , sizer , new_type , em , this ]( PresetComboBox ** cb_ , PresetBundle * preset_bundle ) {
* cb_ = new PresetComboBox ( this , new_type , wxSize ( em * 35 , - 1 ), preset_bundle );
PresetComboBox * cb = ( * cb_ );
cb -> set_selection_changed_function ([ this , new_type , preset_bundle , cb ]( int selection ) {
if ( m_view_type == Preset :: TYPE_INVALID ) {
std :: string preset_name = cb -> GetString ( selection ). ToUTF8 (). data ();
update_compatibility ( Preset :: remove_suffix_modified ( preset_name ), new_type , preset_bundle );
}
update_tree ();
});
if ( collection -> get_selected_idx () != ( size_t ) - 1 )
cb -> update ( collection -> get_selected_preset (). name );
sizer -> Add ( cb , 1 );
cb -> Show ( new_type == Preset :: TYPE_PRINTER );
};
add_preset_combobox ( & presets_left , m_preset_bundle_left . get ());
sizer -> Add ( equal_bmp , 0 , wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL , 5 );
add_preset_combobox ( & presets_right , m_preset_bundle_right . get ());
presets_sizer -> Add ( sizer , 1 , wxTOP , 5 );
equal_bmp -> Show ( new_type == Preset :: TYPE_PRINTER );
m_preset_combos . push_back ({ presets_left , equal_bmp , presets_right });
equal_bmp -> Bind ( wxEVT_BUTTON , [ presets_left , presets_right , this ]( wxEvent & ) {
std :: string preset_name = get_selection ( presets_left );
presets_right -> update ( preset_name );
if ( m_view_type == Preset :: TYPE_INVALID )
update_compatibility ( Preset :: remove_suffix_modified ( preset_name ), presets_right -> get_type (), m_preset_bundle_right . get ());
update_tree ();
});
}
m_show_all_presets = new wxCheckBox ( this , wxID_ANY , _L ( "Show all preset (including incompatible)" ));
m_show_all_presets -> Bind ( wxEVT_CHECKBOX , [ this ]( wxCommandEvent & ) {
bool show_all = m_show_all_presets -> GetValue ();
for ( auto preset_combos : m_preset_combos ) {
if ( preset_combos . presets_left -> get_type () == Preset :: TYPE_PRINTER )
continue ;
preset_combos . presets_left -> show_all ( show_all );
preset_combos . presets_right -> show_all ( show_all );
}
if ( m_view_type == Preset :: TYPE_INVALID )
update_tree ();
});
m_tree = new DiffViewCtrl ( this , wxSize ( em * 65 , em * 40 ));
m_tree -> AppendBmpTextColumn ( "" , DiffModel :: colIconText , 35 );
m_tree -> AppendBmpTextColumn ( _L ( "Left Preset Value" ), DiffModel :: colOldValue , 15 );
m_tree -> AppendBmpTextColumn ( _L ( "Right Preset Value" ), DiffModel :: colNewValue , 15 );
m_tree -> Hide ();
wxBoxSizer * topSizer = new wxBoxSizer ( wxVERTICAL );
topSizer -> Add ( m_top_info_line , 0 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , 2 * border );
topSizer -> Add ( presets_sizer , 0 , wxEXPAND | wxLEFT | wxTOP | wxRIGHT , border );
topSizer -> Add ( m_show_all_presets , 0 , wxEXPAND | wxALL , border );
topSizer -> Add ( m_bottom_info_line , 0 , wxEXPAND | wxALL , 2 * border );
topSizer -> Add ( m_tree , 1 , wxEXPAND | wxALL , border );
this -> SetMinSize ( wxSize ( 80 * em , 30 * em ));
this -> SetSizer ( topSizer );
topSizer -> SetSizeHints ( this );
}
void DiffPresetDialog :: update_controls_visibility ( Preset :: Type type /* = Preset::TYPE_INVALID*/ )
{
for ( auto preset_combos : m_preset_combos ) {
Preset :: Type cb_type = preset_combos . presets_left -> get_type ();
bool show = type != Preset :: TYPE_INVALID ? type == cb_type :
cb_type == Preset :: TYPE_PRINTER ? true :
m_pr_technology == ptFFF ? cb_type == Preset :: TYPE_PRINT || cb_type == Preset :: TYPE_FILAMENT :
cb_type == Preset :: TYPE_SLA_PRINT || cb_type == Preset :: TYPE_SLA_MATERIAL ;
preset_combos . presets_left -> Show ( show );
preset_combos . equal_bmp -> Show ( show );
preset_combos . presets_right -> Show ( show );
if ( show ) {
preset_combos . presets_left -> update_from_bundle ();
preset_combos . presets_right -> update_from_bundle ();
}
}
m_show_all_presets -> Show ( type != Preset :: TYPE_PRINTER );
}
void DiffPresetDialog :: update_bundles_from_app ()
{
* m_preset_bundle_left = * wxGetApp (). preset_bundle ;
* m_preset_bundle_right = * wxGetApp (). preset_bundle ;
}
void DiffPresetDialog :: show ( Preset :: Type type /* = Preset::TYPE_INVALID*/ )
{
this -> SetTitle ( type == Preset :: TYPE_INVALID ? _L ( "Compare Presets" ) : format_wxstr ( _L ( "Compare %1% Presets" ), wxGetApp (). get_tab ( type ) -> name ()));
m_view_type = type ;
update_bundles_from_app ();
update_controls_visibility ( type );
if ( type == Preset :: TYPE_INVALID )
Fit ();
update_tree ();
// if this dialog is shown it have to be Hide and show again to be placed on the very Top of windows
if ( IsShown ())
Hide ();
Show ();
}
void DiffPresetDialog :: update_presets ( Preset :: Type type )
{
m_pr_technology = m_preset_bundle_left . get () -> printers . get_edited_preset (). printer_technology ();
update_bundles_from_app ();
update_controls_visibility ( type );
if ( type == Preset :: TYPE_INVALID )
for ( auto preset_combos : m_preset_combos ) {
if ( preset_combos . presets_left -> get_type () == Preset :: TYPE_PRINTER ) {
preset_combos . presets_left -> update_from_bundle ();
preset_combos . presets_right -> update_from_bundle ();
break ;
}
}
else
for ( auto preset_combos : m_preset_combos ) {
if ( preset_combos . presets_left -> get_type () == type ) {
preset_combos . presets_left -> update ();
preset_combos . presets_right -> update ();
break ;
}
}
update_tree ();
}
void DiffPresetDialog :: update_tree ()
{
Search :: OptionsSearcher & searcher = wxGetApp (). sidebar (). get_searcher ();
searcher . sort_options_by_opt_key ();
m_tree -> Clear ();
wxString bottom_info = "" ;
bool show_tree = false ;
for ( auto preset_combos : m_preset_combos )
{
if ( ! preset_combos . presets_left -> IsShown ())
continue ;
Preset :: Type type = preset_combos . presets_left -> get_type ();
const PresetCollection * presets = get_preset_collection ( type );
const Preset * left_preset = presets -> find_preset ( get_selection ( preset_combos . presets_left ));
const Preset * right_preset = presets -> find_preset ( get_selection ( preset_combos . presets_right ));
if ( ! left_preset || ! right_preset ) {
bottom_info = _L ( "One of the presets doesn't found" );
preset_combos . equal_bmp -> SetBitmap_ ( ScalableBitmap ( this , "question" ));
preset_combos . equal_bmp -> SetToolTip ( bottom_info );
continue ;
}
const DynamicPrintConfig & left_config = left_preset -> config ;
const PrinterTechnology & left_pt = left_preset -> printer_technology ();
const DynamicPrintConfig & right_congig = right_preset -> config ;
if ( left_pt != right_preset -> printer_technology ()) {
bottom_info = _L ( "Comparable printer presets has different printer technology" );
preset_combos . equal_bmp -> SetBitmap_ ( ScalableBitmap ( this , "question" ));
preset_combos . equal_bmp -> SetToolTip ( bottom_info );
continue ;
}
// Collect dirty options.
const bool deep_compare = ( type == Preset :: TYPE_PRINTER || type == Preset :: TYPE_SLA_MATERIAL );
auto dirty_options = type == Preset :: TYPE_PRINTER && left_pt == ptFFF &&
left_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size () < right_congig . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size () ?
presets -> dirty_options ( right_preset , left_preset , deep_compare ) :
presets -> dirty_options ( left_preset , right_preset , deep_compare );
if ( dirty_options . empty ()) {
bottom_info = _L ( "Presets are the same" );
preset_combos . equal_bmp -> SetBitmap_ ( ScalableBitmap ( this , "equal" ));
preset_combos . equal_bmp -> SetToolTip ( bottom_info );
continue ;
}
show_tree = true ;
preset_combos . equal_bmp -> SetBitmap_ ( ScalableBitmap ( this , "not_equal" ));
preset_combos . equal_bmp -> SetToolTip ( _L ( "Presets are different. \n "
"Click this button to select the same as left preset for the right preset." ));
m_tree -> model -> AddPreset ( type , " \" " + from_u8 ( left_preset -> name ) + " \" vs \" " + from_u8 ( right_preset -> name ) + " \" " , left_pt );
const std :: map < wxString , std :: string >& category_icon_map = wxGetApp (). get_tab ( type ) -> get_category_icon_map ();
// process changes of extruders count
if ( type == Preset :: TYPE_PRINTER && left_pt == ptFFF &&
left_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size () != right_congig . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size ()) {
wxString local_label = _L ( "Extruders count" );
wxString left_val = from_u8 (( boost :: format ( "%1%" ) % left_config . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size ()). str ());
wxString right_val = from_u8 (( boost :: format ( "%1%" ) % right_congig . opt < ConfigOptionStrings > ( "extruder_colour" ) -> values . size ()). str ());
m_tree -> Append ( "extruders_count" , type , _L ( "General" ), _L ( "Capabilities" ), local_label , left_val , right_val , category_icon_map . at ( "General" ));
}
for ( const std :: string & opt_key : dirty_options ) {
wxString left_val = get_string_value ( opt_key , left_config );
wxString right_val = get_string_value ( opt_key , right_congig );
Search :: Option option = searcher . get_option ( opt_key , get_full_label ( opt_key , left_config ), type );
if ( option . opt_key != boost :: nowide :: widen ( opt_key )) {
// temporary solution, just for testing
m_tree -> Append ( opt_key , type , _L ( "Undef category" ), _L ( "Undef group" ), opt_key , left_val , right_val , "question" );
// 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 ;
}
m_tree -> Append ( opt_key , type , option . category_local , option . group_local , option . label_local ,
left_val , right_val , category_icon_map . at ( option . category ));
}
}
bool tree_was_shown = m_tree -> IsShown ();
m_tree -> Show ( show_tree );
if ( ! show_tree )
m_bottom_info_line -> SetLabel ( bottom_info );
m_bottom_info_line -> Show ( ! show_tree );
if ( tree_was_shown == m_tree -> IsShown ())
Layout ();
else {
Fit ();
Refresh ();
}
}
void DiffPresetDialog :: on_dpi_changed ( const wxRect & )
{
int em = em_unit ();
msw_buttons_rescale ( this , em , { wxID_CANCEL });
const wxSize & size = wxSize ( 80 * em , 30 * em );
SetMinSize ( size );
for ( auto preset_combos : m_preset_combos ) {
preset_combos . presets_left -> msw_rescale ();
preset_combos . equal_bmp -> msw_rescale ();
preset_combos . presets_right -> msw_rescale ();
}
m_tree -> Rescale ( em );
Fit ();
Refresh ();
}
void DiffPresetDialog :: on_sys_color_changed ()
{
// msw_rescale updates just icons, so use it
m_tree -> Rescale ();
Refresh ();
}
void DiffPresetDialog :: update_compatibility ( const std :: string & preset_name , Preset :: Type type , PresetBundle * preset_bundle )
{
PresetCollection * presets = get_preset_collection ( type , preset_bundle );
bool print_tab = type == Preset :: TYPE_PRINT || type == Preset :: TYPE_SLA_PRINT ;
bool printer_tab = type == Preset :: TYPE_PRINTER ;
bool technology_changed = false ;
if ( printer_tab ) {
const Preset & new_printer_preset = * presets -> find_preset ( preset_name , true );
PrinterTechnology old_printer_technology = presets -> get_selected_preset (). printer_technology ();
PrinterTechnology new_printer_technology = new_printer_preset . printer_technology ();
technology_changed = old_printer_technology != new_printer_technology ;
}
// Mark the print & filament enabled if they are compatible with the currently selected preset.
// The following method should not discard changes of current print or filament presets on change of a printer profile,
// if they are compatible with the current printer.
auto update_compatible_type = []( bool technology_changed , bool on_page , bool show_incompatible_presets ) {
return technology_changed ? PresetSelectCompatibleType :: Always :
on_page ? PresetSelectCompatibleType :: Never :
show_incompatible_presets ? PresetSelectCompatibleType :: OnlyIfWasCompatible : PresetSelectCompatibleType :: Always ;
};
if ( print_tab || printer_tab )
preset_bundle -> update_compatible (
update_compatible_type ( technology_changed , print_tab , true ),
update_compatible_type ( technology_changed , false , true ));
bool is_left_presets = preset_bundle == m_preset_bundle_left . get ();
PrinterTechnology pr_tech = preset_bundle -> printers . get_selected_preset (). printer_technology ();
// update preset comboboxes
for ( auto preset_combos : m_preset_combos )
{
PresetComboBox * cb = is_left_presets ? preset_combos . presets_left : preset_combos . presets_right ;
Preset :: Type presets_type = cb -> get_type ();
2021-02-08 13:14:35 +01:00
if (( print_tab && (
( pr_tech == ptFFF && presets_type == Preset :: TYPE_FILAMENT ) ||
( pr_tech == ptSLA && presets_type == Preset :: TYPE_SLA_MATERIAL ) )) ||
( printer_tab && (
( pr_tech == ptFFF && ( presets_type == Preset :: TYPE_PRINT || presets_type == Preset :: TYPE_FILAMENT ) ) ||
( pr_tech == ptSLA && ( presets_type == Preset :: TYPE_SLA_PRINT || presets_type == Preset :: TYPE_SLA_MATERIAL ) )) ))
2021-02-04 09:42:32 +01:00
cb -> update ();
}
if ( technology_changed &&
m_preset_bundle_left . get () -> printers . get_selected_preset (). printer_technology () ==
m_preset_bundle_right . get () -> printers . get_selected_preset (). printer_technology ())
{
m_pr_technology = m_preset_bundle_left . get () -> printers . get_edited_preset (). printer_technology ();
update_controls_visibility ();
}
}
2020-12-10 10:58:40 +01:00
2020-07-30 16:16:56 +02:00
}
} // namespace Slic3r::GUI