wxMilliClock_t is always mapped to the proper base type and hence can be used even if wxLongLong type is not defined (when wxUSE_LONGLONG is disabled).
2160 lines
61 KiB
C++
2160 lines
61 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/propgrid/propgridpagestate.cpp
|
|
// Purpose: wxPropertyGridPageState class
|
|
// Author: Jaakko Salli
|
|
// Modified by:
|
|
// Created: 2008-08-24
|
|
// Copyright: (c) Jaakko Salli
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_PROPGRID
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/defs.h"
|
|
#include "wx/object.h"
|
|
#include "wx/hash.h"
|
|
#include "wx/string.h"
|
|
#include "wx/log.h"
|
|
#include "wx/event.h"
|
|
#include "wx/window.h"
|
|
#include "wx/panel.h"
|
|
#include "wx/dc.h"
|
|
#include "wx/dcmemory.h"
|
|
#include "wx/pen.h"
|
|
#include "wx/brush.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/stopwatch.h"
|
|
#endif
|
|
|
|
// This define is necessary to prevent macro clearing
|
|
#define __wxPG_SOURCE_FILE__
|
|
|
|
#include "wx/propgrid/propgridpagestate.h"
|
|
#include "wx/propgrid/propgrid.h"
|
|
#include "wx/propgrid/editors.h"
|
|
|
|
#define wxPG_DEFAULT_SPLITTERX 110
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridIterator
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridIteratorBase::Init( wxPropertyGridPageState* state, int flags, wxPGProperty* property, int dir )
|
|
{
|
|
wxASSERT( dir == 1 || dir == -1 );
|
|
|
|
m_state = state;
|
|
m_baseParent = state->DoGetRoot();
|
|
if ( !property && m_baseParent->GetChildCount() )
|
|
property = m_baseParent->Item(0);
|
|
|
|
m_property = property;
|
|
|
|
wxPG_ITERATOR_CREATE_MASKS(flags, m_itemExMask, m_parentExMask)
|
|
|
|
// Need to skip first?
|
|
if ( property && (property->GetFlags() & m_itemExMask) )
|
|
{
|
|
if ( dir == 1 )
|
|
Next();
|
|
else
|
|
Prev();
|
|
}
|
|
}
|
|
|
|
void wxPropertyGridIteratorBase::Init( wxPropertyGridPageState* state, int flags, int startPos, int dir )
|
|
{
|
|
wxPGProperty* property = NULL;
|
|
|
|
if ( startPos == wxTOP )
|
|
{
|
|
if ( dir == 0 )
|
|
dir = 1;
|
|
}
|
|
else if ( startPos == wxBOTTOM )
|
|
{
|
|
property = state->GetLastItem(flags);
|
|
if ( dir == 0 )
|
|
dir = -1;
|
|
}
|
|
else
|
|
{
|
|
wxFAIL_MSG("Only supported starting positions are wxTOP and wxBOTTOM");
|
|
}
|
|
|
|
Init( state, flags, property, dir );
|
|
}
|
|
|
|
void wxPropertyGridIteratorBase::Assign( const wxPropertyGridIteratorBase& it )
|
|
{
|
|
m_property = it.m_property;
|
|
m_state = it.m_state;
|
|
m_baseParent = it.m_baseParent;
|
|
m_itemExMask = it.m_itemExMask;
|
|
m_parentExMask = it.m_parentExMask;
|
|
}
|
|
|
|
void wxPropertyGridIteratorBase::Prev()
|
|
{
|
|
wxPGProperty* property = m_property;
|
|
if ( !property )
|
|
return;
|
|
|
|
wxPGProperty* parent = property->GetParent();
|
|
wxASSERT( parent );
|
|
unsigned int index = property->GetIndexInParent();
|
|
|
|
if ( index > 0 )
|
|
{
|
|
// Previous sibling
|
|
index--;
|
|
|
|
property = parent->Item(index);
|
|
|
|
// Go to last children?
|
|
if ( property->GetChildCount() &&
|
|
wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) )
|
|
{
|
|
// First child
|
|
property = property->Last();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Up to a parent
|
|
if ( parent == m_baseParent )
|
|
{
|
|
m_property = NULL;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
property = parent;
|
|
}
|
|
}
|
|
|
|
m_property = property;
|
|
|
|
// If property does not match our criteria, skip it
|
|
if ( property->GetFlags() & m_itemExMask )
|
|
Prev();
|
|
}
|
|
|
|
void wxPropertyGridIteratorBase::Next( bool iterateChildren )
|
|
{
|
|
wxPGProperty* property = m_property;
|
|
if ( !property )
|
|
return;
|
|
|
|
if ( property->GetChildCount() &&
|
|
wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) &&
|
|
iterateChildren )
|
|
{
|
|
// First child
|
|
property = property->Item(0);
|
|
}
|
|
else
|
|
{
|
|
wxPGProperty* parent = property->GetParent();
|
|
wxASSERT( parent );
|
|
unsigned int index = property->GetIndexInParent() + 1;
|
|
|
|
if ( index < parent->GetChildCount() )
|
|
{
|
|
// Next sibling
|
|
property = parent->Item(index);
|
|
}
|
|
else
|
|
{
|
|
// Next sibling of parent
|
|
if ( parent == m_baseParent )
|
|
{
|
|
m_property = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_property = parent;
|
|
Next(false);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_property = property;
|
|
|
|
// If property does not match our criteria, skip it
|
|
if ( property->GetFlags() & m_itemExMask )
|
|
Next();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridPageState::wxPropertyGridPageState()
|
|
{
|
|
m_pPropGrid = NULL;
|
|
m_regularArray.SetParentState(this);
|
|
m_properties = &m_regularArray;
|
|
m_abcArray = NULL;
|
|
m_currentCategory = NULL;
|
|
m_width = 0;
|
|
m_virtualHeight = 0;
|
|
m_lastCaptionBottomnest = true;
|
|
m_itemsAdded = false;
|
|
m_anyModified = false;
|
|
m_vhCalcPending = false;
|
|
m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
|
|
m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
|
|
m_fSplitterX = wxPG_DEFAULT_SPLITTERX;
|
|
|
|
m_columnProportions.push_back(1);
|
|
m_columnProportions.push_back(1);
|
|
|
|
m_isSplitterPreSet = false;
|
|
m_dontCenterSplitter = false;
|
|
|
|
// By default, we only have the 'value' column editable
|
|
m_editableColumns.push_back(1);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridPageState::~wxPropertyGridPageState()
|
|
{
|
|
delete m_abcArray;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::InitNonCatMode()
|
|
{
|
|
if ( !m_abcArray )
|
|
{
|
|
m_abcArray = new wxPGRootProperty(wxS("<Root_NonCat>"));
|
|
m_abcArray->SetParentState(this);
|
|
m_abcArray->SetFlag(wxPG_PROP_CHILDREN_ARE_COPIES);
|
|
}
|
|
|
|
// Must be called when state::m_properties still points to regularArray.
|
|
wxPGProperty* oldProperties = m_properties;
|
|
|
|
// Must use temp value in state::m_properties for item iteration loop
|
|
// to run as expected.
|
|
m_properties = &m_regularArray;
|
|
|
|
if ( m_properties->GetChildCount() )
|
|
{
|
|
//
|
|
// Prepare m_abcArray
|
|
wxPropertyGridIterator it( this, wxPG_ITERATE_PROPERTIES );
|
|
|
|
for ( ; !it.AtEnd(); it.Next() )
|
|
{
|
|
wxPGProperty* p = it.GetProperty();
|
|
wxPGProperty* parent = p->GetParent();
|
|
if ( parent->IsCategory() || parent->IsRoot() )
|
|
{
|
|
m_abcArray->DoAddChild(p);
|
|
p->m_parent = &m_regularArray;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_properties = oldProperties;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::DoClear()
|
|
{
|
|
if ( m_pPropGrid && m_pPropGrid->GetState() == this )
|
|
{
|
|
m_pPropGrid->ClearSelection(false);
|
|
}
|
|
else
|
|
{
|
|
m_selection.clear();
|
|
}
|
|
|
|
// If handling wxPG event then every property item must be
|
|
// deleted individually (and with deferral).
|
|
if ( m_pPropGrid && m_pPropGrid->m_processedEvent )
|
|
{
|
|
for (unsigned int i = 0; i < m_regularArray.GetChildCount(); i++)
|
|
{
|
|
wxPGProperty* p = m_regularArray.Item(i);
|
|
DoDelete(p, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Properties which will be deleted immediately
|
|
// should be removed from the lists of pending deletions.
|
|
for (unsigned int i = 0; i < m_regularArray.GetChildCount(); i++)
|
|
{
|
|
wxPGProperty* p = m_regularArray.Item(i);
|
|
int index = m_pPropGrid->m_deletedProperties.Index(p);
|
|
if (index != wxNOT_FOUND)
|
|
{
|
|
m_pPropGrid->m_deletedProperties.RemoveAt(index);
|
|
}
|
|
index = m_pPropGrid->m_removedProperties.Index(p);
|
|
if (index != wxNOT_FOUND)
|
|
{
|
|
m_pPropGrid->m_removedProperties.RemoveAt(index);
|
|
}
|
|
}
|
|
|
|
m_regularArray.Empty();
|
|
if ( m_abcArray )
|
|
m_abcArray->Empty();
|
|
|
|
m_dictName.clear();
|
|
|
|
m_currentCategory = NULL;
|
|
m_lastCaptionBottomnest = true;
|
|
m_itemsAdded = false;
|
|
|
|
m_virtualHeight = 0;
|
|
m_vhCalcPending = false;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::CalculateFontAndBitmapStuff( int WXUNUSED(vspacing) )
|
|
{
|
|
wxPropertyGrid* propGrid = GetGrid();
|
|
|
|
VirtualHeightChanged();
|
|
|
|
// Recalculate caption text extents.
|
|
unsigned int i;
|
|
|
|
for ( i=0;i<m_regularArray.GetChildCount();i++ )
|
|
{
|
|
wxPGProperty* p =m_regularArray.Item(i);
|
|
|
|
if ( p->IsCategory() )
|
|
((wxPropertyCategory*)p)->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::SetVirtualWidth( int width )
|
|
{
|
|
// Sometimes width less than 0 is offered. Let's make things easy for
|
|
// everybody and deal with it here.
|
|
if ( width < 0 )
|
|
width = 0;
|
|
|
|
wxPropertyGrid* pg = GetGrid();
|
|
int gw = pg->GetClientSize().x;
|
|
if ( width < gw )
|
|
width = gw;
|
|
|
|
m_width = width;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::OnClientWidthChange( int newWidth, int widthChange, bool fromOnResize )
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
if ( pg->HasVirtualWidth() )
|
|
{
|
|
if ( m_width < newWidth )
|
|
SetVirtualWidth( newWidth );
|
|
|
|
CheckColumnWidths(widthChange);
|
|
}
|
|
else
|
|
{
|
|
SetVirtualWidth( newWidth );
|
|
|
|
// This should be done before splitter auto centering
|
|
// NOTE: Splitter auto-centering is done in this function.
|
|
if ( !fromOnResize )
|
|
widthChange = 0;
|
|
CheckColumnWidths(widthChange);
|
|
|
|
if ( !m_isSplitterPreSet && m_dontCenterSplitter )
|
|
{
|
|
wxMilliClock_t timeSinceCreation = ::wxGetLocalTimeMillis() - GetGrid()->m_timeCreated;
|
|
|
|
// If too long, don't set splitter
|
|
if ( timeSinceCreation < 250 )
|
|
{
|
|
if ( m_properties->GetChildCount() )
|
|
{
|
|
SetSplitterLeft( false );
|
|
}
|
|
else
|
|
{
|
|
DoSetSplitterPosition( newWidth / 2 );
|
|
m_isSplitterPreSet = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState item iteration methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyGridPageState::GetLastItem( int flags )
|
|
{
|
|
if ( !m_properties->GetChildCount() )
|
|
return NULL;
|
|
|
|
wxPG_ITERATOR_CREATE_MASKS(flags, int itemExMask, int parentExMask)
|
|
|
|
// First, get last child of last parent
|
|
wxPGProperty* pwc = (wxPGProperty*)m_properties->Last();
|
|
while ( pwc->GetChildCount() &&
|
|
wxPG_ITERATOR_PARENTEXMASK_TEST(pwc, parentExMask) )
|
|
pwc = (wxPGProperty*) pwc->Last();
|
|
|
|
// Then, if it doesn't fit our criteria, back up until we find something that does
|
|
if ( pwc->GetFlags() & itemExMask )
|
|
{
|
|
wxPropertyGridIterator it( this, flags, pwc );
|
|
for ( ; !it.AtEnd(); it.Prev() )
|
|
;
|
|
pwc = (wxPGProperty*) it.GetProperty();
|
|
}
|
|
|
|
return pwc;
|
|
}
|
|
|
|
wxPropertyCategory* wxPropertyGridPageState::GetPropertyCategory( const wxPGProperty* p ) const
|
|
{
|
|
const wxPGProperty* parent = (const wxPGProperty*)p;
|
|
const wxPGProperty* grandparent = (const wxPGProperty*)parent->GetParent();
|
|
do
|
|
{
|
|
parent = grandparent;
|
|
grandparent = (wxPGProperty*)parent->GetParent();
|
|
if ( parent->IsCategory() && grandparent )
|
|
return (wxPropertyCategory*)parent;
|
|
} while ( grandparent );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState GetPropertyXXX methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if WXWIN_COMPATIBILITY_3_0
|
|
wxPGProperty* wxPropertyGridPageState::GetPropertyByLabel
|
|
( const wxString& label, wxPGProperty* parent ) const
|
|
{
|
|
return BaseGetPropertyByLabel(label, parent);
|
|
}
|
|
#endif // WXWIN_COMPATIBILITY_3_0
|
|
|
|
wxPGProperty* wxPropertyGridPageState::BaseGetPropertyByLabel
|
|
( const wxString& label, wxPGProperty* parent ) const
|
|
{
|
|
if ( !parent )
|
|
{
|
|
parent = (wxPGProperty*) &m_regularArray;
|
|
}
|
|
|
|
for ( size_t i=0; i<parent->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* p = parent->Item(i);
|
|
if ( p->GetLabel() == label )
|
|
return p;
|
|
// Check children recursively.
|
|
if ( p->GetChildCount() )
|
|
{
|
|
p = BaseGetPropertyByLabel(label, p);
|
|
if ( p )
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyGridPageState::BaseGetPropertyByName( const wxString& name ) const
|
|
{
|
|
wxPGHashMapS2P::const_iterator it;
|
|
it = m_dictName.find(name);
|
|
if ( it != m_dictName.end() )
|
|
return (wxPGProperty*) it->second;
|
|
return NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::DoSetPropertyName( wxPGProperty* p,
|
|
const wxString& newName )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
|
|
wxPGProperty* parent = p->GetParent();
|
|
|
|
if ( parent->IsCategory() || parent->IsRoot() )
|
|
{
|
|
if ( !p->GetBaseName().empty() )
|
|
m_dictName.erase( p->GetBaseName() );
|
|
if ( !newName.empty() )
|
|
m_dictName[newName] = (void*) p;
|
|
}
|
|
|
|
p->DoSetName(newName);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState global operations
|
|
// -----------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Item iteration macros
|
|
// NB: Nowadays only needed for alphabetic/categoric mode switching.
|
|
// -----------------------------------------------------------------------
|
|
|
|
//#define II_INVALID_I 0x00FFFFFF
|
|
|
|
#define ITEM_ITERATION_VARIABLES \
|
|
wxPGProperty* parent; \
|
|
unsigned int i; \
|
|
unsigned int iMax;
|
|
|
|
#define ITEM_ITERATION_INIT_FROM_THE_TOP \
|
|
parent = m_properties; \
|
|
i = 0;
|
|
|
|
#if 0
|
|
#define ITEM_ITERATION_INIT(startparent, startindex, state) \
|
|
parent = startparent; \
|
|
i = (unsigned int)startindex; \
|
|
if ( parent == NULL ) \
|
|
{ \
|
|
parent = state->m_properties; \
|
|
i = 0; \
|
|
}
|
|
#endif
|
|
|
|
#define ITEM_ITERATION_LOOP_BEGIN \
|
|
do \
|
|
{ \
|
|
iMax = parent->GetChildCount(); \
|
|
while ( i < iMax ) \
|
|
{ \
|
|
wxPGProperty* p = parent->Item(i);
|
|
|
|
#define ITEM_ITERATION_LOOP_END \
|
|
if ( p->GetChildCount() ) \
|
|
{ \
|
|
i = 0; \
|
|
parent = (wxPGProperty*)p; \
|
|
iMax = parent->GetChildCount(); \
|
|
} \
|
|
else \
|
|
i++; \
|
|
} \
|
|
i = parent->m_arrIndex + 1; \
|
|
parent = parent->m_parent; \
|
|
} \
|
|
while ( parent != NULL );
|
|
|
|
bool wxPropertyGridPageState::EnableCategories( bool enable )
|
|
{
|
|
//
|
|
// NB: We can't use wxPropertyGridIterator in this
|
|
// function, since it depends on m_arrIndexes,
|
|
// which, among other things, is being fixed here.
|
|
//
|
|
ITEM_ITERATION_VARIABLES
|
|
|
|
if ( enable )
|
|
{
|
|
//
|
|
// Enable categories
|
|
//
|
|
|
|
if ( !IsInNonCatMode() )
|
|
return false;
|
|
|
|
m_properties = &m_regularArray;
|
|
|
|
// fix parents, indexes, and depths
|
|
ITEM_ITERATION_INIT_FROM_THE_TOP
|
|
|
|
ITEM_ITERATION_LOOP_BEGIN
|
|
|
|
p->m_arrIndex = i;
|
|
|
|
p->m_parent = parent;
|
|
|
|
// If parent was category, and this is not,
|
|
// then the depth stays the same.
|
|
if ( parent->IsCategory() &&
|
|
!p->IsCategory() )
|
|
p->m_depth = parent->m_depth;
|
|
else
|
|
p->m_depth = parent->m_depth + 1;
|
|
|
|
ITEM_ITERATION_LOOP_END
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Disable categories
|
|
//
|
|
|
|
if ( IsInNonCatMode() )
|
|
return false;
|
|
|
|
// Create array, if necessary.
|
|
if ( !m_abcArray )
|
|
InitNonCatMode();
|
|
|
|
m_properties = m_abcArray;
|
|
|
|
// fix parents, indexes, and depths
|
|
ITEM_ITERATION_INIT_FROM_THE_TOP
|
|
|
|
ITEM_ITERATION_LOOP_BEGIN
|
|
|
|
p->m_arrIndex = i;
|
|
|
|
p->m_parent = parent;
|
|
|
|
p->m_depth = parent->m_depth + 1;
|
|
|
|
ITEM_ITERATION_LOOP_END
|
|
}
|
|
|
|
VirtualHeightChanged();
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
m_pPropGrid->RecalculateVirtualSize();
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static int wxPG_SortFunc_ByFunction(wxPGProperty **pp1, wxPGProperty **pp2)
|
|
{
|
|
wxPGProperty *p1 = *pp1;
|
|
wxPGProperty *p2 = *pp2;
|
|
wxPropertyGrid* pg = p1->GetGrid();
|
|
wxPGSortCallback sortFunction = pg->GetSortFunction();
|
|
return sortFunction(pg, p1, p2);
|
|
}
|
|
|
|
static int wxPG_SortFunc_ByLabel(wxPGProperty **pp1, wxPGProperty **pp2)
|
|
{
|
|
wxPGProperty *p1 = *pp1;
|
|
wxPGProperty *p2 = *pp2;
|
|
return p1->GetLabel().CmpNoCase( p2->GetLabel() );
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// For wxVector w/ wxUSE_STL=1, you would use code like this instead:
|
|
//
|
|
|
|
#include <algorithm>
|
|
|
|
static bool wxPG_SortFunc_ByFunction(wxPGProperty *p1, wxPGProperty *p2)
|
|
{
|
|
wxPropertyGrid* pg = p1->GetGrid();
|
|
wxPGSortCallback sortFunction = pg->GetSortFunction();
|
|
return sortFunction(pg, p1, p2) < 0;
|
|
}
|
|
|
|
static bool wxPG_SortFunc_ByLabel(wxPGProperty *p1, wxPGProperty *p2)
|
|
{
|
|
return p1->GetLabel().CmpNoCase( p2->GetLabel() ) < 0;
|
|
}
|
|
#endif
|
|
|
|
void wxPropertyGridPageState::DoSortChildren( wxPGProperty* p,
|
|
int flags )
|
|
{
|
|
if ( !p )
|
|
p = m_properties;
|
|
|
|
// Can only sort items with children
|
|
if ( !p->GetChildCount() )
|
|
return;
|
|
|
|
// Never sort children of aggregate properties
|
|
if ( p->HasFlag(wxPG_PROP_AGGREGATE) )
|
|
return;
|
|
|
|
if ( (flags & wxPG_SORT_TOP_LEVEL_ONLY)
|
|
&& !p->IsCategory() && !p->IsRoot() )
|
|
return;
|
|
|
|
if ( GetGrid()->GetSortFunction() )
|
|
p->SortChildren(wxPG_SortFunc_ByFunction);
|
|
else
|
|
p->SortChildren(wxPG_SortFunc_ByLabel);
|
|
|
|
// Fix indices
|
|
p->FixIndicesOfChildren();
|
|
|
|
if ( flags & wxPG_RECURSE )
|
|
{
|
|
// Apply sort recursively
|
|
for ( unsigned int i=0; i<p->GetChildCount(); i++ )
|
|
DoSortChildren(p->Item(i), flags);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::DoSort( int flags )
|
|
{
|
|
DoSortChildren( m_properties, flags | wxPG_RECURSE );
|
|
|
|
// We used to sort categories as well here also if in non-categorized
|
|
// mode, but doing would naturally cause child indices to become
|
|
// corrupted.
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::PrepareAfterItemsAdded()
|
|
{
|
|
if ( !m_itemsAdded ) return false;
|
|
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
m_itemsAdded = false;
|
|
|
|
if ( pg->HasFlag(wxPG_AUTO_SORT) )
|
|
DoSort(wxPG_SORT_TOP_LEVEL_ONLY);
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState splitter, column and hittest functions
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyGridPageState::DoGetItemAtY( int y ) const
|
|
{
|
|
// Outside?
|
|
if ( y < 0 )
|
|
return NULL;
|
|
|
|
unsigned int a = 0;
|
|
return m_properties->GetItemAtY(y, GetGrid()->m_lineHeight, &a);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridHitTestResult
|
|
wxPropertyGridPageState::HitTest( const wxPoint&pt ) const
|
|
{
|
|
wxPropertyGridHitTestResult result;
|
|
result.m_column = HitTestH( pt.x, &result.m_splitter,
|
|
&result.m_splitterHitOffset );
|
|
result.m_property = DoGetItemAtY( pt.y );
|
|
return result;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Used by SetSplitterLeft() and DotFitColumns()
|
|
int wxPropertyGridPageState::GetColumnFitWidth(wxClientDC& dc,
|
|
wxPGProperty* pwc,
|
|
unsigned int col,
|
|
bool subProps) const
|
|
{
|
|
wxPropertyGrid* pg = m_pPropGrid;
|
|
size_t i;
|
|
int maxW = 0;
|
|
int w, h;
|
|
|
|
for ( i=0; i<pwc->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* p = pwc->Item(i);
|
|
if ( !p->IsCategory() )
|
|
{
|
|
wxString text;
|
|
p->GetDisplayInfo(col, -1, 0, &text, (wxPGCell*)NULL);
|
|
dc.GetTextExtent(text, &w, &h);
|
|
if ( col == 0 )
|
|
w += ( (p->GetDepth()-1) * pg->m_subgroup_extramargin );
|
|
|
|
// account for the bitmap
|
|
if ( col == 1 )
|
|
w += p->GetImageOffset(pg->GetImageRect(p, -1).GetWidth());
|
|
|
|
|
|
w += (wxPG_XBEFORETEXT*2);
|
|
|
|
if ( w > maxW )
|
|
maxW = w;
|
|
}
|
|
|
|
if ( p->GetChildCount() &&
|
|
( subProps || p->IsCategory() ) )
|
|
{
|
|
w = GetColumnFitWidth( dc, p, col, subProps );
|
|
|
|
if ( w > maxW )
|
|
maxW = w;
|
|
}
|
|
}
|
|
|
|
return maxW;
|
|
}
|
|
|
|
int wxPropertyGridPageState::GetColumnFullWidth( wxClientDC &dc, wxPGProperty *p, unsigned int col )
|
|
{
|
|
if ( p->IsCategory() )
|
|
return 0;
|
|
|
|
wxString text;
|
|
p->GetDisplayInfo(col, -1, 0, &text, (wxPGCell*)NULL);
|
|
int w = dc.GetTextExtent(text).x;
|
|
|
|
if ( col == 0 )
|
|
w += p->GetDepth() * m_pPropGrid->m_subgroup_extramargin;
|
|
|
|
// account for the bitmap
|
|
if ( col == 1 )
|
|
w += p->GetImageOffset(m_pPropGrid->GetImageRect(p, -1).GetWidth());
|
|
|
|
w += (wxPG_XBEFORETEXT*2);
|
|
return w;
|
|
}
|
|
|
|
int wxPropertyGridPageState::DoGetSplitterPosition( int splitterColumn ) const
|
|
{
|
|
int n = GetGrid()->m_marginWidth;
|
|
int i;
|
|
for ( i=0; i<=splitterColumn; i++ )
|
|
n += m_colWidths[i];
|
|
return n;
|
|
}
|
|
|
|
int wxPropertyGridPageState::GetColumnMinWidth( int WXUNUSED(column) ) const
|
|
{
|
|
return wxPG_DRAG_MARGIN;
|
|
}
|
|
|
|
void wxPropertyGridPageState::PropagateColSizeDec( int column,
|
|
int decrease,
|
|
int dir )
|
|
{
|
|
wxASSERT( decrease >= 0 );
|
|
wxASSERT( dir == 1 || dir == -1 );
|
|
|
|
int col = column;
|
|
while(decrease > 0 && col >= 0 && col < (int)m_colWidths.size())
|
|
{
|
|
const int origWidth = m_colWidths[col];
|
|
const int min = GetColumnMinWidth(col);
|
|
m_colWidths[col] -= decrease;
|
|
if ( m_colWidths[col] < min )
|
|
{
|
|
m_colWidths[col] = min;
|
|
}
|
|
decrease -= (origWidth - m_colWidths[col]);
|
|
col += dir;
|
|
}
|
|
// As a last resort, if change of width was not fully absorbed
|
|
// on the requested side we try to do this on the other side.
|
|
col = column;
|
|
dir *= -1;
|
|
while(decrease > 0 && col >= 0 && col < (int)m_colWidths.size())
|
|
{
|
|
const int origWidth = m_colWidths[col];
|
|
const int min = GetColumnMinWidth(col);
|
|
m_colWidths[col] -= decrease;
|
|
if ( m_colWidths[col] < min )
|
|
{
|
|
m_colWidths[col] = min;
|
|
}
|
|
decrease -= (origWidth - m_colWidths[col]);
|
|
col += dir;
|
|
}
|
|
|
|
wxASSERT( decrease == 0 );
|
|
}
|
|
|
|
void wxPropertyGridPageState::DoSetSplitterPosition( int newXPos,
|
|
int splitterColumn,
|
|
int flags )
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
int adjust = newXPos - DoGetSplitterPosition(splitterColumn);
|
|
|
|
if ( !pg->HasVirtualWidth() )
|
|
{
|
|
// No virtual width
|
|
int otherColumn;
|
|
if ( adjust > 0 )
|
|
{
|
|
otherColumn = splitterColumn + 1;
|
|
if ( otherColumn == (int)m_colWidths.size() )
|
|
otherColumn = 0;
|
|
m_colWidths[splitterColumn] += adjust;
|
|
PropagateColSizeDec( otherColumn, adjust, 1 );
|
|
}
|
|
else if ( adjust < 0 )
|
|
{
|
|
otherColumn = splitterColumn + 1;
|
|
if ( otherColumn == (int)m_colWidths.size() )
|
|
otherColumn = 0;
|
|
m_colWidths[otherColumn] -= adjust;
|
|
PropagateColSizeDec( splitterColumn, -adjust, -1 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_colWidths[splitterColumn] += adjust;
|
|
}
|
|
// Actual adjustment can be different from demanded.
|
|
newXPos = DoGetSplitterPosition(splitterColumn);
|
|
|
|
if ( splitterColumn == 0 )
|
|
m_fSplitterX = (double) newXPos;
|
|
|
|
if ( !(flags & wxPG_SPLITTER_FROM_AUTO_CENTER) &&
|
|
!(flags & wxPG_SPLITTER_FROM_EVENT) )
|
|
{
|
|
// Don't allow initial splitter auto-positioning after this.
|
|
m_isSplitterPreSet = true;
|
|
|
|
CheckColumnWidths();
|
|
}
|
|
}
|
|
|
|
// Moves splitter so that all labels are visible, but just.
|
|
void wxPropertyGridPageState::SetSplitterLeft( bool subProps )
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
wxClientDC dc(pg);
|
|
dc.SetFont(pg->GetFont());
|
|
|
|
int maxW = GetColumnFitWidth(dc, m_properties, 0, subProps);
|
|
|
|
if ( maxW > 0 )
|
|
{
|
|
maxW += pg->m_marginWidth;
|
|
DoSetSplitterPosition( maxW );
|
|
}
|
|
|
|
m_dontCenterSplitter = true;
|
|
}
|
|
|
|
wxSize wxPropertyGridPageState::DoFitColumns( bool WXUNUSED(allowGridResize) )
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
wxClientDC dc(pg);
|
|
dc.SetFont(pg->GetFont());
|
|
|
|
int marginWidth = pg->m_marginWidth;
|
|
int accWid = marginWidth;
|
|
int maxColWidth = 500;
|
|
|
|
for ( unsigned int col=0; col < GetColumnCount(); col++ )
|
|
{
|
|
int fitWid = GetColumnFitWidth(dc, m_properties, col, true);
|
|
int colMinWidth = GetColumnMinWidth(col);
|
|
if ( fitWid < colMinWidth )
|
|
fitWid = colMinWidth;
|
|
else if ( fitWid > maxColWidth )
|
|
fitWid = maxColWidth;
|
|
|
|
m_colWidths[col] = fitWid;
|
|
|
|
accWid += fitWid;
|
|
}
|
|
|
|
// Expand last one to fill the width
|
|
int remaining = m_width - accWid;
|
|
m_colWidths[GetColumnCount()-1] += remaining;
|
|
|
|
m_dontCenterSplitter = true;
|
|
|
|
int firstSplitterX = marginWidth + m_colWidths[0];
|
|
m_fSplitterX = (double) firstSplitterX;
|
|
|
|
// Don't allow initial splitter auto-positioning after this.
|
|
if ( pg->GetState() == this )
|
|
{
|
|
pg->SetSplitterPosition(firstSplitterX, false);
|
|
pg->Refresh();
|
|
}
|
|
|
|
int x, y;
|
|
pg->GetVirtualSize(&x, &y);
|
|
|
|
return wxSize(accWid, y);
|
|
}
|
|
|
|
void wxPropertyGridPageState::CheckColumnWidths( int widthChange )
|
|
{
|
|
if ( m_width == 0 )
|
|
return;
|
|
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
unsigned int i;
|
|
unsigned int lastColumn = m_colWidths.size() - 1;
|
|
int width = m_width;
|
|
int clientWidth = pg->GetClientSize().x;
|
|
|
|
//
|
|
// Column to reduce, if needed. Take last one that exceeds minimum width.
|
|
int reduceCol = -1;
|
|
|
|
wxLogTrace("propgrid",
|
|
wxS("ColumnWidthCheck (virtualWidth: %i, clientWidth: %i)"),
|
|
width, clientWidth);
|
|
|
|
//
|
|
// Check min sizes
|
|
for ( i=0; i<m_colWidths.size(); i++ )
|
|
{
|
|
int min = GetColumnMinWidth(i);
|
|
if ( m_colWidths[i] <= min )
|
|
{
|
|
m_colWidths[i] = min;
|
|
}
|
|
else
|
|
{
|
|
// Always reduce the last column that is larger than minimum size
|
|
// (looks nicer, even with auto-centering enabled).
|
|
reduceCol = i;
|
|
}
|
|
}
|
|
|
|
int colsWidth = pg->m_marginWidth;
|
|
for ( i=0; i<m_colWidths.size(); i++ )
|
|
colsWidth += m_colWidths[i];
|
|
|
|
wxLogTrace("propgrid",
|
|
wxS(" HasVirtualWidth: %i colsWidth: %i"),
|
|
(int)pg->HasVirtualWidth(), colsWidth);
|
|
|
|
// Then mode-based requirement
|
|
if ( !pg->HasVirtualWidth() )
|
|
{
|
|
int widthHigher = width - colsWidth;
|
|
|
|
// Adapt colsWidth to width
|
|
if ( colsWidth < width )
|
|
{
|
|
// Increase column
|
|
wxLogTrace("propgrid",
|
|
wxS(" Adjust last column to %i"),
|
|
m_colWidths[lastColumn] + widthHigher);
|
|
m_colWidths[lastColumn] = m_colWidths[lastColumn] + widthHigher;
|
|
}
|
|
else if ( colsWidth > width )
|
|
{
|
|
// Reduce column
|
|
if ( reduceCol != -1 )
|
|
{
|
|
wxLogTrace("propgrid",
|
|
wxT(" Reduce column %i (by %i)"),
|
|
reduceCol, -widthHigher);
|
|
|
|
// Reduce widest column, and recheck
|
|
m_colWidths[reduceCol] = m_colWidths[reduceCol] + widthHigher;
|
|
CheckColumnWidths();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only check colsWidth against clientWidth
|
|
if ( colsWidth < clientWidth )
|
|
{
|
|
m_colWidths[lastColumn] = m_colWidths[lastColumn] + (clientWidth-colsWidth);
|
|
}
|
|
|
|
m_width = colsWidth;
|
|
|
|
// If width changed, recalculate virtual size
|
|
if ( pg->GetState() == this )
|
|
pg->RecalculateVirtualSize();
|
|
}
|
|
|
|
for ( i=0; i<m_colWidths.size(); i++ )
|
|
{
|
|
wxLogTrace("propgrid", wxS("col%i: %i"), i, m_colWidths[i]);
|
|
}
|
|
|
|
// Auto center splitter
|
|
if ( !m_dontCenterSplitter )
|
|
{
|
|
if ( m_colWidths.size() == 2 &&
|
|
m_columnProportions[0] == m_columnProportions[1] )
|
|
{
|
|
//
|
|
// When we have two columns of equal proportion, then use this
|
|
// code. It will look nicer when the scrollbar visibility is
|
|
// toggled on and off.
|
|
//
|
|
// TODO: Adapt this to generic recenter code.
|
|
//
|
|
double centerX = pg->m_width / 2.0;
|
|
double splitterX;
|
|
|
|
if ( m_fSplitterX < 0.0 )
|
|
{
|
|
splitterX = centerX;
|
|
}
|
|
else if ( widthChange )
|
|
{
|
|
//float centerX = float(pg->GetSize().x) * 0.5;
|
|
|
|
// Recenter?
|
|
splitterX = m_fSplitterX + (widthChange * 0.5);
|
|
double deviation = fabs(centerX - splitterX);
|
|
|
|
// If deviating from center, adjust towards it
|
|
if ( deviation > 20.0 )
|
|
{
|
|
if ( splitterX > centerX)
|
|
splitterX -= 2;
|
|
else
|
|
splitterX += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No width change, just keep sure we keep splitter position intact
|
|
splitterX = m_fSplitterX;
|
|
double deviation = fabs(centerX - splitterX);
|
|
if ( deviation > 50.0 )
|
|
{
|
|
splitterX = centerX;
|
|
}
|
|
}
|
|
|
|
DoSetSplitterPosition((int)splitterX, 0,
|
|
wxPG_SPLITTER_FROM_AUTO_CENTER);
|
|
|
|
m_fSplitterX = splitterX; // needed to retain accuracy
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Generic re-center code
|
|
//
|
|
ResetColumnSizes(wxPG_SPLITTER_FROM_AUTO_CENTER);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxPropertyGridPageState::ResetColumnSizes( int setSplitterFlags )
|
|
{
|
|
unsigned int i;
|
|
// Calculate sum of proportions
|
|
int psum = 0;
|
|
for ( i=0; i<m_colWidths.size(); i++ )
|
|
psum += m_columnProportions[i];
|
|
int puwid = (m_pPropGrid->m_width*256) / psum;
|
|
int cpos = 0;
|
|
|
|
// Convert proportion to splitter positions
|
|
for ( i=0; i<(m_colWidths.size() - 1); i++ )
|
|
{
|
|
int cwid = (puwid*m_columnProportions[i]) / 256;
|
|
cpos += cwid;
|
|
DoSetSplitterPosition(cpos, i,
|
|
setSplitterFlags);
|
|
}
|
|
}
|
|
|
|
void wxPropertyGridPageState::SetColumnCount( int colCount )
|
|
{
|
|
wxASSERT( colCount >= 2 );
|
|
m_colWidths.SetCount( colCount, wxPG_DRAG_MARGIN );
|
|
m_columnProportions.SetCount( colCount, 1 );
|
|
if ( m_colWidths.size() > (unsigned int)colCount )
|
|
m_colWidths.RemoveAt( m_colWidths.size()-1,
|
|
m_colWidths.size() - colCount );
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
m_pPropGrid->RecalculateVirtualSize();
|
|
else
|
|
CheckColumnWidths();
|
|
}
|
|
|
|
void wxPropertyGridPageState::DoSetColumnProportion( unsigned int column,
|
|
int proportion )
|
|
{
|
|
wxASSERT_MSG( proportion >= 1,
|
|
"Column proportion must 1 or higher" );
|
|
|
|
if ( proportion < 1 )
|
|
proportion = 1;
|
|
|
|
while ( m_columnProportions.size() <= column )
|
|
m_columnProportions.push_back(1);
|
|
|
|
m_columnProportions[column] = proportion;
|
|
}
|
|
|
|
// Returns column index, -1 for margin
|
|
int wxPropertyGridPageState::HitTestH( int x, int* pSplitterHit, int* pSplitterHitOffset ) const
|
|
{
|
|
int cx = GetGrid()->m_marginWidth;
|
|
int col = -1;
|
|
int prevSplitter = -1;
|
|
|
|
while ( x > cx )
|
|
{
|
|
col++;
|
|
if ( col >= (int)m_colWidths.size() )
|
|
{
|
|
*pSplitterHit = -1;
|
|
return col;
|
|
}
|
|
prevSplitter = cx;
|
|
cx += m_colWidths[col];
|
|
}
|
|
|
|
// Near prev. splitter
|
|
if ( col >= 1 )
|
|
{
|
|
int diff = x - prevSplitter;
|
|
if ( abs(diff) < wxPG_SPLITTERX_DETECTMARGIN1 )
|
|
{
|
|
*pSplitterHit = col - 1;
|
|
*pSplitterHitOffset = diff;
|
|
return col;
|
|
}
|
|
}
|
|
|
|
// Near next splitter
|
|
int nextSplitter = cx;
|
|
if ( col < (int)(m_colWidths.size()-1) )
|
|
{
|
|
int diff = x - nextSplitter;
|
|
if ( abs(diff) < wxPG_SPLITTERX_DETECTMARGIN1 )
|
|
{
|
|
*pSplitterHit = col;
|
|
*pSplitterHitOffset = diff;
|
|
return col;
|
|
}
|
|
}
|
|
|
|
*pSplitterHit = -1;
|
|
return col;
|
|
}
|
|
|
|
bool wxPropertyGridPageState::ArePropertiesAdjacent( wxPGProperty* prop1,
|
|
wxPGProperty* prop2,
|
|
int iterFlags ) const
|
|
{
|
|
const wxPGProperty* ap1 =
|
|
wxPropertyGridConstIterator::OneStep(this,
|
|
iterFlags,
|
|
prop1,
|
|
1);
|
|
if ( ap1 && ap1 == prop2 )
|
|
return true;
|
|
|
|
const wxPGProperty* ap2 =
|
|
wxPropertyGridConstIterator::OneStep(this,
|
|
iterFlags,
|
|
prop1,
|
|
-1);
|
|
if ( ap2 && ap2 == prop2 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState property value setting and getting
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoSetPropertyValueString( wxPGProperty* p, const wxString& value )
|
|
{
|
|
if ( p )
|
|
{
|
|
int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE|wxPG_PROGRAMMATIC_VALUE;
|
|
|
|
wxVariant variant = p->GetValueRef();
|
|
bool res;
|
|
|
|
if ( p->GetMaxLength() <= 0 )
|
|
res = p->StringToValue( variant, value, flags );
|
|
else
|
|
res = p->StringToValue( variant, value.Mid(0,p->GetMaxLength()), flags );
|
|
|
|
if ( res )
|
|
{
|
|
p->SetValue(variant);
|
|
if ( p == m_pPropGrid->GetSelection() &&
|
|
this == m_pPropGrid->GetState() )
|
|
m_pPropGrid->RefreshEditor();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoSetPropertyValue( wxPGProperty* p, wxVariant& value )
|
|
{
|
|
if ( p )
|
|
{
|
|
p->SetValue(value);
|
|
if ( p == m_pPropGrid->GetSelection() &&
|
|
this == m_pPropGrid->GetState() )
|
|
m_pPropGrid->RefreshEditor();
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoSetPropertyValueWxObjectPtr( wxPGProperty* p, wxObject* value )
|
|
{
|
|
if ( p )
|
|
{
|
|
// wnd_primary has to be given so the control can be updated as well.
|
|
wxVariant v(value);
|
|
DoSetPropertyValue(p, v);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState property operations
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoIsPropertySelected( wxPGProperty* prop ) const
|
|
{
|
|
if ( wxPGFindInVector(m_selection, prop) != wxNOT_FOUND )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::DoRemoveFromSelection( wxPGProperty* prop )
|
|
{
|
|
for ( unsigned int i=0; i<m_selection.size(); i++ )
|
|
{
|
|
if ( m_selection[i] == prop )
|
|
{
|
|
wxPropertyGrid* pg = m_pPropGrid;
|
|
if ( i == 0 && pg->GetState() == this )
|
|
{
|
|
// If first item (ie. one with the active editor) was
|
|
// deselected, then we need to take some extra measures.
|
|
wxArrayPGProperty sel = m_selection;
|
|
sel.erase( sel.begin() + i );
|
|
|
|
wxPGProperty* newFirst;
|
|
if ( sel.size() )
|
|
newFirst = sel[0];
|
|
else
|
|
newFirst = NULL;
|
|
|
|
pg->DoSelectProperty(newFirst,
|
|
wxPG_SEL_DONT_SEND_EVENT);
|
|
|
|
m_selection = sel;
|
|
|
|
pg->Refresh();
|
|
}
|
|
else
|
|
{
|
|
m_selection.erase( m_selection.begin() + i );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoCollapse( wxPGProperty* p )
|
|
{
|
|
wxCHECK_MSG( p, false, wxT("invalid property id") );
|
|
|
|
if ( !p->GetChildCount() ) return false;
|
|
|
|
if ( !p->IsExpanded() ) return false;
|
|
|
|
p->SetExpanded(false);
|
|
|
|
VirtualHeightChanged();
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoExpand( wxPGProperty* p )
|
|
{
|
|
wxCHECK_MSG( p, false, wxT("invalid property id") );
|
|
|
|
if ( !p->GetChildCount() ) return false;
|
|
|
|
if ( p->IsExpanded() ) return false;
|
|
|
|
p->SetExpanded(true);
|
|
|
|
VirtualHeightChanged();
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoSelectProperty( wxPGProperty* p, unsigned int flags )
|
|
{
|
|
if ( this == m_pPropGrid->GetState() )
|
|
return m_pPropGrid->DoSelectProperty( p, flags );
|
|
|
|
DoSetSelection(p);
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::DoHideProperty( wxPGProperty* p, bool hide, int flags )
|
|
{
|
|
p->DoHide(hide, flags);
|
|
VirtualHeightChanged();
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState wxVariant related routines
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Returns list of wxVariant objects (non-categories and non-sub-properties only).
|
|
// Never includes sub-properties (unless they are parented by wxParentProperty).
|
|
wxVariant wxPropertyGridPageState::DoGetPropertyValues( const wxString& listname,
|
|
wxPGProperty* baseparent,
|
|
long flags ) const
|
|
{
|
|
wxPGProperty* pwc = (wxPGProperty*) baseparent;
|
|
|
|
// Root is the default base-parent.
|
|
if ( !pwc )
|
|
pwc = m_properties;
|
|
|
|
wxVariantList tempList;
|
|
wxVariant v( tempList, listname );
|
|
|
|
if ( pwc->GetChildCount() )
|
|
{
|
|
if ( flags & wxPG_KEEP_STRUCTURE )
|
|
{
|
|
wxASSERT( !pwc->HasFlag(wxPG_PROP_AGGREGATE) );
|
|
|
|
size_t i;
|
|
for ( i=0; i<pwc->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* p = pwc->Item(i);
|
|
if ( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE) )
|
|
{
|
|
wxVariant variant = p->GetValue();
|
|
variant.SetName( p->GetBaseName() );
|
|
v.Append( variant );
|
|
}
|
|
else
|
|
{
|
|
v.Append( DoGetPropertyValues(p->GetBaseName(),p,flags|wxPG_KEEP_STRUCTURE) );
|
|
}
|
|
if ( (flags & wxPG_INC_ATTRIBUTES) && p->GetAttributes().GetCount() )
|
|
v.Append( p->GetAttributesAsList() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxPropertyGridConstIterator it( this, wxPG_ITERATE_DEFAULT, pwc->Item(0) );
|
|
it.SetBaseParent( pwc );
|
|
|
|
for ( ; !it.AtEnd(); it.Next() )
|
|
{
|
|
const wxPGProperty* p = it.GetProperty();
|
|
|
|
// Use a trick to ignore wxParentProperty itself, but not its sub-properties.
|
|
if ( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE) )
|
|
{
|
|
wxVariant variant = p->GetValue();
|
|
variant.SetName( p->GetName() );
|
|
v.Append( variant );
|
|
if ( (flags & wxPG_INC_ATTRIBUTES) && p->GetAttributes().GetCount() )
|
|
v.Append( p->GetAttributesAsList() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wxPGProperty* defaultCategory )
|
|
{
|
|
bool origFrozen = true;
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
{
|
|
origFrozen = m_pPropGrid->IsFrozen();
|
|
if ( !origFrozen ) m_pPropGrid->Freeze();
|
|
}
|
|
|
|
wxPropertyCategory* use_category = (wxPropertyCategory*)defaultCategory;
|
|
|
|
if ( !use_category )
|
|
use_category = (wxPropertyCategory*)m_properties;
|
|
|
|
// Let's iterate over the list of variants.
|
|
wxVariantList::const_iterator node;
|
|
int numSpecialEntries = 0;
|
|
|
|
//
|
|
// Second pass for special entries
|
|
for ( node = list.begin(); node != list.end(); ++node )
|
|
{
|
|
wxVariant *current = (wxVariant*)*node;
|
|
|
|
// Make sure it is wxVariant.
|
|
wxASSERT( current );
|
|
wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 );
|
|
|
|
const wxString& name = current->GetName();
|
|
if ( !name.empty() )
|
|
{
|
|
//
|
|
// '@' signified a special entry
|
|
if ( name[0] == wxS('@') )
|
|
{
|
|
numSpecialEntries++;
|
|
}
|
|
else
|
|
{
|
|
wxPGProperty* foundProp = BaseGetPropertyByName(name);
|
|
if ( foundProp )
|
|
{
|
|
wxPGProperty* p = foundProp;
|
|
|
|
// If it was a list, we still have to go through it.
|
|
if ( current->IsType(wxPG_VARIANT_TYPE_LIST) )
|
|
{
|
|
DoSetPropertyValues( current->GetList(),
|
|
p->IsCategory()?p:(NULL)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
wxASSERT_LEVEL_2_MSG(
|
|
wxStrcmp(current->GetType(), p->GetValue().GetType()) == 0,
|
|
wxString::Format(
|
|
wxS("setting value of property \"%s\" from variant"),
|
|
p->GetName().c_str())
|
|
);
|
|
|
|
p->SetValue(*current);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Is it list?
|
|
if ( !current->IsType(wxPG_VARIANT_TYPE_LIST) )
|
|
{
|
|
// Not.
|
|
}
|
|
else
|
|
{
|
|
// Yes, it is; create a sub category and append contents there.
|
|
wxPGProperty* newCat = DoInsert(use_category,-1,new wxPropertyCategory(current->GetName(),wxPG_LABEL));
|
|
DoSetPropertyValues( current->GetList(), newCat );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( numSpecialEntries )
|
|
{
|
|
for ( node = list.begin(); node != list.end(); ++node )
|
|
{
|
|
wxVariant *current = (wxVariant*)*node;
|
|
|
|
const wxString& name = current->GetName();
|
|
if ( !name.empty() )
|
|
{
|
|
//
|
|
// '@' signified a special entry
|
|
if ( name[0] == wxS('@') )
|
|
{
|
|
numSpecialEntries--;
|
|
|
|
size_t pos2 = name.rfind(wxS('@'));
|
|
if ( pos2 > 0 && pos2 < (name.size()-1) )
|
|
{
|
|
wxString propName = name.substr(1, pos2-1);
|
|
wxString entryType = name.substr(pos2+1, wxString::npos);
|
|
|
|
if ( entryType == wxS("attr") )
|
|
{
|
|
//
|
|
// List of attributes
|
|
wxPGProperty* foundProp = BaseGetPropertyByName(propName);
|
|
if ( foundProp )
|
|
{
|
|
wxASSERT( current->IsType(wxPG_VARIANT_TYPE_LIST) );
|
|
|
|
wxVariantList& list2 = current->GetList();
|
|
wxVariantList::const_iterator node2;
|
|
|
|
for ( node2 = list2.begin(); node2 != list2.end(); ++node2 )
|
|
{
|
|
wxVariant *attr = (wxVariant*)*node2;
|
|
foundProp->SetAttribute( attr->GetName(), *attr );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ERROR: No such property: 'propName'
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ERROR: Special entry requires name of format @<propname>@<entrytype>
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !numSpecialEntries )
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !origFrozen )
|
|
{
|
|
m_pPropGrid->Thaw();
|
|
|
|
if ( this == m_pPropGrid->GetState() )
|
|
m_pPropGrid->RefreshEditor();
|
|
}
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPageState property adding and removal
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPageState::PrepareToAddItem( wxPGProperty* property,
|
|
wxPGProperty* scheduledParent )
|
|
{
|
|
wxPropertyGrid* propGrid = m_pPropGrid;
|
|
|
|
// This will allow better behaviour.
|
|
if ( scheduledParent == m_properties )
|
|
scheduledParent = NULL;
|
|
|
|
if ( scheduledParent && !scheduledParent->IsCategory() )
|
|
{
|
|
wxASSERT_MSG( property->GetBaseName().length(),
|
|
"Property's children must have unique, non-empty names within their scope" );
|
|
}
|
|
|
|
property->m_parentState = this;
|
|
|
|
if ( property->IsCategory() )
|
|
{
|
|
|
|
// Parent of a category must be either root or another category
|
|
// (otherwise Bad Things might happen).
|
|
wxASSERT_MSG( scheduledParent == NULL ||
|
|
scheduledParent == m_properties ||
|
|
scheduledParent->IsCategory(),
|
|
wxT("Parent of a category must be either root or another category."));
|
|
|
|
// If we already have category with same name, delete given property
|
|
// and use it instead as most recent caption item.
|
|
wxPGProperty* found_id = BaseGetPropertyByName( property->GetBaseName() );
|
|
if ( found_id )
|
|
{
|
|
wxPropertyCategory* pwc = (wxPropertyCategory*) found_id;
|
|
if ( pwc->IsCategory() ) // Must be a category.
|
|
{
|
|
delete property;
|
|
m_currentCategory = pwc;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if wxDEBUG_LEVEL
|
|
// Warn for identical names in debug mode.
|
|
if ( BaseGetPropertyByName(property->GetName()) &&
|
|
(!scheduledParent || scheduledParent->IsCategory()) )
|
|
{
|
|
wxFAIL_MSG(wxString::Format(
|
|
"wxPropertyGrid item with name \"%s\" already exists",
|
|
property->GetName()));
|
|
|
|
wxPGGlobalVars->m_warnings++;
|
|
}
|
|
#endif // wxDEBUG_LEVEL
|
|
|
|
// NULL parent == root parent
|
|
if ( !scheduledParent )
|
|
scheduledParent = DoGetRoot();
|
|
|
|
property->m_parent = scheduledParent;
|
|
|
|
property->InitAfterAdded(this, propGrid);
|
|
|
|
if ( property->IsCategory() )
|
|
{
|
|
wxPropertyCategory* pc = wxStaticCast(property, wxPropertyCategory);
|
|
|
|
m_currentCategory = pc;
|
|
|
|
// Calculate text extent for category caption
|
|
if ( propGrid )
|
|
pc->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyGridPageState::DoAppend( wxPGProperty* property )
|
|
{
|
|
wxPropertyCategory* cur_cat = m_currentCategory;
|
|
if ( property->IsCategory() )
|
|
cur_cat = NULL;
|
|
|
|
return DoInsert( cur_cat, -1, property );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyGridPageState::DoInsert( wxPGProperty* parent, int index, wxPGProperty* property )
|
|
{
|
|
if ( !parent )
|
|
parent = m_properties;
|
|
|
|
wxCHECK_MSG( !parent->HasFlag(wxPG_PROP_AGGREGATE),
|
|
wxNullProperty,
|
|
wxT("when adding properties to fixed parents, use BeginAddChildren and EndAddChildren.") );
|
|
|
|
bool res = PrepareToAddItem( property, (wxPropertyCategory*)parent );
|
|
|
|
// PrepareToAddItem() may just decide to use current category
|
|
// instead of adding new one.
|
|
if ( !res )
|
|
return m_currentCategory;
|
|
|
|
bool parentIsRoot = parent->IsRoot();
|
|
bool parentIsCategory = parent->IsCategory();
|
|
|
|
// Note that item must be added into current mode later.
|
|
|
|
// If parent is wxParentProperty, just stick it in...
|
|
// If parent is root (m_properties), then...
|
|
// In categoric mode: Add as last item in m_abcArray (if not category).
|
|
// Add to given index in m_regularArray.
|
|
// In non-cat mode: Add as last item in m_regularArray.
|
|
// Add to given index in m_abcArray.
|
|
// If parent is category, then...
|
|
// 1) Add to given category in given index.
|
|
// 2) Add as last item in m_abcArray.
|
|
|
|
if ( m_properties == &m_regularArray )
|
|
{
|
|
// We are currently in Categorized mode
|
|
|
|
// Only add non-categories to m_abcArray.
|
|
if ( m_abcArray && !property->IsCategory() &&
|
|
(parentIsCategory || parentIsRoot) )
|
|
{
|
|
m_abcArray->DoAddChild( property, -1, false );
|
|
}
|
|
|
|
// Add to current mode.
|
|
parent->DoAddChild( property, index, true );
|
|
}
|
|
else
|
|
{
|
|
// We are currently in Non-categorized/Alphabetic mode
|
|
|
|
if ( parentIsCategory )
|
|
// Parent is category.
|
|
parent->DoAddChild( property, index, false );
|
|
else if ( parentIsRoot )
|
|
// Parent is root.
|
|
m_regularArray.DoAddChild( property, -1, false );
|
|
|
|
// Add to current mode
|
|
if ( !property->IsCategory() )
|
|
m_abcArray->DoAddChild( property, index, true );
|
|
}
|
|
|
|
// category stuff
|
|
if ( property->IsCategory() )
|
|
{
|
|
// This is a category caption item.
|
|
|
|
// Last caption is not the bottom one (this info required by append)
|
|
m_lastCaptionBottomnest = false;
|
|
}
|
|
|
|
// Only add name to hashmap if parent is root or category
|
|
if ( !property->GetBaseName().empty() &&
|
|
(parentIsCategory || parentIsRoot) )
|
|
m_dictName[property->GetBaseName()] = (void*) property;
|
|
|
|
VirtualHeightChanged();
|
|
|
|
property->UpdateParentValues();
|
|
|
|
m_itemsAdded = true;
|
|
|
|
return property;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPageState::DoRemoveChildrenFromSelection(wxPGProperty* p,
|
|
bool recursive,
|
|
int selFlags)
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
for( unsigned int i = 0; i < p->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* child = p->Item(i);
|
|
if ( DoIsPropertySelected(child) )
|
|
{
|
|
if ( pg && pg->GetState() == this )
|
|
{
|
|
pg->DoRemoveFromSelection(child, selFlags);
|
|
}
|
|
else
|
|
{
|
|
DoRemoveFromSelection(child);
|
|
}
|
|
}
|
|
|
|
if ( recursive )
|
|
{
|
|
DoRemoveChildrenFromSelection(child, recursive, selFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxPropertyGridPageState::DoMarkChildrenAsDeleted(wxPGProperty* p,
|
|
bool recursive)
|
|
{
|
|
for( unsigned int i = 0; i < p->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* child = p->Item(i);
|
|
|
|
child->SetFlag(wxPG_PROP_BEING_DELETED);
|
|
|
|
if ( recursive )
|
|
{
|
|
DoMarkChildrenAsDeleted(child, recursive);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wxPropertyGridPageState::DoInvalidatePropertyName(wxPGProperty* p)
|
|
{
|
|
// Let's trust that no sane property uses prefix like
|
|
// this. It would be anyway fairly inconvenient (in
|
|
// current code) to check whether a new name is used
|
|
// by another property with parent (due to the child
|
|
// name notation).
|
|
wxString newName = wxT("_&/_%$") + p->GetBaseName();
|
|
DoSetPropertyName(p, newName);
|
|
}
|
|
|
|
void wxPropertyGridPageState::DoInvalidateChildrenNames(wxPGProperty* p,
|
|
bool recursive)
|
|
{
|
|
if (p->IsCategory())
|
|
{
|
|
for( unsigned int i = 0; i < p->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* child = p->Item(i);
|
|
DoInvalidatePropertyName(child);
|
|
|
|
if ( recursive )
|
|
{
|
|
DoInvalidateChildrenNames(child, recursive);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool wxPropertyGridPageState::IsChildCategory(wxPGProperty* p,
|
|
wxPropertyCategory* cat,
|
|
bool recursive)
|
|
{
|
|
if (p->IsCategory())
|
|
{
|
|
for( unsigned int i = 0; i < p->GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* child = p->Item(i);
|
|
|
|
if (child->IsCategory() && child == cat)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( recursive && IsChildCategory(child, cat, recursive) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void wxPropertyGridPageState::DoDelete( wxPGProperty* item, bool doDelete )
|
|
{
|
|
wxCHECK_RET( item->GetParent(),
|
|
wxT("wxPropertyGrid: This property was already deleted.") );
|
|
|
|
wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
|
|
wxT("wxPropertyGrid: Do not attempt to remove the root item.") );
|
|
|
|
wxPGProperty* parent = item->GetParent();
|
|
|
|
wxCHECK_RET( !parent->HasFlag(wxPG_PROP_AGGREGATE),
|
|
wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") );
|
|
|
|
wxASSERT( item->GetParentState() == this );
|
|
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
// Try to unselect property and its subproperties.
|
|
if ( DoIsPropertySelected(item) )
|
|
{
|
|
if ( pg && pg->GetState() == this )
|
|
{
|
|
pg->DoRemoveFromSelection(item,
|
|
wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE);
|
|
}
|
|
else
|
|
{
|
|
DoRemoveFromSelection(item);
|
|
}
|
|
}
|
|
|
|
if ( item->IsChildSelected(true) )
|
|
{
|
|
DoRemoveChildrenFromSelection(item, true,
|
|
wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE);
|
|
}
|
|
|
|
// If deleted category or its sub-category is
|
|
// a current category then reset current category marker.
|
|
if ( item->IsCategory() )
|
|
{
|
|
if (item == m_currentCategory || IsChildCategory(item, m_currentCategory, true))
|
|
{
|
|
m_currentCategory = NULL;
|
|
}
|
|
}
|
|
|
|
// Must defer deletion? Yes, if handling a wxPG event.
|
|
if ( pg && pg->m_processedEvent )
|
|
{
|
|
// Prevent adding duplicates to the lists.
|
|
if ( doDelete )
|
|
{
|
|
if ( pg->m_deletedProperties.Index(item) != wxNOT_FOUND )
|
|
return;
|
|
|
|
pg->m_deletedProperties.push_back(item);
|
|
}
|
|
else
|
|
{
|
|
if ( pg->m_removedProperties.Index(item) != wxNOT_FOUND )
|
|
return;
|
|
|
|
pg->m_removedProperties.push_back(item);
|
|
}
|
|
|
|
// Rename the property and its children so it won't remain in the way
|
|
// of the user code.
|
|
DoInvalidatePropertyName(item);
|
|
DoInvalidateChildrenNames(item, true);
|
|
|
|
return;
|
|
}
|
|
|
|
// Property has to be unselected prior deleting.
|
|
// Otherwise crash can happen.
|
|
wxASSERT_MSG( !DoIsPropertySelected(item) && !item->IsChildSelected(true),
|
|
wxT("Failed to unselect deleted property") );
|
|
// Don't attempt to delete current category.
|
|
wxASSERT_MSG( !item->IsCategory() || item != m_currentCategory,
|
|
wxT("Current category cannot be deleted") );
|
|
|
|
// Prevent property and its children from being re-selected
|
|
item->SetFlag(wxPG_PROP_BEING_DELETED);
|
|
DoMarkChildrenAsDeleted(item, true);
|
|
|
|
unsigned int indinparent = item->GetIndexInParent();
|
|
|
|
// Delete children
|
|
if ( item->GetChildCount() && !item->HasFlag(wxPG_PROP_AGGREGATE) )
|
|
{
|
|
// deleting a category
|
|
item->DeleteChildren();
|
|
}
|
|
|
|
if ( !IsInNonCatMode() )
|
|
{
|
|
// categorized mode - non-categorized array
|
|
|
|
// Remove from non-cat array
|
|
if ( !item->IsCategory() &&
|
|
(parent->IsCategory() || parent->IsRoot()) )
|
|
{
|
|
if ( m_abcArray )
|
|
m_abcArray->RemoveChild(item);
|
|
}
|
|
|
|
// categorized mode - categorized array
|
|
parent->RemoveChild(indinparent);
|
|
item->m_parent->FixIndicesOfChildren();
|
|
}
|
|
else
|
|
{
|
|
// non-categorized mode - categorized array
|
|
|
|
// We need to find location of item.
|
|
wxPGProperty* cat_parent = &m_regularArray;
|
|
int cat_index = m_regularArray.GetChildCount();
|
|
size_t i;
|
|
for ( i = 0; i < m_regularArray.GetChildCount(); i++ )
|
|
{
|
|
wxPGProperty* p = m_regularArray.Item(i);
|
|
if ( p == item ) { cat_index = i; break; }
|
|
if ( p->IsCategory() )
|
|
{
|
|
int subind = ((wxPGProperty*)p)->Index(item);
|
|
if ( subind != wxNOT_FOUND )
|
|
{
|
|
cat_parent = ((wxPGProperty*)p);
|
|
cat_index = subind;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
cat_parent->RemoveChild(cat_index);
|
|
|
|
// non-categorized mode - non-categorized array
|
|
if ( !item->IsCategory() )
|
|
{
|
|
wxASSERT( item->m_parent == m_abcArray );
|
|
item->m_parent->RemoveChild(indinparent);
|
|
item->m_parent->FixIndicesOfChildren(indinparent);
|
|
}
|
|
}
|
|
|
|
if ( !item->GetBaseName().empty() &&
|
|
(parent->IsCategory() || parent->IsRoot()) )
|
|
m_dictName.erase(item->GetBaseName());
|
|
|
|
// We need to clear parent grid's m_propHover, if it matches item
|
|
if ( pg && pg->m_propHover == item )
|
|
pg->m_propHover = NULL;
|
|
|
|
// Mark the property as 'unattached'
|
|
item->m_parentState = NULL;
|
|
item->m_parent = NULL;
|
|
|
|
// We can actually delete it now
|
|
if ( doDelete )
|
|
{
|
|
// Remove the item from both lists of pending operations.
|
|
// (Deleted item cannot be also the subject of further removal.)
|
|
int index = pg->m_deletedProperties.Index(item);
|
|
if ( index != wxNOT_FOUND )
|
|
{
|
|
pg->m_deletedProperties.RemoveAt(index);
|
|
}
|
|
wxASSERT_MSG( pg->m_deletedProperties.Index(item) == wxNOT_FOUND,
|
|
wxT("Too many occurrences of the item"));
|
|
|
|
index = pg->m_removedProperties.Index(item);
|
|
if ( index != wxNOT_FOUND )
|
|
{
|
|
pg->m_removedProperties.RemoveAt(index);
|
|
}
|
|
wxASSERT_MSG( pg->m_removedProperties.Index(item) == wxNOT_FOUND,
|
|
wxT("Too many occurrences of the item"));
|
|
|
|
delete item;
|
|
}
|
|
else
|
|
{
|
|
// Remove the item from the list of pending removals.
|
|
int index = pg->m_removedProperties.Index(item);
|
|
if ( index != wxNOT_FOUND )
|
|
{
|
|
pg->m_removedProperties.RemoveAt(index);
|
|
}
|
|
wxASSERT_MSG( pg->m_removedProperties.Index(item) == wxNOT_FOUND,
|
|
wxT("Too many occurrences of the item"));
|
|
|
|
item->OnDetached(this, pg);
|
|
}
|
|
|
|
m_itemsAdded = true; // Not a logical assignment (but required nonetheless).
|
|
|
|
VirtualHeightChanged();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#endif // wxUSE_PROPGRID
|