From 58935d4a5c5546124b4903907b5785da270b4c31 Mon Sep 17 00:00:00 2001 From: Jaakko Salli Date: Sun, 23 Aug 2009 15:31:03 +0000 Subject: [PATCH] Added label editing capability into wxPropertyGrid git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61743 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/propgrid/property.h | 24 +- include/wx/propgrid/propgrid.h | 140 +++++++++-- include/wx/propgrid/propgridiface.h | 3 + include/wx/propgrid/propgridpagestate.h | 3 + interface/wx/propgrid/property.h | 14 ++ interface/wx/propgrid/propgrid.h | 46 ++++ samples/propgrid/propgrid.cpp | 35 ++- samples/propgrid/propgrid.h | 3 + src/propgrid/editors.cpp | 26 +- src/propgrid/property.cpp | 2 +- src/propgrid/propgrid.cpp | 307 +++++++++++++++++++++--- src/propgrid/propgridiface.cpp | 4 +- src/propgrid/propgridpagestate.cpp | 3 + 13 files changed, 548 insertions(+), 62 deletions(-) diff --git a/include/wx/propgrid/property.h b/include/wx/propgrid/property.h index bddf659f98..ca40fb7765 100644 --- a/include/wx/propgrid/property.h +++ b/include/wx/propgrid/property.h @@ -1561,10 +1561,25 @@ public: /** Returns wxPGCell of given column. + + @remarks const version of this member function returns 'default' + wxPGCell object if the property itself didn't hold + cell data. */ const wxPGCell& GetCell( unsigned int column ) const; - wxPGCell& GetCell( unsigned int column ); + /** + Returns wxPGCell of given column, creating one if necessary. + */ + wxPGCell& GetCell( unsigned int column ) + { + return GetOrCreateCell(column); + } + + /** + Returns wxPGCell of given column, creating one if necessary. + */ + wxPGCell& GetOrCreateCell( unsigned int column ); /** Return number of displayed common values for this property. */ @@ -2242,6 +2257,13 @@ protected: /** Deletes all sub-properties. */ void Empty(); + bool HasCell( unsigned int column ) const + { + if ( m_cells.size() > column ) + return true; + return false; + } + void InitAfterAdded( wxPropertyGridPageState* pageState, wxPropertyGrid* propgrid ); diff --git a/include/wx/propgrid/propgrid.h b/include/wx/propgrid/propgrid.h index b33c55ed36..aa6c9cea1b 100644 --- a/include/wx/propgrid/propgrid.h +++ b/include/wx/propgrid/propgrid.h @@ -448,22 +448,23 @@ enum wxPG_KEYBOARD_ACTIONS // wxPropertyGrid::DoSelectProperty flags (selFlags) // Focuses to created editor -#define wxPG_SEL_FOCUS 0x01 +#define wxPG_SEL_FOCUS 0x0001 // Forces deletion and recreation of editor -#define wxPG_SEL_FORCE 0x02 +#define wxPG_SEL_FORCE 0x0002 // For example, doesn't cause EnsureVisible -#define wxPG_SEL_NONVISIBLE 0x04 +#define wxPG_SEL_NONVISIBLE 0x0004 // Do not validate editor's value before selecting -#define wxPG_SEL_NOVALIDATE 0x08 +#define wxPG_SEL_NOVALIDATE 0x0008 // Property being deselected is about to be deleted -#define wxPG_SEL_DELETING 0x10 +#define wxPG_SEL_DELETING 0x0010 // Property's values was set to unspecified by the user -#define wxPG_SEL_SETUNSPEC 0x20 +#define wxPG_SEL_SETUNSPEC 0x0020 // Property's event handler changed the value -#define wxPG_SEL_DIALOGVAL 0x40 +#define wxPG_SEL_DIALOGVAL 0x0040 // Set to disable sending of wxEVT_PG_SELECTED event -#define wxPG_SEL_DONT_SEND_EVENT 0x80 - +#define wxPG_SEL_DONT_SEND_EVENT 0x0080 +// Don't make any graphics updates +#define wxPG_SEL_NO_REFRESH 0x0100 // ----------------------------------------------------------------------- @@ -583,6 +584,14 @@ enum wxPG_KEYBOARD_ACTIONS @event{EVT_PG_ITEM_EXPANDED(id, func)} Respond to wxEVT_PG_ITEM_EXPANDED event, generated when user expands a property or category.. + @event{EVT_PG_LABEL_EDIT_BEGIN(id, func)} + Respond to wxEVT_PG_LABEL_EDIT_BEGIN event, generated when is about to + begin editing a property label. You can veto this event to prevent the + action. + @event{EVT_PG_LABEL_EDIT_ENDING(id, func)} + Respond to wxEVT_PG_LABEL_EDIT_ENDING event, generated when is about to + end editing of a property label. You can veto this event to prevent the + action. @endEventTable @remarks @@ -1000,6 +1009,57 @@ public: return DoRemoveFromSelection(p, wxPG_SEL_DONT_SEND_EVENT); } + /** + Makes given column editable by user. + + @see BeginLabelEdit(), EndLabelEdit() + */ + void MakeColumnEditable( unsigned int column ) + { + wxASSERT( column != 1 ); + m_pState->m_editableColumns.push_back(column); + } + + /** + Creates label editor wxTextCtrl for given column, for property + that is currently selected. When multiple selection is + enabled, this applies to whatever property GetSelection() + returns. + + @param colIndex + Which column's label to edit. Note that you should not + use value 1, which is reserved for property value + column. + + @see EndLabelEdit(), MakeColumnEditable() + */ + void BeginLabelEdit( unsigned int column = 0 ) + { + DoBeginLabelEdit(column, wxPG_SEL_DONT_SEND_EVENT); + } + + /** + Destroys label editor wxTextCtrl, if any. + + @param commit + Use @true (default) to store edited label text in + property cell data. + + @see BeginLabelEdit(), MakeColumnEditable() + */ + void EndLabelEdit( bool commit = true ) + { + DoEndLabelEdit(commit, wxPG_SEL_DONT_SEND_EVENT); + } + + /** + Returns currently active label editor, NULL if none. + */ + wxTextCtrl* GetLabelEditor() const + { + return m_labelEditor; + } + /** Sets category caption background colour. */ void SetCaptionBackgroundColour(const wxColour& col); @@ -1199,6 +1259,7 @@ public: shown). */ void FixPosForTextCtrl( wxWindow* ctrl, + unsigned int forColumn = 1, const wxPoint& offset = wxPoint(0, 0) ); /** Shortcut for creating text editor widget. @@ -1222,7 +1283,8 @@ public: const wxString& value, wxWindow* secondary, int extraStyle = 0, - int maxLen = 0 ); + int maxLen = 0, + unsigned int forColumn = 1 ); /* Generates both textctrl and button. */ @@ -1677,9 +1739,18 @@ protected: /** When drawing next time, clear this many item slots at the end. */ int m_clearThisMany; + // Mouse is hovering over this column (index) + unsigned int m_colHover; + // pointer to property that has mouse hovering wxPGProperty* m_propHover; + // Active label editor + wxTextCtrl* m_labelEditor; + + // For which property the label editor is active + wxPGProperty* m_labelEditorProperty; + // EventObject for wxPropertyGridEvents wxWindow* m_eventObject; @@ -1701,8 +1772,9 @@ protected: // y coordinate of property that mouse hovering int m_propHoverY; + // Which column's editor is selected (usually 1)? - int m_selColumn; + unsigned int m_selColumn; // x relative to splitter (needed for resize). int m_ctrlXAdjust; @@ -1804,6 +1876,7 @@ protected: protected: bool AddToSelectionFromInputEvent( wxPGProperty* prop, + unsigned int colIndex, wxMouseEvent* event = NULL, int selFlags = 0 ); @@ -1858,6 +1931,13 @@ protected: bool DoEditorValidate(); + // Similar to DoSelectProperty() but also works on columns + // other than 1. Does not active editor if column is not + // editable. + bool DoSelectAndEdit( wxPGProperty* prop, + unsigned int colIndex, + unsigned int selFlags ); + void DoSetSelection( const wxArrayPGProperty& newSelection, int selFlags = 0 ); @@ -1867,6 +1947,11 @@ protected: bool DoRemoveFromSelection( wxPGProperty* prop, int selFlags = 0 ); + void DoBeginLabelEdit( unsigned int colIndex, int selFlags = 0 ); + void DoEndLabelEdit( bool commit, int selFlags = 0 ); + void OnLabelEditorEnterPress( wxCommandEvent& event ); + void OnLabelEditorKeyPress( wxKeyEvent& event ); + wxPGProperty* DoGetItemAtY( int y ) const; void DoSetSplitterPosition_( int newxpos, @@ -1874,6 +1959,7 @@ protected: int splitterIndex = 0, bool allPages = false ); + void DestroyEditorWnd( wxWindow* wnd ); void FreeEditors(); virtual bool DoExpand( wxPGProperty* p, bool sendEvent = false ); @@ -1900,10 +1986,11 @@ protected: void PrepareAfterItemsAdded(); - bool SendEvent( int eventType, - wxPGProperty* p, + // Omit the wxPG_SEL_NOVALIDATE flag to allow vetoing the event + bool SendEvent( int eventType, wxPGProperty* p, wxVariant* pValue = NULL, - unsigned int selFlags = 0 ); + unsigned int selFlags = wxPG_SEL_NOVALIDATE, + unsigned int column = 1 ); void SetFocusOnCanvas(); @@ -1980,6 +2067,10 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, wxEVT_PG_PAGE_CHANGED, wxPropert wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, wxEVT_PG_ITEM_EXPANDED, wxPropertyGridEvent ); wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent ); +wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, + wxEVT_PG_LABEL_EDIT_BEGIN, wxPropertyGridEvent ); +wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, + wxEVT_PG_LABEL_EDIT_ENDING, wxPropertyGridEvent ); #else enum { @@ -1991,7 +2082,9 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_PROPGRID, wxEVT_PG_DOUBLE_CLICK, wxPropert wxEVT_PG_PAGE_CHANGED, wxEVT_PG_ITEM_COLLAPSED, wxEVT_PG_ITEM_EXPANDED, - wxEVT_PG_DOUBLE_CLICK + wxEVT_PG_DOUBLE_CLICK, + wxEVT_PG_LABEL_EDIT_BEGIN, + wxEVT_PG_LABEL_EDIT_ENDING }; #endif @@ -2012,6 +2105,8 @@ typedef void (wxEvtHandler::*wxPropertyGridEventFunction)(wxPropertyGridEvent&); #define EVT_PG_PAGE_CHANGED(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_PG_PAGE_CHANGED, id, -1, wxEVENT_HANDLER_CAST( wxPropertyGridEventFunction, fn ), NULL ), #define EVT_PG_ITEM_COLLAPSED(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_PG_ITEM_COLLAPSED, id, -1, wxEVENT_HANDLER_CAST( wxPropertyGridEventFunction, fn ), NULL ), #define EVT_PG_ITEM_EXPANDED(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_PG_ITEM_EXPANDED, id, -1, wxEVENT_HANDLER_CAST( wxPropertyGridEventFunction, fn ), NULL ), +#define EVT_PG_LABEL_EDIT_BEGIN(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_PG_LABEL_EDIT_BEGIN, id, -1, wxEVENT_HANDLER_CAST( wxPropertyGridEventFunction, fn ), NULL ), +#define EVT_PG_LABEL_EDIT_ENDING(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_PG_LABEL_EDIT_ENDING, id, -1, wxEVENT_HANDLER_CAST( wxPropertyGridEventFunction, fn ), NULL ), #define wxPropertyGridEventHandler(fn) \ wxEVENT_HANDLER_CAST( wxPropertyGridEventFunction, fn ) @@ -2043,6 +2138,14 @@ public: /** Copyer. */ virtual wxEvent* Clone() const; + /** + Returns the column index associated with this event. + */ + unsigned int GetColumn() const + { + return m_column; + } + wxPGProperty* GetMainParent() const { wxASSERT(m_property); @@ -2114,6 +2217,11 @@ public: return m_validationInfo->GetFailureBehavior(); } + void SetColumn( unsigned int column ) + { + m_column = column; + } + void SetCanVeto( bool canVeto ) { m_canVeto = canVeto; } bool WasVetoed() const { return m_wasVetoed; } @@ -2137,6 +2245,8 @@ private: wxPropertyGrid* m_pg; wxPGValidationInfo* m_validationInfo; + unsigned int m_column; + bool m_canVeto; bool m_wasVetoed; diff --git a/include/wx/propgrid/propgridiface.h b/include/wx/propgrid/propgridiface.h index a110518c04..cb16eb93d9 100644 --- a/include/wx/propgrid/propgridiface.h +++ b/include/wx/propgrid/propgridiface.h @@ -1287,6 +1287,9 @@ public: static wxPGEditor* GetEditorByName( const wxString& editorName ); + // NOTE: This function reselects the property and may cause + // excess flicker, so to just call Refresh() on a rect + // of single property, call DrawItem() instead. virtual void RefreshProperty( wxPGProperty* p ) = 0; protected: diff --git a/include/wx/propgrid/propgridpagestate.h b/include/wx/propgrid/propgridpagestate.h index 1f5e00078e..3ebebcf9ce 100644 --- a/include/wx/propgrid/propgridpagestate.h +++ b/include/wx/propgrid/propgridpagestate.h @@ -668,6 +668,9 @@ protected: /** List of column widths (first column does not include margin). */ wxArrayInt m_colWidths; + /** List of indices of columns the user can edit by clicking it. */ + wxArrayInt m_editableColumns; + double m_fSplitterX; /** Most recently added category. */ diff --git a/interface/wx/propgrid/property.h b/interface/wx/propgrid/property.h index 82f34fa08f..fb4c7bb7b8 100644 --- a/interface/wx/propgrid/property.h +++ b/interface/wx/propgrid/property.h @@ -1028,9 +1028,23 @@ public: /** Returns wxPGCell of given column. + + @remarks const version of this member function returns 'default' + wxPGCell object if the property itself didn't hold + cell data. */ const wxPGCell& GetCell( unsigned int column ) const; + /** + Returns wxPGCell of given column, creating one if necessary. + */ + wxPGCell& GetCell( unsigned int column ); + + /** + Returns wxPGCell of given column, creating one if necessary. + */ + wxPGCell& GetOrCreateCell( unsigned int column ); + /** Returns number of child properties. */ diff --git a/interface/wx/propgrid/propgrid.h b/interface/wx/propgrid/propgrid.h index 0872e5775d..bfe94e4bc5 100644 --- a/interface/wx/propgrid/propgrid.h +++ b/interface/wx/propgrid/propgrid.h @@ -359,6 +359,14 @@ typedef int (*wxPGSortCallback)(wxPropertyGrid* propGrid, @event{EVT_PG_ITEM_EXPANDED(id, func)} Respond to wxEVT_PG_ITEM_EXPANDED event, generated when user expands a property or category. + @event{EVT_PG_LABEL_EDIT_BEGIN(id, func)} + Respond to wxEVT_PG_LABEL_EDIT_BEGIN event, generated when is about to + begin editing a property label. You can veto this event to prevent the + action. + @event{EVT_PG_LABEL_EDIT_ENDING(id, func)} + Respond to wxEVT_PG_LABEL_EDIT_ENDING event, generated when is about to + end editing of a property label. You can veto this event to prevent the + action. @endEventTable @remarks @@ -430,6 +438,21 @@ public: */ static void AutoGetTranslation( bool enable ); + /** + Creates label editor wxTextCtrl for given column, for property + that is currently selected. When multiple selection is + enabled, this applies to whatever property GetSelection() + returns. + + @param colIndex + Which column's label to edit. Note that you should not + use value 1, which is reserved for property value + column. + + @see EndLabelEdit(), MakeColumnEditable() + */ + void BeginLabelEdit( unsigned int column = 0 ); + /** Changes value of a property, as if from an editor. Use this instead of SetPropertyValue() if you need the value to run through validation @@ -491,6 +514,17 @@ public: */ bool EnableCategories( bool enable ); + /** + Destroys label editor wxTextCtrl, if any. + + @param commit + Use @true (default) to store edited label text in + property cell data. + + @see BeginLabelEdit(), MakeColumnEditable() + */ + void EndLabelEdit( bool commit = true ); + /** Scrolls and/or expands items to ensure that the given item is visible. @@ -515,6 +549,11 @@ public: */ wxSize FitColumns(); + /** + Returns currently active label editor, NULL if none. + */ + wxTextCtrl* GetLabelEditor() const; + /** Returns wxWindow that the properties are painted on, and which should be used as the parent for editor controls. @@ -696,6 +735,13 @@ public: */ bool IsFrozen() const; + /** + Makes given column editable by user. + + @see BeginLabelEdit(), EndLabelEdit() + */ + void MakeColumnEditable( unsigned int column ); + /** It is recommended that you call this function any time your code causes wxPropertyGrid's top-level parent to change. wxPropertyGrid's OnIdle() diff --git a/samples/propgrid/propgrid.cpp b/samples/propgrid/propgrid.cpp index 41ab8716ab..c148abf8a1 100644 --- a/samples/propgrid/propgrid.cpp +++ b/samples/propgrid/propgrid.cpp @@ -687,7 +687,8 @@ enum ID_SELECTSTYLE, ID_SAVESTATE, ID_RESTORESTATE, - ID_RUNMINIMAL + ID_RUNMINIMAL, + ID_ENABLELABELEDITING }; // ----------------------------------------------------------------------- @@ -713,6 +714,12 @@ BEGIN_EVENT_TABLE(FormMain, wxFrame) EVT_PG_DOUBLE_CLICK( PGID, FormMain::OnPropertyGridItemDoubleClick ) // This occurs when propgridmanager's page changes. EVT_PG_PAGE_CHANGED( PGID, FormMain::OnPropertyGridPageChange ) + // This occurs when user starts editing a property label + EVT_PG_LABEL_EDIT_BEGIN( PGID, + FormMain::OnPropertyGridLabelEditBegin ) + // This occurs when user stops editing a property label + EVT_PG_LABEL_EDIT_ENDING( PGID, + FormMain::OnPropertyGridLabelEditEnding ) // This occurs when property's editor button (if any) is clicked. EVT_BUTTON( PGID, FormMain::OnPropertyGridButtonClick ) @@ -745,6 +752,7 @@ BEGIN_EVENT_TABLE(FormMain, wxFrame) EVT_MENU( ID_SETBGCOLOURRECUR, FormMain::OnSetBackgroundColour ) EVT_MENU( ID_CLEARMODIF, FormMain::OnClearModifyStatusClick ) EVT_MENU( ID_FREEZE, FormMain::OnFreezeClick ) + EVT_MENU( ID_ENABLELABELEDITING, FormMain::OnEnableLabelEditing ) EVT_MENU( ID_DUMPLIST, FormMain::OnDumpList ) EVT_MENU( ID_COLOURSCHEME1, FormMain::OnColourScheme ) @@ -1018,6 +1026,22 @@ void FormMain::OnPropertyGridPageChange( wxPropertyGridEvent& WXUNUSED(event) ) // ----------------------------------------------------------------------- +void FormMain::OnPropertyGridLabelEditBegin( wxPropertyGridEvent& event ) +{ + wxLogDebug("wxPG_EVT_LABEL_EDIT_BEGIN(%s)", + event.GetProperty()->GetLabel().c_str()); +} + +// ----------------------------------------------------------------------- + +void FormMain::OnPropertyGridLabelEditEnding( wxPropertyGridEvent& event ) +{ + wxLogDebug("wxPG_EVT_LABEL_EDIT_ENDING(%s)", + event.GetProperty()->GetLabel().c_str()); +} + +// ----------------------------------------------------------------------- + void FormMain::OnPropertyGridHighlight( wxPropertyGridEvent& WXUNUSED(event) ) { } @@ -2219,6 +2243,8 @@ FormMain::FormMain(const wxString& title, const wxPoint& pos, const wxSize& size menuTry->Append(ID_SELECTSTYLE, wxT("Set Window Style"), wxT("Select window style flags used by the grid.")); + menuTry->Append(ID_ENABLELABELEDITING, "Enable label editing", + "This calls wxPropertyGrid::MakeColumnEditable(0)"); menuTry->AppendSeparator(); menuTry->AppendRadioItem( ID_COLOURSCHEME1, wxT("Standard Colour Scheme") ); menuTry->AppendRadioItem( ID_COLOURSCHEME2, wxT("White Colour Scheme") ); @@ -2762,6 +2788,13 @@ void FormMain::OnFreezeClick( wxCommandEvent& event ) // ----------------------------------------------------------------------- +void FormMain::OnEnableLabelEditing( wxCommandEvent& WXUNUSED(event) ) +{ + m_propGrid->MakeColumnEditable(0); +} + +// ----------------------------------------------------------------------- + void FormMain::OnAbout(wxCommandEvent& WXUNUSED(event)) { wxString msg; diff --git a/samples/propgrid/propgrid.h b/samples/propgrid/propgrid.h index 54c5f15ee3..e3c5dc5b6a 100644 --- a/samples/propgrid/propgrid.h +++ b/samples/propgrid/propgrid.h @@ -184,6 +184,7 @@ public: void OnSetBackgroundColour( wxCommandEvent& event ); void OnClearModifyStatusClick( wxCommandEvent& event ); void OnFreezeClick( wxCommandEvent& event ); + void OnEnableLabelEditing( wxCommandEvent& event ); void OnDumpList( wxCommandEvent& event ); void OnCatColours( wxCommandEvent& event ); void OnSetColumns( wxCommandEvent& event ); @@ -230,6 +231,8 @@ public: void OnPropertyGridKeyEvent( wxKeyEvent& event ); void OnPropertyGridItemCollapse( wxPropertyGridEvent& event ); void OnPropertyGridItemExpand( wxPropertyGridEvent& event ); + void OnPropertyGridLabelEditBegin( wxPropertyGridEvent& event ); + void OnPropertyGridLabelEditEnding( wxPropertyGridEvent& event ); void OnAbout( wxCommandEvent& event ); diff --git a/src/propgrid/editors.cpp b/src/propgrid/editors.cpp index 6109df5f0d..940b2f62fe 100644 --- a/src/propgrid/editors.cpp +++ b/src/propgrid/editors.cpp @@ -1603,7 +1603,9 @@ void wxPropertyGrid::CorrectEditorWidgetPosY() // Fixes position of wxTextCtrl-like control (wxSpinCtrl usually // fits into that category as well). -void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl, const wxPoint& offset ) +void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl, + unsigned int forColumn, + const wxPoint& offset ) { // Center the control vertically wxRect finalPos = ctrl->GetRect(); @@ -1616,7 +1618,10 @@ void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl, const wxPoint& offset ) finalPos.y += y_adj; finalPos.height -= (y_adj+sz_dec); - const int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST; + int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST; + + if ( forColumn != 1 ) + textCtrlXAdjust -= 3; // magic number! finalPos.x += textCtrlXAdjust; finalPos.width -= textCtrlXAdjust; @@ -1634,7 +1639,8 @@ wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos, const wxString& value, wxWindow* secondary, int extraStyle, - int maxLen ) + int maxLen, + unsigned int forColumn ) { wxWindowID id = wxPG_SUBID1; wxPGProperty* prop = GetSelection(); @@ -1653,7 +1659,11 @@ wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos, s.x -= 8; #endif - // Take button into acccount + // For label editors, trim the size to allow better splitter grabbing + if ( forColumn != 1 ) + s.x -= 2; + + // Take button into acccount if ( secondary ) { s.x -= (secondary->GetSize().x + wxPG_TEXTCTRL_AND_BUTTON_SPACING); @@ -1681,7 +1691,13 @@ wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos, // Center the control vertically if ( !hasSpecialSize ) - FixPosForTextCtrl(tc); + FixPosForTextCtrl(tc, forColumn); + + if ( forColumn != 1 ) + { + tc->SetBackgroundColour(m_colSelBack); + tc->SetForegroundColour(m_colSelFore); + } #ifdef __WXMSW__ tc->Show(); diff --git a/src/propgrid/property.cpp b/src/propgrid/property.cpp index afb45d3fa6..325ebc094a 100644 --- a/src/propgrid/property.cpp +++ b/src/propgrid/property.cpp @@ -1511,7 +1511,7 @@ const wxPGCell& wxPGProperty::GetCell( unsigned int column ) const return pg->GetPropertyDefaultCell(); } -wxPGCell& wxPGProperty::GetCell( unsigned int column ) +wxPGCell& wxPGProperty::GetOrCreateCell( unsigned int column ) { EnsureCells(column); return m_cells[column]; diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 4f0fe08455..695049d66c 100644 --- a/src/propgrid/propgrid.cpp +++ b/src/propgrid/propgrid.cpp @@ -428,8 +428,11 @@ void wxPropertyGrid::Init1() m_iFlags = 0; m_pState = NULL; m_wndEditor = m_wndEditor2 = NULL; - m_selColumn = -1; + m_selColumn = 1; + m_colHover = 1; m_propHover = NULL; + m_labelEditor = NULL; + m_labelEditorProperty = NULL; m_eventObject = this; m_curFocused = NULL; m_sortFunction = NULL; @@ -759,12 +762,10 @@ bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags ) if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) { - SendEvent( wxEVT_PG_SELECTED, prop, NULL, selFlags ); + SendEvent( wxEVT_PG_SELECTED, prop, NULL ); } - // For some reason, if we use RefreshProperty(prop) here, - // we may go into infinite drawing loop. - Refresh(); + DrawItem(prop); } return true; @@ -785,7 +786,7 @@ bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags ) else { m_pState->DoRemoveFromSelection(prop); - RefreshProperty(prop); + DrawItem(prop); res = true; } @@ -794,7 +795,53 @@ bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags ) // ----------------------------------------------------------------------- +bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty* prop, + unsigned int colIndex, + unsigned int selFlags ) +{ + // + // NB: Enable following if label editor background colour is + // ever changed to any other than m_colSelBack. + // + // We use this workaround to prevent visible flicker when editing + // a cell. Atleast on wxMSW, there is a difficult to find + // (and perhaps prevent) redraw somewhere between making property + // selected and enabling label editing. + // + //wxColour prevColSelBack = m_colSelBack; + //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); + + bool res; + + if ( colIndex == 1 ) + { + res = DoSelectProperty(prop, selFlags); + } + else + { + // send event + DoClearSelection(false, wxPG_SEL_NO_REFRESH); + + if ( m_pState->m_editableColumns.Index(colIndex) == wxNOT_FOUND ) + { + res = DoAddToSelection(prop, selFlags); + } + else + { + res = DoAddToSelection(prop, selFlags|wxPG_SEL_NO_REFRESH); + + DoBeginLabelEdit(colIndex, selFlags); + } + } + + //m_colSelBack = prevColSelBack; + return res; +} + +// ----------------------------------------------------------------------- + bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop, + unsigned int colIndex, wxMouseEvent* mouseEvent, int selFlags ) { @@ -813,7 +860,7 @@ bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop, // disturbing the selection. if ( GetSelectedProperties().size() <= 1 || !alreadySelected ) - return DoSelectProperty(prop, selFlags); + return DoSelectAndEdit(prop, colIndex, selFlags); return true; } else @@ -844,7 +891,7 @@ bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop, } else { - res = DoSelectProperty(prop, selFlags); + res = DoSelectAndEdit(prop, colIndex, selFlags); } return res; @@ -875,6 +922,143 @@ void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection, // ----------------------------------------------------------------------- +void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex, + int selFlags ) +{ + wxPGProperty* selected = GetSelection(); + wxCHECK_RET(selected, wxT("No property selected")); + wxCHECK_RET(colIndex != 1, wxT("Do not use this for column 1")); + + if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) + { + if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN, + selected, NULL, 0, + colIndex ) ) + return; + } + + wxString text; + const wxPGCell* cell = NULL; + if ( selected->HasCell(colIndex) ) + { + cell = &selected->GetCell(colIndex); + if ( !cell->HasText() && colIndex == 0 ) + text = selected->GetLabel(); + } + + if ( !cell ) + { + if ( colIndex == 0 ) + text = selected->GetLabel(); + else + cell = &selected->GetOrCreateCell(colIndex); + } + + if ( cell && cell->HasText() ) + text = cell->GetText(); + + DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); // send event + + m_selColumn = colIndex; + + wxRect r = GetEditorWidgetRect(selected, m_selColumn); + + wxWindow* tc = GenerateEditorTextCtrl(r.GetPosition(), + r.GetSize(), + text, + NULL, + wxTE_PROCESS_ENTER, + 0, + colIndex); + + wxWindowID id = tc->GetId(); + tc->Connect(id, wxEVT_COMMAND_TEXT_ENTER, + wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress), + NULL, this); + tc->Connect(id, wxEVT_KEY_DOWN, + wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress), + NULL, this); + + tc->SetFocus(); + + m_labelEditor = wxStaticCast(tc, wxTextCtrl); + m_labelEditorProperty = selected; +} + +// ----------------------------------------------------------------------- + +void +wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent& WXUNUSED(event) ) +{ + DoEndLabelEdit(true); +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event ) +{ + int keycode = event.GetKeyCode(); + + if ( keycode == WXK_ESCAPE ) + { + DoEndLabelEdit(false); + } + else + { + event.Skip(); + } +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags ) +{ + if ( !m_labelEditor ) + return; + + wxPGProperty* prop = m_labelEditorProperty; + wxASSERT(prop); + + if ( commit ) + { + if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) ) + { + // wxPG_SEL_NOVALIDATE is passed correctly in selFlags + if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING, + prop, NULL, selFlags, + m_selColumn ) ) + return; + } + + wxString text = m_labelEditor->GetValue(); + wxPGCell* cell = NULL; + if ( prop->HasCell(m_selColumn) ) + { + cell = &prop->GetCell(m_selColumn); + } + else + { + if ( m_selColumn == 0 ) + prop->SetLabel(text); + else + cell = &prop->GetOrCreateCell(m_selColumn); + } + + if ( cell ) + cell->SetText(text); + } + + m_selColumn = 1; + + DestroyEditorWnd(m_labelEditor); + m_labelEditor = NULL; + m_labelEditorProperty = NULL; + + DrawItem(prop); +} + +// ----------------------------------------------------------------------- + void wxPropertyGrid::SetExtraStyle( long exStyle ) { if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING ) @@ -2074,28 +2258,36 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, { cellRect.width = nextCellWidth - 1; - bool ctrlCell = false; + wxWindow* cellEditor = NULL; int cellRenderFlags = renderFlags; - // Tree Item Button + // Tree Item Button (must be drawn before clipping is set up) if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) DrawExpanderButton( dc, butRect, p ); // Background - if ( isSelected && ci == 1 ) + if ( isSelected && (ci == 1 || ci == m_selColumn) ) { - if ( p == firstSelected && m_wndEditor ) + if ( p == firstSelected ) + { + if ( ci == 1 && m_wndEditor ) + cellEditor = m_wndEditor; + else if ( ci == m_selColumn && m_labelEditor ) + cellEditor = m_labelEditor; + } + + if ( cellEditor ) { wxColour editorBgCol = - GetEditorControl()->GetBackgroundColour(); + cellEditor->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 ( m_dragStatus != 0 || + (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) ) + cellEditor = NULL; } else { @@ -2128,7 +2320,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, cellRect.width -= textXAdd; // Foreground - if ( !ctrlCell ) + if ( !cellEditor ) { wxPGCellRenderer* renderer; int cmnVal = p->GetCommonValue(); @@ -2732,7 +2924,8 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue if ( flags & SendEvtChanging ) { // SendEvent returns true if event was vetoed - if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue, 0 ) ) + if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, + &evtChangingValue ) ) return false; } @@ -2974,12 +3167,12 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) while ( pwc != changedProperty ) { - SendEvent( wxEVT_PG_CHANGED, pwc, NULL, selFlags ); + SendEvent( wxEVT_PG_CHANGED, pwc, NULL ); pwc = pwc->GetParent(); } } - SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL, selFlags ); + SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL ); m_inDoPropertyChanged = 0; @@ -3375,6 +3568,17 @@ void wxPropertyGrid::SetupChildEventHandling( wxWindow* argWnd ) NULL, this); } +void wxPropertyGrid::DestroyEditorWnd( wxWindow* wnd ) +{ + if ( !wnd ) + return; + + wnd->Hide(); + + // Do not free editors immediately (for sake of processing events) + wxPendingDelete.Append(wnd); +} + void wxPropertyGrid::FreeEditors() { // @@ -3402,7 +3606,7 @@ void wxPropertyGrid::FreeEditors() wxEvtHandler* handler = m_wndEditor2->PopEventHandler(false); m_wndEditor2->Hide(); wxPendingDelete.Append( handler ); - wxPendingDelete.Append( m_wndEditor2 ); + DestroyEditorWnd(m_wndEditor2); m_wndEditor2 = NULL; } @@ -3411,7 +3615,7 @@ void wxPropertyGrid::FreeEditors() wxEvtHandler* handler = m_wndEditor->PopEventHandler(false); m_wndEditor->Hide(); wxPendingDelete.Append( handler ); - wxPendingDelete.Append( m_wndEditor ); + DestroyEditorWnd(m_wndEditor); m_wndEditor = NULL; } } @@ -3453,6 +3657,9 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) if ( prevFirstSel && prevFirstSel->HasFlag(wxPG_PROP_BEING_DELETED) ) prevFirstSel = NULL; + // Always send event, as this is indirect call + DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); + /* if ( prevFirstSel ) wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() ); @@ -3470,7 +3677,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) { m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); m_editorFocused = 0; - m_selColumn = 1; m_pState->DoSetSelection(p); // If frozen, always free controls. But don't worry, as Thaw will @@ -3528,10 +3734,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } FreeEditors(); - m_selColumn = -1; - - // We need to always fully refresh the grid here - Refresh(false); m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); EditorsValueWasNotModified(); @@ -3541,6 +3743,12 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_pState->DoSetSelection(p); + // Redraw unselected + for ( unsigned int i=0; iShow(true); } - DrawItems(p, p); + if ( !(flags & wxPG_SEL_NO_REFRESH) ) + DrawItem(p); } else { @@ -3791,7 +4000,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, p, NULL, flags ); + SendEvent( wxEVT_PG_SELECTED, p, NULL ); return true; } @@ -4137,19 +4346,28 @@ void wxPropertyGrid::SetFocusOnCanvas() // selFlags uses same values DoSelectProperty's flags // Returns true if event was vetoed. -bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, wxVariant* pValue, unsigned int WXUNUSED(selFlags) ) +bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, + wxVariant* pValue, + unsigned int selFlags, + unsigned int column ) { // Send property grid event of specific type and with specific property wxPropertyGridEvent evt( eventType, m_eventObject->GetId() ); evt.SetPropertyGrid(this); evt.SetEventObject(m_eventObject); evt.SetProperty(p); + evt.SetColumn(column); if ( pValue ) { evt.SetCanVeto(true); evt.SetupValidationInfo(); m_validationInfo.m_pValue = pValue; } + else if ( !(selFlags & wxPG_SEL_NOVALIDATE) ) + { + evt.SetCanVeto(true); + } + wxEvtHandler* evtHandler = m_eventObject->GetEventHandler(); evtHandler->ProcessEvent(evt); @@ -4202,7 +4420,9 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even ) ) { - if ( !AddToSelectionFromInputEvent( p, &event ) ) + if ( !AddToSelectionFromInputEvent( p, + columnHit, + &event ) ) return res; // On double-click, expand/collapse. @@ -4222,7 +4442,10 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK; selFlag = wxPG_SEL_FOCUS; } - if ( !AddToSelectionFromInputEvent( p, &event, selFlag ) ) + if ( !AddToSelectionFromInputEvent( p, + columnHit, + &event, + selFlag ) ) return res; m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK); @@ -4250,9 +4473,13 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even } else if ( m_dragStatus == 0 ) { - // - // Begin draggin the splitter - // + // + // Begin draggin the splitter + // + + // send event + DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); + if ( m_wndEditor ) { // Changes must be committed here or the @@ -4321,7 +4548,7 @@ bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), { // Select property here as well wxPGProperty* p = m_propHover; - AddToSelectionFromInputEvent(p, &event); + AddToSelectionFromInputEvent(p, m_colHover, &event); // Send right click event. SendEvent( wxEVT_PG_RIGHT_CLICK, p ); @@ -4342,7 +4569,7 @@ bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), // Select property here as well wxPGProperty* p = m_propHover; - AddToSelectionFromInputEvent(p, &event); + AddToSelectionFromInputEvent(p, m_colHover, &event); // Send double-click event. SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover ); @@ -4392,6 +4619,8 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset ); int splitterX = x - splitterHitOffset; + m_colHover = columnHit; + if ( m_dragStatus > 0 ) { if ( x > (m_marginWidth + wxPG_DRAG_MARGIN) && @@ -5523,13 +5752,15 @@ wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED, wxPropertyGridEvent ); wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED, wxPropertyGridEvent ); wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEvent ); wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent ); - +wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN, wxPropertyGridEvent ); +wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING, wxPropertyGridEvent ); // ----------------------------------------------------------------------- void wxPropertyGridEvent::Init() { m_validationInfo = NULL; + m_column = 1; m_canVeto = false; m_wasVetoed = false; } diff --git a/src/propgrid/propgridiface.cpp b/src/propgrid/propgridiface.cpp index c0e9b5e46e..88e51c6db2 100644 --- a/src/propgrid/propgridiface.cpp +++ b/src/propgrid/propgridiface.cpp @@ -216,7 +216,9 @@ wxPGProperty* wxPropertyGridInterface::GetSelection() const bool wxPropertyGridInterface::ClearSelection( bool validation ) { - return DoClearSelection(validation, wxPG_SEL_DONT_SEND_EVENT); + bool res = DoClearSelection(validation, wxPG_SEL_DONT_SEND_EVENT); + GetPropertyGrid()->Refresh(); + return res; } // ----------------------------------------------------------------------- diff --git a/src/propgrid/propgridpagestate.cpp b/src/propgrid/propgridpagestate.cpp index 9cb00a145a..39765a7ee7 100644 --- a/src/propgrid/propgridpagestate.cpp +++ b/src/propgrid/propgridpagestate.cpp @@ -215,6 +215,9 @@ wxPropertyGridPageState::wxPropertyGridPageState() m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX ); m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX ); m_fSplitterX = wxPG_DEFAULT_SPLITTERX; + + // By default, we only have the 'value' column editable + m_editableColumns.push_back(1); } // -----------------------------------------------------------------------