Files
wxWidgets/src/propgrid/propgridpagestate.cpp
Artur Wieczorek d6e23dd99b Do not mark selected wxPG property as 'being deleted'.
Only unselected wxPG property can be marked as 'being deleted'.
If this is done for selected wxGP property then in some circumstances it cannot be unselected and hence cannot be safely deleted.

See #16617.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@78091 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2014-11-04 19:23:01 +00:00

2105 lines
60 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 )
{
long timeSinceCreation = (::wxGetLocalTimeMillis() - GetGrid()->m_timeCreated).ToLong();
// 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
// -----------------------------------------------------------------------
#ifdef 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->m_label == 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->m_children.Sort( wxPG_SortFunc_ByFunction );
else
p->m_children.Sort( wxPG_SortFunc_ByLabel );
#if 0
//
// For wxVector w/ wxUSE_STL=1, you would use code like this instead:
//
if ( GetGrid()->GetSortFunction() )
std::sort(p->m_children.begin(), p->m_children.end(),
wxPG_SortFunc_ByFunction);
else
std::sort(p->m_children.begin(), p->m_children.end(),
wxPG_SortFunc_ByLabel);
#endif
// 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 += ( ((int)p->m_depth-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 += (int)p->m_depth * 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 )
{
int origWidth = m_colWidths[column];
m_colWidths[column] -= decrease;
int min = GetColumnMinWidth(column);
int more = 0;
if ( m_colWidths[column] < min )
{
more = decrease - (origWidth - min);
m_colWidths[column] = min;
}
//
// FIXME: Causes erratic splitter changing, so as a workaround
// disabled if two or less columns.
if ( m_colWidths.size() <= 2 )
return;
column += dir;
if ( more && column < (int)m_colWidths.size() && column >= 0 )
PropagateColSizeDec( column, more, dir );
}
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
{
otherColumn = splitterColumn + 1;
if ( otherColumn == (int)m_colWidths.size() )
otherColumn = 0;
m_colWidths[otherColumn] -= adjust;
PropagateColSizeDec( splitterColumn, -adjust, -1 );
}
}
else
{
m_colWidths[splitterColumn] += adjust;
}
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->m_name,p,flags|wxPG_KEEP_STRUCTURE) );
}
if ( (flags & wxPG_INC_ATTRIBUTES) && p->m_attributes.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->m_attributes.GetCount() )
v.Append( p->GetAttributesAsList() );
}
}
}
}
return v;
}
// -----------------------------------------------------------------------
void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wxPGProperty* defaultCategory )
{
unsigned char origFrozen = 1;
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 ( wxStrcmp(current->GetType(), wxS("list")) == 0 )
{
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->GetType() != wxS("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->GetType() == 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->m_name.empty() &&
(parentIsCategory || parentIsRoot) )
m_dictName[property->m_name] = (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::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);
}
// 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 so it won't remain in the way
// of the user code.
// 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 = wxS("_&/_%$") + item->GetBaseName();
DoSetPropertyName(item, newName);
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") );
// Prevent property and its children from being re-selected
item->SetFlag(wxPG_PROP_BEING_DELETED);
DoMarkChildrenAsDeleted(item, true);
unsigned int indinparent = item->GetIndexInParent();
wxPGProperty* pwc = (wxPGProperty*)item;
// Delete children
if ( item->GetChildCount() && !item->HasFlag(wxPG_PROP_AGGREGATE) )
{
// deleting a category
if ( item->IsCategory() )
{
if ( pwc == m_currentCategory )
m_currentCategory = NULL;
}
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
wxArrayPGProperty& parentsChildren = parent->m_children;
parentsChildren.erase( parentsChildren.begin() + 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->m_children.erase(cat_parent->m_children.begin()+cat_index);
// non-categorized mode - non-categorized array
if ( !item->IsCategory() )
{
wxASSERT( item->m_parent == m_abcArray );
wxArrayPGProperty& parentsChildren = item->m_parent->m_children;
parentsChildren.erase(parentsChildren.begin() + 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 occurences 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 occurences 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 occurences of the item"));
item->OnDetached(this, pg);
}
m_itemsAdded = true; // Not a logical assignment (but required nonetheless).
VirtualHeightChanged();
}
// -----------------------------------------------------------------------
#endif // wxUSE_PROPGRID