X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f915d44b3f310ceabfca6b0e66c6c81fd778f7b9..f645356813e0479e74db69e15e915384922001ea:/src/propgrid/propgridpagestate.cpp diff --git a/src/propgrid/propgridpagestate.cpp b/src/propgrid/propgridpagestate.cpp index 705baa516a..6cda98c1a5 100644 --- a/src/propgrid/propgridpagestate.cpp +++ b/src/propgrid/propgridpagestate.cpp @@ -6,7 +6,7 @@ // Created: 2008-08-24 // RCS-ID: $Id$ // Copyright: (c) Jaakko Salli -// Licence: wxWindows license +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx/wx.h". @@ -107,7 +107,8 @@ void wxPropertyGridIteratorBase::Assign( const wxPropertyGridIteratorBase& it ) void wxPropertyGridIteratorBase::Prev() { wxPGProperty* property = m_property; - wxASSERT( property ); + if ( !property ) + return; wxPGProperty* parent = property->GetParent(); wxASSERT( parent ); @@ -152,7 +153,8 @@ void wxPropertyGridIteratorBase::Prev() void wxPropertyGridIteratorBase::Next( bool iterateChildren ) { wxPGProperty* property = m_property; - wxASSERT( property ); + if ( !property ) + return; if ( property->GetChildCount() && wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) && @@ -201,12 +203,11 @@ void wxPropertyGridIteratorBase::Next( bool iterateChildren ) 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; @@ -216,6 +217,15 @@ wxPropertyGridPageState::wxPropertyGridPageState() 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); } // ----------------------------------------------------------------------- @@ -231,7 +241,7 @@ void wxPropertyGridPageState::InitNonCatMode() { if ( !m_abcArray ) { - m_abcArray = new wxPGRootProperty(); + m_abcArray = new wxPGRootProperty(wxS("")); m_abcArray->SetParentState(this); m_abcArray->SetFlag(wxPG_PROP_CHILDREN_ARE_COPIES); } @@ -245,17 +255,17 @@ void wxPropertyGridPageState::InitNonCatMode() 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; } } @@ -268,20 +278,27 @@ void wxPropertyGridPageState::InitNonCatMode() 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; } // ----------------------------------------------------------------------- @@ -308,7 +325,11 @@ void wxPropertyGridPageState::CalculateFontAndBitmapStuff( int WXUNUSED(vspacing 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 ) @@ -340,22 +361,21 @@ void wxPropertyGridPageState::OnClientWidthChange( int newWidth, int widthChange 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; } } } @@ -369,7 +389,7 @@ void wxPropertyGridPageState::OnClientWidthChange( int newWidth, int widthChange wxPGProperty* wxPropertyGridPageState::GetLastItem( int flags ) { if ( !m_properties->GetChildCount() ) - return (wxPGProperty*) NULL; + return NULL; wxPG_ITERATOR_CREATE_MASKS(flags, int itemExMask, int parentExMask) @@ -403,7 +423,7 @@ wxPropertyCategory* wxPropertyGridPageState::GetPropertyCategory( const wxPGProp return (wxPropertyCategory*)parent; } while ( grandparent ); - return (wxPropertyCategory*) NULL; + return NULL; } // ----------------------------------------------------------------------- @@ -443,7 +463,7 @@ wxPGProperty* wxPropertyGridPageState::BaseGetPropertyByName( const wxString& na it = m_dictName.find(name); if ( it != m_dictName.end() ) return (wxPGProperty*) it->second; - return (wxPGProperty*) NULL; + return NULL; } // ----------------------------------------------------------------------- @@ -453,8 +473,15 @@ void wxPropertyGridPageState::DoSetPropertyName( wxPGProperty* p, { wxCHECK_RET( p, wxT("invalid property id") ); - if ( p->GetBaseName().Len() ) m_dictName.erase( p->GetBaseName() ); - if ( newName.Len() ) m_dictName[newName] = (void*) p; + 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); } @@ -483,7 +510,7 @@ void wxPropertyGridPageState::DoSetPropertyName( wxPGProperty* p, #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; \ @@ -592,67 +619,113 @@ bool wxPropertyGridPageState::EnableCategories( bool enable ) // ----------------------------------------------------------------------- -#if wxUSE_STL -#include +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 bool wxPG_SortFunc(wxPGProperty *p1, wxPGProperty *p2) +static int wxPG_SortFunc_ByLabel(wxPGProperty **pp1, wxPGProperty **pp2) { - return p1->GetLabel() < p2->GetLabel(); + wxPGProperty *p1 = *pp1; + wxPGProperty *p2 = *pp2; + return p1->GetLabel().CmpNoCase( p2->GetLabel() ); } -#else +#if 0 +// +// For wxVector w/ wxUSE_STL=1, you would use code like this instead: +// + +#include -static int wxPG_SortFunc(wxPGProperty **p1, wxPGProperty **p2) +static bool wxPG_SortFunc_ByFunction(wxPGProperty *p1, wxPGProperty *p2) { - wxPGProperty *pp1 = *p1; - wxPGProperty *pp2 = *p2; - return pp1->GetLabel().compare( pp2->GetLabel() ); + 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::SortChildren( wxPGProperty* p ) +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; -#if wxUSE_STL - std::sort(pwc->m_children.begin(), pwc->m_children.end(), wxPG_SortFunc); -#else - pwc->m_children.Sort( wxPG_SortFunc ); + 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 indexes - pwc->FixIndicesOfChildren(); + // Fix indices + p->FixIndicesOfChildren(); + if ( flags & wxPG_RECURSE ) + { + // Apply sort recursively + for ( unsigned int i=0; iGetChildCount(); 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;iGetChildCount();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; } // ----------------------------------------------------------------------- @@ -663,7 +736,7 @@ wxPGProperty* wxPropertyGridPageState::DoGetItemAtY( int y ) const { // Outside? if ( y < 0 ) - return (wxPGProperty*) NULL; + return NULL; unsigned int a = 0; return m_properties->GetItemAtY(y, GetGrid()->m_lineHeight, &a); @@ -671,11 +744,13 @@ wxPGProperty* wxPropertyGridPageState::DoGetItemAtY( int y ) const // ----------------------------------------------------------------------- -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; } @@ -704,8 +779,10 @@ int wxPropertyGridPageState::GetColumnFitWidth(wxClientDC& dc, 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); @@ -740,7 +817,9 @@ int wxPropertyGridPageState::GetColumnMinWidth( int WXUNUSED(column) ) const 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; @@ -764,7 +843,9 @@ void wxPropertyGridPageState::PropagateColSizeDec( int column, int decrease, int 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(); @@ -799,11 +880,11 @@ void wxPropertyGridPageState::DoSetSplitterPosition( int newXPos, int splitterCo 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(); } @@ -814,7 +895,7 @@ void wxPropertyGridPageState::SetSplitterLeft( bool subProps ) { wxPropertyGrid* pg = GetGrid(); wxClientDC dc(pg); - dc.SetFont(pg->m_font); + dc.SetFont(pg->GetFont()); int maxW = GetColumnFitWidth(dc, m_properties, 0, subProps); @@ -824,14 +905,14 @@ void wxPropertyGridPageState::SetSplitterLeft( bool 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; @@ -855,7 +936,7 @@ wxSize wxPropertyGridPageState::DoFitColumns( bool WXUNUSED(allowGridResize) ) 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; @@ -880,10 +961,6 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) wxPropertyGrid* pg = GetGrid(); -#ifdef __WXDEBUG__ - const bool debug = false; -#endif - unsigned int i; unsigned int lastColumn = m_colWidths.size() - 1; int width = m_width; @@ -891,14 +968,11 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) // // 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; -#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 @@ -911,18 +985,9 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) } 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; } } @@ -930,10 +995,9 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) for ( i=0; iHasVirtualWidth(),colsWidth); -#endif + wxLogTrace("propgrid", + wxS(" HasVirtualWidth: %i colsWidth: %i"), + (int)pg->HasVirtualWidth(), colsWidth); // Then mode-based requirement if ( !pg->HasVirtualWidth() ) @@ -944,10 +1008,9 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) 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 ) @@ -955,10 +1018,10 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) // 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(); @@ -980,54 +1043,91 @@ void wxPropertyGridPageState::CheckColumnWidths( int widthChange ) pg->RecalculateVirtualSize(); } -#ifdef __WXDEBUG__ - if ( debug ) - for ( i=0; iGetInternalFlags() & 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 ) - { - splitterX = centerX; - } - else if ( widthChange ) + if ( m_colWidths.size() == 2 && + m_columnProportions[0] == m_columnProportions[1] ) { - //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; + + 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); + // 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; im_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); } } @@ -1035,8 +1135,10 @@ 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(), m_colWidths.size() - colCount ); + m_colWidths.RemoveAt( m_colWidths.size()-1, + m_colWidths.size() - colCount ); if ( m_pPropGrid->GetState() == this ) m_pPropGrid->RecalculateVirtualSize(); @@ -1044,6 +1146,21 @@ void wxPropertyGridPageState::SetColumnCount( int colCount ) 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 { @@ -1092,6 +1209,29 @@ int wxPropertyGridPageState::HitTestH( int x, int* pSplitterHit, int* pSplitterH 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 // ----------------------------------------------------------------------- @@ -1113,8 +1253,9 @@ bool wxPropertyGridPageState::DoSetPropertyValueString( wxPGProperty* p, const w 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; @@ -1129,8 +1270,9 @@ bool wxPropertyGridPageState::DoSetPropertyValue( wxPGProperty* p, wxVariant& va 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; } @@ -1151,35 +1293,56 @@ bool wxPropertyGridPageState::DoSetPropertyValueWxObjectPtr( wxPGProperty* p, wx 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; im_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 ) @@ -1221,7 +1384,7 @@ bool wxPropertyGridPageState::DoSelectProperty( wxPGProperty* p, unsigned int fl if ( this == m_pPropGrid->GetState() ) return m_pPropGrid->DoSelectProperty( p, flags ); - m_selected = p; + DoSetSelection(p); return true; } @@ -1229,59 +1392,12 @@ bool wxPropertyGridPageState::DoSelectProperty( wxPGProperty* p, unsigned int fl 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 // ----------------------------------------------------------------------- @@ -1382,7 +1498,7 @@ void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wx wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 ); const wxString& name = current->GetName(); - if ( name.length() > 0 ) + if ( !name.empty() ) { // // '@' signified a special entry @@ -1401,18 +1517,17 @@ void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wx 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); } @@ -1442,7 +1557,7 @@ void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wx wxVariant *current = (wxVariant*)*node; const wxString& name = current->GetName(); - if ( name.length() > 0 ) + if ( !name.empty() ) { // // '@' signified a special entry @@ -1497,9 +1612,7 @@ void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wx m_pPropGrid->Thaw(); if ( this == m_pPropGrid->GetState() ) - { - m_selected->UpdateControl(m_pPropGrid->GetEditorControl()); - } + m_pPropGrid->RefreshEditor(); } } @@ -1513,9 +1626,9 @@ bool wxPropertyGridPageState::PrepareToAddItem( wxPGProperty* property, { 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() ) { @@ -1550,20 +1663,18 @@ bool wxPropertyGridPageState::PrepareToAddItem( wxPGProperty* property, } } -#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()); + wxFAIL_MSG(wxString::Format( + "wxPropertyGrid item with name \"%s\" already exists", + property->GetName())); + wxPGGlobalVars->m_warnings++; } -#endif - - // Make sure nothing is selected. - if ( propGrid ) - propGrid->ClearSelection(false); +#endif // wxDEBUG_LEVEL // NULL parent == root parent if ( !scheduledParent ) @@ -1593,7 +1704,7 @@ wxPGProperty* wxPropertyGridPageState::DoAppend( wxPGProperty* property ) { wxPropertyCategory* cur_cat = m_currentCategory; if ( property->IsCategory() ) - cur_cat = (wxPropertyCategory*) NULL; + cur_cat = NULL; return DoInsert( cur_cat, -1, property ); } @@ -1611,11 +1722,14 @@ wxPGProperty* wxPropertyGridPageState::DoInsert( wxPGProperty* parent, int index bool res = PrepareToAddItem( property, (wxPropertyCategory*)parent ); - // PrepareToAddItem() may just decide to use use current category + // 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... @@ -1628,42 +1742,34 @@ wxPGProperty* wxPropertyGridPageState::DoInsert( wxPGProperty* parent, int index // 1) Add to given category in given index. // 2) Add as last item in m_abcArray. - if ( !parent->IsCategory() && !parent->IsRoot() ) + if ( m_properties == &m_regularArray ) { - // Parent is wxParentingProperty: Just stick it in... - parent->AddChild2( property, index ); - } - else - { - // 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 && !property->IsCategory() ) - 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 ( !property->IsCategory() ) - 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 @@ -1676,7 +1782,8 @@ wxPGProperty* wxPropertyGridPageState::DoInsert( wxPGProperty* parent, int index } // 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(); @@ -1698,57 +1805,82 @@ void wxPropertyGridPageState::DoDelete( wxPGProperty* item, bool doDelete ) 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; iGetChildCount(); 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; iGetChildCount(); i++ ) - { - wxPGProperty * p = pwc->Item( i ); - wxASSERT( p != NULL ); - if ( !p->IsCategory() ) - m_abcArray->RemoveChild(p); - } - - if ( IsInNonCatMode() ) - m_abcArray->FixIndicesOfChildren(); + 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->RemoveChild(item); } // categorized mode - categorized array - wxArrayPGProperty& parentsChildren = item->m_parent->m_children; + wxArrayPGProperty& parentsChildren = parent->m_children; parentsChildren.erase( parentsChildren.begin() + indinparent ); item->m_parent->FixIndicesOfChildren(); } @@ -1787,11 +1919,23 @@ void wxPropertyGridPageState::DoDelete( wxPGProperty* item, bool doDelete ) } } - 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 if ( doDelete ) delete item; + else + item->OnDetached(this, pg); m_itemsAdded = 1; // Not a logical assignment (but required nonetheless).