From fc72fab6c65b688cb3c4c96798629195f6e82bae Mon Sep 17 00:00:00 2001 From: Jaakko Salli Date: Mon, 17 Aug 2009 18:36:00 +0000 Subject: [PATCH] Added multiple selection feature to wxPropertyGrid (enabled by setting wxPG_EX_MULTIPLE_SELECTION style) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61681 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/propgrid/manager.h | 6 + include/wx/propgrid/property.h | 6 +- include/wx/propgrid/propgrid.h | 87 +++-- include/wx/propgrid/propgridiface.h | 33 +- include/wx/propgrid/propgridpagestate.h | 40 ++- interface/wx/propgrid/manager.h | 6 + interface/wx/propgrid/propgrid.h | 42 ++- interface/wx/propgrid/propgridiface.h | 20 +- samples/propgrid/propgrid.cpp | 8 +- samples/propgrid/tests.cpp | 71 +++- src/propgrid/editors.cpp | 10 +- src/propgrid/manager.cpp | 20 +- src/propgrid/propgrid.cpp | 431 +++++++++++++++++------- src/propgrid/propgridiface.cpp | 35 +- src/propgrid/propgridpagestate.cpp | 47 ++- 15 files changed, 664 insertions(+), 198 deletions(-) diff --git a/include/wx/propgrid/manager.h b/include/wx/propgrid/manager.h index 3371c5695e..75db8dafdf 100644 --- a/include/wx/propgrid/manager.h +++ b/include/wx/propgrid/manager.h @@ -491,6 +491,12 @@ public: */ bool IsPageModified( size_t index ) const; + /** + Returns true if property is selected. Since selection is page + based, this function checks every page in the manager. + */ + virtual bool IsPropertySelected( wxPGPropArg id ) const; + virtual void Refresh( bool eraseBackground = true, const wxRect* rect = (const wxRect*) NULL ); diff --git a/include/wx/propgrid/property.h b/include/wx/propgrid/property.h index 4920599944..a6a55f0bc2 100644 --- a/include/wx/propgrid/property.h +++ b/include/wx/propgrid/property.h @@ -435,7 +435,11 @@ wxPG_PROP_CLASS_SPECIFIC_1 = 0x00080000, /** Indicates the bit useable by derived properties. */ -wxPG_PROP_CLASS_SPECIFIC_2 = 0x00100000 +wxPG_PROP_CLASS_SPECIFIC_2 = 0x00100000, + +/** Indicates that the property is being deleted and should be ignored. +*/ +wxPG_PROP_BEING_DELETED = 0x00200000 }; diff --git a/include/wx/propgrid/propgrid.h b/include/wx/propgrid/propgrid.h index 974fd034cd..120c6b308b 100644 --- a/include/wx/propgrid/propgrid.h +++ b/include/wx/propgrid/propgrid.h @@ -247,7 +247,20 @@ wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES = 0x00400000, /** Hides page selection buttons from toolbar. */ -wxPG_EX_HIDE_PAGE_BUTTONS = 0x01000000 +wxPG_EX_HIDE_PAGE_BUTTONS = 0x01000000, + +/** Allows multiple properties to be selected by user (by pressing SHIFT + when clicking on a property, or by dragging with left mouse button + down). + + You can get array of selected properties with + wxPropertyGridInterface::GetSelectedProperties(). In multiple selection + mode wxPropertyGridInterface::GetSelection() returns + property which has editor active (usually the first one + selected). Other useful member functions are ClearSelection(), + AddToSelection() and RemoveFromSelection(). +*/ +wxPG_EX_MULTIPLE_SELECTION = 0x02000000 }; @@ -839,12 +852,6 @@ public: /** Returns currently selected property. */ wxPGProperty* GetSelectedProperty() const { return GetSelection(); } - /** Returns currently selected property. */ - wxPGProperty* GetSelection() const - { - return m_selected; - } - /** Returns current selection background colour. */ wxColour GetSelectionBackgroundColour() const { return m_colSelBack; } @@ -953,10 +960,46 @@ public: wxEVT_PG_SELECTED. In wxWidgets 2.9 and later, it no longer does that. - @see wxPropertyGrid::Unselect + @remarks This clears any previous selection. */ bool SelectProperty( wxPGPropArg id, bool focus = false ); + /** + Set entire new selection from given list of properties. + */ + void SetSelection( const wxArrayPGProperty& newSelection ) + { + DoSetSelection( newSelection, wxPG_SEL_DONT_SEND_EVENT ); + } + + /** + Adds given property into selection. If wxPG_EX_MULTIPLE_SELECTION + extra style is not used, then this has same effect as + calling SelectProperty(). + + @remarks Multiple selection is not supported for categories. This + means that if you have properties selected, you cannot + add category to selection, and also if you have category + selected, you cannot add other properties to selection. + This member function will fail silently in these cases, + even returning true. + */ + bool AddToSelection( wxPGPropArg id ) + { + wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) + return DoAddToSelection(p, wxPG_SEL_DONT_SEND_EVENT); + } + + /** + Removes given property from selection. If property is not selected, + an assertion failure will occur. + */ + bool RemoveFromSelection( wxPGPropArg id ) + { + wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) + return DoRemoveFromSelection(p, wxPG_SEL_DONT_SEND_EVENT); + } + /** Sets category caption background colour. */ void SetCaptionBackgroundColour(const wxColour& col); @@ -1443,7 +1486,6 @@ public: // // Public methods for semi-public use - // (not protected for optimization) // bool DoSelectProperty( wxPGProperty* p, unsigned int flags = 0 ); @@ -1635,12 +1677,6 @@ protected: /** When drawing next time, clear this many item slots at the end. */ int m_clearThisMany; - /** Pointer to selected property. Note that this is duplicated in - m_state for better transiency between pages so that the selected - item can be retained. - */ - wxPGProperty* m_selected; - // pointer to property that has mouse hovering wxPGProperty* m_propHover; @@ -1767,6 +1803,10 @@ protected: protected: + bool AddToSelectionFromInputEvent( wxPGProperty* prop, + wxMouseEvent* event = NULL, + int selFlags = 0 ); + /** Adjust the centering of the bitmap icons (collapse / expand) when the caption font changes. @@ -1786,14 +1826,6 @@ protected: */ void CorrectEditorWidgetPosY(); - /** Deselect current selection, if any. Returns true if success - (ie. validator did not intercept). - - Unlike ClearSelection(), DoClearSelection() sends the - wxEVT_PG_SELECTED event. - */ - bool DoClearSelection(); - int DoDrawItems( wxDC& dc, const wxRect* clipRect, bool isBuffered ) const; @@ -1826,6 +1858,15 @@ protected: bool DoEditorValidate(); + void DoSetSelection( const wxArrayPGProperty& newSelection, + int selFlags = 0 ); + + bool DoAddToSelection( wxPGProperty* prop, + int selFlags = 0 ); + + bool DoRemoveFromSelection( wxPGProperty* prop, + int selFlags = 0 ); + wxPGProperty* DoGetItemAtY( int y ) const; void DoSetSplitterPosition_( int newxpos, diff --git a/include/wx/propgrid/propgridiface.h b/include/wx/propgrid/propgridiface.h index e7bbd29a0a..a110518c04 100644 --- a/include/wx/propgrid/propgridiface.h +++ b/include/wx/propgrid/propgridiface.h @@ -591,10 +591,23 @@ public: } #endif - /** Returns currently selected property. */ - wxPGProperty* GetSelection() const + /** + Returns currently selected property. NULL if none. + + @remarks When wxPG_EX_MULTIPLE_SELECTION extra style is used, this + member function returns the focused property, that is the + one which can have active editor. + */ + wxPGProperty* GetSelection() const; + + /** + Returns list of currently selected properties. + + @remarks wxArrayPGProperty should be compatible with std::vector API. + */ + const wxArrayPGProperty& GetSelectedProperties() const { - return m_pState->GetSelection(); + return m_pState->m_selection; } #ifndef SWIG @@ -724,10 +737,19 @@ public: return ( (p->GetFlags() & wxPG_PROP_MODIFIED) ? true : false ); } + /** + Returns true if property is selected. + */ + bool IsPropertySelected( wxPGPropArg id ) const + { + wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) + return m_pState->DoIsPropertySelected(p); + } + /** Returns true if property is shown (ie hideproperty with true not called for it). - */ + */ bool IsPropertyShown( wxPGPropArg id ) const { wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) @@ -1269,6 +1291,9 @@ public: protected: + bool DoClearSelection( bool validation = false, + int selFlags = 0 ); + /** In derived class, implement to set editable state component with given name to given value. diff --git a/include/wx/propgrid/propgridpagestate.h b/include/wx/propgrid/propgridpagestate.h index c6d8cde900..9aa0c11335 100644 --- a/include/wx/propgrid/propgridpagestate.h +++ b/include/wx/propgrid/propgridpagestate.h @@ -475,11 +475,6 @@ public: return (unsigned int) m_colWidths.size(); } - wxPGProperty* GetSelection() const - { - return m_selected; - } - int GetColumnMinWidth( int column ) const; int GetColumnWidth( unsigned int column ) const @@ -500,6 +495,28 @@ public: return ((wxPropertyGridPageState*)this)->GetLastItem(flags); } + /** + Returns currently selected property. + */ + wxPGProperty* GetSelection() const + { + if ( m_selection.size() == 0 ) + return NULL; + return m_selection[0]; + } + + void DoSetSelection( wxPGProperty* prop ) + { + m_selection.clear(); + if ( prop ) + m_selection.push_back(prop); + } + + bool DoClearSelection() + { + return DoSelectProperty(NULL); + } + wxPropertyCategory* GetPropertyCategory( const wxPGProperty* p ) const; wxPGProperty* GetPropertyByLabel( const wxString& name, @@ -590,8 +607,6 @@ public: bool PrepareAfterItemsAdded(); - void SetSelection( wxPGProperty* p ) { m_selected = p; } - /** Called after virtual height needs to be recalculated. */ void VirtualHeightChanged() @@ -605,14 +620,11 @@ public: /** Returns property by its name. */ wxPGProperty* BaseGetPropertyByName( const wxString& name ) const; - void DoClearSelection() - { - m_selected = NULL; - } - /** Called in, for example, wxPropertyGrid::Clear. */ void DoClear(); + bool DoIsPropertySelected( wxPGProperty* prop ) const; + bool DoCollapse( wxPGProperty* p ); bool DoExpand( wxPGProperty* p ); @@ -659,8 +671,8 @@ protected: /** Most recently added category. */ wxPropertyCategory* m_currentCategory; - /** Pointer to selected property. */ - wxPGProperty* m_selected; + /** Array of selected property. */ + wxArrayPGProperty m_selection; /** Virtual width. */ int m_width; diff --git a/interface/wx/propgrid/manager.h b/interface/wx/propgrid/manager.h index ff4a39314a..3aa7e9746c 100644 --- a/interface/wx/propgrid/manager.h +++ b/interface/wx/propgrid/manager.h @@ -388,6 +388,12 @@ public: */ bool IsPageModified( size_t index ) const; + /** + Returns true if property is selected. Since selection is page + based, this function checks every page in the manager. + */ + virtual bool IsPropertySelected( wxPGPropArg id ) const; + /** Removes a page. diff --git a/interface/wx/propgrid/propgrid.h b/interface/wx/propgrid/propgrid.h index 42366a011c..0872e5775d 100644 --- a/interface/wx/propgrid/propgrid.h +++ b/interface/wx/propgrid/propgrid.h @@ -152,7 +152,20 @@ wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES = 0x00400000, /** Hides page selection buttons from tool bar. */ -wxPG_EX_HIDE_PAGE_BUTTONS = 0x01000000 +wxPG_EX_HIDE_PAGE_BUTTONS = 0x01000000, + +/** Allows multiple properties to be selected by user (by pressing SHIFT + when clicking on a property, or by dragging with left mouse button + down). + + You can get array of selected properties with + wxPropertyGridInterface::GetSelectedProperties(). In multiple selection + mode wxPropertyGridInterface::GetSelection() returns + property which has editor active (usually the first one + selected). Other useful member functions are ClearSelection(), + AddToSelection() and RemoveFromSelection(). +*/ +wxPG_EX_MULTIPLE_SELECTION = 0x02000000 }; @@ -394,6 +407,20 @@ public: */ void AddActionTrigger( int action, int keycode, int modifiers = 0 ); + /** + Adds given property into selection. If wxPG_EX_MULTIPLE_SELECTION + extra style is not used, then this has same effect as + calling SelectProperty(). + + @remarks Multiple selection is not supported for categories. This + means that if you have properties selected, you cannot + add category to selection, and also if you have category + selected, you cannot add other properties to selection. + This member function will fail silently in these cases, + even returning true. + */ + bool AddToSelection( wxPGPropArg id ); + /** This static function enables or disables automatic use of wxGetTranslation() for following strings: wxEnumProperty list labels, @@ -708,6 +735,12 @@ public: */ void ResetColours(); + /** + Removes given property from selection. If property is not selected, + an assertion failure will occur. + */ + bool RemoveFromSelection( wxPGPropArg id ); + /** Selects a property. Editor widget is automatically created, but not focused unless focus is true. @@ -725,6 +758,8 @@ public: wxEVT_PG_SELECTED. In wxWidgets 2.9 and later, it no longer does that. + @remarks This clears any previous selection. + @see wxPropertyGridInterface::ClearSelection() */ bool SelectProperty( wxPGPropArg id, bool focus = false ); @@ -790,6 +825,11 @@ public: */ void SetMarginColour(const wxColour& col); + /** + Set entire new selection from given list of properties. + */ + void SetSelection( const wxArrayPGProperty& newSelection ); + /** Sets selection background colour - applies to selected property name background. diff --git a/interface/wx/propgrid/propgridiface.h b/interface/wx/propgrid/propgridiface.h index 512411ec59..3289ee5aec 100644 --- a/interface/wx/propgrid/propgridiface.h +++ b/interface/wx/propgrid/propgridiface.h @@ -418,7 +418,20 @@ public: wxVariant GetPropertyValues( const wxString& listname = wxEmptyString, wxPGProperty* baseparent = NULL, long flags = 0 ) const; - /** Returns currently selected property. */ + /** + Returns list of currently selected properties. + + @remarks wxArrayPGProperty should be compatible with std::vector API. + */ + const wxArrayPGProperty& GetSelectedProperties() const; + + /** + Returns currently selected property. NULL if none. + + @remarks When wxPG_EX_MULTIPLE_SELECTION extra style is used, this + member function returns the focused property, that is the + one which can have active editor. + */ wxPGProperty* GetSelection() const; /** @@ -538,6 +551,11 @@ public: */ bool IsPropertyModified( wxPGPropArg id ) const; + /** + Returns true if property is selected. + */ + virtual bool IsPropertySelected( wxPGPropArg id ) const; + /** Returns @true if property is shown (ie. HideProperty() with @true not called for it). diff --git a/samples/propgrid/propgrid.cpp b/samples/propgrid/propgrid.cpp index f2b0a821d2..41ab8716ab 100644 --- a/samples/propgrid/propgrid.cpp +++ b/samples/propgrid/propgrid.cpp @@ -2045,7 +2045,8 @@ void FormMain::CreateGrid( int style, int extraStyle ) if ( extraStyle == -1 ) // default extra style - extraStyle = wxPG_EX_MODE_BUTTONS; + extraStyle = wxPG_EX_MODE_BUTTONS | + wxPG_EX_MULTIPLE_SELECTION; //| wxPG_EX_AUTO_UNSPECIFIED_VALUES //| wxPG_EX_GREY_LABEL_WHEN_DISABLED //| wxPG_EX_NATIVE_DOUBLE_BUFFERING @@ -2140,7 +2141,8 @@ FormMain::FormMain(const wxString& title, const wxPoint& pos, const wxSize& size wxPG_TOOLBAR | wxPG_DESCRIPTION, // extra style - wxPG_EX_MODE_BUTTONS + wxPG_EX_MODE_BUTTONS | + wxPG_EX_MULTIPLE_SELECTION //| wxPG_EX_AUTO_UNSPECIFIED_VALUES //| wxPG_EX_GREY_LABEL_WHEN_DISABLED //| wxPG_EX_NATIVE_DOUBLE_BUFFERING @@ -2928,6 +2930,8 @@ void FormMain::OnSelectStyle( wxCommandEvent& WXUNUSED(event) ) ADD_FLAG(wxPG_EX_NATIVE_DOUBLE_BUFFERING) ADD_FLAG(wxPG_EX_AUTO_UNSPECIFIED_VALUES) ADD_FLAG(wxPG_EX_WRITEONLY_BUILTIN_ATTRIBUTES) + ADD_FLAG(wxPG_EX_HIDE_PAGE_BUTTONS) + ADD_FLAG(wxPG_EX_MULTIPLE_SELECTION) wxMultiChoiceDialog dlg( this, wxT("Select extra window styles to use"), wxT("wxPropertyGrid Extra Style"), chs ); dlg.SetSelections(sel); diff --git a/samples/propgrid/tests.cpp b/samples/propgrid/tests.cpp index a6b700dbf6..c2b5e5b953 100644 --- a/samples/propgrid/tests.cpp +++ b/samples/propgrid/tests.cpp @@ -249,6 +249,10 @@ protected: failures++; \ } +#define RT_ASSERT(COND) \ + if (!(COND)) \ + RT_FAILURE() + #define RT_FAILURE_MSG(MSG) \ { \ wxString s1 = wxString::Format(wxT("Test failure in tests.cpp, line %i."),__LINE__-1); \ @@ -714,6 +718,63 @@ bool FormMain::RunTests( bool fullTest, bool interactive ) #endif } + { + // + // Test multiple selection + RT_START_TEST(MULTIPLE_SELECTION) + if ( !(pgman->GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) ) + CreateGrid( -1, wxPG_EX_MULTIPLE_SELECTION); + pgman = m_pPropGridManager; + + wxPropertyGrid* pg = pgman->GetGrid(); + + wxPGProperty* prop1 = pg->GetProperty(wxT("Label")); + wxPGProperty* prop2 = pg->GetProperty(wxT("Cell Text Colour")); + wxPGProperty* prop3 = pg->GetProperty(wxT("Height")); + wxPGProperty* catProp = pg->GetProperty(wxT("Appearance")); + + RT_ASSERT( prop1 && prop2 && prop3 ); + + pg->ClearSelection(); + pg->AddToSelection(prop1); + pg->AddToSelection(prop2); + pg->AddToSelection(prop3); + + // Adding category to selection should fail silently + pg->AddToSelection(catProp); + + wxArrayPGProperty selectedProperties = pg->GetSelectedProperties(); + + RT_ASSERT( selectedProperties.size() == 3 ) + RT_ASSERT( pg->IsPropertySelected(prop1) ) + RT_ASSERT( pg->IsPropertySelected(prop2) ) + RT_ASSERT( pg->IsPropertySelected(prop3) ) + RT_ASSERT( !pg->IsPropertySelected(catProp) ) + + pg->RemoveFromSelection(prop1); + wxArrayPGProperty selectedProperties2 = pg->GetSelectedProperties(); + + RT_ASSERT( selectedProperties2.size() == 2 ) + RT_ASSERT( !pg->IsPropertySelected(prop1) ) + RT_ASSERT( pg->IsPropertySelected(prop2) ) + RT_ASSERT( pg->IsPropertySelected(prop3) ) + + pg->ClearSelection(); + + wxArrayPGProperty selectedProperties3 = pg->GetSelectedProperties(); + + RT_ASSERT( selectedProperties3.size() == 0 ) + RT_ASSERT( !pg->IsPropertySelected(prop1) ) + RT_ASSERT( !pg->IsPropertySelected(prop2) ) + RT_ASSERT( !pg->IsPropertySelected(prop3) ) + + pg->SelectProperty(prop2); + + RT_ASSERT( !pg->IsPropertySelected(prop1) ) + RT_ASSERT( pg->IsPropertySelected(prop2) ) + RT_ASSERT( !pg->IsPropertySelected(prop3) ) + } + { RT_START_TEST(Attributes) @@ -985,8 +1046,7 @@ bool FormMain::RunTests( bool fullTest, bool interactive ) pgman = m_pPropGridManager; } - /* - { + /*{ // TODO: This test fails. RT_START_TEST(SetSplitterPosition) @@ -1023,8 +1083,7 @@ bool FormMain::RunTests( bool fullTest, bool interactive ) // Recreate the original grid CreateGrid( -1, -1 ); pgman = m_pPropGridManager; - } - */ + }*/ { RT_START_TEST(HideProperty) @@ -1034,7 +1093,7 @@ bool FormMain::RunTests( bool fullTest, bool interactive ) srand(0x1234); wxArrayPGProperty arr1; - + arr1 = GetPropertiesInRandomOrder(page); if ( !_failed_ ) @@ -1178,7 +1237,7 @@ bool FormMain::RunTests( bool fullTest, bool interactive ) wxASSERT(wxPG_EX_INIT_NOCAT == 0x00001000); - for ( i=12; i<24; i++ ) + for ( i=12; i<26; i++ ) { int flag = 1<GetChoices(); @@ -1576,9 +1576,9 @@ void wxPropertyGrid::CorrectEditorWidgetPosY() if ( m_selColumn == -1 ) return; - if ( m_selected && (m_wndEditor || m_wndEditor2) ) + if ( GetSelection() && (m_wndEditor || m_wndEditor2) ) { - wxRect r = GetEditorWidgetRect(m_selected, m_selColumn); + wxRect r = GetEditorWidgetRect(GetSelection(), m_selColumn); if ( m_wndEditor ) { @@ -1637,7 +1637,7 @@ wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos, int maxLen ) { wxWindowID id = wxPG_SUBID1; - wxPGProperty* prop = m_selected; + wxPGProperty* prop = GetSelection(); wxASSERT(prop); int tcFlags = wxTE_PROCESS_ENTER | extraStyle; @@ -1708,7 +1708,7 @@ wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos, wxWindow* wxPropertyGrid::GenerateEditorButton( const wxPoint& pos, const wxSize& sz ) { wxWindowID id = wxPG_SUBID2; - wxPGProperty* selected = m_selected; + wxPGProperty* selected = GetSelection(); wxASSERT(selected); #ifdef __WXMAC__ diff --git a/src/propgrid/manager.cpp b/src/propgrid/manager.cpp index 444902899c..735f18fc75 100644 --- a/src/propgrid/manager.cpp +++ b/src/propgrid/manager.cpp @@ -423,8 +423,9 @@ wxPropertyGridManager::~wxPropertyGridManager() { END_MOUSE_CAPTURE - m_pPropGrid->DoSelectProperty(NULL); - m_pPropGrid->m_pState = NULL; + //m_pPropGrid->ClearSelection(); + delete m_pPropGrid; + m_pPropGrid = NULL; size_t i; for ( i=0; im_selected ) + if ( m_pPropGrid->GetSelection() ) { if ( !m_pPropGrid->ClearSelection() ) return false; @@ -883,6 +884,19 @@ bool wxPropertyGridManager::IsPageModified( size_t index ) const // ----------------------------------------------------------------------- +bool wxPropertyGridManager::IsPropertySelected( wxPGPropArg id ) const +{ + wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false) + for ( unsigned int i=0; iDoIsPropertySelected(p) ) + return true; + } + return false; +} + +// ----------------------------------------------------------------------- + wxPGProperty* wxPropertyGridManager::GetPageRoot( int index ) const { wxASSERT( index >= 0 ); diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index b323f7f4cf..5f3801518a 100644 --- a/src/propgrid/propgrid.cpp +++ b/src/propgrid/propgrid.cpp @@ -428,7 +428,6 @@ void wxPropertyGrid::Init1() m_iFlags = 0; m_pState = NULL; m_wndEditor = m_wndEditor2 = NULL; - m_selected = NULL; m_selColumn = -1; m_propHover = NULL; m_eventObject = this; @@ -582,7 +581,7 @@ wxPropertyGrid::~wxPropertyGrid() { size_t i; - DoSelectProperty(NULL); + DoSelectProperty(NULL, wxPG_SEL_NOVALIDATE|wxPG_SEL_DONT_SEND_EVENT); // This should do prevent things from going too badly wrong m_iFlags &= ~(wxPG_FL_INITIALIZED); @@ -603,8 +602,6 @@ wxPropertyGrid::~wxPropertyGrid() delete m_doubleBuffer; #endif - //m_selected = NULL; - if ( m_iFlags & wxPG_FL_CREATEDSTATE ) delete m_pState; @@ -731,9 +728,149 @@ void wxPropertyGrid::Thaw() #endif // Force property re-selection - if ( m_selected ) - DoSelectProperty(m_selected, wxPG_SEL_FORCE); + // NB: We must copy the selection. + wxArrayPGProperty selection = m_pState->m_selection; + DoSetSelection(selection, wxPG_SEL_FORCE); + } +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags ) +{ + wxCHECK( prop, false ); + + if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) ) + return DoSelectProperty(prop, selFlags); + + wxArrayPGProperty& selection = m_pState->m_selection; + + if ( !selection.size() ) + { + return DoSelectProperty(prop, selFlags); + } + else + { + // For categories, only one can be selected at a time + if ( prop->IsCategory() || selection[0]->IsCategory() ) + return true; + + selection.push_back(prop); + + if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) + { + SendEvent( wxEVT_PG_SELECTED, prop, NULL, selFlags ); + } + + // For some reason, if we use RefreshProperty(prop) here, + // we may go into infinite drawing loop. + Refresh(); + } + + return true; +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags ) +{ + wxCHECK( prop, false ); + bool res; + + wxArrayPGProperty& selection = m_pState->m_selection; + if ( selection.size() <= 1 ) + { + res = DoSelectProperty(NULL, selFlags); + } + else + { + selection.Remove(prop); + RefreshProperty(prop); + res = true; + } + + return res; +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop, + wxMouseEvent* mouseEvent, + int selFlags ) +{ + bool alreadySelected = m_pState->DoIsPropertySelected(prop); + bool res = true; + bool addToExistingSelection; + + if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION ) + { + if ( mouseEvent ) + { + if ( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN || + mouseEvent->GetEventType() == wxEVT_RIGHT_UP ) + { + // Allow right-click for context menu without + // disturbing the selection. + if ( GetSelectedProperties().size() <= 1 || + !alreadySelected ) + return DoSelectProperty(prop, selFlags); + return true; + } + else + { + addToExistingSelection = mouseEvent->ShiftDown(); + } + } + else + { + addToExistingSelection = false; + } + } + else + { + addToExistingSelection = false; + } + + if ( addToExistingSelection ) + { + if ( !alreadySelected ) + { + res = DoAddToSelection(prop, selFlags); + } + else if ( GetSelectedProperties().size() > 1 ) + { + res = DoRemoveFromSelection(prop, selFlags); + } + } + else + { + res = DoSelectProperty(prop, selFlags); + } + + return res; +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection, + int selFlags ) +{ + if ( newSelection.size() > 0 ) + { + if ( !DoSelectProperty(newSelection[0], selFlags) ) + return; + } + else + { + DoClearSelection(false, selFlags); + } + + for ( unsigned int i = 1; i < newSelection.size(); i++ ) + { + DoAddToSelection(newSelection[i], selFlags); } + + Refresh(); } // ----------------------------------------------------------------------- @@ -1684,7 +1821,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) != 0; - bool isEnabled = IsEnabled(); + bool isPgEnabled = IsEnabled(); // // Prepare some pens and brushes that are often changed to. @@ -1695,6 +1832,12 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, wxBrush capbgbrush(m_colCapBack,wxSOLID); wxPen linepen(m_colLine,1,wxSOLID); + wxColour selBackCol; + if ( isPgEnabled ) + selBackCol = m_colSelBack; + else + selBackCol = m_colMargin; + // pen that has same colour as text wxPen outlinepen(m_colPropFore,1,wxSOLID); @@ -1708,7 +1851,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, dc.DrawRectangle(-1-xRelMod,firstItemTopY-1,x+2,lastItemBottomY-firstItemTopY+2); } - const wxPGProperty* selected = m_selected; + const wxPGProperty* firstSelected = GetSelection(); const wxPropertyGridPageState* state = m_pState; #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT @@ -1808,7 +1951,9 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, wxColour rowFgCol; wxColour rowBgCol; - if ( p != selected ) + bool isSelected = state->DoIsPropertySelected(p); + + if ( !isSelected ) { // Disabled may get different colour. if ( !p->IsEnabled() ) @@ -1820,6 +1965,11 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, } else { +#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT + if ( p == firstSelected ) + wasSelectedPainted = true; +#endif + renderFlags |= wxPGCellRenderer::Selected; if ( !p->IsCategory() ) @@ -1827,25 +1977,23 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, renderFlags |= wxPGCellRenderer::DontUseCellFgCol | wxPGCellRenderer::DontUseCellBgCol; -#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT - wasSelectedPainted = true; -#endif - - // Selected gets different colour. - if ( reallyFocused ) + if ( reallyFocused && p == firstSelected ) { rowFgCol = m_colSelFore; - rowBgCol = m_colSelBack; + rowBgCol = selBackCol; } - else if ( isEnabled ) + else if ( isPgEnabled ) { rowFgCol = m_colPropFore; - rowBgCol = m_colMargin; + if ( p == firstSelected ) + rowBgCol = m_colMargin; + else + rowBgCol = selBackCol; } else { rowFgCol = m_colDisPropFore; - rowBgCol = m_colSelBack; + rowBgCol = selBackCol; } } } @@ -1934,16 +2082,31 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, DrawExpanderButton( dc, butRect, p ); // Background - if ( p == selected && m_wndEditor && ci == 1 ) + if ( isSelected && ci == 1 ) { - wxColour editorBgCol = GetEditorControl()->GetBackgroundColour(); - dc.SetBrush(editorBgCol); - dc.SetPen(editorBgCol); - dc.SetTextForeground(m_colPropFore); - dc.DrawRectangle(cellRect); - - if ( m_dragStatus == 0 && !(m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) ) - ctrlCell = true; + if ( p == firstSelected && m_wndEditor ) + { + wxColour editorBgCol = + GetEditorControl()->GetBackgroundColour(); + dc.SetBrush(editorBgCol); + dc.SetPen(editorBgCol); + dc.SetTextForeground(m_colPropFore); + dc.DrawRectangle(cellRect); + + if ( m_dragStatus == 0 && + !(m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) ) + ctrlCell = true; + } + else + { + dc.SetBrush(m_colPropBack); + dc.SetPen(m_colPropBack); + dc.SetTextForeground(m_colDisPropFore); + if ( p->IsEnabled() ) + dc.SetTextForeground(rowFgCol); + else + dc.SetTextForeground(m_colDisPropFore); + } } else { @@ -2038,7 +2201,7 @@ wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProper // If seleced property is inside the range, we'll extend the range to include // control's size. - wxPGProperty* selected = m_selected; + wxPGProperty* selected = GetSelection(); if ( selected ) { int selectedY = selected->GetY(); @@ -2078,8 +2241,12 @@ void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 ) void wxPropertyGrid::RefreshProperty( wxPGProperty* p ) { - if ( p == m_selected ) - DoSelectProperty(p, wxPG_SEL_FORCE); + if ( m_pState->DoIsPropertySelected(p) ) + { + // NB: We must copy the selection. + wxArrayPGProperty selection = m_pState->m_selection; + DoSetSelection(selection, wxPG_SEL_FORCE); + } DrawItemAndChildren(p); } @@ -2118,7 +2285,8 @@ void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p ) return; // Update child control. - if ( m_selected && m_selected->GetParent() == p ) + wxPGProperty* selected = GetSelection(); + if ( selected && selected->GetParent() == p ) RefreshEditor(); const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem(); @@ -2218,11 +2386,13 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) if ( pNewState == m_pState ) return; - wxPGProperty* oldSelection = m_selected; + wxArrayPGProperty oldSelection = m_pState->m_selection; - DoClearSelection(); + // Call ClearSelection() instead of DoClearSelection() + // so that selection clear events are not sent. + ClearSelection(); - m_pState->m_selected = oldSelection; + m_pState->m_selection = oldSelection; bool orig_mode = m_pState->IsInNonCatMode(); bool new_state_mode = pNewState->IsInNonCatMode(); @@ -2263,9 +2433,9 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState ) // Refresh, if not frozen. m_pState->PrepareAfterItemsAdded(); - // Reselect - if ( m_pState->m_selected ) - DoSelectProperty( m_pState->m_selected ); + // Reselect (Use SetSelection() instead of Do-variant so that + // events won't be sent). + SetSelection(m_pState->m_selection); RecalculateVirtualSize(0); Refresh(); @@ -2289,7 +2459,7 @@ void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int spli if ( refresh ) { - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); Refresh(); @@ -2360,14 +2530,16 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) return false; } + wxPGProperty* selected = GetSelection(); + if ( m_wndEditor && IsEditorsValueModified() && (m_iFlags & wxPG_FL_INITIALIZED) && - m_selected ) + selected ) { m_inCommitChangesFromEditor = 1; - wxVariant variant(m_selected->GetValueRef()); + wxVariant variant(selected->GetValueRef()); bool valueIsPending = false; // JACS - necessary to avoid new focus being found spuriously within OnIdle @@ -2380,10 +2552,13 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) m_chgInfo_changedProperty = NULL; // If truly modified, schedule value as pending. - if ( m_selected->GetEditorClass()->GetValueFromControl( variant, m_selected, GetEditorControl() ) ) + if ( selected->GetEditorClass()-> + GetValueFromControl( variant, + selected, + GetEditorControl() ) ) { if ( DoEditorValidate() && - PerformValidation(m_selected, variant) ) + PerformValidation(selected, variant) ) { valueIsPending = true; } @@ -2409,18 +2584,18 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) m_curFocused = oldFocus; } - res = OnValidationFailure(m_selected, variant); + res = OnValidationFailure(selected, variant); // Now prevent further validation failure messages if ( res ) { EditorsValueWasNotModified(); - OnValidationFailureReset(m_selected); + OnValidationFailureReset(selected); } } else if ( valueIsPending ) { - DoPropertyChanged( m_selected, flags ); + DoPropertyChanged( selected, flags ); EditorsValueWasNotModified(); } @@ -2523,7 +2698,7 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) ) { - if ( changedProperty == m_selected ) + if ( changedProperty == GetSelection() ) { wxWindow* editor = GetEditorControl(); wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) ); @@ -2617,7 +2792,7 @@ bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property, // // For non-wxTextCtrl editors, we do need to revert the value if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) && - property == m_selected ) + property == GetSelection() ) { property->GetEditorClass()->UpdateControl(property, editor); } @@ -2656,7 +2831,7 @@ bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& W DrawItemAndChildren(property); - if ( property == m_selected ) + if ( property == GetSelection() ) { SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL); @@ -2695,7 +2870,7 @@ void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property ) ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL); - if ( property == m_selected && GetEditorControl() ) + if ( property == GetSelection() && GetEditorControl() ) { // Calling this will recreate the control, thus resetting its colour RefreshProperty(property); @@ -2716,6 +2891,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) return true; wxWindow* editor = GetEditorControl(); + wxPGProperty* selected = GetSelection(); m_pState->m_anyModified = 1; @@ -2742,7 +2918,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) if ( !(p->m_flags & wxPG_PROP_MODIFIED) ) { p->m_flags |= wxPG_PROP_MODIFIED; - if ( p == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) ) + if ( p == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) ) { if ( editor ) SetCurControlBoldFont(); @@ -2759,7 +2935,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) { pwc->m_flags |= wxPG_PROP_MODIFIED; - if ( pwc == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) ) + if ( pwc == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) ) { if ( editor ) SetCurControlBoldFont(); @@ -2867,11 +3043,11 @@ bool wxPropertyGrid::DoEditorValidate() void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) { - wxPGProperty* selected = m_selected; + wxPGProperty* selected = GetSelection(); // Somehow, event is handled after property has been deselected. // Possibly, but very rare. - if ( !selected ) + if ( !selected || selected->HasFlag(wxPG_PROP_BEING_DELETED) ) return; if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT ) @@ -2939,7 +3115,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) if ( DoEditorValidate() ) { if ( editor->GetValueFromControl( pendingValue, - m_selected, + selected, wnd ) ) valueIsPending = true; } @@ -2966,7 +3142,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) } if ( !validationFailure && valueIsPending ) - if ( !PerformValidation(m_selected, pendingValue) ) + if ( !PerformValidation(selected, pendingValue) ) validationFailure = true; if ( validationFailure) @@ -3260,17 +3436,26 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_inDoSelectProperty = 1; - wxPGProperty* prev = m_selected; - if ( !m_pState ) { m_inDoSelectProperty = 0; return false; } + wxArrayPGProperty prevSelection = m_pState->m_selection; + wxPGProperty* prevFirstSel; + + if ( prevSelection.size() > 0 ) + prevFirstSel = prevSelection[0]; + else + prevFirstSel = NULL; + + if ( prevFirstSel && prevFirstSel->HasFlag(wxPG_PROP_BEING_DELETED) ) + prevFirstSel = NULL; + /* - if (m_selected) - wxPrintf( "Selected %s\n", m_selected->GetClassInfo()->GetClassName() ); + if ( prevFirstSel ) + wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() ); else wxPrintf( "None selected\n" ); @@ -3285,9 +3470,8 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) { m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); m_editorFocused = 0; - m_selected = p; m_selColumn = 1; - m_pState->m_selected = p; + m_pState->DoSetSelection(p); // If frozen, always free controls. But don't worry, as Thaw will // recall SelectProperty to recreate them. @@ -3299,7 +3483,9 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) else { // Is it the same? - if ( m_selected == p && !(flags & wxPG_SEL_FORCE) ) + if ( prevFirstSel == p && + prevSelection.size() <= 1 && + !(flags & wxPG_SEL_FORCE) ) { // Only set focus if not deselecting if ( p ) @@ -3324,13 +3510,12 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) // // First, deactivate previous - if ( m_selected ) + if ( prevFirstSel ) { - - OnValidationFailureReset(m_selected); + OnValidationFailureReset(prevFirstSel); // Must double-check if this is an selected in case of forceswitch - if ( p != prev ) + if ( p != prevFirstSel ) { if ( !CommitChangesFromEditor(flags) ) { @@ -3345,9 +3530,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) FreeEditors(); m_selColumn = -1; - m_selected = NULL; - m_pState->m_selected = NULL; - // We need to always fully refresh the grid here Refresh(false); @@ -3357,6 +3539,8 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY); + m_pState->DoSetSelection(p); + // // Then, activate the one given. if ( p ) @@ -3365,10 +3549,8 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) int splitterX = GetSplitterPosition(); m_editorFocused = 0; - m_selected = p; - m_pState->m_selected = p; m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE; - if ( p != prev ) + if ( p != prevFirstSel ) m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED); wxASSERT( m_wndEditor == NULL ); @@ -3609,7 +3791,7 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) // call wx event handler (here so that it also occurs on deselection) if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) ) - SendEvent( wxEVT_PG_SELECTED, m_selected, NULL, flags ); + SendEvent( wxEVT_PG_SELECTED, p, NULL, flags ); return true; } @@ -3618,14 +3800,16 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) bool wxPropertyGrid::UnfocusEditor() { - if ( !m_selected || !m_wndEditor || m_frozen ) + wxPGProperty* selected = GetSelection(); + + if ( !selected || !m_wndEditor || m_frozen ) return true; if ( !CommitChangesFromEditor(0) ) return false; SetFocusOnCanvas(); - DrawItem(m_selected); + DrawItem(selected); return true; } @@ -3634,7 +3818,7 @@ bool wxPropertyGrid::UnfocusEditor() void wxPropertyGrid::RefreshEditor() { - wxPGProperty* p = m_selected; + wxPGProperty* p = GetSelection(); if ( !p ) return; @@ -3673,14 +3857,6 @@ bool wxPropertyGrid::SelectProperty( wxPGPropArg id, bool focus ) return DoSelectProperty(p, flags); } -// ----------------------------------------------------------------------- - -bool wxPropertyGrid::DoClearSelection() -{ - // Unlike ClearSelection(), here we send the wxEVT_PG_SELECTED event. - return DoSelectProperty(NULL, 0); -} - // ----------------------------------------------------------------------- // wxPropertyGrid expand/collapse state // ----------------------------------------------------------------------- @@ -3688,9 +3864,10 @@ bool wxPropertyGrid::DoClearSelection() bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents ) { wxPGProperty* pwc = wxStaticCast(p, wxPGProperty); + wxPGProperty* selected = GetSelection(); // If active editor was inside collapsed section, then disable it - if ( m_selected && m_selected->IsSomeParent(p) ) + if ( selected && selected->IsSomeParent(p) ) { DoClearSelection(); } @@ -3773,12 +3950,18 @@ bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags ) if ( m_frozen ) return m_pState->DoHideProperty(p, hide, flags); - if ( m_selected && - ( m_selected == p || m_selected->IsSomeParent(p) ) - ) + wxArrayPGProperty selection = m_pState->m_selection; // Must use a copy + int selRemoveCount = 0; + for ( unsigned int i=0; iIsSomeParent(p) ) { - DoClearSelection(); + if ( !DoRemoveFromSelection(p, flags) ) + return false; + selRemoveCount += 1; } + } m_pState->DoHideProperty(p, hide, flags); @@ -3860,7 +4043,7 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) m_pState->CheckColumnWidths(); - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE; @@ -4019,7 +4202,7 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even ) ) { - if ( !DoSelectProperty( p ) ) + if ( !AddToSelectionFromInputEvent( p, &event ) ) return res; // On double-click, expand/collapse. @@ -4039,7 +4222,7 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK; selFlag = wxPG_SEL_FOCUS; } - if ( !DoSelectProperty( p, selFlag ) ) + if ( !AddToSelectionFromInputEvent( p, &event, selFlag ) ) return res; m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK); @@ -4130,15 +4313,15 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even // ----------------------------------------------------------------------- -bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), unsigned int WXUNUSED(y), - wxMouseEvent& WXUNUSED(event) ) +bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), + unsigned int WXUNUSED(y), + wxMouseEvent& event ) { if ( m_propHover ) { // Select property here as well wxPGProperty* p = m_propHover; - if ( p != m_selected ) - DoSelectProperty( p ); + AddToSelectionFromInputEvent(p, &event); // Send right click event. SendEvent( wxEVT_PG_RIGHT_CLICK, p ); @@ -4150,16 +4333,16 @@ bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), unsigned int WXUNUS // ----------------------------------------------------------------------- -bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), unsigned int WXUNUSED(y), - wxMouseEvent& WXUNUSED(event) ) +bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), + unsigned int WXUNUSED(y), + wxMouseEvent& event ) { if ( m_propHover ) { // Select property here as well wxPGProperty* p = m_propHover; - if ( p != m_selected ) - DoSelectProperty( p ); + AddToSelectionFromInputEvent(p, &event); // Send double-click event. SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover ); @@ -4226,7 +4409,7 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false ); state->m_fSplitterX = (float) newSplitterX; - if ( m_selected ) + if ( GetSelection() ) CorrectEditorWidgetSizeX(); Update(); @@ -4385,6 +4568,18 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event CustomSetCursor( wxCURSOR_ARROW ); } } + + // + // Multi select by dragging + // + if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION && + event.LeftIsDown() && + m_propHover && + GetSelection() && + !state->DoIsPropertySelected(m_propHover) ) + { + DoAddToSelection(m_propHover); + } } return true; } @@ -4434,8 +4629,9 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y), m_dragStatus = 0; // Control background needs to be cleared - if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && m_selected ) - DrawItem( m_selected ); + wxPGProperty* selected = GetSelection(); + if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && selected ) + DrawItem( selected ); if ( m_wndEditor ) { @@ -4659,8 +4855,10 @@ void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent &event ) // but that should not matter (right click is about item, not position). wxPoint pt = m_wndEditor->GetPosition(); CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y ); - wxASSERT( m_selected ); - m_propHover = m_selected; + + // FIXME: Used to set m_propHover to selection here. Was it really + // necessary? + bool res = HandleMouseRightClick(x,y,event); if ( !res ) event.Skip(); } @@ -4758,6 +4956,7 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) wxCHECK2(!m_frozen, return); // Travelsal between items, collapsing/expanding, etc. + wxPGProperty* selected = GetSelection(); int keycode = event.GetKeyCode(); bool editorFocused = IsEditorFocused(); @@ -4774,7 +4973,7 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) { if ( !editorFocused && m_wndEditor ) { - DoSelectProperty( m_selected, wxPG_SEL_FOCUS ); + DoSelectProperty( selected, wxPG_SEL_FOCUS ); } else { @@ -4833,12 +5032,13 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) EditorsValueWasNotModified(); // Update the control as well - m_selected->GetEditorClass()->SetControlStringValue( m_selected, - GetEditorControl(), - m_selected->GetDisplayedString() ); + selected->GetEditorClass()-> + SetControlStringValue( selected, + GetEditorControl(), + selected->GetDisplayedString() ); } - OnValidationFailureReset(m_selected); + OnValidationFailureReset(selected); UnfocusEditor(); return; @@ -4858,13 +5058,13 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) bool wasHandled = false; - if ( m_selected ) + if ( selected ) { // Show dialog? if ( ButtonTriggerKeyTest(action, event) ) return; - wxPGProperty* p = m_selected; + wxPGProperty* p = selected; // Travel and expand/collapse int selectDir = -2; @@ -5065,8 +5265,9 @@ void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused ) } // Redraw selected - if ( m_selected && (m_iFlags & wxPG_FL_INITIALIZED) ) - DrawItem( m_selected ); + wxPGProperty* selected = GetSelection(); + if ( selected && (m_iFlags & wxPG_FL_INITIALIZED) ) + DrawItem( selected ); } } diff --git a/src/propgrid/propgridiface.cpp b/src/propgrid/propgridiface.cpp index ff891b9f4f..a1c037756d 100644 --- a/src/propgrid/propgridiface.cpp +++ b/src/propgrid/propgridiface.cpp @@ -147,10 +147,6 @@ void wxPropertyGridInterface::DeleteProperty( wxPGPropArg id ) wxPG_PROP_ARG_CALL_PROLOG() wxPropertyGridPageState* state = p->GetParentState(); - wxPropertyGrid* grid = state->GetGrid(); - - if ( grid->GetState() == state ) - grid->DoSelectProperty(NULL, wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE); state->DoDelete( p, true ); @@ -167,13 +163,6 @@ wxPGProperty* wxPropertyGridInterface::RemoveProperty( wxPGPropArg id ) wxNullProperty); wxPropertyGridPageState* state = p->GetParentState(); - wxPropertyGrid* grid = state->GetGrid(); - - if ( grid->GetState() == state ) - { - grid->DoSelectProperty(NULL, - wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE); - } state->DoDelete( p, false ); @@ -218,11 +207,25 @@ wxPGProperty* wxPropertyGridInterface::ReplaceProperty( wxPGPropArg id, wxPGProp // wxPropertyGridInterface property operations // ----------------------------------------------------------------------- +wxPGProperty* wxPropertyGridInterface::GetSelection() const +{ + return m_pState->GetSelection(); +} + +// ----------------------------------------------------------------------- + bool wxPropertyGridInterface::ClearSelection( bool validation ) { - int flags = wxPG_SEL_DONT_SEND_EVENT; + return DoClearSelection(validation, wxPG_SEL_DONT_SEND_EVENT); +} + +// ----------------------------------------------------------------------- + +bool wxPropertyGridInterface::DoClearSelection( bool validation, + int selFlags ) +{ if ( !validation ) - flags |= wxPG_SEL_NOVALIDATE; + selFlags |= wxPG_SEL_NOVALIDATE; wxPropertyGridPageState* state = m_pState; @@ -230,9 +233,9 @@ bool wxPropertyGridInterface::ClearSelection( bool validation ) { wxPropertyGrid* pg = state->GetGrid(); if ( pg->GetState() == state ) - return pg->DoSelectProperty(NULL, flags); + return pg->DoSelectProperty(NULL, selFlags); else - state->SetSelection(NULL); + state->DoSetSelection(NULL); } return true; @@ -1026,7 +1029,7 @@ bool wxPropertyGridInterface::RestoreEditableState( const wxString& src, int res else { if ( values[0].length() ) - pageState->SetSelection(GetPropertyByName(value)); + pageState->DoSetSelection(GetPropertyByName(value)); else pageState->DoClearSelection(); } diff --git a/src/propgrid/propgridpagestate.cpp b/src/propgrid/propgridpagestate.cpp index 57668950d1..966a921002 100644 --- a/src/propgrid/propgridpagestate.cpp +++ b/src/propgrid/propgridpagestate.cpp @@ -206,7 +206,6 @@ wxPropertyGridPageState::wxPropertyGridPageState() m_properties = &m_regularArray; m_abcArray = NULL; m_currentCategory = NULL; - m_selected = NULL; m_width = 0; m_virtualHeight = 0; m_lastCaptionBottomnest = 1; @@ -274,7 +273,7 @@ void wxPropertyGridPageState::DoClear() } else { - m_selected = NULL; + m_selection.clear(); } m_regularArray.Empty(); @@ -1150,7 +1149,8 @@ bool wxPropertyGridPageState::DoSetPropertyValueString( wxPGProperty* p, const w if ( res ) { p->SetValue(variant); - if ( m_selected==p && this==m_pPropGrid->GetState() ) + if ( p == m_pPropGrid->GetSelection() && + this == m_pPropGrid->GetState() ) m_pPropGrid->RefreshEditor(); } @@ -1166,7 +1166,8 @@ bool wxPropertyGridPageState::DoSetPropertyValue( wxPGProperty* p, wxVariant& va if ( p ) { p->SetValue(value); - if ( m_selected==p && this==m_pPropGrid->GetState() ) + if ( p == m_pPropGrid->GetSelection() && + this == m_pPropGrid->GetState() ) m_pPropGrid->RefreshEditor(); return true; @@ -1192,6 +1193,21 @@ bool wxPropertyGridPageState::DoSetPropertyValueWxObjectPtr( wxPGProperty* p, wx // wxPropertyGridPageState property operations // ----------------------------------------------------------------------- +bool wxPropertyGridPageState::DoIsPropertySelected( wxPGProperty* prop ) const +{ + const wxArrayPGProperty& selection = m_selection; + + for ( unsigned int i=0; iGetState() ) return m_pPropGrid->DoSelectProperty( p, flags ); - m_selected = p; + DoSetSelection(p); return true; } @@ -1711,6 +1727,25 @@ void wxPropertyGridPageState::DoDelete( wxPGProperty* item, bool doDelete ) wxCHECK_RET( !parent->HasFlag(wxPG_PROP_AGGREGATE), wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") ); + wxASSERT( item->GetParentState() == this ); + + wxPropertyGrid* pg = GetGrid(); + + if ( DoIsPropertySelected(item) ) + { + if ( pg && pg->GetState() == this ) + { + pg->DoRemoveFromSelection(item, + wxPG_SEL_DELETING|wxPG_SEL_NOVALIDATE); + } + else + { + m_selection.Remove(item); + } + } + + item->SetFlag(wxPG_PROP_BEING_DELETED); + // Delete children if ( item->GetChildCount() && !item->HasFlag(wxPG_PROP_AGGREGATE) ) { @@ -1780,8 +1815,6 @@ void wxPropertyGridPageState::DoDelete( wxPGProperty* item, bool doDelete ) (parent->IsCategory() || parent->IsRoot()) ) m_dictName.erase(item->GetBaseName()); - wxPropertyGrid* pg = GetGrid(); - // We need to clear parent grid's m_propHover, if it matches item if ( pg && pg->m_propHover == item ) pg->m_propHover = NULL; -- 2.45.2