2018-02-02 12:38:35 +01:00
#include "wxExtensions.hpp"
2018-05-10 16:36:12 +02:00
#include "GUI.hpp"
#include "../../libslic3r/Utils.hpp"
#include <wx/sizer.h>
#include <wx/statline.h>
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
2018-05-11 11:25:28 +02:00
// ----------------------------------------------------------------------------
2018-06-08 12:43:39 +02:00
// *** PrusaCollapsiblePane ***
// ----------------------------------------------------------------------------
void PrusaCollapsiblePane :: OnStateChange ( const wxSize & sz )
{
2018-06-29 14:00:22 +02:00
#ifdef __WXOSX__
2018-06-29 13:07:58 +02:00
wxCollapsiblePane :: OnStateChange ( sz );
#else
2018-06-08 12:43:39 +02:00
SetSize ( sz );
if ( this -> HasFlag ( wxCP_NO_TLW_RESIZE ))
{
// the user asked to explicitly handle the resizing itself...
return ;
}
auto top = GetParent (); //right_panel
if ( ! top )
return ;
wxSizer * sizer = top -> GetSizer ();
if ( ! sizer )
return ;
const wxSize newBestSize = sizer -> ComputeFittingClientSize ( top );
top -> SetMinClientSize ( newBestSize );
wxWindowUpdateLocker noUpdates_p ( top -> GetParent ());
// we shouldn't attempt to resize a maximized window, whatever happens
// if (!top->IsMaximized())
// top->SetClientSize(newBestSize);
top -> GetParent () -> Layout ();
top -> Refresh ();
2018-06-29 13:07:58 +02:00
#endif //__WXOSX__
2018-06-08 12:43:39 +02:00
}
// ----------------------------------------------------------------------------
// *** PrusaCollapsiblePaneMSW *** used only #ifdef __WXMSW__
2018-05-10 16:36:12 +02:00
// ----------------------------------------------------------------------------
2018-05-11 11:25:28 +02:00
#ifdef __WXMSW__
2018-06-08 12:43:39 +02:00
bool PrusaCollapsiblePaneMSW :: Create ( wxWindow * parent , wxWindowID id , const wxString & label ,
2018-05-10 16:36:12 +02:00
const wxPoint & pos , const wxSize & size , long style , const wxValidator & val , const wxString & name )
{
if ( ! wxControl :: Create ( parent , id , pos , size , style , val , name ))
return false ;
m_pStaticLine = NULL ;
m_strLabel = label ;
// sizer containing the expand button and possibly a static line
m_sz = new wxBoxSizer ( wxHORIZONTAL );
m_bmp_close . LoadFile ( Slic3r :: GUI :: from_u8 ( Slic3r :: var ( "disclosure_triangle_close.png" )), wxBITMAP_TYPE_PNG );
m_bmp_open . LoadFile ( Slic3r :: GUI :: from_u8 ( Slic3r :: var ( "disclosure_triangle_open.png" )), wxBITMAP_TYPE_PNG );
m_pDisclosureTriangleButton = new wxButton ( this , wxID_ANY , m_strLabel , wxPoint ( 0 , 0 ),
wxDefaultSize , wxBU_EXACTFIT | wxNO_BORDER );
2018-05-15 12:01:33 +02:00
UpdateBtnBmp ();
2018-05-10 16:36:12 +02:00
m_pDisclosureTriangleButton -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & event )
{
if ( event . GetEventObject () != m_pDisclosureTriangleButton )
{
event . Skip ();
return ;
}
Collapse ( ! IsCollapsed ());
// this change was generated by the user - send the event
wxCollapsiblePaneEvent ev ( this , GetId (), IsCollapsed ());
GetEventHandler () -> ProcessEvent ( ev );
});
m_sz -> Add ( m_pDisclosureTriangleButton , 0 , wxLEFT | wxTOP | wxBOTTOM , GetBorder ());
// do not set sz as our sizers since we handle the pane window without using sizers
m_pPane = new wxPanel ( this , wxID_ANY , wxDefaultPosition , wxDefaultSize ,
wxTAB_TRAVERSAL | wxNO_BORDER , wxT ( "wxCollapsiblePanePane" ));
wxColour & clr = wxSystemSettings :: GetColour ( wxSYS_COLOUR_WINDOW );
m_pDisclosureTriangleButton -> SetBackgroundColour ( clr );
this -> SetBackgroundColour ( clr );
m_pPane -> SetBackgroundColour ( clr );
// start as collapsed:
m_pPane -> Hide ();
return true ;
}
2018-06-08 12:43:39 +02:00
void PrusaCollapsiblePaneMSW :: UpdateBtnBmp ()
2018-05-10 16:36:12 +02:00
{
2018-05-15 12:01:33 +02:00
if ( IsCollapsed ())
m_pDisclosureTriangleButton -> SetBitmap ( m_bmp_close );
else {
2018-05-10 16:36:12 +02:00
m_pDisclosureTriangleButton -> SetBitmap ( m_bmp_open );
2018-05-15 12:01:33 +02:00
// To updating button bitmap it's needed to lost focus on this button, so
// we set focus to mainframe
//GetParent()->GetParent()->GetParent()->SetFocus();
//or to pane
GetPane () -> SetFocus ();
}
2018-05-10 16:36:12 +02:00
Layout ();
}
2018-06-08 12:43:39 +02:00
void PrusaCollapsiblePaneMSW :: SetLabel ( const wxString & label )
2018-05-10 16:36:12 +02:00
{
m_strLabel = label ;
m_pDisclosureTriangleButton -> SetLabel ( m_strLabel );
Layout ();
}
2018-06-08 12:43:39 +02:00
bool PrusaCollapsiblePaneMSW :: Layout ()
2018-05-10 16:36:12 +02:00
{
if ( ! m_pDisclosureTriangleButton || ! m_pPane || ! m_sz )
return false ; // we need to complete the creation first!
wxSize oursz ( GetSize ());
// move & resize the button and the static line
m_sz -> SetDimension ( 0 , 0 , oursz . GetWidth (), m_sz -> GetMinSize (). GetHeight ());
m_sz -> Layout ();
if ( IsExpanded ())
{
// move & resize the container window
int yoffset = m_sz -> GetSize (). GetHeight () + GetBorder ();
m_pPane -> SetSize ( 0 , yoffset ,
oursz . x , oursz . y - yoffset );
// this is very important to make the pane window layout show correctly
m_pPane -> Layout ();
}
return true ;
}
2018-06-08 12:43:39 +02:00
void PrusaCollapsiblePaneMSW :: Collapse ( bool collapse )
2018-06-05 09:13:03 +02:00
{
// optimization
if ( IsCollapsed () == collapse )
return ;
InvalidateBestSize ();
// update our state
m_pPane -> Show ( ! collapse );
// update button bitmap
UpdateBtnBmp ();
2018-06-08 12:43:39 +02:00
OnStateChange ( GetBestSize ());
2018-06-05 09:13:03 +02:00
}
2018-06-05 11:17:37 +02:00
#endif //__WXMSW__
2018-06-05 09:13:03 +02:00
2018-07-04 08:54:30 +02:00
// *****************************************************************************
// ----------------------------------------------------------------------------
// PrusaObjectDataViewModelNode
// ----------------------------------------------------------------------------
void PrusaObjectDataViewModelNode :: set_object_action_icon () {
2018-07-25 16:13:20 +02:00
m_action_icon = wxBitmap ( Slic3r :: GUI :: from_u8 ( Slic3r :: var ( "add_object.png" )), wxBITMAP_TYPE_PNG );
2018-07-04 08:54:30 +02:00
}
void PrusaObjectDataViewModelNode :: set_part_action_icon () {
m_action_icon = wxBitmap ( Slic3r :: GUI :: from_u8 ( Slic3r :: var ( "cog.png" )), wxBITMAP_TYPE_PNG );
}
2018-05-04 18:32:20 +02:00
// *****************************************************************************
// ----------------------------------------------------------------------------
2018-06-15 22:42:51 +02:00
// PrusaObjectDataViewModel
2018-05-04 18:32:20 +02:00
// ----------------------------------------------------------------------------
2018-06-15 22:42:51 +02:00
wxDataViewItem PrusaObjectDataViewModel :: Add ( wxString & name )
2018-05-04 18:32:20 +02:00
{
2018-06-15 22:42:51 +02:00
auto root = new PrusaObjectDataViewModelNode ( name );
2018-05-30 17:52:28 +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 );
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
2018-06-15 22:42:51 +02:00
wxDataViewItem PrusaObjectDataViewModel :: Add ( wxString & name , int instances_count , int scale )
2018-06-04 15:59:55 +02:00
{
2018-06-15 22:42:51 +02:00
auto root = new PrusaObjectDataViewModelNode ( name , instances_count , scale );
2018-06-04 15:59:55 +02:00
m_objects . push_back ( root );
// notify control
wxDataViewItem child (( void * ) root );
wxDataViewItem parent (( void * ) NULL );
ItemAdded ( parent , child );
return child ;
}
2018-06-15 22:42:51 +02:00
wxDataViewItem PrusaObjectDataViewModel :: AddChild ( const wxDataViewItem & parent_item ,
const wxString & name ,
const wxIcon & icon )
2018-05-29 22:45:35 +02:00
{
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * root = ( PrusaObjectDataViewModelNode * ) parent_item . GetID ();
2018-05-30 17:52:28 +02:00
if ( ! root ) return wxDataViewItem ( 0 );
2018-05-04 18:32:20 +02:00
2018-05-30 00:36:44 +02:00
if ( root -> GetChildren (). Count () == 0 )
{
2018-07-25 16:13:20 +02:00
auto icon_solid_mesh = wxIcon ( Slic3r :: GUI :: from_u8 ( Slic3r :: var ( "object.png" )), wxBITMAP_TYPE_PNG ); //(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
2018-06-18 14:20:29 +02:00
auto node = new PrusaObjectDataViewModelNode ( root , root -> m_name , icon_solid_mesh , 0 );
2018-05-30 00:36:44 +02:00
root -> Append ( node );
// notify control
wxDataViewItem child (( void * ) node );
ItemAdded ( parent_item , child );
}
2018-06-18 14:20:29 +02:00
auto volume_id = root -> GetChildCount ();
auto node = new PrusaObjectDataViewModelNode ( root , name , icon , volume_id );
2018-05-29 22:45:35 +02:00
root -> Append ( node );
// notify control
wxDataViewItem child (( void * ) node );
ItemAdded ( parent_item , child );
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
2018-06-15 22:42:51 +02:00
wxDataViewItem PrusaObjectDataViewModel :: 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 );
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) 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-05-30 17:52:28 +02:00
if ( node_parent ){
auto id = node_parent -> GetChildren (). Index ( node );
2018-06-18 15:26:09 +02:00
auto v_id = node -> GetVolumeId ();
2018-05-30 00:36:44 +02:00
node_parent -> GetChildren (). Remove ( node );
2018-05-30 17:52:28 +02:00
if ( id > 0 ){
if ( id == node_parent -> GetChildCount ()) id -- ;
ret_item = wxDataViewItem ( node_parent -> GetChildren (). Item ( id ));
}
2018-06-18 15:26:09 +02:00
//update volume_id value for remaining child-nodes
auto children = node_parent -> GetChildren ();
for ( size_t i = 0 ; i < node_parent -> GetChildCount (); i ++ )
{
auto volume_id = children [ i ] -> GetVolumeId ();
if ( volume_id > v_id )
children [ i ] -> SetVolumeId ( volume_id - 1 );
}
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 );
auto id = it - m_objects . begin ();
2018-05-30 00:36:44 +02:00
if ( it != m_objects . end ())
m_objects . erase ( it );
2018-05-30 17:52:28 +02:00
if ( id > 0 ){
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-05-30 17:52:28 +02:00
if ( node_parent && node_parent -> GetChildCount () == 0 ){
2018-05-30 00:36:44 +02:00
node_parent -> m_container = false ;
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
}
2018-06-15 22:42:51 +02:00
void PrusaObjectDataViewModel :: DeleteAll ()
2018-06-04 15:59:55 +02:00
{
while ( ! m_objects . empty ())
{
auto object = m_objects . back ();
// object->RemoveAllChildren();
Delete ( wxDataViewItem ( object ));
}
}
2018-06-15 22:42:51 +02:00
wxDataViewItem PrusaObjectDataViewModel :: GetItemById ( int obj_idx )
2018-06-04 15:59:55 +02:00
{
if ( obj_idx >= m_objects . size ())
{
printf ( "Error! Out of objects range. \n " );
return wxDataViewItem ( 0 );
}
return wxDataViewItem ( m_objects [ obj_idx ]);
}
2018-06-15 22:42:51 +02:00
int PrusaObjectDataViewModel :: GetIdByItem ( wxDataViewItem & item )
2018-06-07 00:55:09 +02:00
{
wxASSERT ( item . IsOk ());
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) 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 ();
}
2018-06-18 14:20:29 +02:00
int PrusaObjectDataViewModel :: GetVolumeIdByItem ( wxDataViewItem & item )
{
wxASSERT ( item . IsOk ());
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return - 1 ;
return node -> GetVolumeId ();
}
2018-06-15 22:42:51 +02:00
wxString PrusaObjectDataViewModel :: GetName ( const wxDataViewItem & item ) const
2018-05-04 18:32:20 +02:00
{
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
if ( ! node ) // happens if item.IsOk()==false
return wxEmptyString ;
return node -> m_name ;
}
2018-06-15 22:42:51 +02:00
wxString PrusaObjectDataViewModel :: GetCopy ( const wxDataViewItem & item ) const
2018-05-04 18:32:20 +02:00
{
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
if ( ! node ) // happens if item.IsOk()==false
return wxEmptyString ;
return node -> m_copy ;
}
2018-06-15 22:42:51 +02:00
wxString PrusaObjectDataViewModel :: GetScale ( const wxDataViewItem & item ) const
2018-05-04 18:32:20 +02:00
{
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
if ( ! node ) // happens if item.IsOk()==false
return wxEmptyString ;
return node -> m_scale ;
}
2018-06-15 22:42:51 +02:00
void PrusaObjectDataViewModel :: GetValue ( wxVariant & variant , const wxDataViewItem & item , unsigned int col ) const
2018-05-04 18:32:20 +02:00
{
wxASSERT ( item . IsOk ());
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
switch ( col )
{
2018-06-15 22:42:51 +02:00
case 0 : {
const wxDataViewIconText data ( node -> m_name , node -> m_icon );
variant << data ;
break ;}
2018-05-04 18:32:20 +02:00
case 1 :
variant = node -> m_copy ;
break ;
case 2 :
variant = node -> m_scale ;
break ;
2018-07-04 08:54:30 +02:00
case 3 :
variant = node -> m_extruder ;
break ;
case 4 :
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
}
}
2018-06-15 22:42:51 +02:00
bool PrusaObjectDataViewModel :: SetValue ( const wxVariant & variant , const wxDataViewItem & item , unsigned int col )
2018-05-04 18:32:20 +02:00
{
wxASSERT ( item . IsOk ());
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-06-05 13:17:24 +02:00
return node -> SetValue ( variant , col );
2018-05-04 18:32:20 +02:00
}
2018-06-15 22:42:51 +02:00
bool PrusaObjectDataViewModel :: SetValue ( const wxVariant & variant , const int item_idx , unsigned int col )
2018-06-05 10:41:20 +02:00
{
if ( item_idx < 0 || item_idx >= m_objects . size ())
return false ;
return m_objects [ item_idx ] -> SetValue ( variant , col );
}
2018-06-19 12:24:16 +02:00
wxDataViewItem PrusaObjectDataViewModel :: MoveChildUp ( const wxDataViewItem & item )
{
auto ret_item = wxDataViewItem ( 0 );
wxASSERT ( item . IsOk ());
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return ret_item ;
auto node_parent = node -> GetParent ();
if ( ! node_parent ) // If isn't part, but object
return ret_item ;
auto volume_id = node -> GetVolumeId ();
if ( 0 < volume_id && volume_id < node_parent -> GetChildCount ()){
node_parent -> SwapChildrens ( volume_id - 1 , volume_id );
ret_item = wxDataViewItem ( node_parent -> GetNthChild ( volume_id - 1 ));
ItemChanged ( item );
ItemChanged ( ret_item );
}
else
ret_item = wxDataViewItem ( node_parent -> GetNthChild ( 0 ));
return ret_item ;
}
wxDataViewItem PrusaObjectDataViewModel :: MoveChildDown ( const wxDataViewItem & item )
{
auto ret_item = wxDataViewItem ( 0 );
wxASSERT ( item . IsOk ());
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return ret_item ;
auto node_parent = node -> GetParent ();
if ( ! node_parent ) // If isn't part, but object
return ret_item ;
auto volume_id = node -> GetVolumeId ();
if ( 0 <= volume_id && volume_id + 1 < node_parent -> GetChildCount ()){
node_parent -> SwapChildrens ( volume_id + 1 , volume_id );
ret_item = wxDataViewItem ( node_parent -> GetNthChild ( volume_id + 1 ));
ItemChanged ( item );
ItemChanged ( ret_item );
}
else
ret_item = wxDataViewItem ( node_parent -> GetNthChild ( node_parent -> GetChildCount () - 1 ));
return ret_item ;
}
2018-05-04 18:32:20 +02:00
// bool MyObjectTreeModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
// {
//
// }
2018-06-15 22:42:51 +02:00
wxDataViewItem PrusaObjectDataViewModel :: 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 );
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
2018-05-29 22:45:35 +02:00
// objects nodes has no parent too
2018-05-30 17:52:28 +02:00
if ( find ( m_objects . begin (), m_objects . end (), node ) != m_objects . end ())
2018-05-04 18:32:20 +02:00
return wxDataViewItem ( 0 );
return wxDataViewItem (( void * ) node -> GetParent ());
}
2018-06-15 22:42:51 +02:00
bool PrusaObjectDataViewModel :: 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 ;
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) item . GetID ();
2018-05-04 18:32:20 +02:00
return node -> IsContainer ();
}
2018-06-15 22:42:51 +02:00
unsigned int PrusaObjectDataViewModel :: GetChildren ( const wxDataViewItem & parent , wxDataViewItemArray & array ) const
2018-05-04 18:32:20 +02:00
{
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * node = ( PrusaObjectDataViewModelNode * ) 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 ++ )
{
2018-06-15 22:42:51 +02:00
PrusaObjectDataViewModelNode * 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
// ************************************** EXPERIMENTS ***************************************
// ----------------------------------------------------------------------------
// MyMusicTreeModel
// ----------------------------------------------------------------------------
MyMusicTreeModel :: MyMusicTreeModel ()
{
m_root = new MyMusicTreeModelNode ( NULL , "My Music" );
// setup pop music
2018-07-30 13:19:31 +02:00
// m_pop = new MyMusicTreeModelNode(m_root, "Pop music");
// m_pop->Append(
// new MyMusicTreeModelNode(m_pop, "You are not alone", "Michael Jackson", 1995));
// m_pop->Append(
// new MyMusicTreeModelNode(m_pop, "Take a bow", "Madonna", 1994));
// m_root->Append(m_pop);
//
// // setup classical music
// m_classical = new MyMusicTreeModelNode(m_root, "Classical music");
// m_ninth = new MyMusicTreeModelNode(m_classical, "Ninth symphony",
// "Ludwig van Beethoven", 1824);
// m_classical->Append(m_ninth);
// m_classical->Append(new MyMusicTreeModelNode(m_classical, "German Requiem",
// "Johannes Brahms", 1868));
// m_root->Append(m_classical);
2018-07-30 12:17:32 +02:00
m_classicalMusicIsKnownToControl = false ;
}
wxString MyMusicTreeModel :: GetTitle ( const wxDataViewItem & item ) const
{
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return wxEmptyString ;
return node -> m_title ;
}
wxString MyMusicTreeModel :: GetArtist ( const wxDataViewItem & item ) const
{
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return wxEmptyString ;
return node -> m_artist ;
}
int MyMusicTreeModel :: GetYear ( const wxDataViewItem & item ) const
{
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return 2000 ;
return node -> m_year ;
}
void MyMusicTreeModel :: AddToClassical ( const wxString & title , const wxString & artist ,
unsigned int year )
{
if ( ! m_classical )
{
wxASSERT ( m_root );
// it was removed: restore it
m_classical = new MyMusicTreeModelNode ( m_root , "Classical music" );
m_root -> Append ( m_classical );
// notify control
wxDataViewItem child (( void * ) m_classical );
wxDataViewItem parent (( void * ) m_root );
ItemAdded ( parent , child );
}
// add to the classical music node a new node:
MyMusicTreeModelNode * child_node =
new MyMusicTreeModelNode ( m_classical , title , artist , year );
m_classical -> Append ( child_node );
// FIXME: what's m_classicalMusicIsKnownToControl for?
if ( m_classicalMusicIsKnownToControl )
{
// notify control
wxDataViewItem child (( void * ) child_node );
wxDataViewItem parent (( void * ) m_classical );
ItemAdded ( parent , child );
}
}
void MyMusicTreeModel :: Delete ( const wxDataViewItem & item )
{
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
if ( ! node ) // happens if item.IsOk()==false
return ;
wxDataViewItem parent ( node -> GetParent ());
if ( ! parent . IsOk ())
{
wxASSERT ( node == m_root );
// don't make the control completely empty:
printf ( "Cannot remove the root item!" );
return ;
}
// is the node one of those we keep stored in special pointers?
if ( node == m_pop )
m_pop = NULL ;
else if ( node == m_classical )
m_classical = NULL ;
else if ( node == m_ninth )
m_ninth = NULL ;
// first remove the node from the parent's array of children;
// NOTE: MyMusicTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
node -> GetParent () -> GetChildren (). Remove ( node );
// free the node
delete node ;
// notify control
ItemDeleted ( parent , item );
}
int MyMusicTreeModel :: Compare ( const wxDataViewItem & item1 , const wxDataViewItem & item2 ,
unsigned int column , bool ascending ) const
{
wxASSERT ( item1 . IsOk () && item2 . IsOk ());
// should never happen
if ( IsContainer ( item1 ) && IsContainer ( item2 ))
{
wxVariant value1 , value2 ;
GetValue ( value1 , item1 , 0 );
GetValue ( value2 , item2 , 0 );
wxString str1 = value1 . GetString ();
wxString str2 = value2 . GetString ();
int res = str1 . Cmp ( str2 );
if ( res ) return res ;
// items must be different
wxUIntPtr litem1 = ( wxUIntPtr ) item1 . GetID ();
wxUIntPtr litem2 = ( wxUIntPtr ) item2 . GetID ();
return litem1 - litem2 ;
}
return wxDataViewModel :: Compare ( item1 , item2 , column , ascending );
}
void MyMusicTreeModel :: GetValue ( wxVariant & variant ,
const wxDataViewItem & item , unsigned int col ) const
{
wxASSERT ( item . IsOk ());
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
switch ( col )
{
case 0 :
variant = node -> m_title ;
break ;
case 1 :
variant = node -> m_artist ;
break ;
case 2 :
variant = ( long ) node -> m_year ;
break ;
case 3 :
variant = node -> m_quality ;
break ;
case 4 :
variant = 80L ; // all music is very 80% popular
break ;
case 5 :
if ( GetYear ( item ) < 1900 )
variant = "old" ;
else
variant = "new" ;
break ;
default :
printf ( "MyMusicTreeModel::GetValue: wrong column %d" , col );
}
}
bool MyMusicTreeModel :: SetValue ( const wxVariant & variant ,
const wxDataViewItem & item , unsigned int col )
{
wxASSERT ( item . IsOk ());
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
switch ( col )
{
case 0 :
node -> m_title = variant . GetString ();
return true ;
case 1 :
node -> m_artist = variant . GetString ();
return true ;
case 2 :
node -> m_year = variant . GetLong ();
return true ;
case 3 :
node -> m_quality = variant . GetString ();
return true ;
default :
printf ( "MyMusicTreeModel::SetValue: wrong column" );
}
return false ;
}
bool MyMusicTreeModel :: IsEnabled ( const wxDataViewItem & item ,
unsigned int col ) const
{
wxASSERT ( item . IsOk ());
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
// disable Beethoven's ratings, his pieces can only be good
return ! ( col == 3 && node -> m_artist . EndsWith ( "Beethoven" ));
}
wxDataViewItem MyMusicTreeModel :: GetParent ( const wxDataViewItem & item ) const
{
// the invisible root node has no parent
if ( ! item . IsOk ())
return wxDataViewItem ( 0 );
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
// "MyMusic" also has no parent
if ( node == m_root )
return wxDataViewItem ( 0 );
return wxDataViewItem (( void * ) node -> GetParent ());
}
bool MyMusicTreeModel :: IsContainer ( const wxDataViewItem & item ) const
{
// the invisble root node can have children
// (in our model always "MyMusic")
if ( ! item . IsOk ())
return true ;
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) item . GetID ();
return node -> IsContainer ();
}
unsigned int MyMusicTreeModel :: GetChildren ( const wxDataViewItem & parent ,
wxDataViewItemArray & array ) const
{
MyMusicTreeModelNode * node = ( MyMusicTreeModelNode * ) parent . GetID ();
if ( ! node )
{
array . Add ( wxDataViewItem (( void * ) m_root ));
return 1 ;
}
if ( node == m_classical )
{
MyMusicTreeModel * model = ( MyMusicTreeModel * )( const MyMusicTreeModel * ) this ;
model -> m_classicalMusicIsKnownToControl = true ;
}
if ( node -> GetChildCount () == 0 )
{
return 0 ;
}
unsigned int count = node -> GetChildren (). GetCount ();
for ( unsigned int pos = 0 ; pos < count ; pos ++ )
{
MyMusicTreeModelNode * child = node -> GetChildren (). Item ( pos );
array . Add ( wxDataViewItem (( void * ) child ));
}
return count ;
}
2018-05-04 18:32:20 +02:00
// *****************************************************************************