// Author: Jaakko Salli
// Modified by:
// Created: 2008-08-24
-// RCS-ID: $Id:
+// RCS-ID: $Id$
// Copyright: (c) Jaakko Salli
-// Licence: wxWindows license
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#pragma hdrstop
#endif
+#if wxUSE_PROPGRID
+
#ifndef WX_PRECOMP
#include "wx/defs.h"
#include "wx/object.h"
#include "wx/panel.h"
#include "wx/dc.h"
#include "wx/dcmemory.h"
- #include "wx/button.h"
#include "wx/pen.h"
#include "wx/brush.h"
- #include "wx/cursor.h"
- #include "wx/dialog.h"
- #include "wx/settings.h"
- #include "wx/msgdlg.h"
- #include "wx/choice.h"
- #include "wx/stattext.h"
- #include "wx/scrolwin.h"
- #include "wx/dirdlg.h"
- #include "wx/layout.h"
- #include "wx/sizer.h"
- #include "wx/textdlg.h"
- #include "wx/filedlg.h"
- #include "wx/statusbr.h"
#include "wx/intl.h"
- #include "wx/frame.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>
-
-#include <typeinfo>
+#include "wx/propgrid/propgridpagestate.h"
+#include "wx/propgrid/propgrid.h"
+#include "wx/propgrid/editors.h"
#define wxPG_DEFAULT_SPLITTERX 110
void wxPropertyGridIteratorBase::Init( wxPropertyGridPageState* state, int flags, int startPos, int dir )
{
- wxPGProperty* property;
+ wxPGProperty* property = NULL;
if ( startPos == wxTOP )
{
- property = NULL;
if ( dir == 0 )
dir = 1;
}
}
else
{
- wxASSERT_MSG( false, wxT("Only supported stating positions are wxTOP and wxBOTTOM") );
- property = NULL;
+ wxFAIL_MSG("Only supported starting positions are wxTOP and wxBOTTOM");
}
Init( state, flags, property, dir );
void wxPropertyGridIteratorBase::Prev()
{
wxPGProperty* property = m_property;
- wxASSERT( property );
+ if ( !property )
+ return;
wxPGProperty* parent = property->GetParent();
wxASSERT( parent );
void wxPropertyGridIteratorBase::Next( bool iterateChildren )
{
wxPGProperty* property = m_property;
- wxASSERT( property );
+ if ( !property )
+ return;
if ( property->GetChildCount() &&
wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) &&
wxPropertyGridPageState::wxPropertyGridPageState()
{
- m_pPropGrid = (wxPropertyGrid*) NULL;
+ m_pPropGrid = NULL;
m_regularArray.SetParentState(this);
m_properties = &m_regularArray;
- m_abcArray = (wxPGRootProperty*) NULL;
- m_currentCategory = (wxPropertyCategory*) NULL;
- m_selected = (wxPGProperty*) NULL;
+ m_abcArray = NULL;
+ m_currentCategory = NULL;
m_width = 0;
m_virtualHeight = 0;
m_lastCaptionBottomnest = 1;
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);
}
// -----------------------------------------------------------------------
{
if ( !m_abcArray )
{
- m_abcArray = new wxPGRootProperty();
+ m_abcArray = new wxPGRootProperty(wxS("<Root_NonCat>"));
m_abcArray->SetParentState(this);
m_abcArray->SetFlag(wxPG_PROP_CHILDREN_ARE_COPIES);
}
if ( m_properties->GetChildCount() )
{
- // Copy items.
- wxPropertyGridIterator it( this, wxPG_ITERATE_DEFAULT|wxPG_ITERATE_CATEGORIES );
+ //
+ // Prepare m_abcArray
+ wxPropertyGridIterator it( this, wxPG_ITERATE_PROPERTIES );
for ( ; !it.AtEnd(); it.Next() )
{
wxPGProperty* p = it.GetProperty();
wxPGProperty* parent = p->GetParent();
- if ( p->HasFlag(wxPG_PROP_MISC_PARENT) &&
- ( parent == m_properties || (parent->IsCategory() || parent->IsRoot()) ) )
+ if ( parent->IsCategory() || parent->IsRoot() )
{
- m_abcArray->AddChild2( p );
+ m_abcArray->DoAddChild(p);
p->m_parent = &m_regularArray;
}
}
void wxPropertyGridPageState::DoClear()
{
+ if ( m_pPropGrid && m_pPropGrid->GetState() == this )
+ {
+ m_pPropGrid->ClearSelection(false);
+ }
+ else
+ {
+ m_selection.clear();
+ }
+
m_regularArray.Empty();
if ( m_abcArray )
m_abcArray->Empty();
m_dictName.clear();
- m_currentCategory = (wxPropertyCategory*) NULL;
+ m_currentCategory = NULL;
m_lastCaptionBottomnest = 1;
m_itemsAdded = 0;
m_virtualHeight = 0;
m_vhCalcPending = 0;
-
- m_selected = (wxPGProperty*) NULL;
}
// -----------------------------------------------------------------------
void wxPropertyGridPageState::SetVirtualWidth( int width )
{
- wxASSERT( width >= 0 );
+ // 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 )
widthChange = 0;
CheckColumnWidths(widthChange);
- if ( !(GetGrid()->GetInternalFlags() & wxPG_FL_SPLITTER_PRE_SET) &&
- (GetGrid()->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER) )
+ if ( !m_isSplitterPreSet && m_dontCenterSplitter )
{
long timeSinceCreation = (::wxGetLocalTimeMillis() - GetGrid()->m_timeCreated).ToLong();
// If too long, don't set splitter
- if ( timeSinceCreation < 3000 )
+ if ( timeSinceCreation < 250 )
{
- if ( m_properties->GetChildCount() || timeSinceCreation > 750 )
+ if ( m_properties->GetChildCount() )
{
SetSplitterLeft( false );
}
else
{
DoSetSplitterPosition( newWidth / 2 );
- GetGrid()->ClearInternalFlag(wxPG_FL_SPLITTER_PRE_SET);
+ m_isSplitterPreSet = false;
}
}
}
wxPGProperty* wxPropertyGridPageState::GetLastItem( int flags )
{
if ( !m_properties->GetChildCount() )
- return (wxPGProperty*) NULL;
+ return NULL;
wxPG_ITERATOR_CREATE_MASKS(flags, int itemExMask, int parentExMask)
return (wxPropertyCategory*)parent;
} while ( grandparent );
- return (wxPropertyCategory*) NULL;
+ return NULL;
}
// -----------------------------------------------------------------------
it = m_dictName.find(name);
if ( it != m_dictName.end() )
return (wxPGProperty*) it->second;
- return (wxPGProperty*) NULL;
+ 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);
}
// -----------------------------------------------------------------------
// NB: Nowadays only needed for alphabetic/categoric mode switching.
// -----------------------------------------------------------------------
-#define II_INVALID_I 0x00FFFFFF
+//#define II_INVALID_I 0x00FFFFFF
#define ITEM_ITERATION_VARIABLES \
wxPGProperty* parent; \
parent = m_properties; \
i = 0;
+#if 0
#define ITEM_ITERATION_INIT(startparent, startindex, state) \
parent = startparent; \
i = (unsigned int)startindex; \
- if ( parent == (wxPGProperty*) NULL ) \
+ if ( parent == NULL ) \
{ \
parent = state->m_properties; \
i = 0; \
}
+#endif
#define ITEM_ITERATION_LOOP_BEGIN \
do \
// -----------------------------------------------------------------------
-static int wxPG_SortFunc(void **p1, void **p2)
+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)
{
- wxPGProperty *pp1 = *((wxPGProperty**)p1);
- wxPGProperty *pp2 = *((wxPGProperty**)p2);
- return pp1->GetLabel().compare( pp2->GetLabel() );
+ wxPropertyGrid* pg = p1->GetGrid();
+ wxPGSortCallback sortFunction = pg->GetSortFunction();
+ return sortFunction(pg, p1, p2) < 0;
}
-void wxPropertyGridPageState::SortChildren( wxPGProperty* p )
+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 = (wxPGProperty*)m_properties;
+ p = m_properties;
+ // Can only sort items with children
if ( !p->GetChildCount() )
return;
- wxPGProperty* pwc = (wxPGProperty*)p;
+ // Never sort children of aggregate properties
+ if ( p->HasFlag(wxPG_PROP_AGGREGATE) )
+ return;
- // Can only sort items with children
- if ( pwc->GetChildCount() < 1 )
+ if ( (flags & wxPG_SORT_TOP_LEVEL_ONLY)
+ && !p->IsCategory() && !p->IsRoot() )
return;
- pwc->m_children.Sort( wxPG_SortFunc );
+ if ( GetGrid()->GetSortFunction() )
+ p->m_children.Sort( wxPG_SortFunc_ByFunction );
+ else
+ p->m_children.Sort( wxPG_SortFunc_ByLabel );
- // Fix indexes
- pwc->FixIndexesOfChildren();
+#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::Sort()
+void wxPropertyGridPageState::DoSort( int flags )
{
- SortChildren( m_properties );
+ DoSortChildren( m_properties, flags | wxPG_RECURSE );
- // Sort categories as well
- if ( !IsInNonCatMode() )
- {
- size_t i;
- for ( i=0;i<m_properties->GetChildCount();i++)
- {
- wxPGProperty* p = m_properties->Item(i);
- if ( p->IsCategory() )
- SortChildren( p );
- }
- }
+ // 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 = 0;
+
+ if ( pg->HasFlag(wxPG_AUTO_SORT) )
+ DoSort(wxPG_SORT_TOP_LEVEL_ONLY);
+
+ return true;
}
// -----------------------------------------------------------------------
{
// Outside?
if ( y < 0 )
- return (wxPGProperty*) NULL;
+ return NULL;
unsigned int a = 0;
return m_properties->GetItemAtY(y, GetGrid()->m_lineHeight, &a);
// -----------------------------------------------------------------------
-wxPropertyGridHitTestResult wxPropertyGridPageState::HitTest( const wxPoint&pt ) const
+wxPropertyGridHitTestResult
+wxPropertyGridPageState::HitTest( const wxPoint&pt ) const
{
wxPropertyGridHitTestResult result;
- result.column = HitTestH( pt.x, &result.splitter, &result.splitterHitOffset );
- result.property = DoGetItemAtY( pt.y );
+ result.m_column = HitTestH( pt.x, &result.m_splitter,
+ &result.m_splitterHitOffset );
+ result.m_property = DoGetItemAtY( pt.y );
return result;
}
wxPGProperty* p = pwc->Item(i);
if ( !p->IsCategory() )
{
- dc.GetTextExtent( p->GetColumnText(col), &w, &h );
+ const wxPGCell* cell = NULL;
+ wxString text;
+ p->GetDisplayInfo(col, -1, 0, &text, &cell);
+ dc.GetTextExtent(text, &w, &h);
if ( col == 0 )
w += ( ((int)p->m_depth-1) * pg->m_subgroup_extramargin );
- //
- // TODO: Add bitmap support.
+ // account for the bitmap
+ if ( col == 1 )
+ w += p->GetImageOffset(pg->GetImageRect(p, -1).GetWidth());
+
w += (wxPG_XBEFORETEXT*2);
return wxPG_DRAG_MARGIN;
}
-void wxPropertyGridPageState::PropagateColSizeDec( int column, int decrease, int dir )
+void wxPropertyGridPageState::PropagateColSizeDec( int column,
+ int decrease,
+ int dir )
{
int origWidth = m_colWidths[column];
m_colWidths[column] -= decrease;
PropagateColSizeDec( column, more, dir );
}
-void wxPropertyGridPageState::DoSetSplitterPosition( int newXPos, int splitterColumn, bool WXUNUSED(allPages), bool fromAutoCenter )
+void wxPropertyGridPageState::DoSetSplitterPosition( int newXPos,
+ int splitterColumn,
+ int flags )
{
wxPropertyGrid* pg = GetGrid();
if ( splitterColumn == 0 )
m_fSplitterX = (double) newXPos;
- if ( !fromAutoCenter )
+ if ( !(flags & wxPG_SPLITTER_FROM_AUTO_CENTER) &&
+ !(flags & wxPG_SPLITTER_FROM_EVENT) )
{
// Don't allow initial splitter auto-positioning after this.
- if ( pg->GetState() == this )
- pg->SetInternalFlag(wxPG_FL_SPLITTER_PRE_SET);
+ m_isSplitterPreSet = true;
CheckColumnWidths();
}
{
wxPropertyGrid* pg = GetGrid();
wxClientDC dc(pg);
- dc.SetFont(pg->m_font);
+ dc.SetFont(pg->GetFont());
int maxW = GetColumnFitWidth(dc, m_properties, 0, subProps);
DoSetSplitterPosition( maxW );
}
- pg->SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
+ m_dontCenterSplitter = true;
}
wxSize wxPropertyGridPageState::DoFitColumns( bool WXUNUSED(allowGridResize) )
{
wxPropertyGrid* pg = GetGrid();
wxClientDC dc(pg);
- dc.SetFont(pg->m_font);
+ dc.SetFont(pg->GetFont());
int marginWidth = pg->m_marginWidth;
int accWid = marginWidth;
int remaining = m_width - accWid;
m_colWidths[GetColumnCount()-1] += remaining;
- pg->SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
+ m_dontCenterSplitter = true;
int firstSplitterX = marginWidth + m_colWidths[0];
m_fSplitterX = (double) firstSplitterX;
wxPropertyGrid* pg = GetGrid();
-#ifdef __WXDEBUG__
- const bool debug = false;
-#endif
-
unsigned int i;
unsigned int lastColumn = m_colWidths.size() - 1;
int width = m_width;
//
// Column to reduce, if needed. Take last one that exceeds minimum width.
- // Except if auto splitter centering is used, in which case use widest.
int reduceCol = -1;
- int highestColWidth = 0;
-
- bool minimizedCols = false;
-#ifdef __WXDEBUG__
- if ( debug )
- wxLogDebug(wxT("ColumnWidthCheck (virtualWidth: %i, clientWidth: %i)"), width, clientWidth);
-#endif
+ wxLogTrace("propgrid",
+ wxS("ColumnWidthCheck (virtualWidth: %i, clientWidth: %i)"),
+ width, clientWidth);
//
// Check min sizes
if ( m_colWidths[i] <= min )
{
m_colWidths[i] = min;
- minimizedCols = true;
}
else
{
- if ( pg->HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
- {
- if ( m_colWidths[i] >= highestColWidth )
- {
- highestColWidth = m_colWidths[i];
- reduceCol = i;
- }
- }
- else
- {
- reduceCol = i;
- }
+ // Always reduce the last column that is larger than minimum size
+ // (looks nicer, even with auto-centering enabled).
+ reduceCol = i;
}
}
for ( i=0; i<m_colWidths.size(); i++ )
colsWidth += m_colWidths[i];
-#ifdef __WXDEBUG__
- if ( debug )
- wxLogDebug(wxT(" HasVirtualWidth: %i colsWidth: %i"),(int)pg->HasVirtualWidth(),colsWidth);
-#endif
+ wxLogTrace("propgrid",
+ wxS(" HasVirtualWidth: %i colsWidth: %i"),
+ (int)pg->HasVirtualWidth(), colsWidth);
// Then mode-based requirement
if ( !pg->HasVirtualWidth() )
if ( colsWidth < width )
{
// Increase column
- #ifdef __WXDEBUG__
- if ( debug )
- wxLogDebug(wxT(" Adjust last column to %i"), m_colWidths[lastColumn] + widthHigher);
- #endif
+ 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 )
{
- #ifdef __WXDEBUG__
- if ( debug )
- wxLogDebug(wxT(" Reduce column %i (by %i)"), reduceCol, -widthHigher);
- #endif
+ wxLogTrace("propgrid",
+ wxT(" Reduce column %i (by %i)"),
+ reduceCol, -widthHigher);
+
// Reduce widest column, and recheck
m_colWidths[reduceCol] = m_colWidths[reduceCol] + widthHigher;
CheckColumnWidths();
pg->RecalculateVirtualSize();
}
-#ifdef __WXDEBUG__
- if ( debug )
- for ( i=0; i<m_colWidths.size(); i++ )
- wxLogDebug(wxT("col%i: %i"),i,m_colWidths[i]);
-#endif
+ for ( i=0; i<m_colWidths.size(); i++ )
+ {
+ wxLogTrace("propgrid", wxS("col%i: %i"), i, m_colWidths[i]);
+ }
// Auto center splitter
- if ( !(pg->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER) &&
- m_colWidths.size() == 2 )
+ if ( !m_dontCenterSplitter )
{
- float centerX = (float)(pg->m_width/2);
- float splitterX;
-
- if ( m_fSplitterX < 0.0 )
+ if ( m_colWidths.size() == 2 &&
+ m_columnProportions[0] == m_columnProportions[1] )
{
- splitterX = centerX;
- }
- else if ( widthChange )
- {
- //float centerX = float(pg->GetSize().x) * 0.5;
+ //
+ // 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.
+ //
+ float centerX = (float)(pg->m_width/2);
+ float splitterX;
- // Recenter?
- splitterX = m_fSplitterX + (float(widthChange) * 0.5);
- float deviation = fabs(centerX - splitterX);
+ if ( m_fSplitterX < 0.0 )
+ {
+ splitterX = centerX;
+ }
+ else if ( widthChange )
+ {
+ //float centerX = float(pg->GetSize().x) * 0.5;
+
+ // Recenter?
+ splitterX = m_fSplitterX + (float(widthChange) * 0.5);
+ float deviation = fabs(centerX - splitterX);
- // If deviating from center, adjust towards it
- if ( deviation > 20.0 )
+ // If deviating from center, adjust towards it
+ if ( deviation > 20.0 )
+ {
+ if ( splitterX > centerX)
+ splitterX -= 2;
+ else
+ splitterX += 2;
+ }
+ }
+ else
{
- if ( splitterX > centerX)
- splitterX -= 2;
- else
- splitterX += 2;
+ // No width change, just keep sure we keep splitter position intact
+ splitterX = m_fSplitterX;
+ float 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
{
- // No width change, just keep sure we keep splitter position intact
- splitterX = m_fSplitterX;
- float deviation = fabs(centerX - splitterX);
- if ( deviation > 50.0 )
- {
- splitterX = centerX;
- }
+ //
+ // Generic re-center code
+ //
+ ResetColumnSizes(wxPG_SPLITTER_FROM_AUTO_CENTER);
}
+ }
+}
- DoSetSplitterPosition((int)splitterX, 0, false, true);
+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;
- m_fSplitterX = splitterX; // needed to retain accuracy
+ // 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);
}
}
{
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(), m_colWidths.size() - colCount );
+ m_colWidths.RemoveAt( m_colWidths.size()-1,
+ m_colWidths.size() - colCount );
if ( m_pPropGrid->GetState() == this )
m_pPropGrid->RecalculateVirtualSize();
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
{
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
// -----------------------------------------------------------------------
{
if ( p )
{
- int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE;
+ int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE|wxPG_PROGRAMMATIC_VALUE;
wxVariant variant = p->GetValueRef();
bool res;
if ( res )
{
p->SetValue(variant);
- if ( m_selected==p && this==m_pPropGrid->GetState() )
- p->UpdateControl(m_pPropGrid->GetEditorControl());
+ if ( p == m_pPropGrid->GetSelection() &&
+ this == m_pPropGrid->GetState() )
+ m_pPropGrid->RefreshEditor();
}
return true;
if ( p )
{
p->SetValue(value);
- if ( m_selected==p && this==m_pPropGrid->GetState() )
- p->UpdateControl(m_pPropGrid->GetEditorControl());
+ if ( p == m_pPropGrid->GetSelection() &&
+ this == m_pPropGrid->GetState() )
+ m_pPropGrid->RefreshEditor();
return true;
}
return false;
}
+// -----------------------------------------------------------------------
+// wxPropertyGridPageState property operations
// -----------------------------------------------------------------------
-void wxPropertyGridPageState::DoSetPropertyValueUnspecified( wxPGProperty* p )
+bool wxPropertyGridPageState::DoIsPropertySelected( wxPGProperty* prop ) const
{
- wxCHECK_RET( p, wxT("invalid property id") );
+ if ( wxPGFindInVector(m_selection, prop) != wxNOT_FOUND )
+ return true;
- if ( !p->IsValueUnspecified() )
- {
- // Value should be set first - editor class methods may need it
- p->m_value.MakeNull();
+ return false;
+}
- wxASSERT( m_pPropGrid );
+// -----------------------------------------------------------------------
- if ( m_pPropGrid->GetState() == this )
+void wxPropertyGridPageState::DoRemoveFromSelection( wxPGProperty* prop )
+{
+ for ( unsigned int i=0; i<m_selection.size(); i++ )
+ {
+ if ( m_selection[i] == prop )
{
- if ( m_pPropGrid->m_selected == p && m_pPropGrid->m_wndEditor )
+ 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
{
- p->GetEditorClass()->SetValueToUnspecified(p, m_pPropGrid->GetEditorControl());
+ m_selection.erase( m_selection.begin() + i );
}
+ return;
}
-
- unsigned int i;
- for ( i = 0; i < p->GetChildCount(); i++ )
- DoSetPropertyValueUnspecified( p->Item(i) );
}
}
-// -----------------------------------------------------------------------
-// wxPropertyGridPageState property operations
// -----------------------------------------------------------------------
bool wxPropertyGridPageState::DoCollapse( wxPGProperty* p )
if ( this == m_pPropGrid->GetState() )
return m_pPropGrid->DoSelectProperty( p, flags );
- m_selected = p;
+ DoSetSelection(p);
return true;
}
bool wxPropertyGridPageState::DoHideProperty( wxPGProperty* p, bool hide, int flags )
{
- if ( !hide )
- p->ClearFlag( wxPG_PROP_HIDDEN );
- else
- p->SetFlag( wxPG_PROP_HIDDEN );
-
- if ( flags & wxPG_RECURSE )
- {
- unsigned int i;
- for ( i = 0; i < p->GetChildCount(); i++ )
- DoHideProperty(p->Item(i), hide, flags | wxPG_RECURSE_STARTS);
- }
-
+ p->DoHide(hide, flags);
VirtualHeightChanged();
return true;
}
-// -----------------------------------------------------------------------
-
-bool wxPropertyGridPageState::DoEnableProperty( wxPGProperty* p, bool enable )
-{
- if ( p )
- {
- if ( enable )
- {
- if ( !(p->m_flags & wxPG_PROP_DISABLED) )
- return false;
-
- // Enabling
-
- p->m_flags &= ~(wxPG_PROP_DISABLED);
- }
- else
- {
- if ( p->m_flags & wxPG_PROP_DISABLED )
- return false;
-
- // Disabling
-
- p->m_flags |= wxPG_PROP_DISABLED;
-
- }
-
- // Apply same to sub-properties as well
- unsigned int i;
- for ( i = 0; i < p->GetChildCount(); i++ )
- DoEnableProperty( p->Item(i), enable );
-
- return true;
- }
- return false;
-}
-
// -----------------------------------------------------------------------
// wxPropertyGridPageState wxVariant related routines
// -----------------------------------------------------------------------
//
// Second pass for special entries
- for ( node = list.begin(); node != list.end(); node++ )
+ for ( node = list.begin(); node != list.end(); ++node )
{
wxVariant *current = (wxVariant*)*node;
wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 );
const wxString& name = current->GetName();
- if ( name.length() > 0 )
+ if ( !name.empty() )
{
//
// '@' signified a special entry
if ( wxStrcmp(current->GetType(), wxS("list")) == 0 )
{
DoSetPropertyValues( current->GetList(),
- p->IsCategory()?p:((wxPGProperty*)NULL)
+ p->IsCategory()?p:(NULL)
);
}
else
{
- #ifdef __WXDEBUG__
- if ( wxStrcmp(current->GetType(), p->GetValue().GetType()) != 0)
- {
- wxLogDebug(wxT("wxPropertyGridPageState::DoSetPropertyValues Warning: Setting value of property \"%s\" from variant"),
- p->GetName().c_str());
- }
- #endif
+ 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);
}
if ( numSpecialEntries )
{
- for ( node = list.begin(); node != list.end(); node++ )
+ for ( node = list.begin(); node != list.end(); ++node )
{
wxVariant *current = (wxVariant*)*node;
const wxString& name = current->GetName();
- if ( name.length() > 0 )
+ if ( !name.empty() )
{
//
// '@' signified a special entry
wxPGProperty* foundProp = BaseGetPropertyByName(propName);
if ( foundProp )
{
- wxASSERT( wxPGIsVariantType(*current, list) );
+ wxASSERT( current->GetType() == wxPG_VARIANT_TYPE_LIST );
wxVariantList& list2 = current->GetList();
wxVariantList::const_iterator node2;
- for ( node2 = list2.begin(); node2 != list2.end(); node2++ )
+ for ( node2 = list2.begin(); node2 != list2.end(); ++node2 )
{
wxVariant *attr = (wxVariant*)*node2;
foundProp->SetAttribute( attr->GetName(), *attr );
m_pPropGrid->Thaw();
if ( this == m_pPropGrid->GetState() )
- {
- m_selected->UpdateControl(m_pPropGrid->GetEditorControl());
- }
+ m_pPropGrid->RefreshEditor();
}
}
// wxPropertyGridPageState property adding and removal
// -----------------------------------------------------------------------
-int wxPropertyGridPageState::PrepareToAddItem( wxPGProperty* property,
- wxPGProperty* scheduledParent )
+bool wxPropertyGridPageState::PrepareToAddItem( wxPGProperty* property,
+ wxPGProperty* scheduledParent )
{
wxPropertyGrid* propGrid = m_pPropGrid;
- // This will allow better behavior.
+ // This will allow better behaviour.
if ( scheduledParent == m_properties )
- scheduledParent = (wxPGProperty*) NULL;
+ 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;
{
delete property;
m_currentCategory = pwc;
- return 2; // Tells the caller what we did.
+ return false;
}
}
}
-#ifdef __WXDEBUG__
+#if wxDEBUG_LEVEL
// Warn for identical names in debug mode.
if ( BaseGetPropertyByName(property->GetName()) &&
(!scheduledParent || scheduledParent->IsCategory()) )
{
- wxLogError(wxT("wxPropertyGrid: Warning - item with name \"%s\" already exists."),
- property->GetName().c_str());
- wxPGGlobalVars->m_warnings++;
- }
-#endif
+ wxFAIL_MSG(wxString::Format(
+ "wxPropertyGrid item with name \"%s\" already exists",
+ property->GetName()));
- // Make sure nothing is selected.
- if ( propGrid && propGrid->m_selected )
- {
- bool selRes = propGrid->ClearSelection();
- wxPG_CHECK_MSG_DBG( selRes,
- -1,
- wxT("failed to deselect a property (editor probably had invalid value)") );
- }
-
- if ( scheduledParent )
- {
- // Use parent's colours.
- property->m_bgColIndex = scheduledParent->m_bgColIndex;
- property->m_fgColIndex = scheduledParent->m_fgColIndex;
-
- // Fix no parent does not yet have parenting flag yet, set one now
- if ( !scheduledParent->HasFlag(wxPG_PROP_PARENTAL_FLAGS) )
- scheduledParent->SetParentalType(wxPG_PROP_MISC_PARENT);
- //scheduledParent->SetFlag(wxPG_PROP_MISC_PARENT);
- }
-
- // If in hideable adding mode, or if assigned parent is hideable, then
- // make this one hideable.
- if (
- ( scheduledParent && (scheduledParent->m_flags & wxPG_PROP_HIDDEN) ) ||
- ( propGrid && (propGrid->m_iFlags & wxPG_FL_ADDING_HIDEABLES) )
- )
- property->SetFlag( wxPG_PROP_HIDDEN );
-
- // Set custom image flag.
- int custImgHeight = property->OnMeasureImage().y;
- if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
- {
- property->m_flags |= wxPG_PROP_CUSTOMIMAGE;
+ wxPGGlobalVars->m_warnings++;
}
+#endif // wxDEBUG_LEVEL
- if ( propGrid && (propGrid->GetWindowStyleFlag() & wxPG_LIMITED_EDITING) )
- property->m_flags |= wxPG_PROP_NOEDITOR;
-
- if ( !property->IsCategory() )
- {
- // This is not a category.
-
- //wxASSERT_MSG( property->GetEditorClass(), wxT("Editor class not initialized!") );
-
- // Depth.
- //
- unsigned char depth = 1;
- if ( scheduledParent )
- {
- depth = scheduledParent->m_depth;
- if ( !scheduledParent->IsCategory() )
- depth++;
- }
- property->m_depth = depth;
- unsigned char greyDepth = depth;
-
- if ( scheduledParent )
- {
- wxPropertyCategory* pc;
-
- if ( scheduledParent->IsCategory() || scheduledParent->IsRoot() )
- pc = (wxPropertyCategory*)scheduledParent;
- else
- // This conditional compile is necessary to
- // bypass some compiler bug.
- pc = GetPropertyCategory(scheduledParent);
-
- if ( pc )
- greyDepth = pc->GetDepth();
- else
- greyDepth = scheduledParent->m_depthBgCol;
- }
-
- property->m_depthBgCol = greyDepth;
-
- // Prepare children pre-added children
- if ( property->GetChildCount() )
- {
- property->SetParentalType(wxPG_PROP_AGGREGATE);
+ // NULL parent == root parent
+ if ( !scheduledParent )
+ scheduledParent = DoGetRoot();
- property->SetExpanded(false); // Properties with children are not expanded by default.
- if ( propGrid && propGrid->GetWindowStyleFlag() & wxPG_HIDE_MARGIN )
- property->SetExpanded(true); // ...unless it cannot be expanded.
+ property->m_parent = scheduledParent;
- property->PrepareSubProperties();
-
- return -1;
- }
+ property->InitAfterAdded(this, propGrid);
- if ( propGrid && (propGrid->GetExtraStyle() & wxPG_EX_AUTO_UNSPECIFIED_VALUES) )
- property->SetFlagRecursively(wxPG_PROP_AUTO_UNSPECIFIED, true);
-
- return 0;
- }
- else
+ if ( property->IsCategory() )
{
- // This is a category.
+ wxPropertyCategory* pc = wxStaticCast(property, wxPropertyCategory);
- // depth
- unsigned char depth = 1;
- if ( scheduledParent )
- {
- depth = scheduledParent->m_depth + 1;
- }
- property->m_depth = depth;
- property->m_depthBgCol = depth;
-
- m_currentCategory = (wxPropertyCategory*)property;
+ m_currentCategory = pc;
- wxPropertyCategory* pc = (wxPropertyCategory*)property;
-
- // Calculate text extent for caption item.
+ // Calculate text extent for category caption
if ( propGrid )
pc->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
-
- return 1;
}
+
+ return true;
}
// -----------------------------------------------------------------------
{
wxPropertyCategory* cur_cat = m_currentCategory;
if ( property->IsCategory() )
- cur_cat = (wxPropertyCategory*) NULL;
+ cur_cat = NULL;
return DoInsert( cur_cat, -1, property );
}
wxNullProperty,
wxT("when adding properties to fixed parents, use BeginAddChildren and EndAddChildren.") );
- int parenting = PrepareToAddItem( property, (wxPropertyCategory*)parent );
+ bool res = PrepareToAddItem( property, (wxPropertyCategory*)parent );
- // This type of invalid parenting value indicates we should exit now, returning
- // id of most recent category.
- if ( parenting > 1 )
+ // 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...
// 1) Add to given category in given index.
// 2) Add as last item in m_abcArray.
- if ( !parent->IsCategory() && !parent->IsRoot() )
- {
- // Parent is wxParentingProperty: Just stick it in...
- parent->AddChild2( property, index );
- }
- else
+ if ( m_properties == &m_regularArray )
{
- // Parent is Category or Root.
+ // We are currently in Categorized mode
- if ( m_properties == &m_regularArray )
+ // Only add non-categories to m_abcArray.
+ if ( m_abcArray && !property->IsCategory() &&
+ (parentIsCategory || parentIsRoot) )
{
- // Categorized mode
-
- // Only add non-categories to m_abcArray.
- if ( m_abcArray && parenting <= 0 )
- m_abcArray->AddChild2( property, -1, false );
-
- // Add to current mode.
- parent->AddChild2( property, index );
-
+ m_abcArray->DoAddChild( property, -1, false );
}
- else
- {
- // Non-categorized mode.
-
- if ( parent != m_properties )
- // Parent is category.
- parent->AddChild2( property, index, false );
- else
- // Parent is root.
- m_regularArray.AddChild2( property, -1, false );
- // Add to current mode (no categories).
- if ( parenting <= 0 )
- m_abcArray->AddChild2( property, index );
- }
+ // 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
}
// Only add name to hashmap if parent is root or category
- if ( (parent->IsCategory() || parent->IsRoot()) && property->m_name.length() )
+ if ( !property->m_name.empty() &&
+ (parentIsCategory || parentIsRoot) )
m_dictName[property->m_name] = (void*) property;
VirtualHeightChanged();
// -----------------------------------------------------------------------
-void wxPropertyGridPageState::DoDelete( wxPGProperty* item )
+void wxPropertyGridPageState::DoDelete( wxPGProperty* item, bool doDelete )
{
wxCHECK_RET( item->GetParent(),
wxT("this property was already deleted") );
wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
wxT("wxPropertyGrid: Do not attempt to remove the root item.") );
- size_t i;
+ wxPropertyGrid* pg = GetGrid();
+
+ // Must defer deletion? Yes, if handling a wxPG event.
+ if ( pg && pg->m_processedEvent )
+ {
+ if ( doDelete )
+ pg->m_deletedProperties.push_back(item);
+ else
+ 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;
+ }
+
unsigned int indinparent = item->GetIndexInParent();
wxPGProperty* pwc = (wxPGProperty*)item;
+ wxPGProperty* parent = item->GetParent();
- wxCHECK_RET( !item->GetParent()->HasFlag(wxPG_PROP_AGGREGATE),
+ wxCHECK_RET( !parent->HasFlag(wxPG_PROP_AGGREGATE),
wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") );
- if ( item->IsCategory() )
- {
- // deleting a category
+ wxASSERT( item->GetParentState() == this );
- // erase category entries from the hash table
- for ( i=0; i<pwc->GetChildCount(); i++ )
+ if ( DoIsPropertySelected(item) )
+ {
+ if ( pg && pg->GetState() == this )
{
- wxPGProperty* sp = pwc->Item( i );
- if ( sp->GetBaseName().Len() ) m_dictName.erase(sp->GetBaseName());
+ pg->DoRemoveFromSelection(item,
+ wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE);
}
+ else
+ {
+ DoRemoveFromSelection(item);
+ }
+ }
- if ( pwc == m_currentCategory )
- m_currentCategory = (wxPropertyCategory*) NULL;
+ item->SetFlag(wxPG_PROP_BEING_DELETED);
- if ( m_abcArray )
+ // Delete children
+ if ( item->GetChildCount() && !item->HasFlag(wxPG_PROP_AGGREGATE) )
+ {
+ // deleting a category
+ if ( item->IsCategory() )
{
- // Remove children from non-categorized array.
- for ( i=0; i<pwc->GetChildCount(); i++ )
- {
- wxPGProperty * p = pwc->Item( i );
- wxASSERT( p != NULL );
- if ( !p->IsCategory() )
- m_abcArray->m_children.Remove( p );
- }
-
- if ( IsInNonCatMode() )
- m_abcArray->FixIndexesOfChildren();
+ if ( pwc == m_currentCategory )
+ m_currentCategory = NULL;
}
+
+ item->DeleteChildren();
}
if ( !IsInNonCatMode() )
{
// categorized mode - non-categorized array
- // Remove from non-cat array, but only if parent is in it
- if ( !item->IsCategory() && item->GetParent()->IsCategory() )
+ // Remove from non-cat array
+ if ( !item->IsCategory() &&
+ (parent->IsCategory() || parent->IsRoot()) )
{
if ( m_abcArray )
- {
- m_abcArray->m_children.Remove( item );
- }
+ m_abcArray->RemoveChild(item);
}
// categorized mode - categorized array
- item->m_parent->m_children.RemoveAt(indinparent);
- item->m_parent->FixIndexesOfChildren(/*indinparent*/);
+ wxArrayPGProperty& parentsChildren = parent->m_children;
+ parentsChildren.erase( parentsChildren.begin() + indinparent );
+ item->m_parent->FixIndicesOfChildren();
}
else
{
}
}
}
- cat_parent->m_children.RemoveAt(cat_index);
+ 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 );
- item->m_parent->m_children.RemoveAt(indinparent);
- item->m_parent->FixIndexesOfChildren(indinparent);
+ wxArrayPGProperty& parentsChildren = item->m_parent->m_children;
+ parentsChildren.erase(parentsChildren.begin() + indinparent);
+ item->m_parent->FixIndicesOfChildren(indinparent);
}
}
- if ( item->GetBaseName().Len() ) m_dictName.erase(item->GetBaseName());
+ 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
- delete item;
+ if ( doDelete )
+ delete item;
+ else
+ item->OnDetached(this, pg);
m_itemsAdded = 1; // Not a logical assignment (but required nonetheless).
}
// -----------------------------------------------------------------------
+
+#endif // wxUSE_PROPGRID