2018-02-02 12:38:35 +01:00
#include "wxExtensions.hpp"
2019-04-05 18:53:02 +02:00
#include <stdexcept>
2019-05-17 19:12:52 +02:00
#include <cmath>
2019-04-05 18:53:02 +02:00
2018-11-26 14:41:58 +01:00
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
2018-05-10 16:36:12 +02:00
#include <wx/sizer.h>
2019-09-30 14:03:50 +02:00
#include <wx/bmpcbox.h>
2018-05-10 16:36:12 +02:00
#include <wx/statline.h>
2018-08-21 02:03:10 +02:00
#include <wx/dcclient.h>
2018-08-21 17:47:05 +02:00
#include <wx/numformatter.h>
2018-11-26 14:41:58 +01:00
2019-04-04 09:20:11 +02:00
#include <boost/algorithm/string/replace.hpp>
2018-11-26 14:41:58 +01:00
#include "BitmapCache.hpp"
2019-01-07 19:17:10 +01:00
#include "GUI.hpp"
2018-10-05 23:29:15 +02:00
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
2019-01-10 17:24:23 +01:00
#include "libslic3r/GCode/PreviewData.hpp"
2019-02-11 14:14:35 +01:00
#include "I18N.hpp"
2019-04-17 21:35:53 +02:00
#include "GUI_Utils.hpp"
2019-09-30 14:03:50 +02:00
#include "PresetBundle.hpp"
2019-05-17 19:12:52 +02:00
#include "../Utils/MacDarkMode.hpp"
2018-05-10 16:36:12 +02:00
2019-01-07 19:17:10 +01:00
using Slic3r :: GUI :: from_u8 ;
2018-11-07 14:44:47 +01:00
wxDEFINE_EVENT ( wxCUSTOMEVT_TICKSCHANGED , wxEvent );
2018-11-12 13:47:24 +01:00
wxDEFINE_EVENT ( wxCUSTOMEVT_LAST_VOLUME_IS_DELETED , wxCommandEvent );
2018-11-07 14:44:47 +01:00
2019-05-13 14:27:51 +02:00
#ifndef __WXGTK__ // msw_menuitem_bitmaps is used for MSW and OSX
2019-05-07 13:35:37 +02:00
static std :: map < int , std :: string > msw_menuitem_bitmaps ;
2019-05-13 14:27:51 +02:00
#ifdef __WXMSW__
2019-05-03 09:44:19 +02:00
void msw_rescale_menu ( wxMenu * menu )
{
2019-05-07 13:35:37 +02:00
struct update_icons {
static void run ( wxMenuItem * item ) {
const auto it = msw_menuitem_bitmaps . find ( item -> GetId ());
if ( it != msw_menuitem_bitmaps . end ()) {
const wxBitmap & item_icon = create_scaled_bitmap ( nullptr , it -> second );
if ( item_icon . IsOk ())
item -> SetBitmap ( item_icon );
}
if ( item -> IsSubMenu ())
for ( wxMenuItem * sub_item : item -> GetSubMenu () -> GetMenuItems ())
update_icons :: run ( sub_item );
}
};
2019-05-03 09:44:19 +02:00
2019-05-07 13:35:37 +02:00
for ( wxMenuItem * item : menu -> GetMenuItems ())
update_icons :: run ( item );
2019-05-03 09:44:19 +02:00
}
2019-05-07 13:35:37 +02:00
#endif /* __WXMSW__ */
2019-05-13 14:27:51 +02:00
#endif /* no __WXGTK__ */
void enable_menu_item ( wxUpdateUIEvent & evt , std :: function < bool () > const cb_condition , wxMenuItem * item )
{
const bool enable = cb_condition ();
evt . Enable ( enable );
#ifdef __WXOSX__
const auto it = msw_menuitem_bitmaps . find ( item -> GetId ());
if ( it != msw_menuitem_bitmaps . end ())
{
2019-05-13 17:57:31 +02:00
const wxBitmap & item_icon = create_scaled_bitmap ( nullptr , it -> second , 16 , false , ! enable );
if ( item_icon . IsOk ())
item -> SetBitmap ( item_icon );
2019-05-13 14:27:51 +02:00
}
#endif // __WXOSX__
}
2019-05-03 09:44:19 +02:00
2018-10-17 12:17:25 +02:00
wxMenuItem * append_menu_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
2019-05-13 14:27:51 +02:00
std :: function < void ( wxCommandEvent & event ) > cb , const wxBitmap & icon , wxEvtHandler * event_handler ,
std :: function < bool () > const cb_condition , wxWindow * parent )
2018-10-17 12:17:25 +02:00
{
if ( id == wxID_ANY )
id = wxNewId ();
2019-05-07 13:35:37 +02:00
auto * item = new wxMenuItem ( menu , id , string , description );
if ( icon . IsOk ()) {
2019-05-03 09:44:19 +02:00
item -> SetBitmap ( icon );
}
2019-05-07 13:35:37 +02:00
menu -> Append ( item );
2018-10-17 12:17:25 +02:00
2018-12-06 14:49:14 +01:00
#ifdef __WXMSW__
2018-12-07 17:50:48 +01:00
if ( event_handler != nullptr && event_handler != menu )
2018-10-18 14:42:21 +02:00
event_handler -> Bind ( wxEVT_MENU , cb , id );
else
2018-12-07 17:50:48 +01:00
#endif // __WXMSW__
2018-10-18 14:42:21 +02:00
menu -> Bind ( wxEVT_MENU , cb , id );
2019-05-13 14:27:51 +02:00
if ( parent ) {
parent -> Bind ( wxEVT_UPDATE_UI , [ cb_condition , item ]( wxUpdateUIEvent & evt ) {
enable_menu_item ( evt , cb_condition , item ); }, id );
}
2018-10-18 14:42:21 +02:00
return item ;
}
2018-12-06 14:49:14 +01:00
wxMenuItem * append_menu_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
2019-05-13 14:27:51 +02:00
std :: function < void ( wxCommandEvent & event ) > cb , const std :: string & icon , wxEvtHandler * event_handler ,
std :: function < bool () > const cb_condition , wxWindow * parent )
2018-12-06 14:49:14 +01:00
{
2019-05-03 09:44:19 +02:00
if ( id == wxID_ANY )
id = wxNewId ();
2019-04-05 18:53:02 +02:00
const wxBitmap & bmp = ! icon . empty () ? create_scaled_bitmap ( nullptr , icon ) : wxNullBitmap ; // FIXME: pass window ptr
2019-05-13 14:27:51 +02:00
//#ifdef __WXMSW__
#ifndef __WXGTK__
2019-05-03 09:44:19 +02:00
if ( bmp . IsOk ())
2019-05-07 13:35:37 +02:00
msw_menuitem_bitmaps [ id ] = icon ;
#endif /* __WXMSW__ */
2019-05-03 09:44:19 +02:00
2019-05-13 14:27:51 +02:00
return append_menu_item ( menu , id , string , description , cb , bmp , event_handler , cb_condition , parent );
2018-12-06 14:49:14 +01:00
}
2019-05-13 14:27:51 +02:00
wxMenuItem * append_submenu ( wxMenu * menu , wxMenu * sub_menu , int id , const wxString & string , const wxString & description , const std :: string & icon ,
std :: function < bool () > const cb_condition , wxWindow * parent )
2018-10-18 14:42:21 +02:00
{
if ( id == wxID_ANY )
id = wxNewId ();
wxMenuItem * item = new wxMenuItem ( menu , id , string , description );
2019-05-07 13:35:37 +02:00
if ( ! icon . empty ()) {
2019-04-05 18:53:02 +02:00
item -> SetBitmap ( create_scaled_bitmap ( nullptr , icon )); // FIXME: pass window ptr
2019-05-13 14:27:51 +02:00
//#ifdef __WXMSW__
#ifndef __WXGTK__
2019-05-07 13:35:37 +02:00
msw_menuitem_bitmaps [ id ] = icon ;
#endif /* __WXMSW__ */
}
2018-10-18 14:42:21 +02:00
item -> SetSubMenu ( sub_menu );
menu -> Append ( item );
2019-05-13 14:27:51 +02:00
if ( parent ) {
parent -> Bind ( wxEVT_UPDATE_UI , [ cb_condition , item ]( wxUpdateUIEvent & evt ) {
enable_menu_item ( evt , cb_condition , item ); }, id );
}
2018-10-17 12:17:25 +02:00
return item ;
}
2019-03-28 16:28:34 +01:00
wxMenuItem * append_menu_radio_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
std :: function < void ( wxCommandEvent & event ) > cb , wxEvtHandler * event_handler )
{
if ( id == wxID_ANY )
id = wxNewId ();
wxMenuItem * item = menu -> AppendRadioItem ( id , string , description );
#ifdef __WXMSW__
if ( event_handler != nullptr && event_handler != menu )
event_handler -> Bind ( wxEVT_MENU , cb , id );
else
#endif // __WXMSW__
menu -> Bind ( wxEVT_MENU , cb , id );
return item ;
}
2019-07-31 10:12:13 +02:00
wxMenuItem * append_menu_check_item ( wxMenu * menu , int id , const wxString & string , const wxString & description ,
std :: function < void ( wxCommandEvent & event ) > cb , wxEvtHandler * event_handler )
{
if ( id == wxID_ANY )
id = wxNewId ();
wxMenuItem * item = menu -> AppendCheckItem ( id , string , description );
#ifdef __WXMSW__
if ( event_handler != nullptr && event_handler != menu )
event_handler -> Bind ( wxEVT_MENU , cb , id );
else
#endif // __WXMSW__
menu -> Bind ( wxEVT_MENU , cb , id );
return item ;
}
2018-02-20 14:25:40 +01:00
const unsigned int wxCheckListBoxComboPopup :: DefaultWidth = 200 ;
const unsigned int wxCheckListBoxComboPopup :: DefaultHeight = 200 ;
2018-02-20 14:44:00 +01:00
const unsigned int wxCheckListBoxComboPopup :: DefaultItemHeight = 18 ;
2018-02-02 12:38:35 +01:00
bool wxCheckListBoxComboPopup :: Create ( wxWindow * parent )
{
2018-02-06 12:43:25 +01:00
return wxCheckListBox :: Create ( parent , wxID_HIGHEST + 1 , wxPoint ( 0 , 0 ));
2018-02-02 12:38:35 +01:00
}
wxWindow * wxCheckListBoxComboPopup :: GetControl ()
{
return this ;
}
void wxCheckListBoxComboPopup :: SetStringValue ( const wxString & value )
{
m_text = value ;
}
wxString wxCheckListBoxComboPopup :: GetStringValue () const
{
return m_text ;
}
wxSize wxCheckListBoxComboPopup :: GetAdjustedSize ( int minWidth , int prefHeight , int maxHeight )
{
// matches owner wxComboCtrl's width
2018-02-20 14:25:40 +01:00
// and sets height dinamically in dependence of contained items count
2018-02-02 12:38:35 +01:00
wxComboCtrl * cmb = GetComboCtrl ();
if ( cmb != nullptr )
{
wxSize size = GetComboCtrl () -> GetSize ();
2018-02-20 14:25:40 +01:00
unsigned int count = GetCount ();
if ( count > 0 )
2018-02-20 14:44:00 +01:00
size . SetHeight ( count * DefaultItemHeight );
2018-02-20 14:25:40 +01:00
else
size . SetHeight ( DefaultHeight );
2018-02-02 12:38:35 +01:00
return size ;
}
else
2018-02-20 14:25:40 +01:00
return wxSize ( DefaultWidth , DefaultHeight );
}
void wxCheckListBoxComboPopup :: OnKeyEvent ( wxKeyEvent & evt )
{
2018-02-22 08:59:47 +01:00
// filters out all the keys which are not working properly
switch ( evt . GetKeyCode ())
{
case WXK_LEFT :
case WXK_UP :
case WXK_RIGHT :
case WXK_DOWN :
case WXK_PAGEUP :
case WXK_PAGEDOWN :
case WXK_END :
case WXK_HOME :
case WXK_NUMPAD_LEFT :
case WXK_NUMPAD_UP :
case WXK_NUMPAD_RIGHT :
case WXK_NUMPAD_DOWN :
case WXK_NUMPAD_PAGEUP :
case WXK_NUMPAD_PAGEDOWN :
case WXK_NUMPAD_END :
case WXK_NUMPAD_HOME :
{
break ;
}
default :
{
evt . Skip ();
break ;
}
}
2018-02-02 12:38:35 +01:00
}
void wxCheckListBoxComboPopup :: OnCheckListBox ( wxCommandEvent & evt )
{
// forwards the checklistbox event to the owner wxComboCtrl
2018-05-17 10:23:02 +02:00
if ( m_check_box_events_status == OnCheckListBoxFunction :: FreeToProceed )
2018-02-02 12:38:35 +01:00
{
2018-05-17 10:23:02 +02:00
wxComboCtrl * cmb = GetComboCtrl ();
if ( cmb != nullptr ) {
wxCommandEvent event ( wxEVT_CHECKLISTBOX , cmb -> GetId ());
event . SetEventObject ( cmb );
cmb -> ProcessWindowEvent ( event );
}
2018-02-02 12:38:35 +01:00
}
2018-02-20 15:22:30 +01:00
evt . Skip ();
2018-05-17 10:23:02 +02:00
#ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file)
if ( m_check_box_events_status == OnCheckListBoxFunction :: RefuseToProceed )
// this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should
// explicitly change the state on the checkbox
m_check_box_events_status = OnCheckListBoxFunction :: WasRefusedLastTime ;
else
// if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it
m_check_box_events_status = OnCheckListBoxFunction :: RefuseToProceed ;
#endif
2018-02-02 12:38:35 +01:00
}
void wxCheckListBoxComboPopup :: OnListBoxSelection ( wxCommandEvent & evt )
{
// transforms list box item selection event into checklistbox item toggle event
int selId = GetSelection ();
if ( selId != wxNOT_FOUND )
{
2018-05-17 10:23:02 +02:00
#ifndef _WIN32
if ( m_check_box_events_status == OnCheckListBoxFunction :: RefuseToProceed )
#endif
Check (( unsigned int ) selId , ! IsChecked (( unsigned int ) selId ));
2018-02-02 12:38:35 +01:00
2018-05-17 10:23:02 +02:00
m_check_box_events_status = OnCheckListBoxFunction :: FreeToProceed ; // so the checkbox reacts to square-click the next time
SetSelection ( wxNOT_FOUND );
2018-02-02 12:38:35 +01:00
wxCommandEvent event ( wxEVT_CHECKLISTBOX , GetId ());
event . SetInt ( selId );
event . SetEventObject ( this );
ProcessEvent ( event );
}
}
2018-04-06 13:37:00 +02:00
// *** wxDataViewTreeCtrlComboPopup ***
const unsigned int wxDataViewTreeCtrlComboPopup :: DefaultWidth = 270 ;
const unsigned int wxDataViewTreeCtrlComboPopup :: DefaultHeight = 200 ;
const unsigned int wxDataViewTreeCtrlComboPopup :: DefaultItemHeight = 22 ;
bool wxDataViewTreeCtrlComboPopup :: Create ( wxWindow * parent )
{
2018-04-09 16:50:17 +02:00
return wxDataViewTreeCtrl :: Create ( parent , wxID_ANY /*HIGHEST + 1*/ , wxPoint ( 0 , 0 ), wxDefaultSize /*wxSize(270, -1)*/ , wxDV_NO_HEADER );
2018-04-06 13:37:00 +02:00
}
2018-04-09 12:41:25 +02:00
/*
2018-04-06 13:37:00 +02:00
wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
{
// matches owner wxComboCtrl's width
// and sets height dinamically in dependence of contained items count
2018-04-06 15:42:52 +02:00
wxComboCtrl* cmb = GetComboCtrl();
if (cmb != nullptr)
{
wxSize size = GetComboCtrl()->GetSize();
if (m_cnt_open_items > 0)
size.SetHeight(m_cnt_open_items * DefaultItemHeight);
else
size.SetHeight(DefaultHeight);
return size;
}
else
return wxSize(DefaultWidth, DefaultHeight);
2018-04-06 13:37:00 +02:00
}
2018-04-09 12:41:25 +02:00
*/
2018-04-06 13:37:00 +02:00
void wxDataViewTreeCtrlComboPopup :: OnKeyEvent ( wxKeyEvent & evt )
{
// filters out all the keys which are not working properly
if ( evt . GetKeyCode () == WXK_UP )
{
return ;
}
else if ( evt . GetKeyCode () == WXK_DOWN )
{
return ;
}
else
{
evt . Skip ();
return ;
}
}
void wxDataViewTreeCtrlComboPopup :: OnDataViewTreeCtrlSelection ( wxCommandEvent & evt )
{
wxComboCtrl * cmb = GetComboCtrl ();
auto selected = GetItemText ( GetSelection ());
cmb -> SetText ( selected );
}
2018-05-04 18:32:20 +02:00
2019-04-26 10:52:38 +02:00
// edit tooltip : change Slic3r to SLIC3R_APP_KEY
// Temporary workaround for localization
void edit_tooltip ( wxString & tooltip )
{
tooltip . Replace ( "Slic3r" , SLIC3R_APP_KEY , true );
}
2019-04-25 15:06:44 +02:00
/* Function for rescale of buttons in Dialog under MSW if dpi is changed.
* btn_ids - vector of buttons identifiers
*/
void msw_buttons_rescale ( wxDialog * dlg , const int em_unit , const std :: vector < int >& btn_ids )
{
const wxSize & btn_size = wxSize ( - 1 , int ( 2.5f * em_unit + 0.5f ));
for ( int btn_id : btn_ids ) {
// There is a case [FirmwareDialog], when we have wxControl instead of wxButton
// so let casting everything to the wxControl
wxControl * btn = static_cast < wxControl *> ( dlg -> FindWindowById ( btn_id , dlg ));
if ( btn )
btn -> SetMinSize ( btn_size );
}
}
2019-04-17 21:35:53 +02:00
/* Function for getting of em_unit value from correct parent.
* In most of cases it is m_em_unit value from GUI_App,
* but for DPIDialogs it's its own value.
* This value will be used to correct rescale after moving between
* Displays with different HDPI */
int em_unit ( wxWindow * win )
{
2019-04-18 15:05:17 +02:00
if ( win )
{
2019-05-06 18:28:23 +02:00
wxTopLevelWindow * toplevel = Slic3r :: GUI :: find_toplevel_parent ( win );
Slic3r :: GUI :: DPIDialog * dlg = dynamic_cast < Slic3r :: GUI :: DPIDialog *> ( toplevel );
2019-04-17 21:35:53 +02:00
if ( dlg )
2019-04-18 15:05:17 +02:00
return dlg -> em_unit ();
2019-05-06 18:28:23 +02:00
Slic3r :: GUI :: DPIFrame * frame = dynamic_cast < Slic3r :: GUI :: DPIFrame *> ( toplevel );
if ( frame )
return frame -> em_unit ();
2019-04-17 21:35:53 +02:00
}
return Slic3r :: GUI :: wxGetApp (). em_unit ();
}
2019-04-08 09:37:23 +02:00
// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true
2019-05-13 17:57:31 +02:00
wxBitmap create_scaled_bitmap ( wxWindow * win , const std :: string & bmp_name_in ,
const int px_cnt /* = 16*/ , const bool is_horizontal /* = false*/ , const bool grayscale /* = false*/ )
2019-04-08 09:37:23 +02:00
{
static Slic3r :: GUI :: BitmapCache cache ;
2019-04-09 17:39:04 +02:00
#ifdef __APPLE__
2019-05-17 19:12:52 +02:00
// Note: win->GetContentScaleFactor() is not used anymore here because it tends to
// return bogus results quite often (such as 1.0 on Retina or even 0.0).
// We're using the max scaling factor across all screens because it's very likely to be good enough.
static float max_scaling_factor = NAN ;
if ( std :: isnan ( max_scaling_factor )) {
max_scaling_factor = Slic3r :: GUI :: mac_max_scaling_factor ();
}
const float scale_factor = win != nullptr ? max_scaling_factor : 1.0f ;
2019-04-09 17:39:04 +02:00
#else
( void )( win );
const float scale_factor = 1.0f ;
#endif
2019-04-05 18:53:02 +02:00
2019-04-08 09:37:23 +02:00
unsigned int height , width = height = 0 ;
unsigned int & scale_base = is_horizontal ? width : height ;
2019-04-05 18:53:02 +02:00
2019-04-17 21:35:53 +02:00
scale_base = ( unsigned int )( em_unit ( win ) * px_cnt * 0.1f + 0.5f );
2019-04-08 09:37:23 +02:00
std :: string bmp_name = bmp_name_in ;
2019-04-09 14:18:03 +02:00
boost :: replace_last ( bmp_name , ".png" , "" );
2019-04-05 18:53:02 +02:00
// Try loading an SVG first, then PNG if SVG is not found:
2019-05-13 17:57:31 +02:00
wxBitmap * bmp = cache . load_svg ( bmp_name , width , height , scale_factor , grayscale );
2019-04-09 14:18:03 +02:00
if ( bmp == nullptr ) {
2019-05-13 17:57:31 +02:00
bmp = cache . load_png ( bmp_name , width , height , grayscale );
2019-04-05 18:53:02 +02:00
}
2019-04-09 14:18:03 +02:00
if ( bmp == nullptr ) {
2019-04-05 18:53:02 +02:00
// Neither SVG nor PNG has been found, raise error
throw std :: runtime_error ( "Could not load bitmap: " + bmp_name );
}
2019-04-08 09:37:23 +02:00
return * bmp ;
}
2018-07-04 08:54:30 +02:00
// *****************************************************************************
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// ObjectDataViewModelNode
2018-07-04 08:54:30 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
2019-08-01 14:58:04 +02:00
void ObjectDataViewModelNode :: init_container ()
{
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true ;
#endif //__WXGTK__
}
2019-09-17 13:41:44 +02:00
#define LAYER_ROOT_ICON "edit_layers_all"
#define LAYER_ICON "edit_layers_some"
2019-05-04 02:07:07 +02:00
ObjectDataViewModelNode :: ObjectDataViewModelNode ( ObjectDataViewModelNode * parent , const ItemType type ) :
m_parent ( parent ),
m_type ( type ),
m_extruder ( wxEmptyString )
{
2019-05-27 13:07:37 +02:00
if ( type == itSettings )
2019-05-04 02:07:07 +02:00
m_name = "Settings to modified" ;
2019-05-27 13:07:37 +02:00
else if ( type == itInstanceRoot )
2019-05-04 02:07:07 +02:00
m_name = _ ( L ( "Instances" ));
2019-05-27 13:07:37 +02:00
else if ( type == itInstance )
{
2019-05-04 02:07:07 +02:00
m_idx = parent -> GetChildCount ();
2019-05-09 18:18:21 +02:00
m_name = wxString :: Format ( _ ( L ( "Instance %d" )), m_idx + 1 );
2019-05-04 02:07:07 +02:00
set_action_icon ();
}
2019-05-27 13:07:37 +02:00
else if ( type == itLayerRoot )
{
2019-09-17 13:41:44 +02:00
m_bmp = create_scaled_bitmap ( nullptr , LAYER_ROOT_ICON ); // FIXME: pass window ptr
2019-05-27 13:07:37 +02:00
m_name = _ ( L ( "Layers" ));
}
2019-05-30 14:41:16 +02:00
if ( type & ( itInstanceRoot | itLayerRoot ))
2019-08-01 14:58:04 +02:00
init_container ();
2019-05-04 02:07:07 +02:00
}
2019-05-30 14:41:16 +02:00
ObjectDataViewModelNode :: ObjectDataViewModelNode ( ObjectDataViewModelNode * parent ,
2019-06-06 14:14:29 +02:00
const t_layer_height_range & layer_range ,
2019-05-31 15:29:09 +02:00
const int idx /*= -1 */ ,
const wxString & extruder ) :
2019-05-30 14:41:16 +02:00
m_parent ( parent ),
m_type ( itLayer ),
m_idx ( idx ),
2019-06-06 14:14:29 +02:00
m_layer_range ( layer_range ),
2019-05-30 14:41:16 +02:00
m_extruder ( extruder )
{
2019-05-31 15:29:09 +02:00
const int children_cnt = parent -> GetChildCount ();
if ( idx < 0 )
m_idx = children_cnt ;
else
{
// update indexes for another Laeyr Nodes
for ( int i = m_idx ; i < children_cnt ; i ++ )
parent -> GetNthChild ( i ) -> SetIdx ( i + 1 );
}
2019-06-06 14:14:29 +02:00
const std :: string label_range = ( boost :: format ( " %.2f-%.2f " ) % layer_range . first % layer_range . second ). str ();
2019-05-30 16:53:17 +02:00
m_name = _ ( L ( "Range" )) + label_range + "(" + _ ( L ( "mm" )) + ")" ;
2019-09-17 13:41:44 +02:00
m_bmp = create_scaled_bitmap ( nullptr , LAYER_ICON ); // FIXME: pass window ptr
2019-05-30 14:41:16 +02:00
set_action_icon ();
2019-08-01 14:58:04 +02:00
init_container ();
2019-05-30 14:41:16 +02:00
}
2019-07-31 16:36:56 +02:00
#ifndef NDEBUG
bool ObjectDataViewModelNode :: valid ()
{
// Verify that the object was not deleted yet.
assert ( m_idx >= - 1 );
return m_idx >= - 1 ;
}
#endif /* NDEBUG */
2019-04-25 01:45:00 +02:00
void ObjectDataViewModelNode :: set_action_icon ()
2019-04-16 10:05:45 +02:00
{
2019-05-27 13:07:37 +02:00
m_action_icon_name = m_type & itObject ? "advanced_plus" :
m_type & ( itVolume | itLayer ) ? "cog" : /*m_type & itInstance*/ "set_separate_obj" ;
2019-04-16 10:05:45 +02:00
m_action_icon = create_scaled_bitmap ( nullptr , m_action_icon_name ); // FIXME: pass window ptr
}
2018-07-04 08:54:30 +02:00
2019-08-01 14:58:04 +02:00
void ObjectDataViewModelNode :: set_printable_icon ( PrintIndicator printable )
{
m_printable = printable ;
m_printable_icon = m_printable == piUndef ? m_empty_bmp :
create_scaled_bitmap ( nullptr , m_printable == piPrintable ? "eye_open.png" : "eye_closed.png" );
}
2018-09-17 15:12:13 +02:00
Slic3r :: GUI :: BitmapCache * m_bitmap_cache = nullptr ;
2019-04-25 01:45:00 +02:00
void ObjectDataViewModelNode :: update_settings_digest_bitmaps ()
2019-04-16 10:05:45 +02:00
{
m_bmp = m_empty_bmp ;
std :: map < std :: string , wxBitmap >& categories_icon = Slic3r :: GUI :: wxGetApp (). obj_list () -> CATEGORY_ICON ;
std :: string scaled_bitmap_name = m_name . ToUTF8 (). data ();
scaled_bitmap_name += "-em" + std :: to_string ( Slic3r :: GUI :: wxGetApp (). em_unit ());
wxBitmap * bmp = m_bitmap_cache -> find ( scaled_bitmap_name );
if ( bmp == nullptr ) {
std :: vector < wxBitmap > bmps ;
for ( auto & cat : m_opt_categories )
bmps . emplace_back ( categories_icon . find ( cat ) == categories_icon . end () ?
wxNullBitmap : categories_icon . at ( cat ));
bmp = m_bitmap_cache -> insert ( scaled_bitmap_name , bmps );
}
m_bmp = * bmp ;
}
2019-04-25 01:45:00 +02:00
bool ObjectDataViewModelNode :: update_settings_digest ( const std :: vector < std :: string >& categories )
2018-09-17 15:12:13 +02:00
{
2018-10-16 11:08:37 +02:00
if ( m_type != itSettings || m_opt_categories == categories )
2018-09-17 15:12:13 +02:00
return false ;
m_opt_categories = categories ;
m_name = wxEmptyString ;
for ( auto & cat : m_opt_categories )
2019-05-04 02:07:07 +02:00
m_name += _ ( cat ) + "; " ;
2018-11-23 11:54:06 +01:00
if ( ! m_name . IsEmpty ())
m_name . erase ( m_name . Length () - 2 , 2 ); // Delete last "; "
2019-04-16 10:05:45 +02:00
update_settings_digest_bitmaps ();
2018-09-17 15:12:13 +02:00
return true ;
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModelNode :: msw_rescale ()
2019-04-16 10:05:45 +02:00
{
if ( ! m_action_icon_name . empty ())
m_action_icon = create_scaled_bitmap ( nullptr , m_action_icon_name );
2019-09-17 13:41:44 +02:00
if ( m_printable != piUndef )
m_printable_icon = create_scaled_bitmap ( nullptr , m_printable == piPrintable ? "eye_open.png" : "eye_closed.png" );
2019-04-16 10:05:45 +02:00
if ( ! m_opt_categories . empty ())
update_settings_digest_bitmaps ();
}
2019-07-22 10:28:25 +02:00
bool ObjectDataViewModelNode :: SetValue ( const wxVariant & variant , unsigned col )
{
switch ( col )
{
2019-08-01 14:58:04 +02:00
case colPrint :
m_printable_icon << variant ;
return true ;
case colName : {
2019-07-22 10:28:25 +02:00
DataViewBitmapText data ;
data << variant ;
m_bmp = data . GetBitmap ();
m_name = data . GetText ();
return true ; }
2019-08-01 14:58:04 +02:00
case colExtruder : {
2019-07-22 10:28:25 +02:00
const wxString & val = variant . GetString ();
m_extruder = val == "0" ? _ ( L ( "default" )) : val ;
return true ; }
2019-08-01 14:58:04 +02:00
case colEditing :
2019-07-22 10:28:25 +02:00
m_action_icon << variant ;
return true ;
default :
printf ( "MyObjectTreeModel::SetValue: wrong column" );
}
return false ;
}
2019-05-09 18:18:21 +02:00
void ObjectDataViewModelNode :: SetIdx ( const int & idx )
{
m_idx = idx ;
// update name if this node is instance
if ( m_type == itInstance )
m_name = wxString :: Format ( _ ( L ( "Instance %d" )), m_idx + 1 );
}
2018-05-04 18:32:20 +02:00
// *****************************************************************************
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// ObjectDataViewModel
2018-05-04 18:32:20 +02:00
// ----------------------------------------------------------------------------
2019-05-27 16:13:24 +02:00
static int get_root_idx ( ObjectDataViewModelNode * parent_node , const ItemType root_type )
{
// because of istance_root and layers_root are at the end of the list, so
// start locking from the end
for ( int root_idx = parent_node -> GetChildCount () - 1 ; root_idx >= 0 ; root_idx -- )
{
// if there is SettingsItem or VolumeItem, then RootItems don't exist in current ObjectItem
if ( parent_node -> GetNthChild ( root_idx ) -> GetType () & ( itSettings | itVolume ))
break ;
if ( parent_node -> GetNthChild ( root_idx ) -> GetType () & root_type )
return root_idx ;
}
return - 1 ;
}
2019-04-25 01:45:00 +02:00
ObjectDataViewModel :: ObjectDataViewModel ()
2018-09-17 15:12:13 +02:00
{
m_bitmap_cache = new Slic3r :: GUI :: BitmapCache ;
}
2019-04-25 01:45:00 +02:00
ObjectDataViewModel ::~ ObjectDataViewModel ()
2018-09-17 15:12:13 +02:00
{
for ( auto object : m_objects )
delete object ;
delete m_bitmap_cache ;
m_bitmap_cache = nullptr ;
}
2019-04-29 15:27:59 +02:00
wxDataViewItem ObjectDataViewModel :: Add ( const wxString & name ,
const int extruder ,
const bool has_errors /* = false*/ )
2018-05-04 18:32:20 +02:00
{
2019-05-10 12:54:20 +02:00
const wxString extruder_str = extruder == 0 ? _ ( L ( "default" )) : wxString :: Format ( "%d" , extruder );
2019-04-25 01:45:00 +02:00
auto root = new ObjectDataViewModelNode ( name , extruder_str );
2019-04-29 15:27:59 +02:00
// Add error icon if detected auto-repaire
if ( has_errors )
root -> m_bmp = * m_warning_bmp ;
2019-09-24 16:01:01 +02:00
m_objects . push_back ( root );
2018-05-29 22:45:35 +02:00
// notify control
wxDataViewItem child (( void * ) root );
wxDataViewItem parent (( void * ) NULL );
2019-04-29 15:27:59 +02:00
2018-05-29 22:45:35 +02:00
ItemAdded ( parent , child );
2018-05-30 17:52:28 +02:00
return child ;
2018-05-29 22:45:35 +02:00
}
2018-05-04 18:32:20 +02:00
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: AddVolumeChild ( const wxDataViewItem & parent_item ,
const wxString & name ,
const Slic3r :: ModelVolumeType volume_type ,
2019-04-29 15:27:59 +02:00
const bool has_errors /* = false*/ ,
2019-04-25 01:45:00 +02:00
const int extruder /* = 0*/ ,
const bool create_frst_child /* = true*/ )
2018-05-29 22:45:35 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * root = ( ObjectDataViewModelNode * ) parent_item . GetID ();
2018-05-30 17:52:28 +02:00
if ( ! root ) return wxDataViewItem ( 0 );
2018-05-04 18:32:20 +02:00
2019-05-10 12:54:20 +02:00
wxString extruder_str = extruder == 0 ? _ ( L ( "default" )) : wxString :: Format ( "%d" , extruder );
2018-08-15 13:59:33 +02:00
2019-05-27 16:13:24 +02:00
// get insertion position according to the existed Layers and/or Instances Items
int insert_position = get_root_idx ( root , itLayerRoot );
if ( insert_position < 0 )
insert_position = get_root_idx ( root , itInstanceRoot );
2018-11-06 11:39:38 +01:00
2019-04-29 15:27:59 +02:00
const bool obj_errors = root -> m_bmp . IsOk ();
2018-10-18 10:40:26 +02:00
if ( create_frst_child && root -> m_volumes_cnt == 0 )
2019-04-10 09:27:42 +02:00
{
2019-04-29 15:27:59 +02:00
const Slic3r :: ModelVolumeType type = Slic3r :: ModelVolumeType :: MODEL_PART ;
const auto node = new ObjectDataViewModelNode ( root , root -> m_name , GetVolumeIcon ( type , obj_errors ), extruder_str , 0 );
node -> m_volume_type = type ;
2018-11-06 11:39:38 +01:00
insert_position < 0 ? root -> Append ( node ) : root -> Insert ( node , insert_position );
2018-05-30 00:36:44 +02:00
// notify control
2018-09-17 15:12:13 +02:00
const wxDataViewItem child (( void * ) node );
2018-05-30 00:36:44 +02:00
ItemAdded ( parent_item , child );
2018-10-18 10:40:26 +02:00
root -> m_volumes_cnt ++ ;
2019-05-27 16:13:24 +02:00
if ( insert_position >= 0 ) insert_position ++ ;
2018-05-30 00:36:44 +02:00
}
2019-04-29 15:27:59 +02:00
const auto node = new ObjectDataViewModelNode ( root , name , GetVolumeIcon ( volume_type , has_errors ), extruder_str , root -> m_volumes_cnt );
2018-11-06 11:39:38 +01:00
insert_position < 0 ? root -> Append ( node ) : root -> Insert ( node , insert_position );
2019-04-29 15:27:59 +02:00
// if part with errors is added, but object wasn't marked, then mark it
if ( ! obj_errors && has_errors )
root -> SetBitmap ( * m_warning_bmp );
2018-05-29 22:45:35 +02:00
// notify control
2019-09-24 16:01:01 +02:00
const wxDataViewItem child (( void * ) node );
2018-10-18 10:40:26 +02:00
ItemAdded ( parent_item , child );
root -> m_volumes_cnt ++ ;
2019-04-16 10:05:45 +02:00
node -> m_volume_type = volume_type ;
2018-05-30 17:52:28 +02:00
return child ;
2018-05-29 22:45:35 +02:00
}
2018-05-10 16:36:12 +02:00
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: AddSettingsChild ( const wxDataViewItem & parent_item )
2018-09-17 15:12:13 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * root = ( ObjectDataViewModelNode * ) parent_item . GetID ();
2018-09-17 15:12:13 +02:00
if ( ! root ) return wxDataViewItem ( 0 );
2019-04-25 01:45:00 +02:00
const auto node = new ObjectDataViewModelNode ( root , itSettings );
2018-09-17 15:12:13 +02:00
root -> Insert ( node , 0 );
// notify control
const wxDataViewItem child (( void * ) node );
ItemAdded ( parent_item , child );
return child ;
}
2019-05-27 13:07:37 +02:00
/* return values:
* true => root_node is created and added to the parent_root
* false => root node alredy exists
*/
static bool append_root_node ( ObjectDataViewModelNode * parent_node ,
ObjectDataViewModelNode ** root_node ,
const ItemType root_type )
{
const int inst_root_id = get_root_idx ( parent_node , root_type );
* root_node = inst_root_id < 0 ?
new ObjectDataViewModelNode ( parent_node , root_type ) :
parent_node -> GetNthChild ( inst_root_id );
if ( inst_root_id < 0 ) {
if (( root_type & itInstanceRoot ) ||
2019-09-03 10:27:16 +02:00
( ( root_type & itLayerRoot ) && get_root_idx ( parent_node , itInstanceRoot ) < 0 ) )
2019-05-27 13:07:37 +02:00
parent_node -> Append ( * root_node );
else if ( root_type & itLayerRoot )
2019-05-30 14:41:16 +02:00
parent_node -> Insert ( * root_node , static_cast < unsigned int > ( get_root_idx ( parent_node , itInstanceRoot )));
2019-05-27 13:07:37 +02:00
return true ;
}
return false ;
}
2019-08-01 14:58:04 +02:00
wxDataViewItem ObjectDataViewModel :: AddRoot ( const wxDataViewItem & parent_item , ItemType root_type )
2018-10-18 10:40:26 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * parent_node = ( ObjectDataViewModelNode * ) parent_item . GetID ();
2018-10-18 10:40:26 +02:00
if ( ! parent_node ) return wxDataViewItem ( 0 );
2019-05-27 13:07:37 +02:00
// get InstanceRoot node
2019-08-01 14:58:04 +02:00
ObjectDataViewModelNode * root_node { nullptr };
const bool appended = append_root_node ( parent_node , & root_node , root_type );
if ( ! root_node ) return wxDataViewItem ( 0 );
2018-10-18 10:40:26 +02:00
2019-08-01 14:58:04 +02:00
const wxDataViewItem root_item (( void * ) root_node );
2018-10-18 10:40:26 +02:00
2019-05-27 13:07:37 +02:00
if ( appended )
2019-08-01 14:58:04 +02:00
ItemAdded ( parent_item , root_item ); // notify control
return root_item ;
}
wxDataViewItem ObjectDataViewModel :: AddInstanceRoot ( const wxDataViewItem & parent_item )
{
return AddRoot ( parent_item , itInstanceRoot );
}
wxDataViewItem ObjectDataViewModel :: AddInstanceChild ( const wxDataViewItem & parent_item , size_t num )
{
2019-09-02 10:03:07 +02:00
std :: vector < bool > print_indicator ( num , true );
// if InstanceRoot item isn't created for this moment
if ( ! GetInstanceRootItem ( parent_item ). IsOk ())
// use object's printable state to first instance
print_indicator [ 0 ] = IsPrintable ( parent_item );
2019-08-01 14:58:04 +02:00
return wxDataViewItem (( void * ) AddInstanceChild ( parent_item , print_indicator ));
}
wxDataViewItem ObjectDataViewModel :: AddInstanceChild ( const wxDataViewItem & parent_item ,
const std :: vector < bool >& print_indicator )
{
const wxDataViewItem inst_root_item = AddInstanceRoot ( parent_item );
if ( ! inst_root_item ) return wxDataViewItem ( 0 );
ObjectDataViewModelNode * inst_root_node = ( ObjectDataViewModelNode * ) inst_root_item . GetID ();
2018-10-18 10:40:26 +02:00
// Add instance nodes
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * instance_node = nullptr ;
2018-10-18 10:40:26 +02:00
size_t counter = 0 ;
2019-08-01 14:58:04 +02:00
while ( counter < print_indicator . size ()) {
2019-04-25 01:45:00 +02:00
instance_node = new ObjectDataViewModelNode ( inst_root_node , itInstance );
2019-08-13 18:15:12 +02:00
2019-08-30 09:35:34 +02:00
instance_node -> set_printable_icon ( print_indicator [ counter ] ? piPrintable : piUnprintable );
2019-08-05 08:44:55 +02:00
2018-10-18 10:40:26 +02:00
inst_root_node -> Append ( instance_node );
// notify control
const wxDataViewItem instance_item (( void * ) instance_node );
ItemAdded ( inst_root_item , instance_item );
++ counter ;
}
2019-08-07 14:11:41 +02:00
// update object_node printable property
UpdateObjectPrintable ( parent_item );
2018-10-18 10:40:26 +02:00
return wxDataViewItem (( void * ) instance_node );
}
2019-08-07 14:11:41 +02:00
void ObjectDataViewModel :: UpdateObjectPrintable ( wxDataViewItem parent_item )
{
const wxDataViewItem inst_root_item = GetInstanceRootItem ( parent_item );
if ( ! inst_root_item )
return ;
ObjectDataViewModelNode * inst_root_node = ( ObjectDataViewModelNode * ) inst_root_item . GetID ();
const size_t child_cnt = inst_root_node -> GetChildren (). Count ();
PrintIndicator obj_pi = piUnprintable ;
for ( size_t i = 0 ; i < child_cnt ; i ++ )
if ( inst_root_node -> GetNthChild ( i ) -> IsPrintable () & piPrintable ) {
obj_pi = piPrintable ;
break ;
}
// and set printable state for object_node to piUndef
ObjectDataViewModelNode * obj_node = ( ObjectDataViewModelNode * ) parent_item . GetID ();
obj_node -> set_printable_icon ( obj_pi );
ItemChanged ( parent_item );
}
// update printable property for all instances from object
void ObjectDataViewModel :: UpdateInstancesPrintable ( wxDataViewItem parent_item )
{
const wxDataViewItem inst_root_item = GetInstanceRootItem ( parent_item );
if ( ! inst_root_item )
return ;
ObjectDataViewModelNode * obj_node = ( ObjectDataViewModelNode * ) parent_item . GetID ();
const PrintIndicator obj_pi = obj_node -> IsPrintable ();
ObjectDataViewModelNode * inst_root_node = ( ObjectDataViewModelNode * ) inst_root_item . GetID ();
const size_t child_cnt = inst_root_node -> GetChildren (). Count ();
for ( size_t i = 0 ; i < child_cnt ; i ++ )
{
ObjectDataViewModelNode * inst_node = inst_root_node -> GetNthChild ( i );
// and set printable state for object_node to piUndef
inst_node -> set_printable_icon ( obj_pi );
ItemChanged ( wxDataViewItem (( void * ) inst_node ));
}
}
bool ObjectDataViewModel :: IsPrintable ( const wxDataViewItem & item ) const
{
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
if ( ! node )
return false ;
return node -> IsPrintable () == piPrintable ;
}
2019-05-27 13:07:37 +02:00
wxDataViewItem ObjectDataViewModel :: AddLayersRoot ( const wxDataViewItem & parent_item )
{
2019-08-01 14:58:04 +02:00
return AddRoot ( parent_item , itLayerRoot );
2019-05-27 13:07:37 +02:00
}
2019-05-31 15:29:09 +02:00
wxDataViewItem ObjectDataViewModel :: AddLayersChild ( const wxDataViewItem & parent_item ,
2019-06-07 11:32:46 +02:00
const t_layer_height_range & layer_range ,
const int extruder /* = 0*/ ,
2019-05-31 15:29:09 +02:00
const int index /* = -1*/ )
2019-05-27 13:07:37 +02:00
{
ObjectDataViewModelNode * parent_node = ( ObjectDataViewModelNode * ) parent_item . GetID ();
if ( ! parent_node ) return wxDataViewItem ( 0 );
2019-06-07 11:32:46 +02:00
wxString extruder_str = extruder == 0 ? _ ( L ( "default" )) : wxString :: Format ( "%d" , extruder );
2019-05-27 13:07:37 +02:00
// get LayerRoot node
2019-05-27 16:13:24 +02:00
ObjectDataViewModelNode * layer_root_node ;
2019-05-30 14:41:16 +02:00
wxDataViewItem layer_root_item ;
2019-05-27 16:13:24 +02:00
2019-05-30 14:41:16 +02:00
if ( parent_node -> GetType () & itLayerRoot ) {
2019-05-27 16:13:24 +02:00
layer_root_node = parent_node ;
2019-05-30 14:41:16 +02:00
layer_root_item = parent_item ;
}
2019-05-27 16:13:24 +02:00
else {
const int root_idx = get_root_idx ( parent_node , itLayerRoot );
if ( root_idx < 0 ) return wxDataViewItem ( 0 );
layer_root_node = parent_node -> GetNthChild ( root_idx );
2019-05-30 14:41:16 +02:00
layer_root_item = wxDataViewItem (( void * ) layer_root_node );
2019-05-27 16:13:24 +02:00
}
2019-05-27 13:07:37 +02:00
// Add layer node
2019-06-07 11:32:46 +02:00
ObjectDataViewModelNode * layer_node = new ObjectDataViewModelNode ( layer_root_node , layer_range , index , extruder_str );
2019-05-31 15:29:09 +02:00
if ( index < 0 )
layer_root_node -> Append ( layer_node );
else
layer_root_node -> Insert ( layer_node , index );
2019-05-27 13:07:37 +02:00
// notify control
2019-05-30 14:41:16 +02:00
const wxDataViewItem layer_item (( void * ) layer_node );
ItemAdded ( layer_root_item , layer_item );
2019-05-27 13:07:37 +02:00
2019-05-30 14:41:16 +02:00
return layer_item ;
2019-05-27 13:07:37 +02:00
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: Delete ( const wxDataViewItem & item )
2018-05-29 22:45:35 +02:00
{
2018-05-30 17:52:28 +02:00
auto ret_item = wxDataViewItem ( 0 );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-05-29 22:45:35 +02:00
if ( ! node ) // happens if item.IsOk()==false
2018-05-30 17:52:28 +02:00
return ret_item ;
2018-05-29 22:45:35 +02:00
2018-05-30 00:36:44 +02:00
auto node_parent = node -> GetParent ();
wxDataViewItem parent ( node_parent );
2018-05-29 22:45:35 +02:00
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
2018-10-31 12:56:08 +01:00
if ( node_parent ) {
2019-05-27 16:13:24 +02:00
if ( node -> m_type & ( itInstanceRoot | itLayerRoot ))
2018-11-12 13:47:24 +01:00
{
2019-07-15 17:09:06 +02:00
// node can be deleted by the Delete, let's check its type while we safely can
bool is_instance_root = ( node -> m_type & itInstanceRoot );
2019-09-06 17:46:55 +02:00
for ( int i = int ( node -> GetChildCount () - 1 ); i >= ( is_instance_root ? 1 : 0 ); i -- )
2018-11-12 13:47:24 +01:00
Delete ( wxDataViewItem ( node -> GetNthChild ( i )));
2019-07-15 17:09:06 +02:00
2018-11-12 13:47:24 +01:00
return parent ;
}
2018-05-30 17:52:28 +02:00
auto id = node_parent -> GetChildren (). Index ( node );
2018-10-16 11:08:37 +02:00
auto idx = node -> GetIdx ();
2018-11-02 23:27:31 +01:00
2018-11-12 13:47:24 +01:00
2019-05-27 16:13:24 +02:00
if ( node -> m_type & ( itVolume | itLayer )) {
2018-11-02 23:27:31 +01:00
node_parent -> m_volumes_cnt -- ;
2018-11-12 13:47:24 +01:00
DeleteSettings ( item );
}
node_parent -> GetChildren (). Remove ( node );
2018-11-02 23:27:31 +01:00
2018-10-31 12:56:08 +01:00
if ( id > 0 ) {
2018-05-30 17:52:28 +02:00
if ( id == node_parent -> GetChildCount ()) id -- ;
ret_item = wxDataViewItem ( node_parent -> GetChildren (). Item ( id ));
}
2018-06-18 15:26:09 +02:00
2018-10-16 11:08:37 +02:00
//update idx value for remaining child-nodes
2018-06-18 15:26:09 +02:00
auto children = node_parent -> GetChildren ();
2018-10-16 11:08:37 +02:00
for ( size_t i = 0 ; i < node_parent -> GetChildCount () && idx >= 0 ; i ++ )
2018-06-18 15:26:09 +02:00
{
2018-10-16 11:08:37 +02:00
auto cur_idx = children [ i ] -> GetIdx ();
if ( cur_idx > idx )
children [ i ] -> SetIdx ( cur_idx - 1 );
2018-06-18 15:26:09 +02:00
}
2018-10-18 10:40:26 +02:00
// if there is last instance item, delete both of it and instance root item
if ( node_parent -> GetChildCount () == 1 && node_parent -> GetNthChild ( 0 ) -> m_type == itInstance )
{
delete node ;
ItemDeleted ( parent , item );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * last_instance_node = node_parent -> GetNthChild ( 0 );
2019-08-05 08:44:55 +02:00
PrintIndicator last_instance_printable = last_instance_node -> IsPrintable ();
2018-10-18 10:40:26 +02:00
node_parent -> GetChildren (). Remove ( last_instance_node );
delete last_instance_node ;
ItemDeleted ( parent , wxDataViewItem ( last_instance_node ));
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * obj_node = node_parent -> GetParent ();
2019-08-05 08:44:55 +02:00
obj_node -> set_printable_icon ( last_instance_printable );
2018-10-18 10:40:26 +02:00
obj_node -> GetChildren (). Remove ( node_parent );
delete node_parent ;
ret_item = wxDataViewItem ( obj_node );
2019-05-27 16:13:24 +02:00
#ifndef __WXGTK__
if ( obj_node -> GetChildCount () == 0 )
obj_node -> m_container = false ;
#endif //__WXGTK__
ItemDeleted ( ret_item , wxDataViewItem ( node_parent ));
return ret_item ;
}
2019-08-07 14:11:41 +02:00
if ( node -> m_type & itInstance )
UpdateObjectPrintable ( wxDataViewItem ( node_parent -> GetParent ()));
2019-05-27 16:13:24 +02:00
// if there was last layer item, delete this one and layers root item
if ( node_parent -> GetChildCount () == 0 && node_parent -> m_type == itLayerRoot )
{
ObjectDataViewModelNode * obj_node = node_parent -> GetParent ();
obj_node -> GetChildren (). Remove ( node_parent );
delete node_parent ;
ret_item = wxDataViewItem ( obj_node );
2018-10-18 10:40:26 +02:00
#ifndef __WXGTK__
if ( obj_node -> GetChildCount () == 0 )
obj_node -> m_container = false ;
#endif //__WXGTK__
2018-11-12 13:47:24 +01:00
ItemDeleted ( ret_item , wxDataViewItem ( node_parent ));
2018-10-18 10:40:26 +02:00
return ret_item ;
}
2018-11-12 13:47:24 +01:00
// if there is last volume item after deleting, delete this last volume too
2019-05-27 16:13:24 +02:00
if ( node_parent -> GetChildCount () <= 3 ) // 3??? #ys_FIXME
2018-11-12 13:47:24 +01:00
{
int vol_cnt = 0 ;
int vol_idx = 0 ;
2019-09-06 17:46:55 +02:00
for ( size_t i = 0 ; i < node_parent -> GetChildCount (); ++ i ) {
2018-11-12 13:47:24 +01:00
if ( node_parent -> GetNthChild ( i ) -> GetType () == itVolume ) {
vol_idx = i ;
vol_cnt ++ ;
}
if ( vol_cnt > 1 )
break ;
}
if ( vol_cnt == 1 ) {
delete node ;
ItemDeleted ( parent , item );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * last_child_node = node_parent -> GetNthChild ( vol_idx );
2018-11-12 13:47:24 +01:00
DeleteSettings ( wxDataViewItem ( last_child_node ));
node_parent -> GetChildren (). Remove ( last_child_node );
2018-11-12 16:03:29 +01:00
node_parent -> m_volumes_cnt = 0 ;
2018-11-12 13:47:24 +01:00
delete last_child_node ;
#ifndef __WXGTK__
if ( node_parent -> GetChildCount () == 0 )
node_parent -> m_container = false ;
#endif //__WXGTK__
ItemDeleted ( parent , wxDataViewItem ( last_child_node ));
wxCommandEvent event ( wxCUSTOMEVT_LAST_VOLUME_IS_DELETED );
auto it = find ( m_objects . begin (), m_objects . end (), node_parent );
event . SetInt ( it == m_objects . end () ? - 1 : it - m_objects . begin ());
wxPostEvent ( m_ctrl , event );
ret_item = parent ;
return ret_item ;
}
}
2018-05-30 17:52:28 +02:00
}
2018-05-30 00:36:44 +02:00
else
{
2018-05-30 17:52:28 +02:00
auto it = find ( m_objects . begin (), m_objects . end (), node );
2019-09-06 17:46:55 +02:00
size_t id = it - m_objects . begin ();
2018-05-30 00:36:44 +02:00
if ( it != m_objects . end ())
2018-11-12 13:47:24 +01:00
{
// Delete all sub-items
int i = m_objects [ id ] -> GetChildCount () - 1 ;
while ( i >= 0 ) {
Delete ( wxDataViewItem ( m_objects [ id ] -> GetNthChild ( i )));
i = m_objects [ id ] -> GetChildCount () - 1 ;
}
2018-05-30 00:36:44 +02:00
m_objects . erase ( it );
2018-11-12 13:47:24 +01:00
}
2018-10-31 12:56:08 +01:00
if ( id > 0 ) {
2018-05-30 17:52:28 +02:00
if ( id == m_objects . size ()) id -- ;
ret_item = wxDataViewItem ( m_objects [ id ]);
}
2018-05-30 00:36:44 +02:00
}
2018-05-29 22:45:35 +02:00
// free the node
delete node ;
2018-05-30 00:36:44 +02:00
// set m_containet to FALSE if parent has no child
2018-09-17 15:12:13 +02:00
if ( node_parent ) {
2018-07-31 15:31:12 +02:00
#ifndef __WXGTK__
2018-09-17 15:12:13 +02:00
if ( node_parent -> GetChildCount () == 0 )
node_parent -> m_container = false ;
2018-07-31 15:31:12 +02:00
#endif //__WXGTK__
2018-05-30 17:52:28 +02:00
ret_item = parent ;
}
2018-05-30 00:36:44 +02:00
2018-05-29 22:45:35 +02:00
// notify control
ItemDeleted ( parent , item );
2018-05-30 17:52:28 +02:00
return ret_item ;
2018-05-04 18:32:20 +02:00
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: DeleteLastInstance ( const wxDataViewItem & parent_item , size_t num )
2018-10-18 10:40:26 +02:00
{
auto ret_item = wxDataViewItem ( 0 );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * parent_node = ( ObjectDataViewModelNode * ) parent_item . GetID ();
2018-10-18 10:40:26 +02:00
if ( ! parent_node ) return ret_item ;
2019-05-27 13:07:37 +02:00
const int inst_root_id = get_root_idx ( parent_node , itInstanceRoot );
2018-10-18 10:40:26 +02:00
if ( inst_root_id < 0 ) return ret_item ;
wxDataViewItemArray items ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * inst_root_node = parent_node -> GetNthChild ( inst_root_id );
2018-10-18 10:40:26 +02:00
const wxDataViewItem inst_root_item (( void * ) inst_root_node );
const int inst_cnt = inst_root_node -> GetChildCount ();
const bool delete_inst_root_item = inst_cnt - num < 2 ? true : false ;
2019-08-05 08:44:55 +02:00
PrintIndicator last_inst_printable = piUndef ;
2018-10-18 10:40:26 +02:00
int stop = delete_inst_root_item ? 0 : inst_cnt - num ;
for ( int i = inst_cnt - 1 ; i >= stop ; -- i ) {
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * last_instance_node = inst_root_node -> GetNthChild ( i );
2019-08-05 08:44:55 +02:00
if ( i == 0 ) last_inst_printable = last_instance_node -> IsPrintable ();
2018-10-18 10:40:26 +02:00
inst_root_node -> GetChildren (). Remove ( last_instance_node );
delete last_instance_node ;
ItemDeleted ( inst_root_item , wxDataViewItem ( last_instance_node ));
}
if ( delete_inst_root_item ) {
ret_item = parent_item ;
parent_node -> GetChildren (). Remove ( inst_root_node );
2019-08-05 08:44:55 +02:00
parent_node -> set_printable_icon ( last_inst_printable );
2018-10-18 10:40:26 +02:00
ItemDeleted ( parent_item , inst_root_item );
2019-08-05 08:44:55 +02:00
ItemChanged ( parent_item );
2018-10-18 10:40:26 +02:00
#ifndef __WXGTK__
if ( parent_node -> GetChildCount () == 0 )
parent_node -> m_container = false ;
#endif //__WXGTK__
}
2019-08-07 14:11:41 +02:00
// update object_node printable property
UpdateObjectPrintable ( parent_item );
2018-10-18 10:40:26 +02:00
return ret_item ;
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: DeleteAll ()
2018-06-04 15:59:55 +02:00
{
while ( ! m_objects . empty ())
{
auto object = m_objects . back ();
// object->RemoveAllChildren();
Delete ( wxDataViewItem ( object ));
}
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: DeleteChildren ( wxDataViewItem & parent )
2018-07-31 15:31:12 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * root = ( ObjectDataViewModelNode * ) parent . GetID ();
2018-07-31 15:31:12 +02:00
if ( ! root ) // happens if item.IsOk()==false
return ;
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
auto & children = root -> GetChildren ();
for ( int id = root -> GetChildCount () - 1 ; id >= 0 ; -- id )
{
auto node = children [ id ];
auto item = wxDataViewItem ( node );
children . RemoveAt ( id );
2018-11-02 23:27:31 +01:00
if ( node -> m_type == itVolume )
root -> m_volumes_cnt -- ;
2018-07-31 15:31:12 +02:00
// free the node
delete node ;
// notify control
ItemDeleted ( parent , item );
}
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root -> m_container = false ;
#endif //__WXGTK__
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: DeleteVolumeChildren ( wxDataViewItem & parent )
2018-11-06 09:06:26 +01:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * root = ( ObjectDataViewModelNode * ) parent . GetID ();
2018-11-06 09:06:26 +01:00
if ( ! root ) // happens if item.IsOk()==false
return ;
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
auto & children = root -> GetChildren ();
for ( int id = root -> GetChildCount () - 1 ; id >= 0 ; -- id )
{
auto node = children [ id ];
if ( node -> m_type != itVolume )
continue ;
auto item = wxDataViewItem ( node );
2018-11-12 13:47:24 +01:00
DeleteSettings ( item );
2018-11-06 09:06:26 +01:00
children . RemoveAt ( id );
// free the node
delete node ;
// notify control
ItemDeleted ( parent , item );
}
2018-11-12 13:47:24 +01:00
root -> m_volumes_cnt = 0 ;
2018-11-06 09:06:26 +01:00
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root -> m_container = false ;
#endif //__WXGTK__
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: DeleteSettings ( const wxDataViewItem & parent )
2018-11-12 13:47:24 +01:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) parent . GetID ();
2018-11-12 13:47:24 +01:00
if ( ! node ) return ;
// if volume has a "settings"item, than delete it before volume deleting
if ( node -> GetChildCount () > 0 && node -> GetNthChild ( 0 ) -> GetType () == itSettings ) {
auto settings_node = node -> GetNthChild ( 0 );
auto settings_item = wxDataViewItem ( settings_node );
node -> GetChildren (). RemoveAt ( 0 );
delete settings_node ;
ItemDeleted ( parent , settings_item );
}
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetItemById ( int obj_idx )
2018-06-04 15:59:55 +02:00
{
2019-09-06 17:46:55 +02:00
if ( size_t ( obj_idx ) >= m_objects . size ())
2018-06-04 15:59:55 +02:00
{
printf ( "Error! Out of objects range. \n " );
return wxDataViewItem ( 0 );
}
return wxDataViewItem ( m_objects [ obj_idx ]);
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetItemByVolumeId ( int obj_idx , int volume_idx )
2018-09-17 15:12:13 +02:00
{
2019-09-06 17:46:55 +02:00
if ( size_t ( obj_idx ) >= m_objects . size ()) {
2018-09-17 15:12:13 +02:00
printf ( "Error! Out of objects range. \n " );
return wxDataViewItem ( 0 );
}
auto parent = m_objects [ obj_idx ];
2018-10-12 12:00:37 +02:00
if ( parent -> GetChildCount () == 0 ||
2018-10-16 11:08:37 +02:00
( parent -> GetChildCount () == 1 && parent -> GetNthChild ( 0 ) -> GetType () & itSettings )) {
2018-10-11 15:57:09 +02:00
if ( volume_idx == 0 )
return GetItemById ( obj_idx );
2018-09-17 15:12:13 +02:00
printf ( "Error! Object has no one volume. \n " );
return wxDataViewItem ( 0 );
}
for ( size_t i = 0 ; i < parent -> GetChildCount (); i ++ )
2018-11-09 18:39:07 +01:00
if ( parent -> GetNthChild ( i ) -> m_idx == volume_idx && parent -> GetNthChild ( i ) -> GetType () & itVolume )
2018-09-17 15:12:13 +02:00
return wxDataViewItem ( parent -> GetNthChild ( i ));
return wxDataViewItem ( 0 );
}
2019-05-31 10:54:52 +02:00
wxDataViewItem ObjectDataViewModel :: GetItemById ( const int obj_idx , const int sub_obj_idx , const ItemType parent_type )
2018-11-05 13:53:30 +01:00
{
2019-09-06 17:46:55 +02:00
if ( size_t ( obj_idx ) >= m_objects . size ()) {
2018-11-05 13:53:30 +01:00
printf ( "Error! Out of objects range. \n " );
return wxDataViewItem ( 0 );
}
2019-05-31 10:54:52 +02:00
auto item = GetItemByType ( wxDataViewItem ( m_objects [ obj_idx ]), parent_type );
if ( ! item )
2018-11-05 13:53:30 +01:00
return wxDataViewItem ( 0 );
2019-06-06 14:14:29 +02:00
auto parent = ( ObjectDataViewModelNode * ) item . GetID ();
2018-11-05 13:53:30 +01:00
for ( size_t i = 0 ; i < parent -> GetChildCount (); i ++ )
2019-05-31 10:54:52 +02:00
if ( parent -> GetNthChild ( i ) -> m_idx == sub_obj_idx )
2018-11-05 13:53:30 +01:00
return wxDataViewItem ( parent -> GetNthChild ( i ));
return wxDataViewItem ( 0 );
}
2019-05-31 10:54:52 +02:00
wxDataViewItem ObjectDataViewModel :: GetItemByInstanceId ( int obj_idx , int inst_idx )
{
return GetItemById ( obj_idx , inst_idx , itInstanceRoot );
}
wxDataViewItem ObjectDataViewModel :: GetItemByLayerId ( int obj_idx , int layer_idx )
{
return GetItemById ( obj_idx , layer_idx , itLayerRoot );
}
2019-06-06 14:14:29 +02:00
wxDataViewItem ObjectDataViewModel :: GetItemByLayerRange ( const int obj_idx , const t_layer_height_range & layer_range )
{
2019-09-06 17:46:55 +02:00
if ( size_t ( obj_idx ) >= m_objects . size ()) {
2019-06-06 14:14:29 +02:00
printf ( "Error! Out of objects range. \n " );
return wxDataViewItem ( 0 );
}
auto item = GetItemByType ( wxDataViewItem ( m_objects [ obj_idx ]), itLayerRoot );
if ( ! item )
return wxDataViewItem ( 0 );
auto parent = ( ObjectDataViewModelNode * ) item . GetID ();
for ( size_t i = 0 ; i < parent -> GetChildCount (); i ++ )
if ( parent -> GetNthChild ( i ) -> m_layer_range == layer_range )
return wxDataViewItem ( parent -> GetNthChild ( i ));
return wxDataViewItem ( 0 );
}
int ObjectDataViewModel :: GetItemIdByLayerRange ( const int obj_idx , const t_layer_height_range & layer_range )
{
wxDataViewItem item = GetItemByLayerRange ( obj_idx , layer_range );
if ( ! item )
return - 1 ;
return GetLayerIdByItem ( item );
}
2019-04-25 01:45:00 +02:00
int ObjectDataViewModel :: GetIdByItem ( const wxDataViewItem & item ) const
2018-06-07 00:55:09 +02:00
{
2019-06-10 15:22:09 +02:00
if ( ! item . IsOk ())
return - 1 ;
2018-06-07 00:55:09 +02:00
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-06-07 00:55:09 +02:00
auto it = find ( m_objects . begin (), m_objects . end (), node );
if ( it == m_objects . end ())
return - 1 ;
return it - m_objects . begin ();
}
2019-04-25 01:45:00 +02:00
int ObjectDataViewModel :: GetIdByItemAndType ( const wxDataViewItem & item , const ItemType type ) const
2018-06-18 14:20:29 +02:00
{
wxASSERT ( item . IsOk ());
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-10-18 10:40:26 +02:00
if ( ! node || node -> m_type != type )
2018-06-18 14:20:29 +02:00
return - 1 ;
2018-10-16 11:08:37 +02:00
return node -> GetIdx ();
2018-06-18 14:20:29 +02:00
}
2019-04-25 01:45:00 +02:00
int ObjectDataViewModel :: GetObjectIdByItem ( const wxDataViewItem & item ) const
2018-12-12 08:40:10 +01:00
{
return GetIdByItem ( GetTopParent ( item ));
}
2019-04-25 01:45:00 +02:00
int ObjectDataViewModel :: GetVolumeIdByItem ( const wxDataViewItem & item ) const
2018-10-18 10:40:26 +02:00
{
return GetIdByItemAndType ( item , itVolume );
}
2019-04-25 01:45:00 +02:00
int ObjectDataViewModel :: GetInstanceIdByItem ( const wxDataViewItem & item ) const
2018-10-18 10:40:26 +02:00
{
return GetIdByItemAndType ( item , itInstance );
}
2019-05-30 14:41:16 +02:00
int ObjectDataViewModel :: GetLayerIdByItem ( const wxDataViewItem & item ) const
{
return GetIdByItemAndType ( item , itLayer );
}
2019-06-06 14:14:29 +02:00
t_layer_height_range ObjectDataViewModel :: GetLayerRangeByItem ( const wxDataViewItem & item ) const
{
wxASSERT ( item . IsOk ());
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
if ( ! node || node -> m_type != itLayer )
return { 0.0f , 0.0f };
return node -> GetLayerRange ();
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: GetItemInfo ( const wxDataViewItem & item , ItemType & type , int & obj_idx , int & idx )
2018-10-12 12:00:37 +02:00
{
wxASSERT ( item . IsOk ());
2018-10-16 11:08:37 +02:00
type = itUndef ;
2018-10-12 12:00:37 +02:00
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2019-09-03 10:27:16 +02:00
if ( ! node ||
node -> GetIdx () <- 1 ||
( node -> GetIdx () == - 1 &&
! ( node -> GetType () & ( itObject | itSettings | itInstanceRoot | itLayerRoot /* | itLayer*/ ))
)
)
2018-10-18 10:40:26 +02:00
return ;
2018-10-16 11:08:37 +02:00
idx = node -> GetIdx ();
2018-10-18 10:40:26 +02:00
type = node -> GetType ();
2018-10-12 12:00:37 +02:00
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * parent_node = node -> GetParent ();
2018-10-12 12:00:37 +02:00
if ( ! parent_node ) return ;
2019-06-06 14:14:29 +02:00
// get top parent (Object) node
while ( parent_node -> m_type != itObject )
parent_node = parent_node -> GetParent ();
2018-10-12 12:00:37 +02:00
auto it = find ( m_objects . begin (), m_objects . end (), parent_node );
if ( it != m_objects . end ())
obj_idx = it - m_objects . begin ();
2018-10-18 10:40:26 +02:00
else
type = itUndef ;
2018-10-12 12:00:37 +02:00
}
2019-04-25 01:45:00 +02:00
int ObjectDataViewModel :: GetRowByItem ( const wxDataViewItem & item ) const
2018-12-17 12:46:36 +01:00
{
if ( m_objects . empty ())
return - 1 ;
int row_num = 0 ;
2019-09-06 17:46:55 +02:00
for ( size_t i = 0 ; i < m_objects . size (); i ++ )
2018-12-17 12:46:36 +01:00
{
row_num ++ ;
if ( item == wxDataViewItem ( m_objects [ i ]))
return row_num ;
2019-09-06 17:46:55 +02:00
for ( size_t j = 0 ; j < m_objects [ i ] -> GetChildCount (); j ++ )
2018-12-17 12:46:36 +01:00
{
row_num ++ ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * cur_node = m_objects [ i ] -> GetNthChild ( j );
2018-12-17 12:46:36 +01:00
if ( item == wxDataViewItem ( cur_node ))
return row_num ;
if ( cur_node -> m_type == itVolume && cur_node -> GetChildCount () == 1 )
row_num ++ ;
if ( cur_node -> m_type == itInstanceRoot )
{
row_num ++ ;
2019-09-06 17:46:55 +02:00
for ( size_t t = 0 ; t < cur_node -> GetChildCount (); t ++ )
2018-12-17 12:46:36 +01:00
{
row_num ++ ;
if ( item == wxDataViewItem ( cur_node -> GetNthChild ( t )))
return row_num ;
}
}
}
}
return - 1 ;
}
2019-08-02 17:53:12 +02:00
bool ObjectDataViewModel :: InvalidItem ( const wxDataViewItem & item )
{
if ( ! item )
return true ;
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
if ( ! node || node -> invalid ())
return true ;
return false ;
}
2019-04-25 01:45:00 +02:00
wxString ObjectDataViewModel :: GetName ( const wxDataViewItem & item ) const
2018-05-04 18:32:20 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
if ( ! node ) // happens if item.IsOk()==false
return wxEmptyString ;
return node -> m_name ;
}
2019-04-25 01:45:00 +02:00
wxBitmap & ObjectDataViewModel :: GetBitmap ( const wxDataViewItem & item ) const
2018-09-17 15:12:13 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-09-17 15:12:13 +02:00
return node -> m_bmp ;
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: GetValue ( wxVariant & variant , const wxDataViewItem & item , unsigned int col ) const
2018-05-04 18:32:20 +02:00
{
wxASSERT ( item . IsOk ());
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
switch ( col )
{
2019-08-01 14:58:04 +02:00
case colPrint :
variant << node -> m_printable_icon ;
break ;
case colName :
2019-04-25 01:45:00 +02:00
variant << DataViewBitmapText ( node -> m_name , node -> m_bmp );
2018-05-04 18:32:20 +02:00
break ;
2019-08-01 14:58:04 +02:00
case colExtruder :
2018-07-04 08:54:30 +02:00
variant = node -> m_extruder ;
break ;
2019-08-01 14:58:04 +02:00
case colEditing :
2018-07-04 08:54:30 +02:00
variant << node -> m_action_icon ;
break ;
2018-05-04 18:32:20 +02:00
default :
2018-06-04 15:59:55 +02:00
;
2018-05-04 18:32:20 +02:00
}
}
2019-04-25 01:45:00 +02:00
bool ObjectDataViewModel :: SetValue ( const wxVariant & variant , const wxDataViewItem & item , unsigned int col )
2018-05-04 18:32:20 +02:00
{
wxASSERT ( item . IsOk ());
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-06-05 13:17:24 +02:00
return node -> SetValue ( variant , col );
2018-05-04 18:32:20 +02:00
}
2019-04-25 01:45:00 +02:00
bool ObjectDataViewModel :: SetValue ( const wxVariant & variant , const int item_idx , unsigned int col )
2018-06-05 10:41:20 +02:00
{
2019-09-06 17:46:55 +02:00
if ( size_t ( item_idx ) >= m_objects . size ())
2018-06-05 10:41:20 +02:00
return false ;
return m_objects [ item_idx ] -> SetValue ( variant , col );
}
2018-06-19 12:24:16 +02:00
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: ReorganizeChildren ( const int current_volume_id ,
const int new_volume_id ,
const wxDataViewItem & parent )
2018-08-13 14:15:43 +02:00
{
auto ret_item = wxDataViewItem ( 0 );
if ( current_volume_id == new_volume_id )
return ret_item ;
wxASSERT ( parent . IsOk ());
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node_parent = ( ObjectDataViewModelNode * ) parent . GetID ();
2018-08-13 14:15:43 +02:00
if ( ! node_parent ) // happens if item.IsOk()==false
return ret_item ;
2018-10-16 11:08:37 +02:00
const size_t shift = node_parent -> GetChildren (). Item ( 0 ) -> m_type == itSettings ? 1 : 0 ;
2018-09-17 15:12:13 +02:00
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * deleted_node = node_parent -> GetNthChild ( current_volume_id + shift );
2018-08-13 14:15:43 +02:00
node_parent -> GetChildren (). Remove ( deleted_node );
ItemDeleted ( parent , wxDataViewItem ( deleted_node ));
2018-09-17 15:12:13 +02:00
node_parent -> Insert ( deleted_node , new_volume_id + shift );
2018-08-13 14:15:43 +02:00
ItemAdded ( parent , wxDataViewItem ( deleted_node ));
2018-10-16 11:08:37 +02:00
const auto settings_item = GetSettingsItem ( wxDataViewItem ( deleted_node ));
2018-09-17 15:12:13 +02:00
if ( settings_item )
ItemAdded ( wxDataViewItem ( deleted_node ), settings_item );
2018-08-13 14:15:43 +02:00
//update volume_id value for child-nodes
auto children = node_parent -> GetChildren ();
int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id ;
int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id ;
for ( int id = id_frst ; id <= id_last ; ++ id )
2018-10-16 11:08:37 +02:00
children [ id + shift ] -> SetIdx ( id );
2018-08-13 14:15:43 +02:00
2018-09-17 15:12:13 +02:00
return wxDataViewItem ( node_parent -> GetNthChild ( new_volume_id + shift ));
2018-08-13 14:15:43 +02:00
}
2019-04-25 01:45:00 +02:00
bool ObjectDataViewModel :: IsEnabled ( const wxDataViewItem & item , unsigned int col ) const
2018-09-17 15:12:13 +02:00
{
wxASSERT ( item . IsOk ());
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-09-17 15:12:13 +02:00
2018-12-04 18:14:02 +01:00
// disable extruder selection for the non "itObject|itVolume" item
2019-08-01 14:58:04 +02:00
return ! ( col == colExtruder && node -> m_extruder . IsEmpty ());
2018-09-17 15:12:13 +02:00
}
2018-05-04 18:32:20 +02:00
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetParent ( const wxDataViewItem & item ) const
2018-05-04 18:32:20 +02:00
{
// the invisible root node has no parent
if ( ! item . IsOk ())
return wxDataViewItem ( 0 );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2019-07-31 16:36:56 +02:00
assert ( node != nullptr && node -> valid ());
2018-05-04 18:32:20 +02:00
2018-05-29 22:45:35 +02:00
// objects nodes has no parent too
2018-10-18 10:40:26 +02:00
if ( node -> m_type == itObject )
2018-05-04 18:32:20 +02:00
return wxDataViewItem ( 0 );
return wxDataViewItem (( void * ) node -> GetParent ());
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetTopParent ( const wxDataViewItem & item ) const
2018-10-18 10:40:26 +02:00
{
// the invisible root node has no parent
if ( ! item . IsOk ())
return wxDataViewItem ( 0 );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-10-18 10:40:26 +02:00
if ( node -> m_type == itObject )
return item ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * parent_node = node -> GetParent ();
2018-10-18 10:40:26 +02:00
while ( parent_node -> m_type != itObject )
2019-06-06 14:14:29 +02:00
parent_node = parent_node -> GetParent ();
2018-10-18 10:40:26 +02:00
return wxDataViewItem (( void * ) parent_node );
}
2019-04-25 01:45:00 +02:00
bool ObjectDataViewModel :: IsContainer ( const wxDataViewItem & item ) const
2018-05-04 18:32:20 +02:00
{
2018-05-30 00:36:44 +02:00
// the invisible root node can have children
2018-05-04 18:32:20 +02:00
if ( ! item . IsOk ())
return true ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
return node -> IsContainer ();
}
2019-04-25 01:45:00 +02:00
unsigned int ObjectDataViewModel :: GetChildren ( const wxDataViewItem & parent , wxDataViewItemArray & array ) const
2018-05-04 18:32:20 +02:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) parent . GetID ();
2018-05-04 18:32:20 +02:00
if ( ! node )
{
2018-05-09 14:36:20 +02:00
for ( auto object : m_objects )
array . Add ( wxDataViewItem (( void * ) object ));
2018-05-04 18:32:20 +02:00
return m_objects . size ();
}
if ( node -> GetChildCount () == 0 )
{
return 0 ;
}
unsigned int count = node -> GetChildren (). GetCount ();
for ( unsigned int pos = 0 ; pos < count ; pos ++ )
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * child = node -> GetChildren (). Item ( pos );
2018-05-04 18:32:20 +02:00
array . Add ( wxDataViewItem (( void * ) child ));
}
return count ;
}
2018-07-30 12:17:32 +02:00
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: GetAllChildren ( const wxDataViewItem & parent , wxDataViewItemArray & array ) const
2019-03-29 14:36:09 +01:00
{
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) parent . GetID ();
2019-03-29 14:36:09 +01:00
if ( ! node ) {
for ( auto object : m_objects )
array . Add ( wxDataViewItem (( void * ) object ));
}
else if ( node -> GetChildCount () == 0 )
return ;
else {
const size_t count = node -> GetChildren (). GetCount ();
for ( size_t pos = 0 ; pos < count ; pos ++ ) {
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * child = node -> GetChildren (). Item ( pos );
2019-03-29 14:36:09 +01:00
array . Add ( wxDataViewItem (( void * ) child ));
}
}
wxDataViewItemArray new_array = array ;
for ( const auto item : new_array )
{
wxDataViewItemArray children ;
GetAllChildren ( item , children );
WX_APPEND_ARRAY ( array , children );
}
}
2019-04-25 01:45:00 +02:00
ItemType ObjectDataViewModel :: GetItemType ( const wxDataViewItem & item ) const
2018-10-16 11:08:37 +02:00
{
if ( ! item . IsOk ())
return itUndef ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2019-07-23 17:09:19 +02:00
return node -> m_type < 0 ? itUndef : node -> m_type ;
2018-10-16 11:08:37 +02:00
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetItemByType ( const wxDataViewItem & parent_item , ItemType type ) const
2018-09-17 15:12:13 +02:00
{
2018-11-05 13:53:30 +01:00
if ( ! parent_item . IsOk ())
2018-09-17 15:12:13 +02:00
return wxDataViewItem ( 0 );
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) parent_item . GetID ();
2018-09-17 15:12:13 +02:00
if ( node -> GetChildCount () == 0 )
return wxDataViewItem ( 0 );
2019-09-06 17:46:55 +02:00
for ( size_t i = 0 ; i < node -> GetChildCount (); i ++ ) {
2018-11-05 13:53:30 +01:00
if ( node -> GetNthChild ( i ) -> m_type == type )
return wxDataViewItem (( void * ) node -> GetNthChild ( i ));
}
2018-09-17 15:12:13 +02:00
return wxDataViewItem ( 0 );
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetSettingsItem ( const wxDataViewItem & item ) const
2018-11-05 13:53:30 +01:00
{
return GetItemByType ( item , itSettings );
}
2019-04-25 01:45:00 +02:00
wxDataViewItem ObjectDataViewModel :: GetInstanceRootItem ( const wxDataViewItem & item ) const
2018-11-05 13:53:30 +01:00
{
return GetItemByType ( item , itInstanceRoot );
}
2019-05-31 10:54:52 +02:00
wxDataViewItem ObjectDataViewModel :: GetLayerRootItem ( const wxDataViewItem & item ) const
{
return GetItemByType ( item , itLayerRoot );
}
2019-04-25 01:45:00 +02:00
bool ObjectDataViewModel :: IsSettingsItem ( const wxDataViewItem & item ) const
2018-09-17 15:12:13 +02:00
{
if ( ! item . IsOk ())
return false ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-10-16 11:08:37 +02:00
return node -> m_type == itSettings ;
2018-09-17 15:12:13 +02:00
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: UpdateSettingsDigest ( const wxDataViewItem & item ,
2018-09-17 15:12:13 +02:00
const std :: vector < std :: string >& categories )
{
if ( ! item . IsOk ()) return ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2018-09-17 15:12:13 +02:00
if ( ! node -> update_settings_digest ( categories ))
return ;
ItemChanged ( item );
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: SetVolumeType ( const wxDataViewItem & item , const Slic3r :: ModelVolumeType type )
2018-11-02 23:27:31 +01:00
{
if ( ! item . IsOk () || GetItemType ( item ) != itVolume )
return ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
2019-02-22 12:12:10 +01:00
node -> SetBitmap ( * m_volume_bmps [ int ( type )]);
2018-11-02 23:27:31 +01:00
ItemChanged ( item );
}
2019-08-05 08:44:55 +02:00
wxDataViewItem ObjectDataViewModel :: SetPrintableState (
2019-08-05 10:05:28 +02:00
PrintIndicator printable ,
2019-08-05 08:44:55 +02:00
int obj_idx ,
int subobj_idx /* = -1*/ ,
ItemType subobj_type /* = itInstance*/ )
{
wxDataViewItem item = wxDataViewItem ( 0 );
if ( subobj_idx < 0 )
item = GetItemById ( obj_idx );
else
item = subobj_type & itInstance ? GetItemByInstanceId ( obj_idx , subobj_idx ) :
GetItemByVolumeId ( obj_idx , subobj_idx );
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
if ( ! node )
return wxDataViewItem ( 0 );
node -> set_printable_icon ( printable );
2019-08-05 10:05:28 +02:00
ItemChanged ( item );
2019-08-05 08:44:55 +02:00
2019-08-07 14:11:41 +02:00
if ( subobj_idx >= 0 )
UpdateObjectPrintable ( GetItemById ( obj_idx ));
2019-08-05 08:44:55 +02:00
return item ;
}
2019-08-07 14:11:41 +02:00
wxDataViewItem ObjectDataViewModel :: SetObjectPrintableState (
PrintIndicator printable ,
wxDataViewItem obj_item )
{
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) obj_item . GetID ();
if ( ! node )
return wxDataViewItem ( 0 );
node -> set_printable_icon ( printable );
ItemChanged ( obj_item );
UpdateInstancesPrintable ( obj_item );
return obj_item ;
}
2019-04-25 01:45:00 +02:00
void ObjectDataViewModel :: Rescale ()
2019-04-16 10:05:45 +02:00
{
wxDataViewItemArray all_items ;
GetAllChildren ( wxDataViewItem ( 0 ), all_items );
for ( wxDataViewItem item : all_items )
{
if ( ! item . IsOk ())
continue ;
2019-04-25 01:45:00 +02:00
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
node -> msw_rescale ();
2019-04-16 10:05:45 +02:00
2019-09-17 13:41:44 +02:00
switch ( node -> m_type )
{
case itObject :
if ( node -> m_bmp . IsOk ()) node -> m_bmp = * m_warning_bmp ;
break ;
case itVolume :
2019-04-29 15:27:59 +02:00
node -> m_bmp = GetVolumeIcon ( node -> m_volume_type , node -> m_bmp . GetWidth () != node -> m_bmp . GetHeight ());
2019-09-17 13:41:44 +02:00
break ;
case itLayerRoot :
node -> m_bmp = create_scaled_bitmap ( nullptr , LAYER_ROOT_ICON ); // FIXME: pass window ptr
break ;
case itLayer :
node -> m_bmp = create_scaled_bitmap ( nullptr , LAYER_ICON ); // FIXME: pass window ptr
break ;
default : break ;
}
2019-04-16 10:05:45 +02:00
ItemChanged ( item );
}
}
2019-04-29 15:27:59 +02:00
wxBitmap ObjectDataViewModel :: GetVolumeIcon ( const Slic3r :: ModelVolumeType vol_type , const bool is_marked /* = false*/ )
{
if ( ! is_marked )
return * m_volume_bmps [ static_cast < int > ( vol_type )];
std :: string scaled_bitmap_name = "warning" + std :: to_string ( static_cast < int > ( vol_type ));
scaled_bitmap_name += "-em" + std :: to_string ( Slic3r :: GUI :: wxGetApp (). em_unit ());
wxBitmap * bmp = m_bitmap_cache -> find ( scaled_bitmap_name );
if ( bmp == nullptr ) {
std :: vector < wxBitmap > bmps ;
bmps . emplace_back ( * m_warning_bmp );
bmps . emplace_back ( * m_volume_bmps [ static_cast < int > ( vol_type )]);
bmp = m_bitmap_cache -> insert ( scaled_bitmap_name , bmps );
}
return * bmp ;
}
void ObjectDataViewModel :: DeleteWarningIcon ( const wxDataViewItem & item , const bool unmark_object /* = false*/ )
{
if ( ! item . IsOk ())
return ;
ObjectDataViewModelNode * node = ( ObjectDataViewModelNode * ) item . GetID ();
if ( ! node -> GetBitmap (). IsOk () || ! ( node -> GetType () & ( itVolume | itObject )))
return ;
if ( node -> GetType () & itVolume ) {
node -> SetBitmap ( * m_volume_bmps [ static_cast < int > ( node -> volume_type ())]);
return ;
}
node -> SetBitmap ( wxNullBitmap );
if ( unmark_object )
{
wxDataViewItemArray children ;
GetChildren ( item , children );
for ( const wxDataViewItem & child : children )
DeleteWarningIcon ( child );
}
}
2018-10-15 13:25:22 +02:00
//-----------------------------------------------------------------------------
2019-09-30 14:03:50 +02:00
// DataViewBitmapText
2018-10-15 13:25:22 +02:00
//-----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
wxIMPLEMENT_DYNAMIC_CLASS ( DataViewBitmapText , wxObject )
2018-10-15 13:25:22 +02:00
2019-04-25 01:45:00 +02:00
IMPLEMENT_VARIANT_OBJECT ( DataViewBitmapText )
2018-10-15 13:25:22 +02:00
2018-09-17 15:12:13 +02:00
// ---------------------------------------------------------
2019-04-25 01:45:00 +02:00
// BitmapTextRenderer
2018-09-17 15:12:13 +02:00
// ---------------------------------------------------------
2018-12-17 08:37:50 +01:00
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
2019-04-25 01:45:00 +02:00
BitmapTextRenderer :: BitmapTextRenderer ( wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/ ,
2018-12-17 08:37:50 +01:00
int align /*= wxDVR_DEFAULT_ALIGNMENT*/ ) :
wxDataViewRenderer ( wxT ( "PrusaDataViewBitmapText" ), mode , align )
{
SetMode ( mode );
SetAlignment ( align );
}
#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
2019-04-25 01:45:00 +02:00
bool BitmapTextRenderer :: SetValue ( const wxVariant & value )
2018-09-17 15:12:13 +02:00
{
m_value << value ;
return true ;
}
2019-04-25 01:45:00 +02:00
bool BitmapTextRenderer :: GetValue ( wxVariant & WXUNUSED ( value )) const
2018-09-17 15:12:13 +02:00
{
return false ;
}
2018-12-17 08:37:50 +01:00
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
2019-04-25 01:45:00 +02:00
wxString BitmapTextRenderer :: GetAccessibleDescription () const
2018-12-14 16:07:28 +01:00
{
return m_value . GetText ();
}
2018-12-17 08:37:50 +01:00
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
2018-12-14 16:07:28 +01:00
2019-04-25 01:45:00 +02:00
bool BitmapTextRenderer :: Render ( wxRect rect , wxDC * dc , int state )
2018-09-17 15:12:13 +02:00
{
int xoffset = 0 ;
const wxBitmap & icon = m_value . GetBitmap ();
if ( icon . IsOk ())
{
dc -> DrawBitmap ( icon , rect . x , rect . y + ( rect . height - icon . GetHeight ()) / 2 );
xoffset = icon . GetWidth () + 4 ;
}
RenderText ( m_value . GetText (), xoffset , rect , dc , state );
return true ;
}
2019-04-25 01:45:00 +02:00
wxSize BitmapTextRenderer :: GetSize () const
2018-09-17 15:12:13 +02:00
{
if ( ! m_value . GetText (). empty ())
{
wxSize size = GetTextExtent ( m_value . GetText ());
if ( m_value . GetBitmap (). IsOk ())
size . x += m_value . GetBitmap (). GetWidth () + 4 ;
return size ;
}
return wxSize ( 80 , 20 );
}
2018-08-29 11:21:22 +02:00
2019-04-25 01:45:00 +02:00
wxWindow * BitmapTextRenderer :: CreateEditorCtrl ( wxWindow * parent , wxRect labelRect , const wxVariant & value )
2018-12-12 13:18:38 +01:00
{
wxDataViewCtrl * const dv_ctrl = GetOwner () -> GetOwner ();
2019-04-25 01:45:00 +02:00
ObjectDataViewModel * const model = dynamic_cast < ObjectDataViewModel *> ( dv_ctrl -> GetModel ());
2018-12-12 13:18:38 +01:00
if ( ! ( model -> GetItemType ( dv_ctrl -> GetSelection ()) & ( itVolume | itObject )) )
return nullptr ;
2019-04-25 01:45:00 +02:00
DataViewBitmapText data ;
2018-12-12 13:18:38 +01:00
data << value ;
2018-12-14 16:07:28 +01:00
2018-12-12 14:35:18 +01:00
m_was_unusable_symbol = false ;
2018-12-12 13:18:38 +01:00
wxPoint position = labelRect . GetPosition ();
2018-12-14 16:07:28 +01:00
if ( data . GetBitmap (). IsOk ()) {
const int bmp_width = data . GetBitmap (). GetWidth ();
2018-12-12 13:18:38 +01:00
position . x += bmp_width ;
labelRect . SetWidth ( labelRect . GetWidth () - bmp_width );
}
wxTextCtrl * text_editor = new wxTextCtrl ( parent , wxID_ANY , data . GetText (),
position , labelRect . GetSize (), wxTE_PROCESS_ENTER );
text_editor -> SetInsertionPointEnd ();
2018-12-14 16:07:28 +01:00
text_editor -> SelectAll ();
2018-12-12 13:18:38 +01:00
return text_editor ;
}
2019-04-25 01:45:00 +02:00
bool BitmapTextRenderer :: GetValueFromEditorCtrl ( wxWindow * ctrl , wxVariant & value )
2018-12-12 13:18:38 +01:00
{
wxTextCtrl * text_editor = wxDynamicCast ( ctrl , wxTextCtrl );
2018-12-12 14:35:18 +01:00
if ( ! text_editor || text_editor -> GetValue (). IsEmpty ())
2018-12-12 13:18:38 +01:00
return false ;
2018-12-12 14:35:18 +01:00
std :: string chosen_name = Slic3r :: normalize_utf8_nfc ( text_editor -> GetValue (). ToUTF8 ());
const char * unusable_symbols = "<>:/ \\ |?* \" " ;
for ( size_t i = 0 ; i < std :: strlen ( unusable_symbols ); i ++ ) {
if ( chosen_name . find_first_of ( unusable_symbols [ i ]) != std :: string :: npos ) {
m_was_unusable_symbol = true ;
return false ;
}
}
2018-12-14 16:07:28 +01:00
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld ;
2019-08-01 14:58:04 +02:00
GetView () -> GetModel () -> GetValue ( valueOld , m_item , colName );
2018-12-14 16:07:28 +01:00
2019-04-25 01:45:00 +02:00
DataViewBitmapText bmpText ;
2018-12-14 16:07:28 +01:00
bmpText << valueOld ;
// But replace the text with the value entered by user.
bmpText . SetText ( text_editor -> GetValue ());
value << bmpText ;
2018-12-12 13:18:38 +01:00
return true ;
}
2019-09-30 14:03:50 +02:00
// ----------------------------------------------------------------------------
// BitmapChoiseRenderer
// ----------------------------------------------------------------------------
bool BitmapChoiseRenderer :: SetValue ( const wxVariant & value )
{
m_value << value ;
return true ;
}
bool BitmapChoiseRenderer :: GetValue ( wxVariant & value ) const
{
value << m_value ;
return true ;
}
bool BitmapChoiseRenderer :: Render ( wxRect rect , wxDC * dc , int state )
{
int xoffset = 0 ;
const wxBitmap & icon = m_value . GetBitmap ();
if ( icon . IsOk ())
{
dc -> DrawBitmap ( icon , rect . x , rect . y + ( rect . height - icon . GetHeight ()) / 2 );
xoffset = icon . GetWidth () + 4 ;
}
RenderText ( m_value . GetText (), xoffset , rect , dc , state );
return true ;
}
wxSize BitmapChoiseRenderer :: GetSize () const
{
if ( ! m_value . GetText (). empty ())
{
wxSize size = GetTextExtent ( m_value . GetText ());
if ( m_value . GetBitmap (). IsOk ())
size . x += m_value . GetBitmap (). GetWidth () + 4 ;
return size ;
}
return wxSize ( 80 , 20 );
/* from wxDataViewChoiceRenderer
wxSize sz;
for ( wxArrayString::const_iterator i = m_choices.begin(); i != m_choices.end(); ++i )
sz.IncTo(GetTextExtent(*i));
// Allow some space for the right-side button, which is approximately the
// size of a scrollbar (and getting pixel-exact value would be complicated).
// Also add some whitespace between the text and the button:
sz.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
sz.x += GetTextExtent("M").x;
return sz;
*/
}
static void update_extruder_color_icons_in_cache ()
{
// Create the bitmap with color bars.
std :: vector < wxBitmap *> bmps ;
std :: vector < std :: string > colors = Slic3r :: GUI :: wxGetApp (). plater () -> get_extruder_colors_from_plater_config ();
unsigned char rgb [ 3 ];
/* It's supposed that standard size of an icon is 36px*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 = Slic3r :: GUI :: wxGetApp (). em_unit ();
const int icon_width = lround ( 3.6 * em );
const int icon_height = lround ( 1.6 * em );
for ( const std :: string & color : colors )
{
wxBitmap * bitmap = m_bitmap_cache -> find ( color );
if ( bitmap == nullptr ) {
// Paint the color icon.
Slic3r :: PresetBundle :: parse_color ( color , rgb );
bitmap = m_bitmap_cache -> insert ( color , m_bitmap_cache -> mksolid ( icon_width , icon_height , rgb ));
}
bmps . emplace_back ( bitmap );
}
}
wxWindow * BitmapChoiseRenderer :: CreateEditorCtrl ( wxWindow * parent , wxRect labelRect , const wxVariant & value )
{
wxDataViewCtrl * const dv_ctrl = GetOwner () -> GetOwner ();
ObjectDataViewModel * const model = dynamic_cast < ObjectDataViewModel *> ( dv_ctrl -> GetModel ());
if ( ! ( model -> GetItemType ( dv_ctrl -> GetSelection ()) & ( itVolume | itObject )))
return nullptr ;
DataViewBitmapText data ;
data << value ;
// m_was_unusable_symbol = false;
wxPoint position = labelRect . GetPosition ();
if ( data . GetBitmap (). IsOk ()) {
const int bmp_width = data . GetBitmap (). GetWidth ();
position . x += bmp_width ;
labelRect . SetWidth ( labelRect . GetWidth () - bmp_width );
}
auto c_editor = new wxBitmapComboBox ( parent , wxID_ANY , wxEmptyString , //data.GetText(),
labelRect . GetTopLeft (), wxSize ( labelRect . GetWidth (), - 1 ),
0 , nullptr , wxCB_READONLY );
c_editor -> Move ( labelRect . GetRight () - c_editor -> GetRect (). width , wxDefaultCoord );
c_editor -> SetStringSelection ( data . GetText ());
return c_editor ;
}
bool BitmapChoiseRenderer :: GetValueFromEditorCtrl ( wxWindow * ctrl , wxVariant & value )
{
wxBitmapComboBox * c = ( wxBitmapComboBox * ) ctrl ;
int selection = c -> GetSelection ();
if ( selection < 0 )
return false ;
DataViewBitmapText bmpText ;
bmpText . SetText ( c -> GetString ( selection ));
bmpText . SetBitmap ( c -> GetItemBitmap ( selection ));
value << bmpText ;
return true ;
}
2018-08-29 11:21:22 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// DoubleSlider
2018-08-29 11:21:22 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
DoubleSlider :: DoubleSlider ( wxWindow * parent ,
wxWindowID id ,
int lowerValue ,
int higherValue ,
int minValue ,
int maxValue ,
const wxPoint & pos ,
const wxSize & size ,
long style ,
const wxValidator & val ,
const wxString & name ) :
2018-08-21 17:47:05 +02:00
wxControl ( parent , id , pos , size , wxWANTS_CHARS | wxBORDER_NONE ),
2018-08-21 02:03:10 +02:00
m_lower_value ( lowerValue ), m_higher_value ( higherValue ),
m_min_value ( minValue ), m_max_value ( maxValue ),
2018-08-22 10:44:11 +02:00
m_style ( style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style : wxSL_HORIZONTAL )
2018-08-21 02:03:10 +02:00
{
2019-02-26 15:51:23 +01:00
#ifdef __WXOSX__
is_osx = true ;
2018-08-22 08:54:07 +02:00
#endif //__WXOSX__
2019-02-26 15:51:23 +01:00
if ( ! is_osx )
SetDoubleBuffered ( true ); // SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
2018-08-21 02:03:10 +02:00
2019-04-25 01:45:00 +02:00
m_bmp_thumb_higher = ( style == wxSL_HORIZONTAL ? ScalableBitmap ( this , "right_half_circle.png" ) : ScalableBitmap ( this , "up_half_circle.png" , 16 , true ));
m_bmp_thumb_lower = ( style == wxSL_HORIZONTAL ? ScalableBitmap ( this , "left_half_circle.png" ) : ScalableBitmap ( this , "down_half_circle.png" , 16 , true ));
2019-04-16 10:05:45 +02:00
m_thumb_size = m_bmp_thumb_lower . bmp (). GetSize ();
2018-08-23 13:01:18 +02:00
2019-04-25 01:45:00 +02:00
m_bmp_add_tick_on = ScalableBitmap ( this , "colorchange_add_on.png" );
m_bmp_add_tick_off = ScalableBitmap ( this , "colorchange_add_off.png" );
m_bmp_del_tick_on = ScalableBitmap ( this , "colorchange_delete_on.png" );
m_bmp_del_tick_off = ScalableBitmap ( this , "colorchange_delete_off.png" );
2019-04-16 10:05:45 +02:00
m_tick_icon_dim = m_bmp_add_tick_on . bmp (). GetSize (). x ;
2018-08-24 13:34:38 +02:00
2019-04-25 01:45:00 +02:00
m_bmp_one_layer_lock_on = ScalableBitmap ( this , "one_layer_lock_on.png" );
m_bmp_one_layer_lock_off = ScalableBitmap ( this , "one_layer_lock_off.png" );
m_bmp_one_layer_unlock_on = ScalableBitmap ( this , "one_layer_unlock_on.png" );
m_bmp_one_layer_unlock_off = ScalableBitmap ( this , "one_layer_unlock_off.png" );
2019-04-16 10:05:45 +02:00
m_lock_icon_dim = m_bmp_one_layer_lock_on . bmp (). GetSize (). x ;
2018-08-23 13:01:18 +02:00
2019-06-24 13:11:18 +02:00
m_bmp_revert = ScalableBitmap ( this , "undo" );
m_revert_icon_dim = m_bmp_revert . bmp (). GetSize (). x ;
2018-08-21 02:03:10 +02:00
m_selection = ssUndef ;
// slider events
2019-04-25 01:45:00 +02:00
Bind ( wxEVT_PAINT , & DoubleSlider :: OnPaint , this );
2019-06-19 16:28:56 +02:00
Bind ( wxEVT_CHAR , & DoubleSlider :: OnChar , this );
2019-04-25 01:45:00 +02:00
Bind ( wxEVT_LEFT_DOWN , & DoubleSlider :: OnLeftDown , this );
Bind ( wxEVT_MOTION , & DoubleSlider :: OnMotion , this );
Bind ( wxEVT_LEFT_UP , & DoubleSlider :: OnLeftUp , this );
Bind ( wxEVT_MOUSEWHEEL , & DoubleSlider :: OnWheel , this );
Bind ( wxEVT_ENTER_WINDOW , & DoubleSlider :: OnEnterWin , this );
Bind ( wxEVT_LEAVE_WINDOW , & DoubleSlider :: OnLeaveWin , this );
Bind ( wxEVT_KEY_DOWN , & DoubleSlider :: OnKeyDown , this );
Bind ( wxEVT_KEY_UP , & DoubleSlider :: OnKeyUp , this );
Bind ( wxEVT_RIGHT_DOWN , & DoubleSlider :: OnRightDown , this );
Bind ( wxEVT_RIGHT_UP , & DoubleSlider :: OnRightUp , this );
2018-08-21 02:03:10 +02:00
// control's view variables
2019-04-16 10:05:45 +02:00
SLIDER_MARGIN = 4 + Slic3r :: GUI :: wxGetApp (). em_unit ();
2018-08-21 02:03:10 +02:00
DARK_ORANGE_PEN = wxPen ( wxColour ( 253 , 84 , 2 ));
ORANGE_PEN = wxPen ( wxColour ( 253 , 126 , 66 ));
LIGHT_ORANGE_PEN = wxPen ( wxColour ( 254 , 177 , 139 ));
DARK_GREY_PEN = wxPen ( wxColour ( 128 , 128 , 128 ));
GREY_PEN = wxPen ( wxColour ( 164 , 164 , 164 ));
LIGHT_GREY_PEN = wxPen ( wxColour ( 204 , 204 , 204 ));
2019-04-25 01:45:00 +02:00
m_line_pens = { & DARK_GREY_PEN , & GREY_PEN , & LIGHT_GREY_PEN };
m_segm_pens = { & DARK_ORANGE_PEN , & ORANGE_PEN , & LIGHT_ORANGE_PEN };
2019-02-26 15:51:23 +01:00
2019-02-27 11:05:59 +01:00
const wxFont & font = GetFont ();
2019-02-26 15:51:23 +01:00
m_font = is_osx ? font . Smaller (). Smaller () : font . Smaller ();
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: msw_rescale ()
2019-04-16 10:05:45 +02:00
{
const wxFont & font = Slic3r :: GUI :: wxGetApp (). normal_font ();
m_font = is_osx ? font . Smaller (). Smaller () : font . Smaller ();
2019-04-25 01:45:00 +02:00
m_bmp_thumb_higher . msw_rescale ();
m_bmp_thumb_lower . msw_rescale ();
2019-04-16 10:05:45 +02:00
m_thumb_size = m_bmp_thumb_lower . bmp (). GetSize ();
2019-04-25 01:45:00 +02:00
m_bmp_add_tick_on . msw_rescale ();
m_bmp_add_tick_off . msw_rescale ();
m_bmp_del_tick_on . msw_rescale ();
m_bmp_del_tick_off . msw_rescale ();
2019-04-16 10:05:45 +02:00
m_tick_icon_dim = m_bmp_add_tick_on . bmp (). GetSize (). x ;
2019-04-25 01:45:00 +02:00
m_bmp_one_layer_lock_on . msw_rescale ();
m_bmp_one_layer_lock_off . msw_rescale ();
m_bmp_one_layer_unlock_on . msw_rescale ();
m_bmp_one_layer_unlock_off . msw_rescale ();
2019-04-16 10:05:45 +02:00
m_lock_icon_dim = m_bmp_one_layer_lock_on . bmp (). GetSize (). x ;
2019-06-24 13:11:18 +02:00
m_bmp_revert . msw_rescale ();
m_revert_icon_dim = m_bmp_revert . bmp (). GetSize (). x ;
2019-04-16 10:05:45 +02:00
SLIDER_MARGIN = 4 + Slic3r :: GUI :: wxGetApp (). em_unit ();
SetMinSize ( get_min_size ());
GetParent () -> Layout ();
}
2019-04-25 01:45:00 +02:00
int DoubleSlider :: GetActiveValue () const
2018-08-23 14:37:17 +02:00
{
return m_selection == ssLower ?
m_lower_value : m_selection == ssHigher ?
m_higher_value : - 1 ;
}
2019-04-25 01:45:00 +02:00
wxSize DoubleSlider :: get_min_size () const
2019-04-16 10:05:45 +02:00
{
const int min_side = is_horizontal () ?
( is_osx ? 8 : 6 ) * Slic3r :: GUI :: wxGetApp (). em_unit () :
/*(is_osx ? 10 : 8)*/ 10 * Slic3r :: GUI :: wxGetApp (). em_unit ();
return wxSize ( min_side , min_side );
}
2019-04-25 01:45:00 +02:00
wxSize DoubleSlider :: DoGetBestSize () const
2018-08-22 10:44:11 +02:00
{
2018-08-22 14:24:30 +02:00
const wxSize size = wxControl :: DoGetBestSize ();
2018-08-22 10:44:11 +02:00
if ( size . x > 1 && size . y > 1 )
return size ;
2019-04-16 10:05:45 +02:00
return get_min_size ();
2018-08-22 10:44:11 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: SetLowerValue ( const int lower_val )
2018-08-21 02:03:10 +02:00
{
2018-09-17 15:12:13 +02:00
m_selection = ssLower ;
2018-08-21 02:03:10 +02:00
m_lower_value = lower_val ;
2018-09-17 15:12:13 +02:00
correct_lower_value ();
2018-08-21 02:03:10 +02:00
Refresh ();
Update ();
2018-09-17 15:12:13 +02:00
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
ProcessWindowEvent ( e );
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: SetHigherValue ( const int higher_val )
2018-08-21 02:03:10 +02:00
{
2018-09-17 15:12:13 +02:00
m_selection = ssHigher ;
2018-08-21 02:03:10 +02:00
m_higher_value = higher_val ;
2018-09-17 15:12:13 +02:00
correct_higher_value ();
2018-08-21 02:03:10 +02:00
Refresh ();
Update ();
2018-09-17 15:12:13 +02:00
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
ProcessWindowEvent ( e );
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: SetSelectionSpan ( const int lower_val , const int higher_val )
2018-12-20 10:55:50 +01:00
{
m_lower_value = std :: max ( lower_val , m_min_value );
m_higher_value = std :: max ( std :: min ( higher_val , m_max_value ), m_lower_value );
if ( m_lower_value < m_higher_value )
m_is_one_layer = false ;
Refresh ();
Update ();
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
ProcessWindowEvent ( e );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: SetMaxValue ( const int max_value )
2018-08-23 14:37:17 +02:00
{
m_max_value = max_value ;
Refresh ();
Update ();
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_scroll_line ( wxDC & dc , const int lower_pos , const int higher_pos )
2018-08-21 02:03:10 +02:00
{
int width ;
int height ;
2018-08-24 13:34:38 +02:00
get_size ( & width , & height );
2018-08-21 02:03:10 +02:00
wxCoord line_beg_x = is_horizontal () ? SLIDER_MARGIN : width * 0.5 - 1 ;
wxCoord line_beg_y = is_horizontal () ? height * 0.5 - 1 : SLIDER_MARGIN ;
wxCoord line_end_x = is_horizontal () ? width - SLIDER_MARGIN + 1 : width * 0.5 - 1 ;
wxCoord line_end_y = is_horizontal () ? height * 0.5 - 1 : height - SLIDER_MARGIN + 1 ;
wxCoord segm_beg_x = is_horizontal () ? lower_pos : width * 0.5 - 1 ;
2019-02-26 15:51:23 +01:00
wxCoord segm_beg_y = is_horizontal () ? height * 0.5 - 1 : lower_pos /*-1*/ ;
2018-08-21 02:03:10 +02:00
wxCoord segm_end_x = is_horizontal () ? higher_pos : width * 0.5 - 1 ;
wxCoord segm_end_y = is_horizontal () ? height * 0.5 - 1 : higher_pos - 1 ;
2019-09-06 17:46:55 +02:00
for ( size_t id = 0 ; id < m_line_pens . size (); id ++ )
2018-08-21 02:03:10 +02:00
{
2019-04-25 01:45:00 +02:00
dc . SetPen ( * m_line_pens [ id ]);
2018-08-21 02:03:10 +02:00
dc . DrawLine ( line_beg_x , line_beg_y , line_end_x , line_end_y );
2019-04-25 01:45:00 +02:00
dc . SetPen ( * m_segm_pens [ id ]);
2018-08-21 02:03:10 +02:00
dc . DrawLine ( segm_beg_x , segm_beg_y , segm_end_x , segm_end_y );
if ( is_horizontal ())
line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1 ;
else
line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1 ;
}
}
2019-04-25 01:45:00 +02:00
double DoubleSlider :: get_scroll_step ()
2018-08-21 02:03:10 +02:00
{
2018-08-24 13:34:38 +02:00
const wxSize sz = get_size ();
2018-08-21 02:03:10 +02:00
const int & slider_len = m_style == wxSL_HORIZONTAL ? sz . x : sz . y ;
return double ( slider_len - SLIDER_MARGIN * 2 ) / ( m_max_value - m_min_value );
}
2018-08-22 18:00:48 +02:00
// get position on the slider line from entered value
2019-04-25 01:45:00 +02:00
wxCoord DoubleSlider :: get_position_from_value ( const int value )
2018-08-22 18:00:48 +02:00
{
const double step = get_scroll_step ();
const int val = is_horizontal () ? value : m_max_value - value ;
return wxCoord ( SLIDER_MARGIN + int ( val * step + 0.5 ));
}
2019-04-25 01:45:00 +02:00
wxSize DoubleSlider :: get_size ()
2018-08-24 13:34:38 +02:00
{
int w , h ;
get_size ( & w , & h );
return wxSize ( w , h );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: get_size ( int * w , int * h )
2018-08-24 13:34:38 +02:00
{
GetSize ( w , h );
is_horizontal () ? * w -= m_lock_icon_dim : * h -= m_lock_icon_dim ;
}
2019-04-25 01:45:00 +02:00
double DoubleSlider :: get_double_value ( const SelectedSlider & selection )
2018-09-17 15:12:13 +02:00
{
2018-11-28 12:32:43 +01:00
if ( m_values . empty () || m_lower_value < 0 )
2018-09-17 15:12:13 +02:00
return 0.0 ;
2018-11-27 12:19:39 +01:00
if ( m_values . size () <= m_higher_value ) {
correct_higher_value ();
2019-09-05 11:47:04 +02:00
return m_values . back ();
2018-11-27 12:19:39 +01:00
}
2019-09-05 11:47:04 +02:00
return m_values [ selection == ssLower ? m_lower_value : m_higher_value ];
2018-09-17 15:12:13 +02:00
}
2019-04-25 01:45:00 +02:00
std :: vector < double > DoubleSlider :: GetTicksValues () const
2018-11-07 14:44:47 +01:00
{
std :: vector < double > values ;
2019-09-04 08:53:21 +02:00
const int val_size = m_values . size ();
2018-11-07 14:44:47 +01:00
if ( ! m_values . empty ())
2019-09-04 08:53:21 +02:00
for ( int tick : m_ticks ) {
if ( tick > val_size )
2019-02-26 15:51:23 +01:00
break ;
2019-09-05 11:47:04 +02:00
values . push_back ( m_values [ tick ]);
2019-02-26 15:51:23 +01:00
}
2018-11-07 14:44:47 +01:00
return values ;
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: SetTicksValues ( const std :: vector < double >& heights )
2018-11-07 14:44:47 +01:00
{
if ( m_values . empty ())
return ;
2019-02-27 13:37:16 +01:00
const bool was_empty = m_ticks . empty ();
2018-11-07 14:44:47 +01:00
m_ticks . clear ();
for ( auto h : heights ) {
2019-09-05 11:47:04 +02:00
auto it = std :: lower_bound ( m_values . begin (), m_values . end (), h - epsilon ());
if ( it == m_values . end ())
continue ;
m_ticks . insert ( it - m_values . begin ());
2018-11-07 14:44:47 +01:00
}
2019-02-27 13:37:16 +01:00
if ( ! was_empty && m_ticks . empty ())
// Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one
wxPostEvent ( this -> GetParent (), wxCommandEvent ( wxCUSTOMEVT_TICKSCHANGED ));
2018-11-07 14:44:47 +01:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: get_lower_and_higher_position ( int & lower_pos , int & higher_pos )
2018-08-21 02:03:10 +02:00
{
const double step = get_scroll_step ();
if ( is_horizontal ()) {
lower_pos = SLIDER_MARGIN + int ( m_lower_value * step + 0.5 );
higher_pos = SLIDER_MARGIN + int ( m_higher_value * step + 0.5 );
}
else {
lower_pos = SLIDER_MARGIN + int (( m_max_value - m_lower_value ) * step + 0.5 );
higher_pos = SLIDER_MARGIN + int (( m_max_value - m_higher_value ) * step + 0.5 );
}
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_focus_rect ()
2018-08-21 17:47:05 +02:00
{
if ( ! m_is_focused )
return ;
const wxSize sz = GetSize ();
wxPaintDC dc ( this );
const wxPen pen = wxPen ( wxColour ( 128 , 128 , 10 ), 1 , wxPENSTYLE_DOT );
dc . SetPen ( pen );
dc . SetBrush ( wxBrush ( wxColour ( 0 , 0 , 0 ), wxBRUSHSTYLE_TRANSPARENT ));
2018-08-24 13:34:38 +02:00
dc . DrawRectangle ( 1 , 1 , sz . x - 2 , sz . y - 2 );
2018-08-21 17:47:05 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: render ()
2018-08-21 02:03:10 +02:00
{
SetBackgroundColour ( GetParent () -> GetBackgroundColour ());
2018-08-21 17:47:05 +02:00
draw_focus_rect ();
2018-08-21 02:03:10 +02:00
2018-08-23 13:01:18 +02:00
wxPaintDC dc ( this );
2019-02-26 15:51:23 +01:00
dc . SetFont ( m_font );
2018-08-21 02:03:10 +02:00
2018-08-22 18:00:48 +02:00
const wxCoord lower_pos = get_position_from_value ( m_lower_value );
const wxCoord higher_pos = get_position_from_value ( m_higher_value );
2018-08-21 02:03:10 +02:00
2019-01-10 17:24:23 +01:00
// draw colored band on the background of a scroll line
// and only in a case of no-empty m_values
2019-01-30 14:45:18 +01:00
draw_colored_band ( dc );
2019-01-10 17:24:23 +01:00
2018-08-21 02:03:10 +02:00
// draw line
draw_scroll_line ( dc , lower_pos , higher_pos );
2018-08-24 13:34:38 +02:00
// //lower slider:
// draw_thumb(dc, lower_pos, ssLower);
// //higher slider:
// draw_thumb(dc, higher_pos, ssHigher);
2018-08-21 02:03:10 +02:00
2018-08-24 13:34:38 +02:00
// draw both sliders
draw_thumbs ( dc , lower_pos , higher_pos );
2018-08-22 18:00:48 +02:00
//draw color print ticks
draw_ticks ( dc );
2018-08-24 13:34:38 +02:00
2019-06-24 13:11:18 +02:00
//draw lock/unlock
2018-08-24 13:34:38 +02:00
draw_one_layer_icon ( dc );
2019-06-24 13:11:18 +02:00
//draw revert bitmap (if it's shown)
draw_revert_icon ( dc );
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_action_icon ( wxDC & dc , const wxPoint pt_beg , const wxPoint pt_end )
2018-08-23 13:01:18 +02:00
{
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value ;
2019-04-25 01:45:00 +02:00
2019-09-02 16:46:37 +02:00
// suppress add tick on first layer
if ( tick == 0 )
return ;
2019-05-16 13:16:43 +02:00
wxBitmap * icon = m_is_action_icon_focesed ? & m_bmp_add_tick_off . bmp () : & m_bmp_add_tick_on . bmp ();
2018-08-23 13:01:18 +02:00
if ( m_ticks . find ( tick ) != m_ticks . end ())
2019-05-16 13:16:43 +02:00
icon = m_is_action_icon_focesed ? & m_bmp_del_tick_off . bmp () : & m_bmp_del_tick_on . bmp ();
2018-08-23 13:01:18 +02:00
wxCoord x_draw , y_draw ;
is_horizontal () ? x_draw = pt_beg . x - 0.5 * m_tick_icon_dim : y_draw = pt_beg . y - 0.5 * m_tick_icon_dim ;
if ( m_selection == ssLower )
is_horizontal () ? y_draw = pt_end . y + 3 : x_draw = pt_beg . x - m_tick_icon_dim - 2 ;
else
is_horizontal () ? y_draw = pt_beg . y - m_tick_icon_dim - 2 : x_draw = pt_end . x + 3 ;
2019-05-16 13:16:43 +02:00
dc . DrawBitmap ( * icon , x_draw , y_draw );
2018-08-23 13:01:18 +02:00
//update rect of the tick action icon
m_rect_tick_action = wxRect ( x_draw , y_draw , m_tick_icon_dim , m_tick_icon_dim );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_info_line_with_icon ( wxDC & dc , const wxPoint & pos , const SelectedSlider selection )
2018-08-21 17:47:05 +02:00
{
if ( m_selection == selection ) {
2018-08-23 13:01:18 +02:00
//draw info line
2018-08-21 17:47:05 +02:00
dc . SetPen ( DARK_ORANGE_PEN );
2019-02-26 15:51:23 +01:00
const wxPoint pt_beg = is_horizontal () ? wxPoint ( pos . x , pos . y - m_thumb_size . y ) : wxPoint ( pos . x - m_thumb_size . x , pos . y /* - 1*/ );
const wxPoint pt_end = is_horizontal () ? wxPoint ( pos . x , pos . y + m_thumb_size . y ) : wxPoint ( pos . x + m_thumb_size . x , pos . y /* - 1*/ );
2018-08-23 13:01:18 +02:00
dc . DrawLine ( pt_beg , pt_end );
//draw action icon
2018-11-28 16:02:29 +01:00
if ( m_is_enabled_tick_manipulation )
draw_action_icon ( dc , pt_beg , pt_end );
2018-08-21 17:47:05 +02:00
}
}
2019-04-25 01:45:00 +02:00
wxString DoubleSlider :: get_label ( const SelectedSlider & selection ) const
2018-08-21 17:47:05 +02:00
{
2018-08-22 14:24:30 +02:00
const int value = selection == ssLower ? m_lower_value : m_higher_value ;
2018-08-23 14:37:17 +02:00
if ( m_label_koef == 1.0 && m_values . empty ())
2018-08-23 13:01:18 +02:00
return wxString :: Format ( "%d" , value );
2018-11-01 14:16:03 +01:00
if ( value >= m_values . size ())
return "ErrVal" ;
2018-08-23 13:01:18 +02:00
2018-08-23 14:37:17 +02:00
const wxString str = m_values . empty () ?
wxNumberFormatter :: ToString ( m_label_koef * value , 2 , wxNumberFormatter :: Style_None ) :
2019-09-05 11:47:04 +02:00
wxNumberFormatter :: ToString ( m_values [ value ], 2 , wxNumberFormatter :: Style_None );
return wxString :: Format ( "%s \n (%d)" , str , m_values . empty () ? value : value + 1 );
2018-08-21 17:47:05 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_thumb_text ( wxDC & dc , const wxPoint & pos , const SelectedSlider & selection ) const
2018-08-21 02:03:10 +02:00
{
2019-09-03 10:27:16 +02:00
if ( selection == ssUndef ||
(( m_is_one_layer || m_higher_value == m_lower_value ) && selection != m_selection ) )
2018-08-24 13:34:38 +02:00
return ;
2018-08-22 14:24:30 +02:00
wxCoord text_width , text_height ;
const wxString label = get_label ( selection );
2018-08-23 13:01:18 +02:00
dc . GetMultiLineTextExtent ( label , & text_width , & text_height );
2018-08-22 14:24:30 +02:00
wxPoint text_pos ;
if ( selection == ssLower )
text_pos = is_horizontal () ? wxPoint ( pos . x + 1 , pos . y + m_thumb_size . x ) :
wxPoint ( pos . x + m_thumb_size . x + 1 , pos . y - 0.5 * text_height - 1 );
else
text_pos = is_horizontal () ? wxPoint ( pos . x - text_width - 1 , pos . y - m_thumb_size . x - text_height ) :
wxPoint ( pos . x - text_width - 1 - m_thumb_size . x , pos . y - 0.5 * text_height + 1 );
dc . DrawText ( label , text_pos );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_thumb_item ( wxDC & dc , const wxPoint & pos , const SelectedSlider & selection )
2018-08-22 14:24:30 +02:00
{
wxCoord x_draw , y_draw ;
if ( selection == ssLower ) {
if ( is_horizontal ()) {
x_draw = pos . x - m_thumb_size . x ;
y_draw = pos . y - int ( 0.5 * m_thumb_size . y );
}
else {
x_draw = pos . x - int ( 0.5 * m_thumb_size . x );
2019-02-26 15:51:23 +01:00
y_draw = pos . y + 1 ;
2018-08-22 14:24:30 +02:00
}
}
else {
if ( is_horizontal ()) {
x_draw = pos . x ;
y_draw = pos . y - int ( 0.5 * m_thumb_size . y );
}
else {
x_draw = pos . x - int ( 0.5 * m_thumb_size . x );
y_draw = pos . y - m_thumb_size . y ;
}
}
2019-04-16 10:05:45 +02:00
dc . DrawBitmap ( selection == ssLower ? m_bmp_thumb_lower . bmp () : m_bmp_thumb_higher . bmp (), x_draw , y_draw );
2018-08-22 14:24:30 +02:00
// Update thumb rect
update_thumb_rect ( x_draw , y_draw , selection );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_thumb ( wxDC & dc , const wxCoord & pos_coord , const SelectedSlider & selection )
2018-08-22 14:24:30 +02:00
{
//calculate thumb position on slider line
int width , height ;
2018-08-24 13:34:38 +02:00
get_size ( & width , & height );
2018-08-22 14:24:30 +02:00
const wxPoint pos = is_horizontal () ? wxPoint ( pos_coord , height * 0.5 ) : wxPoint ( 0.5 * width , pos_coord );
2018-08-21 02:03:10 +02:00
// Draw thumb
2018-08-22 14:24:30 +02:00
draw_thumb_item ( dc , pos , selection );
2018-08-21 17:47:05 +02:00
// Draw info_line
2018-08-23 13:01:18 +02:00
draw_info_line_with_icon ( dc , pos , selection );
2018-08-21 02:03:10 +02:00
// Draw thumb text
2018-08-22 14:24:30 +02:00
draw_thumb_text ( dc , pos , selection );
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_thumbs ( wxDC & dc , const wxCoord & lower_pos , const wxCoord & higher_pos )
2018-08-24 13:34:38 +02:00
{
//calculate thumb position on slider line
int width , height ;
get_size ( & width , & height );
const wxPoint pos_l = is_horizontal () ? wxPoint ( lower_pos , height * 0.5 ) : wxPoint ( 0.5 * width , lower_pos );
const wxPoint pos_h = is_horizontal () ? wxPoint ( higher_pos , height * 0.5 ) : wxPoint ( 0.5 * width , higher_pos );
// Draw lower thumb
draw_thumb_item ( dc , pos_l , ssLower );
// Draw lower info_line
draw_info_line_with_icon ( dc , pos_l , ssLower );
// Draw higher thumb
draw_thumb_item ( dc , pos_h , ssHigher );
// Draw higher info_line
draw_info_line_with_icon ( dc , pos_h , ssHigher );
// Draw higher thumb text
draw_thumb_text ( dc , pos_h , ssHigher );
// Draw lower thumb text
draw_thumb_text ( dc , pos_l , ssLower );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_ticks ( wxDC & dc )
2018-08-22 18:00:48 +02:00
{
2019-07-02 13:13:17 +02:00
if ( ! m_is_enabled_tick_manipulation )
return ;
2018-11-28 16:02:29 +01:00
dc . SetPen ( m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN );
2018-08-22 18:00:48 +02:00
int height , width ;
2018-08-24 13:34:38 +02:00
get_size ( & width , & height );
2018-08-22 18:00:48 +02:00
const wxCoord mid = is_horizontal () ? 0.5 * height : 0.5 * width ;
for ( auto tick : m_ticks )
{
const wxCoord pos = get_position_from_value ( tick );
is_horizontal () ? dc . DrawLine ( pos , mid - 14 , pos , mid - 9 ) :
2019-02-26 15:51:23 +01:00
dc . DrawLine ( mid - 14 , pos /* - 1*/ , mid - 9 , pos /* - 1*/ );
2018-08-22 18:00:48 +02:00
is_horizontal () ? dc . DrawLine ( pos , mid + 14 , pos , mid + 9 ) :
2019-02-26 15:51:23 +01:00
dc . DrawLine ( mid + 14 , pos /* - 1*/ , mid + 9 , pos /* - 1*/ );
2018-08-22 18:00:48 +02:00
}
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_colored_band ( wxDC & dc )
2019-01-10 17:24:23 +01:00
{
2019-07-02 13:13:17 +02:00
if ( ! m_is_enabled_tick_manipulation )
return ;
2019-01-10 17:24:23 +01:00
int height , width ;
get_size ( & width , & height );
wxRect main_band = m_rect_lower_thumb ;
if ( is_horizontal ()) {
main_band . SetLeft ( SLIDER_MARGIN );
main_band . SetRight ( width - SLIDER_MARGIN + 1 );
}
else {
const int cut = 2 ;
main_band . x += cut ;
main_band . width -= 2 * cut ;
main_band . SetTop ( SLIDER_MARGIN );
main_band . SetBottom ( height - SLIDER_MARGIN + 1 );
}
if ( m_ticks . empty ()) {
dc . SetPen ( GetParent () -> GetBackgroundColour ());
dc . SetBrush ( GetParent () -> GetBackgroundColour ());
dc . DrawRectangle ( main_band );
return ;
}
2019-01-30 14:45:18 +01:00
const std :: vector < std :: string >& colors = Slic3r :: GCodePreviewData :: ColorPrintColors ();
const size_t colors_cnt = colors . size ();
wxColour clr ( colors [ 0 ]);
2019-01-10 17:24:23 +01:00
dc . SetPen ( clr );
dc . SetBrush ( clr );
dc . DrawRectangle ( main_band );
2019-09-06 17:46:55 +02:00
size_t i = 1 ;
2019-01-10 17:24:23 +01:00
for ( auto tick : m_ticks )
{
2019-01-30 14:45:18 +01:00
if ( i == colors_cnt )
2019-01-10 17:24:23 +01:00
i = 0 ;
const wxCoord pos = get_position_from_value ( tick );
is_horizontal () ? main_band . SetLeft ( SLIDER_MARGIN + pos ) :
main_band . SetBottom ( pos - 1 );
2019-01-30 14:45:18 +01:00
clr = wxColour ( colors [ i ]);
2019-01-10 17:24:23 +01:00
dc . SetPen ( clr );
dc . SetBrush ( clr );
dc . DrawRectangle ( main_band );
i ++ ;
}
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: draw_one_layer_icon ( wxDC & dc )
2018-08-24 13:34:38 +02:00
{
2019-04-16 10:05:45 +02:00
const wxBitmap & icon = m_is_one_layer ?
m_is_one_layer_icon_focesed ? m_bmp_one_layer_lock_off . bmp () : m_bmp_one_layer_lock_on . bmp () :
m_is_one_layer_icon_focesed ? m_bmp_one_layer_unlock_off . bmp () : m_bmp_one_layer_unlock_on . bmp ();
2018-08-24 13:34:38 +02:00
int width , height ;
get_size ( & width , & height );
wxCoord x_draw , y_draw ;
is_horizontal () ? x_draw = width - 2 : x_draw = 0.5 * width - 0.5 * m_lock_icon_dim ;
is_horizontal () ? y_draw = 0.5 * height - 0.5 * m_lock_icon_dim : y_draw = height - 2 ;
2019-04-25 01:45:00 +02:00
dc . DrawBitmap ( icon , x_draw , y_draw );
2018-08-24 13:34:38 +02:00
//update rect of the lock/unlock icon
m_rect_one_layer_icon = wxRect ( x_draw , y_draw , m_lock_icon_dim , m_lock_icon_dim );
}
2019-06-24 13:11:18 +02:00
void DoubleSlider :: draw_revert_icon ( wxDC & dc )
{
2019-07-02 13:13:17 +02:00
if ( m_ticks . empty () || ! m_is_enabled_tick_manipulation )
2019-06-24 13:11:18 +02:00
return ;
int width , height ;
get_size ( & width , & height );
wxCoord x_draw , y_draw ;
is_horizontal () ? x_draw = width - 2 : x_draw = 0.25 * SLIDER_MARGIN ;
is_horizontal () ? y_draw = 0.25 * SLIDER_MARGIN : y_draw = height - 2 ;
dc . DrawBitmap ( m_bmp_revert . bmp (), x_draw , y_draw );
//update rect of the lock/unlock icon
m_rect_revert_icon = wxRect ( x_draw , y_draw , m_revert_icon_dim , m_revert_icon_dim );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: update_thumb_rect ( const wxCoord & begin_x , const wxCoord & begin_y , const SelectedSlider & selection )
2018-08-21 02:03:10 +02:00
{
2018-08-22 14:24:30 +02:00
const wxRect & rect = wxRect ( begin_x , begin_y , m_thumb_size . x , m_thumb_size . y );
if ( selection == ssLower )
m_rect_lower_thumb = rect ;
else
m_rect_higher_thumb = rect ;
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
int DoubleSlider :: get_value_from_position ( const wxCoord x , const wxCoord y )
2018-08-21 02:03:10 +02:00
{
2018-08-24 13:34:38 +02:00
const int height = get_size (). y ;
2018-08-21 02:03:10 +02:00
const double step = get_scroll_step ();
if ( is_horizontal ())
return int ( double ( x - SLIDER_MARGIN ) / step + 0.5 );
2019-06-24 13:11:18 +02:00
return int ( m_min_value + double ( height - SLIDER_MARGIN - y ) / step + 0.5 );
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: detect_selected_slider ( const wxPoint & pt )
2018-08-21 02:03:10 +02:00
{
m_selection = is_point_in_rect ( pt , m_rect_lower_thumb ) ? ssLower :
is_point_in_rect ( pt , m_rect_higher_thumb ) ? ssHigher : ssUndef ;
}
2019-04-25 01:45:00 +02:00
bool DoubleSlider :: is_point_in_rect ( const wxPoint & pt , const wxRect & rect )
2018-08-21 02:03:10 +02:00
{
if ( rect . GetLeft () <= pt . x && pt . x <= rect . GetRight () &&
rect . GetTop () <= pt . y && pt . y <= rect . GetBottom ())
return true ;
return false ;
}
2019-04-25 01:45:00 +02:00
int DoubleSlider :: is_point_near_tick ( const wxPoint & pt )
2018-11-27 11:08:34 +01:00
{
for ( auto tick : m_ticks ) {
const wxCoord pos = get_position_from_value ( tick );
if ( is_horizontal ()) {
if ( pos - 4 <= pt . x && pt . x <= pos + 4 )
return tick ;
}
else {
if ( pos - 4 <= pt . y && pt . y <= pos + 4 )
return tick ;
}
}
return - 1 ;
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: ChangeOneLayerLock ()
2018-09-17 15:12:13 +02:00
{
m_is_one_layer = ! m_is_one_layer ;
m_selection == ssLower ? correct_lower_value () : correct_higher_value ();
if ( ! m_selection ) m_selection = ssHigher ;
Refresh ();
Update ();
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
ProcessWindowEvent ( e );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnLeftDown ( wxMouseEvent & event )
2018-08-21 02:03:10 +02:00
{
2019-06-24 13:11:18 +02:00
if ( HasCapture ())
return ;
2018-08-24 13:34:38 +02:00
this -> CaptureMouse ();
2019-06-24 13:11:18 +02:00
2018-08-21 02:03:10 +02:00
wxClientDC dc ( this );
wxPoint pos = event . GetLogicalPosition ( dc );
2018-11-28 16:02:29 +01:00
if ( is_point_in_rect ( pos , m_rect_tick_action ) && m_is_enabled_tick_manipulation ) {
2018-08-24 13:34:38 +02:00
action_tick ( taOnIcon );
2018-08-23 13:01:18 +02:00
return ;
}
m_is_left_down = true ;
2018-10-31 12:56:08 +01:00
if ( is_point_in_rect ( pos , m_rect_one_layer_icon )) {
2019-06-24 13:11:18 +02:00
// switch on/off one layer mode
2018-08-24 13:34:38 +02:00
m_is_one_layer = ! m_is_one_layer ;
2018-11-27 11:08:34 +01:00
if ( ! m_is_one_layer ) {
SetLowerValue ( m_min_value );
SetHigherValue ( m_max_value );
}
2018-08-24 13:34:38 +02:00
m_selection == ssLower ? correct_lower_value () : correct_higher_value ();
if ( ! m_selection ) m_selection = ssHigher ;
}
2019-07-02 13:13:17 +02:00
else if ( is_point_in_rect ( pos , m_rect_revert_icon ) && m_is_enabled_tick_manipulation ) {
2019-06-24 13:11:18 +02:00
// discard all color changes
SetLowerValue ( m_min_value );
SetHigherValue ( m_max_value );
m_selection == ssLower ? correct_lower_value () : correct_higher_value ();
if ( ! m_selection ) m_selection = ssHigher ;
m_ticks . clear ();
wxPostEvent ( this -> GetParent (), wxCommandEvent ( wxCUSTOMEVT_TICKSCHANGED ));
}
2018-08-24 13:34:38 +02:00
else
detect_selected_slider ( pos );
2019-06-24 13:11:18 +02:00
if ( ! m_selection ) {
const int tick_val = is_point_near_tick ( pos );
/* Set current thumb position to the nearest tick (if it is)
* OR to a value corresponding to the mouse click
* */
const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val :
get_value_from_position ( pos . x , pos . y );
if ( mouse_val >= 0 )
2018-11-27 11:08:34 +01:00
{
2019-06-24 13:11:18 +02:00
if ( abs ( mouse_val - m_lower_value ) < abs ( mouse_val - m_higher_value )) {
SetLowerValue ( mouse_val );
2018-11-27 11:08:34 +01:00
correct_lower_value ();
m_selection = ssLower ;
}
else {
2019-06-24 13:11:18 +02:00
SetHigherValue ( mouse_val );
2018-11-27 11:08:34 +01:00
correct_higher_value ();
m_selection = ssHigher ;
}
}
}
2018-08-22 14:24:30 +02:00
Refresh ();
Update ();
2018-08-21 02:03:10 +02:00
event . Skip ();
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: correct_lower_value ()
2018-08-21 02:03:10 +02:00
{
if ( m_lower_value < m_min_value )
m_lower_value = m_min_value ;
else if ( m_lower_value > m_max_value )
m_lower_value = m_max_value ;
2018-08-24 13:34:38 +02:00
2019-09-03 10:27:16 +02:00
if (( m_lower_value >= m_higher_value && m_lower_value <= m_max_value ) || m_is_one_layer )
2018-08-24 13:34:38 +02:00
m_higher_value = m_lower_value ;
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: correct_higher_value ()
2018-08-21 02:03:10 +02:00
{
if ( m_higher_value > m_max_value )
m_higher_value = m_max_value ;
else if ( m_higher_value < m_min_value )
m_higher_value = m_min_value ;
2018-08-24 13:34:38 +02:00
2019-09-03 10:27:16 +02:00
if (( m_higher_value <= m_lower_value && m_higher_value >= m_min_value ) || m_is_one_layer )
2018-08-24 13:34:38 +02:00
m_lower_value = m_higher_value ;
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnMotion ( wxMouseEvent & event )
2018-08-21 02:03:10 +02:00
{
2018-11-27 14:50:57 +01:00
bool action = false ;
2018-08-24 13:34:38 +02:00
const wxClientDC dc ( this );
const wxPoint pos = event . GetLogicalPosition ( dc );
2019-06-24 13:11:18 +02:00
2018-08-24 13:34:38 +02:00
m_is_one_layer_icon_focesed = is_point_in_rect ( pos , m_rect_one_layer_icon );
2019-06-24 13:11:18 +02:00
bool is_revert_icon_focused = false ;
2018-10-31 12:56:08 +01:00
if ( ! m_is_left_down && ! m_is_one_layer ) {
2018-08-23 13:01:18 +02:00
m_is_action_icon_focesed = is_point_in_rect ( pos , m_rect_tick_action );
2019-06-24 13:11:18 +02:00
is_revert_icon_focused = ! m_ticks . empty () && is_point_in_rect ( pos , m_rect_revert_icon );
2018-08-21 02:03:10 +02:00
}
2018-10-31 12:56:08 +01:00
else if ( m_is_left_down || m_is_right_down ) {
2018-08-23 13:01:18 +02:00
if ( m_selection == ssLower ) {
2019-05-17 15:04:06 +02:00
int current_value = m_lower_value ;
2018-08-24 13:34:38 +02:00
m_lower_value = get_value_from_position ( pos . x , pos . y );
2018-08-23 13:01:18 +02:00
correct_lower_value ();
2019-05-17 15:04:06 +02:00
action = ( current_value != m_lower_value );
2018-08-23 13:01:18 +02:00
}
else if ( m_selection == ssHigher ) {
2019-05-17 15:04:06 +02:00
int current_value = m_higher_value ;
2018-08-24 13:34:38 +02:00
m_higher_value = get_value_from_position ( pos . x , pos . y );
2018-08-23 13:01:18 +02:00
correct_higher_value ();
2019-05-17 15:04:06 +02:00
action = ( current_value != m_higher_value );
2018-08-23 13:01:18 +02:00
}
2018-08-21 02:03:10 +02:00
}
Refresh ();
Update ();
event . Skip ();
2018-09-17 15:12:13 +02:00
2019-06-24 13:11:18 +02:00
// Set tooltips with information for each icon
const wxString tooltip = m_is_one_layer_icon_focesed ? _ ( L ( "One layer mode" )) :
m_is_action_icon_focesed ? _ ( L ( "Add/Del color change" )) :
2019-06-24 13:45:35 +02:00
is_revert_icon_focused ? _ ( L ( "Discard all color changes" )) : "" ;
2019-06-24 13:11:18 +02:00
this -> SetToolTip ( tooltip );
2018-11-27 14:50:57 +01:00
if ( action )
{
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
2019-05-17 15:04:06 +02:00
e . SetString ( "moving" );
2018-11-27 14:50:57 +01:00
ProcessWindowEvent ( e );
}
2018-08-21 02:03:10 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnLeftUp ( wxMouseEvent & event )
2018-08-21 02:03:10 +02:00
{
2019-01-11 09:00:35 +01:00
if ( ! HasCapture ())
return ;
2018-08-24 13:34:38 +02:00
this -> ReleaseMouse ();
2018-08-21 02:03:10 +02:00
m_is_left_down = false ;
2018-08-21 17:47:05 +02:00
Refresh ();
Update ();
2018-08-21 02:03:10 +02:00
event . Skip ();
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
ProcessWindowEvent ( e );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: enter_window ( wxMouseEvent & event , const bool enter )
2018-08-21 02:03:10 +02:00
{
2018-08-24 13:34:38 +02:00
m_is_focused = enter ;
2018-08-21 17:47:05 +02:00
Refresh ();
Update ();
event . Skip ();
}
2018-08-21 02:03:10 +02:00
2018-08-21 17:47:05 +02:00
// "condition" have to be true for:
// - value increase (if wxSL_VERTICAL)
// - value decrease (if wxSL_HORIZONTAL)
2019-04-25 01:45:00 +02:00
void DoubleSlider :: move_current_thumb ( const bool condition )
2018-08-21 17:47:05 +02:00
{
2018-12-19 15:58:01 +01:00
// m_is_one_layer = wxGetKeyState(WXK_CONTROL);
2018-08-21 17:47:05 +02:00
int delta = condition ? - 1 : 1 ;
2018-08-21 02:03:10 +02:00
if ( is_horizontal ())
delta *= - 1 ;
2018-08-21 17:47:05 +02:00
2018-08-21 02:03:10 +02:00
if ( m_selection == ssLower ) {
m_lower_value -= delta ;
correct_lower_value ();
}
else if ( m_selection == ssHigher ) {
m_higher_value -= delta ;
correct_higher_value ();
}
Refresh ();
Update ();
wxCommandEvent e ( wxEVT_SCROLL_CHANGED );
e . SetEventObject ( this );
ProcessWindowEvent ( e );
}
2018-08-21 17:47:05 +02:00
2019-04-25 01:45:00 +02:00
void DoubleSlider :: action_tick ( const TicksAction action )
2018-08-23 13:01:18 +02:00
{
if ( m_selection == ssUndef )
return ;
const int tick = m_selection == ssLower ? m_lower_value : m_higher_value ;
2018-11-26 16:07:09 +01:00
if ( action == taOnIcon ) {
if ( ! m_ticks . insert ( tick ). second )
m_ticks . erase ( tick );
}
2018-08-24 13:34:38 +02:00
else {
const auto it = m_ticks . find ( tick );
if ( it == m_ticks . end () && action == taAdd )
m_ticks . insert ( tick );
else if ( it != m_ticks . end () && action == taDel )
m_ticks . erase ( tick );
}
2018-08-23 13:01:18 +02:00
2018-11-07 14:44:47 +01:00
wxPostEvent ( this -> GetParent (), wxCommandEvent ( wxCUSTOMEVT_TICKSCHANGED ));
2018-08-23 13:01:18 +02:00
Refresh ();
Update ();
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnWheel ( wxMouseEvent & event )
2018-08-21 17:47:05 +02:00
{
2019-03-11 12:58:46 +01:00
// Set nearest to the mouse thumb as a selected, if there is not selected thumb
if ( m_selection == ssUndef )
{
const wxPoint & pt = event . GetLogicalPosition ( wxClientDC ( this ));
if ( is_horizontal ())
m_selection = abs ( pt . x - m_rect_lower_thumb . GetRight ()) <=
abs ( pt . x - m_rect_higher_thumb . GetLeft ()) ?
ssLower : ssHigher ;
else
m_selection = abs ( pt . y - m_rect_lower_thumb . GetTop ()) <=
abs ( pt . y - m_rect_higher_thumb . GetBottom ()) ?
ssLower : ssHigher ;
}
2018-08-21 17:47:05 +02:00
move_current_thumb ( event . GetWheelRotation () > 0 );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnKeyDown ( wxKeyEvent & event )
2018-08-21 17:47:05 +02:00
{
2018-08-23 13:01:18 +02:00
const int key = event . GetKeyCode ();
2019-06-18 11:40:26 +02:00
if ( key == WXK_NUMPAD_ADD )
2018-08-23 13:01:18 +02:00
action_tick ( taAdd );
2019-06-18 11:40:26 +02:00
else if ( key == 390 || key == WXK_DELETE || key == WXK_BACK )
2018-08-23 13:01:18 +02:00
action_tick ( taDel );
else if ( is_horizontal ())
2018-08-21 17:47:05 +02:00
{
2018-08-23 13:01:18 +02:00
if ( key == WXK_LEFT || key == WXK_RIGHT )
move_current_thumb ( key == WXK_LEFT );
2018-10-31 12:56:08 +01:00
else if ( key == WXK_UP || key == WXK_DOWN ) {
2018-08-23 13:01:18 +02:00
m_selection = key == WXK_UP ? ssHigher : ssLower ;
2018-08-21 17:47:05 +02:00
Refresh ();
}
}
else {
2018-08-23 13:01:18 +02:00
if ( key == WXK_LEFT || key == WXK_RIGHT ) {
m_selection = key == WXK_LEFT ? ssHigher : ssLower ;
2018-08-21 17:47:05 +02:00
Refresh ();
}
2018-08-23 13:01:18 +02:00
else if ( key == WXK_UP || key == WXK_DOWN )
move_current_thumb ( key == WXK_UP );
2018-08-21 17:47:05 +02:00
}
2019-06-19 16:28:56 +02:00
event . Skip (); // !Needed to have EVT_CHAR generated as well
2018-08-21 17:47:05 +02:00
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnKeyUp ( wxKeyEvent & event )
2018-08-22 18:00:48 +02:00
{
2018-08-24 13:34:38 +02:00
if ( event . GetKeyCode () == WXK_CONTROL )
m_is_one_layer = false ;
2018-08-22 18:00:48 +02:00
Refresh ();
Update ();
2018-08-24 13:34:38 +02:00
event . Skip ();
}
2019-06-18 11:40:26 +02:00
void DoubleSlider :: OnChar ( wxKeyEvent & event )
{
const int key = event . GetKeyCode ();
if ( key == '+' )
action_tick ( taAdd );
else if ( key == '-' )
action_tick ( taDel );
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnRightDown ( wxMouseEvent & event )
2018-08-24 13:34:38 +02:00
{
2019-06-24 13:11:18 +02:00
if ( HasCapture ()) return ;
2018-08-24 13:34:38 +02:00
this -> CaptureMouse ();
2019-06-24 13:11:18 +02:00
2018-08-24 13:34:38 +02:00
const wxClientDC dc ( this );
detect_selected_slider ( event . GetLogicalPosition ( dc ));
if ( ! m_selection )
return ;
if ( m_selection == ssLower )
m_higher_value = m_lower_value ;
else
m_lower_value = m_higher_value ;
m_is_right_down = m_is_one_layer = true ;
Refresh ();
Update ();
event . Skip ();
}
2019-04-25 01:45:00 +02:00
void DoubleSlider :: OnRightUp ( wxMouseEvent & event )
2018-08-24 13:34:38 +02:00
{
2019-01-11 09:00:35 +01:00
if ( ! HasCapture ())
return ;
2018-08-24 13:34:38 +02:00
this -> ReleaseMouse ();
m_is_right_down = m_is_one_layer = false ;
Refresh ();
Update ();
event . Skip ();
2018-08-22 18:00:48 +02:00
}
2018-08-28 15:51:53 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// LockButton
2018-08-28 15:51:53 +02:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
LockButton :: LockButton ( wxWindow * parent ,
wxWindowID id ,
const wxPoint & pos /*= wxDefaultPosition*/ ,
const wxSize & size /*= wxDefaultSize*/ ) :
wxButton ( parent , id , wxEmptyString , pos , size , wxBU_EXACTFIT | wxNO_BORDER )
2018-08-28 15:51:53 +02:00
{
2019-07-31 17:14:32 +02:00
m_bmp_lock_closed = ScalableBitmap ( this , "lock_closed" );
m_bmp_lock_closed_f = ScalableBitmap ( this , "lock_closed_f" );
m_bmp_lock_open = ScalableBitmap ( this , "lock_open" );
m_bmp_lock_open_f = ScalableBitmap ( this , "lock_open_f" );
2018-08-28 15:51:53 +02:00
#ifdef __WXMSW__
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
#endif // __WXMSW__
2019-07-31 17:14:32 +02:00
SetBitmap ( m_bmp_lock_open . bmp ());
SetBitmapDisabled ( m_bmp_lock_open . bmp ());
SetBitmapHover ( m_bmp_lock_closed_f . bmp ());
2018-08-28 15:51:53 +02:00
//button events
2019-07-31 17:14:32 +02:00
Bind ( wxEVT_BUTTON , & LockButton :: OnButton , this );
2018-08-28 15:51:53 +02:00
}
2019-04-25 01:45:00 +02:00
void LockButton :: OnButton ( wxCommandEvent & event )
2018-08-28 15:51:53 +02:00
{
2019-06-20 12:56:23 +02:00
if ( m_disabled )
return ;
2018-08-28 15:51:53 +02:00
m_is_pushed = ! m_is_pushed ;
2019-07-31 17:14:32 +02:00
update_button_bitmaps ();
2018-08-28 15:51:53 +02:00
event . Skip ();
}
2019-04-25 01:45:00 +02:00
void LockButton :: SetLock ( bool lock )
2019-01-09 08:48:25 +01:00
{
m_is_pushed = lock ;
2019-07-31 17:14:32 +02:00
update_button_bitmaps ();
2019-01-09 08:48:25 +01:00
}
2019-04-25 01:45:00 +02:00
void LockButton :: msw_rescale ()
2019-04-16 10:05:45 +02:00
{
2019-07-31 17:14:32 +02:00
m_bmp_lock_closed . msw_rescale ();
m_bmp_lock_closed_f . msw_rescale ();
m_bmp_lock_open . msw_rescale ();
m_bmp_lock_open_f . msw_rescale ();
2019-04-16 10:05:45 +02:00
}
2019-07-31 17:14:32 +02:00
void LockButton :: update_button_bitmaps ()
2018-08-28 15:51:53 +02:00
{
2019-07-31 17:14:32 +02:00
SetBitmap ( m_is_pushed ? m_bmp_lock_closed . bmp () : m_bmp_lock_open . bmp ());
SetBitmapHover ( m_is_pushed ? m_bmp_lock_closed_f . bmp () : m_bmp_lock_open_f . bmp ());
2018-08-28 15:51:53 +02:00
Refresh ();
Update ();
}
2019-01-10 11:05:58 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// ModeButton
2019-01-10 11:05:58 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
ModeButton :: ModeButton ( wxWindow * parent ,
wxWindowID id ,
const std :: string & icon_name /* = ""*/ ,
const wxString & mode /* = wxEmptyString*/ ,
const wxSize & size /* = wxDefaultSize*/ ,
const wxPoint & pos /* = wxDefaultPosition*/ ) :
2019-07-25 16:23:32 +02:00
ScalableButton ( parent , id , icon_name , mode , size , pos , wxBU_EXACTFIT )
2019-01-10 11:05:58 +01:00
{
2019-02-26 10:50:00 +01:00
m_tt_focused = wxString :: Format ( _ ( L ( "Switch to the %s mode" )), mode );
m_tt_selected = wxString :: Format ( _ ( L ( "Current mode is %s" )), mode );
2019-07-25 16:23:32 +02:00
SetBitmapMargins ( 3 , 0 );
2019-01-10 11:05:58 +01:00
//button events
2019-04-25 01:45:00 +02:00
Bind ( wxEVT_BUTTON , & ModeButton :: OnButton , this );
Bind ( wxEVT_ENTER_WINDOW , & ModeButton :: OnEnterBtn , this );
Bind ( wxEVT_LEAVE_WINDOW , & ModeButton :: OnLeaveBtn , this );
2019-01-10 11:05:58 +01:00
}
2019-04-25 01:45:00 +02:00
void ModeButton :: OnButton ( wxCommandEvent & event )
2019-01-10 11:05:58 +01:00
{
m_is_selected = true ;
focus_button ( m_is_selected );
event . Skip ();
}
2019-04-25 01:45:00 +02:00
void ModeButton :: SetState ( const bool state )
2019-01-10 11:05:58 +01:00
{
m_is_selected = state ;
focus_button ( m_is_selected );
2019-02-26 10:50:00 +01:00
SetToolTip ( state ? m_tt_selected : m_tt_focused );
2019-01-10 11:05:58 +01:00
}
2019-04-25 01:45:00 +02:00
void ModeButton :: focus_button ( const bool focus )
2019-01-10 11:05:58 +01:00
{
2019-04-29 09:43:42 +02:00
const wxFont & new_font = focus ?
Slic3r :: GUI :: wxGetApp (). bold_font () :
Slic3r :: GUI :: wxGetApp (). normal_font ();
2019-04-10 09:56:32 +02:00
2019-01-10 11:05:58 +01:00
SetFont ( new_font );
2019-07-25 16:23:32 +02:00
SetForegroundColour ( wxSystemSettings :: GetColour ( focus ? wxSYS_COLOUR_BTNTEXT : wxSYS_COLOUR_BTNSHADOW ));
2019-01-10 11:05:58 +01:00
Refresh ();
Update ();
}
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// ModeSizer
2019-01-10 11:05:58 +01:00
// ----------------------------------------------------------------------------
2019-07-25 16:23:32 +02:00
ModeSizer :: ModeSizer ( wxWindow * parent , int hgap /* = 0*/ ) :
2019-02-11 15:36:05 +01:00
wxFlexGridSizer ( 3 , 0 , hgap )
2019-01-10 11:05:58 +01:00
{
SetFlexibleDirection ( wxHORIZONTAL );
2019-04-13 23:46:52 +02:00
std :: vector < std :: pair < wxString , std :: string >> buttons = {
2019-04-25 01:45:00 +02:00
{ _ ( L ( "Simple" )), "mode_simple_sq.png" },
2019-05-13 12:13:28 +02:00
{ _ ( L ( "Advanced" )), "mode_advanced_sq.png" },
2019-04-25 01:45:00 +02:00
{ _ ( L ( "Expert" )), "mode_expert_sq.png" }
2019-02-11 14:14:35 +01:00
};
2019-02-06 15:29:13 +01:00
2019-05-21 14:06:43 +02:00
auto modebtnfn = []( wxCommandEvent & event , int mode_id ) {
Slic3r :: GUI :: wxGetApp (). save_mode ( mode_id );
event . Skip ();
};
2019-04-25 01:45:00 +02:00
m_mode_btns . reserve ( 3 );
2019-02-11 14:14:35 +01:00
for ( const auto & button : buttons ) {
2019-07-25 16:23:32 +02:00
m_mode_btns . push_back ( new ModeButton ( parent , wxID_ANY , button . second , button . first ));
2019-05-27 13:07:37 +02:00
m_mode_btns . back () -> Bind ( wxEVT_BUTTON , std :: bind ( modebtnfn , std :: placeholders :: _1 , int ( m_mode_btns . size () - 1 )));
2019-05-21 14:06:43 +02:00
Add ( m_mode_btns . back ());
2019-02-11 14:14:35 +01:00
}
2019-01-10 11:05:58 +01:00
}
2019-04-25 01:45:00 +02:00
void ModeSizer :: SetMode ( const int mode )
2019-01-10 11:05:58 +01:00
{
2019-05-21 14:06:43 +02:00
for ( size_t m = 0 ; m < m_mode_btns . size (); m ++ )
m_mode_btns [ m ] -> SetState ( int ( m ) == mode );
2019-01-10 11:05:58 +01:00
}
2019-01-25 13:16:32 +01:00
2019-04-25 01:45:00 +02:00
void ModeSizer :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2019-05-21 14:06:43 +02:00
for ( size_t m = 0 ; m < m_mode_btns . size (); m ++ )
2019-04-25 01:45:00 +02:00
m_mode_btns [ m ] -> msw_rescale ();
2019-04-13 23:46:52 +02:00
}
2019-01-25 13:16:32 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
// MenuWithSeparators
2019-01-25 13:16:32 +01:00
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
void MenuWithSeparators :: DestroySeparators ()
2019-01-25 13:16:32 +01:00
{
if ( m_separator_frst ) {
Destroy ( m_separator_frst );
m_separator_frst = nullptr ;
}
if ( m_separator_scnd ) {
Destroy ( m_separator_scnd );
m_separator_scnd = nullptr ;
}
}
2019-04-25 01:45:00 +02:00
void MenuWithSeparators :: SetFirstSeparator ()
{
m_separator_frst = this -> AppendSeparator ();
}
void MenuWithSeparators :: SetSecondSeparator ()
{
m_separator_scnd = this -> AppendSeparator ();
}
2019-01-25 13:16:32 +01:00
2019-04-13 23:46:52 +02:00
// ----------------------------------------------------------------------------
// PrusaBitmap
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
ScalableBitmap :: ScalableBitmap ( wxWindow * parent ,
const std :: string & icon_name /* = ""*/ ,
const int px_cnt /* = 16*/ ,
const bool is_horizontal /* = false*/ ) :
m_parent ( parent ), m_icon_name ( icon_name ),
m_px_cnt ( px_cnt ), m_is_horizontal ( is_horizontal )
2019-04-13 23:46:52 +02:00
{
2019-04-16 10:05:45 +02:00
m_bmp = create_scaled_bitmap ( parent , icon_name , px_cnt , is_horizontal );
2019-04-13 23:46:52 +02:00
}
2019-04-25 01:45:00 +02:00
void ScalableBitmap :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2019-04-16 10:05:45 +02:00
m_bmp = create_scaled_bitmap ( m_parent , m_icon_name , m_px_cnt , m_is_horizontal );
2019-04-13 23:46:52 +02:00
}
// ----------------------------------------------------------------------------
// PrusaButton
// ----------------------------------------------------------------------------
2019-04-25 01:45:00 +02:00
ScalableButton :: ScalableButton ( wxWindow * parent ,
wxWindowID id ,
const std :: string & icon_name /*= ""*/ ,
const wxString & label /* = wxEmptyString*/ ,
const wxSize & size /* = wxDefaultSize*/ ,
const wxPoint & pos /* = wxDefaultPosition*/ ,
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/ ) :
2019-04-13 23:46:52 +02:00
m_current_icon_name ( icon_name ),
m_parent ( parent )
{
Create ( parent , id , label , pos , size , style );
#ifdef __WXMSW__
if ( style & wxNO_BORDER )
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
#endif // __WXMSW__
2019-05-17 19:11:11 +02:00
SetBitmap ( create_scaled_bitmap ( parent , icon_name ));
2019-08-01 11:35:43 +02:00
if ( size != wxDefaultSize )
{
const int em = em_unit ( parent );
m_width = size . x / em ;
m_height = size . y / em ;
}
2019-04-13 23:46:52 +02:00
}
2019-04-25 01:45:00 +02:00
ScalableButton :: ScalableButton ( wxWindow * parent ,
wxWindowID id ,
const ScalableBitmap & bitmap ,
const wxString & label /*= wxEmptyString*/ ,
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/ ) :
2019-04-13 23:46:52 +02:00
m_current_icon_name ( bitmap . name ()),
m_parent ( parent )
{
Create ( parent , id , label , wxDefaultPosition , wxDefaultSize , style );
#ifdef __WXMSW__
if ( style & wxNO_BORDER )
SetBackgroundColour ( wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW ));
#endif // __WXMSW__
2019-04-16 10:05:45 +02:00
SetBitmap ( bitmap . bmp ());
2019-04-13 23:46:52 +02:00
}
2019-04-25 01:45:00 +02:00
void ScalableButton :: SetBitmap_ ( const ScalableBitmap & bmp )
2019-04-13 23:46:52 +02:00
{
SetBitmap ( bmp . bmp ());
m_current_icon_name = bmp . name ();
}
2019-08-01 11:35:43 +02:00
void ScalableButton :: SetBitmapDisabled_ ( const ScalableBitmap & bmp )
{
SetBitmapDisabled ( bmp . bmp ());
m_disabled_icon_name = bmp . name ();
}
2019-04-25 01:45:00 +02:00
void ScalableButton :: msw_rescale ()
2019-04-13 23:46:52 +02:00
{
2019-08-01 11:35:43 +02:00
SetBitmap ( create_scaled_bitmap ( m_parent , m_current_icon_name ));
if ( ! m_disabled_icon_name . empty ())
SetBitmapDisabled ( create_scaled_bitmap ( m_parent , m_disabled_icon_name ));
2019-04-13 23:46:52 +02:00
2019-08-01 11:35:43 +02:00
if ( m_width > 0 || m_height > 0 )
{
const int em = em_unit ( m_parent );
wxSize size ( m_width * em , m_height * em );
SetMinSize ( size );
}
2019-04-13 23:46:52 +02:00
}
2018-08-29 11:21:22 +02:00
2018-08-28 15:51:53 +02:00
2018-08-29 11:21:22 +02:00